Files
DemonGatanjieu-Anomalous_Mo…/web/modules/ui_prompt_composer.js
T
DemonGatanjieuandClaude Opus 5.5 e6de76502a Prompt Studio edits the selected node's prompt boxes as tags
The assembly board, its write-to-node step and the positive/negative tabs
are gone. The studio now shows the prompt boxes of the prompt node last
selected on the canvas (or a draft) as tags that are edited in place:
weight, edit, remove, reorder, type to add with Chinese translated to
English, and an optional Chinese meaning under each tag. Every change is
one node write with Ctrl+Z, and the studio has its own Undo.

Cards: three common ones instead of six, plus saved prompts; a saved
prompt's text is read once instead of every 30 seconds. Current node
lists the prompt boxes with Edit in Prompt Studio instead of its own
translation. The inspector modal, starter-card data, plan loading and
about 2,000 lines of CSS for the old board are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 03:05:08 +08:00

87 lines
3.9 KiB
JavaScript

/**
* The Prompt Studio drawer beside the canvas: docked left or right (remembered), resizable by its
* edge (double-click resets), closed with Close or Esc. The browser folds away while it is open
* and comes back when it closes. Its content is ui_prompt_workbench.js.
*/
import { createViewScope, bindDrawerResize } from './ui_lifecycle.js';
import { createPromptWorkbench } from './ui_prompt_workbench.js';
import { translate as t } from './locales.js';
const DEFAULT_WIDTH = 620;
let activeStudio = null;
function stored(key, fallback) {
try { return localStorage.getItem(key) || fallback; } catch { return fallback; }
}
function store(key, value) {
try { localStorage.setItem(key, value); } catch { /* remembered for this visit only */ }
}
/** Closes the studio; the browser it folded away comes back (not when another studio replaces it). */
export function closePromptStudio(owner, { replacing = false } = {}) {
if (!activeStudio) return;
const previous = activeStudio;
activeStudio = null;
previous.scope.dispose();
document.body.classList.remove('anomalous-prompt-studio-open');
if (replacing) return;
if (previous.reopenBrowser) previous.owner.show?.();
else if (!previous.owner.modal?.classList.contains('visible')) previous.owner.setTriggerVisible?.(true);
}
export async function openPromptStudio(owner = this) {
// The browser folds away so the canvas has room, and comes back when the studio closes.
const reopenBrowser = Boolean(owner?.modal?.classList.contains('visible') || activeStudio?.reopenBrowser);
closePromptStudio(owner, { replacing: true });
if (owner?.modal?.classList.contains('visible')) owner.close?.();
owner?.setTriggerVisible?.(false);
document.body.classList.add('anomalous-prompt-studio-open');
const overlay = document.createElement('div');
overlay.className = 'anomalous-prompt-studio-overlay';
const scope = createViewScope();
activeStudio = { scope, owner, reopenBrowser };
scope.onDispose(() => overlay.remove());
const drawer = document.createElement('aside');
drawer.className = 'anomalous-prompt-studio-drawer';
if (stored('anomalous_studio_dock_side', 'left') === 'left') drawer.classList.add('is-dock-left');
const savedWidth = parseInt(stored('anomalous_studio_sidebar_width', String(DEFAULT_WIDTH)), 10) || DEFAULT_WIDTH;
drawer.style.width = `${Math.max(420, Math.min(window.innerWidth * 0.85, savedWidth))}px`;
overlay.appendChild(drawer);
const resizeHandle = document.createElement('div');
resizeHandle.className = 'anomalous-studio-resize-handle';
resizeHandle.title = t('promptStudioResize');
drawer.appendChild(resizeHandle);
bindDrawerResize(resizeHandle, drawer, scope, {
side: () => drawer.classList.contains('is-dock-left') ? 'left' : 'right',
minWidth: 420,
setWidth: width => { drawer.style.width = `${width}px`; },
saveWidth: width => store('anomalous_studio_sidebar_width', String(Math.round(width))),
});
resizeHandle.ondblclick = () => {
drawer.style.width = `${DEFAULT_WIDTH}px`;
store('anomalous_studio_sidebar_width', String(DEFAULT_WIDTH));
};
// Esc in a text field of the studio only leaves the field.
scope.listen(window, 'keydown', (event) => {
if (event.key !== 'Escape' || document.querySelector('.anomalous-card-preview-popover.is-pinned')) return;
const field = document.activeElement;
if (drawer.contains(field) && field.matches('input, textarea')) { field.blur(); return; }
closePromptStudio(owner);
});
createPromptWorkbench(owner, drawer, scope, {
onClose: () => closePromptStudio(owner),
onToggleDockSide: () => {
const isLeft = drawer.classList.toggle('is-dock-left');
store('anomalous_studio_dock_side', isLeft ? 'left' : 'right');
return isLeft;
},
});
document.body.appendChild(overlay);
}