@musnows/scriverse 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -21
- package/README.en.md +8 -0
- package/README.md +12 -3
- package/dist/ai-protocol.js +216 -0
- package/dist/ai-protocol.js.map +1 -0
- package/dist/ai.js +1082 -187
- package/dist/ai.js.map +1 -1
- package/dist/app.js +51 -6
- package/dist/app.js.map +1 -1
- package/dist/database.js +339 -0
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +77 -16
- package/dist/public/display-labels.d.ts +1 -0
- package/dist/public/display-labels.js +8 -0
- package/dist/public/index.html +13 -7
- package/dist/public/styles.css +17 -0
- package/dist/relationship-search.js +196 -0
- package/dist/relationship-search.js.map +1 -0
- package/dist/store.js +59 -8
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +23 -6
- package/dist/user-auth.js.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
package/dist/public/app.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
occurrenceRoleLabel,
|
|
22
22
|
outlineStatusLabel,
|
|
23
23
|
providerConnectionLabel,
|
|
24
|
+
providerProtocolLabel,
|
|
24
25
|
providerStatusLabel,
|
|
25
26
|
relationshipCategoryLabel,
|
|
26
27
|
relationshipConfirmationLabel,
|
|
@@ -31,7 +32,7 @@ import {
|
|
|
31
32
|
taskScopeLabel,
|
|
32
33
|
timelineStatusLabel,
|
|
33
34
|
characterStateFieldLabel
|
|
34
|
-
} from "/display-labels.js?v=
|
|
35
|
+
} from "/display-labels.js?v=20260726-anthropic-messages-v2";
|
|
35
36
|
import { parsePageRoute, serializePageRoute } from "/page-route.js?v=20260723-knowledge-editor-page";
|
|
36
37
|
import { splitRelationshipKeywordInput, splitRelationshipKeywords, uniqueRelationshipKeywords } from "/relationship-keywords.js?v=20260720-relationship-keyword-chips";
|
|
37
38
|
import { tokenizeVisibleSpaces } from "/whitespace-visualization.js?v=20260718-visible-whitespace";
|
|
@@ -147,6 +148,14 @@ function analysisTaskTypeLabel(taskType) {
|
|
|
147
148
|
return analysisTaskTypeLabels.get(String(taskType)) ?? "其他分析";
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
function analysisTaskModelPurpose(taskType) {
|
|
152
|
+
if (taskType === "timeline-analysis") return "timeline-analysis";
|
|
153
|
+
if (taskType === "relationship-analysis") return "relationship-analysis";
|
|
154
|
+
if (taskType === "consistency-check") return "consistency-check";
|
|
155
|
+
if (taskType === "chapter-analysis") return "chapter-analysis";
|
|
156
|
+
return "book-analysis";
|
|
157
|
+
}
|
|
158
|
+
|
|
150
159
|
function analysisTaskStatusLabel(status) {
|
|
151
160
|
return ({
|
|
152
161
|
pending: "待执行",
|
|
@@ -2646,6 +2655,7 @@ async function openMembersDialog(targetWork = state.work) {
|
|
|
2646
2655
|
if (!targetWork) return;
|
|
2647
2656
|
memberDialogWork = targetWork;
|
|
2648
2657
|
const canManage = ["admin", "owner"].includes(String(targetWork.accessRole));
|
|
2658
|
+
$("#members-settings-return").classList.toggle("hidden", $("#settings-hub-view").classList.contains("hidden"));
|
|
2649
2659
|
$("#members-dialog-eyebrow").textContent = `作品权限 · 《${targetWork.title}》`;
|
|
2650
2660
|
$("#members-dialog-title").textContent = "成员模块权限";
|
|
2651
2661
|
$("#members-list").innerHTML = '<p class="empty-state">正在读取成员……</p>';
|
|
@@ -2755,6 +2765,13 @@ async function showSettingsHub() {
|
|
|
2755
2765
|
return true;
|
|
2756
2766
|
}
|
|
2757
2767
|
|
|
2768
|
+
async function returnToSettingsHub(actionSelector, dialogSelector = null) {
|
|
2769
|
+
const dialog = dialogSelector ? $(dialogSelector) : null;
|
|
2770
|
+
if (dialog?.open) dialog.close();
|
|
2771
|
+
if (!(await showSettingsHub())) return;
|
|
2772
|
+
queueMicrotask(() => $(actionSelector)?.focus());
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2758
2775
|
async function returnFromSettings() {
|
|
2759
2776
|
const context = settingsReturnContext ?? { view: "shelf" };
|
|
2760
2777
|
settingsReturnContext = null;
|
|
@@ -3438,9 +3455,11 @@ function openReviewDetailDialog(item) {
|
|
|
3438
3455
|
const evidenceHtml = evidence.length
|
|
3439
3456
|
? `<ul>${evidence.map((entry) => {
|
|
3440
3457
|
if (!entry || typeof entry !== "object") return `<li>${esc(String(entry))}</li>`;
|
|
3441
|
-
const source = entry.chapterTitle || entry.chapterId || "相关证据";
|
|
3458
|
+
const source = entry.sourceTitle || entry.chapterTitle || entry.chapterId || "相关证据";
|
|
3442
3459
|
const quote = entry.quote ? `<blockquote>${esc(entry.quote)}</blockquote>` : "";
|
|
3443
|
-
const supports = entry.
|
|
3460
|
+
const supports = entry.reason
|
|
3461
|
+
? `<small>${esc(entry.reason)}${typeof entry.confidence === "number" ? ` · 置信度 ${Math.round(entry.confidence * 100)}%` : ""}</small>`
|
|
3462
|
+
: entry.supports ? `<small>${esc(entry.supports)}</small>` : "";
|
|
3444
3463
|
return `<li><strong>${esc(source)}</strong>${quote}${supports}</li>`;
|
|
3445
3464
|
}).join("")}</ul>`
|
|
3446
3465
|
: "<p>暂无证据</p>";
|
|
@@ -4031,9 +4050,10 @@ async function renderTasks(page = taskListPage) {
|
|
|
4031
4050
|
</div>
|
|
4032
4051
|
</div>
|
|
4033
4052
|
</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) => `
|
|
4053
|
+
${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
4054
|
<tr>
|
|
4036
4055
|
<td>${esc(analysisTaskTypeLabel(item.taskType))}</td>
|
|
4056
|
+
<td>${esc(item.model?.displayName || "运行时使用默认模型")}</td>
|
|
4037
4057
|
<td>${esc(item.scopeSummary || taskScopeLabel(item.scope?.type || "book"))}</td>
|
|
4038
4058
|
<td class="task-status-cell">${renderAnalysisTaskStatus(item)}</td>
|
|
4039
4059
|
<td class="task-progress-cell">${renderAnalysisTaskProgress(item)}</td>
|
|
@@ -4113,7 +4133,7 @@ async function renderTasks(page = taskListPage) {
|
|
|
4113
4133
|
const cancel = button.parentElement.querySelector("[data-cancel-task]");
|
|
4114
4134
|
if (cancel) cancel.textContent = "取消运行";
|
|
4115
4135
|
scheduleTaskProgressRefresh(workId, 1);
|
|
4116
|
-
const completed = await api(`/api/tasks/${button.dataset.runTask}/run`, { method: "POST", body: {
|
|
4136
|
+
const completed = await api(`/api/tasks/${button.dataset.runTask}/run`, { method: "POST", body: {} });
|
|
4117
4137
|
toast(completed.status === "cancelled" ? "分析任务已取消" : completed.status === "expired" ? "正文已变化,本次分析已过期" : "分析已完成");
|
|
4118
4138
|
if (state.module === "tasks" && state.work?.id === workId) await renderTasks();
|
|
4119
4139
|
} catch (error) {
|
|
@@ -4440,6 +4460,7 @@ function openTaskDetailDialog(task, trace) {
|
|
|
4440
4460
|
<section class="task-detail-overview">
|
|
4441
4461
|
<p><strong>任务 ID</strong><br><code>${esc(task.id)}</code><br><small>创建于 ${esc(formatDateTime(task.createdAt))} · 更新于 ${esc(formatDateTime(task.updatedAt))}</small></p>
|
|
4442
4462
|
<p><strong>类型</strong> ${esc(analysisTaskTypeLabel(task.taskType))}</p>
|
|
4463
|
+
<p><strong>任务模型</strong> ${esc(task.model?.displayName || "运行时使用默认模型")}${task.model?.modelId ? ` · <code>${esc(task.model.modelId)}</code>` : ""}</p>
|
|
4443
4464
|
<p><strong>状态</strong> ${esc(analysisTaskStatusLabel(task.status))} · 进度 ${Number(task.progress ?? 0)}%</p>
|
|
4444
4465
|
<p><strong>范围摘要</strong> ${esc(task.scopeSummary || "未指定")}</p>
|
|
4445
4466
|
<div><strong>范围详情</strong><ul>${detailHtml}</ul></div>
|
|
@@ -4457,11 +4478,11 @@ function openTaskDetailDialog(task, trace) {
|
|
|
4457
4478
|
|
|
4458
4479
|
function renderProviderCards(providers, models) {
|
|
4459
4480
|
return providers.length ? `<div class="card-grid provider-card-grid">${providers.map((provider) => `
|
|
4460
|
-
<article class="record-card provider-card"><small>平台级 · ${esc(providerStatusLabel(provider.status))} · ${esc(providerConnectionLabel(provider.connectionStatus))}</small><h3>${esc(provider.name)}</h3>
|
|
4481
|
+
<article class="record-card provider-card"><small>平台级 · ${esc(providerProtocolLabel(provider.protocol))} · ${esc(providerStatusLabel(provider.status))} · ${esc(providerConnectionLabel(provider.connectionStatus))}</small><h3>${esc(provider.name)}</h3>
|
|
4461
4482
|
<p>${esc(provider.baseUrl)}\n密钥:${esc(provider.apiKey)}\n并发:${provider.concurrencyLimit} · 每分钟请求:${provider.rpmLimit} · 最大输出:${provider.maxTokens ?? 32000}${provider.lastError ? `\n错误:${esc(provider.lastError)}` : ""}</p>
|
|
4462
4483
|
<div class="provider-models">${models.filter((model) => model.providerId === provider.id).map((model) => `<button class="pill model-pill" type="button" data-edit-model="${esc(model.id)}" aria-label="编辑模型 ${esc(model.displayName)}">${esc(model.displayName)} · ${model.enabled ? "启用" : "停用"} · 思考模式 ${model.thinkingEnabled ? "开启" : "关闭"} · 上下文 ${Number(model.contextWindow ?? 128000).toLocaleString("zh-CN")} 令牌 · 最大输出 ${Number(model.preset?.max_tokens ?? 32000).toLocaleString("zh-CN")}</button>`).join("")}</div>
|
|
4463
4484
|
<div class="card-actions"><button data-edit-provider="${esc(provider.id)}">编辑配置</button><button data-test-provider="${esc(provider.id)}">测试连接</button><button data-add-model="${esc(provider.id)}">添加模型</button></div></article>`).join("")}</div>`
|
|
4464
|
-
: emptyModule("尚未配置 AI 供应商", "添加 OpenAI 兼容接口地址和密钥,测试成功后再添加模型。");
|
|
4485
|
+
: emptyModule("尚未配置 AI 供应商", "添加 OpenAI 或 Anthropic 兼容接口地址和密钥,测试成功后再添加模型。");
|
|
4465
4486
|
}
|
|
4466
4487
|
|
|
4467
4488
|
function bindPlatformProviderActions(host, providers, models) {
|
|
@@ -6401,14 +6422,26 @@ function openReviewDialog() {
|
|
|
6401
6422
|
async function openTaskDialog() {
|
|
6402
6423
|
const chapterOptions = state.work.volumes.flatMap((volume) => volume.chapters.map((chapter) => [chapter.id, `${volume.title} / ${chapter.title}`]));
|
|
6403
6424
|
let relationshipCharacters = [];
|
|
6425
|
+
let taskModels = [];
|
|
6426
|
+
let taskDefaults = [];
|
|
6404
6427
|
try {
|
|
6405
|
-
relationshipCharacters =
|
|
6406
|
-
|
|
6407
|
-
|
|
6428
|
+
[relationshipCharacters, taskModels, taskDefaults] = await Promise.all([
|
|
6429
|
+
canReadModule("characters")
|
|
6430
|
+
? apiAllPages(`/api/works/${state.work.id}/characters`)
|
|
6431
|
+
: Promise.resolve([]),
|
|
6432
|
+
api(`/api/works/${state.work.id}/models`),
|
|
6433
|
+
api(`/api/works/${state.work.id}/task-defaults`)
|
|
6434
|
+
]);
|
|
6408
6435
|
} catch (error) {
|
|
6409
|
-
toast(
|
|
6436
|
+
toast(`分析任务配置加载失败:${error.message}`, "error");
|
|
6410
6437
|
return;
|
|
6411
6438
|
}
|
|
6439
|
+
const defaultModelByTask = new Map(taskDefaults.map((item) => [item.taskType, item.model.id]));
|
|
6440
|
+
const availableTaskModels = taskModels.filter((model) =>
|
|
6441
|
+
model.enabled
|
|
6442
|
+
&& model.providerStatus === "enabled"
|
|
6443
|
+
&& model.providerConnectionStatus === "success"
|
|
6444
|
+
);
|
|
6412
6445
|
const characterOptions = relationshipCharacters.map((character) => [character.id, character.name]);
|
|
6413
6446
|
const relationshipCharacterPicker = `<div class="form-field relationship-character-field">
|
|
6414
6447
|
<span id="relationship-character-label">被分析角色(可多选)</span>
|
|
@@ -6429,6 +6462,11 @@ async function openTaskDialog() {
|
|
|
6429
6462
|
</div>`;
|
|
6430
6463
|
const defaultTaskType = ANALYSIS_TYPES[0].value;
|
|
6431
6464
|
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>`;
|
|
6465
|
+
const defaultModelId = defaultModelByTask.get(analysisTaskModelPurpose(defaultTaskType)) ?? "";
|
|
6466
|
+
const modelField = `<label>任务模型<select name="modelId" required aria-describedby="analysis-task-model-help">
|
|
6467
|
+
<option value="" ${availableTaskModels.some((model) => model.id === defaultModelId) ? "" : "selected"} disabled>${availableTaskModels.length ? "请选择模型" : "没有可用模型"}</option>
|
|
6468
|
+
${availableTaskModels.map((model) => `<option value="${esc(model.id)}" ${model.id === defaultModelId ? "selected" : ""}>${esc(modelOptionLabel(model))}</option>`).join("")}
|
|
6469
|
+
</select><small id="analysis-task-model-help">默认值来自“本书 AI 设置”,只修改当前任务,不会改变全书默认模型。</small></label>`;
|
|
6432
6470
|
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
6471
|
const relationshipFields = `<div class="relationship-analysis-options hidden">
|
|
6434
6472
|
${relationshipCharacterPicker}
|
|
@@ -6439,8 +6477,9 @@ async function openTaskDialog() {
|
|
|
6439
6477
|
</div>
|
|
6440
6478
|
<label>额外分析提示<textarea name="additionalPrompt" maxlength="10000" placeholder="例如:重点识别权力继承、师承变化或隐秘亲缘关系"></textarea><small>将同时追加到证据收集和全局关系归纳提示词,仅影响本次任务。</small></label>
|
|
6441
6479
|
</div>`;
|
|
6442
|
-
openDialog("开始 AI 分析", taskTypeField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + chapterField + relationshipFields, async (form) => {
|
|
6480
|
+
openDialog("开始 AI 分析", taskTypeField + modelField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + chapterField + relationshipFields, async (form) => {
|
|
6443
6481
|
const taskType = String(form.get("taskType"));
|
|
6482
|
+
const modelId = String(form.get("modelId"));
|
|
6444
6483
|
const scopeType = String(form.get("scopeType"));
|
|
6445
6484
|
const includeAllSettings = taskType === "relationship-analysis" && scopeType === "book-with-settings";
|
|
6446
6485
|
const settingsOnly = taskType === "relationship-analysis" && scopeType === "settings";
|
|
@@ -6452,7 +6491,7 @@ async function openTaskDialog() {
|
|
|
6452
6491
|
: taskType === "character-identity-audit" || scopeType === "book" || includeAllSettings
|
|
6453
6492
|
? { type: "book", ...(includeAllSettings ? { includeAllSettings: true } : {}), ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) }
|
|
6454
6493
|
: { 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 } });
|
|
6494
|
+
await api(`/api/works/${state.work.id}/tasks`, { method: "POST", body: { taskType, scope, modelId } });
|
|
6456
6495
|
taskListPage = 1;
|
|
6457
6496
|
toast("分析任务已创建,已进入任务队列");
|
|
6458
6497
|
void renderTasks(1).catch((error) => toast(`任务已创建,但列表刷新失败:${error.message}`, "error"));
|
|
@@ -6463,6 +6502,7 @@ async function openTaskDialog() {
|
|
|
6463
6502
|
errorPrefix: "任务创建失败:"
|
|
6464
6503
|
});
|
|
6465
6504
|
const taskTypeSelect = $("#dialog-fields").querySelector('select[name="taskType"]');
|
|
6505
|
+
const taskModelSelect = $("#dialog-fields").querySelector('select[name="modelId"]');
|
|
6466
6506
|
const scopeTypeSelect = $("#dialog-fields").querySelector('select[name="scopeType"]');
|
|
6467
6507
|
const chapterSelect = $("#dialog-fields").querySelector('select[name="chapterId"]');
|
|
6468
6508
|
const chapterFieldElement = chapterSelect.closest(".task-chapter-field");
|
|
@@ -6544,8 +6584,13 @@ async function openTaskDialog() {
|
|
|
6544
6584
|
filterRelationshipCharacters();
|
|
6545
6585
|
syncChapterField();
|
|
6546
6586
|
};
|
|
6587
|
+
const syncTaskModelDefault = () => {
|
|
6588
|
+
const defaultId = defaultModelByTask.get(analysisTaskModelPurpose(taskTypeSelect.value)) ?? "";
|
|
6589
|
+
taskModelSelect.value = availableTaskModels.some((model) => model.id === defaultId) ? defaultId : "";
|
|
6590
|
+
};
|
|
6547
6591
|
taskTypeSelect.addEventListener("change", () => {
|
|
6548
6592
|
description.textContent = analysisTypeDescription(taskTypeSelect.value);
|
|
6593
|
+
syncTaskModelDefault();
|
|
6549
6594
|
syncRelationshipOptions();
|
|
6550
6595
|
});
|
|
6551
6596
|
relationshipCharacterTrigger.addEventListener("click", () => {
|
|
@@ -6571,13 +6616,24 @@ async function openTaskDialog() {
|
|
|
6571
6616
|
}
|
|
6572
6617
|
|
|
6573
6618
|
function openProviderDialog(item) {
|
|
6574
|
-
|
|
6575
|
-
|
|
6619
|
+
const protocol = item?.protocol ?? "openai-chat-completions";
|
|
6620
|
+
const defaultBaseUrl = protocol === "anthropic-messages" ? "https://api.anthropic.com" : "https://api.openai.com/v1";
|
|
6621
|
+
openDialog(item ? "编辑 AI 供应商" : "新建 AI 供应商", field("name", "显示名称", "text", item?.name) + field("protocol", "接口协议", "select", protocol, [["openai-chat-completions", "OpenAI Chat Completions"], ["anthropic-messages", "Anthropic Messages"]]) + field("baseUrl", "API 基础地址", "url", item?.baseUrl ?? defaultBaseUrl) + field("apiKey", item ? "替换 API 密钥(留空则不变)" : "API 密钥", "password") + field("concurrencyLimit", "最大并发请求数", "number", item?.concurrencyLimit ?? 10) + field("rpmLimit", "每分钟请求上限", "number", item?.rpmLimit ?? 10) + field("maxTokens", "最大输出令牌数", "number", item?.maxTokens ?? 32000) + field("note", "用途备注", "textarea", item?.note) + field("enabled", item ? "启用供应商" : "立即启用", "checkbox", item ? item.status === "enabled" : true), async (form) => {
|
|
6622
|
+
const body = { name: form.get("name"), protocol: form.get("protocol"), baseUrl: form.get("baseUrl"), concurrencyLimit: Number(form.get("concurrencyLimit")), rpmLimit: Number(form.get("rpmLimit")), maxTokens: Number(form.get("maxTokens")), note: form.get("note"), status: form.get("enabled") === "on" ? "enabled" : "disabled" };
|
|
6576
6623
|
if (!item || String(form.get("apiKey") ?? "").trim()) body.apiKey = form.get("apiKey");
|
|
6577
6624
|
await api(item ? `/api/providers/${item.id}` : "/api/platform/ai/providers", { method: item ? "PATCH" : "POST", body });
|
|
6578
6625
|
await renderPlatformAiConfig();
|
|
6579
6626
|
await loadModels();
|
|
6580
|
-
}, item ? "
|
|
6627
|
+
}, item ? "协议、限流与凭据" : "OpenAI / Anthropic 兼容协议");
|
|
6628
|
+
if (!item) {
|
|
6629
|
+
const protocolSelect = $("#dialog-fields select[name='protocol']");
|
|
6630
|
+
const baseUrlInput = $("#dialog-fields input[name='baseUrl']");
|
|
6631
|
+
protocolSelect.addEventListener("change", () => {
|
|
6632
|
+
baseUrlInput.value = protocolSelect.value === "anthropic-messages"
|
|
6633
|
+
? "https://api.anthropic.com"
|
|
6634
|
+
: "https://api.openai.com/v1";
|
|
6635
|
+
});
|
|
6636
|
+
}
|
|
6581
6637
|
}
|
|
6582
6638
|
|
|
6583
6639
|
function openModelDialog(providerId, item = null) {
|
|
@@ -7549,6 +7605,7 @@ $("#register-form").addEventListener("submit", async (event) => {
|
|
|
7549
7605
|
});
|
|
7550
7606
|
$("#settings-return").addEventListener("click", () => returnFromSettings().catch((error) => toast(error.message, "error")));
|
|
7551
7607
|
$("#platform-ai-button").addEventListener("click", () => showPlatformAi().catch((error) => toast(error.message, "error")));
|
|
7608
|
+
$("#platform-ai-return").addEventListener("click", () => returnToSettingsHub("#platform-ai-button").catch((error) => toast(error.message, "error")));
|
|
7552
7609
|
$("#user-management-button").addEventListener("click", openUsersDialog);
|
|
7553
7610
|
$("#platform-ui-settings-button").addEventListener("click", openPlatformUiSettingsDialog);
|
|
7554
7611
|
$("#collaboration-button").addEventListener("click", () => openMembersDialog());
|
|
@@ -7558,7 +7615,9 @@ $("#presence-button").addEventListener("click", () => {
|
|
|
7558
7615
|
$("#presence-button").setAttribute("aria-expanded", String(open));
|
|
7559
7616
|
});
|
|
7560
7617
|
$("#users-dialog-close").addEventListener("click", () => $("#users-dialog").close());
|
|
7618
|
+
$("#users-settings-return").addEventListener("click", () => returnToSettingsHub("#user-management-button", "#users-dialog").catch((error) => toast(error.message, "error")));
|
|
7561
7619
|
$("#platform-ui-settings-close").addEventListener("click", () => $("#platform-ui-settings-dialog").close());
|
|
7620
|
+
$("#platform-ui-settings-return").addEventListener("click", () => returnToSettingsHub("#platform-ui-settings-button", "#platform-ui-settings-dialog").catch((error) => toast(error.message, "error")));
|
|
7562
7621
|
$("#platform-ui-settings-cancel").addEventListener("click", () => $("#platform-ui-settings-dialog").close());
|
|
7563
7622
|
$("#platform-ui-settings-form").addEventListener("submit", async (event) => {
|
|
7564
7623
|
event.preventDefault();
|
|
@@ -7588,6 +7647,7 @@ $("#platform-ui-settings-form").addEventListener("submit", async (event) => {
|
|
|
7588
7647
|
}
|
|
7589
7648
|
});
|
|
7590
7649
|
$("#members-dialog-close").addEventListener("click", () => $("#members-dialog").close());
|
|
7650
|
+
$("#members-settings-return").addEventListener("click", () => returnToSettingsHub("#collaboration-button", "#members-dialog").catch((error) => toast(error.message, "error")));
|
|
7591
7651
|
$("#members-dialog").addEventListener("close", () => {
|
|
7592
7652
|
memberDialogWork = null;
|
|
7593
7653
|
memberDialogMembers = [];
|
|
@@ -7657,6 +7717,7 @@ function cleanupExpandedRelationshipMap() {
|
|
|
7657
7717
|
$("#relationship-map-close").addEventListener("click", () => $("#relationship-map-dialog").close());
|
|
7658
7718
|
$("#relationship-map-dialog").addEventListener("close", cleanupExpandedRelationshipMap);
|
|
7659
7719
|
$("#appearance-button").addEventListener("click", openAppearanceDialog);
|
|
7720
|
+
$("#appearance-settings-return").addEventListener("click", () => returnToSettingsHub("#appearance-button", "#appearance-dialog").catch((error) => toast(error.message, "error")));
|
|
7660
7721
|
$("#toggle-whitespace-appearance").addEventListener("click", toggleChapterWhitespaceVisibility);
|
|
7661
7722
|
$("#theme-toggle").addEventListener("click", () => {
|
|
7662
7723
|
const theme = nextTheme(currentColorTheme());
|
|
@@ -10,6 +10,7 @@ export function reviewStatusLabel(value: unknown): string;
|
|
|
10
10
|
export function taskScopeLabel(value: unknown): string;
|
|
11
11
|
export function providerStatusLabel(value: unknown): string;
|
|
12
12
|
export function providerConnectionLabel(value: unknown): string;
|
|
13
|
+
export function providerProtocolLabel(value: unknown): string;
|
|
13
14
|
export function chapterVersionSourceLabel(value: unknown): string;
|
|
14
15
|
export function occurrenceRoleLabel(value: unknown): string;
|
|
15
16
|
export function searchResultTypeLabel(value: unknown): string;
|
|
@@ -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": "关系冲突",
|
|
@@ -64,6 +65,13 @@ export function providerConnectionLabel(value) {
|
|
|
64
65
|
return enumLabel({ unchecked: "未测试", success: "连接正常", failed: "连接失败" }, value, "未知连接状态");
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
export function providerProtocolLabel(value) {
|
|
69
|
+
return enumLabel({
|
|
70
|
+
"openai-chat-completions": "OpenAI Chat Completions",
|
|
71
|
+
"anthropic-messages": "Anthropic Messages"
|
|
72
|
+
}, value, "未知接口协议");
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
export function chapterVersionSourceLabel(value) {
|
|
68
76
|
return enumLabel({ manual: "人工保存", auto: "自动保存", "ai-suggestion": "AI 建议", restore: "历史恢复", import: "文件导入", create: "初始版本" }, value, "其他来源");
|
|
69
77
|
}
|
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=20260726-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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-
|
|
754
|
+
<script type="module" src="/app.js?v=20260726-anthropic-messages-v2"></script>
|
|
749
755
|
</body>
|
|
750
756
|
</html>
|
package/dist/public/styles.css
CHANGED
|
@@ -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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"}
|