Files
DemonGatanjieu-Anomalous_Mo…/web/modules/shell_open_rules.js
T
DemonGatanjieuandClaude Opus 5.5 80805b88d3 Open where the selected node is; number keys for the rail's pages
Opening the browser (the trigger, the action-bar button, the menu or its
shortcut) now follows the node selected on the canvas: a node with prompt
boxes and no model drop-down opens Prompt Studio, which targets that node;
any other node (a KSampler, a model loader) opens Current node; nothing
selected reopens the page used last. Settings -> Opening and window can turn
it off. Prompt Studio closing, the doctor banner and MCP actions still open
the browser as it was.

While the browser is open, the plain keys 1-9 go to the rail's pages from
the top. They are ignored in text fields, while an input method is
composing and while a dialog is open; ComfyUI binds no plain digits (W is
its Workflows key, so W/S were not used). The rail's tooltips name each
page with its number.

The rules are in shell_open_rules.js with tests/shell_open_rules.mjs.
Checked live on a test ComfyUI: a CLIP Text Encode opens Prompt Studio on
it, a KSampler opens Current node, 1/3/4/5/7/8 switch pages, digits typed
into the search box stay there, and the setting turns the rule off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:43:53 +08:00

49 lines
2.0 KiB
JavaScript

/**
* Two small rules of the shell, without DOM: which page opening the browser shows for the node
* selected on the canvas (Settings -> Opening and window, on by default), and which rail page a
* number key goes to while the browser is open (1-9, the rail from the top).
*/
export const FOLLOW_SELECTION_KEY = 'anomalous_open_follows_node';
/** Whether opening the browser follows the selected node (a per-viewer convenience). */
export function followsSelection() {
try {
return localStorage.getItem(FOLLOW_SELECTION_KEY) !== 'false';
} catch {
return true;
}
}
export function setFollowsSelection(on) {
try {
localStorage.setItem(FOLLOW_SELECTION_KEY, on ? 'true' : 'false');
} catch { /* stays on for this visit */ }
}
/**
* The page for the selected `node`: 'prompts' (Prompt Studio, which edits that node's boxes)
* for a node with prompt boxes and no model drop-down, 'assistant' (Current node) for any other
* node, null when nothing is selected (the browser reopens the page used last).
* `takesPrompt(node)` and `hasModel(node)` say what the node holds.
*/
export function pageForNode(node, { takesPrompt, hasModel }) {
if (!node) return null;
return takesPrompt(node) && !hasModel(node) ? 'prompts' : 'assistant';
}
const EDITABLE = 'input, textarea, select, [contenteditable=""], [contenteditable="true"], [role="dialog"]';
/**
* The rail page a key press goes to, or null: a plain 1-9 (top row or number pad) picks the
* page at that place of `pages` (RAIL_PAGES), unless the key is typed into a field or a dialog.
*/
export function railPageForKey(event, pages) {
if (!event || event.isComposing || event.repeat) return null;
if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) return null;
if (event.target?.closest?.(EDITABLE)) return null;
const digit = /^(?:Digit|Numpad)([1-9])$/.exec(event.code || '')?.[1] ?? (/^[1-9]$/.test(event.key || '') ? event.key : null);
if (!digit) return null;
return pages[Number(digit) - 1]?.page ?? null;
}