@musnows/scriverse 0.7.7 → 0.7.9
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 +209 -56
- package/dist/ai.js.map +1 -1
- package/dist/app.js +24 -6
- package/dist/app.js.map +1 -1
- package/dist/database.js +36 -2
- package/dist/database.js.map +1 -1
- package/dist/hybrid-search.js +36 -5
- package/dist/hybrid-search.js.map +1 -1
- package/dist/public/ai-message-meta.js +11 -2
- package/dist/public/app.js +113 -93
- package/dist/public/index.html +3 -3
- package/dist/public/styles.css +12 -7
- package/dist/store.js +138 -27
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +5 -4
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function resolveAiTokensPerSecond(outputTokens, durationMs) {
|
|
2
|
+
const tokenCount = Number(outputTokens);
|
|
3
|
+
const duration = Number(durationMs);
|
|
4
|
+
if (!Number.isFinite(tokenCount) || tokenCount < 0 || !Number.isFinite(duration) || duration <= 0) return null;
|
|
5
|
+
return Math.max(0, Math.round(tokenCount / (duration / 1000)));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function formatAiMessageMeta(modelDisplayName, outputTokens, cacheHitPercent, suffix = "", durationMs) {
|
|
2
9
|
const modelName = String(modelDisplayName || "模型").trim();
|
|
3
10
|
const tokenCount = Math.max(0, Math.round(Number(outputTokens) || 0)).toLocaleString("zh-CN");
|
|
11
|
+
const tokensPerSecond = resolveAiTokensPerSecond(outputTokens, durationMs);
|
|
4
12
|
const cachePercent = Number(cacheHitPercent);
|
|
5
13
|
const roundedCachePercent = Math.round((cachePercent + Number.EPSILON) * 10) / 10;
|
|
6
14
|
const cacheLabel = Number.isFinite(cachePercent)
|
|
7
15
|
? `缓存命中 ${Math.max(0, Math.min(100, roundedCachePercent)).toLocaleString("zh-CN")}%`
|
|
8
16
|
: "";
|
|
9
|
-
|
|
17
|
+
const speedLabel = tokensPerSecond === null ? "" : `${tokensPerSecond.toLocaleString("zh-CN")}\u00a0tok/s`;
|
|
18
|
+
return [modelName, `${tokenCount} tok`, speedLabel, cacheLabel, String(suffix || "").trim()].filter(Boolean).join(" · ");
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
export function estimateAiMessageTokens(value) {
|
package/dist/public/app.js
CHANGED
|
@@ -18,7 +18,7 @@ import { buildVditorLineNumberRows } from "/vditor-line-number-layout.js?v=20260
|
|
|
18
18
|
import { MIN_MODEL_CONTEXT_WINDOW, MODEL_PURPOSE_OPTIONS, isKimiModelId, modelContextWindowGuidance, modelFormValues, modelOptionLabel, modelPayload, supportsMultimodalModelProtocol } from "/model-config.js?v=20260803-multimodal-model-config-v2";
|
|
19
19
|
import { connectivityConfigurationSavedToast, connectivityTestErrorToast, connectivityTestResultToast } from "/ai-connectivity-test.js?v=20260812-connectivity-cooldown-v1";
|
|
20
20
|
import { shouldSendAiPrompt } from "/ai-prompt-keyboard.js?v=20260713-enter-to-send";
|
|
21
|
-
import { estimateAiMessageTokens, formatAiMessageMeta } from "/ai-message-meta.js?v=
|
|
21
|
+
import { estimateAiMessageTokens, formatAiMessageMeta } from "/ai-message-meta.js?v=20260814-ai-model-lock-v1";
|
|
22
22
|
import { createStreamTypewriter } from "/stream-typewriter.js?v=20260730-ai-stream-typewriter-v3";
|
|
23
23
|
import { assertAiStreamCompleted, readAiEventStream } from "/ai-stream-protocol.js?v=20260812-ai-stream-complete-v1";
|
|
24
24
|
import { buildUsageCalendar, formatCacheHitRate, formatTokenCount } from "/ai-usage.js?v=20260727-ai-usage-v1";
|
|
@@ -156,6 +156,7 @@ const state = {
|
|
|
156
156
|
aiTaskType: "chat",
|
|
157
157
|
aiContextScope: { type: "none" },
|
|
158
158
|
aiConversationId: null,
|
|
159
|
+
aiConversationModelId: null,
|
|
159
160
|
aiConversations: [],
|
|
160
161
|
aiRoleplayCharacter: null,
|
|
161
162
|
aiLastMessageAt: null,
|
|
@@ -2324,6 +2325,7 @@ async function openAiConversation(conversationId, hideHistory = true, focusMessa
|
|
|
2324
2325
|
if (String(conversation.workId ?? "") !== navigation.workId) throw new Error("AI 对话不属于当前作品");
|
|
2325
2326
|
upsertAiConversationSummary(conversation);
|
|
2326
2327
|
state.aiConversationId = conversation.id;
|
|
2328
|
+
state.aiConversationModelId = typeof conversation.modelId === "string" ? conversation.modelId : null;
|
|
2327
2329
|
state.aiPromptSent = conversation.messages.some((message) => message.role === "user");
|
|
2328
2330
|
applyAiConversationTaskType(conversation.taskType);
|
|
2329
2331
|
applyAiConversationContextScope(conversation.contextScope);
|
|
@@ -2366,6 +2368,7 @@ function focusAiConversationMessage(messageId) {
|
|
|
2366
2368
|
function applyNewAiConversation(conversation) {
|
|
2367
2369
|
upsertAiConversationSummary(conversation);
|
|
2368
2370
|
state.aiConversationId = conversation.id;
|
|
2371
|
+
state.aiConversationModelId = null;
|
|
2369
2372
|
state.aiPromptSent = false;
|
|
2370
2373
|
applyAiConversationTaskType(conversation.taskType);
|
|
2371
2374
|
applyAiConversationContextScope(conversation.contextScope);
|
|
@@ -2401,7 +2404,7 @@ function renderAiRoleplayCharacterSelect() {
|
|
|
2401
2404
|
const availableCharacters = state.characters.filter((character) => !character.mergedIntoCharacterId);
|
|
2402
2405
|
const options = [{ id: "", name: "选择角色卡" }, ...availableCharacters.map((character) => ({
|
|
2403
2406
|
id: String(character.id),
|
|
2404
|
-
name: String(character.name)
|
|
2407
|
+
name: `${String(character.name)}${character.isDead ? "(已死亡)" : ""}`
|
|
2405
2408
|
}))];
|
|
2406
2409
|
if (selectedId && !options.some((option) => option.id === selectedId)) {
|
|
2407
2410
|
options.push({ id: selectedId, name: String(state.aiRoleplayCharacter.name) });
|
|
@@ -2435,6 +2438,8 @@ function syncAiTaskOptions() {
|
|
|
2435
2438
|
$("#ai-scope").title = state.aiPromptSent
|
|
2436
2439
|
? "对话开始后不能切换上下文引用"
|
|
2437
2440
|
: roleplaySelected ? "角色扮演模式只使用角色自身的记忆" : "";
|
|
2441
|
+
$("#ai-model").disabled = interactionBusy || state.aiPromptSent;
|
|
2442
|
+
$("#ai-model").title = state.aiPromptSent ? "对话开始后不能切换模型" : "";
|
|
2438
2443
|
}
|
|
2439
2444
|
|
|
2440
2445
|
function applyAiConversationTaskType(taskType) {
|
|
@@ -3597,7 +3602,8 @@ async function refreshAuthCaptcha(target = "login") {
|
|
|
3597
3602
|
}
|
|
3598
3603
|
|
|
3599
3604
|
function clearAuthenticationOverlays() {
|
|
3600
|
-
|
|
3605
|
+
const toastRegion = $("#toast-region");
|
|
3606
|
+
toastRegion.replaceChildren();
|
|
3601
3607
|
document.querySelectorAll("[popover]").forEach((popover) => {
|
|
3602
3608
|
if (typeof popover.hidePopover === "function" && popover.matches(":popover-open")) popover.hidePopover();
|
|
3603
3609
|
});
|
|
@@ -3739,47 +3745,27 @@ function raiseToastRegion() {
|
|
|
3739
3745
|
region.showPopover();
|
|
3740
3746
|
}
|
|
3741
3747
|
|
|
3742
|
-
|
|
3743
|
-
const toastTimers = new Map();
|
|
3744
|
-
|
|
3745
|
-
function hideToastRegionIfEmpty() {
|
|
3748
|
+
function dismissDeleteToasts() {
|
|
3746
3749
|
const region = $("#toast-region");
|
|
3750
|
+
region.querySelectorAll(".delete-toast").forEach((element) => element.remove());
|
|
3747
3751
|
if (!region.childElementCount && typeof region.hidePopover === "function" && region.matches(":popover-open")) {
|
|
3748
3752
|
region.hidePopover();
|
|
3749
3753
|
}
|
|
3750
3754
|
}
|
|
3751
3755
|
|
|
3752
|
-
function
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
window.clearTimeout(timer);
|
|
3756
|
-
toastTimers.delete(element);
|
|
3757
|
-
}
|
|
3758
|
-
element.remove();
|
|
3759
|
-
hideToastRegionIfEmpty();
|
|
3760
|
-
}
|
|
3761
|
-
|
|
3762
|
-
function clearToastRegion() {
|
|
3763
|
-
toastTimers.forEach((timer) => window.clearTimeout(timer));
|
|
3764
|
-
toastTimers.clear();
|
|
3765
|
-
$("#toast-region").replaceChildren();
|
|
3766
|
-
hideToastRegionIfEmpty();
|
|
3767
|
-
}
|
|
3768
|
-
|
|
3769
|
-
function clearTransientToasts() {
|
|
3770
|
-
const region = $("#toast-region");
|
|
3771
|
-
[...region.children]
|
|
3772
|
-
.filter((element) => !element.classList.contains("toast-confirmation") && !element.classList.contains("chapter-insight-toast"))
|
|
3773
|
-
.forEach(removeToastElement);
|
|
3756
|
+
function deleteToast(message) {
|
|
3757
|
+
toast(message);
|
|
3758
|
+
$("#toast-region").lastElementChild?.classList.add("delete-toast");
|
|
3774
3759
|
}
|
|
3775
3760
|
|
|
3776
3761
|
function dismissChapterInsightToast() {
|
|
3777
3762
|
chapterInsightRequestId += 1;
|
|
3778
3763
|
const region = $("#toast-region");
|
|
3779
|
-
|
|
3780
|
-
if (element) removeToastElement(element);
|
|
3764
|
+
region.querySelector(".chapter-insight-toast")?.remove();
|
|
3781
3765
|
$("#insight-button").setAttribute("aria-expanded", "false");
|
|
3782
|
-
|
|
3766
|
+
if (!region.childElementCount && typeof region.hidePopover === "function" && region.matches(":popover-open")) {
|
|
3767
|
+
region.hidePopover();
|
|
3768
|
+
}
|
|
3783
3769
|
}
|
|
3784
3770
|
|
|
3785
3771
|
function toast(message, type = "info") {
|
|
@@ -3792,7 +3778,12 @@ function toast(message, type = "info") {
|
|
|
3792
3778
|
element.textContent = message;
|
|
3793
3779
|
region.append(element);
|
|
3794
3780
|
raiseToastRegion();
|
|
3795
|
-
|
|
3781
|
+
setTimeout(() => {
|
|
3782
|
+
element.remove();
|
|
3783
|
+
if (!region.childElementCount && typeof region.hidePopover === "function" && region.matches(":popover-open")) {
|
|
3784
|
+
region.hidePopover();
|
|
3785
|
+
}
|
|
3786
|
+
}, 3600);
|
|
3796
3787
|
}
|
|
3797
3788
|
|
|
3798
3789
|
async function runEntityEditorSave({ busyTarget, button, prepare, save }) {
|
|
@@ -4290,7 +4281,7 @@ async function initializePage() {
|
|
|
4290
4281
|
function showShelf() {
|
|
4291
4282
|
stopBackgroundTaskCenter();
|
|
4292
4283
|
dismissChapterInsightToast();
|
|
4293
|
-
|
|
4284
|
+
dismissDeleteToasts();
|
|
4294
4285
|
state.dirty = false;
|
|
4295
4286
|
settingsReturnContext = null;
|
|
4296
4287
|
updateDocumentTitle();
|
|
@@ -4591,7 +4582,7 @@ async function showWorkAudit() {
|
|
|
4591
4582
|
workAuditRecords = [];
|
|
4592
4583
|
workAuditNextPage = null;
|
|
4593
4584
|
dismissChapterInsightToast();
|
|
4594
|
-
|
|
4585
|
+
dismissDeleteToasts();
|
|
4595
4586
|
updateDocumentTitle(state.work);
|
|
4596
4587
|
$("#app").classList.add("shelf-mode");
|
|
4597
4588
|
$("#shelf-view").classList.add("hidden");
|
|
@@ -4769,7 +4760,7 @@ function renderS3BackupTargets() {
|
|
|
4769
4760
|
try {
|
|
4770
4761
|
await api(`/api/platform/backups/targets/${encodeURIComponent(target.id)}`, { method: "DELETE" });
|
|
4771
4762
|
await loadS3BackupData();
|
|
4772
|
-
|
|
4763
|
+
deleteToast(`备份目标“${target.name}”已删除`);
|
|
4773
4764
|
} catch (error) {
|
|
4774
4765
|
button.disabled = false;
|
|
4775
4766
|
toast(error.message, "error");
|
|
@@ -5137,7 +5128,7 @@ function renderMembers(members) {
|
|
|
5137
5128
|
const updated = await api(`/api/works/${encodeURIComponent(work.id)}/members/${encodeURIComponent(button.dataset.removeMember)}`, { method: "DELETE" });
|
|
5138
5129
|
renderMembers(updated);
|
|
5139
5130
|
renderMemberSelector();
|
|
5140
|
-
|
|
5131
|
+
deleteToast("协作者已移除");
|
|
5141
5132
|
} catch (error) { toast(error.message, "error"); }
|
|
5142
5133
|
}));
|
|
5143
5134
|
}
|
|
@@ -5514,7 +5505,7 @@ async function showSettingsHub() {
|
|
|
5514
5505
|
state.dirty = false;
|
|
5515
5506
|
}
|
|
5516
5507
|
dismissChapterInsightToast();
|
|
5517
|
-
|
|
5508
|
+
dismissDeleteToasts();
|
|
5518
5509
|
updateDocumentTitle(state.work);
|
|
5519
5510
|
$("#app").classList.add("shelf-mode");
|
|
5520
5511
|
$("#shelf-view").classList.add("hidden");
|
|
@@ -5562,7 +5553,7 @@ async function showPlatformAi() {
|
|
|
5562
5553
|
if (state.dirty && !(await confirmDiscardChanges("当前章节有未保存修改,进入平台 AI 管理将放弃本地修改。是否继续?"))) return false;
|
|
5563
5554
|
state.dirty = false;
|
|
5564
5555
|
dismissChapterInsightToast();
|
|
5565
|
-
|
|
5556
|
+
dismissDeleteToasts();
|
|
5566
5557
|
updateDocumentTitle();
|
|
5567
5558
|
$("#app").classList.add("shelf-mode");
|
|
5568
5559
|
$("#shelf-view").classList.add("hidden");
|
|
@@ -5585,7 +5576,7 @@ async function showPlatformUsage() {
|
|
|
5585
5576
|
if (state.dirty && !(await confirmDiscardChanges("当前章节有未保存修改,进入 Token 用量面板将放弃本地修改。是否继续?"))) return false;
|
|
5586
5577
|
state.dirty = false;
|
|
5587
5578
|
dismissChapterInsightToast();
|
|
5588
|
-
|
|
5579
|
+
dismissDeleteToasts();
|
|
5589
5580
|
updateDocumentTitle();
|
|
5590
5581
|
$("#app").classList.add("shelf-mode");
|
|
5591
5582
|
$("#shelf-view").classList.add("hidden");
|
|
@@ -5681,6 +5672,7 @@ function resetWorkScopedUiCaches() {
|
|
|
5681
5672
|
state.aiReferences = [];
|
|
5682
5673
|
state.aiPromptSent = false;
|
|
5683
5674
|
state.aiConversationId = null;
|
|
5675
|
+
state.aiConversationModelId = null;
|
|
5684
5676
|
state.aiConversations = [];
|
|
5685
5677
|
state.aiRoleplayCharacter = null;
|
|
5686
5678
|
renderAiCitations();
|
|
@@ -6140,7 +6132,7 @@ async function deleteChapter(chapterId) {
|
|
|
6140
6132
|
} else {
|
|
6141
6133
|
renderTree();
|
|
6142
6134
|
}
|
|
6143
|
-
|
|
6135
|
+
deleteToast(`已删除章节“${chapter.title}”,正文和版本记录已保留`);
|
|
6144
6136
|
} catch (error) {
|
|
6145
6137
|
resumeAutoSave();
|
|
6146
6138
|
toast(error.message, "error");
|
|
@@ -6349,6 +6341,7 @@ async function selectChapter(chapterId, { editMode = false } = {}) {
|
|
|
6349
6341
|
if (selectionRequestId === chapterSelectionRequestId) void loadChapterForeshadowReminders();
|
|
6350
6342
|
return false;
|
|
6351
6343
|
}
|
|
6344
|
+
dismissDeleteToasts();
|
|
6352
6345
|
if (selectionGeneration !== chapterSelectionRequestGeneration || selectionRequestId !== chapterSelectionRequestId || state.work?.id !== workId) return false;
|
|
6353
6346
|
cancelChapterAutoSave();
|
|
6354
6347
|
let selectedChapter = state.chapter;
|
|
@@ -6380,7 +6373,6 @@ async function selectChapter(chapterId, { editMode = false } = {}) {
|
|
|
6380
6373
|
clearChapterLineSelection();
|
|
6381
6374
|
scheduleChapterLineNumbers();
|
|
6382
6375
|
dismissChapterInsightToast();
|
|
6383
|
-
clearTransientToasts();
|
|
6384
6376
|
updateChapterStats();
|
|
6385
6377
|
if (!canEditProse()) setSaveState("正文只读");
|
|
6386
6378
|
else if (chapterEditorReadOnly) setSaveState("阅读模式");
|
|
@@ -6899,7 +6891,7 @@ function showWelcome(hasWork = false) {
|
|
|
6899
6891
|
chapterSelectionRequestId += 1;
|
|
6900
6892
|
clearChapterForeshadowReminders({ invalidateRequest: true });
|
|
6901
6893
|
dismissChapterInsightToast();
|
|
6902
|
-
|
|
6894
|
+
dismissDeleteToasts();
|
|
6903
6895
|
$("#editor-view").classList.add("hidden");
|
|
6904
6896
|
$("#module-view").classList.add("hidden");
|
|
6905
6897
|
$("#welcome-view").classList.remove("hidden");
|
|
@@ -6944,6 +6936,7 @@ async function showModule(module) {
|
|
|
6944
6936
|
}
|
|
6945
6937
|
if (module !== "editor" && state.module === "editor" && !(await confirmDiscardChanges())) return;
|
|
6946
6938
|
if (module !== "editor" && state.module === "editor" && state.dirty) setSaveState("已放弃修改");
|
|
6939
|
+
dismissDeleteToasts();
|
|
6947
6940
|
state.module = module;
|
|
6948
6941
|
if (module !== "editor") {
|
|
6949
6942
|
chapterSelectionRequestId += 1;
|
|
@@ -6963,7 +6956,6 @@ async function showModule(module) {
|
|
|
6963
6956
|
}
|
|
6964
6957
|
showSystemStatus();
|
|
6965
6958
|
dismissChapterInsightToast();
|
|
6966
|
-
clearTransientToasts();
|
|
6967
6959
|
$("#welcome-view").classList.add("hidden");
|
|
6968
6960
|
$("#editor-view").classList.add("hidden");
|
|
6969
6961
|
$("#module-view").classList.remove("hidden");
|
|
@@ -7143,7 +7135,7 @@ async function deleteManagedEntity({ typeLabel, item, endpoint, refresh, warning
|
|
|
7143
7135
|
await onDeleted?.();
|
|
7144
7136
|
await refresh();
|
|
7145
7137
|
await loadAiReferences();
|
|
7146
|
-
|
|
7138
|
+
deleteToast(`已删除${typeLabel}“${item.name}”`);
|
|
7147
7139
|
} catch (error) {
|
|
7148
7140
|
toast(error.message, "error");
|
|
7149
7141
|
}
|
|
@@ -7445,28 +7437,17 @@ async function deleteDraft(item) {
|
|
|
7445
7437
|
if (!await confirmToast(`确认删除想法“${item.title}”吗?想法将从当前列表移除。`, {
|
|
7446
7438
|
title: "删除想法",
|
|
7447
7439
|
confirmLabel: "继续删除"
|
|
7448
|
-
}))
|
|
7449
|
-
openDraftDialog(item);
|
|
7450
|
-
return;
|
|
7451
|
-
}
|
|
7440
|
+
})) return;
|
|
7452
7441
|
if (!await confirmToast(`删除想法“${item.title}”后将从想法列表移除,版本历史仍会保留。仍要删除吗?`, {
|
|
7453
7442
|
title: "删除操作需要再次确认",
|
|
7454
7443
|
confirmLabel: "确认删除"
|
|
7455
|
-
}))
|
|
7456
|
-
openDraftDialog(item);
|
|
7457
|
-
return;
|
|
7458
|
-
}
|
|
7444
|
+
})) return;
|
|
7459
7445
|
try {
|
|
7460
7446
|
await api(`/api/drafts/${encodeURIComponent(item.id)}`, { method: "DELETE", body: { expectedVersionNo: item.versionNo } });
|
|
7461
7447
|
await renderDrafts(moduleListPages.drafts);
|
|
7462
|
-
|
|
7448
|
+
deleteToast("想法已删除");
|
|
7463
7449
|
} catch (error) {
|
|
7464
7450
|
toast(error.message, "error");
|
|
7465
|
-
try {
|
|
7466
|
-
openDraftDialog(await api(`/api/drafts/${encodeURIComponent(item.id)}`));
|
|
7467
|
-
} catch (reloadError) {
|
|
7468
|
-
toast(reloadError.message, "error");
|
|
7469
|
-
}
|
|
7470
7451
|
}
|
|
7471
7452
|
}
|
|
7472
7453
|
|
|
@@ -7474,19 +7455,14 @@ function openDraftDialog(item = null, { readOnly = false } = {}) {
|
|
|
7474
7455
|
const viewOnly = readOnly || !canEditModule("drafts");
|
|
7475
7456
|
const volumeOptions = [["", "全局(不绑定分卷)"], ...(state.work?.volumes ?? []).map((volume) => [volume.id, volume.title])];
|
|
7476
7457
|
const settingModuleOptions = [["", "全局(不绑定设定模块)"], ...draftSettingModules];
|
|
7477
|
-
const management = item && !viewOnly ? `<section class="entity-dialog-management" aria-label="想法操作">
|
|
7478
|
-
<div><strong>想法操作</strong><small>删除后将从想法列表移除,版本历史仍会保留。</small></div>
|
|
7479
|
-
<div class="entity-dialog-management-actions"><button class="danger-button" type="button" data-dialog-draft-delete>删除想法</button></div>
|
|
7480
|
-
</section>` : "";
|
|
7481
7458
|
const fields = field("draftType", "想法类型", "select", item?.draftType ?? "prose", [["prose", "正文想法"], ["setting", "设定想法"]])
|
|
7482
7459
|
+ `<div class="draft-binding-field" data-draft-binding-field="prose">${field("volumeId", "绑定分卷", "select", item?.volumeId ?? "", volumeOptions)}</div>`
|
|
7483
7460
|
+ `<div class="draft-binding-field" data-draft-binding-field="setting">${field("settingModule", "绑定设定模块", "select", item?.settingModule ?? "", settingModuleOptions)}</div>`
|
|
7484
|
-
+ field("title", "标题", "text", item?.title ?? "")
|
|
7485
7461
|
+ field("content", "内容", "markdown", item?.content ?? "", {
|
|
7486
7462
|
placeholder: "记录尚未定稿的片段、方向或设定想法……",
|
|
7487
7463
|
attachmentModule: "drafts",
|
|
7488
7464
|
readOnly: viewOnly
|
|
7489
|
-
})
|
|
7465
|
+
});
|
|
7490
7466
|
openDialog(item ? viewOnly ? "查看想法" : "编辑想法" : "新建想法", fields, async (form) => {
|
|
7491
7467
|
if (viewOnly) return;
|
|
7492
7468
|
const title = String(form.get("title") ?? "").trim();
|
|
@@ -7511,7 +7487,9 @@ function openDraftDialog(item = null, { readOnly = false } = {}) {
|
|
|
7511
7487
|
hideCancel: viewOnly,
|
|
7512
7488
|
editor: true,
|
|
7513
7489
|
errorPrefix: "想法保存失败:",
|
|
7514
|
-
meta: "这里记录未确认的临时想法,可能采用,也可能永远不会写入正文或正式设定。"
|
|
7490
|
+
meta: "这里记录未确认的临时想法,可能采用,也可能永远不会写入正文或正式设定。",
|
|
7491
|
+
titleInput: { value: item?.title ?? "", readOnly: viewOnly, placeholder: "输入想法标题", label: "想法标题" },
|
|
7492
|
+
dangerAction: item && !viewOnly ? { label: "删除想法", onClick: () => deleteDraft(item) } : null
|
|
7515
7493
|
});
|
|
7516
7494
|
const draftTypeSelect = $("#dialog-fields").querySelector('select[name="draftType"]');
|
|
7517
7495
|
const syncDraftBindingFields = () => {
|
|
@@ -7531,9 +7509,6 @@ function openDraftDialog(item = null, { readOnly = false } = {}) {
|
|
|
7531
7509
|
else control.readOnly = true;
|
|
7532
7510
|
});
|
|
7533
7511
|
}
|
|
7534
|
-
$("#dialog-fields").querySelector("[data-dialog-draft-delete]")?.addEventListener("click", () => {
|
|
7535
|
-
void deleteDraft(item);
|
|
7536
|
-
});
|
|
7537
7512
|
}
|
|
7538
7513
|
|
|
7539
7514
|
async function renderDrafts(page = moduleListPages.drafts) {
|
|
@@ -8015,7 +7990,7 @@ async function deleteTimelineEvent(item, page = moduleListPages.timeline) {
|
|
|
8015
7990
|
try {
|
|
8016
7991
|
await api(`/api/timeline/${encodeURIComponent(item.id)}`, { method: "DELETE", body: { expectedVersionNo: item.versionNo } });
|
|
8017
7992
|
await renderTimeline(page);
|
|
8018
|
-
|
|
7993
|
+
deleteToast(`已删除时间事件“${item.name}”`);
|
|
8019
7994
|
} catch (error) {
|
|
8020
7995
|
toast(error.message, "error");
|
|
8021
7996
|
}
|
|
@@ -8075,6 +8050,7 @@ async function renderTimeline(page = moduleListPages.timeline) {
|
|
|
8075
8050
|
$("#module-content").querySelectorAll("[data-timeline-track-tab]").forEach((button) => button.addEventListener("click", async () => {
|
|
8076
8051
|
const nextTrackId = button.dataset.timelineTrackTab ?? "";
|
|
8077
8052
|
if (String(nextTrackId) === String(timelineActiveTrackId)) return;
|
|
8053
|
+
dismissDeleteToasts();
|
|
8078
8054
|
timelineActiveTrackId = nextTrackId;
|
|
8079
8055
|
await renderTimeline(1);
|
|
8080
8056
|
}));
|
|
@@ -10498,7 +10474,11 @@ function applyAiModels(models) {
|
|
|
10498
10474
|
select.innerHTML = state.models.length
|
|
10499
10475
|
? state.models.map((model) => `<option value="${esc(model.id)}">${esc(modelOptionLabel(model))}</option>`).join("")
|
|
10500
10476
|
: '<option value="">请先配置模型</option>';
|
|
10477
|
+
if (state.aiConversationModelId && state.models.some((model) => model.id === state.aiConversationModelId)) {
|
|
10478
|
+
select.value = state.aiConversationModelId;
|
|
10479
|
+
}
|
|
10501
10480
|
setAiContextMeter(null);
|
|
10481
|
+
syncAiTaskOptions();
|
|
10502
10482
|
}
|
|
10503
10483
|
|
|
10504
10484
|
async function ensureAiModelsLoaded() {
|
|
@@ -10866,10 +10846,18 @@ function openDialog(title, fields, onSubmit, eyebrow = "新增", options = {}) {
|
|
|
10866
10846
|
const submit = $("#dialog-submit");
|
|
10867
10847
|
const submitStatus = $("#dialog-submit-status");
|
|
10868
10848
|
const submitStatusMessage = $("#dialog-submit-status-message");
|
|
10849
|
+
const titleInput = $("#dialog-title-input");
|
|
10869
10850
|
const submitLabel = options.submitLabel ?? "保存";
|
|
10870
10851
|
let submitting = false;
|
|
10871
10852
|
let disabledStates = [];
|
|
10872
10853
|
$("#dialog-title").textContent = title;
|
|
10854
|
+
$("#dialog-title").classList.toggle("hidden", Boolean(options.titleInput));
|
|
10855
|
+
titleInput.classList.toggle("hidden", !options.titleInput);
|
|
10856
|
+
titleInput.name = options.titleInput ? "title" : "";
|
|
10857
|
+
titleInput.value = options.titleInput ? String(options.titleInput.value ?? "") : "";
|
|
10858
|
+
titleInput.placeholder = options.titleInput?.placeholder ?? "";
|
|
10859
|
+
titleInput.readOnly = Boolean(options.titleInput?.readOnly);
|
|
10860
|
+
titleInput.setAttribute("aria-label", options.titleInput?.label ?? "记录标题");
|
|
10873
10861
|
$("#dialog-eyebrow").textContent = eyebrow;
|
|
10874
10862
|
$("#dialog-meta").textContent = options.meta ?? "";
|
|
10875
10863
|
$("#dialog-meta").classList.toggle("hidden", !options.meta);
|
|
@@ -10880,6 +10868,17 @@ function openDialog(title, fields, onSubmit, eyebrow = "新增", options = {}) {
|
|
|
10880
10868
|
form.classList.remove("is-submitting");
|
|
10881
10869
|
form.removeAttribute("aria-busy");
|
|
10882
10870
|
$("#dynamic-form .dialog-actions [value='cancel']").classList.toggle("hidden", Boolean(options.hideCancel));
|
|
10871
|
+
const dialogActions = form.querySelector(".dialog-actions");
|
|
10872
|
+
dialogActions?.querySelector("[data-dialog-danger-action]")?.remove();
|
|
10873
|
+
if (options.dangerAction) {
|
|
10874
|
+
const dangerButton = document.createElement("button");
|
|
10875
|
+
dangerButton.className = "danger-button dialog-danger-action";
|
|
10876
|
+
dangerButton.type = "button";
|
|
10877
|
+
dangerButton.dataset.dialogDangerAction = "";
|
|
10878
|
+
dangerButton.textContent = options.dangerAction.label;
|
|
10879
|
+
dangerButton.addEventListener("click", () => { void options.dangerAction.onClick(); });
|
|
10880
|
+
dialogActions?.insertBefore(dangerButton, dialogActions.querySelector("[value='cancel']"));
|
|
10881
|
+
}
|
|
10883
10882
|
dialog.classList.toggle("wide-dialog", Boolean(options.wide));
|
|
10884
10883
|
dialog.classList.toggle("trace-dialog", Boolean(options.trace));
|
|
10885
10884
|
dialog.classList.toggle("large-dialog", Boolean(options.large));
|
|
@@ -11006,7 +11005,7 @@ function bindWorkCoverControls(work) {
|
|
|
11006
11005
|
Object.assign(work, updated);
|
|
11007
11006
|
refreshWorkCoverField(work);
|
|
11008
11007
|
renderShelf();
|
|
11009
|
-
|
|
11008
|
+
deleteToast("封面已移除");
|
|
11010
11009
|
} catch (error) {
|
|
11011
11010
|
toast(error.message, "error");
|
|
11012
11011
|
}
|
|
@@ -11193,7 +11192,7 @@ async function deleteWork(work) {
|
|
|
11193
11192
|
state.chapter = null;
|
|
11194
11193
|
}
|
|
11195
11194
|
await loadWorks();
|
|
11196
|
-
|
|
11195
|
+
deleteToast(`作品“${work.title}”已移入回收站`);
|
|
11197
11196
|
} catch (error) {
|
|
11198
11197
|
if (deletingCurrentWork && state.dirty) scheduleChapterAutoSave();
|
|
11199
11198
|
toast(error.message, "error");
|
|
@@ -11263,7 +11262,7 @@ async function deleteVolume(item) {
|
|
|
11263
11262
|
state.collapsedVolumeIds.delete(item.id);
|
|
11264
11263
|
state.work = await api(`/api/works/${state.work.id}`);
|
|
11265
11264
|
renderTree();
|
|
11266
|
-
|
|
11265
|
+
deleteToast(`分卷“${item.title}”已移入回收站`);
|
|
11267
11266
|
} catch (error) {
|
|
11268
11267
|
toast(error.message, "error");
|
|
11269
11268
|
openVolumeDialog(item);
|
|
@@ -11956,7 +11955,7 @@ function knowledgeSectionEditorHtml(section = null) {
|
|
|
11956
11955
|
<div><span class="eyebrow">${label} Markdown 设定</span><input id="knowledge-section-title" class="character-section-editor-title-input" maxlength="200" value="${esc(section?.title ?? "")}" placeholder="新建设定" aria-label="设定标题" required></div>
|
|
11957
11956
|
<button class="entity-editor-back" type="button" data-knowledge-section-edit-close>返回设定列表</button>
|
|
11958
11957
|
</header>
|
|
11959
|
-
<section class="character-markdown-editor" aria-label="${section ? "编辑" : "新建"}${label} Markdown 设定">
|
|
11958
|
+
<section class="character-markdown-editor knowledge-markdown-editor" aria-label="${section ? "编辑" : "新建"}${label} Markdown 设定">
|
|
11960
11959
|
<div id="knowledge-section-markdown" class="vditor-editor-host" data-vditor-editor aria-label="Markdown 编辑器"></div>
|
|
11961
11960
|
<div class="character-markdown-editor-footer">
|
|
11962
11961
|
<div class="character-markdown-editor-actions"><button type="button" data-knowledge-section-edit-cancel>取消</button><button type="button" class="primary-button" data-knowledge-section-edit-save>${section ? "保存设定" : "添加设定"}</button></div>
|
|
@@ -12232,7 +12231,7 @@ function renderCharacterMarkdownSections() {
|
|
|
12232
12231
|
characterEditorSections = await api(`/api/characters/${characterEditorItem.id}/sections`);
|
|
12233
12232
|
renderCharacterMarkdownSections();
|
|
12234
12233
|
await Promise.all([renderCharacters(), loadAiReferences()]);
|
|
12235
|
-
|
|
12234
|
+
deleteToast("人物 Markdown 章节已删除");
|
|
12236
12235
|
} catch (error) {
|
|
12237
12236
|
button.disabled = false;
|
|
12238
12237
|
toast(error.message, "error");
|
|
@@ -12940,7 +12939,7 @@ async function openRelationshipDialog(item, options = {}) {
|
|
|
12940
12939
|
try {
|
|
12941
12940
|
await api(`/api/relationships/${item.id}`, { method: "DELETE", body: { expectedVersionNo: item.versionNo } });
|
|
12942
12941
|
await Promise.all([refreshRelationshipSurfaces(options.characterId ?? null), loadAiReferences()]);
|
|
12943
|
-
|
|
12942
|
+
deleteToast(`已删除人物关系“${relationshipName}”`);
|
|
12944
12943
|
} catch (error) {
|
|
12945
12944
|
reopenDialog();
|
|
12946
12945
|
toast(error.message, "error");
|
|
@@ -13465,12 +13464,13 @@ async function sendAiWithOptions({ ignoreContextWarning = false } = {}) {
|
|
|
13465
13464
|
"user",
|
|
13466
13465
|
instruction,
|
|
13467
13466
|
citations,
|
|
13468
|
-
{},
|
|
13467
|
+
{ modelId },
|
|
13469
13468
|
{ signal: request.signal }
|
|
13470
13469
|
);
|
|
13471
13470
|
assertAiRequestCurrent(request);
|
|
13472
13471
|
updateAiConversationSummaryFromMessage(persistedUserMessage);
|
|
13473
13472
|
requestHolder.snapshot = aiRequestManager.bind(request, { userMessageId: persistedUserMessage.id });
|
|
13473
|
+
state.aiConversationModelId = modelId;
|
|
13474
13474
|
state.aiPromptSent = true;
|
|
13475
13475
|
syncAiTaskOptions();
|
|
13476
13476
|
renderAiRoleplayCharacterSelect();
|
|
@@ -13508,7 +13508,7 @@ async function sendAiWithOptions({ ignoreContextWarning = false } = {}) {
|
|
|
13508
13508
|
const request = assertAiRequestCurrent(requestHolder.snapshot);
|
|
13509
13509
|
suggestion = await api(`/api/works/${encodeURIComponent(request.workId)}/suggestions`, {
|
|
13510
13510
|
method: "POST",
|
|
13511
|
-
body: { taskType, instruction, scope, modelId, citations },
|
|
13511
|
+
body: { taskType, instruction, scope, modelId, citations, conversationId: requestHolder.snapshot.conversationId },
|
|
13512
13512
|
signal: request.signal
|
|
13513
13513
|
});
|
|
13514
13514
|
assertAiRequestCurrent(request);
|
|
@@ -13518,7 +13518,7 @@ async function sendAiWithOptions({ ignoreContextWarning = false } = {}) {
|
|
|
13518
13518
|
if (suggestionFailed) setAiAssistantStatus("error");
|
|
13519
13519
|
setAiContextMeter(suggestion.contextUsage, false);
|
|
13520
13520
|
assistantContent = suggestion.content;
|
|
13521
|
-
assistantMetadata = { modelDisplayName: suggestion.model?.displayName, outputTokens: suggestion.outputTokens, cacheHitPercent: suggestion.cacheHitPercent };
|
|
13521
|
+
assistantMetadata = { modelId, modelDisplayName: suggestion.model?.displayName, outputTokens: suggestion.outputTokens, cacheHitPercent: suggestion.cacheHitPercent, processDurationMs: suggestion.processDurationMs };
|
|
13522
13522
|
}
|
|
13523
13523
|
try {
|
|
13524
13524
|
const request = assertAiRequestCurrent(requestHolder.snapshot);
|
|
@@ -13738,6 +13738,10 @@ async function streamChat(requestHolder, body, idempotencyKey) {
|
|
|
13738
13738
|
}
|
|
13739
13739
|
requestHolder.snapshot = aiRequestManager.bind(requestHolder.snapshot, { userMessageId: persistedUserMessage.id });
|
|
13740
13740
|
updateAiConversationSummaryFromMessage(persistedUserMessage);
|
|
13741
|
+
const lockedModelId = typeof persistedUserMessage.metadata?.modelId === "string"
|
|
13742
|
+
? persistedUserMessage.metadata.modelId
|
|
13743
|
+
: typeof body.modelId === "string" ? body.modelId : null;
|
|
13744
|
+
state.aiConversationModelId = lockedModelId;
|
|
13741
13745
|
state.aiPromptSent = true;
|
|
13742
13746
|
syncAiTaskOptions();
|
|
13743
13747
|
renderAiRoleplayCharacterSelect();
|
|
@@ -13808,10 +13812,12 @@ async function streamChat(requestHolder, body, idempotencyKey) {
|
|
|
13808
13812
|
message.querySelector(".message-heading > span").textContent = "助手";
|
|
13809
13813
|
toolCalls = Array.isArray(payload.toolCalls) ? payload.toolCalls : toolCalls;
|
|
13810
13814
|
processSteps = Array.isArray(payload.processSteps) ? payload.processSteps : processSteps;
|
|
13811
|
-
const processDurationMs =
|
|
13815
|
+
const processDurationMs = Number.isFinite(payload.processDurationMs) && payload.processDurationMs >= 0
|
|
13816
|
+
? payload.processDurationMs
|
|
13817
|
+
: elapsedProcessTime();
|
|
13812
13818
|
generatedMetadata = { modelDisplayName: payload.model?.displayName, outputTokens: payload.outputTokens, cacheHitPercent: payload.cacheHitPercent, toolCalls, processSteps, processDurationMs };
|
|
13813
13819
|
renderAiProcessSteps(message, processSteps, true, processDurationMs);
|
|
13814
|
-
meta.textContent = formatAiMessageMeta(payload.model?.displayName, payload.outputTokens, payload.cacheHitPercent);
|
|
13820
|
+
meta.textContent = formatAiMessageMeta(payload.model?.displayName, payload.outputTokens, payload.cacheHitPercent, "", processDurationMs);
|
|
13815
13821
|
attachAssistantCopyAction(message, streamedText);
|
|
13816
13822
|
scrollAiFeedToBottom();
|
|
13817
13823
|
} else if (eventName === "request_status") {
|
|
@@ -13929,11 +13935,16 @@ function appendMessage(role, text, citations = [], createdAt = null, metadata =
|
|
|
13929
13935
|
}
|
|
13930
13936
|
message.append(references);
|
|
13931
13937
|
}
|
|
13932
|
-
|
|
13933
|
-
|
|
13938
|
+
const processSteps = role === "assistant"
|
|
13939
|
+
? (Array.isArray(metadata?.processSteps) && metadata.processSteps.length
|
|
13934
13940
|
? metadata.processSteps
|
|
13935
|
-
: (Array.isArray(metadata?.toolCalls) ? metadata.toolCalls : []).map((toolCall) => aiToolProcessStep(toolCall))
|
|
13936
|
-
|
|
13941
|
+
: (Array.isArray(metadata?.toolCalls) ? metadata.toolCalls : []).map((toolCall) => aiToolProcessStep(toolCall)))
|
|
13942
|
+
: [];
|
|
13943
|
+
const processDurationMs = role === "assistant"
|
|
13944
|
+
? resolveAiProcessDuration(metadata, processSteps, createdAt)
|
|
13945
|
+
: null;
|
|
13946
|
+
if (role === "assistant") {
|
|
13947
|
+
renderAiProcessSteps(message, processSteps, true, processDurationMs);
|
|
13937
13948
|
}
|
|
13938
13949
|
if (role === "assistant" && !text.startsWith("调用失败:")) {
|
|
13939
13950
|
const selectedModel = state.models.find((model) => model.id === $("#ai-model").value) ?? state.models[0];
|
|
@@ -13943,7 +13954,7 @@ function appendMessage(role, text, citations = [], createdAt = null, metadata =
|
|
|
13943
13954
|
meta.className = "message-meta";
|
|
13944
13955
|
meta.textContent = isInterrupted
|
|
13945
13956
|
? formatAiStreamInterruptionMeta(interruptionCode, text.length)
|
|
13946
|
-
: formatAiMessageMeta(modelDisplayName, outputTokens, metadata?.cacheHitPercent);
|
|
13957
|
+
: formatAiMessageMeta(modelDisplayName, outputTokens, metadata?.cacheHitPercent, "", processDurationMs);
|
|
13947
13958
|
message.append(meta);
|
|
13948
13959
|
attachAssistantCopyAction(message, text);
|
|
13949
13960
|
}
|
|
@@ -13958,7 +13969,7 @@ function appendSuggestion(suggestion, createdAt = null, messageId = null) {
|
|
|
13958
13969
|
const applicable = suggestion.action !== "note";
|
|
13959
13970
|
const guard = suggestion.guard;
|
|
13960
13971
|
const guardHtml = guard ? `<section class="guard-card ${esc(guard.status)}" data-testid="continuation-guard"><strong>${guard.status === "clear" ? "一致性守卫:未发现冲突" : guard.status === "warning" ? `一致性守卫:发现 ${guard.issues.length} 项风险` : "一致性守卫:检查失败"}</strong>${guard.status === "failed" ? `<p>${esc(guard.failure || "无法完成检查,请谨慎采纳")}</p>` : guard.issues.map((issue) => `<p><b>${esc(levelLabel(issue.severity))} · ${esc(reviewItemTypeLabel(issue.type))}</b> ${esc(issue.title)}${issue.description ? `:${esc(issue.description)}` : ""}</p>`).join("")}</section>` : "";
|
|
13961
|
-
message.innerHTML = `<div class="message-body">${renderMarkdown(suggestion.content)}</div><div class="message-meta">${esc(formatAiMessageMeta(suggestion.model?.displayName, suggestion.outputTokens, suggestion.cacheHitPercent, `基于 v${suggestion.chapterVersion ?? "-"}
|
|
13972
|
+
message.innerHTML = `<div class="message-body">${renderMarkdown(suggestion.content)}</div><div class="message-meta">${esc(formatAiMessageMeta(suggestion.model?.displayName, suggestion.outputTokens, suggestion.cacheHitPercent, `基于 v${suggestion.chapterVersion ?? "-"}`, suggestion.processDurationMs))}</div>${guardHtml}${applicable ? '<div class="message-actions"><button data-action="accept">采纳到正文</button><button data-action="reject">拒绝</button></div>' : ""}`;
|
|
13962
13973
|
attachMessageHeading(message, "助手建议", createdAt ?? undefined);
|
|
13963
13974
|
attachAssistantCopyAction(message, suggestion.content);
|
|
13964
13975
|
attachMessageIdentity(message, messageId);
|
|
@@ -14270,7 +14281,7 @@ function renderWorkRecycleBin(result) {
|
|
|
14270
14281
|
});
|
|
14271
14282
|
await loadWorkRecycleBin();
|
|
14272
14283
|
dialog.showModal();
|
|
14273
|
-
|
|
14284
|
+
deleteToast(`已彻底删除作品“${work.title}”`);
|
|
14274
14285
|
} catch (error) {
|
|
14275
14286
|
dialog.showModal();
|
|
14276
14287
|
toast(error.message, "error");
|
|
@@ -14376,7 +14387,7 @@ function renderChapterRecycleBin(result) {
|
|
|
14376
14387
|
renderTree();
|
|
14377
14388
|
await loadChapterRecycleBin();
|
|
14378
14389
|
dialog.showModal();
|
|
14379
|
-
|
|
14390
|
+
deleteToast(`已彻底删除分卷“${volume.title}”`);
|
|
14380
14391
|
} catch (error) {
|
|
14381
14392
|
dialog.showModal();
|
|
14382
14393
|
toast(error.message, "error");
|
|
@@ -14432,7 +14443,7 @@ function renderChapterRecycleBin(result) {
|
|
|
14432
14443
|
renderTree();
|
|
14433
14444
|
await loadChapterRecycleBin();
|
|
14434
14445
|
dialog.showModal();
|
|
14435
|
-
|
|
14446
|
+
deleteToast(`已彻底删除章节“${chapter.title}”`);
|
|
14436
14447
|
} catch (error) {
|
|
14437
14448
|
dialog.showModal();
|
|
14438
14449
|
toast(error.message, "error");
|
|
@@ -14937,7 +14948,7 @@ $("#avatar-remove-button").addEventListener("click", async () => {
|
|
|
14937
14948
|
const updated = await api("/api/auth/avatar", { method: "DELETE" });
|
|
14938
14949
|
applyAuthenticatedUser({ user: updated, csrfToken: state.csrfToken });
|
|
14939
14950
|
renderProfileAvatar();
|
|
14940
|
-
|
|
14951
|
+
deleteToast("头像已移除");
|
|
14941
14952
|
} catch (error) {
|
|
14942
14953
|
toast(error.message, "error");
|
|
14943
14954
|
} finally {
|
|
@@ -15482,7 +15493,13 @@ $("#ai-prompt").addEventListener("focus", () => {
|
|
|
15482
15493
|
$("#ai-model").addEventListener("focus", () => {
|
|
15483
15494
|
ensureAiModelsLoaded().catch((error) => toast(`模型加载失败:${error.message}`, "error"));
|
|
15484
15495
|
});
|
|
15485
|
-
$("#ai-model").addEventListener("change", () =>
|
|
15496
|
+
$("#ai-model").addEventListener("change", (event) => {
|
|
15497
|
+
if (state.aiPromptSent) {
|
|
15498
|
+
event.currentTarget.value = state.aiConversationModelId ?? event.currentTarget.value;
|
|
15499
|
+
return toast("当前对话已经开始,请新建对话后再切换模型", "error");
|
|
15500
|
+
}
|
|
15501
|
+
setAiContextMeter(null);
|
|
15502
|
+
});
|
|
15486
15503
|
$("#ai-roleplay-character").addEventListener("focus", async () => {
|
|
15487
15504
|
try {
|
|
15488
15505
|
await ensureAiReferencesLoaded();
|
|
@@ -15990,7 +16007,10 @@ document.addEventListener("visibilitychange", () => {
|
|
|
15990
16007
|
if (state.user?.role === "admin" && !systemRestartDetected) void refreshS3BackupEvents();
|
|
15991
16008
|
void refreshSystemHealth();
|
|
15992
16009
|
});
|
|
15993
|
-
|
|
16010
|
+
document.addEventListener("click", (event) => {
|
|
16011
|
+
if (event.target instanceof Element && event.target.closest('[role="tab"]')) dismissDeleteToasts();
|
|
16012
|
+
}, true);
|
|
16013
|
+
window.addEventListener("pagehide", dismissDeleteToasts);
|
|
15994
16014
|
window.addEventListener("beforeunload", (event) => {
|
|
15995
16015
|
if (hasUnsavedEditorChanges()) event.preventDefault();
|
|
15996
16016
|
});
|
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=20260814-
|
|
13
|
+
<link rel="stylesheet" href="/styles.css?v=20260814-vditor-ui-fixes-v1">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
<dialog id="form-dialog" class="dialog">
|
|
513
513
|
<form id="dynamic-form" method="dialog">
|
|
514
514
|
<div class="dialog-header">
|
|
515
|
-
<div><span id="dialog-eyebrow" class="eyebrow">新增</span><h2 id="dialog-title">创建记录</h2><p id="dialog-meta" class="dialog-header-meta hidden"></p></div>
|
|
515
|
+
<div><span id="dialog-eyebrow" class="eyebrow">新增</span><h2 id="dialog-title">创建记录</h2><input id="dialog-title-input" class="dialog-title-input hidden" type="text" autocomplete="off" aria-label="记录标题"><p id="dialog-meta" class="dialog-header-meta hidden"></p></div>
|
|
516
516
|
<button class="dialog-close" value="cancel" aria-label="关闭" type="submit">×</button>
|
|
517
517
|
</div>
|
|
518
518
|
<div id="dialog-fields" class="dialog-fields"></div>
|
|
@@ -1156,6 +1156,6 @@
|
|
|
1156
1156
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1157
1157
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1158
1158
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1159
|
-
<script type="module" src="/app.js?v=20260814-
|
|
1159
|
+
<script type="module" src="/app.js?v=20260814-vditor-ui-fixes-v1"></script>
|
|
1160
1160
|
</body>
|
|
1161
1161
|
</html>
|