Files
crank/apps/ui/js/wizard.js
T
github-ops ba29ac7b94
Deploy / deploy (push) Successful in 2m44s
CI / Rust Checks (push) Successful in 5m31s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 2s
CI / Frontend E2E (push) Successful in 4m24s
chore: publish clean community baseline
2026-06-19 16:45:51 +00:00

287 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function tKey(key) {
return typeof t === 'function' ? t(key) : key;
}
function tfKey(key, vars) {
return typeof tf === 'function' ? tf(key, vars) : key;
}
var TOTAL_STEPS = window.CrankWizardShell.TOTAL_STEPS;
var loadProtocolCapabilities = window.CrankWizardState.loadProtocolCapabilities;
var loadEditionCapabilities = window.CrankWizardState.loadEditionCapabilities;
var currentProtocolCapabilities = window.CrankWizardState.currentProtocolCapabilities;
var loadStep = window.CrankWizardShell.loadStep;
var goToStep = window.CrankWizardShell.goToStep;
var _doGoToStep = window.CrankWizardShell.doGoToStep;
var loadWizardPanels = window.CrankWizardShell.loadWizardPanels;
var bindProtocolCards = window.CrankWizardShell.bindProtocolCards;
var applyEditionProtocolVisibility = window.CrankWizardShell.applyEditionProtocolVisibility;
var step3PanelId = window.CrankWizardShell.step3PanelId;
var saveOperation = window.CrankWizardLive.saveOperation;
var loadOperationForEdit = window.CrankWizardLive.loadOperationForEdit;
var bindWizardLiveActions = window.CrankWizardLive.bindWizardLiveActions;
var updateWizardProtocolVisibility = window.CrankWizardLive.updateWizardProtocolVisibility;
var currentStep = window.currentStep;
var wizardProtocol = window.wizardProtocol;
var wizardMode = window.wizardMode;
var wizardEditId = window.wizardEditId;
var wizardWorkspaceId = window.wizardWorkspaceId;
var wizardCurrentOperation = window.wizardCurrentOperation;
var wizardCurrentVersion = window.wizardCurrentVersion;
var wizardProtoUpload = window.wizardProtoUpload;
var wizardDescriptorSetUpload = window.wizardDescriptorSetUpload;
var wizardSoapWsdlUpload = window.wizardSoapWsdlUpload;
var wizardSoapXsdUpload = window.wizardSoapXsdUpload;
var wizardTestResponsePreview = window.wizardTestResponsePreview;
var wizardProtocolCapabilities = window.wizardProtocolCapabilities;
var wizardSecrets = window.wizardSecrets;
var wizardAuthProfiles = window.wizardAuthProfiles;
var selectedUpstreamId = window.selectedUpstreamId;
var editingUpstreamId = window.editingUpstreamId;
var protoParsed = window.protoParsed;
var selectedRpcMethod = window.selectedRpcMethod;
async function initWizardPage() {
renderSidebarBrand('create');
document.querySelector('.btn-continue').addEventListener('click', function() {
if (currentStep < TOTAL_STEPS) {
goToStep(currentStep + 1);
} else {
saveOperation();
}
});
document.querySelector('.btn-back').addEventListener('click', function() {
if (!this.disabled && currentStep > 1) goToStep(currentStep - 1);
});
document.querySelectorAll('.step-item').forEach(function(item, i) {
item.addEventListener('click', function() { goToStep(i + 1); });
});
var backToCatalog = document.getElementById('back-to-catalog');
if (backToCatalog) {
backToCatalog.addEventListener('click', function() {
window.location.href = (window.CrankRoutes && window.CrankRoutes.home) || '/';
});
}
var closeBtn = document.querySelector('.progress-close');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
window.location.href = (window.CrankRoutes && window.CrankRoutes.home) || '/';
});
}
var saveDraftBtn = document.querySelector('.btn-save-draft');
if (saveDraftBtn) {
saveDraftBtn.addEventListener('click', function() {
saveOperation(true);
});
}
await (window.whenWorkspacesReady ? window.whenWorkspacesReady() : Promise.resolve());
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
wizardWorkspaceId = workspace ? workspace.id : null;
window.wizardWorkspaceId = wizardWorkspaceId;
var editionCapabilities = await loadEditionCapabilities();
await loadProtocolCapabilities();
await loadWizardPanels([1, 2, 3, 4, 5]);
if (window.CrankOverlay && typeof window.CrankOverlay.render === 'function') {
await window.CrankOverlay.render(document, {
workspace: workspace,
capabilities: editionCapabilities,
locale: localStorage.getItem('crank_lang') || 'en',
});
}
await loadWizardAuthResources();
bindProtocolCards();
applyEditionProtocolVisibility(editionCapabilities);
bindWizardLiveActions();
updateUpstreamAuthUi();
renderEditionCapabilityHints(editionCapabilities);
var quickSecretModal = document.getElementById('quick-secret-modal');
var quickSecretClose = document.getElementById('quick-secret-close-btn');
var quickSecretCancel = document.getElementById('quick-secret-cancel-btn');
var quickSecretSubmit = document.getElementById('quick-secret-submit-btn');
if (quickSecretClose) quickSecretClose.addEventListener('click', closeQuickSecretModal);
if (quickSecretCancel) quickSecretCancel.addEventListener('click', closeQuickSecretModal);
if (quickSecretModal) {
quickSecretModal.addEventListener('click', function(event) {
if (event.target === quickSecretModal) closeQuickSecretModal();
});
}
if (quickSecretSubmit) {
quickSecretSubmit.addEventListener('click', async function() {
var button = quickSecretSubmit;
var original = button.textContent;
button.disabled = true;
button.textContent = tKey('wizard.step2.quick_secret_submit');
try {
await submitQuickSecret();
} catch (error) {
if (window.CrankUi) {
window.CrankUi.error(
error.message || tKey('wizard.toast.quick_secret_error_title'),
tKey('wizard.toast.quick_secret_error_title')
);
}
} finally {
button.disabled = false;
button.textContent = original;
}
});
}
var params = new URLSearchParams(window.location.search);
if (params.get('mode') === 'edit' && params.get('operationId')) {
wizardMode = 'edit';
wizardEditId = params.get('operationId');
window.wizardMode = wizardMode;
window.wizardEditId = wizardEditId;
document.title = 'Crank — ' + tKey('wizard.progress.edit');
await loadOperationForEdit();
}
updateWizardProtocolVisibility();
_doGoToStep(1);
}
function renderEditionCapabilityHints(capabilities) {
var securityNote = document.getElementById('wizard-security-level-note');
var securityText = securityNote ? securityNote.querySelector('.info-callout-text') : null;
if (!securityText) return;
var levels = capabilities && Array.isArray(capabilities.supported_security_levels)
? capabilities.supported_security_levels
: ['standard'];
securityNote.hidden = !(levels.length === 1 && levels[0] === 'standard');
if (!securityNote.hidden) {
securityText.textContent = tKey('wizard.step5.community_security_note');
}
}
window.renderEditionCapabilityHints = renderEditionCapabilityHints;
if (window.CrankDiagnostics && typeof window.CrankDiagnostics.bootstrap === 'function') {
window.CrankDiagnostics.bootstrap('wizard', initWizardPage);
} else {
document.addEventListener('DOMContentLoaded', function() {
void initWizardPage();
});
}
/* ── HTTP method picker (Step 4 REST) ── */
var METHOD_CALLOUTS = {
GET: null,
DELETE: null,
POST: { icon: 'info', text: { en: { title: 'POST selected — body encoding required.', body: 'In step 4 you will define the JSON schema for the body payload. Crank will automatically serialize MCP tool arguments into the format you specify.' }, ru: { title: 'Выбран POST — требуется кодирование тела запроса.', body: 'На шаге 4 вы зададите JSON-схему для body payload. Crank автоматически сериализует аргументы MCP-инструмента в выбранный формат.' } } },
PUT: { icon: 'info', text: { en: { title: 'PUT selected — full-resource replacement.', body: 'The request body must contain a complete resource representation. Define the body schema in step 4.' }, ru: { title: 'Выбран PUT — полная замена ресурса.', body: 'Request body должен содержать полное представление ресурса. Задайте body schema на шаге 4.' } } },
PATCH: { icon: 'info', text: { en: { title: 'PATCH selected — partial update.', body: 'Only include the fields you want to modify in the body schema defined in step 4.' }, ru: { title: 'Выбран PATCH — частичное обновление.', body: 'В body schema на шаге 4 включайте только те поля, которые хотите изменить.' } } },
};
function renderSidebarBrand(mode) {
var sidebarBrand = document.querySelector('.step-sidebar-brand');
if (!sidebarBrand) return;
var language = localStorage.getItem('crank_lang') || 'en';
var parts;
if (mode === 'edit') {
parts = language === 'ru'
? { prefix: 'Редактировать ', suffix: 'операцию' }
: { prefix: 'Edit ', suffix: 'operation' };
} else {
parts = language === 'ru'
? { prefix: 'Создать ', suffix: 'операцию' }
: { prefix: 'Create ', suffix: 'operation' };
}
sidebarBrand.dataset.wizardBrand = mode;
sidebarBrand.textContent = parts.prefix;
var accent = document.createElement('span');
accent.textContent = parts.suffix;
sidebarBrand.appendChild(accent);
}
window.addEventListener('crank:langchange', function() {
renderSidebarBrand(wizardMode === 'edit' ? 'edit' : 'create');
});
function selectMethod(btn) {
document.querySelectorAll('.method-card').forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
var method = btn.dataset.method;
var callout = document.getElementById('method-callout-rest');
if (!callout) return;
var info = METHOD_CALLOUTS[method];
if (info) {
var lang = localStorage.getItem('crank_lang') || 'en';
var text = typeof info.text === 'string' ? info.text : (info.text[lang] || info.text.en);
callout.innerHTML = '';
var icon = buildIconSvg(
(window.APP_BASE || '') + 'icons/general/info-circle.svg#icon',
15,
15
);
icon.style.flexShrink = '0';
icon.style.color = 'var(--accent)';
callout.appendChild(icon);
var content = document.createElement('span');
var title = document.createElement('strong');
title.textContent = text.title;
content.appendChild(title);
content.appendChild(document.createTextNode(' ' + text.body));
callout.appendChild(content);
callout.hidden = false;
} else {
callout.replaceChildren();
callout.hidden = true;
}
}
/* ── GraphQL operation type picker (Step 4 GraphQL) ── */
function selectGqlType(btn) {
document.querySelectorAll('.gql-type-card').forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
// Update query editor placeholder if it's still the default
var editor = document.getElementById('gql-query-editor');
if (!editor) return;
var type = btn.dataset.gqlType;
var defaults = {
query: 'query GetContact($id: ID!) {\n contact(id: $id) {\n id\n firstName\n lastName\n email\n company {\n id\n name\n }\n createdAt\n }\n}',
mutation: 'mutation CreateContact(\n $firstName: String!\n $lastName: String!\n $email: String!\n $company: String\n) {\n createContact(input: {\n firstName: $firstName\n lastName: $lastName\n email: $email\n company: $company\n }) {\n id\n firstName\n lastName\n createdAt\n }\n}'
};
if (defaults[type]) editor.value = defaults[type];
}
function escapeHtml(str) {
return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// Close dropdown on outside click
document.addEventListener('click', function() {
if (dropdownOpen) closeUpstreamDropdown();
});
function textValue(id) {
var element = document.getElementById(id);
return element ? element.value.trim() : '';
}
function setValue(id, value) {
var element = document.getElementById(id);
if (element) element.value = value || '';
}
function parseStructuredText(text) {
if (!text.trim()) return {};
try {
return JSON.parse(text);
} catch (_error) {
if (!window.jsyaml) throw new Error(tKey('wizard.error.parser_unavailable'));
return window.jsyaml.load(text) || {};
}
}