Skip to content
wiki.fftac.org

Spiralist Prompt Workbench - Source Excerpt 62

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

personaLabHistoryContainers.forEach((container) => {
        const list = container.querySelector('[data-persona-lab-history-list]');
        const empty = container.querySelector('[data-persona-lab-history-empty]');
        const count = container.querySelector('[data-persona-lab-history-count]');
        const clearButton = container.querySelector('[data-persona-lab-history-clear]');
        const statusNode = container.querySelector('[data-persona-lab-history-status]');
        const filters = getPersonaLabHistoryFilterState(container);
        const filteredItems = items.filter((item) => (
          matchPersonaLabHistorySearch(item, filters.search)
          && matchPersonaLabHistoryFilter(item, filters.filter)
        ));
        const filtersActive = isPersonaLabHistoryFilterActive(filters);

        container.classList.toggle('has-history', items.length > 0);
        if (count) {
          count.textContent = items.length === 0
            ? t('portablePromptPersonaLabHistoryCountEmpty', 'No snapshots')
            : (filtersActive
              ? t('portablePromptPersonaLabHistoryCountFiltered', `${filteredItems.length} of ${items.length} snapshots`)
            : (items.length === 1
              ? t('portablePromptPersonaLabHistoryCountOne', '1 snapshot')
              : t('portablePromptPersonaLabHistoryCountMany', `${items.length} snapshots`)));
        }
        if (clearButton) {
          clearButton.hidden = items.length === 0;
        }
        if (empty) {
          empty.textContent = items.length && filteredItems.length === 0
            ? (empty.dataset.historyNoMatchText || t('portablePromptPersonaLabHistoryNoMatch', 'No matching local snapshots.'))
            : (empty.dataset.historyEmptyText || t('portablePromptPersonaLabHistoryEmpty', 'No local snapshots yet.'));
          empty.hidden = filteredItems.length > 0;
        }
        if (list) {
          list.replaceChildren(...filteredItems.map((item) => {
            const card = document.createElement('article');
            card.className = 'spiralist-persona-lab-history-card';
            card.dataset.personaLabHistoryCard = item.id;

            const badge = document.createElement('span');
            badge.className = 'spiralist-persona-lab-history-card__badge';
            badge.textContent = 'Spiralist AI';

            const title = document.createElement('h5');
            title.textContent = item.title;

            const meta = document.createElement('small');
            meta.textContent = getPersonaLabHistoryMeta(item);

            const preview = document.createElement('p');
            preview.textContent = getPromptDraftPreview(item.prompt);

            const actions = document.createElement('div');
            actions.className = 'spiralist-persona-lab-history-card__actions';
            actions.append(
              createPersonaLabHistoryButton(item.id, 'restore', t('portablePromptPersonaLabHistoryRestore', 'Restore')),
              createPersonaLabHistoryButton(item.id, 'copy', t('portablePromptPersonaLabHistoryCopy', 'Copy')),
              createPersonaLabHistoryButton(item.id, 'delete', t('portablePromptPersonaLabHistoryDelete', 'Delete'))
            );

            card.append(badge, title, meta, preview, actions);
            return card;
          }));
        }
        if (statusNode && message) {
          statusNode.textContent = message;
        }
      });
    };
    const makePersonaLabHistoryItem = (prompt, source = 'Spiralist AI Persona Lab', button = null) => {
      const cleanPrompt = toCleanString(prompt, 50000);
      if (!cleanPrompt) {
        return null;
      }

      const settings = normalizePersonaLabHistorySettings(getPersonaLabSettings(button));
      return normalizePersonaLabHistoryItem({
        id: `persona-history-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        title: extractPromptTitle(cleanPrompt, 'Spiralist AI Persona Profile'),
        prompt: cleanPrompt,
        source,
        setupUrl: updateShareUrl(getPromptState(), true),
        settings,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      });
    };
    const savePersonaLabHistory = (prompt, source = 'Spiralist AI Persona Lab', button = null, message = '') => {
      const item = makePersonaLabHistoryItem(prompt, source, button);
      if (!item) {
        return null;
      }

      const next = [
        item,
        ...readPersonaLabHistory().filter((stored) => stored.prompt !== item.prompt || stored.source !== item.source),
      ].slice(0, personaLabHistoryLimit);

      if (!writePersonaLabHistory(next)) {
        renderPersonaLabHistory(t('portablePromptPersonaLabHistorySaveFailed', 'Local history could not be saved in this browser.'));
        return null;
      }

      renderPersonaLabHistory(message || t('portablePromptPersonaLabHistorySaved', 'Spiralist AI local history snapshot saved.'));
      return item;
    };
    const deletePersonaLabHistoryItem = (id) => {
      const next = readPersonaLabHistory().filter((item) => item.id !== id);
      writePersonaLabHistory(next);
      renderPersonaLabHistory(t('portablePromptPersonaLabHistoryDeleted', 'Local history snapshot deleted.'));
    };
    const clearPersonaLabHistory = () => {
      writePersonaLabHistory([]);
      renderPersonaLabHistory(t('portablePromptPersonaLabHistoryCleared', 'Spiralist AI local history cleared.'));
    };
    const normalizePersonaLabTranscriptHistoryItem = (item) => {
      if (!item || typeof item !== 'object') {
        return null;
      }

      const now = new Date().toISOString();
      const title = toCleanString(item.title, 140) || 'Spiralist AI Persona Profile';
      const numericScore = item.numericScore === null || item.numericScore === undefined || item.numericScore === ''
        ? null
        : Number.parseInt(item.numericScore, 10);
      const maxScore = item.maxScore === null || item.maxScore === undefined || item.maxScore === ''
        ? null
        : Number.parseInt(item.maxScore, 10);
      const scoreLabel = Number.isFinite(numericScore) && Number.isFinite(maxScore)
        ? `${numericScore}/${maxScore}`
        : (toCleanString(item.scoreLabel, 80) || 'not scored');
      const weakAreas = Array.isArray(item.weakAreas)
        ? item.weakAreas.map((weakArea) => {
          const weakScore = weakArea && weakArea.score !== null && weakArea.score !== undefined && weakArea.score !== ''
            ? Number.parseInt(weakArea.score, 10)
            : null;
          return {
            area: toCleanString(weakArea && weakArea.area, 160),
            score: Number.isFinite(weakScore) ? weakScore : null,
            status: toCleanString(weakArea && weakArea.status, 80) || 'review',
            repair: toCleanString(weakArea && weakArea.repair, 300),
          };
        }).filter((weakArea) => weakArea.area).slice(0, 7)
        : [];

      return {
        id: toCleanString(item.id, 120) || `transcript-score-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        title,
        promptKey: toCleanString(item.promptKey, 80),
        createdAt: toCleanString(item.createdAt, 80) || now,
        scoreLabel,
        numericScore: Number.isFinite(numericScore) ? numericScore : null,
        maxScore: Number.isFinite(maxScore) ? maxScore : null,
        status: toCleanString(item.status, 240) || 'Review score before reuse.',
        state: ['ready', 'review', 'neutral'].includes(item.state) ? item.state : 'neutral',
        suiteId: toCleanString(item.suiteId, 80) || 'core',
        suiteLabel: toCleanString(item.suiteLabel, 160) || getPersonaLabStressPreset(item.suiteId || 'core').label,
        runtimeFitLabel: toCleanString(item.runtimeFitLabel, 160) || 'Universal manual-send runtime',
        engagementPatternLabel: toCleanString(item.engagementPatternLabel, 160) || 'Curiosity ladder',
        frameworkLabel: toCleanString(item.frameworkLabel, 160) || 'Traits-first operational lens',
        socialDynamicsLabel: toCleanString(item.socialDynamicsLabel, 160) || 'No extra social-dynamics lens',
        transcriptPreview: toCleanString(item.transcriptPreview || item.transcript || '', 600),
        weakAreas,
      };
    };
    const readPersonaLabTranscriptHistory = () => {
      const raw = getStorageValue(personaLabTranscriptHistoryKey, '[]');
      let parsed = [];
      try {
        parsed = JSON.parse(raw);
      } catch (error) {
        parsed = [];
      }