Current node lists the selected node's prompt boxes (prompt_boxes.js, so each is marked positive or negative by its wiring). Chinese text gets "translate to English", written back only if the box still holds the same text when the translation arrives (one undo); English text can show its Chinese meaning without writing. The standalone Prompt Translator (ui_prompt_translator.js, 838 lines, its four fallback ways of guessing a text box, its styles and its toolbox entry) is removed, with Prompt Studio's appendPromptToStudio that only it used. Prompt Studio keeps its own translate buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.2 KiB
JavaScript
104 lines
4.2 KiB
JavaScript
/**
|
|
* The current-node panel's prompts: each prompt box of the node (prompt_boxes.js) with its
|
|
* role and text, and translation in place: a box with Chinese is translated to English and
|
|
* written back (one undo in the receipt); any other text can show its Chinese meaning
|
|
* underneath without writing anything. Translation goes through translation_service.js.
|
|
*/
|
|
|
|
import { app } from "../../../scripts/app.js";
|
|
import { translate as t } from './locales.js';
|
|
import { applyNodeMaterialValues } from './node_material_actions.js';
|
|
import { promptBoxes } from './prompt_boxes.js';
|
|
import { hasChinese, translatePromptText } from './translation_service.js';
|
|
import { showMaterialApplication } from './ui_material_application.js';
|
|
|
|
const ROLE_KEYS = { positive: 'recipePromptRolePositive', negative: 'recipePromptRoleNegative', both: 'recipePromptRoleBoth' };
|
|
|
|
function el(tag, className, text) {
|
|
const node = document.createElement(tag);
|
|
if (className) node.className = className;
|
|
if (text !== undefined) node.textContent = text;
|
|
return node;
|
|
}
|
|
|
|
function button(className, label, onClick) {
|
|
const node = el('button', className, label);
|
|
node.type = 'button';
|
|
node.onclick = onClick;
|
|
return node;
|
|
}
|
|
|
|
function boxRow(node, box, receiptHost) {
|
|
const row = el('div', 'anomalous-node-prompt');
|
|
const head = el('div', 'anomalous-node-param-head');
|
|
head.append(el('span', `anomalous-node-param-role is-${box.role || 'plain'}`, t(ROLE_KEYS[box.role] || 'currentNodePromptPlain')),
|
|
el('span', 'anomalous-node-prompt-name', box.name));
|
|
const textEl = el('p', 'anomalous-node-prompt-text');
|
|
const note = el('p', 'anomalous-node-prompt-note');
|
|
note.hidden = true;
|
|
const actions = el('div', 'anomalous-node-prompt-actions');
|
|
row.append(head, textEl, note, actions);
|
|
|
|
const draw = () => {
|
|
const value = box.widget.value;
|
|
textEl.textContent = value.trim() || t('currentNodePromptEmpty');
|
|
textEl.classList.toggle('is-empty', !value.trim());
|
|
actions.replaceChildren();
|
|
if (!value.trim()) return;
|
|
if (hasChinese(value)) actions.append(button('anomalous-node-section-btn', t('currentNodeToEnglish'), toEnglish));
|
|
else actions.append(button('anomalous-node-section-btn', t('currentNodeShowChinese'), showChinese));
|
|
};
|
|
const busy = (target, label) => {
|
|
target.disabled = true;
|
|
target.textContent = label;
|
|
};
|
|
const say = (message) => {
|
|
note.hidden = false;
|
|
note.textContent = message;
|
|
};
|
|
|
|
async function toEnglish(event) {
|
|
const before = box.widget.value;
|
|
busy(event.currentTarget, t('currentNodeTranslating'));
|
|
const result = await translatePromptText(before, { targetLang: 'en' });
|
|
if (!row.isConnected) return;
|
|
if (!result.ok || !result.translated) {
|
|
draw();
|
|
say(t('currentNodeTranslateFailed'));
|
|
return;
|
|
}
|
|
// The box may have been edited while the translation was on its way.
|
|
if (node.widgets?.[box.index] !== box.widget || box.widget.value !== before) {
|
|
draw();
|
|
say(t('currentNodeChangedMeanwhile'));
|
|
return;
|
|
}
|
|
const applied = applyNodeMaterialValues(app, node, [{ index: box.index, value: result.translated }]);
|
|
showMaterialApplication(receiptHost, { undo() { applied.undo(); draw(); } }, node);
|
|
note.hidden = true;
|
|
draw();
|
|
}
|
|
|
|
async function showChinese(event) {
|
|
busy(event.currentTarget, t('currentNodeTranslating'));
|
|
const result = await translatePromptText(box.widget.value, { targetLang: 'zh-CN' });
|
|
if (!row.isConnected) return;
|
|
draw();
|
|
say(result.ok ? result.translated : t('currentNodeTranslateFailed'));
|
|
}
|
|
|
|
draw();
|
|
return row;
|
|
}
|
|
|
|
/** Appends the prompts section for `node` to `container`, when the node has prompt boxes. */
|
|
export function renderNodePrompts(node, container) {
|
|
const boxes = promptBoxes(node);
|
|
if (!boxes.length) return;
|
|
const section = el('section', 'anomalous-node-section');
|
|
const head = el('div', 'anomalous-node-section-head');
|
|
head.append(el('h3', '', t('currentNodePrompts')));
|
|
section.append(head, ...boxes.map(box => boxRow(node, box, section)));
|
|
container.append(section);
|
|
}
|