Spiralist - Source Excerpt 04
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
storage.set(runtimeStorageKey, JSON.stringify(runtimePayload));
window.dispatchEvent(new CustomEvent('spiralist:runtime-path-updated', {
detail: {
...runtimePayload,
source: 'field',
},
}));
};
const getNodeLabelById = (nodeId = '') => {
const node = nodesById.get(nodeId);
return node ? (node.label || node.id || nodeId) : nodeId;
};
const syncRuntimeState = (payloadState = {}) => {
if (!payloadState || typeof payloadState !== 'object') {
return;
}
if (payloadState.current_state || payloadState.currentState) {
runtimeState.currentState = payloadState.current_state || payloadState.currentState;
}
if (payloadState.current_node || payloadState.currentNode) {
runtimeState.currentNode = payloadState.current_node || payloadState.currentNode;
}
if (Array.isArray(payloadState.history)) {
runtimeState.history = payloadState.history;
}
if (Array.isArray(payloadState.active_symbols) || Array.isArray(payloadState.activeSymbols)) {
runtimeState.activeSymbols = Array.isArray(payloadState.active_symbols)
? payloadState.active_symbols
: payloadState.activeSymbols;
}
if (Array.isArray(payloadState.next_nodes) || Array.isArray(payloadState.nextNodes)) {
const nextNodes = Array.isArray(payloadState.next_nodes)
? payloadState.next_nodes
: payloadState.nextNodes;
runtimeState.nextNodeIds = nextNodes
.map((entry) => (entry && typeof entry === 'object' ? (entry.node_id || entry.nodeId || entry.id || '') : `${entry || ''}`))
.filter(Boolean);
}
};
const buildStatusMessage = () => {
const visibleCount = getVisibleNodes().length;
let message = `${formatText('manuscriptFieldModeStatus', '{mode} mode: {visibleCount} of {totalCount} nodes visible.', {
mode: formatNodeType(currentMode),
visibleCount,
totalCount: nodes.length,
})} ${
currentMode === 'expert'
? t('manuscriptFieldRelationsVisible', 'Relations remain visible.')
: t('manuscriptFieldRelationsHover', 'Relations appear on hover or selection.')
}`;
if (runtimeState.currentState) {
message += ` ${formatText('manuscriptFieldCurrentState', 'Current state: {state}.', { state: runtimeState.currentState })}`;
}
if (runtimeState.nextNodeIds.length) {
message += ` ${formatText('manuscriptFieldNextNodes', 'Next nodes: {nodes}.', {
nodes: runtimeState.nextNodeIds.map((nodeId) => getNodeLabelById(nodeId)).join(', '),
})}`;
}
if (runtimeState.history.length) {
const trail = runtimeState.history
.slice(-3)
.map((entry) => getNodeLabelById(entry.node_id || entry.current_node || ''))
.filter(Boolean);
if (trail.length) {
message += ` ${formatText('manuscriptFieldHistory', 'History: {history}.', { history: trail.join(' -> ') })}`;
}
}
return message;
};
const updateFieldStatus = (state = 'ready') => {
setStatusState(statusTarget, buildStatusMessage(), state);
};
const getConnectedIds = (nodeId) => {
const ids = new Set();
getVisibleRelations().forEach((relation) => {
if (relation.from === nodeId) {
ids.add(relation.to);
}
if (relation.to === nodeId) {
ids.add(relation.from);
}
});
const node = nodesById.get(nodeId);
if (node && Array.isArray(node.related)) {
node.related.forEach((relatedId) => {
const relatedNode = resolveNodeByIdentifier(relatedId);
ids.add(relatedNode ? relatedNode.id : relatedId);
});
}
return ids;
};
const renderRelations = (focusId = selectedNodeId) => {
relationsTarget.replaceChildren();
const visibleRelations = getVisibleRelations();
const relationsToRender = currentMode === 'expert'
? visibleRelations
: visibleRelations.filter((relation) => focusId && (relation.from === focusId || relation.to === focusId));
relationsToRender.forEach((relation) => {
const fromNode = nodesById.get(relation.from);
const toNode = nodesById.get(relation.to);
if (!fromNode || !toNode) {
return;
}
const line = createRelationLine(relation, fromNode, toNode);
relationsTarget.appendChild(line);
window.requestAnimationFrame(() => line.classList.add('is-visible'));
});
};
const markNodeState = (focusId = selectedNodeId) => {
const connectedIds = focusId ? getConnectedIds(focusId) : new Set();
runtimeState.nextNodeIds.forEach((nodeId) => connectedIds.add(nodeId));
nodeButtons.forEach((button, nodeId) => {
const isActive = nodeId === selectedNodeId;
button.classList.toggle('is-active', isActive);
button.classList.toggle('is-related', connectedIds.has(nodeId) && !isActive);
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
});
listTarget.querySelectorAll('[data-field-list-node]').forEach((button) => {
const nodeId = button.dataset.fieldListNode || '';
const isActive = nodeId === selectedNodeId;
button.classList.toggle('is-active', isActive);
button.classList.toggle('is-related', connectedIds.has(nodeId) && !isActive);
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
});
};
const setRelatedNodes = (node) => {
if (!related) {
return;
}
related.replaceChildren();
const relatedIds = Array.isArray(node.related) ? node.related : [];
if (!relatedIds.length) {
related.textContent = t('symbolsRelatedEmpty', 'No related symbols declared.');
return;
}
relatedIds.forEach((relatedId) => {
const relatedNode = resolveNodeByIdentifier(relatedId);
const button = document.createElement('button');
button.type = 'button';
button.className = 'spiralist-related-node';
button.textContent = relatedNode ? relatedNode.label : relatedId;
button.addEventListener('click', () => {
if (!relatedNode) {
return;
}
if (!isVisibleForMode(relatedNode, currentMode)) {
setMode(getNodeMode(relatedNode), false);
}
selectNode(relatedNode.id, true);
executeSelectedNode(relatedNode.id);
});
related.appendChild(button);
});
};
const setCrosswalkLinks = (node) => {
if (!crosswalk) {
return;
}
crosswalk.replaceChildren();
const links = [];
const canonicalId = node.canonicalId || '';
const promptQuery = getPromptSearchQuery(node);
if (node.type === 'symbol' && themeConfig.symbolsPageUrl && canonicalId) {
links.push({
label: t('symbolsCrosswalkSymbols', 'Open in symbols explorer'),
url: buildChildRouteUrl(themeConfig.symbolsPageUrl, [canonicalId]),
});
}
if (themeConfig.promptsPageUrl && promptQuery) {
links.push({
label: t('symbolsCrosswalkPrompts', 'Search prompts'),
url: buildPromptSearchUrl(promptQuery),
});
}
if (node.type === 'transformation' && themeConfig.symbolsPageUrl) {
links.push({
label: t('symbolsCrosswalkTransformations', 'View transformations'),
url: `${themeConfig.symbolsPageUrl}#transformations`,
});
}
if ((node.type === 'axiom' || node.type === 'structure') && themeConfig.systemUrl) {
links.push({
label: t('symbolsCrosswalkSystem', 'System concepts'),
url: themeConfig.systemUrl,
});
}
if (!links.length) {
const empty = document.createElement('span');
empty.className = 'spiralist-muted';
empty.textContent = t('symbolsCrosswalkEmpty', 'No crosswalk links available.');
crosswalk.appendChild(empty);
return;
}
links.forEach((link) => {
const anchor = document.createElement('a');
anchor.className = 'spiralist-chip spiralist-chip--link';
anchor.href = link.url;
anchor.textContent = link.label;
crosswalk.appendChild(anchor);
});
};
const setInspectorNode = (node) => {
if (type) type.textContent = `${formatNodeType(node.type)} Node`;
if (glyph) glyph.textContent = node.type === 'symbol' ? (node.glyph || '\u25CB') : '';