@musnows/scriverse 0.3.6 → 0.3.7

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.
@@ -0,0 +1,8 @@
1
+ export type AnalysisType = Readonly<{
2
+ value: string;
3
+ label: string;
4
+ desc: string;
5
+ }>;
6
+
7
+ export const ANALYSIS_TYPES: ReadonlyArray<AnalysisType>;
8
+ export function analysisTypeDescription(value: unknown): string;
@@ -0,0 +1,53 @@
1
+ export const ANALYSIS_TYPES = Object.freeze([
2
+ Object.freeze({
3
+ value: "chapter-analysis",
4
+ label: "章节理解",
5
+ desc: "分析所选章节,生成情节概要,并提取事件、出场角色、设定、原文证据和不确定项。"
6
+ }),
7
+ Object.freeze({
8
+ value: "character-extraction",
9
+ label: "全书角色抽取",
10
+ desc: "扫描分析范围内的正文,识别有跨章节意义的角色及可靠别名,并创建或更新角色档案。"
11
+ }),
12
+ Object.freeze({
13
+ value: "character-identity-audit",
14
+ label: "AI 角色查重",
15
+ desc: "对照全书正文与现有角色档案,找出可能属于同一角色的重复档案;只生成审核建议,不会自动合并。"
16
+ }),
17
+ Object.freeze({
18
+ value: "timeline-analysis",
19
+ label: "时间轴与事件抽取",
20
+ desc: "从正文提取事件、发生时间、地点和参与者,区分发生时间与叙述时间,并保存为待确认候选。"
21
+ }),
22
+ Object.freeze({
23
+ value: "relationship-analysis",
24
+ label: "全书人物关系分析",
25
+ desc: "根据原文证据识别角色之间具有长期意义的关系及变化,生成可供确认的人物关系候选。"
26
+ }),
27
+ Object.freeze({
28
+ value: "worldview-analysis",
29
+ label: "世界观分析",
30
+ desc: "归纳正文中的自然、社会、历史、科技、文化等世界观维度,同时标出冲突和证据不足之处。"
31
+ }),
32
+ Object.freeze({
33
+ value: "setting-extraction",
34
+ label: "设定抽取",
35
+ desc: "提取会影响后续创作的地点、物品、能力、制度、规则等设定,附带原文证据并等待作者确认。"
36
+ }),
37
+ Object.freeze({
38
+ value: "consistency-check",
39
+ label: "一致性校对",
40
+ desc: "检查人物状态、关系、时间与作品设定是否相互冲突,并按严重程度给出证据和修改建议。"
41
+ }),
42
+ Object.freeze({
43
+ value: "book-analysis",
44
+ label: "全书综合分析",
45
+ desc: "基于所选范围生成开放式综合分析,适合查看作品整体表现、主要问题和可改进方向。"
46
+ })
47
+ ]);
48
+
49
+ const analysisTypeDescriptions = new Map(ANALYSIS_TYPES.map(({ value, desc }) => [value, desc]));
50
+
51
+ export function analysisTypeDescription(value) {
52
+ return analysisTypeDescriptions.get(String(value)) ?? "请选择一种分析类型以查看用途说明。";
53
+ }
@@ -18,6 +18,7 @@ import { parsePageRoute, serializePageRoute } from "/page-route.js?v=20260714-re
18
18
  import { splitRelationshipKeywordInput, splitRelationshipKeywords, uniqueRelationshipKeywords } from "/relationship-keywords.js?v=20260720-relationship-keyword-chips";
19
19
  import { tokenizeVisibleSpaces } from "/whitespace-visualization.js?v=20260718-visible-whitespace";
20
20
  import { buildRaceForest, eligibleRaceParents, racePathLabel } from "/race-hierarchy.js?v=20260721-race-hierarchy";
21
+ import { ANALYSIS_TYPES, analysisTypeDescription } from "/analysis-types.js?v=20260721-analysis-descriptions";
21
22
 
22
23
  const state = {
23
24
  user: null,
@@ -3988,11 +3989,18 @@ function openReviewDialog() {
3988
3989
 
3989
3990
  function openTaskDialog() {
3990
3991
  const chapterOptions = state.work.volumes.flatMap((volume) => volume.chapters.map((chapter) => [chapter.id, `${volume.title} / ${chapter.title}`]));
3991
- openDialog("开始 AI 分析", field("taskType", "分析类型", "select", "chapter-analysis", [["chapter-analysis", "章节理解"], ["character-extraction", "全书角色抽取"], ["character-identity-audit", "AI 角色查重"], ["timeline-analysis", "时间轴与事件抽取"], ["relationship-analysis", "全书人物关系分析"], ["worldview-analysis", "世界观分析"], ["setting-extraction", "设定抽取"], ["consistency-check", "一致性校对"], ["book-analysis", "全书综合分析"]]) + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + field("chapterId", "章节", "select", chapterOptions[0]?.[0] ?? "", chapterOptions), async (form) => {
3992
+ const defaultTaskType = ANALYSIS_TYPES[0].value;
3993
+ const taskTypeField = `<div class="form-field analysis-type-field"><label>分析类型<select name="taskType" aria-describedby="analysis-type-description">${ANALYSIS_TYPES.map(({ value, label }) => `<option value="${esc(value)}" ${value === defaultTaskType ? "selected" : ""}>${esc(label)}</option>`).join("")}</select></label><p id="analysis-type-description" class="analysis-type-description" aria-live="polite">${esc(analysisTypeDescription(defaultTaskType))}</p></div>`;
3994
+ openDialog("开始 AI 分析", taskTypeField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + field("chapterId", "章节", "select", chapterOptions[0]?.[0] ?? "", chapterOptions), async (form) => {
3992
3995
  const scope = form.get("taskType") === "character-identity-audit" || form.get("scopeType") === "book" ? { type: "book" } : { type: "chapter", chapterId: form.get("chapterId") };
3993
3996
  await api(`/api/works/${state.work.id}/tasks`, { method: "POST", body: { taskType: form.get("taskType"), scope } });
3994
3997
  await renderTasks();
3995
3998
  });
3999
+ const taskTypeSelect = $("#dialog-fields").querySelector('select[name="taskType"]');
4000
+ const description = $("#analysis-type-description");
4001
+ taskTypeSelect.addEventListener("change", () => {
4002
+ description.textContent = analysisTypeDescription(taskTypeSelect.value);
4003
+ });
3996
4004
  }
3997
4005
 
3998
4006
  function openProviderDialog(item) {
@@ -9,7 +9,7 @@
9
9
  <script src="/theme-init.js?v=20260720-route-skeleton"></script>
10
10
  <link rel="icon" href="/icon.svg?v=20260712" type="image/svg+xml">
11
11
  <link rel="manifest" href="/site.webmanifest">
12
- <link rel="stylesheet" href="/styles.css?v=20260721-release-0.3.6">
12
+ <link rel="stylesheet" href="/styles.css?v=20260721-analysis-descriptions">
13
13
  </head>
14
14
  <body class="auth-pending">
15
15
  <section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
@@ -546,6 +546,6 @@
546
546
  </dialog>
547
547
 
548
548
  <div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
549
- <script type="module" src="/app.js?v=20260721-release-0.3.6"></script>
549
+ <script type="module" src="/app.js?v=20260721-analysis-descriptions"></script>
550
550
  </body>
551
551
  </html>
@@ -1345,6 +1345,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
1345
1345
  .dialog-fields input, .dialog-fields textarea, .dialog-fields select { width: 100%; padding: 9px 10px; font-size: 13px; }
1346
1346
  .dialog-fields textarea { resize: vertical; min-height: 90px; line-height: 1.6; }
1347
1347
  .form-field { display: grid; gap: 6px; color: var(--muted); font-size: 11px; }
1348
+ .analysis-type-description { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.55; }
1348
1349
  .item-list-rows { display: grid; gap: 7px; }
1349
1350
  .item-list-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px; }
1350
1351
  .item-list-row button, .item-list-add { border: 1px solid var(--line); border-radius: 4px; background: var(--panel); color: var(--muted); font-size: 10px; }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const APP_VERSION = "0.3.6";
1
+ export const APP_VERSION = "0.3.7";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musnows/scriverse",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Command-line client and local server for the Scriverse long-form fiction workspace",
5
5
  "license": "MIT",
6
6
  "author": "musnows",