(() => {
'use strict';
const MODEL_WORKFLOW_ENDPOINT =
window.MODEL_WORKFLOW_ENDPOINT ||
'https://ayambabu23--workflow-execute-workflow.modal.run/';
const MODEL_API_ENDPOINT =
window.MODEL_API_ENDPOINT ||
'https://vici-bio--api-execute-workflow.modal.run/';
const MODEL_STATUS_ENDPOINT =
window.MODEL_STATUS_ENDPOINT ||
'https://vici-bio--api-check-status.modal.run/';
const root = document.getElementById('boltzgen-ui');
if (!root) return;
const tabs = Array.from(root.querySelectorAll('.model-tab[data-tab]'));
const apiCodeEl = document.getElementById('api-code-block');
const apiLangTabs = Array.from(root.querySelectorAll('.api-lang-tab[data-lang]'));
const apiActionBtns = Array.from(root.querySelectorAll('.api-action-btn'));
const executeBtnMembers = root.querySelector('.model-actions [data-ms-content="members"]');
const executeBtnGuest = root.querySelector('.model-actions [data-ms-content="!members"]');
const modelSlug = String(root.dataset.model || 'boltzgen').trim() || 'boltzgen';
const modelKey = 'boltzgen';
const API_LANGS = ['python', 'curl', 'javascript'];
let currentTab = inferInitialTab();
let currentApiLang = 'python';
let currentApiSnippet = { text: '', html: '' };
// For BoltzGen: Sync means "build from form" and include placeholders for uploads.
// If you later decide you want to actually inline base64 uploads into the API snippet,
// flip this to true and we will attempt to read files (can be large).
let apiInlineUploads = false;
let isRenderingApiSnippet = false;
let renderSeq = 0;
const API_DEF_CONTENT = window.VICI_API_DEF_CONTENT || {
'token-id': {
title: 'Token-ID',
html: `
Your Vici Token ID. Send it as the Token-ID header.
Generate it in your
Account
.
`
},
'token-secret': {
title: 'Token-Secret',
html: `
Your Vici Token Secret. Send it as the Token-Secret header.
You only see this once when you generate it.
Generate it in your
Account
.
`
},
'workflow-name': {
title: `workflow_name / ${modelKey}.name`,
html: `A friendly run name shown in your Dashboard. The outer workflow_name and inner ${modelKey}.name should match.`
}
};
// --- Hash tab routing ---
function tabFromHash(hash = window.location.hash) {
const h = String(hash || '').trim().toLowerCase();
if (h === '#basic') return 'basic';
if (h === '#advanced') return 'advanced';
if (h === '#api') return 'api';
return null;
}
function syncHashToTab(tab, { replace = true } = {}) {
const nextHash = `#${tab}`;
if (String(window.location.hash || '').toLowerCase() === nextHash) return;
try {
const url = new URL(window.location.href);
url.hash = nextHash;
if (replace && window.history?.replaceState) window.history.replaceState(null, '', url.toString());
else if (!replace && window.history?.pushState) window.history.pushState(null, '', url.toString());
else window.location.hash = nextHash;
} catch {
window.location.hash = nextHash;
}
}
function bindHashRouting() {
window.addEventListener('hashchange', () => {
const next = tabFromHash();
if (!next || next === currentTab) return;
setTab(next, { silent: true, syncHash: false });
});
}
function inferInitialTab() {
const fromHash = tabFromHash();
if (fromHash) return fromHash;
if (root.classList.contains('is-tab-api')) return 'api';
if (root.classList.contains('is-tab-advanced')) return 'advanced';
return 'basic';
}
// --- Helpers ---
function escapeHtml(str) {
return String(str ?? '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
}
function isPlainObject(v) {
return Object.prototype.toString.call(v) === '[object Object]';
}
function deepClone(v) {
return JSON.parse(JSON.stringify(v));
}
function stripExecutionContextForApi(value) {
const blocked = new Set(['member_id', 'msid', 'user_id', 'team_id']);
if (Array.isArray(value)) return value.map(stripExecutionContextForApi);
if (!isPlainObject(value)) return value;
const out = {};
Object.entries(value).forEach(([k, v]) => {
if (blocked.has(k)) return;
out[k] = stripExecutionContextForApi(v);
});
return out;
}
function pulseBtn(btn, cls) {
if (!btn) return;
btn.classList.remove(cls);
void btn.offsetWidth;
btn.classList.add(cls);
const onEnd = () => {
btn.classList.remove(cls);
btn.removeEventListener('animationend', onEnd);
};
btn.addEventListener('animationend', onEnd);
}
function copyTextRobust(text) {
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
return new Promise((resolve, reject) => {
try {
const ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'fixed';
ta.style.left = '-9999px';
ta.style.top = '-9999px';
document.body.appendChild(ta);
ta.select();
ta.setSelectionRange(0, ta.value.length);
const ok = document.execCommand('copy');
ta.remove();
ok ? resolve() : reject(new Error('copy failed'));
} catch (err) {
reject(err);
}
});
}
// --- Tabs ---
function setTab(tab, { silent = false, syncHash = true, replaceHash = false } = {}) {
if (!['basic', 'advanced', 'api'].includes(tab)) return;
currentTab = tab;
root.classList.remove('is-tab-basic', 'is-tab-advanced', 'is-tab-api');
root.classList.add(`is-tab-${tab}`);
tabs.forEach(btn => {
const active = btn.dataset.tab === tab;
btn.classList.toggle('is-active', active);
btn.setAttribute('aria-selected', active ? 'true' : 'false');
btn.setAttribute('tabindex', active ? '0' : '-1');
});
if (syncHash) syncHashToTab(tab, { replace: replaceHash || silent });
if (tab === 'api') renderApiSnippet({ forceDefault: false, toast: false });
}
function initTabs() {
tabs.forEach(btn => {
btn.addEventListener('click', () => setTab(btn.dataset.tab, { replaceHash: false }));
btn.addEventListener('keydown', (e) => {
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) return;
e.preventDefault();
const i = tabs.indexOf(btn);
let next = i;
if (e.key === 'ArrowRight') next = (i + 1) % tabs.length;
if (e.key === 'ArrowLeft') next = (i - 1 + tabs.length) % tabs.length;
if (e.key === 'Home') next = 0;
if (e.key === 'End') next = tabs.length - 1;
tabs[next]?.focus();
setTab(tabs[next]?.dataset.tab || 'basic', { replaceHash: false });
});
});
setTab(currentTab || 'basic', { silent: true, syncHash: true, replaceHash: true });
}
// --- Build BoltzGen payload for API preview ---
function safeBoltzgenName(raw) {
if (typeof window.canonicalizeBoltzgenName === 'function') {
return window.canonicalizeBoltzgenName(raw);
}
// fallback
let s = String(raw || '').trim().replace(/\s+/g, '_');
try { s = s.normalize('NFKD'); } catch {}
s = s.replace(/[^\w-]+/g, '').replace(/_+/g, '_').toLowerCase();
s = s.replace(/^[^a-z0-9]+/, '').replace(/[^a-z0-9]+$/, '');
return s.slice(0, 64);
}
function defaultApiPayload() {
return {
workflow_name: `my_${modelKey}_run`,
[modelKey]: {
name: `my_${modelKey}_run`,
protocol: 'protein-anything',
num_designs: 10,
budget: 2,
stages: {
design: { recycling_steps: 3, sampling_steps: 500, diffusion_samples: 1 },
affinity: { recycling_steps: 3, sampling_steps: 200, diffusion_samples: 5 },
fold: { recycling_steps: 3, sampling_steps: 200, diffusion_samples: 5 },
inverse_fold: { recycling_steps: 3, sampling_steps: 200, diffusion_samples: 1, noise: 0.2 }
},
filtering: {
alpha: 0.001,
filter_biased: true,
additional_filters: []
},
entities: [
{ file: { path: 'target.cif' } },
{ protein: { id: 'B', sequence: '80..120' } }
],
constraints: [],
uploads: { 'target.cif': '' }
}
};
}
function readNumber(id, fallback) {
const el = document.getElementById(id);
if (!el) return fallback;
const val = Number(el.value);
return Number.isFinite(val) ? val : fallback;
}
function readBoolSelect(id, fallback = true) {
const el = document.getElementById(id);
if (!el) return fallback;
const v = String(el.value || '').toLowerCase();
if (v === 'true') return true;
if (v === 'false') return false;
return fallback;
}
function getEntityBlocks() {
return Array.from(document.querySelectorAll('#boltzgen-entity-container .entity-block:not([data-removing])'));
}
// Light gather: build entities + placeholder uploads without reading base64.
// Uses your existing global helpers if present.
function gatherEntitiesLight() {
const blocks = getEntityBlocks();
const entities = [];
const uploads = {};
if (!blocks.length) return { entities: [], uploads: {} };
for (const block of blocks) {
const chain = (block.dataset.chain || '').trim();
const kind = block.dataset.kind || '';
const entityId = chain || 'A';
if (kind === 'structure') {
const fileInput = block.querySelector('input[type="file"]');
const file = fileInput?.files?.[0];
const path = file?.name || 'target.cif';
// placeholder upload
uploads[path] = '';
// Reuse the same optional section collectors if they exist globally.
// If they do not exist, we still submit a minimal file block.
const fileObj = { path };
try {
if (typeof window.boltzgenCollectRows === 'function') {
const state = window.BOLTZGEN_STRUCTURE_STATE?.get?.(block);
const canonical = window.boltzgenCanonicalResidues || ((v) => String(v || '').trim());
const a2l = window.boltzgenAuthRangesToLabels;
const translate = (cid, raw) => {
const txt = canonical(raw || '').trim();
if (!txt) return '';
if (typeof a2l === 'function' && state) return a2l(state, cid, txt);
return txt;
};
const include = window.boltzgenCollectRows(block, 'include', row => {
const cid = row.querySelector('.include-chain')?.value.trim() || chain;
const res = canonical(row.querySelector('.include-range')?.value || '').trim();
if (!cid || !res) return null;
const labelRes = translate(cid, res);
if (!labelRes) return null;
return { chain: { id: cid, res_index: labelRes } };
});
const includeProx = window.boltzgenCollectRows(block, 'include_proximity', row => {
const cid = row.querySelector('.include-chain')?.value.trim() || chain;
const res = canonical(row.querySelector('.include-range')?.value || '').trim();
const radiusRaw = row.querySelector('.include-radius')?.value;
if (!cid || !res) return null;
const labelRes = translate(cid, res);
if (!labelRes) return null;
const out = { chain: { id: cid, res_index: labelRes } };
if (radiusRaw !== undefined && radiusRaw !== null && String(radiusRaw).trim() !== '') out.radius = Number(radiusRaw);
return out;
});
const bindingTypes = window.boltzgenCollectRows(block, 'binding_types', row => {
const cid = row.querySelector('.binding-chain')?.value.trim() || chain;
const mode = row.querySelector('.binding-mode')?.value || 'binding';
const val = canonical(row.querySelector('.binding-value')?.value || '').trim();
if (!cid || !val) return null;
const labelVal = translate(cid, val);
if (!labelVal) return null;
return { chain: { id: cid, [mode]: labelVal } };
});
const groups = window.boltzgenCollectRows(block, 'structure_groups', row => {
const gid = row.querySelector('.group-id')?.value.trim();
const visRaw = row.querySelector('.group-visibility')?.value;
const res = canonical(row.querySelector('.group-range')?.value || '').trim();
if (!gid && !res && !visRaw) return null;
const group = {};
if (gid) group.id = gid;
if (visRaw !== undefined && visRaw !== null && String(visRaw).trim() !== '') group.visibility = Number(visRaw);
const labelRes = translate(chain, res);
if (labelRes) group.res_index = labelRes;
return { group };
});
const design = window.boltzgenCollectRows(block, 'design', row => {
const cid = row.querySelector('.design-chain')?.value.trim() || chain;
const res = canonical(row.querySelector('.design-range')?.value || '').trim();
if (!cid || !res) return null;
const labelRes = translate(cid, res);
if (!labelRes) return null;
return { chain: { id: cid, res_index: labelRes } };
});
const secondary = window.boltzgenCollectRows(block, 'secondary_structure', row => {
const cid = row.querySelector('.sec-chain')?.value.trim() || chain;
const loop = canonical(row.querySelector('.sec-loop')?.value || '').trim();
const helix = canonical(row.querySelector('.sec-helix')?.value || '').trim();
const sheet = canonical(row.querySelector('.sec-sheet')?.value || '').trim();
if (!cid || (!loop && !helix && !sheet)) return null;
const chainObj = { id: cid };
const loopL = translate(cid, loop);
const helixL = translate(cid, helix);
const sheetL = translate(cid, sheet);
if (loopL) chainObj.loop = loopL;
if (helixL) chainObj.helix = helixL;
if (sheetL) chainObj.sheet = sheetL;
return { chain: chainObj };
});
const insertions = window.boltzgenCollectRows(block, 'design_insertions', row => {
const cid = row.querySelector('.ins-id')?.value.trim() || chain;
const resIndex = canonical(row.querySelector('.ins-res')?.value || '').trim();
const numRes = (row.querySelector('.ins-num')?.value || '').trim();
const ss = row.querySelector('.ins-ss')?.value || '';
if (!cid || !resIndex || !numRes) return null;
const labelResIndex = translate(cid, resIndex);
if (!labelResIndex) return null;
const insertion = { id: cid, res_index: labelResIndex, num_residues: numRes };
if (ss) insertion.secondary_structure = ss;
return { insertion };
});
if (include?.length) fileObj.include = include;
if (includeProx?.length) fileObj.include_proximity = includeProx;
if (bindingTypes?.length) fileObj.binding_types = bindingTypes;
if (groups?.length) fileObj.structure_groups = groups;
if (design?.length) fileObj.design = design;
if (secondary?.length) fileObj.secondary_structure = secondary;
if (insertions?.length) fileObj.design_insertions = insertions;
}
} catch (err) {
console.warn('[boltzgen api] entity light gather failed', err);
}
entities.push({ file: fileObj });
continue;
}
if (kind === 'protein') {
const mode = block.querySelector('.protein-mode')?.value || 'sequence';
let sequenceVal = '';
if (mode === 'sequence') {
const seq = (block.querySelector('.protein-sequence')?.value || '').trim();
sequenceVal = seq || 'ACDEFGHIKLMNPQRSTVWY';
} else {
const min = parseInt(block.querySelector('.protein-min')?.value || '80', 10);
const max = parseInt(block.querySelector('.protein-max')?.value || '120', 10);
sequenceVal = `${min}..${max}`;
}
const proteinObj = { id: entityId, sequence: sequenceVal };
try {
if (typeof window.boltzgenReadBinding === 'function') {
const bindingTypes = window.boltzgenReadBinding(block.querySelector('.protein-binding-section'));
if (bindingTypes) proteinObj.binding_types = bindingTypes;
}
} catch {}
const sec = (block.querySelector('.protein-secondary')?.value || '').trim();
if (sec) proteinObj.secondary_structure = sec;
const cyclic = !!block.querySelector('.protein-cyclic')?.checked;
if (cyclic) proteinObj.cyclic = true;
entities.push({ protein: proteinObj });
continue;
}
if (kind === 'ligand') {
const raw = (block.querySelector('.ligand-code')?.value || '').trim();
const ligandObj = { id: entityId };
if (raw) {
const normalized = raw.replace(/\s+/g, '');
const isCcd = /^[A-Za-z0-9]{2,6}$/.test(normalized) && !/[a-z]/.test(normalized);
if (isCcd) ligandObj.ccd = normalized.toUpperCase();
else ligandObj.smiles = raw;
} else {
ligandObj.ccd = 'ATP';
}
try {
if (typeof window.boltzgenReadBinding === 'function') {
const bindingTypes = window.boltzgenReadBinding(block.querySelector('.ligand-binding-section'));
if (bindingTypes) ligandObj.binding_types = bindingTypes;
}
} catch {}
entities.push({ ligand: ligandObj });
continue;
}
}
return { entities, uploads };
}
function gatherConstraintsSafe() {
try {
if (typeof window.boltzgenGatherConstraints === 'function') {
return window.boltzgenGatherConstraints();
}
} catch (err) {
console.warn('[boltzgen api] constraints gather failed', err);
}
return [];
}
async function buildBoltzgenPayloadFromForm({ forApi = false } = {}) {
const nameEl = document.getElementById('boltzgen-name');
const rawName = String(nameEl?.value || '').trim();
const jobName = safeBoltzgenName(rawName || `my_${modelKey}_run`);
const protocol = String(document.getElementById('boltzgen-protocol')?.value || 'protein-anything').trim();
const numDesigns = parseInt(String(document.getElementById('boltzgen-num-designs')?.value || '0'), 10) || 0;
const budget = parseInt(String(document.getElementById('boltzgen-budget')?.value || '0'), 10) || 0;
const stages = {
design: {
recycling_steps: readNumber('design-recycles', 3),
sampling_steps: readNumber('design-sampling', 500),
diffusion_samples: readNumber('design-diffusion', 1)
},
affinity: {
recycling_steps: readNumber('affinity-recycles', 3),
sampling_steps: readNumber('affinity-sampling', 200),
diffusion_samples: readNumber('affinity-diffusion', 5)
},
fold: {
recycling_steps: readNumber('fold-recycles', 3),
sampling_steps: readNumber('fold-sampling', 200),
diffusion_samples: readNumber('fold-diffusion', 5)
},
inverse_fold: {
recycling_steps: readNumber('inverse-recycles', 3),
sampling_steps: readNumber('inverse-sampling', 200),
diffusion_samples: readNumber('inverse-diffusion', 1),
noise: readNumber('inverse-noise', 0.2)
}
};
const alphaRaw = parseFloat(String(document.getElementById('filter-alpha')?.value || '0.001'));
const filtering = {
alpha: Number.isFinite(alphaRaw) ? alphaRaw : 0.001,
filter_biased: readBoolSelect('filter-biased', true),
additional_filters: Array.from(document.querySelectorAll('#boltzgen-filter-list .filter-row'))
.map(row => {
const metric = (row.querySelector('.filter-metric')?.value || '').trim();
const comp = row.querySelector('.filter-comparator')?.value || '>';
const valRaw = row.querySelector('.filter-value')?.value;
const val = valRaw !== undefined && valRaw !== null ? String(valRaw).trim() : '';
if (!metric || !val) return null;
return `${metric}${comp}${val}`;
})
.filter(Boolean)
};
const rmsdRaw = parseFloat(String(document.getElementById('filter-rmsd')?.value || ''));
if (Number.isFinite(rmsdRaw)) filtering.refolding_rmsd_threshold = rmsdRaw;
// Entities + uploads
const { entities, uploads } = gatherEntitiesLight();
// Constraints
const constraints = gatherConstraintsSafe();
const job = {
name: jobName,
protocol,
num_designs: numDesigns > 0 ? numDesigns : 10,
budget: budget > 0 ? budget : 2,
stages,
filtering,
entities: entities.length ? entities : deepClone(defaultApiPayload()[modelKey].entities),
constraints,
uploads: uploads && Object.keys(uploads).length ? uploads : { 'target.cif': '' }
};
const payload = {
workflow_name: job.name,
[modelKey]: job
};
return stripExecutionContextForApi(payload);
}
// --- API snippet marker rendering (kept from template, shortened) ---
function toDefRefSafe(path) {
return String(path).replace(/[^a-zA-Z0-9._:-]+/g, '_').slice(0, 180);
}
function valueTypeLabel(v) {
if (Array.isArray(v)) return 'array';
if (v === null) return 'null';
return typeof v;
}
function buildGenericPayloadDef(path, value) {
const pathLabel = String(path || 'payload');
const type = valueTypeLabel(value);
let typeHint = `Expected type: ${escapeHtml(type)}.`;
if (type === 'string') typeHint = 'Expected type: string. Replace with the value for your run.';
if (type === 'number') typeHint = 'Expected type: number. Use an integer or decimal.';
if (type === 'boolean') typeHint = 'Expected type: boolean (true or false).';
let extra = 'Replace this example value with a valid value for your model.';
const pathLower = pathLabel.toLowerCase();
if (pathLower.endsWith('.name')) extra = 'Run/job name for this model block. Keep aligned with workflow_name.';
if (pathLower.includes('uploads')) extra = 'For UI submissions this is uploaded automatically. For API usage, replace with base64 file data.';
if (pathLower.includes('entities')) extra = 'Entity specification. Structure files use file.path plus optional directives.';
if (pathLower.includes('constraints')) extra = 'Optional constraints such as bonds or length constraints.';
return {
title: pathLabel,
html: `
${escapeHtml(pathLabel)}
${typeHint}
${escapeHtml(extra)}
`
};
}
let apiDynamicDefContent = {};
function stringifyPayloadWithMarkers(payloadObj) {
const markers = [];
const dynamicDefs = {};
const mark = (value, kind = 'string', defRef = '') => {
const token = `__MARK_${markers.length}__`;
markers.push({ token, value, kind, defRef });
return token;
};
const payload = deepClone(payloadObj);
function walk(node, pathParts = []) {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
const v = node[i];
const childPath = [...pathParts, `[${i}]`];
if (v && typeof v === 'object') {
walk(v, childPath);
continue;
}
const pathStr = childPath.join('.');
const defRef = toDefRefSafe(`payload:${pathStr}`);
dynamicDefs[defRef] = buildGenericPayloadDef(pathStr, v);
let kind = 'string';
if (typeof v === 'number') kind = 'number';
else if (typeof v === 'boolean') kind = 'boolean';
else if (v === null) kind = 'null';
node[i] = mark(v, kind, defRef);
}
return;
}
if (!node || typeof node !== 'object') return;
Object.keys(node).forEach((key) => {
const v = node[key];
const childPath = [...pathParts, key];
if (v && typeof v === 'object') {
walk(v, childPath);
return;
}
const pathStr = childPath.join('.');
const isWorkflowName = pathStr === 'workflow_name';
const isInnerModelName = pathStr === `${modelKey}.name`;
let defRef = 'workflow-name';
if (!isWorkflowName && !isInnerModelName) {
defRef = toDefRefSafe(`payload:${pathStr}`);
dynamicDefs[defRef] = buildGenericPayloadDef(pathStr, v);
}
let kind = 'string';
if (typeof v === 'number') kind = 'number';
else if (typeof v === 'boolean') kind = 'boolean';
else if (v === null) kind = 'null';
node[key] = mark(v, kind, defRef);
});
}
walk(payload, []);
const jsonText = JSON.stringify(payload, null, 2);
let text = jsonText;
let html = escapeHtml(jsonText);
markers.forEach((m) => {
const quotedToken = `"${m.token}"`;
const quotedTokenHtml = `"${m.token}"`;
const jsonEscaped = JSON.stringify(String(m.value));
let textVal = jsonEscaped;
let htmlVal = `${escapeHtml(jsonEscaped)}`;
if (m.kind === 'number') {
textVal = String(m.value);
htmlVal = `${escapeHtml(String(m.value))}`;
} else if (m.kind === 'boolean') {
textVal = m.value ? 'true' : 'false';
htmlVal = `${m.value ? 'true' : 'false'}`;
} else if (m.kind === 'null') {
textVal = 'null';
htmlVal = `null`;
}
text = text.split(quotedToken).join(textVal);
html = html.split(quotedTokenHtml).join(htmlVal);
});
return { text, html, defs: dynamicDefs };
}
function getApiTemplate(lang, payloadText, payloadHtml) {
const HEREDOC_TAG = '__VICI_PAYLOAD_JSON__';
if (lang === 'python') {
return {
text: [
'# POST a BoltzGen job (Python)',
'# Set TOKEN_ID and TOKEN_SECRET to your values.',
'import json',
'import requests',
'',
`API_URL = "${MODEL_API_ENDPOINT}"`,
'TOKEN_ID = ""',
'TOKEN_SECRET = ""',
'',
'payload = json.loads(r"""',
payloadText,
'""")',
'',
'resp = requests.post(',
' API_URL,',
' headers={',
' "Content-Type": "application/json",',
' "Token-ID": TOKEN_ID,',
' "Token-Secret": TOKEN_SECRET,',
' },',
' json=payload',
')',
'',
'resp.raise_for_status()',
'print(resp.json())'
].join('\n'),
html: [
'',
'',
'import json',
'import requests',
'',
`API_URL = "${escapeHtml(MODEL_API_ENDPOINT)}"`,
`TOKEN_ID = "<TOKEN_ID>"`,
`TOKEN_SECRET = "<TOKEN_SECRET>"`,
'',
'payload = json.loads(r"""',
payloadHtml,
'""")',
'',
'resp = requests.post(',
' API_URL,',
' headers={',
' "Content-Type": "application/json",',
' "Token-ID": TOKEN_ID,',
' "Token-Secret": TOKEN_SECRET,',
' },',
' json=payload',
')',
'',
'resp.raise_for_status()',
'print(resp.json())'
].join('\n')
};
}
if (lang === 'curl') {
return {
text: [
'# POST a BoltzGen job (cURL)',
'# Set TOKEN_ID and TOKEN_SECRET to your values.',
'',
`curl -X POST "${MODEL_API_ENDPOINT}" \\`,
' -H "Content-Type: application/json" \\',
' -H "Token-ID: " \\',
' -H "Token-Secret: " \\',
` --data-binary @- <<'${HEREDOC_TAG}'`,
payloadText,
HEREDOC_TAG
].join('\n'),
html: [
'',
'',
'',
`curl -X POST "${escapeHtml(MODEL_API_ENDPOINT)}" \\`,
' -H "Content-Type: application/json" \\',
' -H "Token-ID: <TOKEN_ID>" \\',
' -H "Token-Secret: <TOKEN_SECRET>" \\',
` --data-binary @- <<'${escapeHtml(HEREDOC_TAG)}'`,
payloadHtml,
`${escapeHtml(HEREDOC_TAG)}`
].join('\n')
};
}
return {
text: [
'// POST a BoltzGen job (JavaScript)',
'// Set TOKEN_ID and TOKEN_SECRET to your values.',
'',
'(async () => {',
` const API_URL = "${MODEL_API_ENDPOINT}";`,
' const TOKEN_ID = "";',
' const TOKEN_SECRET = "";',
'',
` const payload = ${payloadText};`,
'',
' const resp = await fetch(API_URL, {',
' method: "POST",',
' headers: {',
' "Content-Type": "application/json",',
' "Token-ID": TOKEN_ID,',
' "Token-Secret": TOKEN_SECRET,',
' },',
' body: JSON.stringify(payload),',
' });',
'',
' if (!resp.ok) throw new Error(`Request failed: ${resp.status}`);',
'',
' console.log(await resp.json());',
'})().catch((err) => {',
' console.error(err);',
'});'
].join('\n'),
html: [
'',
'',
'',
'(async () => {',
` const API_URL = "${escapeHtml(MODEL_API_ENDPOINT)}";`,
` const TOKEN_ID = "<TOKEN_ID>";`,
` const TOKEN_SECRET = "<TOKEN_SECRET>";`,
'',
` const payload = ${payloadHtml};`,
'',
' const resp = await fetch(API_URL, {',
' method: "POST",',
' headers: {',
' "Content-Type": "application/json",',
' "Token-ID": TOKEN_ID,',
' "Token-Secret": TOKEN_SECRET,',
' },',
' body: JSON.stringify(payload),',
' });',
'',
' if (!resp.ok) throw new Error(`Request failed: ${resp.status}`);',
'',
' console.log(await resp.json());',
'})().catch((err) => {',
' console.error(err);',
'});'
].join('\n')
};
}
// --- API definitions popout ---
let apiDefPopoutEl = null;
let apiDefAnchorEl = null;
let apiDefHideTimer = null;
function ensureApiDefPopout() {
if (apiDefPopoutEl) return apiDefPopoutEl;
const el = document.createElement('div');
el.className = 'api-def-popout';
el.setAttribute('role', 'dialog');
el.setAttribute('aria-hidden', 'true');
el.innerHTML = `
`;
el.addEventListener('mouseenter', () => {
if (apiDefHideTimer) {
clearTimeout(apiDefHideTimer);
apiDefHideTimer = null;
}
});
el.addEventListener('mouseleave', () => scheduleHideApiDefPopout());
document.body.appendChild(el);
apiDefPopoutEl = el;
return el;
}
function getApiDefinition(defRef) {
return apiDynamicDefContent?.[defRef] || API_DEF_CONTENT?.[defRef] || null;
}
function positionApiDefPopout(anchorEl) {
const pop = ensureApiDefPopout();
if (!anchorEl) return;
const a = anchorEl.getBoundingClientRect();
const p = pop.getBoundingClientRect();
const gap = 10;
const margin = 12;
let left = a.left;
let top = a.bottom + gap;
if (left + p.width > window.innerWidth - margin) left = window.innerWidth - p.width - margin;
if (left < margin) left = margin;
if (top + p.height > window.innerHeight - margin) top = a.top - p.height - gap;
if (top < margin) top = margin;
pop.style.left = `${Math.round(left)}px`;
pop.style.top = `${Math.round(top)}px`;
}
function showApiDefPopoutFor(targetEl) {
if (!targetEl) return;
const defRef = targetEl.getAttribute('data-def-ref');
if (!defRef) return;
const def = getApiDefinition(defRef);
if (!def) return;
if (apiDefHideTimer) {
clearTimeout(apiDefHideTimer);
apiDefHideTimer = null;
}
const pop = ensureApiDefPopout();
pop.querySelector('.api-def-popout__title').textContent = def.title || defRef;
pop.querySelector('.api-def-popout__body').innerHTML = def.html || '';
apiDefAnchorEl = targetEl;
pop.classList.add('is-visible');
pop.setAttribute('aria-hidden', 'false');
positionApiDefPopout(targetEl);
}
function hideApiDefPopout() {
const pop = ensureApiDefPopout();
pop.classList.remove('is-visible');
pop.setAttribute('aria-hidden', 'true');
apiDefAnchorEl = null;
}
function scheduleHideApiDefPopout(delay = 120) {
if (apiDefHideTimer) clearTimeout(apiDefHideTimer);
apiDefHideTimer = setTimeout(() => {
apiDefHideTimer = null;
hideApiDefPopout();
}, delay);
}
function bindApiDefinitionPopout() {
if (!apiCodeEl) return;
ensureApiDefPopout();
apiCodeEl.addEventListener('mouseover', (e) => {
const target = e.target.closest('.tok-editable[data-def-ref]');
if (!target || !apiCodeEl.contains(target)) return;
showApiDefPopoutFor(target);
});
apiCodeEl.addEventListener('mouseout', (e) => {
const from = e.target.closest('.tok-editable[data-def-ref]');
if (!from || !apiCodeEl.contains(from)) return;
const to = e.relatedTarget;
if (to && (from.contains(to) || ensureApiDefPopout().contains(to))) return;
scheduleHideApiDefPopout();
});
apiCodeEl.addEventListener('mousemove', (e) => {
const target = e.target.closest('.tok-editable[data-def-ref]');
if (!target || !apiCodeEl.contains(target)) return;
if (apiDefAnchorEl === target) positionApiDefPopout(target);
});
window.addEventListener('scroll', () => {
if (apiDefAnchorEl && apiDefPopoutEl?.classList.contains('is-visible')) positionApiDefPopout(apiDefAnchorEl);
}, true);
window.addEventListener('resize', () => {
if (apiDefAnchorEl && apiDefPopoutEl?.classList.contains('is-visible')) positionApiDefPopout(apiDefAnchorEl);
});
}
// --- API render ---
async function renderApiSnippet({ forceDefault = false, toast = false } = {}) {
if (!apiCodeEl) return;
if (isRenderingApiSnippet) return;
const seq = ++renderSeq;
isRenderingApiSnippet = true;
try {
apiCodeEl.textContent = '# Building API snippet...';
let payloadSource = null;
apiDynamicDefContent = {};
if (!forceDefault) {
try {
payloadSource = await buildBoltzgenPayloadFromForm({ forApi: true });
} catch (err) {
payloadSource = null;
}
}
if (!payloadSource) {
payloadSource = deepClone(defaultApiPayload());
}
payloadSource = stripExecutionContextForApi(payloadSource);
const payloadBlock = stringifyPayloadWithMarkers(payloadSource);
apiDynamicDefContent = payloadBlock.defs || {};
const snippet = getApiTemplate(currentApiLang, payloadBlock.text, payloadBlock.html);
currentApiSnippet = snippet;
if (seq !== renderSeq) return;
apiCodeEl.innerHTML = snippet.html;
if (toast) {
showToast?.('success', forceDefault ? 'Reset API snippet to defaults.' : 'Synced API snippet from form.');
}
} finally {
isRenderingApiSnippet = false;
}
}
function setApiLang(lang) {
if (!API_LANGS.includes(lang)) return;
currentApiLang = lang;
apiLangTabs.forEach(btn => {
const active = btn.dataset.lang === lang;
btn.classList.toggle('is-active', active);
btn.setAttribute('aria-selected', active ? 'true' : 'false');
});
renderApiSnippet({ forceDefault: false, toast: false });
}
function findApiActionButtons() {
let syncBtn = null, copyBtn = null, resetApiBtn = null;
apiActionBtns.forEach((btn, i) => {
const label = `${btn.getAttribute('aria-label') || ''} ${btn.title || ''} ${btn.textContent || ''}`.toLowerCase();
if (!syncBtn && label.includes('sync')) syncBtn = btn;
else if (!copyBtn && label.includes('copy')) copyBtn = btn;
else if (!resetApiBtn && label.includes('reset')) resetApiBtn = btn;
if (i === 0 && !syncBtn) syncBtn = btn;
if (i === 1 && !copyBtn) copyBtn = btn;
if (i === 2 && !resetApiBtn) resetApiBtn = btn;
});
return { syncBtn, copyBtn, resetApiBtn };
}
function bindApiControls() {
apiLangTabs.forEach(btn => {
btn.addEventListener('click', () => setApiLang(btn.dataset.lang || 'python'));
btn.addEventListener('keydown', (e) => {
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) return;
e.preventDefault();
const i = apiLangTabs.indexOf(btn);
let next = i;
if (e.key === 'ArrowRight') next = (i + 1) % apiLangTabs.length;
if (e.key === 'ArrowLeft') next = (i - 1 + apiLangTabs.length) % apiLangTabs.length;
if (e.key === 'Home') next = 0;
if (e.key === 'End') next = apiLangTabs.length - 1;
apiLangTabs[next]?.focus();
setApiLang(apiLangTabs[next]?.dataset.lang || 'python');
});
});
const { syncBtn, copyBtn, resetApiBtn } = findApiActionButtons();
syncBtn?.addEventListener('click', async () => {
pulseBtn(syncBtn, 'pulse-blue');
// Keep uploads as placeholders by default
apiInlineUploads = false;
await renderApiSnippet({ forceDefault: false, toast: true });
});
copyBtn?.addEventListener('click', () => {
const text = currentApiSnippet?.text?.trim();
if (!text) {
showToast?.('error', 'Nothing to copy yet.');
return;
}
pulseBtn(copyBtn, 'pulse-green');
copyTextRobust(text)
.then(() => showToast?.('success', 'Copied API snippet.'))
.catch(() => showToast?.('error', 'Copy failed. Select code and copy manually.'));
});
resetApiBtn?.addEventListener('click', async () => {
pulseBtn(resetApiBtn, 'pulse-red');
apiInlineUploads = false;
await renderApiSnippet({ forceDefault: true, toast: true });
});
}
// --- Execute ---
function bindExecute() {
if (executeBtnMembers && executeBtnMembers.tagName.toLowerCase() === 'button') {
executeBtnMembers.type = 'button';
executeBtnMembers.addEventListener('click', (e) => {
e.preventDefault();
if (typeof window.submitBoltzgenJob === 'function') {
window.submitBoltzgenJob();
} else {
showToast?.('error', 'submitBoltzgenJob is not available on this page.');
}
});
}
if (executeBtnGuest && executeBtnGuest.tagName.toLowerCase() === 'a') {
// guest link stays as-is
}
}
// Keep API preview updated when user edits while on API tab (cheap, light gather)
function bindApiAutoRefresh() {
root.addEventListener('input', () => {
if (currentTab === 'api') renderApiSnippet({ forceDefault: false, toast: false });
});
root.addEventListener('change', () => {
if (currentTab === 'api') renderApiSnippet({ forceDefault: false, toast: false });
});
}
function init() {
bindHashRouting();
initTabs();
bindApiControls();
bindApiDefinitionPopout();
bindExecute();
bindApiAutoRefresh();
setApiLang('python');
renderApiSnippet({ forceDefault: false, toast: false });
window.ModelPage = {
root,
modelSlug,
modelKey,
setTab,
getCurrentTab: () => currentTab,
renderApiSnippet,
setApiLang,
getApiSnippet: () => ({ ...currentApiSnippet }),
endpoints: {
workflow: MODEL_WORKFLOW_ENDPOINT,
api: MODEL_API_ENDPOINT,
status: MODEL_STATUS_ENDPOINT
}
};
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, { once: true });
} else {
init();
}
})();