@musnows/scriverse 0.6.0 → 0.6.1
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-tool-results.js +177 -0
- package/dist/ai-tool-results.js.map +1 -0
- package/dist/ai.js +412 -122
- package/dist/ai.js.map +1 -1
- package/dist/app.js +81 -30
- package/dist/app.js.map +1 -1
- package/dist/cli-contract.js +5 -5
- package/dist/cli-core.js +8 -2
- package/dist/cli-core.js.map +1 -1
- package/dist/collaboration-presence.js +1 -0
- package/dist/collaboration-presence.js.map +1 -1
- package/dist/public/ai-context-meter.js +7 -0
- package/dist/public/app.js +455 -206
- package/dist/public/entity-version.js +2 -2
- package/dist/public/index.html +17 -10
- package/dist/public/model-config.d.ts +3 -0
- package/dist/public/model-config.js +14 -0
- package/dist/public/page-route.js +1 -0
- package/dist/public/styles.css +30 -10
- package/dist/public/work-permissions.d.ts +1 -1
- package/dist/public/work-permissions.js +1 -1
- package/dist/store.js +58 -6
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +4 -5
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/work-permissions.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { levelLabel, relationshipCategoryLabel } from "./display-labels.js?v=20260725-enum-labels-zh";
|
|
2
2
|
|
|
3
3
|
export const VERSIONED_ENTITY_LABELS = Object.freeze({
|
|
4
|
-
draft: "
|
|
4
|
+
draft: "创作想法",
|
|
5
5
|
setting: "世界观设定",
|
|
6
6
|
race: "种族档案",
|
|
7
7
|
organization: "组织档案",
|
|
@@ -25,7 +25,7 @@ export function entityVersionSourceLabel(source) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function entityVersionSnapshotSummary(type, snapshot = {}) {
|
|
28
|
-
if (type === "draft") return `${snapshot.draftType === "setting" ? "
|
|
28
|
+
if (type === "draft") return `${snapshot.draftType === "setting" ? "设定想法" : "正文想法"} · ${snapshot.title || "未命名想法"}`;
|
|
29
29
|
if (type === "setting") return `${snapshot.category || "未分类"} · ${snapshot.title || "未命名设定"}`;
|
|
30
30
|
if (type === "race") return `${snapshot.name || "未命名种族"} · ${(snapshot.memberIds ?? []).length} 位角色`;
|
|
31
31
|
if (type === "organization") return `${snapshot.name || "未命名组织"} · ${(snapshot.memberIds ?? []).length} 位成员`;
|
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=
|
|
13
|
+
<link rel="stylesheet" href="/styles.css?v=20260731-model-context-guidance-v1">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
<div class="top-actions">
|
|
76
76
|
<span id="save-state" class="save-state">就绪</span>
|
|
77
77
|
<div id="presence-control" class="presence-control hidden">
|
|
78
|
-
<button id="presence-button" class="presence-button" type="button" aria-expanded="false" aria-controls="presence-panel"><span class="presence-status-dot" aria-hidden="true"></span><span id="presence-count">1
|
|
78
|
+
<button id="presence-button" class="presence-button" type="button" aria-expanded="false" aria-controls="presence-panel"><span class="presence-status-dot" aria-hidden="true"></span><span id="presence-count">1 人</span></button>
|
|
79
79
|
<section id="presence-panel" class="presence-panel hidden" aria-label="当前作品在线协作者">
|
|
80
80
|
<header><strong>正在访问这部作品</strong><small>离开后状态会自动过期</small></header>
|
|
81
81
|
<div id="presence-list" class="presence-list" aria-live="polite"></div>
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
</div>
|
|
124
124
|
<nav id="module-nav" class="module-nav" aria-label="作品模块">
|
|
125
125
|
<button type="button" data-module="editor" class="active"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>正文</button>
|
|
126
|
-
<button type="button" data-module="drafts"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4l11-11a2.8 2.8 0 0 0-4-4L4 16v4Z"/><path d="m13.5 6.5 4 4"/></svg
|
|
126
|
+
<button type="button" data-module="drafts"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4l11-11a2.8 2.8 0 0 0-4-4L4 16v4Z"/><path d="m13.5 6.5 4 4"/></svg>想法</button>
|
|
127
127
|
<button type="button" data-module="settings"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m16 6 4 14"/><path d="M12 6v14"/><path d="M8 8v12"/><path d="M4 4v16"/></svg>设定库</button>
|
|
128
128
|
<button type="button" data-module="characters"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>角色</button>
|
|
129
129
|
<button type="button" data-module="organizations"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>组织</button>
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
<button type="button" data-module="races"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/></svg>种族</button>
|
|
133
133
|
<button class="ai-analysis-entry" type="button" data-module="tasks"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/></svg>AI 分析</button>
|
|
134
134
|
<button id="module-more-button" type="button" aria-expanded="false"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg><span class="nav-label">更多</span></button>
|
|
135
|
+
<button class="module-nav-secondary hidden" type="button" data-module="comments"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4Z"/><path d="M8 9h8M8 13h5"/></svg><span class="nav-label">正文评论</span></button>
|
|
135
136
|
<button class="module-nav-secondary hidden" type="button" data-module="outlines"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg><span class="nav-label">大纲/伏笔</span></button>
|
|
136
137
|
<button class="module-nav-secondary hidden" type="button" data-module="reviews"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="m9 14 2 2 4-4"/></svg>审核</button>
|
|
137
138
|
<button class="module-nav-secondary hidden" type="button" data-module="ai-settings"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="16" height="16" x="4" y="4" rx="2"/><rect width="6" height="6" x="9" y="9" rx="1"/><path d="M15 2v2"/><path d="M15 20v2"/><path d="M2 15h2"/><path d="M2 9h2"/><path d="M20 15h2"/><path d="M20 9h2"/><path d="M9 2v2"/><path d="M9 20v2"/></svg>AI 设置</button>
|
|
@@ -227,7 +228,7 @@
|
|
|
227
228
|
<span id="chapter-stats" class="chapter-stats">0 字 · v0</span>
|
|
228
229
|
<button id="insight-button" class="ghost-button" type="button" aria-controls="chapter-insight-toast" aria-expanded="false">章节概览</button>
|
|
229
230
|
<button id="versions-button" class="ghost-button" type="button">版本</button>
|
|
230
|
-
<button id="chapter-annotations-button" class="ghost-button" type="button"
|
|
231
|
+
<button id="chapter-annotations-button" class="ghost-button" type="button">评论</button>
|
|
231
232
|
<button id="chapter-delete-button" class="danger-button" type="button">删除章节</button>
|
|
232
233
|
<button id="chapter-edit-button" class="primary-button hidden" type="button">编辑</button>
|
|
233
234
|
<button id="tidy-blank-lines-button" class="ghost-button" type="button">整理空行</button>
|
|
@@ -332,10 +333,10 @@
|
|
|
332
333
|
<button class="danger-button" type="button" role="menuitem" data-delete-chapter>删除章节</button>
|
|
333
334
|
</div>
|
|
334
335
|
</div>
|
|
335
|
-
<div id="line-citation-menu" class="line-citation-menu hidden" role="menu" aria-label="
|
|
336
|
+
<div id="line-citation-menu" class="line-citation-menu hidden" role="menu" aria-label="正文行操作">
|
|
336
337
|
<strong id="line-citation-label">已选择正文</strong>
|
|
337
338
|
<button id="add-line-citation" type="button" role="menuitem">添加到创作助手引用</button>
|
|
338
|
-
<button id="add-line-annotation" type="button" role="menuitem"
|
|
339
|
+
<button id="add-line-annotation" type="button" role="menuitem">添加评论</button>
|
|
339
340
|
<button id="add-line-todo" type="button" role="menuitem">添加待办</button>
|
|
340
341
|
</div>
|
|
341
342
|
<div id="markdown-table-menu" class="markdown-table-menu hidden" role="menu" aria-label="Markdown 表格显示方式">
|
|
@@ -691,8 +692,8 @@
|
|
|
691
692
|
</dialog>
|
|
692
693
|
|
|
693
694
|
<dialog id="chapter-annotations-dialog" class="dialog wide-dialog chapter-annotations-dialog" aria-labelledby="chapter-annotations-title">
|
|
694
|
-
<div class="dialog-header"><div><span class="eyebrow">正文协作</span><h2 id="chapter-annotations-title"
|
|
695
|
-
<div id="chapter-annotations-list" class="chapter-annotations-list"></div>
|
|
695
|
+
<div class="dialog-header"><div><span class="eyebrow">正文协作</span><h2 id="chapter-annotations-title">正文评论</h2><p id="chapter-annotations-meta" class="dialog-header-meta">在正文行上点击右键即可添加评论。</p></div><button id="chapter-annotations-close" class="dialog-close" aria-label="关闭正文评论" type="button">×</button></div>
|
|
696
|
+
<div id="chapter-annotations-list" class="chapter-annotations-list" aria-live="polite"></div>
|
|
696
697
|
<div class="dialog-actions"><button id="chapter-annotations-refresh" class="ghost-button" type="button">刷新</button><button id="chapter-annotations-done" class="primary-button" type="button">完成</button></div>
|
|
697
698
|
</dialog>
|
|
698
699
|
|
|
@@ -724,13 +725,14 @@
|
|
|
724
725
|
<legend>可分页模块单页数量</legend>
|
|
725
726
|
<small>以下模块独立设置,允许 10–100 条;默认均为 30 条。种族层级始终完整展示。</small>
|
|
726
727
|
<div class="pagination-settings-grid">
|
|
727
|
-
<label
|
|
728
|
+
<label>想法列表<input id="page-size-drafts" name="pageSizeDrafts" type="number" min="10" max="100" step="1" required aria-label="想法列表单页数量"></label>
|
|
728
729
|
<label>设定库<input id="page-size-settings" name="pageSizeSettings" type="number" min="10" max="100" step="1" required aria-label="设定库单页数量"></label>
|
|
729
730
|
<label>角色列表<input id="page-size-characters" name="pageSizeCharacters" type="number" min="10" max="100" step="1" required aria-label="角色列表单页数量"></label>
|
|
730
731
|
<label>组织列表<input id="page-size-organizations" name="pageSizeOrganizations" type="number" min="10" max="100" step="1" required aria-label="组织列表单页数量"></label>
|
|
731
732
|
<label>时间线事件<input id="page-size-timeline" name="pageSizeTimeline" type="number" min="10" max="100" step="1" required aria-label="时间线事件单页数量"></label>
|
|
732
733
|
<label>大纲与伏笔<input id="page-size-outlines" name="pageSizeOutlines" type="number" min="10" max="100" step="1" required aria-label="大纲与伏笔单页数量"></label>
|
|
733
734
|
<label>人物关系列表<input id="page-size-relationships" name="pageSizeRelationships" type="number" min="10" max="100" step="1" required aria-label="人物关系列表单页数量"></label>
|
|
735
|
+
<label>正文评论<input id="page-size-comments" name="pageSizeComments" type="number" min="10" max="100" step="1" required aria-label="正文评论单页数量"></label>
|
|
734
736
|
<label>审核列表<input id="page-size-reviews" name="pageSizeReviews" type="number" min="10" max="100" step="1" required aria-label="审核列表单页数量"></label>
|
|
735
737
|
<label>AI 分析任务<input id="page-size-analysis-tasks" name="pageSizeAnalysisTasks" type="number" min="10" max="100" step="1" required aria-label="AI 分析任务单页数量"></label>
|
|
736
738
|
<label>正文导入历史<input id="page-size-file-versions" name="pageSizeFileVersions" type="number" min="10" max="100" step="1" required aria-label="正文导入历史单页数量"></label>
|
|
@@ -854,6 +856,11 @@
|
|
|
854
856
|
</div>
|
|
855
857
|
<div class="ai-history-dialog-body">
|
|
856
858
|
<div id="ai-history-list" class="ai-history-list" aria-live="polite"></div>
|
|
859
|
+
<nav id="ai-history-pagination" class="module-pagination ai-history-pagination hidden" aria-label="历史记录分页">
|
|
860
|
+
<button id="ai-history-previous" type="button" disabled>上一页</button>
|
|
861
|
+
<span id="ai-history-page-label">第 1 页</span>
|
|
862
|
+
<button id="ai-history-next" type="button" disabled>下一页</button>
|
|
863
|
+
</nav>
|
|
857
864
|
</div>
|
|
858
865
|
</dialog>
|
|
859
866
|
|
|
@@ -888,6 +895,6 @@
|
|
|
888
895
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
889
896
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
890
897
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
891
|
-
<script type="module" src="/app.js?v=
|
|
898
|
+
<script type="module" src="/app.js?v=20260731-model-context-guidance-v1"></script>
|
|
892
899
|
</body>
|
|
893
900
|
</html>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const MODEL_PURPOSE_OPTIONS: ReadonlyArray<readonly [string, string]>;
|
|
2
|
+
export const MIN_MODEL_CONTEXT_WINDOW: number;
|
|
3
|
+
export const RECOMMENDED_MODEL_CONTEXT_WINDOW: number;
|
|
2
4
|
|
|
3
5
|
export type ModelFormValues = {
|
|
4
6
|
displayName: string;
|
|
@@ -13,6 +15,7 @@ export type ModelFormValues = {
|
|
|
13
15
|
|
|
14
16
|
export function normalizeModelPurposes(purposes: unknown): string[];
|
|
15
17
|
export function isKimiModelId(modelId: unknown): boolean;
|
|
18
|
+
export function modelContextWindowGuidance(value: unknown): { belowMinimum: boolean; showRecommendation: boolean };
|
|
16
19
|
export function modelFormValues(model?: Record<string, unknown> | null): ModelFormValues;
|
|
17
20
|
export function modelPayload(values: ModelFormValues, existingPreset?: Record<string, unknown>): Record<string, unknown> & { thinkingEnabled: boolean };
|
|
18
21
|
export function modelOptionLabel(model: Record<string, unknown> | null | undefined): string;
|
|
@@ -9,6 +9,9 @@ export const MODEL_PURPOSE_OPTIONS = Object.freeze([
|
|
|
9
9
|
["consistency-check", "一致性校对"]
|
|
10
10
|
]);
|
|
11
11
|
|
|
12
|
+
export const MIN_MODEL_CONTEXT_WINDOW = 32_768;
|
|
13
|
+
export const RECOMMENDED_MODEL_CONTEXT_WINDOW = 128_000;
|
|
14
|
+
|
|
12
15
|
const purposeAliases = new Map([
|
|
13
16
|
...MODEL_PURPOSE_OPTIONS.flatMap(([key, label]) => [[key, key], [label, key]]),
|
|
14
17
|
["章节分析", "chapter-analysis"],
|
|
@@ -25,6 +28,17 @@ export function isKimiModelId(modelId) {
|
|
|
25
28
|
return String(modelId ?? "").toLowerCase().includes("kimi");
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
export function modelContextWindowGuidance(value) {
|
|
32
|
+
const contextWindow = Number(value);
|
|
33
|
+
const hasNumericValue = String(value ?? "").trim() !== "" && Number.isFinite(contextWindow);
|
|
34
|
+
return {
|
|
35
|
+
belowMinimum: hasNumericValue && contextWindow < MIN_MODEL_CONTEXT_WINDOW,
|
|
36
|
+
showRecommendation: hasNumericValue
|
|
37
|
+
&& contextWindow >= MIN_MODEL_CONTEXT_WINDOW
|
|
38
|
+
&& contextWindow < RECOMMENDED_MODEL_CONTEXT_WINDOW
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
28
42
|
export function modelFormValues(model = null) {
|
|
29
43
|
const modelId = String(model?.modelId ?? "");
|
|
30
44
|
const configuredTemperature = model?.preset?.temperature;
|
package/dist/public/styles.css
CHANGED
|
@@ -509,7 +509,7 @@ input[type="checkbox"][hidden] { display: none; }
|
|
|
509
509
|
|
|
510
510
|
.left-panel, .ai-panel { position: relative; min-height: 0; background: var(--panel); }
|
|
511
511
|
.left-panel { display: flex; flex-direction: column; border-right: 1px solid var(--line); padding: 0; overflow: hidden; }
|
|
512
|
-
.left-panel-body { flex: 1 1 auto; min-height: 0; padding: 18px
|
|
512
|
+
.left-panel-body { flex: 1 1 auto; min-height: 0; padding: 18px 24px 16px 14px; overflow-y: auto; scrollbar-gutter: stable; }
|
|
513
513
|
.ai-panel { border-left: 1px solid var(--line); display: flex; flex-direction: column; padding: 14px 16px 18px; min-width: 0; }
|
|
514
514
|
.panel-collapse-button { z-index: 8; flex: 0 0 auto; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--panel); color: var(--muted); font-size: 18px; line-height: 1; }
|
|
515
515
|
.ai-heading #ai-panel-toggle { flex-basis: 30px; width: 30px; height: 30px; }
|
|
@@ -1318,9 +1318,13 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1318
1318
|
.provider-card .model-pill { flex: 1 1 auto; max-width: 100%; margin: 0; border: 0; text-align: left; white-space: normal; cursor: pointer; }
|
|
1319
1319
|
.provider-model-row .model-pill { min-width: 0; }
|
|
1320
1320
|
.provider-model-row.is-unavailable .model-pill { color: var(--muted); opacity: .72; }
|
|
1321
|
-
.provider-model-row .model-test-button { flex: 0 0 auto; min-height: 30px; padding: 6px 9px; }
|
|
1322
1321
|
.provider-card .model-pill:hover, .provider-card .model-pill:focus-visible { background: var(--surface-hover); color: var(--ink); }
|
|
1323
1322
|
.provider-card .card-actions { flex-wrap: wrap; }
|
|
1323
|
+
.model-connection-test { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px; border: 1px solid var(--line); border-radius: 4px; background: var(--surface-soft); }
|
|
1324
|
+
.model-connection-test > div { display: grid; gap: 4px; }
|
|
1325
|
+
.model-connection-test strong { color: var(--ink); font-size: 11px; }
|
|
1326
|
+
.model-connection-test p { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.5; }
|
|
1327
|
+
.model-connection-test button { flex: 0 0 auto; min-height: 34px; }
|
|
1324
1328
|
.pill { display: inline-block; padding: 3px 7px; margin: 0 4px 4px 0; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--muted); font-size: 9px; }
|
|
1325
1329
|
.organization-links { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin: 8px 0 6px; }.organization-links b { color: var(--muted); font-size: 9px; font-weight: 500; }.organization-links .pill { margin: 0; }.organization-empty { color: var(--muted); font-size: 9px; }.organization-pill { border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--line)); color: var(--accent-dark); background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }.organization-settings { margin: 10px 0 6px; }.organization-members { margin-top: 9px !important; }.relationship-keyword { white-space: nowrap; }
|
|
1326
1330
|
.race-settings { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 6px; }.race-settings .pill { margin: 0; }.race-members { margin-top: 9px !important; }
|
|
@@ -2046,12 +2050,20 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2046
2050
|
.quick-actions button { min-width: 0; min-height: 30px; border: 1px solid var(--line); background: transparent; border-radius: 3px; padding: 5px 3px; color: var(--muted); font-size: 10px; white-space: nowrap; }
|
|
2047
2051
|
.quick-actions button:hover { color: var(--ink); background: var(--paper-deep); }
|
|
2048
2052
|
.ai-feed { flex: 1; min-height: 0; overflow-y: auto; margin: 10px -5px 12px; padding: 0 5px; }
|
|
2053
|
+
.ai-context-compaction-divider { display: grid; grid-template-columns: minmax(16px, 1fr) auto minmax(16px, 1fr); gap: 9px; align-items: center; margin: 4px 0 16px; color: var(--muted); font-size: 9px; line-height: 1; text-align: center; }
|
|
2054
|
+
.ai-context-compaction-divider::before, .ai-context-compaction-divider::after { height: 1px; background: color-mix(in srgb, var(--muted) 36%, transparent); content: ""; }
|
|
2055
|
+
.ai-context-compaction-divider span { letter-spacing: .08em; white-space: nowrap; }
|
|
2049
2056
|
.assistant-message, .user-message { position: relative; margin-bottom: 12px; padding: 12px 13px; border-radius: 5px; font-size: 12px; line-height: 1.55; }
|
|
2050
2057
|
.assistant-message { background: var(--paper-deep); }
|
|
2058
|
+
.assistant-message.is-error { border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line)); border-left: 3px solid var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--paper-deep)); }
|
|
2051
2059
|
.assistant-message.has-message-actions, .user-message.has-message-actions { margin-bottom: 42px; }
|
|
2052
2060
|
.user-message { margin-left: 24px; background: var(--accent); color: #fff; }
|
|
2053
2061
|
.assistant-message > .message-heading, .user-message > .message-heading { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 5px; opacity: .65; font-size: 9px; }
|
|
2054
2062
|
.message-heading > span { letter-spacing: .12em; }.message-heading time { flex: 0 0 auto; font-variant-numeric: tabular-nums; letter-spacing: .03em; }
|
|
2063
|
+
.assistant-message.is-error > .message-heading { color: var(--accent-dark); opacity: 1; }
|
|
2064
|
+
.assistant-message.is-error > .message-heading > span { display: inline-flex; align-items: center; gap: 7px; }
|
|
2065
|
+
.ai-message-status { display: inline-flex; align-items: center; min-height: 20px; padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; font-size: 9px; font-weight: 700; line-height: 1; letter-spacing: .08em; }
|
|
2066
|
+
.ai-message-status.is-error { border-color: var(--accent); background: var(--accent); color: #fff; }
|
|
2055
2067
|
.message-body { min-width: 0; overflow-wrap: anywhere; }
|
|
2056
2068
|
.assistant-message.is-error .message-body { font-family: inherit; font-size: inherit; line-height: inherit; }
|
|
2057
2069
|
.assistant-message.is-error .ai-error-text { margin: 0; white-space: pre-wrap; font: inherit; }
|
|
@@ -2102,6 +2114,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2102
2114
|
.ai-process-step-body { max-height: 220px; overflow: auto; padding: 8px 9px; border-left: 2px solid color-mix(in srgb, var(--accent) 46%, var(--line)); background: var(--paper-deep); color: var(--muted); font-size: 10px; }
|
|
2103
2115
|
.ai-process-intermediate-step .ai-process-step-body { border-left-color: var(--muted); }
|
|
2104
2116
|
.ai-process-tool-step { padding-top: 1px; }
|
|
2117
|
+
.ai-process-context-compaction { display: grid; grid-template-columns: minmax(12px, 1fr) auto minmax(12px, 1fr); gap: 8px; align-items: center; min-width: 0; padding: 2px 0; color: var(--muted); font-size: 8px; line-height: 1; text-align: center; }
|
|
2118
|
+
.ai-process-context-compaction::before, .ai-process-context-compaction::after { height: 1px; background: color-mix(in srgb, var(--muted) 38%, transparent); content: ""; }
|
|
2119
|
+
.ai-process-context-compaction span { letter-spacing: .08em; white-space: nowrap; }
|
|
2105
2120
|
@keyframes ai-process-pulse { 0%, 100% { opacity: .45; transform: scale(.8); } 50% { opacity: 1; transform: scale(1); } }
|
|
2106
2121
|
@media (prefers-reduced-motion: reduce) { .is-streaming .ai-process-details[open] > summary::before { animation: none; } }
|
|
2107
2122
|
.ai-tool-call-list { display: grid; gap: 5px; margin-top: 10px; }
|
|
@@ -2163,7 +2178,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2163
2178
|
.ai-context-meter b { position: relative; z-index: 1; font-size: 9px; font-weight: 600; }.ai-context-meter.is-warning { --context-meter-color: #bf6a35; }.ai-context-meter.is-danger { --context-meter-color: #ad463c; }.ai-context-meter.is-empty { opacity: .62; }
|
|
2164
2179
|
.ai-context-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 40; display: grid; width: min(320px, calc(100vw - 32px)); gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
2165
2180
|
.ai-context-popover.hidden { display: none; }
|
|
2166
|
-
.ai-context-popover::after { position: absolute; right:
|
|
2181
|
+
.ai-context-popover::after { position: absolute; right: 71px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
2167
2182
|
.ai-context-popover-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
|
|
2168
2183
|
.ai-context-popover-header > div { display: grid; gap: 3px; min-width: 0; }
|
|
2169
2184
|
.ai-context-popover-header strong { font-size: 12px; }
|
|
@@ -2295,14 +2310,17 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2295
2310
|
.markdown-table-menu button small { color: var(--muted); font-size: 9px; }
|
|
2296
2311
|
.chapter-annotations-dialog { width: min(820px, 94vw); }
|
|
2297
2312
|
.chapter-annotations-list { display: grid; gap: 10px; max-height: 62vh; overflow: auto; padding: 18px 24px; }
|
|
2313
|
+
.chapter-comment-module-list { display: grid; gap: 10px; }
|
|
2298
2314
|
.chapter-annotation-card { display: grid; gap: 8px; padding: 13px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
|
|
2299
2315
|
.chapter-annotation-card.is-resolved { opacity: .65; }
|
|
2300
2316
|
.chapter-annotation-card header { display: flex; align-items: center; gap: 8px; }
|
|
2301
2317
|
.chapter-annotation-card header strong { font-size: 11px; }
|
|
2318
|
+
.chapter-annotation-card header .chapter-annotation-source { min-width: 0; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2319
|
+
.chapter-annotation-line { flex: none; color: var(--muted); font-family: var(--font-latin), monospace; font-size: 9px; }
|
|
2302
2320
|
.chapter-annotation-card header em { margin-left: auto; color: var(--muted); font-size: 9px; font-style: normal; }
|
|
2303
2321
|
.chapter-annotation-kind { padding: 2px 6px; border: 1px solid var(--accent); border-radius: 10px; color: var(--accent-dark); font-size: 8px; }
|
|
2304
|
-
.chapter-annotation-card blockquote { max-height: 100px; overflow: auto; margin: 0; padding: 8px 10px; border-left: 3px solid var(--line); background: var(--surface); color: var(--muted); font:
|
|
2305
|
-
.chapter-annotation-card p { margin: 0; font-size: 12px; line-height: 1.6; white-space: pre-wrap; }
|
|
2322
|
+
.chapter-annotation-card blockquote { max-height: 100px; overflow: auto; margin: 0; padding: 8px 10px; border-left: 3px solid var(--line); background: var(--surface); color: var(--muted); font-family: var(--font-mono), monospace; font-size: 10px; line-height: 1.6; white-space: pre-wrap; }
|
|
2323
|
+
.chapter-annotation-card p { margin: 0; font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
2306
2324
|
.chapter-annotation-card > small { color: var(--muted); font-size: 9px; }
|
|
2307
2325
|
.chapter-annotation-card footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 7px; }
|
|
2308
2326
|
.chapter-annotation-card footer button { min-height: 30px; padding: 5px 9px; font-size: 9px; }
|
|
@@ -2423,7 +2441,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2423
2441
|
.relationship-source-preview-match { padding: 2px 6px; border-radius: 10px; background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent-dark); font-size: 8px; white-space: nowrap; }
|
|
2424
2442
|
.relationship-source-preview-match.is-fuzzy { background: color-mix(in srgb, var(--warning, #a56f19) 12%, transparent); color: var(--warning, #8b5c10); }
|
|
2425
2443
|
.relationship-source-preview-empty { margin: 0; padding: 9px; border: 1px dashed var(--line); border-radius: 4px; }
|
|
2426
|
-
.model-temperature-hint { margin: 0; color: var(--accent-dark); font-size: 10px; line-height: 1.55; }
|
|
2444
|
+
.model-temperature-hint, .model-context-window-hint { margin: 0; color: var(--accent-dark); font-size: 10px; line-height: 1.55; }
|
|
2427
2445
|
.item-list-rows { display: grid; gap: 7px; }
|
|
2428
2446
|
.item-list-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px; }
|
|
2429
2447
|
.item-list-row button, .item-list-add { border: 1px solid var(--line); border-radius: 4px; background: var(--panel); color: var(--muted); font-size: 10px; }
|
|
@@ -2933,9 +2951,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2933
2951
|
.brand-block .brand-mark { width: 30px; height: 30px; }
|
|
2934
2952
|
.brand-block strong { font-size: 14px; }
|
|
2935
2953
|
.brand-block small { display: none; }
|
|
2936
|
-
.mobile-module-tab { position: fixed; z-index:
|
|
2954
|
+
.mobile-module-tab { position: fixed; z-index: 38; top: 132px; right: 0; display: grid; width: 34px; height: 42px; padding: 0; place-items: center; border: 1px solid var(--line); border-right: 0; border-radius: 5px 0 0 5px; background: var(--surface-soft); color: var(--accent-dark); font-size: 10px; writing-mode: vertical-rl; }
|
|
2937
2955
|
.app-shell.shelf-mode .mobile-module-tab { display: none; }
|
|
2938
|
-
.mobile-panel-backdrop { position: fixed; z-index:
|
|
2956
|
+
.mobile-panel-backdrop { position: fixed; z-index: 36; inset: 0; width: 100%; height: 100%; padding: 0; border: 0; background: rgba(20, 17, 14, .28); }
|
|
2939
2957
|
.app-shell:not(.shelf-mode):not(.left-panel-collapsed) .mobile-panel-backdrop { display: block; }
|
|
2940
2958
|
.work-meta {
|
|
2941
2959
|
grid-column: 1 / -1;
|
|
@@ -2960,7 +2978,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2960
2978
|
|
|
2961
2979
|
.left-panel {
|
|
2962
2980
|
position: fixed;
|
|
2963
|
-
z-index:
|
|
2981
|
+
z-index: 37;
|
|
2964
2982
|
top: 0;
|
|
2965
2983
|
bottom: 0;
|
|
2966
2984
|
right: 0;
|
|
@@ -3083,7 +3101,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3083
3101
|
.dialog-header { padding: 14px 16px 12px; }
|
|
3084
3102
|
.dialog-header h2 { font-size: 20px; }
|
|
3085
3103
|
.settings-dialog-header-actions { gap: 6px; }
|
|
3086
|
-
.dialog-fields, .appearance-grid, .access-dialog-body, .account-settings-body, .versions-list, .entity-history-list { padding-inline: 16px; }
|
|
3104
|
+
.dialog-fields, .appearance-grid, .access-dialog-body, .account-settings-body, .versions-list, .entity-history-list, .chapter-annotations-list { padding-inline: 16px; }
|
|
3087
3105
|
.task-detail-overview { grid-template-columns: minmax(0, 1fr); padding: 12px; }
|
|
3088
3106
|
.task-detail-overview > * { grid-column: 1 !important; }
|
|
3089
3107
|
.task-result-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
@@ -3109,6 +3127,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3109
3127
|
.dialog-submit-status { padding: 10px 16px 4px; }
|
|
3110
3128
|
.dialog-actions { flex-wrap: wrap; padding: 12px 16px 16px; }
|
|
3111
3129
|
.dialog-actions > button { flex: 1 1 120px; }
|
|
3130
|
+
.model-connection-test { align-items: stretch; flex-direction: column; }
|
|
3131
|
+
.model-connection-test button { width: 100%; }
|
|
3112
3132
|
.search-form { grid-template-columns: minmax(0, 1fr); }
|
|
3113
3133
|
.search-form .primary-button { width: 100%; }
|
|
3114
3134
|
.appearance-grid { grid-template-columns: minmax(0, 1fr); }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type WorkPermissionModule = "prose" | "drafts" | "settings" | "characters" | "races" | "organizations" | "timeline" | "relationships" | "outlines" | "reviews" | "ai-chat" | "ai-analysis" | "ai-settings";
|
|
2
|
-
export type WorkUiModule = "editor" | "drafts" | "settings" | "characters" | "races" | "organizations" | "timeline" | "relationships" | "outlines" | "reviews" | "tasks" | "ai-settings";
|
|
2
|
+
export type WorkUiModule = "editor" | "drafts" | "settings" | "characters" | "races" | "organizations" | "timeline" | "comments" | "relationships" | "outlines" | "reviews" | "tasks" | "ai-settings";
|
|
3
3
|
export type WorkModuleAccess = "none" | "read" | "write";
|
|
4
4
|
export type WorkModulePermissions = Record<WorkPermissionModule, WorkModuleAccess>;
|
|
5
5
|
export type WorkPermissionAware = { accessRole?: string | null; modulePermissions?: WorkModulePermissions };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const WORK_PERMISSION_MODULES = Object.freeze([
|
|
2
2
|
{ id: "prose", uiModule: "editor", label: "正文" },
|
|
3
|
-
{ id: "drafts", uiModule: "drafts", label: "
|
|
3
|
+
{ id: "drafts", uiModule: "drafts", label: "想法" },
|
|
4
4
|
{ id: "settings", uiModule: "settings", label: "设定库" },
|
|
5
5
|
{ id: "characters", uiModule: "characters", label: "角色" },
|
|
6
6
|
{ id: "races", uiModule: "races", label: "种族" },
|
package/dist/store.js
CHANGED
|
@@ -16,6 +16,7 @@ const defaultPlatformPageSizes = {
|
|
|
16
16
|
timeline: 30,
|
|
17
17
|
outlines: 30,
|
|
18
18
|
relationships: 30,
|
|
19
|
+
comments: 30,
|
|
19
20
|
reviews: 30,
|
|
20
21
|
analysisTasks: 30,
|
|
21
22
|
fileVersions: 30
|
|
@@ -39,6 +40,7 @@ function platformPageSizes(value) {
|
|
|
39
40
|
timeline: pageSize("timeline"),
|
|
40
41
|
outlines: pageSize("outlines"),
|
|
41
42
|
relationships: pageSize("relationships"),
|
|
43
|
+
comments: pageSize("comments"),
|
|
42
44
|
reviews: pageSize("reviews"),
|
|
43
45
|
analysisTasks: pageSize("analysisTasks"),
|
|
44
46
|
fileVersions: pageSize("fileVersions")
|
|
@@ -1573,6 +1575,46 @@ export class Store {
|
|
|
1573
1575
|
WHERE annotation.chapter_id = ? AND annotation.deleted_at IS NULL
|
|
1574
1576
|
ORDER BY CASE annotation.status WHEN 'open' THEN 0 ELSE 1 END, annotation.start_line, annotation.created_at`, chapterId).map((row) => this.mapChapterAnnotation(row));
|
|
1575
1577
|
}
|
|
1578
|
+
listWorkChapterAnnotations(workId) {
|
|
1579
|
+
this.getWork(workId);
|
|
1580
|
+
return this.db.all(`SELECT annotation.*, user.display_name AS actor_display_name, user.username AS actor_username,
|
|
1581
|
+
chapter.title AS chapter_title, volume.title AS volume_title
|
|
1582
|
+
FROM chapter_annotations annotation
|
|
1583
|
+
JOIN chapters chapter ON chapter.id = annotation.chapter_id
|
|
1584
|
+
JOIN volumes volume ON volume.id = chapter.volume_id
|
|
1585
|
+
LEFT JOIN users user ON user.id = annotation.updated_by_user_id
|
|
1586
|
+
WHERE annotation.work_id = ? AND annotation.deleted_at IS NULL AND chapter.deleted_at IS NULL
|
|
1587
|
+
ORDER BY CASE annotation.status WHEN 'open' THEN 0 ELSE 1 END,
|
|
1588
|
+
volume.sort_order, volume.created_at, chapter.sort_order, chapter.created_at,
|
|
1589
|
+
annotation.start_line, annotation.created_at`, workId).map((row) => ({
|
|
1590
|
+
...this.mapChapterAnnotation(row),
|
|
1591
|
+
volumeTitle: requiredString(row, "volume_title"),
|
|
1592
|
+
chapterTitle: requiredString(row, "chapter_title")
|
|
1593
|
+
}));
|
|
1594
|
+
}
|
|
1595
|
+
listWorkChapterAnnotationsPage(workId, pagination) {
|
|
1596
|
+
this.getWork(workId);
|
|
1597
|
+
const page = paginationSql(pagination);
|
|
1598
|
+
const rows = this.db.all(`SELECT annotation.*, user.display_name AS actor_display_name, user.username AS actor_username,
|
|
1599
|
+
chapter.title AS chapter_title, volume.title AS volume_title
|
|
1600
|
+
FROM chapter_annotations annotation
|
|
1601
|
+
JOIN chapters chapter ON chapter.id = annotation.chapter_id
|
|
1602
|
+
JOIN volumes volume ON volume.id = chapter.volume_id
|
|
1603
|
+
LEFT JOIN users user ON user.id = annotation.updated_by_user_id
|
|
1604
|
+
WHERE annotation.work_id = ? AND annotation.deleted_at IS NULL AND chapter.deleted_at IS NULL
|
|
1605
|
+
ORDER BY CASE annotation.status WHEN 'open' THEN 0 ELSE 1 END,
|
|
1606
|
+
volume.sort_order, volume.created_at, chapter.sort_order, chapter.created_at,
|
|
1607
|
+
annotation.start_line, annotation.created_at${page.sql}`, workId, ...page.params);
|
|
1608
|
+
const total = numberValue(this.db.get(`SELECT COUNT(*) AS count
|
|
1609
|
+
FROM chapter_annotations annotation
|
|
1610
|
+
JOIN chapters chapter ON chapter.id = annotation.chapter_id
|
|
1611
|
+
WHERE annotation.work_id = ? AND annotation.deleted_at IS NULL AND chapter.deleted_at IS NULL`, workId) ?? {}, "count");
|
|
1612
|
+
return paginated(rows.map((row) => ({
|
|
1613
|
+
...this.mapChapterAnnotation(row),
|
|
1614
|
+
volumeTitle: requiredString(row, "volume_title"),
|
|
1615
|
+
chapterTitle: requiredString(row, "chapter_title")
|
|
1616
|
+
})), pagination, total);
|
|
1617
|
+
}
|
|
1576
1618
|
createChapterAnnotation(chapterId, input) {
|
|
1577
1619
|
const chapter = this.getChapter(chapterId);
|
|
1578
1620
|
const lines = String(chapter.content).replace(/\r\n?/gu, "\n").split("\n");
|
|
@@ -2238,7 +2280,7 @@ export class Store {
|
|
|
2238
2280
|
this.db.run(`INSERT INTO drafts (id, work_id, draft_type, title, content, created_at, updated_at)
|
|
2239
2281
|
VALUES (?, ?, ?, ?, ?, ?, ?)`, draftId, workId, input.draftType, input.title, input.content, timestamp, timestamp);
|
|
2240
2282
|
this.syncMarkdownAttachmentReferences(workId, "draft", draftId, input.content);
|
|
2241
|
-
this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "
|
|
2283
|
+
this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "建立创作想法", timestamp);
|
|
2242
2284
|
this.audit(workId, source === "restore" ? "draft.restored" : "draft.created", "draft", draftId, {
|
|
2243
2285
|
draftType: input.draftType,
|
|
2244
2286
|
source,
|
|
@@ -2278,17 +2320,17 @@ export class Store {
|
|
|
2278
2320
|
getDraft(draftId) {
|
|
2279
2321
|
const row = this.db.get("SELECT * FROM drafts WHERE id = ?", draftId);
|
|
2280
2322
|
if (!row)
|
|
2281
|
-
throw notFound("
|
|
2323
|
+
throw notFound("想法");
|
|
2282
2324
|
return this.mapDraft(row, true);
|
|
2283
2325
|
}
|
|
2284
2326
|
updateDraft(draftId, input, source = "manual", sourceRef = null, changeNote = "", expectedVersionNo) {
|
|
2285
2327
|
const current = this.getDraft(draftId);
|
|
2286
2328
|
const content = input.content ?? String(current.content);
|
|
2287
2329
|
this.db.transaction(() => {
|
|
2288
|
-
this.assertExpectedVersion("draft", draftId, expectedVersionNo, "
|
|
2330
|
+
this.assertExpectedVersion("draft", draftId, expectedVersionNo, "想法");
|
|
2289
2331
|
this.db.run("UPDATE drafts SET draft_type = ?, title = ?, content = ?, updated_at = ? WHERE id = ?", input.draftType ?? String(current.draftType), input.title ?? String(current.title), content, now(), draftId);
|
|
2290
2332
|
this.syncMarkdownAttachmentReferences(String(current.workId), "draft", draftId, content);
|
|
2291
|
-
this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "
|
|
2333
|
+
this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "更新创作想法");
|
|
2292
2334
|
this.audit(String(current.workId), "draft.updated", "draft", draftId, { fields: Object.keys(input), source, sourceRef });
|
|
2293
2335
|
});
|
|
2294
2336
|
return this.getDraft(draftId);
|
|
@@ -2296,8 +2338,8 @@ export class Store {
|
|
|
2296
2338
|
deleteDraft(draftId, expectedVersionNo) {
|
|
2297
2339
|
const current = this.getDraft(draftId);
|
|
2298
2340
|
this.db.transaction(() => {
|
|
2299
|
-
this.assertExpectedVersion("draft", draftId, expectedVersionNo, "
|
|
2300
|
-
this.recordEntityVersion("draft", draftId, "delete", null, "
|
|
2341
|
+
this.assertExpectedVersion("draft", draftId, expectedVersionNo, "想法");
|
|
2342
|
+
this.recordEntityVersion("draft", draftId, "delete", null, "删除创作想法");
|
|
2301
2343
|
this.clearMarkdownAttachmentReferences("draft", draftId);
|
|
2302
2344
|
this.db.run("DELETE FROM drafts WHERE id = ?", draftId);
|
|
2303
2345
|
this.audit(String(current.workId), "draft.deleted", "draft", draftId);
|
|
@@ -4110,6 +4152,16 @@ export class Store {
|
|
|
4110
4152
|
ORDER BY conversation.updated_at DESC, conversation.created_at DESC${page.sql}`, workId, ...page.params);
|
|
4111
4153
|
return paginated(rows.map((row) => this.mapAiConversation(row)), pagination);
|
|
4112
4154
|
}
|
|
4155
|
+
getAiConversationSummary(conversationId) {
|
|
4156
|
+
const row = this.db.get(`SELECT conversation.*,
|
|
4157
|
+
(SELECT COUNT(*) FROM ai_conversation_messages message WHERE message.conversation_id = conversation.id) AS message_count,
|
|
4158
|
+
COALESCE((SELECT content FROM ai_conversation_messages message WHERE message.conversation_id = conversation.id ORDER BY message.created_at DESC, message.rowid DESC LIMIT 1), '') AS preview
|
|
4159
|
+
FROM ai_conversations conversation
|
|
4160
|
+
WHERE conversation.id = ?`, conversationId);
|
|
4161
|
+
if (!row)
|
|
4162
|
+
throw notFound("AI 对话");
|
|
4163
|
+
return this.mapAiConversation(row);
|
|
4164
|
+
}
|
|
4113
4165
|
getAiConversation(conversationId) {
|
|
4114
4166
|
const row = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
|
|
4115
4167
|
if (!row)
|