@musnows/scriverse 0.5.2 → 0.5.3

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.
@@ -31,7 +31,7 @@ import {
31
31
  taskScopeLabel,
32
32
  timelineStatusLabel,
33
33
  characterStateFieldLabel
34
- } from "/display-labels.js?v=20260725-state-field-zh";
34
+ } from "/display-labels.js?v=20260726-character-name-variant";
35
35
  import { parsePageRoute, serializePageRoute } from "/page-route.js?v=20260723-knowledge-editor-page";
36
36
  import { splitRelationshipKeywordInput, splitRelationshipKeywords, uniqueRelationshipKeywords } from "/relationship-keywords.js?v=20260720-relationship-keyword-chips";
37
37
  import { tokenizeVisibleSpaces } from "/whitespace-visualization.js?v=20260718-visible-whitespace";
@@ -147,6 +147,14 @@ function analysisTaskTypeLabel(taskType) {
147
147
  return analysisTaskTypeLabels.get(String(taskType)) ?? "其他分析";
148
148
  }
149
149
 
150
+ function analysisTaskModelPurpose(taskType) {
151
+ if (taskType === "timeline-analysis") return "timeline-analysis";
152
+ if (taskType === "relationship-analysis") return "relationship-analysis";
153
+ if (taskType === "consistency-check") return "consistency-check";
154
+ if (taskType === "chapter-analysis") return "chapter-analysis";
155
+ return "book-analysis";
156
+ }
157
+
150
158
  function analysisTaskStatusLabel(status) {
151
159
  return ({
152
160
  pending: "待执行",
@@ -2646,6 +2654,7 @@ async function openMembersDialog(targetWork = state.work) {
2646
2654
  if (!targetWork) return;
2647
2655
  memberDialogWork = targetWork;
2648
2656
  const canManage = ["admin", "owner"].includes(String(targetWork.accessRole));
2657
+ $("#members-settings-return").classList.toggle("hidden", $("#settings-hub-view").classList.contains("hidden"));
2649
2658
  $("#members-dialog-eyebrow").textContent = `作品权限 · 《${targetWork.title}》`;
2650
2659
  $("#members-dialog-title").textContent = "成员模块权限";
2651
2660
  $("#members-list").innerHTML = '<p class="empty-state">正在读取成员……</p>';
@@ -2755,6 +2764,13 @@ async function showSettingsHub() {
2755
2764
  return true;
2756
2765
  }
2757
2766
 
2767
+ async function returnToSettingsHub(actionSelector, dialogSelector = null) {
2768
+ const dialog = dialogSelector ? $(dialogSelector) : null;
2769
+ if (dialog?.open) dialog.close();
2770
+ if (!(await showSettingsHub())) return;
2771
+ queueMicrotask(() => $(actionSelector)?.focus());
2772
+ }
2773
+
2758
2774
  async function returnFromSettings() {
2759
2775
  const context = settingsReturnContext ?? { view: "shelf" };
2760
2776
  settingsReturnContext = null;
@@ -3438,9 +3454,11 @@ function openReviewDetailDialog(item) {
3438
3454
  const evidenceHtml = evidence.length
3439
3455
  ? `<ul>${evidence.map((entry) => {
3440
3456
  if (!entry || typeof entry !== "object") return `<li>${esc(String(entry))}</li>`;
3441
- const source = entry.chapterTitle || entry.chapterId || "相关证据";
3457
+ const source = entry.sourceTitle || entry.chapterTitle || entry.chapterId || "相关证据";
3442
3458
  const quote = entry.quote ? `<blockquote>${esc(entry.quote)}</blockquote>` : "";
3443
- const supports = entry.supports ? `<small>${esc(entry.supports)}</small>` : "";
3459
+ const supports = entry.reason
3460
+ ? `<small>${esc(entry.reason)}${typeof entry.confidence === "number" ? ` · 置信度 ${Math.round(entry.confidence * 100)}%` : ""}</small>`
3461
+ : entry.supports ? `<small>${esc(entry.supports)}</small>` : "";
3444
3462
  return `<li><strong>${esc(source)}</strong>${quote}${supports}</li>`;
3445
3463
  }).join("")}</ul>`
3446
3464
  : "<p>暂无证据</p>";
@@ -4031,9 +4049,10 @@ async function renderTasks(page = taskListPage) {
4031
4049
  </div>
4032
4050
  </div>
4033
4051
  </section>
4034
- ${tasks.length ? `<table class="table-list task-table"><thead><tr><th>分析类型</th><th>范围</th><th>状态</th><th>进度</th><th>操作</th></tr></thead><tbody>${tasks.map((item) => `
4052
+ ${tasks.length ? `<table class="table-list task-table"><thead><tr><th>分析类型</th><th>任务模型</th><th>范围</th><th>状态</th><th>进度</th><th>操作</th></tr></thead><tbody>${tasks.map((item) => `
4035
4053
  <tr>
4036
4054
  <td>${esc(analysisTaskTypeLabel(item.taskType))}</td>
4055
+ <td>${esc(item.model?.displayName || "运行时使用默认模型")}</td>
4037
4056
  <td>${esc(item.scopeSummary || taskScopeLabel(item.scope?.type || "book"))}</td>
4038
4057
  <td class="task-status-cell">${renderAnalysisTaskStatus(item)}</td>
4039
4058
  <td class="task-progress-cell">${renderAnalysisTaskProgress(item)}</td>
@@ -4113,7 +4132,7 @@ async function renderTasks(page = taskListPage) {
4113
4132
  const cancel = button.parentElement.querySelector("[data-cancel-task]");
4114
4133
  if (cancel) cancel.textContent = "取消运行";
4115
4134
  scheduleTaskProgressRefresh(workId, 1);
4116
- const completed = await api(`/api/tasks/${button.dataset.runTask}/run`, { method: "POST", body: { modelId: $("#ai-model").value || undefined } });
4135
+ const completed = await api(`/api/tasks/${button.dataset.runTask}/run`, { method: "POST", body: {} });
4117
4136
  toast(completed.status === "cancelled" ? "分析任务已取消" : completed.status === "expired" ? "正文已变化,本次分析已过期" : "分析已完成");
4118
4137
  if (state.module === "tasks" && state.work?.id === workId) await renderTasks();
4119
4138
  } catch (error) {
@@ -4440,6 +4459,7 @@ function openTaskDetailDialog(task, trace) {
4440
4459
  <section class="task-detail-overview">
4441
4460
  <p><strong>任务 ID</strong><br><code>${esc(task.id)}</code><br><small>创建于 ${esc(formatDateTime(task.createdAt))} · 更新于 ${esc(formatDateTime(task.updatedAt))}</small></p>
4442
4461
  <p><strong>类型</strong> ${esc(analysisTaskTypeLabel(task.taskType))}</p>
4462
+ <p><strong>任务模型</strong> ${esc(task.model?.displayName || "运行时使用默认模型")}${task.model?.modelId ? ` · <code>${esc(task.model.modelId)}</code>` : ""}</p>
4443
4463
  <p><strong>状态</strong> ${esc(analysisTaskStatusLabel(task.status))} · 进度 ${Number(task.progress ?? 0)}%</p>
4444
4464
  <p><strong>范围摘要</strong> ${esc(task.scopeSummary || "未指定")}</p>
4445
4465
  <div><strong>范围详情</strong><ul>${detailHtml}</ul></div>
@@ -6401,14 +6421,26 @@ function openReviewDialog() {
6401
6421
  async function openTaskDialog() {
6402
6422
  const chapterOptions = state.work.volumes.flatMap((volume) => volume.chapters.map((chapter) => [chapter.id, `${volume.title} / ${chapter.title}`]));
6403
6423
  let relationshipCharacters = [];
6424
+ let taskModels = [];
6425
+ let taskDefaults = [];
6404
6426
  try {
6405
- relationshipCharacters = canReadModule("characters")
6406
- ? await apiAllPages(`/api/works/${state.work.id}/characters`)
6407
- : [];
6427
+ [relationshipCharacters, taskModels, taskDefaults] = await Promise.all([
6428
+ canReadModule("characters")
6429
+ ? apiAllPages(`/api/works/${state.work.id}/characters`)
6430
+ : Promise.resolve([]),
6431
+ api(`/api/works/${state.work.id}/models`),
6432
+ api(`/api/works/${state.work.id}/task-defaults`)
6433
+ ]);
6408
6434
  } catch (error) {
6409
- toast(`角色列表加载失败:${error.message}`, "error");
6435
+ toast(`分析任务配置加载失败:${error.message}`, "error");
6410
6436
  return;
6411
6437
  }
6438
+ const defaultModelByTask = new Map(taskDefaults.map((item) => [item.taskType, item.model.id]));
6439
+ const availableTaskModels = taskModels.filter((model) =>
6440
+ model.enabled
6441
+ && model.providerStatus === "enabled"
6442
+ && model.providerConnectionStatus === "success"
6443
+ );
6412
6444
  const characterOptions = relationshipCharacters.map((character) => [character.id, character.name]);
6413
6445
  const relationshipCharacterPicker = `<div class="form-field relationship-character-field">
6414
6446
  <span id="relationship-character-label">被分析角色(可多选)</span>
@@ -6429,6 +6461,11 @@ async function openTaskDialog() {
6429
6461
  </div>`;
6430
6462
  const defaultTaskType = ANALYSIS_TYPES[0].value;
6431
6463
  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>`;
6464
+ const defaultModelId = defaultModelByTask.get(analysisTaskModelPurpose(defaultTaskType)) ?? "";
6465
+ const modelField = `<label>任务模型<select name="modelId" required aria-describedby="analysis-task-model-help">
6466
+ <option value="" ${availableTaskModels.some((model) => model.id === defaultModelId) ? "" : "selected"} disabled>${availableTaskModels.length ? "请选择模型" : "没有可用模型"}</option>
6467
+ ${availableTaskModels.map((model) => `<option value="${esc(model.id)}" ${model.id === defaultModelId ? "selected" : ""}>${esc(modelOptionLabel(model))}</option>`).join("")}
6468
+ </select><small id="analysis-task-model-help">默认值来自“本书 AI 设置”,只修改当前任务,不会改变全书默认模型。</small></label>`;
6432
6469
  const chapterField = `<label class="task-chapter-field">章节<select name="chapterId">${chapterOptions.map(([key, text], index) => `<option value="${esc(key)}" ${index === 0 ? "selected" : ""}>${esc(text)}</option>`).join("")}</select></label>`;
6433
6470
  const relationshipFields = `<div class="relationship-analysis-options hidden">
6434
6471
  ${relationshipCharacterPicker}
@@ -6439,8 +6476,9 @@ async function openTaskDialog() {
6439
6476
  </div>
6440
6477
  <label>额外分析提示<textarea name="additionalPrompt" maxlength="10000" placeholder="例如:重点识别权力继承、师承变化或隐秘亲缘关系"></textarea><small>将同时追加到证据收集和全局关系归纳提示词,仅影响本次任务。</small></label>
6441
6478
  </div>`;
6442
- openDialog("开始 AI 分析", taskTypeField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + chapterField + relationshipFields, async (form) => {
6479
+ openDialog("开始 AI 分析", taskTypeField + modelField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + chapterField + relationshipFields, async (form) => {
6443
6480
  const taskType = String(form.get("taskType"));
6481
+ const modelId = String(form.get("modelId"));
6444
6482
  const scopeType = String(form.get("scopeType"));
6445
6483
  const includeAllSettings = taskType === "relationship-analysis" && scopeType === "book-with-settings";
6446
6484
  const settingsOnly = taskType === "relationship-analysis" && scopeType === "settings";
@@ -6452,7 +6490,7 @@ async function openTaskDialog() {
6452
6490
  : taskType === "character-identity-audit" || scopeType === "book" || includeAllSettings
6453
6491
  ? { type: "book", ...(includeAllSettings ? { includeAllSettings: true } : {}), ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) }
6454
6492
  : { type: "chapter", chapterId: form.get("chapterId"), ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) };
6455
- await api(`/api/works/${state.work.id}/tasks`, { method: "POST", body: { taskType, scope } });
6493
+ await api(`/api/works/${state.work.id}/tasks`, { method: "POST", body: { taskType, scope, modelId } });
6456
6494
  taskListPage = 1;
6457
6495
  toast("分析任务已创建,已进入任务队列");
6458
6496
  void renderTasks(1).catch((error) => toast(`任务已创建,但列表刷新失败:${error.message}`, "error"));
@@ -6463,6 +6501,7 @@ async function openTaskDialog() {
6463
6501
  errorPrefix: "任务创建失败:"
6464
6502
  });
6465
6503
  const taskTypeSelect = $("#dialog-fields").querySelector('select[name="taskType"]');
6504
+ const taskModelSelect = $("#dialog-fields").querySelector('select[name="modelId"]');
6466
6505
  const scopeTypeSelect = $("#dialog-fields").querySelector('select[name="scopeType"]');
6467
6506
  const chapterSelect = $("#dialog-fields").querySelector('select[name="chapterId"]');
6468
6507
  const chapterFieldElement = chapterSelect.closest(".task-chapter-field");
@@ -6544,8 +6583,13 @@ async function openTaskDialog() {
6544
6583
  filterRelationshipCharacters();
6545
6584
  syncChapterField();
6546
6585
  };
6586
+ const syncTaskModelDefault = () => {
6587
+ const defaultId = defaultModelByTask.get(analysisTaskModelPurpose(taskTypeSelect.value)) ?? "";
6588
+ taskModelSelect.value = availableTaskModels.some((model) => model.id === defaultId) ? defaultId : "";
6589
+ };
6547
6590
  taskTypeSelect.addEventListener("change", () => {
6548
6591
  description.textContent = analysisTypeDescription(taskTypeSelect.value);
6592
+ syncTaskModelDefault();
6549
6593
  syncRelationshipOptions();
6550
6594
  });
6551
6595
  relationshipCharacterTrigger.addEventListener("click", () => {
@@ -7549,6 +7593,7 @@ $("#register-form").addEventListener("submit", async (event) => {
7549
7593
  });
7550
7594
  $("#settings-return").addEventListener("click", () => returnFromSettings().catch((error) => toast(error.message, "error")));
7551
7595
  $("#platform-ai-button").addEventListener("click", () => showPlatformAi().catch((error) => toast(error.message, "error")));
7596
+ $("#platform-ai-return").addEventListener("click", () => returnToSettingsHub("#platform-ai-button").catch((error) => toast(error.message, "error")));
7552
7597
  $("#user-management-button").addEventListener("click", openUsersDialog);
7553
7598
  $("#platform-ui-settings-button").addEventListener("click", openPlatformUiSettingsDialog);
7554
7599
  $("#collaboration-button").addEventListener("click", () => openMembersDialog());
@@ -7558,7 +7603,9 @@ $("#presence-button").addEventListener("click", () => {
7558
7603
  $("#presence-button").setAttribute("aria-expanded", String(open));
7559
7604
  });
7560
7605
  $("#users-dialog-close").addEventListener("click", () => $("#users-dialog").close());
7606
+ $("#users-settings-return").addEventListener("click", () => returnToSettingsHub("#user-management-button", "#users-dialog").catch((error) => toast(error.message, "error")));
7561
7607
  $("#platform-ui-settings-close").addEventListener("click", () => $("#platform-ui-settings-dialog").close());
7608
+ $("#platform-ui-settings-return").addEventListener("click", () => returnToSettingsHub("#platform-ui-settings-button", "#platform-ui-settings-dialog").catch((error) => toast(error.message, "error")));
7562
7609
  $("#platform-ui-settings-cancel").addEventListener("click", () => $("#platform-ui-settings-dialog").close());
7563
7610
  $("#platform-ui-settings-form").addEventListener("submit", async (event) => {
7564
7611
  event.preventDefault();
@@ -7588,6 +7635,7 @@ $("#platform-ui-settings-form").addEventListener("submit", async (event) => {
7588
7635
  }
7589
7636
  });
7590
7637
  $("#members-dialog-close").addEventListener("click", () => $("#members-dialog").close());
7638
+ $("#members-settings-return").addEventListener("click", () => returnToSettingsHub("#collaboration-button", "#members-dialog").catch((error) => toast(error.message, "error")));
7591
7639
  $("#members-dialog").addEventListener("close", () => {
7592
7640
  memberDialogWork = null;
7593
7641
  memberDialogMembers = [];
@@ -7657,6 +7705,7 @@ function cleanupExpandedRelationshipMap() {
7657
7705
  $("#relationship-map-close").addEventListener("click", () => $("#relationship-map-dialog").close());
7658
7706
  $("#relationship-map-dialog").addEventListener("close", cleanupExpandedRelationshipMap);
7659
7707
  $("#appearance-button").addEventListener("click", openAppearanceDialog);
7708
+ $("#appearance-settings-return").addEventListener("click", () => returnToSettingsHub("#appearance-button", "#appearance-dialog").catch((error) => toast(error.message, "error")));
7660
7709
  $("#toggle-whitespace-appearance").addEventListener("click", toggleChapterWhitespaceVisibility);
7661
7710
  $("#theme-toggle").addEventListener("click", () => {
7662
7711
  const theme = nextTheme(currentColorTheme());
@@ -37,6 +37,7 @@ export function reviewItemTypeLabel(value) {
37
37
  return enumLabel({
38
38
  consistency: "一致性问题",
39
39
  "character-duplicate": "角色重复",
40
+ "character-name-variant": "疑似人物名错字",
40
41
  "timeline-conflict": "时间线冲突",
41
42
  "setting-conflict": "设定冲突",
42
43
  "relationship-conflict": "关系冲突",
@@ -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=20260726-relationship-trace-results-v8">
13
+ <link rel="stylesheet" href="/styles.css?v=20260726-settings-select-spacing-v1">
14
14
  </head>
15
15
  <body class="auth-pending">
16
16
  <section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
@@ -159,7 +159,10 @@
159
159
  <section id="platform-ai-view" class="shelf-view hidden" aria-labelledby="platform-ai-title">
160
160
  <div class="shelf-header">
161
161
  <div><span class="eyebrow">平台资源</span><h1 id="platform-ai-title">AI 管理</h1><p>供应商、模型与全局系统提示词对所有作品生效;每本书可在“AI 设置”中追加自己的系统提示词。</p></div>
162
- <button id="platform-new-provider" class="primary-button" type="button">新建供应商</button>
162
+ <div class="settings-detail-actions">
163
+ <button id="platform-ai-return" class="ghost-button settings-parent-button" type="button">返回设置</button>
164
+ <button id="platform-new-provider" class="primary-button" type="button">新建供应商</button>
165
+ </div>
163
166
  </div>
164
167
  <div id="platform-ai-content" class="module-content"></div>
165
168
  </section>
@@ -497,7 +500,10 @@
497
500
  <form id="appearance-form" method="dialog">
498
501
  <div class="dialog-header">
499
502
  <div><span class="eyebrow">本机偏好</span><h2>显示设置</h2></div>
500
- <button class="dialog-close" value="cancel" aria-label="关闭" type="submit">×</button>
503
+ <div class="settings-dialog-header-actions">
504
+ <button id="appearance-settings-return" class="ghost-button settings-parent-button" type="button">返回设置</button>
505
+ <button class="dialog-close" value="cancel" aria-label="关闭" type="submit">×</button>
506
+ </div>
501
507
  </div>
502
508
  <div class="appearance-grid">
503
509
  <label>中文字体
@@ -572,14 +578,14 @@
572
578
  </dialog>
573
579
 
574
580
  <dialog id="users-dialog" class="dialog wide-dialog" aria-labelledby="users-dialog-title">
575
- <div class="dialog-header"><div><span class="eyebrow">系统管理员</span><h2 id="users-dialog-title">用户管理</h2></div><button id="users-dialog-close" class="dialog-close" aria-label="关闭" type="button">×</button></div>
581
+ <div class="dialog-header"><div><span class="eyebrow">系统管理员</span><h2 id="users-dialog-title">用户管理</h2></div><div class="settings-dialog-header-actions"><button id="users-settings-return" class="ghost-button settings-parent-button" type="button">返回设置</button><button id="users-dialog-close" class="dialog-close" aria-label="关闭" type="button">×</button></div></div>
576
582
  <div class="access-dialog-body">
577
583
  <div id="users-list" class="access-list"></div>
578
584
  </div>
579
585
  </dialog>
580
586
 
581
587
  <dialog id="platform-ui-settings-dialog" class="dialog" aria-labelledby="platform-ui-settings-title">
582
- <div class="dialog-header"><div><span class="eyebrow">系统管理员</span><h2 id="platform-ui-settings-title">界面与分页</h2></div><button id="platform-ui-settings-close" class="dialog-close" aria-label="关闭" type="button">×</button></div>
588
+ <div class="dialog-header"><div><span class="eyebrow">系统管理员</span><h2 id="platform-ui-settings-title">界面与分页</h2></div><div class="settings-dialog-header-actions"><button id="platform-ui-settings-return" class="ghost-button settings-parent-button" type="button">返回设置</button><button id="platform-ui-settings-close" class="dialog-close" aria-label="关闭" type="button">×</button></div></div>
583
589
  <form id="platform-ui-settings-form">
584
590
  <div class="dialog-fields">
585
591
  <label>Toast 提示位置<select id="toast-position" name="toastPosition" aria-label="Toast 提示位置"><option value="bottom-right">右下角(默认)</option><option value="top-right">右上角</option></select></label>
@@ -660,7 +666,7 @@
660
666
  </dialog>
661
667
 
662
668
  <dialog id="members-dialog" class="dialog wide-dialog" aria-labelledby="members-dialog-title">
663
- <div class="dialog-header"><div><span id="members-dialog-eyebrow" class="eyebrow">作品权限</span><h2 id="members-dialog-title">成员模块权限</h2></div><button id="members-dialog-close" class="dialog-close" aria-label="关闭" type="button">×</button></div>
669
+ <div class="dialog-header"><div><span id="members-dialog-eyebrow" class="eyebrow">作品权限</span><h2 id="members-dialog-title">成员模块权限</h2></div><div class="settings-dialog-header-actions"><button id="members-settings-return" class="ghost-button settings-parent-button hidden" type="button">返回设置</button><button id="members-dialog-close" class="dialog-close" aria-label="关闭" type="button">×</button></div></div>
664
670
  <div class="access-dialog-body">
665
671
  <form id="member-permission-form" class="member-permission-form">
666
672
  <label class="member-person-field">选择成员<select id="member-user-select" required></select><small>先选择已注册用户,再逐项配置模块权限。</small></label>
@@ -745,6 +751,6 @@
745
751
  <div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
746
752
  <script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
747
753
  <script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
748
- <script type="module" src="/app.js?v=20260726-relationship-trace-results-v8"></script>
754
+ <script type="module" src="/app.js?v=20260726-relationship-pinyin-v10"></script>
749
755
  </body>
750
756
  </html>
@@ -521,6 +521,9 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
521
521
  .shelf-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 30px; width: 100%; max-width: 1400px; margin: 0 auto 38px; }
522
522
  .shelf-header h1 { margin: 0 0 10px; font-size: clamp(30px, 4vw, 52px); font-weight: 560; letter-spacing: -.04em; }
523
523
  .shelf-header p { margin: 0; color: var(--muted); }
524
+ .settings-detail-actions, .settings-dialog-header-actions { display: flex; align-items: center; gap: 10px; }
525
+ .settings-detail-actions { flex: 0 0 auto; }
526
+ .settings-parent-button { white-space: nowrap; }
524
527
  .settings-hub-header { align-items: center; }
525
528
  .settings-hub-header h1 { font-size: clamp(26px, 3vw, 36px); line-height: 1.15; letter-spacing: -.02em; }
526
529
  .settings-hub-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; max-width: 1000px; margin: 0 auto; }
@@ -620,6 +623,17 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
620
623
  .member-permission-row select { min-height: 34px; padding-block: 6px; font-size: 11px; }
621
624
  .member-permission-form .primary-button { justify-self: end; min-height: 38px; white-space: nowrap; }
622
625
  .permission-hidden { display: none !important; }
626
+ :is(#users-dialog, #platform-ui-settings-dialog, #members-dialog, #appearance-dialog) select:not([multiple]) {
627
+ appearance: none;
628
+ -webkit-appearance: none;
629
+ padding-inline-end: 40px;
630
+ background-image:
631
+ linear-gradient(45deg, transparent 50%, var(--muted) 50%),
632
+ linear-gradient(135deg, var(--muted) 50%, transparent 50%);
633
+ background-position: right 21px center, right 16px center;
634
+ background-repeat: no-repeat;
635
+ background-size: 5px 5px, 5px 5px;
636
+ }
623
637
  .app-shell.ai-hidden-mode:not(.shelf-mode) { grid-template-columns: var(--left-panel-width) minmax(480px, 1fr); }
624
638
  .app-shell.prose-hidden-mode:not(.shelf-mode) .left-actions,
625
639
  .app-shell.prose-hidden-mode:not(.shelf-mode) .panel-heading,
@@ -2587,6 +2601,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2587
2601
  .shelf-header, .module-header { align-items: stretch; flex-direction: column; gap: 14px; margin-bottom: 20px; }
2588
2602
  .shelf-header h1 { font-size: clamp(24px, 7.5vw, 32px); }
2589
2603
  .shelf-header .primary-button { align-self: flex-start; min-width: 150px; min-height: 40px; padding: 10px 18px; }
2604
+ .settings-detail-actions { align-items: stretch; flex-direction: column; }
2605
+ .settings-detail-actions .primary-button, .settings-detail-actions .settings-parent-button { align-self: stretch; width: 100%; }
2590
2606
  .module-header-actions { width: 100%; }
2591
2607
  .book-shelf { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 14px; }
2592
2608
  .book-info { padding-top: 10px; padding-right: 0; }
@@ -2632,6 +2648,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2632
2648
  }
2633
2649
  .dialog-header { padding: 14px 16px 12px; }
2634
2650
  .dialog-header h2 { font-size: 20px; }
2651
+ .settings-dialog-header-actions { gap: 6px; }
2635
2652
  .dialog-fields, .appearance-grid, .access-dialog-body, .account-settings-body, .versions-list, .entity-history-list { padding-inline: 16px; }
2636
2653
  .task-detail-overview { grid-template-columns: minmax(0, 1fr); padding: 12px; }
2637
2654
  .task-detail-overview > * { grid-column: 1 !important; }
@@ -0,0 +1,196 @@
1
+ import { pinyin } from "pinyin-pro";
2
+ export const RELATIONSHIP_SEARCH_POLICY_VERSION = 1;
3
+ export function normalizeRelationshipSearchText(value) {
4
+ return value.normalize("NFKC").toLocaleLowerCase("zh-CN");
5
+ }
6
+ function encodedCodePoint(value) {
7
+ return [...value].map((character) => character.codePointAt(0).toString(16)).join("x");
8
+ }
9
+ function safePinyinToken(value) {
10
+ const normalized = value.normalize("NFKC").toLocaleLowerCase("zh-CN").replaceAll("ü", "v");
11
+ return /^[a-z0-9]+$/u.test(normalized) ? `p${normalized}` : `u${encodedCodePoint(normalized)}`;
12
+ }
13
+ export function relationshipCharacterTokens(value) {
14
+ return [...normalizeRelationshipSearchText(value)].map((character) => `u${encodedCodePoint(character)}`);
15
+ }
16
+ export function relationshipPinyinSyllables(value) {
17
+ const normalized = normalizeRelationshipSearchText(value);
18
+ return pinyin(normalized, { toneType: "none", type: "array" })
19
+ .map((item) => item.normalize("NFKC").toLocaleLowerCase("zh-CN").replaceAll("ü", "v"));
20
+ }
21
+ export function relationshipPinyinTokens(value) {
22
+ return relationshipPinyinSyllables(value).map(safePinyinToken);
23
+ }
24
+ export function relationshipCharacterTokenText(value) {
25
+ return relationshipCharacterTokens(value).join(" ");
26
+ }
27
+ export function relationshipPinyinTokenText(value) {
28
+ return relationshipPinyinTokens(value).join(" ");
29
+ }
30
+ export function ftsPhrase(tokens) {
31
+ return `"${tokens.join(" ").replaceAll('"', '""')}"`;
32
+ }
33
+ export function damerauLevenshteinDistance(left, right, maximum = Number.POSITIVE_INFINITY) {
34
+ if (Math.abs(left.length - right.length) > maximum)
35
+ return maximum + 1;
36
+ const previousPrevious = new Array(right.length + 1).fill(0);
37
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
38
+ for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
39
+ const current = new Array(right.length + 1).fill(0);
40
+ current[0] = leftIndex;
41
+ let rowMinimum = current[0];
42
+ for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
43
+ const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
44
+ current[rightIndex] = Math.min(previous[rightIndex] + 1, current[rightIndex - 1] + 1, previous[rightIndex - 1] + substitutionCost);
45
+ if (leftIndex > 1 && rightIndex > 1
46
+ && left[leftIndex - 1] === right[rightIndex - 2]
47
+ && left[leftIndex - 2] === right[rightIndex - 1]) {
48
+ current[rightIndex] = Math.min(current[rightIndex], previousPrevious[rightIndex - 2] + 1);
49
+ }
50
+ rowMinimum = Math.min(rowMinimum, current[rightIndex]);
51
+ }
52
+ if (rowMinimum > maximum)
53
+ return maximum + 1;
54
+ for (let index = 0; index < previous.length; index += 1)
55
+ previousPrevious[index] = previous[index];
56
+ previous = current;
57
+ }
58
+ return previous[right.length];
59
+ }
60
+ export class RelationshipApproximateMatchLimitError extends Error {
61
+ maximumCandidates;
62
+ constructor(maximumCandidates) {
63
+ super(`Approximate relationship match candidates exceeded ${maximumCandidates}`);
64
+ this.maximumCandidates = maximumCandidates;
65
+ this.name = "RelationshipApproximateMatchLimitError";
66
+ }
67
+ }
68
+ function selectApproximateNameMatches(matches, referenceLength, limit) {
69
+ const ranked = matches.sort((left, right) => Math.min(left.characterDistance, left.pinyinDistance) - Math.min(right.characterDistance, right.pinyinDistance)
70
+ || Math.abs([...left.observed].length - referenceLength) - Math.abs([...right.observed].length - referenceLength)
71
+ || left.start - right.start);
72
+ const selected = [];
73
+ for (const match of ranked) {
74
+ if (selected.some((item) => match.start < item.end && match.end > item.start))
75
+ continue;
76
+ selected.push(match);
77
+ if (selected.length >= limit)
78
+ break;
79
+ }
80
+ return selected.sort((left, right) => left.start - right.start);
81
+ }
82
+ export function findApproximateNameMatches(content, reference, limit = 3, excludedObserved = new Set(), maximumCandidates = 256) {
83
+ const normalizedContent = normalizeRelationshipSearchText(content);
84
+ const normalizedReference = normalizeRelationshipSearchText(reference).trim();
85
+ const sourceCharacters = [...normalizedContent];
86
+ const utf16Offsets = new Array(sourceCharacters.length + 1).fill(0);
87
+ for (let index = 0; index < sourceCharacters.length; index += 1) {
88
+ utf16Offsets[index + 1] = utf16Offsets[index] + sourceCharacters[index].length;
89
+ }
90
+ const referenceCharacters = [...normalizedReference];
91
+ if (referenceCharacters.length < 2 || sourceCharacters.length === 0)
92
+ return [];
93
+ const hanReference = referenceCharacters.every((character) => /\p{Script=Han}/u.test(character));
94
+ const normalizedExcluded = new Set([...excludedObserved].map((item) => normalizeRelationshipSearchText(item).trim()));
95
+ const referencePinyin = relationshipPinyinSyllables(normalizedReference);
96
+ const sourcePinyin = relationshipPinyinSyllables(normalizedContent);
97
+ const exactCoverage = new Uint8Array(sourceCharacters.length);
98
+ for (let start = 0; start + referenceCharacters.length <= sourceCharacters.length; start += 1) {
99
+ if (!referenceCharacters.every((character, index) => sourceCharacters[start + index] === character))
100
+ continue;
101
+ exactCoverage.fill(1, start, start + referenceCharacters.length);
102
+ }
103
+ const matches = [];
104
+ const seen = new Set();
105
+ for (const windowLength of [referenceCharacters.length, referenceCharacters.length - 1, referenceCharacters.length + 1]) {
106
+ if (windowLength < 1)
107
+ continue;
108
+ for (let start = 0; start + windowLength <= sourceCharacters.length; start += 1) {
109
+ const observedCharacters = sourceCharacters.slice(start, start + windowLength);
110
+ if (observedCharacters.length < 2)
111
+ continue;
112
+ if (hanReference && !observedCharacters.every((character) => /\p{Script=Han}/u.test(character)))
113
+ continue;
114
+ const observed = observedCharacters.join("");
115
+ if (!observed.trim() || observed === normalizedReference || normalizedExcluded.has(observed))
116
+ continue;
117
+ if (exactCoverage.subarray(start, start + windowLength).some((value) => value === 1))
118
+ continue;
119
+ const characterDistance = damerauLevenshteinDistance(referenceCharacters, observedCharacters, 1);
120
+ const observedPinyin = sourcePinyin.slice(start, start + windowLength);
121
+ const pinyinDistance = damerauLevenshteinDistance(referencePinyin, observedPinyin, 1);
122
+ if (characterDistance > 1 && pinyinDistance > 1)
123
+ continue;
124
+ const key = `${start}:${observed}`;
125
+ if (seen.has(key))
126
+ continue;
127
+ seen.add(key);
128
+ matches.push({
129
+ observed,
130
+ start,
131
+ end: start + windowLength,
132
+ utf16Start: utf16Offsets[start],
133
+ utf16End: utf16Offsets[start + windowLength],
134
+ characterDistance,
135
+ pinyinDistance
136
+ });
137
+ if (matches.length > maximumCandidates)
138
+ throw new RelationshipApproximateMatchLimitError(maximumCandidates);
139
+ }
140
+ }
141
+ return selectApproximateNameMatches(matches, referenceCharacters.length, limit);
142
+ }
143
+ export async function findApproximateNameMatchesChunked(content, reference, limit = 3, excludedObserved = new Set(), maximumCandidates = 256, chunkSize = 16_384) {
144
+ const normalizedContent = normalizeRelationshipSearchText(content);
145
+ const referenceLength = [...normalizeRelationshipSearchText(reference).trim()].length;
146
+ if (referenceLength < 2 || !normalizedContent || chunkSize < 1)
147
+ return [];
148
+ const overlapSize = referenceLength + 1;
149
+ const matches = [];
150
+ const seen = new Set();
151
+ let processedCharacters = 0;
152
+ let processedCodeUnits = 0;
153
+ let tail = [];
154
+ let chunk = [];
155
+ const processChunk = async () => {
156
+ if (chunk.length === 0)
157
+ return;
158
+ const prefixLength = tail.length;
159
+ const prefixCodeUnits = tail.reduce((total, character) => total + character.length, 0);
160
+ const combined = [...tail, ...chunk];
161
+ const baseOffset = processedCharacters - prefixLength;
162
+ const baseCodeUnitOffset = processedCodeUnits - prefixCodeUnits;
163
+ const chunkMatches = findApproximateNameMatches(combined.join(""), reference, maximumCandidates + 1, excludedObserved, maximumCandidates);
164
+ for (const match of chunkMatches) {
165
+ if (match.end <= prefixLength)
166
+ continue;
167
+ const adjusted = {
168
+ ...match,
169
+ start: match.start + baseOffset,
170
+ end: match.end + baseOffset,
171
+ utf16Start: match.utf16Start + baseCodeUnitOffset,
172
+ utf16End: match.utf16End + baseCodeUnitOffset
173
+ };
174
+ const key = `${adjusted.start}:${adjusted.observed}`;
175
+ if (seen.has(key))
176
+ continue;
177
+ seen.add(key);
178
+ matches.push(adjusted);
179
+ if (matches.length > maximumCandidates)
180
+ throw new RelationshipApproximateMatchLimitError(maximumCandidates);
181
+ }
182
+ processedCharacters += chunk.length;
183
+ processedCodeUnits += chunk.reduce((total, character) => total + character.length, 0);
184
+ tail = combined.slice(-overlapSize);
185
+ chunk = [];
186
+ await new Promise((resolve) => setImmediate(resolve));
187
+ };
188
+ for (const character of normalizedContent) {
189
+ chunk.push(character);
190
+ if (chunk.length >= chunkSize)
191
+ await processChunk();
192
+ }
193
+ await processChunk();
194
+ return selectApproximateNameMatches(matches, referenceLength, limit);
195
+ }
196
+ //# sourceMappingURL=relationship-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-search.js","sourceRoot":"","sources":["../src/relationship-search.ts"],"names":[],"mappings":"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"}