Refactor Ena Planner to iframe settings and harden save ack flow
This commit is contained in:
@@ -182,5 +182,6 @@ export const TasksStorage = new StorageFile('LittleWhiteBox_Tasks.json');
|
|||||||
export const StoryOutlineStorage = new StorageFile('LittleWhiteBox_StoryOutline.json');
|
export const StoryOutlineStorage = new StorageFile('LittleWhiteBox_StoryOutline.json');
|
||||||
export const NovelDrawStorage = new StorageFile('LittleWhiteBox_NovelDraw.json', { debounceMs: 800 });
|
export const NovelDrawStorage = new StorageFile('LittleWhiteBox_NovelDraw.json', { debounceMs: 800 });
|
||||||
export const TtsStorage = new StorageFile('LittleWhiteBox_TTS.json', { debounceMs: 800 });
|
export const TtsStorage = new StorageFile('LittleWhiteBox_TTS.json', { debounceMs: 800 });
|
||||||
|
export const EnaPlannerStorage = new StorageFile('LittleWhiteBox_EnaPlanner.json', { debounceMs: 800 });
|
||||||
export const CommonSettingStorage = new StorageFile('LittleWhiteBox_CommonSettings.json', { debounceMs: 1000 });
|
export const CommonSettingStorage = new StorageFile('LittleWhiteBox_CommonSettings.json', { debounceMs: 1000 });
|
||||||
export const VectorStorage = new StorageFile('LittleWhiteBox_Vectors.json', { debounceMs: 3000 });
|
export const VectorStorage = new StorageFile('LittleWhiteBox_Vectors.json', { debounceMs: 3000 });
|
||||||
|
|||||||
30
index.js
30
index.js
@@ -27,7 +27,7 @@ import { initNovelDraw, cleanupNovelDraw } from "./modules/novel-draw/novel-draw
|
|||||||
import "./modules/story-summary/story-summary.js";
|
import "./modules/story-summary/story-summary.js";
|
||||||
import "./modules/story-outline/story-outline.js";
|
import "./modules/story-outline/story-outline.js";
|
||||||
import { initTts, cleanupTts } from "./modules/tts/tts.js";
|
import { initTts, cleanupTts } from "./modules/tts/tts.js";
|
||||||
import { initEnaPlanner } from "./modules/ena-planner/ena-planner.js";
|
import { initEnaPlanner, cleanupEnaPlanner } from "./modules/ena-planner/ena-planner.js";
|
||||||
|
|
||||||
extension_settings[EXT_ID] = extension_settings[EXT_ID] || {
|
extension_settings[EXT_ID] = extension_settings[EXT_ID] || {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -45,6 +45,7 @@ extension_settings[EXT_ID] = extension_settings[EXT_ID] || {
|
|||||||
storyOutline: { enabled: false },
|
storyOutline: { enabled: false },
|
||||||
novelDraw: { enabled: false },
|
novelDraw: { enabled: false },
|
||||||
tts: { enabled: false },
|
tts: { enabled: false },
|
||||||
|
enaPlanner: { enabled: false },
|
||||||
useBlob: false,
|
useBlob: false,
|
||||||
wrapperIframe: true,
|
wrapperIframe: true,
|
||||||
renderEnabled: true,
|
renderEnabled: true,
|
||||||
@@ -277,7 +278,8 @@ function toggleSettingsControls(enabled) {
|
|||||||
'xiaobaix_use_blob', 'xiaobaix_variables_core_enabled', 'xiaobaix_variables_mode', 'Wrapperiframe', 'xiaobaix_render_enabled',
|
'xiaobaix_use_blob', 'xiaobaix_variables_core_enabled', 'xiaobaix_variables_mode', 'Wrapperiframe', 'xiaobaix_render_enabled',
|
||||||
'xiaobaix_max_rendered', 'xiaobaix_story_outline_enabled', 'xiaobaix_story_summary_enabled',
|
'xiaobaix_max_rendered', 'xiaobaix_story_outline_enabled', 'xiaobaix_story_summary_enabled',
|
||||||
'xiaobaix_novel_draw_enabled', 'xiaobaix_novel_draw_open_settings',
|
'xiaobaix_novel_draw_enabled', 'xiaobaix_novel_draw_open_settings',
|
||||||
'xiaobaix_tts_enabled', 'xiaobaix_tts_open_settings'
|
'xiaobaix_tts_enabled', 'xiaobaix_tts_open_settings',
|
||||||
|
'xiaobaix_ena_planner_enabled', 'xiaobaix_ena_planner_open_settings'
|
||||||
];
|
];
|
||||||
controls.forEach(id => {
|
controls.forEach(id => {
|
||||||
$(`#${id}`).prop('disabled', !enabled).closest('.flex-container').toggleClass('disabled-control', !enabled);
|
$(`#${id}`).prop('disabled', !enabled).closest('.flex-container').toggleClass('disabled-control', !enabled);
|
||||||
@@ -312,6 +314,7 @@ async function toggleAllFeatures(enabled) {
|
|||||||
{ condition: extension_settings[EXT_ID].variablesCore?.enabled, init: initVariablesCore },
|
{ condition: extension_settings[EXT_ID].variablesCore?.enabled, init: initVariablesCore },
|
||||||
{ condition: extension_settings[EXT_ID].novelDraw?.enabled, init: initNovelDraw },
|
{ condition: extension_settings[EXT_ID].novelDraw?.enabled, init: initNovelDraw },
|
||||||
{ condition: extension_settings[EXT_ID].tts?.enabled, init: initTts },
|
{ condition: extension_settings[EXT_ID].tts?.enabled, init: initTts },
|
||||||
|
{ condition: extension_settings[EXT_ID].enaPlanner?.enabled, init: initEnaPlanner },
|
||||||
{ condition: true, init: initStreamingGeneration },
|
{ condition: true, init: initStreamingGeneration },
|
||||||
{ condition: true, init: initButtonCollapse }
|
{ condition: true, init: initButtonCollapse }
|
||||||
];
|
];
|
||||||
@@ -347,6 +350,7 @@ async function toggleAllFeatures(enabled) {
|
|||||||
try { cleanupVareventEditor(); } catch (e) { }
|
try { cleanupVareventEditor(); } catch (e) { }
|
||||||
try { cleanupNovelDraw(); } catch (e) { }
|
try { cleanupNovelDraw(); } catch (e) { }
|
||||||
try { cleanupTts(); } catch (e) { }
|
try { cleanupTts(); } catch (e) { }
|
||||||
|
try { cleanupEnaPlanner(); } catch (e) { }
|
||||||
try { clearBlobCaches(); } catch (e) { }
|
try { clearBlobCaches(); } catch (e) { }
|
||||||
toggleSettingsControls(false);
|
toggleSettingsControls(false);
|
||||||
try { window.cleanupWorldbookHostBridge && window.cleanupWorldbookHostBridge(); document.getElementById('xb-worldbook')?.remove(); } catch (e) { }
|
try { window.cleanupWorldbookHostBridge && window.cleanupWorldbookHostBridge(); document.getElementById('xb-worldbook')?.remove(); } catch (e) { }
|
||||||
@@ -391,7 +395,8 @@ async function setupSettings() {
|
|||||||
{ id: 'xiaobaix_story_summary_enabled', key: 'storySummary' },
|
{ id: 'xiaobaix_story_summary_enabled', key: 'storySummary' },
|
||||||
{ id: 'xiaobaix_story_outline_enabled', key: 'storyOutline' },
|
{ id: 'xiaobaix_story_outline_enabled', key: 'storyOutline' },
|
||||||
{ id: 'xiaobaix_novel_draw_enabled', key: 'novelDraw', init: initNovelDraw },
|
{ id: 'xiaobaix_novel_draw_enabled', key: 'novelDraw', init: initNovelDraw },
|
||||||
{ id: 'xiaobaix_tts_enabled', key: 'tts', init: initTts }
|
{ id: 'xiaobaix_tts_enabled', key: 'tts', init: initTts },
|
||||||
|
{ id: 'xiaobaix_ena_planner_enabled', key: 'enaPlanner', init: initEnaPlanner }
|
||||||
];
|
];
|
||||||
|
|
||||||
moduleConfigs.forEach(({ id, key, init }) => {
|
moduleConfigs.forEach(({ id, key, init }) => {
|
||||||
@@ -407,6 +412,9 @@ async function setupSettings() {
|
|||||||
if (!enabled && key === 'tts') {
|
if (!enabled && key === 'tts') {
|
||||||
try { cleanupTts(); } catch (e) { }
|
try { cleanupTts(); } catch (e) { }
|
||||||
}
|
}
|
||||||
|
if (!enabled && key === 'enaPlanner') {
|
||||||
|
try { cleanupEnaPlanner(); } catch (e) { }
|
||||||
|
}
|
||||||
settings[key] = extension_settings[EXT_ID][key] || {};
|
settings[key] = extension_settings[EXT_ID][key] || {};
|
||||||
settings[key].enabled = enabled;
|
settings[key].enabled = enabled;
|
||||||
extension_settings[EXT_ID][key] = settings[key];
|
extension_settings[EXT_ID][key] = settings[key];
|
||||||
@@ -450,6 +458,15 @@ async function setupSettings() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#xiaobaix_ena_planner_open_settings").on("click", function () {
|
||||||
|
if (!isXiaobaixEnabled) return;
|
||||||
|
if (settings.enaPlanner?.enabled && window.xiaobaixEnaPlanner?.openSettings) {
|
||||||
|
window.xiaobaixEnaPlanner.openSettings();
|
||||||
|
} else {
|
||||||
|
toastr.warning('请先启用剧情规划模块');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#xiaobaix_use_blob").prop("checked", !!settings.useBlob).on("change", async function () {
|
$("#xiaobaix_use_blob").prop("checked", !!settings.useBlob).on("change", async function () {
|
||||||
if (!isXiaobaixEnabled) return;
|
if (!isXiaobaixEnabled) return;
|
||||||
settings.useBlob = $(this).prop("checked");
|
settings.useBlob = $(this).prop("checked");
|
||||||
@@ -512,10 +529,11 @@ async function setupSettings() {
|
|||||||
variablesPanel: 'xiaobaix_variables_panel_enabled',
|
variablesPanel: 'xiaobaix_variables_panel_enabled',
|
||||||
variablesCore: 'xiaobaix_variables_core_enabled',
|
variablesCore: 'xiaobaix_variables_core_enabled',
|
||||||
novelDraw: 'xiaobaix_novel_draw_enabled',
|
novelDraw: 'xiaobaix_novel_draw_enabled',
|
||||||
tts: 'xiaobaix_tts_enabled'
|
tts: 'xiaobaix_tts_enabled',
|
||||||
|
enaPlanner: 'xiaobaix_ena_planner_enabled'
|
||||||
};
|
};
|
||||||
const ON = ['templateEditor', 'tasks', 'variablesCore', 'audio', 'storySummary', 'recorded'];
|
const ON = ['templateEditor', 'tasks', 'variablesCore', 'audio', 'storySummary', 'recorded'];
|
||||||
const OFF = ['preview', 'immersive', 'variablesPanel', 'fourthWall', 'storyOutline', 'novelDraw', 'tts'];
|
const OFF = ['preview', 'immersive', 'variablesPanel', 'fourthWall', 'storyOutline', 'novelDraw', 'tts', 'enaPlanner'];
|
||||||
function setChecked(id, val) {
|
function setChecked(id, val) {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -650,11 +668,11 @@ jQuery(async () => {
|
|||||||
{ condition: settings.variablesCore?.enabled, init: initVariablesCore },
|
{ condition: settings.variablesCore?.enabled, init: initVariablesCore },
|
||||||
{ condition: settings.novelDraw?.enabled, init: initNovelDraw },
|
{ condition: settings.novelDraw?.enabled, init: initNovelDraw },
|
||||||
{ condition: settings.tts?.enabled, init: initTts },
|
{ condition: settings.tts?.enabled, init: initTts },
|
||||||
|
{ condition: settings.enaPlanner?.enabled, init: initEnaPlanner },
|
||||||
{ condition: true, init: initStreamingGeneration },
|
{ condition: true, init: initStreamingGeneration },
|
||||||
{ condition: true, init: initButtonCollapse }
|
{ condition: true, init: initButtonCollapse }
|
||||||
];
|
];
|
||||||
moduleInits.forEach(({ condition, init }) => { if (condition) init(); });
|
moduleInits.forEach(({ condition, init }) => { if (condition) init(); });
|
||||||
try { initEnaPlanner(); } catch (e) { console.error('[EnaPlanner] Init failed:', e); }
|
|
||||||
|
|
||||||
if (settings.preview?.enabled || settings.recorded?.enabled) {
|
if (settings.preview?.enabled || settings.recorded?.enabled) {
|
||||||
setTimeout(initMessagePreview, 1500);
|
setTimeout(initMessagePreview, 1500);
|
||||||
|
|||||||
@@ -1,242 +1,188 @@
|
|||||||
/* Ena Planner v0.5 — collapsible, clean */
|
:root {
|
||||||
|
--bg: #121212;
|
||||||
|
--card: #1b1b1b;
|
||||||
|
--line: #343434;
|
||||||
|
--muted: #a8a8a8;
|
||||||
|
--text: #e9e9e9;
|
||||||
|
--ok: #85d48b;
|
||||||
|
--err: #f48f8f;
|
||||||
|
--btn: #2c2c2c;
|
||||||
|
--primary: #355fcf;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
.wrap {
|
||||||
|
max-width: 1120px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ff9800;
|
||||||
|
}
|
||||||
|
.dot.ok { background: #4caf50; }
|
||||||
|
|
||||||
/* ===== Settings panel inside inline-drawer ===== */
|
.tabs {
|
||||||
#ena_planner_panel {
|
display: flex;
|
||||||
padding: 8px 0;
|
gap: 6px;
|
||||||
}
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.75;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
opacity: 1;
|
||||||
|
background: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
#ena_planner_panel .ep-row {
|
.panel { display: none; }
|
||||||
display: flex;
|
.panel.active { display: block; }
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel label {
|
.card {
|
||||||
font-size: 12px;
|
background: var(--card);
|
||||||
opacity: .9;
|
border: 1px solid var(--line);
|
||||||
display: block;
|
border-radius: 10px;
|
||||||
margin-bottom: 4px;
|
padding: 12px;
|
||||||
}
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.col {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 230px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #d0d0d0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
input, select, textarea {
|
||||||
|
width: 100%;
|
||||||
|
background: #111;
|
||||||
|
color: #efefef;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 7px 8px;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
min-height: 110px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--btn);
|
||||||
|
color: #fff;
|
||||||
|
padding: 6px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.btn.primary {
|
||||||
|
border-color: var(--primary);
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
min-height: 18px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
color: var(--ok);
|
||||||
|
}
|
||||||
|
.status.error { color: var(--err); }
|
||||||
|
|
||||||
#ena_planner_panel input[type="text"],
|
.prompt-block {
|
||||||
#ena_planner_panel input[type="password"],
|
border: 1px solid #404040;
|
||||||
#ena_planner_panel input[type="number"],
|
border-radius: 8px;
|
||||||
#ena_planner_panel select,
|
padding: 8px;
|
||||||
#ena_planner_panel textarea {
|
margin-bottom: 8px;
|
||||||
width: 100%;
|
}
|
||||||
box-sizing: border-box;
|
.prompt-head {
|
||||||
}
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.prompt-head-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
#ena_planner_panel .ep-col {
|
.log-list {
|
||||||
flex: 1 1 220px;
|
max-height: 62vh;
|
||||||
min-width: 220px;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.log-item {
|
||||||
#ena_planner_panel .ep-col.wide {
|
border-bottom: 1px solid #2f2f2f;
|
||||||
flex: 1 1 100%;
|
padding: 8px 0;
|
||||||
min-width: 260px;
|
}
|
||||||
}
|
.log-meta {
|
||||||
|
display: flex;
|
||||||
/* Tabs */
|
justify-content: space-between;
|
||||||
#ena_planner_panel .ep-tabs {
|
font-size: 12px;
|
||||||
display: flex;
|
color: var(--muted);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
margin-bottom: 10px;
|
.log-error {
|
||||||
}
|
margin: 5px 0;
|
||||||
|
color: var(--err);
|
||||||
#ena_planner_panel .ep-tab {
|
font-size: 12px;
|
||||||
padding: 6px 10px;
|
white-space: pre-wrap;
|
||||||
border-radius: 999px;
|
}
|
||||||
cursor: pointer;
|
.log-pre {
|
||||||
border: 1px solid var(--SmartThemeBorderColor, #333);
|
margin-top: 6px;
|
||||||
opacity: .85;
|
white-space: pre-wrap;
|
||||||
user-select: none;
|
word-break: break-word;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
}
|
background: #0f0f0f;
|
||||||
|
border: 1px solid #2f2f2f;
|
||||||
#ena_planner_panel .ep-tab.active {
|
border-radius: 6px;
|
||||||
opacity: 1;
|
padding: 8px;
|
||||||
background: rgba(255, 255, 255, .06);
|
max-height: 260px;
|
||||||
}
|
overflow: auto;
|
||||||
|
}
|
||||||
#ena_planner_panel .ep-panel {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel .ep-panel.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel .ep-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel .ep-hint {
|
|
||||||
font-size: 11px;
|
|
||||||
opacity: .7;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel .ep-hint-box {
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: .85;
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: rgba(255, 255, 255, .04);
|
|
||||||
border: 1px solid rgba(255, 255, 255, .08);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ena_planner_panel .ep-divider {
|
|
||||||
margin: 10px 0;
|
|
||||||
border-top: 1px dashed rgba(255, 255, 255, .15);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inline badge (in drawer header) */
|
|
||||||
.ep-badge-inline {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: .9;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-badge-inline .dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #888;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-badge-inline.ok .dot {
|
|
||||||
background: #2ecc71;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-badge-inline.warn .dot {
|
|
||||||
background: #f39c12;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prompt block */
|
|
||||||
.ep-prompt-block {
|
|
||||||
border: 1px solid rgba(255, 255, 255, .12);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-prompt-head {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== Log modal ===== */
|
|
||||||
.ep-log-modal {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0, 0, 0, .65);
|
|
||||||
z-index: 99999;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-modal.open {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-modal .ep-log-card {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: min(980px, 96vw);
|
|
||||||
height: min(82vh, 900px);
|
|
||||||
background: rgba(20, 20, 20, .95);
|
|
||||||
border: 1px solid rgba(255, 255, 255, .15);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 14px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-modal .ep-log-head {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-modal .ep-log-head .title {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-modal .ep-log-body {
|
|
||||||
overflow: auto;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
border: 1px solid rgba(255, 255, 255, .08);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item {
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, .08);
|
|
||||||
padding: 12px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item .meta {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
opacity: .85;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item .ep-log-error {
|
|
||||||
color: #ffb3b3;
|
|
||||||
font-size: 12px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item details {
|
|
||||||
margin: 6px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ep-log-item details summary {
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: .85;
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Issue #3: proper log formatting with line breaks */
|
|
||||||
.ep-log-pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.5;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: rgba(255, 255, 255, .04);
|
|
||||||
border: 1px solid rgba(255, 255, 255, .06);
|
|
||||||
max-height: 400px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|||||||
653
modules/ena-planner/ena-planner.html
Normal file
653
modules/ena-planner/ena-planner.html
Normal file
@@ -0,0 +1,653 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Ena Planner</title>
|
||||||
|
<link rel="stylesheet" href="./ena-planner.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="top">
|
||||||
|
<strong>Ena Planner</strong>
|
||||||
|
<span id="stateBadge" class="badge"><span class="dot"></span><span>Disabled</span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs">
|
||||||
|
<div class="tab active" data-tab="general">General</div>
|
||||||
|
<div class="tab" data-tab="api">API</div>
|
||||||
|
<div class="tab" data-tab="prompt">Prompt</div>
|
||||||
|
<div class="tab" data-tab="debug">Debug</div>
|
||||||
|
<div class="tab" data-tab="logs">Logs</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel active" data-panel="general">
|
||||||
|
<div class="card">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Enabled</label>
|
||||||
|
<select id="ep_enabled"><option value="true">Enabled</option><option value="false">Disabled</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<label>Skip if input already contains <plot></label>
|
||||||
|
<select id="ep_skip_plot"><option value="true">Yes</option><option value="false">No</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Include global worldbooks</label>
|
||||||
|
<select id="ep_include_global_wb"><option value="false">No</option><option value="true">Yes</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<label>Exclude worldbook entries with position=4</label>
|
||||||
|
<select id="ep_wb_pos4"><option value="true">Yes</option><option value="false">No</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Worldbook name excludes (comma separated)</label>
|
||||||
|
<input id="ep_wb_exclude_names" type="text" placeholder="mvu_update">
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<label>Recent plot count</label>
|
||||||
|
<input id="ep_plot_n" type="number" min="0" step="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Chat exclude tags (comma separated)</label>
|
||||||
|
<input id="ep_exclude_tags" type="text" placeholder="ActionOptions, UpdateVariable, StatusPlaceHolderImpl">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Persist logs</label>
|
||||||
|
<select id="ep_logs_persist"><option value="true">Yes</option><option value="false">No</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<label>Log max count</label>
|
||||||
|
<input id="ep_logs_max" type="number" min="1" max="200" step="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" data-panel="api">
|
||||||
|
<div class="card">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Channel</label>
|
||||||
|
<select id="ep_api_channel">
|
||||||
|
<option value="openai">OpenAI compatible</option>
|
||||||
|
<option value="gemini">Gemini compatible</option>
|
||||||
|
<option value="claude">Claude compatible</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<label>Prefix mode</label>
|
||||||
|
<select id="ep_prefix_mode"><option value="auto">Auto</option><option value="custom">Custom</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>API base URL</label><input id="ep_api_base" type="text" placeholder="https://..."></div>
|
||||||
|
<div class="col"><label>Custom prefix</label><input id="ep_prefix_custom" type="text" placeholder="/v1"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>API key</label><input id="ep_api_key" type="password" placeholder="sk-..."></div>
|
||||||
|
<div class="col"><label>Model</label><input id="ep_model" type="text" placeholder="model-name"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>Stream</label><select id="ep_stream"><option value="true">Enabled</option><option value="false">Disabled</option></select></div>
|
||||||
|
<div class="col"><label>Temperature</label><input id="ep_temp" type="number" step="0.1"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>Top P</label><input id="ep_top_p" type="number" step="0.05"></div>
|
||||||
|
<div class="col"><label>Top K</label><input id="ep_top_k" type="number" step="1"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>Presence penalty</label><input id="ep_pp" type="text"></div>
|
||||||
|
<div class="col"><label>Frequency penalty</label><input id="ep_fp" type="text"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"><label>Max tokens</label><input id="ep_mt" type="text"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_fetch_models" class="btn">Fetch models</button>
|
||||||
|
<button id="ep_test_conn" class="btn">Test connection</button>
|
||||||
|
</div>
|
||||||
|
<div class="hint">Models preview: <span id="ep_models_preview">Not loaded</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" data-panel="prompt">
|
||||||
|
<div class="card">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>Template</label>
|
||||||
|
<select id="ep_tpl_select"><option value="">-- Select template --</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_tpl_save" class="btn">Save</button>
|
||||||
|
<button id="ep_tpl_saveas" class="btn">Save as</button>
|
||||||
|
<button id="ep_tpl_delete" class="btn">Delete</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="ep_prompt_list"></div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_add_prompt" class="btn">Add block</button>
|
||||||
|
<button id="ep_reset_prompt" class="btn">Reset default</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" data-panel="debug">
|
||||||
|
<div class="card">
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_debug_worldbook" class="btn">Debug worldbook</button>
|
||||||
|
<button id="ep_debug_char" class="btn">Debug character</button>
|
||||||
|
<button id="ep_test_planner" class="btn">Run planner test</button>
|
||||||
|
</div>
|
||||||
|
<pre id="ep_debug_output" class="log-pre" style="display:none;"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" data-panel="logs">
|
||||||
|
<div class="card">
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_open_logs" class="btn">Refresh logs</button>
|
||||||
|
<button id="ep_log_export" class="btn">Export JSON</button>
|
||||||
|
<button id="ep_log_clear" class="btn">Clear</button>
|
||||||
|
</div>
|
||||||
|
<div id="ep_log_body" class="log-list"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button id="ep_save" class="btn primary">Save</button>
|
||||||
|
<button id="ep_close" class="btn">Close</button>
|
||||||
|
</div>
|
||||||
|
<div id="ep_status" class="status"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const PARENT_ORIGIN = (() => {
|
||||||
|
try { return new URL(document.referrer).origin; } catch { return window.location.origin; }
|
||||||
|
})();
|
||||||
|
|
||||||
|
const post = (type, payload) => parent.postMessage({ type, payload }, PARENT_ORIGIN);
|
||||||
|
const $ = (id) => document.getElementById(id);
|
||||||
|
const $$ = (sel) => document.querySelectorAll(sel);
|
||||||
|
|
||||||
|
let cfg = null;
|
||||||
|
let logs = [];
|
||||||
|
let pendingSave = null;
|
||||||
|
|
||||||
|
function setStatus(text, isError = false) {
|
||||||
|
const el = $('ep_status');
|
||||||
|
el.textContent = text || '';
|
||||||
|
el.className = `status${isError ? ' error' : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSaveButtonBusy(busy) {
|
||||||
|
const btn = $('ep_save');
|
||||||
|
if (!btn) return;
|
||||||
|
if (busy) {
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.dataset.originalText = btn.textContent;
|
||||||
|
btn.textContent = 'Saving...';
|
||||||
|
} else {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = btn.dataset.originalText || 'Save';
|
||||||
|
delete btn.dataset.originalText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPendingSave(requestId, pendingText = 'Saving...') {
|
||||||
|
pendingSave = {
|
||||||
|
requestId,
|
||||||
|
timer: setTimeout(() => {
|
||||||
|
if (!pendingSave || pendingSave.requestId !== requestId) return;
|
||||||
|
pendingSave = null;
|
||||||
|
setSaveButtonBusy(false);
|
||||||
|
setStatus('Save timeout after 3s', true);
|
||||||
|
}, 3000)
|
||||||
|
};
|
||||||
|
setSaveButtonBusy(true);
|
||||||
|
setStatus(pendingText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBadge(enabled) {
|
||||||
|
const badge = $('stateBadge');
|
||||||
|
badge.innerHTML = `<span class="dot${enabled ? ' ok' : ''}"></span><span>${enabled ? 'Enabled' : 'Disabled'}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateTab(tabId) {
|
||||||
|
$$('.tab').forEach((t) => t.classList.toggle('active', t.dataset.tab === tabId));
|
||||||
|
$$('.panel').forEach((p) => p.classList.toggle('active', p.dataset.panel === tabId));
|
||||||
|
if (tabId === 'logs') post('xb-ena:logs-request');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBool(v, fallback = false) {
|
||||||
|
if (v === true || v === false) return v;
|
||||||
|
if (v === 'true') return true;
|
||||||
|
if (v === 'false') return false;
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNum(v, fallback = 0) {
|
||||||
|
const n = Number(v);
|
||||||
|
return Number.isFinite(n) ? n : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrToCsv(arr) {
|
||||||
|
return Array.isArray(arr) ? arr.join(', ') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function csvToArr(text) {
|
||||||
|
return String(text || '').split(',').map((x) => x.trim()).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromptBlockElement(block, idx, total) {
|
||||||
|
const wrap = document.createElement('div');
|
||||||
|
wrap.className = 'prompt-block';
|
||||||
|
|
||||||
|
const head = document.createElement('div');
|
||||||
|
head.className = 'prompt-head';
|
||||||
|
|
||||||
|
const left = document.createElement('div');
|
||||||
|
left.className = 'prompt-head-left';
|
||||||
|
|
||||||
|
const name = document.createElement('input');
|
||||||
|
name.type = 'text';
|
||||||
|
name.placeholder = 'Block name';
|
||||||
|
name.value = block.name || '';
|
||||||
|
name.style.minWidth = '180px';
|
||||||
|
name.addEventListener('change', () => {
|
||||||
|
block.name = name.value || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const role = document.createElement('select');
|
||||||
|
['system', 'user', 'assistant'].forEach((r) => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = r;
|
||||||
|
opt.textContent = r;
|
||||||
|
opt.selected = (block.role || 'system') === r;
|
||||||
|
role.appendChild(opt);
|
||||||
|
});
|
||||||
|
role.addEventListener('change', () => {
|
||||||
|
block.role = role.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
left.append(name, role);
|
||||||
|
|
||||||
|
const right = document.createElement('div');
|
||||||
|
right.style.display = 'flex';
|
||||||
|
right.style.gap = '6px';
|
||||||
|
|
||||||
|
const up = document.createElement('button');
|
||||||
|
up.className = 'btn';
|
||||||
|
up.textContent = 'Up';
|
||||||
|
up.disabled = idx === 0;
|
||||||
|
up.addEventListener('click', () => {
|
||||||
|
if (idx === 0) return;
|
||||||
|
const list = cfg.promptBlocks;
|
||||||
|
[list[idx - 1], list[idx]] = [list[idx], list[idx - 1]];
|
||||||
|
renderPromptList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const down = document.createElement('button');
|
||||||
|
down.className = 'btn';
|
||||||
|
down.textContent = 'Down';
|
||||||
|
down.disabled = idx === total - 1;
|
||||||
|
down.addEventListener('click', () => {
|
||||||
|
if (idx >= total - 1) return;
|
||||||
|
const list = cfg.promptBlocks;
|
||||||
|
[list[idx], list[idx + 1]] = [list[idx + 1], list[idx]];
|
||||||
|
renderPromptList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const del = document.createElement('button');
|
||||||
|
del.className = 'btn';
|
||||||
|
del.textContent = 'Delete';
|
||||||
|
del.addEventListener('click', () => {
|
||||||
|
cfg.promptBlocks.splice(idx, 1);
|
||||||
|
renderPromptList();
|
||||||
|
});
|
||||||
|
|
||||||
|
right.append(up, down, del);
|
||||||
|
|
||||||
|
const content = document.createElement('textarea');
|
||||||
|
content.value = block.content || '';
|
||||||
|
content.placeholder = 'Prompt block content';
|
||||||
|
content.addEventListener('change', () => {
|
||||||
|
block.content = content.value || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
head.append(left, right);
|
||||||
|
wrap.append(head, content);
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplateSelect(selected = '') {
|
||||||
|
const sel = $('ep_tpl_select');
|
||||||
|
sel.textContent = '';
|
||||||
|
const first = document.createElement('option');
|
||||||
|
first.value = '';
|
||||||
|
first.textContent = '-- Select template --';
|
||||||
|
sel.appendChild(first);
|
||||||
|
|
||||||
|
const names = Object.keys(cfg?.promptTemplates || {});
|
||||||
|
names.forEach((name) => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = name;
|
||||||
|
opt.textContent = name;
|
||||||
|
opt.selected = name === selected;
|
||||||
|
sel.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPromptList() {
|
||||||
|
const list = $('ep_prompt_list');
|
||||||
|
list.textContent = '';
|
||||||
|
const blocks = cfg?.promptBlocks || [];
|
||||||
|
|
||||||
|
if (!blocks.length) {
|
||||||
|
const empty = document.createElement('div');
|
||||||
|
empty.className = 'hint';
|
||||||
|
empty.textContent = 'No prompt blocks';
|
||||||
|
list.appendChild(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.forEach((block, idx) => {
|
||||||
|
list.appendChild(createPromptBlockElement(block, idx, blocks.length));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLogs() {
|
||||||
|
const body = $('ep_log_body');
|
||||||
|
body.textContent = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(logs) || logs.length === 0) {
|
||||||
|
const empty = document.createElement('div');
|
||||||
|
empty.className = 'hint';
|
||||||
|
empty.textContent = 'No logs';
|
||||||
|
body.appendChild(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logs.forEach((item) => {
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'log-item';
|
||||||
|
|
||||||
|
const meta = document.createElement('div');
|
||||||
|
meta.className = 'log-meta';
|
||||||
|
const left = document.createElement('span');
|
||||||
|
left.textContent = `${item.time || '-'} | ${item.ok ? 'OK' : 'FAIL'}`;
|
||||||
|
const right = document.createElement('span');
|
||||||
|
right.textContent = item.model || '-';
|
||||||
|
meta.append(left, right);
|
||||||
|
row.appendChild(meta);
|
||||||
|
|
||||||
|
if (item.error) {
|
||||||
|
const err = document.createElement('div');
|
||||||
|
err.className = 'log-error';
|
||||||
|
err.textContent = String(item.error);
|
||||||
|
row.appendChild(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = document.createElement('details');
|
||||||
|
const reqSm = document.createElement('summary');
|
||||||
|
reqSm.textContent = 'Request messages';
|
||||||
|
const reqPre = document.createElement('pre');
|
||||||
|
reqPre.className = 'log-pre';
|
||||||
|
reqPre.textContent = JSON.stringify(item.requestMessages || [], null, 2);
|
||||||
|
req.append(reqSm, reqPre);
|
||||||
|
row.appendChild(req);
|
||||||
|
|
||||||
|
const raw = document.createElement('details');
|
||||||
|
const rawSm = document.createElement('summary');
|
||||||
|
rawSm.textContent = 'Raw reply';
|
||||||
|
const rawPre = document.createElement('pre');
|
||||||
|
rawPre.className = 'log-pre';
|
||||||
|
rawPre.textContent = String(item.rawReply || '');
|
||||||
|
raw.append(rawSm, rawPre);
|
||||||
|
row.appendChild(raw);
|
||||||
|
|
||||||
|
const filtered = document.createElement('details');
|
||||||
|
filtered.open = true;
|
||||||
|
const filteredSm = document.createElement('summary');
|
||||||
|
filteredSm.textContent = 'Filtered reply';
|
||||||
|
const filteredPre = document.createElement('pre');
|
||||||
|
filteredPre.className = 'log-pre';
|
||||||
|
filteredPre.textContent = String(item.filteredReply || '');
|
||||||
|
filtered.append(filteredSm, filteredPre);
|
||||||
|
row.appendChild(filtered);
|
||||||
|
|
||||||
|
body.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyConfig(nextCfg) {
|
||||||
|
cfg = nextCfg || {};
|
||||||
|
logs = Array.isArray(cfg.logs) ? cfg.logs : [];
|
||||||
|
|
||||||
|
$('ep_enabled').value = String(toBool(cfg.enabled, true));
|
||||||
|
$('ep_skip_plot').value = String(toBool(cfg.skipIfPlotPresent, true));
|
||||||
|
$('ep_include_global_wb').value = String(toBool(cfg.includeGlobalWorldbooks, false));
|
||||||
|
$('ep_wb_pos4').value = String(toBool(cfg.excludeWorldbookPosition4, true));
|
||||||
|
$('ep_wb_exclude_names').value = arrToCsv(cfg.worldbookExcludeNames);
|
||||||
|
$('ep_plot_n').value = String(toNum(cfg.plotCount, 2));
|
||||||
|
$('ep_exclude_tags').value = arrToCsv(cfg.chatExcludeTags);
|
||||||
|
$('ep_logs_persist').value = String(toBool(cfg.logsPersist, true));
|
||||||
|
$('ep_logs_max').value = String(toNum(cfg.logsMax, 20));
|
||||||
|
|
||||||
|
const api = cfg.api || {};
|
||||||
|
$('ep_api_channel').value = api.channel || 'openai';
|
||||||
|
$('ep_prefix_mode').value = api.prefixMode || 'auto';
|
||||||
|
$('ep_api_base').value = api.baseUrl || '';
|
||||||
|
$('ep_prefix_custom').value = api.customPrefix || '';
|
||||||
|
$('ep_api_key').value = api.apiKey || '';
|
||||||
|
$('ep_model').value = api.model || '';
|
||||||
|
$('ep_stream').value = String(toBool(api.stream, false));
|
||||||
|
$('ep_temp').value = String(toNum(api.temperature, 1));
|
||||||
|
$('ep_top_p').value = String(toNum(api.top_p, 1));
|
||||||
|
$('ep_top_k').value = String(toNum(api.top_k, 0));
|
||||||
|
$('ep_pp').value = api.presence_penalty ?? '';
|
||||||
|
$('ep_fp').value = api.frequency_penalty ?? '';
|
||||||
|
$('ep_mt').value = api.max_tokens ?? '';
|
||||||
|
|
||||||
|
setBadge(toBool(cfg.enabled, true));
|
||||||
|
|
||||||
|
renderTemplateSelect();
|
||||||
|
renderPromptList();
|
||||||
|
renderLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectPatch() {
|
||||||
|
const next = structuredClone(cfg || {});
|
||||||
|
|
||||||
|
next.enabled = toBool($('ep_enabled').value, true);
|
||||||
|
next.skipIfPlotPresent = toBool($('ep_skip_plot').value, true);
|
||||||
|
next.includeGlobalWorldbooks = toBool($('ep_include_global_wb').value, false);
|
||||||
|
next.excludeWorldbookPosition4 = toBool($('ep_wb_pos4').value, true);
|
||||||
|
next.worldbookExcludeNames = csvToArr($('ep_wb_exclude_names').value);
|
||||||
|
next.plotCount = Math.max(0, Math.floor(toNum($('ep_plot_n').value, 2)));
|
||||||
|
next.chatExcludeTags = csvToArr($('ep_exclude_tags').value);
|
||||||
|
next.logsPersist = toBool($('ep_logs_persist').value, true);
|
||||||
|
next.logsMax = Math.max(1, Math.min(200, Math.floor(toNum($('ep_logs_max').value, 20))));
|
||||||
|
|
||||||
|
next.api = next.api || {};
|
||||||
|
next.api.channel = $('ep_api_channel').value;
|
||||||
|
next.api.prefixMode = $('ep_prefix_mode').value;
|
||||||
|
next.api.baseUrl = $('ep_api_base').value.trim();
|
||||||
|
next.api.customPrefix = $('ep_prefix_custom').value.trim();
|
||||||
|
next.api.apiKey = $('ep_api_key').value;
|
||||||
|
next.api.model = $('ep_model').value.trim();
|
||||||
|
next.api.stream = toBool($('ep_stream').value, false);
|
||||||
|
next.api.temperature = toNum($('ep_temp').value, 1);
|
||||||
|
next.api.top_p = toNum($('ep_top_p').value, 1);
|
||||||
|
next.api.top_k = Math.floor(toNum($('ep_top_k').value, 0));
|
||||||
|
next.api.presence_penalty = $('ep_pp').value.trim();
|
||||||
|
next.api.frequency_penalty = $('ep_fp').value.trim();
|
||||||
|
next.api.max_tokens = $('ep_mt').value.trim();
|
||||||
|
|
||||||
|
next.promptBlocks = Array.isArray(cfg?.promptBlocks) ? cfg.promptBlocks : [];
|
||||||
|
next.promptTemplates = cfg?.promptTemplates || {};
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindUiEvents() {
|
||||||
|
$$('.tab').forEach((tab) => {
|
||||||
|
tab.addEventListener('click', () => activateTab(tab.dataset.tab));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_enabled').addEventListener('change', () => setBadge(toBool($('ep_enabled').value, true)));
|
||||||
|
|
||||||
|
$('ep_add_prompt').addEventListener('click', () => {
|
||||||
|
cfg.promptBlocks = cfg.promptBlocks || [];
|
||||||
|
cfg.promptBlocks.push({
|
||||||
|
id: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
role: 'system',
|
||||||
|
name: 'New block',
|
||||||
|
content: ''
|
||||||
|
});
|
||||||
|
renderPromptList();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_reset_prompt').addEventListener('click', () => {
|
||||||
|
if (!confirm('Reset prompt blocks to default?')) return;
|
||||||
|
if (pendingSave) return;
|
||||||
|
const requestId = `ena_reset_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||||
|
startPendingSave(requestId, 'Resetting prompt blocks...');
|
||||||
|
post('xb-ena:reset-prompt-default', { requestId });
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_tpl_select').addEventListener('change', () => {
|
||||||
|
const name = $('ep_tpl_select').value;
|
||||||
|
if (!name) return;
|
||||||
|
const blocks = cfg?.promptTemplates?.[name];
|
||||||
|
if (!Array.isArray(blocks)) return;
|
||||||
|
cfg.promptBlocks = structuredClone(blocks);
|
||||||
|
renderPromptList();
|
||||||
|
setStatus(`Template loaded: ${name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_tpl_save').addEventListener('click', () => {
|
||||||
|
const name = $('ep_tpl_select').value;
|
||||||
|
if (!name) return setStatus('Select a template first', true);
|
||||||
|
cfg.promptTemplates = cfg.promptTemplates || {};
|
||||||
|
cfg.promptTemplates[name] = structuredClone(cfg.promptBlocks || []);
|
||||||
|
renderTemplateSelect(name);
|
||||||
|
setStatus(`Template saved: ${name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_tpl_saveas').addEventListener('click', () => {
|
||||||
|
const name = prompt('Template name');
|
||||||
|
if (!name) return;
|
||||||
|
cfg.promptTemplates = cfg.promptTemplates || {};
|
||||||
|
cfg.promptTemplates[name] = structuredClone(cfg.promptBlocks || []);
|
||||||
|
renderTemplateSelect(name);
|
||||||
|
setStatus(`Template saved as: ${name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_tpl_delete').addEventListener('click', () => {
|
||||||
|
const name = $('ep_tpl_select').value;
|
||||||
|
if (!name) return;
|
||||||
|
if (!confirm(`Delete template \"${name}\"?`)) return;
|
||||||
|
delete cfg.promptTemplates[name];
|
||||||
|
renderTemplateSelect('');
|
||||||
|
setStatus(`Template deleted: ${name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_open_logs').addEventListener('click', () => post('xb-ena:logs-request'));
|
||||||
|
$('ep_log_clear').addEventListener('click', () => post('xb-ena:logs-clear'));
|
||||||
|
$('ep_log_export').addEventListener('click', () => {
|
||||||
|
const blob = new Blob([JSON.stringify(logs || [], null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `ena-planner-logs-${Date.now()}.json`;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ep_fetch_models').addEventListener('click', () => post('xb-ena:fetch-models'));
|
||||||
|
$('ep_test_conn').addEventListener('click', () => post('xb-ena:fetch-models'));
|
||||||
|
$('ep_debug_worldbook').addEventListener('click', () => post('xb-ena:debug-worldbook'));
|
||||||
|
$('ep_debug_char').addEventListener('click', () => post('xb-ena:debug-char'));
|
||||||
|
$('ep_test_planner').addEventListener('click', () => post('xb-ena:run-test', { text: '(test input) Please plan the next story step.' }));
|
||||||
|
|
||||||
|
$('ep_save').addEventListener('click', () => {
|
||||||
|
if (pendingSave) return;
|
||||||
|
const requestId = `ena_save_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||||
|
const patch = collectPatch();
|
||||||
|
startPendingSave(requestId, 'Saving...');
|
||||||
|
post('xb-ena:save-config', { requestId, patch });
|
||||||
|
});
|
||||||
|
$('ep_close').addEventListener('click', () => post('xb-ena:close'));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', (ev) => {
|
||||||
|
if (ev.origin !== PARENT_ORIGIN) return;
|
||||||
|
const { type, payload } = ev.data || {};
|
||||||
|
|
||||||
|
if (type === 'xb-ena:config' || type === 'xb-ena:config-saved') {
|
||||||
|
applyConfig(payload || {});
|
||||||
|
if (type === 'xb-ena:config-saved') {
|
||||||
|
const requestId = payload?.requestId || '';
|
||||||
|
if (pendingSave && pendingSave.requestId === requestId) {
|
||||||
|
clearTimeout(pendingSave.timer);
|
||||||
|
pendingSave = null;
|
||||||
|
setSaveButtonBusy(false);
|
||||||
|
setStatus('Saved');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type === 'xb-ena:config-save-error') {
|
||||||
|
const requestId = payload?.requestId || '';
|
||||||
|
if (pendingSave && pendingSave.requestId === requestId) {
|
||||||
|
clearTimeout(pendingSave.timer);
|
||||||
|
pendingSave = null;
|
||||||
|
setSaveButtonBusy(false);
|
||||||
|
}
|
||||||
|
setStatus(payload?.message || 'Save failed', true);
|
||||||
|
} else if (type === 'xb-ena:test-done') {
|
||||||
|
setStatus('Planner test completed');
|
||||||
|
} else if (type === 'xb-ena:test-error') {
|
||||||
|
setStatus(payload?.message || 'Planner test failed', true);
|
||||||
|
} else if (type === 'xb-ena:logs') {
|
||||||
|
logs = Array.isArray(payload?.logs) ? payload.logs : [];
|
||||||
|
renderLogs();
|
||||||
|
} else if (type === 'xb-ena:models') {
|
||||||
|
const models = Array.isArray(payload?.models) ? payload.models : [];
|
||||||
|
const preview = models.slice(0, 8).join(', ');
|
||||||
|
$('ep_models_preview').textContent = models.length ? `${models.length} models: ${preview}` : 'No models';
|
||||||
|
setStatus(`Fetched ${models.length} models`);
|
||||||
|
} else if (type === 'xb-ena:models-error') {
|
||||||
|
setStatus(payload?.message || 'Model fetch failed', true);
|
||||||
|
} else if (type === 'xb-ena:debug-output') {
|
||||||
|
const out = $('ep_debug_output');
|
||||||
|
out.style.display = '';
|
||||||
|
out.textContent = String(payload?.output || '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bindUiEvents();
|
||||||
|
post('xb-ena:ready');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1275,7 +1275,7 @@ select.input { cursor: pointer; }
|
|||||||
<div class="tip-box" style="margin-bottom: 16px;">
|
<div class="tip-box" style="margin-bottom: 16px;">
|
||||||
<i class="fa-solid fa-info-circle"></i>
|
<i class="fa-solid fa-info-circle"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>试用音色</strong> — 无需配置,使用插件服务器(11个音色)<br>
|
<strong>试用音色</strong> — 无需配置,使用插件服务器(21个音色)<br>
|
||||||
<strong>鉴权音色</strong> — 需配置火山引擎 API(200+ 音色 + 复刻)
|
<strong>鉴权音色</strong> — 需配置火山引擎 API(200+ 音色 + 复刻)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1719,6 +1719,7 @@ let selectedTrialVoiceValue = '';
|
|||||||
let selectedAuthVoiceValue = '';
|
let selectedAuthVoiceValue = '';
|
||||||
let editingVoiceValue = null;
|
let editingVoiceValue = null;
|
||||||
let activeSaveBtn = null;
|
let activeSaveBtn = null;
|
||||||
|
let pendingSaveRequest = null;
|
||||||
|
|
||||||
const TRIAL_VOICES = [
|
const TRIAL_VOICES = [
|
||||||
{ key: 'female_1', name: '晓晓', tag: '温暖百变', gender: 'female' },
|
{ key: 'female_1', name: '晓晓', tag: '温暖百变', gender: 'female' },
|
||||||
@@ -1791,6 +1792,25 @@ function handleSaveResult(success) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requestSaveConfig(form, btn = null) {
|
||||||
|
const requestId = `tts_save_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||||
|
|
||||||
|
if (pendingSaveRequest?.timer) clearTimeout(pendingSaveRequest.timer);
|
||||||
|
if (btn) setSavingState(btn);
|
||||||
|
|
||||||
|
pendingSaveRequest = {
|
||||||
|
requestId,
|
||||||
|
timer: setTimeout(() => {
|
||||||
|
if (!pendingSaveRequest || pendingSaveRequest.requestId !== requestId) return;
|
||||||
|
pendingSaveRequest = null;
|
||||||
|
handleSaveResult(false);
|
||||||
|
post('xb-tts:toast', { type: 'error', message: '保存超时(3秒)' });
|
||||||
|
}, 3000),
|
||||||
|
};
|
||||||
|
|
||||||
|
post('xb-tts:save-config', { requestId, patch: form });
|
||||||
|
}
|
||||||
|
|
||||||
function setTestStatus(elId, status, text) {
|
function setTestStatus(elId, status, text) {
|
||||||
const el = $(elId);
|
const el = $(elId);
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -2060,7 +2080,7 @@ function bindMyVoiceEvents(listEl) {
|
|||||||
const input = btn.closest('.voice-item').querySelector('.voice-edit-input');
|
const input = btn.closest('.voice-item').querySelector('.voice-edit-input');
|
||||||
if (item && input?.value?.trim()) {
|
if (item && input?.value?.trim()) {
|
||||||
item.name = input.value.trim();
|
item.name = input.value.trim();
|
||||||
post('xb-tts:save-config', collectForm());
|
requestSaveConfig(collectForm());
|
||||||
}
|
}
|
||||||
editingVoiceValue = null;
|
editingVoiceValue = null;
|
||||||
renderMyVoiceList();
|
renderMyVoiceList();
|
||||||
@@ -2090,7 +2110,7 @@ function bindMyVoiceEvents(listEl) {
|
|||||||
renderTrialVoiceList();
|
renderTrialVoiceList();
|
||||||
renderAuthVoiceList();
|
renderAuthVoiceList();
|
||||||
updateCurrentVoiceDisplay();
|
updateCurrentVoiceDisplay();
|
||||||
post('xb-tts:save-config', collectForm());
|
requestSaveConfig(collectForm());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2313,11 +2333,17 @@ window.addEventListener('message', ev => {
|
|||||||
fillForm(payload);
|
fillForm(payload);
|
||||||
break;
|
break;
|
||||||
case 'xb-tts:config-saved':
|
case 'xb-tts:config-saved':
|
||||||
|
if (pendingSaveRequest?.requestId && payload?.requestId && pendingSaveRequest.requestId !== payload.requestId) break;
|
||||||
|
if (pendingSaveRequest?.timer) clearTimeout(pendingSaveRequest.timer);
|
||||||
|
pendingSaveRequest = null;
|
||||||
fillForm(payload);
|
fillForm(payload);
|
||||||
handleSaveResult(true);
|
handleSaveResult(true);
|
||||||
post('xb-tts:toast', { type: 'success', message: '配置已保存' });
|
post('xb-tts:toast', { type: 'success', message: '配置已保存' });
|
||||||
break;
|
break;
|
||||||
case 'xb-tts:config-save-error':
|
case 'xb-tts:config-save-error':
|
||||||
|
if (pendingSaveRequest?.requestId && payload?.requestId && pendingSaveRequest.requestId !== payload.requestId) break;
|
||||||
|
if (pendingSaveRequest?.timer) clearTimeout(pendingSaveRequest.timer);
|
||||||
|
pendingSaveRequest = null;
|
||||||
handleSaveResult(false);
|
handleSaveResult(false);
|
||||||
post('xb-tts:toast', { type: 'error', message: payload?.message || '保存失败' });
|
post('xb-tts:toast', { type: 'error', message: payload?.message || '保存失败' });
|
||||||
break;
|
break;
|
||||||
@@ -2432,7 +2458,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
$$('.voice-tab')[0].classList.add('active');
|
$$('.voice-tab')[0].classList.add('active');
|
||||||
$('panel-myVoice').classList.add('active');
|
$('panel-myVoice').classList.add('active');
|
||||||
|
|
||||||
post('xb-tts:save-config', collectForm());
|
requestSaveConfig(collectForm());
|
||||||
post('xb-tts:toast', { type: 'success', message: `已添加:${name}` });
|
post('xb-tts:toast', { type: 'success', message: `已添加:${name}` });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2456,7 +2482,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
$$('.voice-tab')[0].classList.add('active');
|
$$('.voice-tab')[0].classList.add('active');
|
||||||
$('panel-myVoice').classList.add('active');
|
$('panel-myVoice').classList.add('active');
|
||||||
|
|
||||||
post('xb-tts:save-config', collectForm());
|
requestSaveConfig(collectForm());
|
||||||
post('xb-tts:toast', { type: 'success', message: `已添加:${name}` });
|
post('xb-tts:toast', { type: 'success', message: `已添加:${name}` });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2475,12 +2501,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
renderMyVoiceList();
|
renderMyVoiceList();
|
||||||
updateCurrentVoiceDisplay();
|
updateCurrentVoiceDisplay();
|
||||||
post('xb-tts:save-config', collectForm());
|
requestSaveConfig(collectForm());
|
||||||
post('xb-tts:toast', { type: 'success', message: `已添加:${name || id}` });
|
post('xb-tts:toast', { type: 'success', message: `已添加:${name || id}` });
|
||||||
});
|
});
|
||||||
|
|
||||||
['saveConfigBtn', 'saveVoiceBtn', 'saveAdvancedBtn', 'saveCacheBtn'].forEach(id => {
|
['saveConfigBtn', 'saveVoiceBtn', 'saveAdvancedBtn', 'saveCacheBtn'].forEach(id => {
|
||||||
$(id)?.addEventListener('click', () => { setSavingState($(id)); post('xb-tts:save-config', collectForm()); });
|
$(id)?.addEventListener('click', () => { requestSaveConfig(collectForm(), $(id)); });
|
||||||
});
|
});
|
||||||
|
|
||||||
$('cacheRefreshBtn').addEventListener('click', () => post('xb-tts:cache-refresh'));
|
$('cacheRefreshBtn').addEventListener('click', () => post('xb-tts:cache-refresh'));
|
||||||
|
|||||||
@@ -1079,15 +1079,17 @@ async function handleIframeMessage(ev) {
|
|||||||
closeSettings();
|
closeSettings();
|
||||||
break;
|
break;
|
||||||
case 'xb-tts:save-config': {
|
case 'xb-tts:save-config': {
|
||||||
const ok = await saveConfig(payload);
|
const requestId = payload?.requestId || '';
|
||||||
|
const patch = (payload && typeof payload.patch === 'object') ? payload.patch : payload;
|
||||||
|
const ok = await saveConfig(patch);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
const cacheStats = await getCacheStatsSafe();
|
const cacheStats = await getCacheStatsSafe();
|
||||||
postToIframe(iframe, { type: 'xb-tts:config-saved', payload: { ...config, cacheStats } });
|
postToIframe(iframe, { type: 'xb-tts:config-saved', payload: { ...config, cacheStats, requestId } });
|
||||||
updateAutoSpeakAll();
|
updateAutoSpeakAll();
|
||||||
updateSpeedAll();
|
updateSpeedAll();
|
||||||
updateVoiceAll();
|
updateVoiceAll();
|
||||||
} else {
|
} else {
|
||||||
postToIframe(iframe, { type: 'xb-tts:config-save-error', payload: { message: '保存失败' } });
|
postToIframe(iframe, { type: 'xb-tts:config-save-error', payload: { message: '保存失败', requestId } });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,14 @@
|
|||||||
<input type="checkbox" id="xiaobaix_story_outline_enabled" />
|
<input type="checkbox" id="xiaobaix_story_outline_enabled" />
|
||||||
<label for="xiaobaix_story_outline_enabled" class="has-tooltip" data-tooltip="在X按钮区域添加地图图标,点击可打开可视化剧情地图编辑器">小白板</label>
|
<label for="xiaobaix_story_outline_enabled" class="has-tooltip" data-tooltip="在X按钮区域添加地图图标,点击可打开可视化剧情地图编辑器">小白板</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<input type="checkbox" id="xiaobaix_ena_planner_enabled" />
|
||||||
|
<label for="xiaobaix_ena_planner_enabled" class="has-tooltip" data-tooltip="发送前剧情规划,自动注入 plot/note">剧情规划</label>
|
||||||
|
<button id="xiaobaix_ena_planner_open_settings" class="menu_button menu_button_icon" type="button" style="margin-left:auto;" title="打开剧情规划设置">
|
||||||
|
<i class="fa-solid fa-compass-drafting"></i>
|
||||||
|
<small>规划设置</small>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="section-divider">变量控制</div>
|
<div class="section-divider">变量控制</div>
|
||||||
<hr class="sysHR" />
|
<hr class="sysHR" />
|
||||||
@@ -519,14 +527,15 @@
|
|||||||
audio: 'xiaobaix_audio_enabled',
|
audio: 'xiaobaix_audio_enabled',
|
||||||
storySummary: 'xiaobaix_story_summary_enabled',
|
storySummary: 'xiaobaix_story_summary_enabled',
|
||||||
tts: 'xiaobaix_tts_enabled',
|
tts: 'xiaobaix_tts_enabled',
|
||||||
|
enaPlanner: 'xiaobaix_ena_planner_enabled',
|
||||||
storyOutline: 'xiaobaix_story_outline_enabled',
|
storyOutline: 'xiaobaix_story_outline_enabled',
|
||||||
useBlob: 'xiaobaix_use_blob',
|
useBlob: 'xiaobaix_use_blob',
|
||||||
wrapperIframe: 'Wrapperiframe',
|
wrapperIframe: 'Wrapperiframe',
|
||||||
renderEnabled: 'xiaobaix_render_enabled',
|
renderEnabled: 'xiaobaix_render_enabled',
|
||||||
};
|
};
|
||||||
const DEFAULTS_ON = ['templateEditor', 'tasks', 'variablesCore', 'audio', 'storySummary', 'recorded'];
|
const DEFAULTS_ON = ['templateEditor', 'tasks', 'variablesCore', 'audio', 'storySummary', 'recorded'];
|
||||||
const DEFAULTS_OFF = ['preview', 'scriptAssistant', 'immersive', 'variablesPanel', 'fourthWall', 'storyOutline', 'novelDraw', 'tts'];
|
const DEFAULTS_OFF = ['preview', 'scriptAssistant', 'immersive', 'variablesPanel', 'fourthWall', 'storyOutline', 'novelDraw', 'tts', 'enaPlanner'];
|
||||||
const MODULE_KEYS = ['templateEditor', 'tasks', 'fourthWall', 'variablesCore', 'recorded', 'preview', 'scriptAssistant', 'immersive', 'variablesPanel', 'audio', 'storySummary', 'storyOutline', 'novelDraw', 'tts'];
|
const MODULE_KEYS = ['templateEditor', 'tasks', 'fourthWall', 'variablesCore', 'recorded', 'preview', 'scriptAssistant', 'immersive', 'variablesPanel', 'audio', 'storySummary', 'storyOutline', 'novelDraw', 'tts', 'enaPlanner'];
|
||||||
function setModuleEnabled(key, enabled) {
|
function setModuleEnabled(key, enabled) {
|
||||||
try {
|
try {
|
||||||
if (!extension_settings[EXT_ID][key]) extension_settings[EXT_ID][key] = {};
|
if (!extension_settings[EXT_ID][key]) extension_settings[EXT_ID][key] = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user