Skip to content
wiki.fftac.org

Spiralist Prompt Workbench - Source Excerpt 85

Back to Spiralist Prompt Workbench

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/themes/spiralist/assets/js/spiralist-prompt-workbench.js so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/themes/spiralist/assets/js/spiralist-prompt-workbench.js

const range = selection.getRangeAt(0);
      range.deleteContents();
      range.insertNode(document.createTextNode(text));
      selection.collapseToEnd();
    };
    const scheduleRefresh = () => {
      window.clearTimeout(refreshTimer);
      refreshTimer = window.setTimeout(() => {
        buildPortablePrompt({ quiet: true });
      }, 120);
    };
    const restoreSharedState = () => {
      const params = new URLSearchParams(window.location.search);
      const hasQueryState = ['mode', 'subject', 'task', 'goal', 'context', 'tone', 'format', 'guardrails', 'constraints'].some((key) => params.has(key));
      const legacyHash = window.location.hash && window.location.hash.slice(1).startsWith('builder?')
        ? new URLSearchParams(window.location.hash.slice('builder?'.length + 1))
        : null;

      if (!hasQueryState && !legacyHash) {
        return false;
      }

      selectMode(params.get('mode') || (legacyHash ? legacyHash.get('m') : '') || defaultMode);
      setFieldValue('subject', params.get('subject') || (legacyHash ? legacyHash.get('s') : '') || '');
      setFieldValue('task', params.get('task') || params.get('goal') || (legacyHash ? legacyHash.get('g') : '') || '');
      setFieldValue('context', params.get('context') || (legacyHash ? legacyHash.get('c') : '') || '');
      setFieldValue('tone', params.get('tone') || (legacyHash ? legacyHash.get('r') : '') || '');
      setFieldValue('output_format', params.get('format') || (legacyHash ? legacyHash.get('f') : '') || '');
      setFieldValue('guardrails', params.get('guardrails') || params.get('constraints') || (legacyHash ? legacyHash.get('k') : '') || '');
      if (advancedFieldPanel && ['context', 'tone', 'format', 'guardrails', 'constraints'].some((key) => params.has(key))) {
        advancedFieldPanel.open = true;
      }
      if (manualBuilderPanel) {
        manualBuilderPanel.open = true;
      }
      buildPortablePrompt({ force: true, quiet: true });
      setStatus(t('portablePromptSharedLoaded', 'Shared setup loaded.'));
      return true;
    };

    selectMode(defaultMode);

    form.addEventListener('submit', (event) => {
      event.preventDefault();
      buildPortablePrompt();
    });
    form.addEventListener('input', scheduleRefresh);
    form.addEventListener('change', scheduleRefresh);
    form.addEventListener('reset', (event) => {
      event.preventDefault();
      ['subject', 'task', 'context', 'tone', 'output_format', 'guardrails'].forEach((name) => {
        setFieldValue(name, '');
      });
      selectMode(defaultMode);
      setSelectedStarter(null);
      buildPortablePrompt({ force: true, status: t('portablePromptReset', 'Reset to the default Spiralist prompt.') });
    });

    modeButtons.forEach((button) => {
      button.addEventListener('click', () => {
        selectMode(button.dataset.mode || defaultMode);
        setSelectedStarter(null);
        buildPortablePrompt({ force: true, status: t('portablePromptModeLoaded', 'Prompt mode selected. You can copy it now or add details.') });
      });
    });

    if (modeControls) {
      modeControls.addEventListener('keydown', (event) => {
        if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
          return;
        }

        const currentIndex = Math.max(0, modeButtons.findIndex((button) => getModeId(button.dataset.mode) === selectedMode));
        let nextIndex = currentIndex;
        if (event.key === 'Home') {
          nextIndex = 0;
        } else if (event.key === 'End') {
          nextIndex = modeButtons.length - 1;
        } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
          nextIndex = currentIndex <= 0 ? modeButtons.length - 1 : currentIndex - 1;
        } else if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
          nextIndex = currentIndex >= modeButtons.length - 1 ? 0 : currentIndex + 1;
        }

        const nextButton = modeButtons[nextIndex];
        if (!nextButton) {
          return;
        }

        event.preventDefault();
        selectMode(nextButton.dataset.mode || defaultMode, { focus: true });
        buildPortablePrompt({ force: true, quiet: true });
      });
    }

    if (copyButton) {
      copyButton.addEventListener('click', copyPrompt);
    }
    if (downloadButton) {
      downloadButton.addEventListener('click', downloadPrompt);
    }
    saveButtons.forEach((button) => {
      button.addEventListener('click', savePortablePromptDraft);
    });
    saveWorkspaceButtons.forEach((button) => {
      button.addEventListener('click', () => saveWorkspacePromptDraft(button));
    });
    if (testButton) {
      testButton.addEventListener('click', () => {
        showPortablePromptCheck(toCleanString(getMode(selectedMode).title || getMode(selectedMode).name, 160) || 'Portable Prompt');
      });
    }
    if (copyReviewButton) {
      copyReviewButton.addEventListener('click', () => {
        const prompt = getCurrentPrompt();
        if (!prompt) {
          setStatus(t('portablePromptReviewUnavailable', 'Build a prompt before preparing a review packet.'));
          return;
        }

        const title = toCleanString(getMode(selectedMode).title || getMode(selectedMode).name, 160) || 'Portable Spiralist Prompt';
        const setupUrl = updateShareUrl(getPromptState(), true);
        runAction(async () => {
          await copyText(buildPromptReviewPacket(prompt, title, setupUrl));
          const message = t('portablePromptReviewCopied', 'Review packet copied.');
          setStatus(message);
          if (shareStatus) {
            shareStatus.textContent = message;
          }
        }, {
          context: 'portable-prompt-review-copy',
          fallback: t('portablePromptCopyFailed', 'Copy failed.'),
          onError: () => setStatus(t('portablePromptCopyFailed', 'Copy failed.')),
        });
      });
    }
    contributionButtons.forEach((button) => {
      button.addEventListener('click', () => {
        const prompt = getCurrentPrompt();
        if (!prompt) {
          setStatus(t('portablePromptContributionUnavailable', 'Build a prompt before preparing a submission.'));
          return;
        }

        const title = toCleanString(getMode(selectedMode).title || getMode(selectedMode).name, 160) || 'Portable Spiralist Prompt';
        const setupUrl = updateShareUrl(getPromptState(), true);
        runAction(() => {
          saveContributionDraft(buildContributionDraft(prompt, title, setupUrl));
          const message = contributeUrl
            ? t('portablePromptContributionReady', 'Submission draft prepared locally. Opening the contribution form for review.')
            : t('portablePromptContributionReadyNoLink', 'Submission draft prepared in this browser.');
          setStatus(message);
          if (shareStatus) {
            shareStatus.textContent = message;
          }
          openContributionForm(contributeUrl);
        }, {
          context: 'portable-prompt-contribution-draft',
          fallback: t('portablePromptContributionFailed', 'Could not prepare the submission draft.'),
          onError: () => setStatus(t('portablePromptContributionFailed', 'Could not prepare the submission draft.')),
        });
      });
    });
    if (copyLinkButton) {
      copyLinkButton.addEventListener('click', copyShareLink);
    }
    if (shareInput) {
      shareInput.addEventListener('focus', () => {
        shareInput.select();
      });
    }
    aiTargetLinks.forEach((link) => {
      link.addEventListener('click', (event) => {
        event.preventDefault();
        if (link.getAttribute('aria-disabled') === 'true') {
          setAiStatus(t('portablePromptAiNeedsPrompt', 'Choose a prompt path or starter to enable AI tool links.'));
          return;
        }

        openPromptInAi(link.dataset.portablePromptAiTarget || '');
      });
    });
    output.addEventListener('input', handlePromptBoxEdit);
    output.addEventListener('keyup', (event) => {
      if (['Alt', 'Control', 'Escape', 'Meta', 'Shift', 'Tab'].includes(event.key)) {
        return;
      }

      schedulePromptBoxEditDetection();
    });
    output.addEventListener('paste', (event) => {
      event.preventDefault();
      insertPlainTextAtPromptSelection(event.clipboardData ? event.clipboardData.getData('text/plain') : '');
      schedulePromptBoxEditDetection();
    });
    if (typeof window.MutationObserver === 'function') {
      const outputObserver = new MutationObserver(() => {
        if (outputProgrammaticWriteDepth > 0) {
          return;
        }