@musnows/scriverse 0.8.2 → 0.8.4
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 +18 -8
- package/dist/ai.js.map +1 -1
- package/dist/public/ai-context-meter.js +2 -1
- package/dist/public/app.js +56 -24
- package/dist/public/index.html +2 -2
- package/dist/public/phone-client.js +6 -0
- package/dist/public/styles.css +5 -1
- package/dist/store.js +17 -16
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -48,7 +48,8 @@ export function formatAiContextUsagePercent(occupiedTokens, contextWindow) {
|
|
|
48
48
|
const occupied = tokenCount(occupiedTokens);
|
|
49
49
|
const window = tokenCount(contextWindow);
|
|
50
50
|
const percent = window > 0 ? Math.min(100, occupied / window * 100) : 0;
|
|
51
|
-
|
|
51
|
+
const roundedDecimal = Number(percent.toFixed(1));
|
|
52
|
+
return roundedDecimal < 10 ? `${roundedDecimal.toFixed(1)}%` : `${Math.round(percent)}%`;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export function normalizeAiContextTokenDistribution(usage) {
|
package/dist/public/app.js
CHANGED
|
@@ -24,7 +24,8 @@ import { createStreamTypewriter, createStreamTypewriterSpeedController } from "/
|
|
|
24
24
|
import { assertAiStreamCompleted, readAiEventStream } from "/ai-stream-protocol.js?v=20260812-ai-stream-complete-v1";
|
|
25
25
|
import { buildUsageCalendar, formatCacheHitRate, formatTokenCount } from "/ai-usage.js?v=20260727-ai-usage-v1";
|
|
26
26
|
import { formatAiMessageTime } from "/ai-message-time.js?v=20260801-month-day-time";
|
|
27
|
-
import { formatAiContextUsagePercent, formatAiContextUsageTooltip, mergeAiContextUsage, normalizeAiContextTokenDistribution, resolveAiContextUsage } from "/ai-context-meter.js?v=
|
|
27
|
+
import { formatAiContextUsagePercent, formatAiContextUsageTooltip, mergeAiContextUsage, normalizeAiContextTokenDistribution, resolveAiContextUsage } from "/ai-context-meter.js?v=20260819-context-percent-v1";
|
|
28
|
+
import { isPhoneClient } from "/phone-client.js?v=20260819-phone-client-v1";
|
|
28
29
|
import { formatAiToolCallResult } from "/ai-tool-call.js?v=20260801-ai-tool-result-chars-v1";
|
|
29
30
|
import { copyAiRawMarkdown } from "/ai-message-actions.js?v=20260713-copy-raw-markdown";
|
|
30
31
|
import { bindPlainTextPaste } from "/plain-text-paste.js?v=20260815-plain-text-paste-v1";
|
|
@@ -1331,12 +1332,15 @@ function constrainPanelLayout() {
|
|
|
1331
1332
|
}
|
|
1332
1333
|
|
|
1333
1334
|
function applyPanelLayout(persist = false) {
|
|
1335
|
+
const phoneClient = isPhoneClient();
|
|
1336
|
+
if (phoneClient && !aiConversationWorkspaceOpen) panelLayout.aiCollapsed = true;
|
|
1334
1337
|
constrainPanelLayout();
|
|
1335
1338
|
const app = $("#app");
|
|
1336
1339
|
app.style.setProperty("--left-panel-width", `${panelLayout.leftWidth}px`);
|
|
1337
1340
|
app.style.setProperty("--ai-panel-width", `${panelLayout.aiWidth}px`);
|
|
1338
1341
|
app.classList.toggle("left-panel-collapsed", panelLayout.leftCollapsed);
|
|
1339
1342
|
app.classList.toggle("ai-panel-collapsed", panelLayout.aiCollapsed);
|
|
1343
|
+
app.classList.toggle("phone-client", phoneClient);
|
|
1340
1344
|
const mobileViewport = isMobileViewport();
|
|
1341
1345
|
$("#left-panel-toggle").textContent = mobileViewport ? "›" : (panelLayout.leftCollapsed ? "›" : "‹");
|
|
1342
1346
|
$("#left-panel-toggle").setAttribute("aria-expanded", String(!panelLayout.leftCollapsed));
|
|
@@ -1356,7 +1360,7 @@ function applyPanelLayout(persist = false) {
|
|
|
1356
1360
|
|
|
1357
1361
|
function ensureAiPanelExpanded() {
|
|
1358
1362
|
if (!state.work || !canReadPermissionModule(state.work, "ai-chat")) return;
|
|
1359
|
-
if (isMobileViewport()) {
|
|
1363
|
+
if (isPhoneClient() || isMobileViewport()) {
|
|
1360
1364
|
setAiConversationWorkspaceVisible(true);
|
|
1361
1365
|
return;
|
|
1362
1366
|
}
|
|
@@ -2219,7 +2223,7 @@ function setAiConversationSwitcherVisible(visible) {
|
|
|
2219
2223
|
}
|
|
2220
2224
|
|
|
2221
2225
|
function setAiConversationWorkspaceVisible(visible) {
|
|
2222
|
-
const mobileWorkspace = isMobileViewport();
|
|
2226
|
+
const mobileWorkspace = isPhoneClient() || isMobileViewport();
|
|
2223
2227
|
aiConversationWorkspaceOpen = Boolean(visible);
|
|
2224
2228
|
panelLayout.aiCollapsed = aiConversationWorkspaceOpen ? false : mobileWorkspace;
|
|
2225
2229
|
$("#app").classList.toggle("ai-workspace-mode", aiConversationWorkspaceOpen);
|
|
@@ -3251,6 +3255,10 @@ function renderAiRoleplayUserCharacterSelect() {
|
|
|
3251
3255
|
|
|
3252
3256
|
const aiConversationOptionLockedMessage = "会话选项在会话开始后不支持修改,若需要修改,请新建会话";
|
|
3253
3257
|
|
|
3258
|
+
function aiConversationModelLocked() {
|
|
3259
|
+
return typeof state.aiConversationModelId === "string" && state.aiConversationModelId.trim().length > 0;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3254
3262
|
function selectedAiModelLabel() {
|
|
3255
3263
|
return $("#ai-model").selectedOptions[0]?.textContent?.trim() || "尚未选择模型";
|
|
3256
3264
|
}
|
|
@@ -3259,20 +3267,21 @@ function syncAiModelPicker() {
|
|
|
3259
3267
|
const select = $("#ai-model");
|
|
3260
3268
|
const button = $("#ai-model-picker");
|
|
3261
3269
|
const interactionBusy = aiInteractionBusy();
|
|
3270
|
+
const modelLocked = aiConversationModelLocked();
|
|
3262
3271
|
const selectedLabel = selectedAiModelLabel();
|
|
3263
|
-
const label =
|
|
3272
|
+
const label = modelLocked
|
|
3264
3273
|
? `当前模型:${selectedLabel}。${aiConversationOptionLockedMessage}`
|
|
3265
3274
|
: `选择实际使用模型:${selectedLabel}`;
|
|
3266
3275
|
select.disabled = interactionBusy;
|
|
3267
|
-
select.title =
|
|
3276
|
+
select.title = modelLocked ? aiConversationOptionLockedMessage : "";
|
|
3268
3277
|
button.disabled = interactionBusy;
|
|
3269
3278
|
button.title = label;
|
|
3270
3279
|
button.setAttribute("aria-label", label);
|
|
3271
3280
|
if (interactionBusy) setAiModelPickerVisible(false);
|
|
3272
3281
|
}
|
|
3273
3282
|
|
|
3274
|
-
function notifyAiConversationOptionLocked(select) {
|
|
3275
|
-
if (!
|
|
3283
|
+
function notifyAiConversationOptionLocked(select, locked = state.aiPromptSent) {
|
|
3284
|
+
if (!locked) return false;
|
|
3276
3285
|
const now = Date.now();
|
|
3277
3286
|
const lastToastAt = Number(select.dataset.lockedToastAt ?? 0);
|
|
3278
3287
|
if (now - lastToastAt > 500) toast(aiConversationOptionLockedMessage);
|
|
@@ -3280,9 +3289,16 @@ function notifyAiConversationOptionLocked(select) {
|
|
|
3280
3289
|
return true;
|
|
3281
3290
|
}
|
|
3282
3291
|
|
|
3292
|
+
function notifyAiConversationModelLocked(control) {
|
|
3293
|
+
return notifyAiConversationOptionLocked(control, aiConversationModelLocked());
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3283
3296
|
function blockLockedAiConversationOptionInteraction(event) {
|
|
3284
3297
|
const select = event.currentTarget;
|
|
3285
|
-
|
|
3298
|
+
const locked = select.id === "ai-model"
|
|
3299
|
+
? notifyAiConversationModelLocked(select)
|
|
3300
|
+
: notifyAiConversationOptionLocked(select);
|
|
3301
|
+
if (!locked) return;
|
|
3286
3302
|
event.preventDefault();
|
|
3287
3303
|
event.stopPropagation();
|
|
3288
3304
|
}
|
|
@@ -3808,8 +3824,7 @@ function renderChapterAnnotations() {
|
|
|
3808
3824
|
},
|
|
3809
3825
|
locate: (annotation) => {
|
|
3810
3826
|
$("#chapter-annotations-dialog").close();
|
|
3811
|
-
|
|
3812
|
-
selectChapterLines(annotation.startLine - 1, annotation.endLine - 1);
|
|
3827
|
+
revealChapterLines(annotation.startLine, annotation.endLine);
|
|
3813
3828
|
},
|
|
3814
3829
|
overlayDialog: $("#chapter-annotations-dialog")
|
|
3815
3830
|
});
|
|
@@ -6385,7 +6400,7 @@ async function runWorkSearch() {
|
|
|
6385
6400
|
renderSearchResults(results, query);
|
|
6386
6401
|
}
|
|
6387
6402
|
|
|
6388
|
-
function
|
|
6403
|
+
function revealChapterLines(startLine, endLine) {
|
|
6389
6404
|
const start = Math.max(0, Number(startLine) - 1);
|
|
6390
6405
|
const end = Math.max(start, Number(endLine ?? startLine) - 1);
|
|
6391
6406
|
const input = $("#chapter-content");
|
|
@@ -6440,7 +6455,7 @@ async function openSearchResult(result) {
|
|
|
6440
6455
|
if (target.kind === "chapter") {
|
|
6441
6456
|
await selectChapter(target.id);
|
|
6442
6457
|
if (state.chapter?.id === target.id && target.startLine) {
|
|
6443
|
-
|
|
6458
|
+
revealChapterLines(target.startLine, target.endLine);
|
|
6444
6459
|
toast(target.startLine === target.endLine ? `已定位到第 ${target.startLine} 行` : `已定位到第 ${target.startLine}-${target.endLine} 行`);
|
|
6445
6460
|
}
|
|
6446
6461
|
return;
|
|
@@ -9441,10 +9456,10 @@ async function renderWorkChapterComments(page = moduleListPages.comments) {
|
|
|
9441
9456
|
bindChapterAnnotationCards($("#module-content"), result.items, {
|
|
9442
9457
|
refresh: () => renderWorkChapterComments(pageResult.page),
|
|
9443
9458
|
locate: async (annotation) => {
|
|
9444
|
-
await selectChapter(annotation.chapterId);
|
|
9459
|
+
const selected = await selectChapter(annotation.chapterId);
|
|
9460
|
+
if (!selected || String(state.chapter?.id ?? "") !== String(annotation.chapterId)) return;
|
|
9445
9461
|
await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
|
9446
|
-
|
|
9447
|
-
selectChapterLines(annotation.startLine - 1, annotation.endLine - 1);
|
|
9462
|
+
revealChapterLines(annotation.startLine, annotation.endLine);
|
|
9448
9463
|
}
|
|
9449
9464
|
});
|
|
9450
9465
|
}
|
|
@@ -12909,8 +12924,17 @@ function updateVditorWordCount(editor, markdown) {
|
|
|
12909
12924
|
counter.title = "当前 Markdown 正文的字数,不计空白字符";
|
|
12910
12925
|
toolbar.append(counter);
|
|
12911
12926
|
}
|
|
12927
|
+
let value = counter.querySelector("[data-markdown-word-count-value]");
|
|
12928
|
+
if (!value) {
|
|
12929
|
+
value = document.createElement("span");
|
|
12930
|
+
value.className = "markdown-word-count-value";
|
|
12931
|
+
value.dataset.markdownWordCountValue = "true";
|
|
12932
|
+
const unit = document.createElement("span");
|
|
12933
|
+
unit.textContent = " 字";
|
|
12934
|
+
counter.replaceChildren(value, unit);
|
|
12935
|
+
}
|
|
12912
12936
|
const count = Array.from(String(markdown ?? "").replace(/\s/gu, "")).length;
|
|
12913
|
-
|
|
12937
|
+
value.textContent = count.toLocaleString("zh-CN");
|
|
12914
12938
|
}
|
|
12915
12939
|
|
|
12916
12940
|
function transformVditorPreview(html) {
|
|
@@ -15013,6 +15037,18 @@ async function sendAiWithOptions({ ignoreContextWarning = false } = {}) {
|
|
|
15013
15037
|
}
|
|
15014
15038
|
}
|
|
15015
15039
|
|
|
15040
|
+
function createAiStreamCharacterCount(value) {
|
|
15041
|
+
const count = document.createElement("span");
|
|
15042
|
+
count.className = "ai-stream-character-count";
|
|
15043
|
+
count.textContent = Math.max(0, Number(value) || 0).toLocaleString("zh-CN");
|
|
15044
|
+
return count;
|
|
15045
|
+
}
|
|
15046
|
+
|
|
15047
|
+
function renderAiStreamingCharacterProgress(meta, visibleCharacters) {
|
|
15048
|
+
const visible = Math.max(0, Number(visibleCharacters) || 0);
|
|
15049
|
+
meta.replaceChildren("正在生成 · ", createAiStreamCharacterCount(visible), " 字");
|
|
15050
|
+
}
|
|
15051
|
+
|
|
15016
15052
|
async function streamChat(requestHolder, body, idempotencyKey) {
|
|
15017
15053
|
const tab = aiChatTabForRequest(requestHolder.snapshot);
|
|
15018
15054
|
if (!tab) throw createAiRequestAbortError("Agent 对话页签已关闭");
|
|
@@ -15039,10 +15075,7 @@ async function streamChat(requestHolder, body, idempotencyKey) {
|
|
|
15039
15075
|
onRender: (text, progress) => {
|
|
15040
15076
|
if (!aiRequestTargetsCurrentState(requestHolder.snapshot) || !mountAssistantMessage()) return;
|
|
15041
15077
|
content.innerHTML = renderMarkdown(text);
|
|
15042
|
-
|
|
15043
|
-
meta.textContent = progress.pendingCharacters > 0
|
|
15044
|
-
? `正在生成 · ${progress.visibleCharacters} / ${receivedCharacters} 字`
|
|
15045
|
-
: `正在生成 · ${progress.visibleCharacters} 字`;
|
|
15078
|
+
renderAiStreamingCharacterProgress(meta, progress.visibleCharacters);
|
|
15046
15079
|
scrollAiFeedToBottom(feed);
|
|
15047
15080
|
}
|
|
15048
15081
|
});
|
|
@@ -15152,7 +15185,6 @@ async function streamChat(requestHolder, body, idempotencyKey) {
|
|
|
15152
15185
|
streamedText += delta;
|
|
15153
15186
|
streamedPendingText += delta;
|
|
15154
15187
|
typewriter.append(delta);
|
|
15155
|
-
meta.textContent = "正在生成回复……";
|
|
15156
15188
|
} else if (eventName === "process_step") {
|
|
15157
15189
|
mountAssistantMessage();
|
|
15158
15190
|
const step = { ...payload };
|
|
@@ -16919,7 +16951,7 @@ $("#ai-assistant-entry").addEventListener("click", () => {
|
|
|
16919
16951
|
return toast("当前账户没有创作助手读取权限", "error");
|
|
16920
16952
|
}
|
|
16921
16953
|
setModuleNavExpanded(false);
|
|
16922
|
-
if (isMobileViewport()) {
|
|
16954
|
+
if (isPhoneClient() || isMobileViewport()) {
|
|
16923
16955
|
panelLayout.leftCollapsed = true;
|
|
16924
16956
|
applyPanelLayout(true);
|
|
16925
16957
|
setAiConversationWorkspaceVisible(true);
|
|
@@ -16970,7 +17002,7 @@ $("#ai-model").addEventListener("focus", () => {
|
|
|
16970
17002
|
ensureAiModelsLoaded().catch((error) => toast(`模型加载失败:${error.message}`, "error"));
|
|
16971
17003
|
});
|
|
16972
17004
|
$("#ai-model").addEventListener("change", (event) => {
|
|
16973
|
-
if (
|
|
17005
|
+
if (aiConversationModelLocked()) {
|
|
16974
17006
|
event.currentTarget.value = state.aiConversationModelId ?? event.currentTarget.value;
|
|
16975
17007
|
syncAiModelPicker();
|
|
16976
17008
|
return toast(aiConversationOptionLockedMessage);
|
|
@@ -16981,7 +17013,7 @@ $("#ai-model").addEventListener("change", (event) => {
|
|
|
16981
17013
|
});
|
|
16982
17014
|
$("#ai-model-picker").addEventListener("click", async (event) => {
|
|
16983
17015
|
const button = event.currentTarget;
|
|
16984
|
-
if (
|
|
17016
|
+
if (notifyAiConversationModelLocked(button)) return;
|
|
16985
17017
|
const willOpen = $("#ai-model-popover").classList.contains("hidden");
|
|
16986
17018
|
setAiContextDistributionVisible(false);
|
|
16987
17019
|
setAiModelPickerVisible(willOpen);
|
package/dist/public/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<link rel="icon" href="/icon.svg?v=20260712" type="image/svg+xml">
|
|
11
11
|
<link rel="manifest" href="/site.webmanifest">
|
|
12
12
|
<link rel="stylesheet" href="/vendor/vditor/dist/index.css?v=3.11.2">
|
|
13
|
-
<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=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1">
|
|
13
|
+
<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=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=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -1210,6 +1210,6 @@
|
|
|
1210
1210
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1211
1211
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1212
1212
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1213
|
-
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-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-v1&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-v1&feature=volume-detail-icon-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-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-model-picker-v1"></script>
|
|
1213
|
+
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-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-v1&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-v1&feature=volume-detail-icon-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-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-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=phone-client-entry-v1"></script>
|
|
1214
1214
|
</body>
|
|
1215
1215
|
</html>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export function isPhoneClient(navigatorLike = globalThis.navigator) {
|
|
2
|
+
const mobile = navigatorLike?.userAgentData?.mobile;
|
|
3
|
+
if (typeof mobile === "boolean") return mobile;
|
|
4
|
+
const userAgent = String(navigatorLike?.userAgent ?? "");
|
|
5
|
+
return /(?:Android.*Mobile|iPhone|iPod|IEMobile|Windows Phone|Opera Mini)/iu.test(userAgent);
|
|
6
|
+
}
|
package/dist/public/styles.css
CHANGED
|
@@ -561,6 +561,8 @@ input[type="checkbox"][hidden] { display: none; }
|
|
|
561
561
|
.panel-resize-handle:hover::after, .panel-resize-handle:focus-visible::after, body.is-panel-resizing .panel-resize-handle::after { background: var(--accent); }
|
|
562
562
|
body.is-panel-resizing { cursor: col-resize; user-select: none; }
|
|
563
563
|
.app-shell.left-panel-collapsed { --left-panel-width: 42px !important; }.app-shell.ai-panel-collapsed { --ai-panel-width: 42px !important; }
|
|
564
|
+
.app-shell.phone-client.ai-panel-collapsed:not(.ai-workspace-mode) { --ai-panel-width: 0px !important; }
|
|
565
|
+
.app-shell.phone-client.ai-panel-collapsed:not(.ai-workspace-mode) .ai-panel { display: none; }
|
|
564
566
|
.app-shell.left-panel-collapsed .left-panel, .app-shell.ai-panel-collapsed .ai-panel { padding: 0; overflow: hidden; }
|
|
565
567
|
.app-shell.left-panel-collapsed .left-panel > .panel-resize-handle,
|
|
566
568
|
.app-shell.left-panel-collapsed .left-panel-body > :not(.left-actions),
|
|
@@ -1415,7 +1417,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1415
1417
|
.chapter-line-number { position: absolute; left: 0; box-sizing: border-box; display: flex; width: 100%; align-items: flex-start; justify-content: center; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; font-variant-numeric: tabular-nums; text-align: center; }
|
|
1416
1418
|
.chapter-line-number-select { position: relative; display: flex; width: 100%; height: 100%; align-items: flex-start; justify-content: center; padding-inline: 3px; border: 0; background: transparent; color: inherit; font: inherit; font-variant-numeric: tabular-nums; cursor: pointer; }
|
|
1417
1419
|
.chapter-line-number-select:focus-visible { z-index: 1; outline: 1px solid var(--accent); outline-offset: -1px; }
|
|
1418
|
-
.chapter-line-annotation-count { position: absolute; top: -
|
|
1420
|
+
.chapter-line-annotation-count { position: absolute; top: -4px; right: -10px; z-index: 2; display: grid; min-width: 14px; height: 14px; place-items: center; padding: 0 3px; border: 1px solid var(--panel); border-radius: 999px; background: var(--accent); color: var(--paper); font-family: var(--font-latin), monospace; font-size: 9px; font-weight: 700; line-height: 1; cursor: pointer; box-shadow: 0 1px 3px rgba(49,42,32,.16); }
|
|
1419
1421
|
.chapter-line-annotation-count:hover, .chapter-line-annotation-count:focus-visible { background: var(--accent-dark); }
|
|
1420
1422
|
.chapter-line-annotation-count:focus-visible { outline: 1px solid var(--accent-dark); outline-offset: 1px; }
|
|
1421
1423
|
.chapter-line-number:hover { background: rgba(139,61,44,.08); color: var(--accent-dark); }
|
|
@@ -2655,6 +2657,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2655
2657
|
.message-body .markdown-image img { display: block; max-width: 100%; height: auto; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-soft); }
|
|
2656
2658
|
.message-body .markdown-image small { color: var(--muted); font-size: 9px; line-height: 1.45; text-align: center; }
|
|
2657
2659
|
.message-meta { margin-top: 8px; color: var(--muted); font-size: 9px; }
|
|
2660
|
+
.message-meta .ai-stream-character-count { display: inline-block; min-width: 6ch; font-variant-numeric: tabular-nums; text-align: right; }
|
|
2658
2661
|
.ai-process-details { margin: 0 0 10px; overflow: hidden; border: 1px solid var(--line); border-radius: 5px; background: color-mix(in srgb, var(--surface-soft) 82%, transparent); }
|
|
2659
2662
|
.ai-process-details > summary { display: flex; align-items: center; justify-content: space-between; gap: 9px; padding: 8px 10px; color: var(--muted); cursor: pointer; font-size: 10px; list-style: none; }
|
|
2660
2663
|
.ai-process-details > summary::-webkit-details-marker { display: none; }
|
|
@@ -3321,6 +3324,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3321
3324
|
.vditor-editor-host.vditor--dark { --border-color: var(--line); --panel-background-color: var(--surface); --toolbar-background-color: var(--surface-soft); --toolbar-icon-color: var(--muted); --toolbar-icon-hover-color: var(--accent); --textarea-background-color: var(--surface); --textarea-text-color: var(--ink); --heading-border-color: var(--line); --blockquote-color: var(--muted); }
|
|
3322
3325
|
.vditor-editor-host .vditor-toolbar { display: flex; align-items: center; flex: 0 0 auto; }
|
|
3323
3326
|
.vditor-editor-host .markdown-word-count { display: inline-flex; align-items: center; align-self: center; min-height: 28px; margin-left: 24px; padding: 0 10px; color: var(--muted); font: 10px/1 var(--font-latin), var(--font-cjk), sans-serif; white-space: nowrap; }
|
|
3327
|
+
.vditor-editor-host .markdown-word-count-value { display: inline-block; min-width: 6ch; font-variant-numeric: tabular-nums; text-align: right; }
|
|
3324
3328
|
.vditor-editor-host .vditor-content { position: relative; }
|
|
3325
3329
|
.vditor-editor-host .vditor-line-numbers { display: none; position: absolute; z-index: 1; inset: 0 auto 0 0; width: 42px; padding: 0; overflow: hidden; border-right: 1px solid var(--line); background: color-mix(in srgb, var(--surface-soft) 88%, transparent); color: var(--muted); font: 12px/1.75 var(--font-latin), monospace; text-align: center; user-select: none; pointer-events: none; will-change: transform; }
|
|
3326
3330
|
.vditor-editor-host.vditor.show-line-numbers .vditor-line-numbers { display: block; }
|
package/dist/store.js
CHANGED
|
@@ -5582,12 +5582,18 @@ export class Store {
|
|
|
5582
5582
|
throw notFound("AI 对话");
|
|
5583
5583
|
if (requiredString(conversation, "work_id") !== workId)
|
|
5584
5584
|
throw new AppError(400, "CONVERSATION_WORK_MISMATCH", "AI 对话不属于当前作品");
|
|
5585
|
-
|
|
5585
|
+
return this.aiConversationLockedModelId(conversationId);
|
|
5586
|
+
}
|
|
5587
|
+
aiConversationLockedModelId(conversationId) {
|
|
5588
|
+
const messages = this.db.all(`SELECT metadata_json FROM ai_conversation_messages
|
|
5586
5589
|
WHERE conversation_id = ? AND role = 'user'
|
|
5587
|
-
ORDER BY created_at, rowid
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5590
|
+
ORDER BY created_at, rowid`, conversationId);
|
|
5591
|
+
for (const message of messages) {
|
|
5592
|
+
const metadata = json(requiredString(message, "metadata_json"), {});
|
|
5593
|
+
if (typeof metadata.modelId === "string" && metadata.modelId.trim())
|
|
5594
|
+
return metadata.modelId.trim();
|
|
5595
|
+
}
|
|
5596
|
+
return null;
|
|
5591
5597
|
}
|
|
5592
5598
|
getAiConversationInjectedEntities(conversationId, workId) {
|
|
5593
5599
|
const conversation = this.db.get("SELECT work_id, injected_entities_json FROM ai_conversations WHERE id = ?", conversationId);
|
|
@@ -6070,7 +6076,11 @@ export class Store {
|
|
|
6070
6076
|
? JSON.stringify(normalizeWorkAgentTools(this.getWorkAiSettings(workId).agentTools))
|
|
6071
6077
|
: String(conversation.agent_tools_json), injectedEntitiesJson, systemClockText, timestamp, timestamp, currentRequestActor()?.userId ?? null);
|
|
6072
6078
|
for (const message of messages.slice(0, targetIndex + 1)) {
|
|
6073
|
-
|
|
6079
|
+
const role = requiredString(message, "role");
|
|
6080
|
+
const inheritedMetadata = json(requiredString(message, "metadata_json"), {});
|
|
6081
|
+
if (role === "user")
|
|
6082
|
+
delete inheritedMetadata.modelId;
|
|
6083
|
+
this.db.run("INSERT INTO ai_conversation_messages (id, conversation_id, role, content, citations_json, metadata_json, request_id, created_at, created_by_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", id("message"), forkId, role, requiredString(message, "content"), requiredString(message, "citations_json"), JSON.stringify(inheritedMetadata), optionalString(message, "request_id"), requiredString(message, "created_at"), currentRequestActor()?.userId ?? null);
|
|
6074
6084
|
}
|
|
6075
6085
|
if (normalizedRequestId) {
|
|
6076
6086
|
this.db.run("INSERT INTO ai_conversation_forks (source_conversation_id, source_message_id, request_id, conversation_id, created_at) VALUES (?, ?, ?, ?, ?)", conversationId, messageId, normalizedRequestId, forkId, timestamp);
|
|
@@ -6110,16 +6120,7 @@ export class Store {
|
|
|
6110
6120
|
mapAiConversation(row) {
|
|
6111
6121
|
const roleplayCharacterId = optionalString(row, "roleplay_character_id");
|
|
6112
6122
|
const roleplayUserCharacterId = optionalString(row, "roleplay_user_character_id");
|
|
6113
|
-
const
|
|
6114
|
-
WHERE conversation_id = ? AND role = 'user'
|
|
6115
|
-
ORDER BY created_at, rowid
|
|
6116
|
-
LIMIT 1`, requiredString(row, "id"));
|
|
6117
|
-
const firstUserMetadata = firstUserMessage
|
|
6118
|
-
? json(requiredString(firstUserMessage, "metadata_json"), {})
|
|
6119
|
-
: {};
|
|
6120
|
-
const lockedModelId = typeof firstUserMetadata.modelId === "string" && firstUserMetadata.modelId.trim()
|
|
6121
|
-
? firstUserMetadata.modelId.trim()
|
|
6122
|
-
: null;
|
|
6123
|
+
const lockedModelId = this.aiConversationLockedModelId(requiredString(row, "id"));
|
|
6123
6124
|
const roleplayCharacter = roleplayCharacterId
|
|
6124
6125
|
? this.db.get("SELECT id, name, code FROM characters WHERE id = ? AND work_id = ?", roleplayCharacterId, requiredString(row, "work_id"))
|
|
6125
6126
|
: undefined;
|