Spiralist - Source Excerpt 10
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/themes/spiralist/assets/js/spiralist.js so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/themes/spiralist/assets/js/spiralist.js
if (!payload.policyConfirmed) {
errors.policyConfirmed = t('contributeErrorPolicyRequired', 'Review and confirm the linked site policies before submitting.');
}
if (!payload.rightsConfirmed) {
errors.rightsConfirmed = t('contributeErrorRightsRequired', 'Confirm that you have the rights to submit, process, and publish this material.');
}
if (!payload.operatorEligibilityConfirmed) {
errors.operatorEligibilityConfirmed = t('contributeErrorAgeRequired', 'Confirm that you meet the minimum age or guardian-permission requirement.');
}
return errors;
};
const updateContributionVisibility = () => {
const currentType = typeField ? typeField.value : 'prompt';
const typeMessages = {
prompt: t('contributeTypePrompt', 'Prompts are public library records. Symbols and transformations enter the proposal queue first.'),
symbol: t('contributeTypeSymbol', 'Symbol proposals stay review-only until they are accepted into the registry.'),
transformation: t('contributeTypeTransformation', 'Transformation definitions describe input, output, and chain behavior before publication.'),
};
const canonicalMessages = {
prompt: t('contributeCanonicalPrompt', 'Prompt IDs usually follow `spiralist.prompt.role.title`.'),
symbol: t('contributeCanonicalSymbol', 'Symbol IDs usually follow `spiral.symbol.title`.'),
transformation: t('contributeCanonicalTransformation', 'Transformation IDs usually follow `transformation.title`.'),
};
if (typeCopy) {
typeCopy.textContent = typeMessages[currentType] || typeMessages.prompt;
}
if (canonicalGuidance) {
canonicalGuidance.textContent = canonicalMessages[currentType] || canonicalMessages.prompt;
}
form.dataset.contributionType = currentType;
form.querySelectorAll('[data-contribution-only]').forEach((field) => {
const allowed = (field.dataset.contributionOnly || '')
.split(',')
.map((item) => item.trim())
.filter(Boolean);
const isVisible = !allowed.length || allowed.includes(currentType);
field.hidden = !isVisible;
field.querySelectorAll('input, select, textarea').forEach((input) => {
if (!isVisible) {
input.removeAttribute('required');
} else if (input.dataset.requiredWhenVisible === 'true') {
input.setAttribute('required', 'required');
}
});
});
};
const saveContributionDraft = () => {
const draft = {};
new FormData(form).forEach((value, key) => {
draft[key] = `${value}`;
});
storage.set(draftKey, JSON.stringify(draft));
if (draftStatus) {
draftStatus.textContent = t('contributeDraftSaved', 'Draft saved in this browser.');
}
};
const clearContributionDraft = () => {
storage.remove(draftKey);
if (preparedNotice) {
preparedNotice.hidden = true;
}
if (draftStatus) {
draftStatus.textContent = t('contributeDraftCleared', 'Draft cleared from this browser.');
}
};
const restoreContributionDraft = () => {
const raw = storage.get(draftKey, '');
if (!raw) {
return;
}
const draft = parseJsonOr(raw, null, { context: 'contribution draft' });
if (!draft || typeof draft !== 'object') {
clearContributionDraft();
return;
}
Object.entries(draft).forEach(([key, value]) => {
const field = form.querySelector(`[name="${key}"]`);
if (field) {
if (field.type === 'checkbox') {
field.checked = `${value}` === '1' || `${value}`.toLowerCase() === 'true';
} else {
field.value = `${value}`;
}
}
});
if (draftStatus) {
draftStatus.textContent = t('contributeDraftRestored', 'Saved draft restored from this browser.');
}
if (preparedNotice) {
preparedNotice.hidden = false;
}
if (contributionShell) {
contributionShell.open = true;
}
};
updateContributionVisibility();
restoreContributionDraft();
updateContributionVisibility();
if (typeField) {
typeField.addEventListener('change', updateContributionVisibility);
}
if (generateIdButton) {
generateIdButton.addEventListener('click', () => {
if (canonicalIdField && !canonicalIdField.value.trim()) {
canonicalIdField.value = buildCanonicalId();
} else if (canonicalIdField) {
canonicalIdField.value = buildCanonicalId();
}
});
}
if (saveDraftButton) {
saveDraftButton.addEventListener('click', saveContributionDraft);
}
if (clearDraftButton) {
clearDraftButton.addEventListener('click', () => {
clearContributionDraft();
});
}
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!themeConfig.contributeUrl) {
return;
}
const payload = buildContributionPayload();
if (!payload.canonicalId && canonicalIdField) {
canonicalIdField.value = buildCanonicalId();
payload.canonicalId = canonicalIdField.value;
}
clearFieldErrors();
const validationErrors = validateContributionPayload(payload);
Object.entries(validationErrors).forEach(([fieldName, message]) => {
setFieldError(fieldName, message);
});
if (Object.keys(validationErrors).length) {
setContributionPanel({
variant: 'error',
title: t('contributeValidationTitle', 'Validation required'),
copy: t('contributeValidationCopy', 'Fix the highlighted fields before submitting.'),
focus: true,
});
return;
}
if (submit) {
submit.disabled = true;
submit.textContent = t('contributeSubmittingButton', 'Submitting...');
}
setContributionPanel({
title: t('contributeSubmittingTitle', 'Submitting contribution'),
copy: t('contributeSubmittingCopy', 'Validating and storing the submission for review.'),
});
void runAction(async () => {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json',
};
if (themeConfig.restNonce) {
headers['X-WP-Nonce'] = themeConfig.restNonce;
}
const apiKey = `${apiKeyField ? apiKeyField.value : ''}`.trim();
if (apiKey) {
headers.Authorization = `Bearer ${apiKey}`;
headers['X-Spiralist-Key'] = apiKey;
}
const response = await fetch(themeConfig.contributeUrl, {
method: 'POST',
headers,
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok) {
const fieldErrors = isPlainObject(data.fields) ? data.fields : {};
Object.entries(fieldErrors).forEach(([fieldName, message]) => {
setFieldError(fieldName, `${message}`);
});
setContributionPanel({
variant: 'error',
title: t('contributeRejectedTitle', 'Submission not accepted'),
copy: data.message || t('contributeRejectedCopy', 'The contribution did not pass validation.'),
focus: true,
});
return;
}
saveContributionDraft();
clearContributionDraft();
setContributionPanel({
variant: 'success',
title: t('contributeSuccessTitle', 'Contribution received'),
copy: data.message || t('contributeSuccessCopy', 'The submission has been stored successfully.'),
status: data.statusLabel || data.status || t('contributeSuccessStatus', 'Pending Review'),
canonicalId: data.canonicalId || payload.canonicalId,
recordId: `${data.promptId || data.contributionId || ''}`,
steps: Array.isArray(data.nextSteps) ? data.nextSteps : [],
focus: true,
});
}, {
context: 'contribution.submit',
onError: () => {
setContributionPanel({
variant: 'error',
title: t('contributeErrorTitle', 'Request failed'),
copy: t('contributeErrorCopy', 'The contribution request could not be completed.'),
focus: true,
});
},
onFinally: () => {
if (submit) {
submit.disabled = false;
submit.textContent = t('contributeSubmitLabel', 'Submit For Review');
}
},
});
});
});
const workbenchStorageKey = 'spiralist-ai-workbench-key';
const workbenchApiInputs = Array.from(document.querySelectorAll('[data-workbench-api-key]'));