Files
DemonGatanjieu-Anomalous_Mo…/web/modules/ui_prompt_inspector.js
T

194 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createViewScope } from './ui_lifecycle.js';
import { syncDraftSynthesizedText, replaceDraftRoleText } from './prompt_studio_data.js';
import { translatePromptText } from './translation_service.js';
import { translate as t } from './locales.js';
let activeInspector = null;
export function openPromptInspectorModal(draft, defaultRole = 'positive', onApplyToNode = null, onChange = () => {}) {
activeInspector?.dispose();
const scope = createViewScope();
activeInspector = scope;
const overlay = document.createElement('div');
overlay.className = 'anomalous-prompt-inspector-overlay';
scope.onDispose(() => { overlay.remove(); if (activeInspector === scope) activeInspector = null; });
const modal = document.createElement('div');
modal.className = 'anomalous-prompt-inspector-modal';
overlay.appendChild(modal);
let currentRole = defaultRole || 'positive';
// Header
const header = document.createElement('header');
header.className = 'anomalous-inspector-header';
const h3 = document.createElement('h3');
h3.textContent = window.anomalous_browser_lang === 'zh' ? '🔍 提示词深度全览与精修' : '🔍 Prompt Inspector & Editor';
h3.style.margin = '0';
h3.style.fontSize = '0.92rem';
h3.style.color = '#e2e8f0';
header.appendChild(h3);
// Role Tabs
const tabsWrap = document.createElement('div');
tabsWrap.style.display = 'flex';
tabsWrap.style.gap = '6px';
const posTab = document.createElement('button');
posTab.className = `anomalous-btn-ghost anomalous-btn-sm${currentRole === 'positive' ? ' is-active' : ''}`;
posTab.textContent = `🔆 ${window.anomalous_browser_lang === 'zh' ? '正向提示词' : 'Positive'}`;
const negTab = document.createElement('button');
negTab.className = `anomalous-btn-ghost anomalous-btn-sm${currentRole === 'negative' ? ' is-active' : ''}`;
negTab.textContent = `🌙 ${window.anomalous_browser_lang === 'zh' ? '负向提示词' : 'Negative'}`;
tabsWrap.appendChild(posTab);
tabsWrap.appendChild(negTab);
header.appendChild(tabsWrap);
const closeBtn = document.createElement('button');
closeBtn.className = 'anomalous-btn-ghost anomalous-btn-sm';
closeBtn.textContent = '✕';
closeBtn.title = window.anomalous_browser_lang === 'zh' ? '关闭 (Esc)' : 'Close (Esc)';
closeBtn.onclick = () => scope.dispose();
header.appendChild(closeBtn);
// Body
const body = document.createElement('div');
body.className = 'anomalous-inspector-body';
const editHint = document.createElement('small');
editHint.textContent = t('promptFullTextEditHint');
body.appendChild(editHint);
const textarea = document.createElement('textarea');
textarea.className = 'anomalous-inspector-textarea';
textarea.spellcheck = false;
syncDraftSynthesizedText(draft);
textarea.value = draft?.plan?.[currentRole] || '';
const updateStats = () => {
const val = textarea.value;
const words = val.split(/[,,\s\n]+/).filter(Boolean).length;
statsEl.textContent = `${val.length} ${window.anomalous_browser_lang === 'zh' ? '字符' : 'chars'} · ~${words} ${window.anomalous_browser_lang === 'zh' ? '词' : 'words'}`;
};
const updateTabStyles = () => {
posTab.classList.toggle('is-active', currentRole === 'positive');
negTab.classList.toggle('is-active', currentRole === 'negative');
textarea.value = draft?.plan?.[currentRole] || '';
updateStats();
};
posTab.onclick = () => { currentRole = 'positive'; updateTabStyles(); };
negTab.onclick = () => { currentRole = 'negative'; updateTabStyles(); };
body.appendChild(textarea);
// Footer
const footer = document.createElement('footer');
footer.className = 'anomalous-inspector-footer';
const statsEl = document.createElement('div');
statsEl.style.fontSize = '0.75rem';
statsEl.style.color = '#94a3b8';
statsEl.style.fontFamily = 'monospace';
const saveEdits = () => {
replaceDraftRoleText(draft, currentRole, textarea.value);
onChange();
updateStats();
};
textarea.oninput = saveEdits;
updateStats();
footer.appendChild(statsEl);
const actions = document.createElement('div');
actions.style.display = 'flex';
actions.style.gap = '6px';
// Normalize formatting
const normBtn = document.createElement('button');
normBtn.className = 'anomalous-btn-ghost anomalous-btn-sm';
normBtn.textContent = `🧹 ${window.anomalous_browser_lang === 'zh' ? '规范化' : 'Normalize'}`;
normBtn.title = window.anomalous_browser_lang === 'zh' ? '清理多余顿号、分号与重复逗号' : 'Clean formatting and punctuation';
normBtn.onclick = () => {
const cleaned = textarea.value
.replace(/[,,、;;||\n\r]+/g, ', ')
.replace(/\s+,/g, ',')
.replace(/,\s*,/g, ', ')
.replace(/^\s*,\s*|\s*,\s*$/g, '')
.trim();
textarea.value = cleaned;
saveEdits();
};
actions.appendChild(normBtn);
// Translate
const transBtn = document.createElement('button');
transBtn.className = 'anomalous-btn-ghost anomalous-btn-sm';
transBtn.textContent = `🌐 ${window.anomalous_browser_lang === 'zh' ? '翻译' : 'Translate'}`;
transBtn.onclick = async () => {
const raw = textarea.value.trim();
const role = currentRole;
if (!raw) return;
const orig = transBtn.textContent;
transBtn.textContent = '⏳';
try {
const res = await translatePromptText(raw, { signal: scope.signal });
if (!scope.signal.aborted && currentRole === role && textarea.value.trim() === raw && res.ok && res.translated) {
textarea.value = res.translated;
saveEdits();
}
} finally {
transBtn.textContent = orig;
}
};
actions.appendChild(transBtn);
// Copy
const copyBtn = document.createElement('button');
copyBtn.className = 'anomalous-btn-ghost anomalous-btn-sm';
copyBtn.textContent = `📋 ${window.anomalous_browser_lang === 'zh' ? '复制' : 'Copy'}`;
copyBtn.onclick = async () => {
await navigator.clipboard.writeText(textarea.value);
copyBtn.textContent = '✓';
setTimeout(() => { if (copyBtn.isConnected) copyBtn.textContent = `📋 ${window.anomalous_browser_lang === 'zh' ? '复制' : 'Copy'}`; }, 1200);
};
actions.appendChild(copyBtn);
// Apply to node & close
if (typeof onApplyToNode === 'function') {
const applyBtn = document.createElement('button');
applyBtn.className = 'anomalous-btn-primary anomalous-btn-sm';
applyBtn.textContent = `🚀 ${window.anomalous_browser_lang === 'zh' ? '写入节点并关闭' : 'Write Node & Close'}`;
applyBtn.onclick = async () => {
if (await onApplyToNode(currentRole, textarea.value)) scope.dispose();
};
actions.appendChild(applyBtn);
}
footer.appendChild(actions);
modal.appendChild(header);
modal.appendChild(body);
modal.appendChild(footer);
overlay.onclick = (e) => {
if (e.target === overlay) scope.dispose();
};
const onKey = (e) => {
if (e.key === 'Escape') {
scope.dispose();
}
};
scope.listen(window, 'keydown', onKey);
document.body.appendChild(overlay);
return () => scope.dispose();
}