@musnows/scriverse 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai.js +185 -17
- package/dist/ai.js.map +1 -1
- package/dist/app.js +2 -0
- package/dist/app.js.map +1 -1
- package/dist/database.js +48 -2
- package/dist/database.js.map +1 -1
- package/dist/im-orchestrator.js +118 -50
- package/dist/im-orchestrator.js.map +1 -1
- package/dist/im.js +79 -16
- package/dist/im.js.map +1 -1
- package/dist/public/app.js +83 -5
- package/dist/public/im.d.ts +4 -0
- package/dist/public/im.js +126 -5
- package/dist/public/index.html +2 -2
- package/dist/public/styles.css +32 -0
- package/dist/store.js +12 -5
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/public/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildRelationshipGraph, createGalaxyRenderer, normalizeGalaxyFrameRate, normalizeGalaxyMotionMode, renderRelationshipMindMap } from "/relationship-graph.js?v=20260817-relationship-canvas-scale-v1&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1";
|
|
2
2
|
import { formatDateTime, normalizeParagraphSpacing } from "/text-formatting.js?v=20260713-saved-at-seconds";
|
|
3
3
|
import { renderMarkdown } from "/markdown.js?v=20260830-adjacent-blockquotes-v1";
|
|
4
|
-
import { createImWorkspace } from "/im.js?v=
|
|
4
|
+
import { createImWorkspace } from "/im.js?v=20260904-im-judge-outcomes-v106";
|
|
5
5
|
import { findAiMention, listAiMentionOptions, mergeAiReferenceScope, userMessageMentionNames } from "/ai-mentions.js?v=20260811-user-message-mentions-v1";
|
|
6
6
|
import { applyAiSkillCommand, findAiSkillCommand, listAiSkillOptions } from "/ai-skill-menu.js?v=20260830-ai-skill-slash-menu-v1";
|
|
7
7
|
import {
|
|
@@ -9825,7 +9825,7 @@ const moduleMeta = {
|
|
|
9825
9825
|
comments: ["正文协作", "正文评论与待办", "集中查看并处理当前作品所有章节的评论与待办。", ""],
|
|
9826
9826
|
reviews: ["作者决策", "审核队列", "集中处理冲突、候选设定、低置信度关系和时间问题。", "新增审核项"],
|
|
9827
9827
|
tasks: ["AI 深度分析", "AI 分析中心", "对全书或指定章节运行人物关系、世界观、设定、事件与一致性分析。", "开始 AI 分析"],
|
|
9828
|
-
"ai-settings": ["书籍提示词", "本书 AI 设置", "
|
|
9828
|
+
"ai-settings": ["书籍提示词", "本书 AI 设置", "本书系统提示词默认追加在内置提示词和平台全局提示词之后;可在折叠的高级设置中改为彻底覆写。任务默认模型只作用于当前作品。", "保存设置"]
|
|
9829
9829
|
};
|
|
9830
9830
|
|
|
9831
9831
|
async function showModule(module) {
|
|
@@ -13423,6 +13423,53 @@ function startBackgroundTaskCenter(workId) {
|
|
|
13423
13423
|
void refreshBackgroundTaskCenter();
|
|
13424
13424
|
}
|
|
13425
13425
|
|
|
13426
|
+
function systemPromptOverrideDetails({ inputId, checked, scope }) {
|
|
13427
|
+
const platformScope = scope === "platform";
|
|
13428
|
+
const consequence = platformScope
|
|
13429
|
+
? "保存后,普通 AI 请求只会把上方平台提示词作为 system 消息;Scriverse 内置规则、本书提示词、工具指引和时间信息都不会发送。"
|
|
13430
|
+
: "保存后,本书的普通 AI 请求只会把上方本书提示词作为 system 消息;Scriverse 内置规则、平台提示词、工具指引和时间信息都不会发送。";
|
|
13431
|
+
return `<details class="system-prompt-override-settings${checked ? " is-enabled" : ""}" ${checked ? "open" : ""}><summary><span>高级设置</span><strong data-system-prompt-override-state>${checked ? "已选择覆写" : "默认追加"}</strong></summary><div class="system-prompt-override-body"><label class="checkbox-field config-checkbox-field"><input id="${inputId}" type="checkbox" ${checked ? "checked" : ""} aria-describedby="${inputId}-warning">彻底覆写系统提示词</label><div id="${inputId}-warning" class="system-prompt-override-warning" role="alert"><strong>高风险设置</strong><p>${consequence} 空提示词会让请求不携带 system 消息,可能导致上下文、工具和安全约束失效。</p></div></div></details>`;
|
|
13432
|
+
}
|
|
13433
|
+
|
|
13434
|
+
function syncSystemPromptOverridePresentation(input) {
|
|
13435
|
+
const details = input.closest(".system-prompt-override-settings");
|
|
13436
|
+
details?.classList.toggle("is-enabled", input.checked);
|
|
13437
|
+
const stateLabel = details?.querySelector("[data-system-prompt-override-state]");
|
|
13438
|
+
if (stateLabel) stateLabel.textContent = input.checked ? "已选择覆写" : "默认追加";
|
|
13439
|
+
}
|
|
13440
|
+
|
|
13441
|
+
const systemPromptOverrideConfirmationByInput = new WeakMap();
|
|
13442
|
+
|
|
13443
|
+
async function confirmSystemPromptOverride(input, scopeLabel) {
|
|
13444
|
+
if (!input.checked) {
|
|
13445
|
+
syncSystemPromptOverridePresentation(input);
|
|
13446
|
+
return;
|
|
13447
|
+
}
|
|
13448
|
+
const pendingConfirmation = systemPromptOverrideConfirmationByInput.get(input);
|
|
13449
|
+
if (pendingConfirmation) return pendingConfirmation;
|
|
13450
|
+
input.disabled = true;
|
|
13451
|
+
const confirmation = (async () => {
|
|
13452
|
+
const confirmed = await confirmToast(
|
|
13453
|
+
`开启后,${scopeLabel}提示词将彻底替换 Scriverse 内置系统提示词,模型不会收到内置规则、工具指引和其他追加提示。错误配置可能导致功能异常或安全约束失效,确认继续吗?`,
|
|
13454
|
+
{ title: "确认开启系统提示词覆写", confirmLabel: "确认开启" }
|
|
13455
|
+
);
|
|
13456
|
+
if (!confirmed) input.checked = false;
|
|
13457
|
+
syncSystemPromptOverridePresentation(input);
|
|
13458
|
+
})().finally(() => {
|
|
13459
|
+
input.disabled = false;
|
|
13460
|
+
if (systemPromptOverrideConfirmationByInput.get(input) === confirmation) {
|
|
13461
|
+
systemPromptOverrideConfirmationByInput.delete(input);
|
|
13462
|
+
}
|
|
13463
|
+
});
|
|
13464
|
+
systemPromptOverrideConfirmationByInput.set(input, confirmation);
|
|
13465
|
+
return confirmation;
|
|
13466
|
+
}
|
|
13467
|
+
|
|
13468
|
+
async function waitForSystemPromptOverrideConfirmation(input) {
|
|
13469
|
+
const confirmation = systemPromptOverrideConfirmationByInput.get(input);
|
|
13470
|
+
if (confirmation) await confirmation;
|
|
13471
|
+
}
|
|
13472
|
+
|
|
13426
13473
|
async function renderPlatformAiConfig() {
|
|
13427
13474
|
const [providers, models, settings, protocolOptions] = await Promise.all([
|
|
13428
13475
|
api("/api/platform/ai/providers"),
|
|
@@ -13440,12 +13487,23 @@ async function renderPlatformAiConfig() {
|
|
|
13440
13487
|
const available = isSelectableModel({ ...model, providerStatus: provider?.status, providerConnectionStatus: provider?.connectionStatus });
|
|
13441
13488
|
return `<option value="${esc(model.id)}" ${model.id === settings.imageToolModelId ? "selected" : ""} ${available || model.id === settings.imageToolModelId ? "" : "disabled"}>${esc(`${available ? "" : "不可用 · "}${modelOptionLabel({ ...model, providerName: model.providerName || provider?.name })}`)}</option>`;
|
|
13442
13489
|
}).join("");
|
|
13443
|
-
host.innerHTML = `<section class="config-section platform-system-prompt-section"><div class="config-section-header"><div><h2>平台全局系统提示词</h2><p
|
|
13490
|
+
host.innerHTML = `<section class="config-section platform-system-prompt-section"><div class="config-section-header"><div><h2>平台全局系统提示词</h2><p>默认追加在内置系统提示词之后,并在所有作品的专属提示词之前发送给模型。</p></div></div><div class="field-label"><textarea id="platform-system-prompt" rows="7" aria-label="全局系统提示词" placeholder="例如:默认使用简体中文,避免代替作者做最终决定。">${esc(settings.systemPrompt)}</textarea></div>${systemPromptOverrideDetails({ inputId: "platform-system-prompt-override", checked: settings.systemPromptOverride === true, scope: "platform" })}<div class="card-actions"><button id="save-platform-system-prompt" class="primary-button">保存全局提示词</button></div></section><section class="config-section platform-image-tool-section"><div class="config-section-header"><div><h2>多模态读图默认模型</h2><p>Agent 的 image 工具使用这里配置的模型读取设定库图片;作品可以在自己的 AI 设置中覆盖此选择。</p></div></div><div class="platform-image-tool-panel"><label class="platform-image-tool-field"><span>当前平台默认模型</span><select id="platform-image-tool-model" aria-label="平台多模态读图默认模型"><option value="">未配置</option>${imageModelOptions}</select></label><button id="save-platform-image-tool-model" class="ghost-button config-save-button" type="button">保存默认模型</button></div></section><section class="config-section platform-stream-timeout-section"><div class="config-section-header"><div><h2>AI 流事件空闲超时</h2><p>首个流事件或相邻流事件在此时间内没有新数据时,请求会被关闭。默认 90 秒,最低 30 秒,最高 600 秒。</p></div></div><div class="platform-stream-timeout-panel"><label class="platform-stream-timeout-field"><span>超时时间(秒)</span><input id="platform-ai-stream-idle-timeout" type="number" min="30" max="600" step="1" value="${esc(String(settings.streamIdleTimeoutSeconds ?? 90))}" aria-label="AI 流事件空闲超时时间(秒)"></label><button id="save-platform-ai-stream-idle-timeout" class="ghost-button config-save-button" type="button">保存流超时设置</button></div></section><section class="config-section platform-providers-section"><div class="config-section-header"><div><h2>模型供应商配置</h2><p>管理供应商连接、模型列表和连接状态;模型的多模态能力在对应模型配置中设置。</p></div></div>${renderProviderCards(providers, models, protocolOptions)}</section>`;
|
|
13491
|
+
$("#platform-system-prompt-override").addEventListener("change", (event) => {
|
|
13492
|
+
void confirmSystemPromptOverride(event.currentTarget, "平台全局");
|
|
13493
|
+
});
|
|
13444
13494
|
$("#save-platform-system-prompt").addEventListener("click", async () => {
|
|
13445
13495
|
const button = $("#save-platform-system-prompt");
|
|
13446
13496
|
button.disabled = true;
|
|
13447
13497
|
try {
|
|
13448
|
-
|
|
13498
|
+
const overrideInput = $("#platform-system-prompt-override");
|
|
13499
|
+
await waitForSystemPromptOverrideConfirmation(overrideInput);
|
|
13500
|
+
await api("/api/platform/ai/settings", {
|
|
13501
|
+
method: "PATCH",
|
|
13502
|
+
body: {
|
|
13503
|
+
systemPrompt: $("#platform-system-prompt").value,
|
|
13504
|
+
systemPromptOverride: overrideInput.checked
|
|
13505
|
+
}
|
|
13506
|
+
});
|
|
13449
13507
|
toast("平台全局系统提示词已保存");
|
|
13450
13508
|
} catch (error) {
|
|
13451
13509
|
toast(error.message, "error");
|
|
@@ -13861,6 +13919,18 @@ async function renderBookAiSettings() {
|
|
|
13861
13919
|
const available = isAvailableConfiguredModel(model);
|
|
13862
13920
|
return `<option value="${esc(model.id)}" ${model.id === selectedId ? "selected" : ""} ${available || model.id === selectedId ? "" : "disabled"}>${esc(`${available ? "" : "不可用 · "}${modelOptionLabel(model)}`)}</option>`;
|
|
13863
13921
|
}).join("");
|
|
13922
|
+
const workSystemPrompt = host.querySelector("#work-system-prompt");
|
|
13923
|
+
const workSystemPromptDescription = workSystemPromptSection?.querySelector(".config-section-header p");
|
|
13924
|
+
if (workSystemPromptDescription) {
|
|
13925
|
+
workSystemPromptDescription.textContent = `默认追加在内置系统提示词和平台全局系统提示词之后,只影响《${state.work.title}》的 AI 请求。`;
|
|
13926
|
+
}
|
|
13927
|
+
workSystemPrompt?.closest(".field-label")?.insertAdjacentHTML(
|
|
13928
|
+
"afterend",
|
|
13929
|
+
systemPromptOverrideDetails({ inputId: "work-system-prompt-override", checked: settings.systemPromptOverride === true, scope: "work" })
|
|
13930
|
+
);
|
|
13931
|
+
$("#work-system-prompt-override").addEventListener("change", (event) => {
|
|
13932
|
+
void confirmSystemPromptOverride(event.currentTarget, "本书");
|
|
13933
|
+
});
|
|
13864
13934
|
const semanticSection = `<section id="semantic-search-settings" class="config-section semantic-search-settings"><div class="config-section-header"><div><h2>主动语义检索(RAG)</h2><p>与拼音索引并列维护。Embedding 和 rerank 复用平台供应商的端点与凭证保险库;普通聊天、续写、润色和分析不会自动调用此通道。</p></div></div><div class="semantic-settings-grid"><label class="checkbox-field config-checkbox-field semantic-enabled-field"><input id="semantic-search-enabled" type="checkbox" ${settings.semanticSearchEnabled ? "checked" : ""}>启用主动语义检索</label><label>Embedding 模型<select id="semantic-embedding-model" aria-label="RAG Embedding 模型"><option value="">请选择 embedding 模型</option>${semanticModelOptions("embedding", settings.semanticEmbeddingModelId)}</select></label><label>Rerank 模型(可选)<select id="semantic-rerank-model" aria-label="RAG Rerank 模型"><option value="">不使用 rerank</option>${semanticModelOptions("rerank", settings.semanticRerankModelId)}</select></label><label>向量维度<input id="semantic-vector-dimension" type="number" min="1" max="65536" value="${esc(String(settings.semanticVectorDimension ?? 1024))}"></label><label>语义召回数量<input id="semantic-recall-limit" type="number" min="1" max="200" value="${esc(String(settings.semanticRecallLimit ?? 20))}"></label><label>展示结果数量<input id="semantic-result-limit" type="number" min="1" max="100" value="${esc(String(settings.semanticResultLimit ?? 12))}"></label><label>上下文预算(Token)<input id="semantic-budget-tokens" type="number" min="256" max="100000" value="${esc(String(settings.semanticBudgetTokens ?? 4000))}"></label><label>RRF 语义通道权重<input id="semantic-channel-weight" type="number" min="0.1" max="5" step="0.1" value="${esc(String(settings.semanticChannelWeight ?? 1))}"></label></div><p class="usage-measurement-note">API Key 由所选模型所属供应商的凭证保险库加密保存;此页面不会读取或返回明文密钥。更换端点、模型、维度或分片规则后必须完整重建,旧向量不会继续参与检索。</p><div id="semantic-search-index-status" role="status" aria-live="polite">${semanticIndexStatusMarkup(semanticIndex)}</div><div class="relationship-index-actions"><button id="save-semantic-search-settings" class="primary-button config-save-button" type="button">保存 RAG 配置</button><button id="sync-semantic-search-index" class="ghost-button config-save-button" type="button">同步增量</button><button id="refresh-semantic-search-index" class="ghost-button" type="button">刷新状态</button><button id="rebuild-semantic-search-index" class="ghost-button config-save-button" type="button">完整重建 RAG</button></div></section>`;
|
|
13865
13935
|
const relationshipSection = [...host.querySelectorAll(".config-section")].find((section) => section.querySelector("h2")?.textContent === "人物关系拼音索引");
|
|
13866
13936
|
relationshipSection?.insertAdjacentHTML("afterend", semanticSection);
|
|
@@ -14123,7 +14193,15 @@ async function renderBookAiSettings() {
|
|
|
14123
14193
|
const button = $("#save-work-system-prompt");
|
|
14124
14194
|
button.disabled = true;
|
|
14125
14195
|
try {
|
|
14126
|
-
|
|
14196
|
+
const overrideInput = $("#work-system-prompt-override");
|
|
14197
|
+
await waitForSystemPromptOverrideConfirmation(overrideInput);
|
|
14198
|
+
await api(`/api/works/${state.work.id}/ai-settings`, {
|
|
14199
|
+
method: "PATCH",
|
|
14200
|
+
body: {
|
|
14201
|
+
systemPrompt: $("#work-system-prompt").value,
|
|
14202
|
+
systemPromptOverride: overrideInput.checked
|
|
14203
|
+
}
|
|
14204
|
+
});
|
|
14127
14205
|
toast("本书系统提示词已保存");
|
|
14128
14206
|
setAiContextMeter(null);
|
|
14129
14207
|
} catch (error) {
|
package/dist/public/im.d.ts
CHANGED
|
@@ -42,6 +42,10 @@ export function mergeImFailedReplyPages(
|
|
|
42
42
|
...pages: Array<Array<Record<string, unknown>>>
|
|
43
43
|
): Array<Record<string, unknown>>;
|
|
44
44
|
|
|
45
|
+
export function mergeImTriggeredTurnPages(
|
|
46
|
+
...pages: Array<Array<Record<string, unknown>>>
|
|
47
|
+
): Array<Record<string, unknown>>;
|
|
48
|
+
|
|
45
49
|
export function imMessageSequenceBounds(messages: Array<Record<string, unknown>>): {
|
|
46
50
|
minimum: number;
|
|
47
51
|
maximum: number;
|
package/dist/public/im.js
CHANGED
|
@@ -134,10 +134,14 @@ export function mergeImMessagePages(previousMessages, ...nextPages) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export function mergeImFailedReplyPages(...pages) {
|
|
137
|
+
return mergeImTriggeredTurnPages(...pages);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function mergeImTriggeredTurnPages(...pages) {
|
|
137
141
|
const byId = new Map();
|
|
138
142
|
for (const reply of pages.flatMap(array)) byId.set(String(reply?.id || ""), reply);
|
|
139
143
|
byId.delete("");
|
|
140
|
-
return [...byId.values()].sort((left, right) => Number(left.triggerSequence) - Number(right.triggerSequence)
|
|
144
|
+
return [...byId.values()].sort((left, right) => Number(left.triggerSequence ?? left.sourceSequence) - Number(right.triggerSequence ?? right.sourceSequence)
|
|
141
145
|
|| String(left.createdAt || "").localeCompare(String(right.createdAt || ""))
|
|
142
146
|
|| String(left.id || "").localeCompare(String(right.id || "")));
|
|
143
147
|
}
|
|
@@ -220,6 +224,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
220
224
|
let settings = null;
|
|
221
225
|
let eventSource = null;
|
|
222
226
|
const provisionalReplies = new Map();
|
|
227
|
+
const provisionalJudges = new Map();
|
|
223
228
|
const conversationDrafts = new Map();
|
|
224
229
|
const groupSettingsDrafts = new Map();
|
|
225
230
|
let mentionOptions = [];
|
|
@@ -561,6 +566,52 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
561
566
|
}
|
|
562
567
|
}
|
|
563
568
|
|
|
569
|
+
function upsertProvisionalJudge(payload) {
|
|
570
|
+
const turnId = String(payload?.turnId || payload?.id || "");
|
|
571
|
+
if (!turnId) return null;
|
|
572
|
+
const previous = provisionalJudges.get(turnId) || {};
|
|
573
|
+
const eventCharacter = value(payload, "character", {});
|
|
574
|
+
const characterId = String(payload?.characterId || eventCharacter.characterId || previous.characterId || "");
|
|
575
|
+
const character = activeCharacters().find((item) => item.characterId === characterId);
|
|
576
|
+
const eventError = value(payload, "error", {});
|
|
577
|
+
const next = {
|
|
578
|
+
...previous,
|
|
579
|
+
turnId,
|
|
580
|
+
chainId: String(payload?.chainId || previous.chainId || ""),
|
|
581
|
+
sourceMessageId: String(payload?.sourceMessageId || previous.sourceMessageId || ""),
|
|
582
|
+
characterId,
|
|
583
|
+
name: eventCharacter.name || character?.name || previous.name || "角色",
|
|
584
|
+
avatarUrl: eventCharacter.avatarUrl || character?.avatarUrl || previous.avatarUrl || null,
|
|
585
|
+
status: String(payload?.status || previous.status || "pending"),
|
|
586
|
+
selected: payload?.selected === true,
|
|
587
|
+
error: eventError.message || payload?.failure || previous.error || ""
|
|
588
|
+
};
|
|
589
|
+
provisionalJudges.set(turnId, next);
|
|
590
|
+
return next;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function syncProvisionalJudges() {
|
|
594
|
+
const previous = new Map(provisionalJudges);
|
|
595
|
+
provisionalJudges.clear();
|
|
596
|
+
for (const turn of array(current?.judgeOutcomes)) {
|
|
597
|
+
const retained = previous.get(String(turn.id));
|
|
598
|
+
const next = upsertProvisionalJudge({ ...retained, ...turn, turnId: turn.id });
|
|
599
|
+
if (next && retained?.error && !next.error) next.error = retained.error;
|
|
600
|
+
}
|
|
601
|
+
for (const turn of array(current?.activeChain?.judges)) {
|
|
602
|
+
if (turn.selected === true) continue;
|
|
603
|
+
const retained = previous.get(String(turn.id));
|
|
604
|
+
const next = upsertProvisionalJudge({ ...retained, ...turn, turnId: turn.id });
|
|
605
|
+
if (next && retained?.error && !next.error) next.error = retained.error;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function clearPendingProvisionalJudges() {
|
|
610
|
+
for (const [turnId, judge] of provisionalJudges) {
|
|
611
|
+
if (["pending", "running"].includes(String(judge.status))) provisionalJudges.delete(turnId);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
564
615
|
function provisionalReplyBodyHtml(reply) {
|
|
565
616
|
const failed = ["failed", "skipped"].includes(reply.status);
|
|
566
617
|
const pendingCopy = reply.status === "pending" ? "等待角色开始回答…" : "正在组织回答…";
|
|
@@ -569,10 +620,27 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
569
620
|
return `${content}${failure}`;
|
|
570
621
|
}
|
|
571
622
|
|
|
623
|
+
function provisionalJudgeStatusLabel(judge) {
|
|
624
|
+
if (judge.status === "pending") return "等待判断";
|
|
625
|
+
if (judge.status === "running") return "模型判断中";
|
|
626
|
+
if (judge.status === "completed") return "无需回答";
|
|
627
|
+
if (judge.status === "cancelled") return "判断已取消";
|
|
628
|
+
return "判断失败";
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function provisionalJudgeBodyHtml(judge) {
|
|
632
|
+
if (["failed", "cancelled"].includes(judge.status)) {
|
|
633
|
+
return `<p class="im-provisional-error">${esc(judge.error || "模型判断失败")}</p>`;
|
|
634
|
+
}
|
|
635
|
+
if (judge.status === "completed") return '<p class="im-provisional-placeholder">模型判断当前无需回答</p>';
|
|
636
|
+
return '<p class="im-provisional-placeholder">模型正在判断是否回答…</p>';
|
|
637
|
+
}
|
|
638
|
+
|
|
572
639
|
function syncGeneratingSummary() {
|
|
573
640
|
const count = [...provisionalReplies.values()].filter((reply) => ["pending", "running"].includes(reply.status)).length;
|
|
574
641
|
const summary = feed.querySelector(".im-generating-summary");
|
|
575
|
-
|
|
642
|
+
const label = count ? `${count} 个角色正在生成回答` : "";
|
|
643
|
+
if (summary && label) summary.textContent = label;
|
|
576
644
|
else if (summary) summary.remove();
|
|
577
645
|
return count;
|
|
578
646
|
}
|
|
@@ -595,6 +663,11 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
595
663
|
if (follow) feed.scrollTop = feed.scrollHeight;
|
|
596
664
|
}
|
|
597
665
|
|
|
666
|
+
function updateProvisionalJudgeElement(judge) {
|
|
667
|
+
const follow = feed.scrollHeight - feed.scrollTop - feed.clientHeight < 80;
|
|
668
|
+
renderMessages({ scrollToBottom: follow });
|
|
669
|
+
}
|
|
670
|
+
|
|
598
671
|
function commitRealtimeMessage(message) {
|
|
599
672
|
if (!message?.id) return;
|
|
600
673
|
current.messages = mergeImMessagePages(current.messages, [message]);
|
|
@@ -609,7 +682,9 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
609
682
|
const previousTop = feed.scrollTop;
|
|
610
683
|
const messages = array(current?.messages);
|
|
611
684
|
const provisional = [...provisionalReplies.values()];
|
|
685
|
+
const provisionalJudgeList = [...provisionalJudges.values()];
|
|
612
686
|
const failedRepliesByMessage = new Map();
|
|
687
|
+
const judgesByMessage = new Map();
|
|
613
688
|
for (const reply of array(current?.failedReplies)) {
|
|
614
689
|
const triggerMessageId = String(reply.triggerMessageId || "");
|
|
615
690
|
if (!triggerMessageId) continue;
|
|
@@ -617,7 +692,14 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
617
692
|
replies.push(reply);
|
|
618
693
|
failedRepliesByMessage.set(triggerMessageId, replies);
|
|
619
694
|
}
|
|
620
|
-
|
|
695
|
+
for (const judge of provisionalJudgeList) {
|
|
696
|
+
const sourceMessageId = String(judge.sourceMessageId || "");
|
|
697
|
+
if (!sourceMessageId) continue;
|
|
698
|
+
const judges = judgesByMessage.get(sourceMessageId) ?? [];
|
|
699
|
+
judges.push(judge);
|
|
700
|
+
judgesByMessage.set(sourceMessageId, judges);
|
|
701
|
+
}
|
|
702
|
+
if (!messages.length && !provisional.length && !provisionalJudgeList.length && failedRepliesByMessage.size === 0) {
|
|
621
703
|
feed.innerHTML = '<p class="im-feed-empty">从一条消息开始。角色单聊会直接回复;群聊按当前回复模式调度 AI。</p>';
|
|
622
704
|
return;
|
|
623
705
|
}
|
|
@@ -628,6 +710,14 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
628
710
|
const generatingSummary = generatingCount
|
|
629
711
|
? `<div class="im-generating-summary" role="status">${generatingCount} 个角色正在生成回答</div>`
|
|
630
712
|
: "";
|
|
713
|
+
const provisionalJudgeHtml = (judge) => {
|
|
714
|
+
const failed = ['failed', 'cancelled'].includes(judge.status);
|
|
715
|
+
const quiet = judge.status === "completed";
|
|
716
|
+
return `<article class="im-message is-character is-provisional is-judge${failed ? " is-failed" : ""}${quiet ? " is-quiet" : ""}" data-im-provisional-judge="${esc(judge.turnId)}" data-im-provisional-judge-status="${esc(judge.status)}">
|
|
717
|
+
<header>${imAvatarHtml(judge, "character", "im-message-avatar")}<strong>${esc(judge.name || "角色")}</strong><span class="im-provisional-status">${provisionalJudgeStatusLabel(judge)}</span></header>
|
|
718
|
+
<div class="im-message-body message-body">${provisionalJudgeBodyHtml(judge)}</div>
|
|
719
|
+
</article>`;
|
|
720
|
+
};
|
|
631
721
|
const provisionalHtml = provisional.map((reply) => {
|
|
632
722
|
const failed = ['failed', 'skipped'].includes(reply.status);
|
|
633
723
|
const statusLabel = reply.status === "pending" ? "等待生成" : reply.status === "running" ? "正在生成" : reply.status === "skipped" ? "未生成" : "生成失败";
|
|
@@ -645,6 +735,12 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
645
735
|
const avatar = announcement || message.senderKind === "system"
|
|
646
736
|
? ""
|
|
647
737
|
: imAvatarHtml(sender, message.senderKind === "character" ? "character" : "user", "im-message-avatar");
|
|
738
|
+
const judges = array(judgesByMessage.get(String(message.id)));
|
|
739
|
+
const judgingCount = judges.filter((judge) => ['pending', 'running'].includes(judge.status)).length;
|
|
740
|
+
const judgeSummary = judgingCount
|
|
741
|
+
? `<div class="im-generating-summary" role="status">${judgingCount} 个角色正在判断是否回答</div>`
|
|
742
|
+
: "";
|
|
743
|
+
const judgeResults = judges.map(provisionalJudgeHtml).join("");
|
|
648
744
|
const failedReplies = array(failedRepliesByMessage.get(String(message.id))).map((reply) => {
|
|
649
745
|
const character = value(reply, "character", {});
|
|
650
746
|
return `<article class="im-message is-character is-provisional is-failed" data-im-failed-turn="${esc(reply.id)}">
|
|
@@ -656,7 +752,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
656
752
|
<header>${avatar}<strong>${esc(label)}</strong><time>${esc(new Date(message.createdAt).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }))}</time></header>
|
|
657
753
|
<div class="im-message-body message-body">${messageHtml(message)}</div>
|
|
658
754
|
${message.senderKind === "character" ? `<details class="im-model-details"><summary>调用详情</summary><span>${esc(model.modelDisplayName || model.modelId || "未知模型")} · ${model.modelStage === "fallback" ? "fallback" : "主模型"} · ${Number(model.attemptCount || 1)} 次请求 · ${Number(model.durationMs || 0)} ms</span></details>` : ""}
|
|
659
|
-
</article>${failedReplies}`;
|
|
755
|
+
</article>${judgeSummary}${judgeResults}${failedReplies}`;
|
|
660
756
|
}).join("") + generatingSummary + provisionalHtml;
|
|
661
757
|
bindImAvatarFallbacks(feed);
|
|
662
758
|
feed.scrollTop = follow ? feed.scrollHeight : previousTop;
|
|
@@ -679,6 +775,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
679
775
|
].map((message) => [message.id, message]));
|
|
680
776
|
current.messages = [...messagesById.values()].sort((left, right) => Number(left.sequence) - Number(right.sequence));
|
|
681
777
|
current.failedReplies = mergeImFailedReplyPages(page.failedReplies, current.failedReplies);
|
|
778
|
+
current.judgeOutcomes = mergeImTriggeredTurnPages(page.judgeOutcomes, current.judgeOutcomes);
|
|
682
779
|
current.hasMoreMessages = page.hasMoreMessages === true;
|
|
683
780
|
renderMessages();
|
|
684
781
|
feed.scrollTop = previousTop + Math.max(0, feed.scrollHeight - previousHeight);
|
|
@@ -989,17 +1086,20 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
989
1086
|
const conversationChanged = !previousConversation;
|
|
990
1087
|
if (previousConversation) {
|
|
991
1088
|
const gapFailedReplies = [];
|
|
1089
|
+
const gapJudgeOutcomes = [];
|
|
992
1090
|
const gapMessages = await collectImMessageGap(previousConversation.messages, nextConversation.messages, async (cursor) => {
|
|
993
1091
|
const page = await api(`/api/im/conversations/${encodeURIComponent(conversationId)}?afterSequence=${encodeURIComponent(cursor)}`);
|
|
994
1092
|
if (request !== conversationRequest || (requestedConversationId && requestedConversationId !== conversationId)) {
|
|
995
1093
|
throw new Error("IM 会话请求已失效");
|
|
996
1094
|
}
|
|
997
1095
|
gapFailedReplies.push(...array(page.failedReplies));
|
|
1096
|
+
gapJudgeOutcomes.push(...array(page.judgeOutcomes));
|
|
998
1097
|
return page;
|
|
999
1098
|
});
|
|
1000
1099
|
if (request !== conversationRequest || (requestedConversationId && requestedConversationId !== conversationId)) return;
|
|
1001
1100
|
nextConversation.messages = mergeImMessagePages(previousConversation.messages, gapMessages, nextConversation.messages);
|
|
1002
1101
|
nextConversation.failedReplies = mergeImFailedReplyPages(previousConversation.failedReplies, gapFailedReplies, nextConversation.failedReplies);
|
|
1102
|
+
nextConversation.judgeOutcomes = mergeImTriggeredTurnPages(previousConversation.judgeOutcomes, gapJudgeOutcomes, nextConversation.judgeOutcomes);
|
|
1003
1103
|
nextConversation.hasMoreMessages = previousConversation.hasMoreMessages === true;
|
|
1004
1104
|
}
|
|
1005
1105
|
if (current?.id && current.id !== conversationId) {
|
|
@@ -1015,6 +1115,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1015
1115
|
if (requestedConversationId === conversationId) requestedConversationId = null;
|
|
1016
1116
|
workspace.classList.add("has-conversation");
|
|
1017
1117
|
syncProvisionalReplies();
|
|
1118
|
+
syncProvisionalJudges();
|
|
1018
1119
|
const shouldRestoreDetailsFocus = detailsFocus && document.activeElement?.id === detailsFocus.id;
|
|
1019
1120
|
renderConversationList();
|
|
1020
1121
|
renderConversation(conversationChanged);
|
|
@@ -1427,6 +1528,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1427
1528
|
conversationDrafts.delete(conversationId);
|
|
1428
1529
|
closeMentionMenu();
|
|
1429
1530
|
provisionalReplies.clear();
|
|
1531
|
+
clearPendingProvisionalJudges();
|
|
1430
1532
|
let committed = false;
|
|
1431
1533
|
try {
|
|
1432
1534
|
const result = await api(`/api/im/conversations/${encodeURIComponent(conversationId)}/messages`, {
|
|
@@ -1473,7 +1575,11 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1473
1575
|
return;
|
|
1474
1576
|
}
|
|
1475
1577
|
if (envelope.type === "message" && current?.id === eventConversationId && envelope.payload.message) {
|
|
1476
|
-
if (Object.prototype.hasOwnProperty.call(envelope.payload, "chain"))
|
|
1578
|
+
if (Object.prototype.hasOwnProperty.call(envelope.payload, "chain")) {
|
|
1579
|
+
const nextChain = envelope.payload.chain ?? null;
|
|
1580
|
+
if (String(current.activeChain?.id || "") !== String(nextChain?.id || "")) clearPendingProvisionalJudges();
|
|
1581
|
+
current.activeChain = nextChain;
|
|
1582
|
+
}
|
|
1477
1583
|
commitRealtimeMessage(envelope.payload.message);
|
|
1478
1584
|
}
|
|
1479
1585
|
if (envelope.type === "turn" && current?.id === eventConversationId && envelope.payload.kind === "reply") {
|
|
@@ -1490,6 +1596,18 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1490
1596
|
}
|
|
1491
1597
|
return;
|
|
1492
1598
|
}
|
|
1599
|
+
if (envelope.type === "turn" && current?.id === eventConversationId && envelope.payload.kind === "judge") {
|
|
1600
|
+
if (!isImRealtimeChainCurrent(current.activeChain, envelope.payload)) return;
|
|
1601
|
+
const turnId = String(envelope.payload.turnId || "");
|
|
1602
|
+
if (envelope.payload.selected === true) {
|
|
1603
|
+
provisionalJudges.delete(turnId);
|
|
1604
|
+
renderMessages();
|
|
1605
|
+
} else {
|
|
1606
|
+
const provisional = upsertProvisionalJudge(envelope.payload);
|
|
1607
|
+
if (provisional) updateProvisionalJudgeElement(provisional);
|
|
1608
|
+
}
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1493
1611
|
if (envelope.type === "delta" && current?.id === eventConversationId) {
|
|
1494
1612
|
if (!isImRealtimeChainCurrent(current.activeChain, envelope.payload)) return;
|
|
1495
1613
|
const provisional = upsertProvisionalReply({ ...envelope.payload, status: "running" });
|
|
@@ -1570,6 +1688,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1570
1688
|
resetDetailsDrawer();
|
|
1571
1689
|
document.querySelector("#app").classList.remove("im-mode");
|
|
1572
1690
|
provisionalReplies.clear();
|
|
1691
|
+
provisionalJudges.clear();
|
|
1573
1692
|
closeMentionMenu();
|
|
1574
1693
|
}
|
|
1575
1694
|
|
|
@@ -1715,6 +1834,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1715
1834
|
const result = await performMutation(event.currentTarget, () => api(`/api/im/conversations/${encodeURIComponent(conversationId)}/stop`, { method: "POST", body: {} }));
|
|
1716
1835
|
if (!result.ok) return;
|
|
1717
1836
|
provisionalReplies.clear();
|
|
1837
|
+
provisionalJudges.clear();
|
|
1718
1838
|
if (current?.id === conversationId) await refreshAfterMutation("停止 AI", () => openConversation(conversationId));
|
|
1719
1839
|
});
|
|
1720
1840
|
document.querySelector("#im-retry").addEventListener("click", async (event) => {
|
|
@@ -1744,6 +1864,7 @@ export function createImWorkspace({ api, esc, renderMarkdown, toast, confirmToas
|
|
|
1744
1864
|
workspace.classList.remove("has-conversation");
|
|
1745
1865
|
resetDetailsDrawer();
|
|
1746
1866
|
provisionalReplies.clear();
|
|
1867
|
+
provisionalJudges.clear();
|
|
1747
1868
|
renderConversationList();
|
|
1748
1869
|
renderConversation();
|
|
1749
1870
|
const previousConversationButton = [...listHost.querySelectorAll("[data-im-conversation]")]
|
package/dist/public/index.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<script src="/theme-init.js?v=20260827-reader-prefetch-v1"></script>
|
|
10
10
|
<link rel="icon" href="/icon.svg?v=20260712" type="image/svg+xml">
|
|
11
11
|
<link rel="manifest" href="/site.webmanifest">
|
|
12
|
-
<link rel="stylesheet" href="/styles.css?v=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=chapter-comment-filters-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=ai-stream-character-count-five-digit-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1&feature=character-card-header-alignment-v6&feature=character-card-title-fit-v2&feature=book-import-progress-v1&feature=editor-toolbar-compact-v2&feature=reader-first-frame-v1&feature=auth-compact-v2&feature=editor-preview-toggle-v2&feature=setting-title-chrome-v2&feature=entity-pin-icon-center-v1&feature=chapter-center-bottom-space-v1&feature=work-editor-preferences-v1&feature=work-editor-preference-checkbox-v1&feature=ai-roleplay-memory-v2&feature=ai-roleplay-memory-v3&feature=ai-roleplay-memory-v5&feature=character-editor-header-actions-v1&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-
|
|
12
|
+
<link rel="stylesheet" href="/styles.css?v=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=chapter-comment-filters-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=ai-stream-character-count-five-digit-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1&feature=character-card-header-alignment-v6&feature=character-card-title-fit-v2&feature=book-import-progress-v1&feature=editor-toolbar-compact-v2&feature=reader-first-frame-v1&feature=auth-compact-v2&feature=editor-preview-toggle-v2&feature=setting-title-chrome-v2&feature=entity-pin-icon-center-v1&feature=chapter-center-bottom-space-v1&feature=work-editor-preferences-v1&feature=work-editor-preference-checkbox-v1&feature=ai-roleplay-memory-v2&feature=ai-roleplay-memory-v3&feature=ai-roleplay-memory-v5&feature=character-editor-header-actions-v1&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=roleplay-memory-pin-border-v1&feature=ai-write-tools-v2&feature=ai-write-card-actions-compact-v1&feature=ai-write-plan-actions-footer-v1&feature=ai-question-actions-footer-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-answer-limit-v1&feature=ai-question-batch-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v1&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=ai-settings-textarea-font-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=ai-skill-slash-menu-v1&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=im-narration-contrast-v1&feature=im-member-add-plus-v2&feature=im-button-hierarchy-v1&feature=im-icon-button-size-v1&feature=compact-sidebar-directory-v5&feature=ai-model-config-dialog-v1&feature=system-prompt-override-v3">
|
|
13
13
|
</head>
|
|
14
14
|
<body class="auth-pending">
|
|
15
15
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -1425,6 +1425,6 @@
|
|
|
1425
1425
|
</dialog>
|
|
1426
1426
|
|
|
1427
1427
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1428
|
-
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-history-rename-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-roleplay-message-reference-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v2&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=entity-pin-icon-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1&feature=book-import-progress-v1&feature=presence-multiple-users-v1&feature=ai-context-input-output-v1&feature=editor-blank-lines-preserved-v1&feature=reader-first-frame-v1&feature=vditor-lazy-load-v1&feature=ui-module-preload-v1&feature=reader-initial-prefetch-v1&feature=editor-preview-toggle-v2&feature=vditor-fullscreen-disabled-v1&feature=chapter-auto-indent-v1&feature=chapter-centered-scroll-v1&feature=work-editor-preferences-v1&feature=ai-context-output-usage-v1&feature=ai-roleplay-memory-v4&feature=ai-roleplay-memory-v5&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-header-toolbar-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=annotation-line-anchor-v1&feature=stable-line-ids-v1&feature=live-annotation-anchors-v1&feature=chapter-comment-filters-v1&feature=ai-write-tools-v3&feature=ai-question-option-supplement-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-continuation-ui-v1&feature=ai-question-tool-result-v1&feature=ai-question-tool-summary-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-batch-v1&feature=ai-question-answer-limit-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v3&feature=ai-cancel-preserve-process-v2&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=chapter-batch-editor-refresh-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=semantic-search-rag-label-v1&feature=ai-skill-slash-menu-v1&feature=ai-roleplay-scene-bubble-v1&feature=ai-roleplay-scene-collapse-v1&feature=markdown-adjacent-blockquotes-v1&feature=chapter-save-shortcut-v2&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-
|
|
1428
|
+
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-history-rename-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-roleplay-message-reference-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v2&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=entity-pin-icon-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1&feature=book-import-progress-v1&feature=presence-multiple-users-v1&feature=ai-context-input-output-v1&feature=editor-blank-lines-preserved-v1&feature=reader-first-frame-v1&feature=vditor-lazy-load-v1&feature=ui-module-preload-v1&feature=reader-initial-prefetch-v1&feature=editor-preview-toggle-v2&feature=vditor-fullscreen-disabled-v1&feature=chapter-auto-indent-v1&feature=chapter-centered-scroll-v1&feature=work-editor-preferences-v1&feature=ai-context-output-usage-v1&feature=ai-roleplay-memory-v4&feature=ai-roleplay-memory-v5&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-header-toolbar-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=annotation-line-anchor-v1&feature=stable-line-ids-v1&feature=live-annotation-anchors-v1&feature=chapter-comment-filters-v1&feature=ai-write-tools-v3&feature=ai-question-option-supplement-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-continuation-ui-v1&feature=ai-question-tool-result-v1&feature=ai-question-tool-summary-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-batch-v1&feature=ai-question-answer-limit-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v3&feature=ai-cancel-preserve-process-v2&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=chapter-batch-editor-refresh-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=semantic-search-rag-label-v1&feature=ai-skill-slash-menu-v1&feature=ai-roleplay-scene-bubble-v1&feature=ai-roleplay-scene-collapse-v1&feature=markdown-adjacent-blockquotes-v1&feature=chapter-save-shortcut-v2&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=im-settings-gear-v1&feature=compact-sidebar-directory-v2"></script>
|
|
1429
1429
|
</body>
|
|
1430
1430
|
</html>
|
package/dist/public/styles.css
CHANGED
|
@@ -1440,6 +1440,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1440
1440
|
display: grid;
|
|
1441
1441
|
grid-template-columns: auto minmax(0, 2em);
|
|
1442
1442
|
align-items: start;
|
|
1443
|
+
justify-content: start;
|
|
1443
1444
|
word-break: break-all;
|
|
1444
1445
|
}
|
|
1445
1446
|
.module-nav > button[data-module="outlines"] {
|
|
@@ -1778,6 +1779,21 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1778
1779
|
.config-section { margin-top: 28px; padding-top: 24px; border-top: 1px solid var(--line); }
|
|
1779
1780
|
.config-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
|
|
1780
1781
|
.platform-system-prompt-section { margin-bottom: 36px; }
|
|
1782
|
+
.system-prompt-override-settings { margin-top: 12px; overflow: hidden; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-soft); }
|
|
1783
|
+
.system-prompt-override-settings.is-enabled { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
|
|
1784
|
+
.system-prompt-override-settings summary { display: grid; min-height: 40px; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 8px; padding: 9px 12px; cursor: pointer; color: var(--muted); font-size: 11px; list-style: none; }
|
|
1785
|
+
.system-prompt-override-settings summary::-webkit-details-marker { display: none; }
|
|
1786
|
+
.system-prompt-override-settings summary::before { content: "›"; color: var(--muted); font-size: 16px; line-height: 1; transform: rotate(0deg); transition: transform .16s ease; }
|
|
1787
|
+
.system-prompt-override-settings[open] summary::before { transform: rotate(90deg); }
|
|
1788
|
+
.system-prompt-override-settings summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
1789
|
+
.system-prompt-override-settings summary strong { color: var(--ink); font-size: 10px; font-weight: 600; }
|
|
1790
|
+
.system-prompt-override-settings.is-enabled summary strong { color: var(--accent-dark); }
|
|
1791
|
+
.system-prompt-override-body { display: grid; gap: 10px; padding: 12px; border-top: 1px solid var(--line); }
|
|
1792
|
+
.system-prompt-override-body > .config-checkbox-field { display: inline-flex; width: fit-content; align-items: center; gap: 8px; margin: 0; color: var(--ink); font-size: 11px; }
|
|
1793
|
+
.system-prompt-override-body > .config-checkbox-field input { width: 18px; min-width: 18px; height: 18px; }
|
|
1794
|
+
.system-prompt-override-warning { padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--line)); border-radius: 5px; background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
|
|
1795
|
+
.system-prompt-override-warning strong { color: var(--ink); font-size: 11px; }
|
|
1796
|
+
.system-prompt-override-warning p { margin: 3px 0 0; color: var(--muted); font-size: 10px; line-height: 1.55; }
|
|
1781
1797
|
.platform-image-tool-section { margin-bottom: 0; }
|
|
1782
1798
|
.platform-image-tool-panel { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; padding: 16px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
|
|
1783
1799
|
.platform-image-tool-field { display: grid; flex: 1 1 360px; gap: 7px; min-width: 0; color: var(--muted); font-size: 10px; }
|
|
@@ -3520,6 +3536,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3520
3536
|
.dialog-fields .form-field-note { margin: 0; color: var(--muted); font-size: 11px; line-height: 1.65; }
|
|
3521
3537
|
.dialog-fields label { display: grid; gap: 6px; color: var(--muted); font-size: 11px; }
|
|
3522
3538
|
.dialog-fields input, .dialog-fields textarea, .dialog-fields select { width: 100%; padding: 9px 10px; font-size: 13px; }
|
|
3539
|
+
.dialog-fields input[type="checkbox"] { flex: 0 0 16px; width: 16px; min-width: 16px; height: 16px; min-height: 16px; margin: 0; padding: 0; }
|
|
3523
3540
|
.dialog-fields select { padding-inline-end: 28px; }
|
|
3524
3541
|
.dialog-fields .character-extraction-candidate-toggle { display: inline-flex; width: auto; align-items: center; gap: 7px; margin: 0; color: var(--ink); font-size: 9px; white-space: nowrap; }
|
|
3525
3542
|
.dialog-fields .character-extraction-candidate-toggle input[type="checkbox"] { flex: 0 0 18px; width: 18px; min-width: 18px; height: 18px; padding: 0; }
|
|
@@ -3570,6 +3587,14 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3570
3587
|
.model-capability-option small, .model-multimodal-note { color: var(--muted); font-size: 9px; line-height: 1.5; }
|
|
3571
3588
|
.model-capability-option:has(input:checked) { border-color: color-mix(in srgb, var(--accent) 62%, var(--line)); background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
|
|
3572
3589
|
.model-multimodal-note { margin: 0 2px; }
|
|
3590
|
+
#form-dialog:has(.model-kind-fields) { width: min(720px, 92vw); }
|
|
3591
|
+
#dialog-fields:has(.model-kind-fields) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
|
|
3592
|
+
#dialog-fields:has(.model-kind-fields) [data-chat-model-fields] { display: contents; }
|
|
3593
|
+
#dialog-fields:has(.model-kind-fields) > .model-kind-fields,
|
|
3594
|
+
#dialog-fields:has(.model-kind-fields) > .checkbox-field,
|
|
3595
|
+
#dialog-fields:has(.model-kind-fields) .chip-field,
|
|
3596
|
+
#dialog-fields:has(.model-kind-fields) .model-multimodal-fields,
|
|
3597
|
+
#dialog-fields:has(.model-kind-fields) .model-connection-test { grid-column: 1 / -1; }
|
|
3573
3598
|
.analysis-type-description { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.55; }
|
|
3574
3599
|
.task-chapter-field { transition: opacity .15s ease; }
|
|
3575
3600
|
.task-chapter-field.is-disabled { opacity: .48; }
|
|
@@ -4467,6 +4492,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
4467
4492
|
margin: auto;
|
|
4468
4493
|
border-radius: 10px;
|
|
4469
4494
|
}
|
|
4495
|
+
#form-dialog:has(.model-kind-fields) { width: calc(100vw - 20px); max-width: calc(100vw - 20px); }
|
|
4470
4496
|
.dialog-header { padding: 14px 16px 12px; }
|
|
4471
4497
|
.dialog-header h2 { font-size: 20px; }
|
|
4472
4498
|
.dialog-title-input { width: min(100%, calc(100vw - 100px)); font-size: 20px; }
|
|
@@ -4573,6 +4599,10 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
4573
4599
|
.onboarding-popover { width: calc(100vw - 20px); }
|
|
4574
4600
|
}
|
|
4575
4601
|
|
|
4602
|
+
@media (max-width: 480px) {
|
|
4603
|
+
#dialog-fields:has(.model-kind-fields) { grid-template-columns: minmax(0, 1fr); }
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4576
4606
|
@media (max-width: 850px) {
|
|
4577
4607
|
#onboarding-menu-button { display: none; }
|
|
4578
4608
|
}
|
|
@@ -4800,6 +4830,8 @@ body.is-im-conversations-resizing { cursor: ew-resize; user-select: none; }
|
|
|
4800
4830
|
.im-generating-summary { width: min(78%, 720px); margin: 2px auto 9px 0; color: var(--muted); font-size: 9px; line-height: 1.4; }
|
|
4801
4831
|
.im-message.is-provisional { opacity: .82; }
|
|
4802
4832
|
.im-message.is-provisional.is-failed { opacity: 1; }
|
|
4833
|
+
.im-message.is-provisional.is-judge .im-message-body { border-style: dashed; background: color-mix(in srgb, var(--panel) 88%, var(--paper-deep)); }
|
|
4834
|
+
.im-message.is-provisional.is-judge.is-quiet { opacity: .72; }
|
|
4803
4835
|
.im-provisional-status { color: var(--muted); font-size: 8px; }
|
|
4804
4836
|
.im-provisional-placeholder { margin: 0; color: var(--muted); }
|
|
4805
4837
|
.im-provisional-error { margin: 0; color: color-mix(in srgb, #c44949 82%, var(--ink)); font-weight: 600; }
|