@musnows/scriverse 0.5.1 → 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.
- package/LICENSE +661 -21
- package/README.en.md +8 -0
- package/README.md +8 -0
- package/dist/ai.js +1259 -163
- package/dist/ai.js.map +1 -1
- package/dist/app.js +138 -19
- package/dist/app.js.map +1 -1
- package/dist/database.js +340 -0
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +171 -56
- package/dist/public/display-labels.js +1 -0
- package/dist/public/index.html +13 -7
- package/dist/public/styles.css +74 -6
- package/dist/relationship-search.js +196 -0
- package/dist/relationship-search.js.map +1 -0
- package/dist/store.js +909 -13
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +23 -6
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
package/dist/public/app.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
taskScopeLabel,
|
|
32
32
|
timelineStatusLabel,
|
|
33
33
|
characterStateFieldLabel
|
|
34
|
-
} from "/display-labels.js?v=
|
|
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.
|
|
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>
|
|
@@ -4089,7 +4108,7 @@ async function renderTasks(page = taskListPage) {
|
|
|
4089
4108
|
button.disabled = true;
|
|
4090
4109
|
const taskId = encodeURIComponent(button.dataset.taskDetail);
|
|
4091
4110
|
Promise.all([
|
|
4092
|
-
api(`/api/tasks/${taskId}`),
|
|
4111
|
+
api(`/api/tasks/${taskId}/detail`),
|
|
4093
4112
|
api(`/api/tasks/${taskId}/trace`).catch((error) => {
|
|
4094
4113
|
if (error.code === "WORK_MODULE_READ_DENIED") return { restricted: true, captured: false, calls: [] };
|
|
4095
4114
|
throw error;
|
|
@@ -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: {
|
|
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) {
|
|
@@ -4241,32 +4260,6 @@ function renderTaskTraceRound(round) {
|
|
|
4241
4260
|
</section>`;
|
|
4242
4261
|
}
|
|
4243
4262
|
|
|
4244
|
-
function renderTaskTraceRoundSummary(round) {
|
|
4245
|
-
return `<section class="task-trace-round task-trace-round-summary">
|
|
4246
|
-
<header class="task-trace-round-header">
|
|
4247
|
-
<span class="task-trace-round-index">${esc(String(round?.round ?? 1))}</span>
|
|
4248
|
-
<div><strong>Agent 轮次 ${esc(String(round?.round ?? 1))}</strong><small>${Number(round?.messageCount ?? 0)} 条消息 · ${Number(round?.attemptCount ?? 0)} 次请求尝试 · ${Number(round?.toolExecutionCount ?? 0)} 次工具执行</small></div>
|
|
4249
|
-
<time>${esc(formatDateTime(round?.requestedAt))}</time>
|
|
4250
|
-
</header>
|
|
4251
|
-
<p>本轮 Prompt 共 ${Number(round?.promptChars ?? 0).toLocaleString("zh-CN")} 字符,正文、模型响应和工具结果尚未传输。</p>
|
|
4252
|
-
</section>`;
|
|
4253
|
-
}
|
|
4254
|
-
|
|
4255
|
-
function renderTaskTraceCallPreview(detail) {
|
|
4256
|
-
const trace = detail?.trace && typeof detail.trace === "object" ? detail.trace : {};
|
|
4257
|
-
const messages = Array.isArray(trace.initialMessages) ? trace.initialMessages : [];
|
|
4258
|
-
const rounds = Array.isArray(trace.rounds) ? trace.rounds : [];
|
|
4259
|
-
return `<div class="task-trace-load-state is-loaded">
|
|
4260
|
-
<div><strong>Prompt 预览</strong><p>全部消息合计最多传输 ${Number(detail?.previewLimit ?? 3000).toLocaleString("zh-CN")} 个字符;模型响应和工具结果仍未传输。</p></div>
|
|
4261
|
-
<details class="task-trace-initial" open>
|
|
4262
|
-
<summary>初始上下文预览(${messages.length} 条消息 · 原文 ${Number(detail?.totalPromptChars ?? 0).toLocaleString("zh-CN")} 字符)</summary>
|
|
4263
|
-
${renderTaskTraceMessages(messages)}
|
|
4264
|
-
</details>
|
|
4265
|
-
<div class="task-trace-rounds">${rounds.map(renderTaskTraceRoundSummary).join("") || '<p class="task-trace-empty">尚未记录 Agent 轮次。</p>'}</div>
|
|
4266
|
-
<div class="card-actions"><button class="ghost-button" type="button" data-load-task-trace-call="full">查看完整调用</button></div>
|
|
4267
|
-
</div>`;
|
|
4268
|
-
}
|
|
4269
|
-
|
|
4270
4263
|
function renderTaskTraceCallFull(detail) {
|
|
4271
4264
|
const trace = detail?.trace && typeof detail.trace === "object" ? detail.trace : {};
|
|
4272
4265
|
const messages = Array.isArray(trace.initialMessages) ? trace.initialMessages : [];
|
|
@@ -4285,19 +4278,17 @@ function bindTaskTraceCallActions(container) {
|
|
|
4285
4278
|
const call = button.closest("[data-task-trace-call]");
|
|
4286
4279
|
const content = call?.querySelector("[data-task-trace-call-content]");
|
|
4287
4280
|
if (!call || !content || button.disabled) return;
|
|
4288
|
-
const mode = button.dataset.loadTaskTraceCall;
|
|
4289
4281
|
button.disabled = true;
|
|
4290
|
-
button.textContent =
|
|
4282
|
+
button.textContent = "正在加载中";
|
|
4291
4283
|
try {
|
|
4292
4284
|
const taskId = encodeURIComponent(call.dataset.taskTraceTask);
|
|
4293
4285
|
const callId = encodeURIComponent(call.dataset.taskTraceCall);
|
|
4294
|
-
const detail = await api(`/api/tasks/${taskId}/trace/calls/${callId}
|
|
4295
|
-
content.innerHTML =
|
|
4296
|
-
bindTaskTraceCallActions(content);
|
|
4286
|
+
const detail = await api(`/api/tasks/${taskId}/trace/calls/${callId}`);
|
|
4287
|
+
content.innerHTML = renderTaskTraceCallFull(detail);
|
|
4297
4288
|
} catch (error) {
|
|
4298
4289
|
toast(error.message, "error");
|
|
4299
4290
|
button.disabled = false;
|
|
4300
|
-
button.textContent =
|
|
4291
|
+
button.textContent = "加载完整内容";
|
|
4301
4292
|
}
|
|
4302
4293
|
}));
|
|
4303
4294
|
}
|
|
@@ -4323,7 +4314,7 @@ function renderTaskTraceVisualization(trace, taskId) {
|
|
|
4323
4314
|
return `<section class="task-trace-section" aria-labelledby="task-trace-title">
|
|
4324
4315
|
<header class="task-trace-heading">
|
|
4325
4316
|
<div><span class="eyebrow">执行追踪</span><h3 id="task-trace-title">完整全流程上下文</h3></div>
|
|
4326
|
-
<p
|
|
4317
|
+
<p>首次只加载调用摘要;每次调用的完整 Prompt、响应和工具结果按需单独请求。</p>
|
|
4327
4318
|
</header>
|
|
4328
4319
|
<div class="task-trace-metrics" aria-label="执行追踪统计">
|
|
4329
4320
|
<div><strong>${capturedCalls.length}</strong><span>模型调用</span></div>
|
|
@@ -4334,10 +4325,12 @@ function renderTaskTraceVisualization(trace, taskId) {
|
|
|
4334
4325
|
<div class="task-trace-calls">${capturedCalls.map((call, index) => {
|
|
4335
4326
|
const modelName = call.model?.displayName || call.model?.modelId || "未知模型";
|
|
4336
4327
|
const providerName = call.provider?.name || "未知供应商";
|
|
4328
|
+
const sourceRefs = Array.isArray(call.sourceRefs) ? call.sourceRefs : [];
|
|
4329
|
+
const sourceSummary = sourceRefs.map((source) => String(source?.title || "")).filter(Boolean).join("、");
|
|
4337
4330
|
return `<details class="task-trace-call is-${esc(call.status || "failed")}" data-task-trace-call="${esc(call.id)}" data-task-trace-task="${esc(taskId)}" ${index === 0 ? "open" : ""}>
|
|
4338
4331
|
<summary>
|
|
4339
4332
|
<span class="task-trace-call-index">${index + 1}</span>
|
|
4340
|
-
<span><strong>${esc(modelName)}</strong><small>${esc(providerName)} · ${Number(call.trace?.roundCount || 0)} 轮 · ${Number(call.inputChars || 0).toLocaleString("zh-CN")} → ${Number(call.outputChars || 0).toLocaleString("zh-CN")} 字符</small
|
|
4333
|
+
<span><strong>${esc(modelName)}</strong><small>${esc(providerName)} · ${Number(call.trace?.roundCount || 0)} 轮 · ${Number(call.inputChars || 0).toLocaleString("zh-CN")} → ${Number(call.outputChars || 0).toLocaleString("zh-CN")} 字符</small>${sourceSummary ? `<small class="task-trace-call-sources" title="${esc(sourceSummary)}">发送:${esc(sourceSummary)}</small>` : ""}</span>
|
|
4341
4334
|
<span class="task-trace-status">${call.status === "completed" ? "已完成" : call.status === "running" ? "运行中" : "失败"}</span>
|
|
4342
4335
|
</summary>
|
|
4343
4336
|
<div class="task-trace-call-body">
|
|
@@ -4346,7 +4339,7 @@ function renderTaskTraceVisualization(trace, taskId) {
|
|
|
4346
4339
|
<div data-task-trace-call-content>
|
|
4347
4340
|
<div class="task-trace-load-state">
|
|
4348
4341
|
<div><strong>调用内容尚未加载</strong><p>这次调用的 Prompt、模型响应和工具结果不会随任务详情传输。</p></div>
|
|
4349
|
-
<button class="ghost-button" type="button" data-load-task-trace-call="
|
|
4342
|
+
<button class="ghost-button" type="button" data-load-task-trace-call="full">加载完整内容</button>
|
|
4350
4343
|
</div>
|
|
4351
4344
|
</div>
|
|
4352
4345
|
</div>
|
|
@@ -4355,6 +4348,85 @@ function renderTaskTraceVisualization(trace, taskId) {
|
|
|
4355
4348
|
</section>`;
|
|
4356
4349
|
}
|
|
4357
4350
|
|
|
4351
|
+
function renderTaskResultEvidence(item) {
|
|
4352
|
+
const evidence = Array.isArray(item.evidence) ? item.evidence : [];
|
|
4353
|
+
if (!evidence.length) return '<p class="task-result-muted">没有保存可展示的证据摘录。</p>';
|
|
4354
|
+
return `<ul class="task-result-evidence">${evidence.map((item) => {
|
|
4355
|
+
const source = item.chapterTitle || item.chapterId || "未标明章节";
|
|
4356
|
+
return `<li><strong>${esc(source)}</strong>${item.quote ? `<q>${esc(item.quote)}</q>` : ""}${item.supports ? `<small>${esc(item.supports)}</small>` : ""}</li>`;
|
|
4357
|
+
}).join("")}</ul>`;
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
function renderTaskResultItem(item) {
|
|
4361
|
+
const tags = Array.isArray(item.tags) ? item.tags : [];
|
|
4362
|
+
const details = Array.isArray(item.details) ? item.details : [];
|
|
4363
|
+
const evidence = Array.isArray(item.evidence) ? item.evidence : [];
|
|
4364
|
+
return `<article class="task-result-item">
|
|
4365
|
+
<header>
|
|
4366
|
+
<div><strong>${esc(item.title || "未命名结果")}</strong>${item.subtitle ? `<small>${esc(item.subtitle)}</small>` : ""}</div>
|
|
4367
|
+
</header>
|
|
4368
|
+
${item.description ? `<p class="task-result-item-description">${esc(item.description)}</p>` : ""}
|
|
4369
|
+
${details.length ? `<dl class="task-result-item-details">${details.map((detail) => `<div><dt>${esc(detail.label || "详情")}</dt><dd>${esc(detail.value ?? "")}</dd></div>`).join("")}</dl>` : ""}
|
|
4370
|
+
${tags.length ? `<div class="task-result-tags">${tags.map((tag) => `<span>${esc(tag)}</span>`).join("")}</div>` : ""}
|
|
4371
|
+
${evidence.length ? `<details><summary>查看原文证据(${evidence.length} 条)</summary>${renderTaskResultEvidence(item)}</details>` : ""}
|
|
4372
|
+
</article>`;
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
function renderTaskResult(task) {
|
|
4376
|
+
const result = task.resultSummary && typeof task.resultSummary === "object" ? task.resultSummary : {};
|
|
4377
|
+
const metrics = Array.isArray(result.metrics) ? result.metrics : [];
|
|
4378
|
+
const storageTargets = Array.isArray(result.storageTargets) ? result.storageTargets : [];
|
|
4379
|
+
const sections = Array.isArray(result.sections) ? result.sections : [];
|
|
4380
|
+
return `<div class="task-result-readable">
|
|
4381
|
+
<section class="task-result-section">
|
|
4382
|
+
<h4>${esc(result.title || "分析结果")}</h4>
|
|
4383
|
+
<p><strong>分析内容</strong> ${esc(result.analysisContent || `${analysisTaskTypeLabel(task.taskType)};范围:${task.scopeSummary || "未指定"}`)}</p>
|
|
4384
|
+
<p class="task-result-summary">${esc(result.summary || "任务尚未产生分析结果。")}</p>
|
|
4385
|
+
${result.restricted ? '<p class="task-result-warning">部分结果因当前账号权限受限而隐藏。</p>' : ""}
|
|
4386
|
+
</section>
|
|
4387
|
+
<section class="task-result-section">
|
|
4388
|
+
<h4>结果保存位置</h4>
|
|
4389
|
+
<p><strong>作品</strong> ${esc(state.work?.title || "当前作品")}</p>
|
|
4390
|
+
<div class="task-result-storage-list">${storageTargets.map((target) => `<article><strong>${esc(target.label || "分析结果")}</strong><p>${esc(target.location || "当前作品 · AI 分析记录")}</p><small>保存 ${Number(target.count || 0)} 条${target.note ? ` · ${esc(target.note)}` : ""}</small></article>`).join("") || "<p>没有可说明的结果保存位置。</p>"}</div>
|
|
4391
|
+
${metrics.length ? `<div class="task-result-metrics" aria-label="分析结果统计">${metrics.map((item) => `<span><strong>${esc(item.value ?? 0)}</strong>${esc(item.label || "数量")}</span>`).join("")}</div>` : ""}
|
|
4392
|
+
</section>
|
|
4393
|
+
${sections.map((section) => {
|
|
4394
|
+
const items = Array.isArray(section.items) ? section.items : [];
|
|
4395
|
+
const totalCount = Number(section.totalCount ?? items.length);
|
|
4396
|
+
return `<section class="task-result-section"><h4>${esc(section.title || "分析结论")}(${totalCount})</h4>${items.map(renderTaskResultItem).join("") || `<p class="task-result-empty">${esc(section.emptyMessage || "没有可展示的结果。")}</p>`}${totalCount > items.length ? `<p class="task-result-muted">可读摘要展示前 ${items.length} 项;完整 ${totalCount} 项可通过下方按钮查看完整 JSON。</p>` : ""}</section>`;
|
|
4397
|
+
}).join("")}
|
|
4398
|
+
<section class="task-result-json-loader">
|
|
4399
|
+
<div><strong>完整返回 JSON</strong><p>点击后按需从服务器拉取本任务的完整 JSON,不做字符截断。</p></div>
|
|
4400
|
+
<button class="ghost-button" type="button" data-load-task-result-json="${esc(task.id)}" ${task.hasResult ? "" : "disabled"}>${task.hasResult ? "查看完整 JSON" : "尚无 JSON 结果"}</button>
|
|
4401
|
+
<div class="task-result-json-content" data-task-result-json-content></div>
|
|
4402
|
+
</section>
|
|
4403
|
+
</div>`;
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
function bindTaskResultActions(container) {
|
|
4407
|
+
container.querySelectorAll("[data-load-task-result-json]").forEach((button) => button.addEventListener("click", async () => {
|
|
4408
|
+
if (button.disabled) return;
|
|
4409
|
+
const content = button.closest(".task-result-json-loader")?.querySelector("[data-task-result-json-content]");
|
|
4410
|
+
if (!content) return;
|
|
4411
|
+
button.disabled = true;
|
|
4412
|
+
button.textContent = "正在拉取完整 JSON";
|
|
4413
|
+
try {
|
|
4414
|
+
const payload = await api(`/api/tasks/${encodeURIComponent(button.dataset.loadTaskResultJson)}/result`);
|
|
4415
|
+
const resultJson = document.createElement("textarea");
|
|
4416
|
+
resultJson.readOnly = true;
|
|
4417
|
+
resultJson.spellcheck = false;
|
|
4418
|
+
resultJson.setAttribute("aria-label", "完整返回 JSON");
|
|
4419
|
+
resultJson.value = JSON.stringify(payload.result, null, 2);
|
|
4420
|
+
content.replaceChildren(resultJson);
|
|
4421
|
+
button.textContent = "完整 JSON 已加载";
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
toast(error.message, "error");
|
|
4424
|
+
button.disabled = false;
|
|
4425
|
+
button.textContent = "重新加载完整 JSON";
|
|
4426
|
+
}
|
|
4427
|
+
}));
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4358
4430
|
function openTaskDetailDialog(task, trace) {
|
|
4359
4431
|
if (!task) return;
|
|
4360
4432
|
const details = Array.isArray(task.scopeDetails) ? task.scopeDetails : [];
|
|
@@ -4371,26 +4443,28 @@ function openTaskDetailDialog(task, trace) {
|
|
|
4371
4443
|
</li>`;
|
|
4372
4444
|
}
|
|
4373
4445
|
if (item.type === "book") return "<li>全书</li>";
|
|
4446
|
+
if (item.type === "selection") return item.restricted
|
|
4447
|
+
? "<li>选定内容(正文读取权限受限)</li>"
|
|
4448
|
+
: `<li>选定内容:${esc(item.selection || "未提供")}</li>`;
|
|
4449
|
+
if (item.type === "none") return "<li>无上下文</li>";
|
|
4374
4450
|
return `<li>${esc(JSON.stringify(item))}</li>`;
|
|
4375
4451
|
}).join("") || "<li>无范围详情</li>";
|
|
4376
4452
|
const failures = Array.isArray(task.failures) ? task.failures : [];
|
|
4377
4453
|
const failureHtml = failures.length
|
|
4378
4454
|
? `<ul>${failures.map((item) => `<li>${esc(item.message || JSON.stringify(item))}</li>`).join("")}</ul>`
|
|
4379
4455
|
: "<p>无</p>";
|
|
4380
|
-
const resultPreview =
|
|
4381
|
-
? `<pre class="task-detail-result">${esc(JSON.stringify(task.result, null, 2).slice(0, 2000))}</pre>`
|
|
4382
|
-
: "<p>尚无结果</p>";
|
|
4456
|
+
const resultPreview = renderTaskResult(task);
|
|
4383
4457
|
openDialog("任务详情",
|
|
4384
4458
|
`<div class="task-detail">
|
|
4385
4459
|
<section class="task-detail-overview">
|
|
4386
|
-
<p><strong>任务 ID</strong><br><code>${esc(task.id)}</code></p>
|
|
4460
|
+
<p><strong>任务 ID</strong><br><code>${esc(task.id)}</code><br><small>创建于 ${esc(formatDateTime(task.createdAt))} · 更新于 ${esc(formatDateTime(task.updatedAt))}</small></p>
|
|
4387
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>
|
|
4388
4463
|
<p><strong>状态</strong> ${esc(analysisTaskStatusLabel(task.status))} · 进度 ${Number(task.progress ?? 0)}%</p>
|
|
4389
4464
|
<p><strong>范围摘要</strong> ${esc(task.scopeSummary || "未指定")}</p>
|
|
4390
4465
|
<div><strong>范围详情</strong><ul>${detailHtml}</ul></div>
|
|
4391
4466
|
<div><strong>失败信息</strong>${failureHtml}</div>
|
|
4392
4467
|
<div><strong>结果摘要</strong>${resultPreview}</div>
|
|
4393
|
-
<p><small>创建于 ${esc(formatDateTime(task.createdAt))} · 更新于 ${esc(formatDateTime(task.updatedAt))}</small></p>
|
|
4394
4468
|
</section>
|
|
4395
4469
|
${renderTaskTraceVisualization(trace, task.id)}
|
|
4396
4470
|
</div>`,
|
|
@@ -4398,6 +4472,7 @@ function openTaskDetailDialog(task, trace) {
|
|
|
4398
4472
|
"AI 分析详情",
|
|
4399
4473
|
{ submitLabel: "关闭", wide: true, trace: true });
|
|
4400
4474
|
bindTaskTraceCallActions($("#dialog-fields"));
|
|
4475
|
+
bindTaskResultActions($("#dialog-fields"));
|
|
4401
4476
|
}
|
|
4402
4477
|
|
|
4403
4478
|
function renderProviderCards(providers, models) {
|
|
@@ -6346,14 +6421,26 @@ function openReviewDialog() {
|
|
|
6346
6421
|
async function openTaskDialog() {
|
|
6347
6422
|
const chapterOptions = state.work.volumes.flatMap((volume) => volume.chapters.map((chapter) => [chapter.id, `${volume.title} / ${chapter.title}`]));
|
|
6348
6423
|
let relationshipCharacters = [];
|
|
6424
|
+
let taskModels = [];
|
|
6425
|
+
let taskDefaults = [];
|
|
6349
6426
|
try {
|
|
6350
|
-
relationshipCharacters =
|
|
6351
|
-
|
|
6352
|
-
|
|
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
|
+
]);
|
|
6353
6434
|
} catch (error) {
|
|
6354
|
-
toast(
|
|
6435
|
+
toast(`分析任务配置加载失败:${error.message}`, "error");
|
|
6355
6436
|
return;
|
|
6356
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
|
+
);
|
|
6357
6444
|
const characterOptions = relationshipCharacters.map((character) => [character.id, character.name]);
|
|
6358
6445
|
const relationshipCharacterPicker = `<div class="form-field relationship-character-field">
|
|
6359
6446
|
<span id="relationship-character-label">被分析角色(可多选)</span>
|
|
@@ -6374,27 +6461,36 @@ async function openTaskDialog() {
|
|
|
6374
6461
|
</div>`;
|
|
6375
6462
|
const defaultTaskType = ANALYSIS_TYPES[0].value;
|
|
6376
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>`;
|
|
6377
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>`;
|
|
6378
6470
|
const relationshipFields = `<div class="relationship-analysis-options hidden">
|
|
6379
6471
|
${relationshipCharacterPicker}
|
|
6380
|
-
<p class="relationship-analysis-helper"><span aria-hidden="true">i</span><span
|
|
6472
|
+
<p class="relationship-analysis-helper"><span aria-hidden="true">i</span><span>留空时使用基础关系抽取;选中角色后,将汇总其跨章节证据再进行全局关系归纳。默认仅追加不存在的关系,不修改或删除已有关系。</span></p>
|
|
6381
6473
|
<div class="relationship-overwrite-card hidden">
|
|
6382
6474
|
<label class="checkbox-field"><input name="replaceExistingRelationships" type="checkbox" disabled><span>用本次结果覆盖所选角色的已有关系</span></label>
|
|
6383
|
-
<p
|
|
6475
|
+
<p>勾选后,任务成功时会先删除所有涉及所选角色的旧关系,再写入本次分析结果;不勾选则只追加新关系。</p>
|
|
6384
6476
|
</div>
|
|
6385
6477
|
<label>额外分析提示<textarea name="additionalPrompt" maxlength="10000" placeholder="例如:重点识别权力继承、师承变化或隐秘亲缘关系"></textarea><small>将同时追加到证据收集和全局关系归纳提示词,仅影响本次任务。</small></label>
|
|
6386
6478
|
</div>`;
|
|
6387
|
-
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) => {
|
|
6388
6480
|
const taskType = String(form.get("taskType"));
|
|
6481
|
+
const modelId = String(form.get("modelId"));
|
|
6389
6482
|
const scopeType = String(form.get("scopeType"));
|
|
6390
6483
|
const includeAllSettings = taskType === "relationship-analysis" && scopeType === "book-with-settings";
|
|
6484
|
+
const settingsOnly = taskType === "relationship-analysis" && scopeType === "settings";
|
|
6391
6485
|
const additionalPrompt = taskType === "relationship-analysis" ? String(form.get("additionalPrompt") ?? "").trim() : "";
|
|
6392
6486
|
const characterIds = taskType === "relationship-analysis" ? form.getAll("characterIds").map(String).filter(Boolean) : [];
|
|
6393
6487
|
const replaceExistingRelationships = characterIds.length > 0 && form.get("replaceExistingRelationships") === "on";
|
|
6394
|
-
const scope =
|
|
6488
|
+
const scope = settingsOnly
|
|
6489
|
+
? { type: "settings", ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) }
|
|
6490
|
+
: taskType === "character-identity-audit" || scopeType === "book" || includeAllSettings
|
|
6395
6491
|
? { type: "book", ...(includeAllSettings ? { includeAllSettings: true } : {}), ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) }
|
|
6396
6492
|
: { type: "chapter", chapterId: form.get("chapterId"), ...(additionalPrompt ? { additionalPrompt } : {}), ...(characterIds.length ? { characterIds } : {}), ...(replaceExistingRelationships ? { replaceExistingRelationships: true } : {}) };
|
|
6397
|
-
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 } });
|
|
6398
6494
|
taskListPage = 1;
|
|
6399
6495
|
toast("分析任务已创建,已进入任务队列");
|
|
6400
6496
|
void renderTasks(1).catch((error) => toast(`任务已创建,但列表刷新失败:${error.message}`, "error"));
|
|
@@ -6405,6 +6501,7 @@ async function openTaskDialog() {
|
|
|
6405
6501
|
errorPrefix: "任务创建失败:"
|
|
6406
6502
|
});
|
|
6407
6503
|
const taskTypeSelect = $("#dialog-fields").querySelector('select[name="taskType"]');
|
|
6504
|
+
const taskModelSelect = $("#dialog-fields").querySelector('select[name="modelId"]');
|
|
6408
6505
|
const scopeTypeSelect = $("#dialog-fields").querySelector('select[name="scopeType"]');
|
|
6409
6506
|
const chapterSelect = $("#dialog-fields").querySelector('select[name="chapterId"]');
|
|
6410
6507
|
const chapterFieldElement = chapterSelect.closest(".task-chapter-field");
|
|
@@ -6424,7 +6521,10 @@ async function openTaskDialog() {
|
|
|
6424
6521
|
const relationshipOverwriteCard = relationshipOptions.querySelector(".relationship-overwrite-card");
|
|
6425
6522
|
const allSettingsOption = document.createElement("option");
|
|
6426
6523
|
allSettingsOption.value = "book-with-settings";
|
|
6427
|
-
allSettingsOption.textContent = "全书 +
|
|
6524
|
+
allSettingsOption.textContent = "全书 + 设定集";
|
|
6525
|
+
const settingsOnlyOption = document.createElement("option");
|
|
6526
|
+
settingsOnlyOption.value = "settings";
|
|
6527
|
+
settingsOnlyOption.textContent = "仅设定集";
|
|
6428
6528
|
const syncChapterField = () => {
|
|
6429
6529
|
const disabled = scopeTypeSelect.value !== "chapter";
|
|
6430
6530
|
chapterSelect.disabled = disabled;
|
|
@@ -6460,10 +6560,15 @@ async function openTaskDialog() {
|
|
|
6460
6560
|
const syncRelationshipOptions = () => {
|
|
6461
6561
|
const enabled = taskTypeSelect.value === "relationship-analysis";
|
|
6462
6562
|
if (enabled && !allSettingsOption.isConnected) scopeTypeSelect.append(allSettingsOption);
|
|
6563
|
+
if (enabled && !settingsOnlyOption.isConnected) scopeTypeSelect.append(settingsOnlyOption);
|
|
6463
6564
|
if (!enabled && allSettingsOption.isConnected) {
|
|
6464
6565
|
if (scopeTypeSelect.value === allSettingsOption.value) scopeTypeSelect.value = "book";
|
|
6465
6566
|
allSettingsOption.remove();
|
|
6466
6567
|
}
|
|
6568
|
+
if (!enabled && settingsOnlyOption.isConnected) {
|
|
6569
|
+
if (scopeTypeSelect.value === settingsOnlyOption.value) scopeTypeSelect.value = "book";
|
|
6570
|
+
settingsOnlyOption.remove();
|
|
6571
|
+
}
|
|
6467
6572
|
relationshipOptions.classList.toggle("hidden", !enabled);
|
|
6468
6573
|
relationshipPrompt.disabled = !enabled;
|
|
6469
6574
|
relationshipCharacterTrigger.disabled = !enabled;
|
|
@@ -6478,8 +6583,13 @@ async function openTaskDialog() {
|
|
|
6478
6583
|
filterRelationshipCharacters();
|
|
6479
6584
|
syncChapterField();
|
|
6480
6585
|
};
|
|
6586
|
+
const syncTaskModelDefault = () => {
|
|
6587
|
+
const defaultId = defaultModelByTask.get(analysisTaskModelPurpose(taskTypeSelect.value)) ?? "";
|
|
6588
|
+
taskModelSelect.value = availableTaskModels.some((model) => model.id === defaultId) ? defaultId : "";
|
|
6589
|
+
};
|
|
6481
6590
|
taskTypeSelect.addEventListener("change", () => {
|
|
6482
6591
|
description.textContent = analysisTypeDescription(taskTypeSelect.value);
|
|
6592
|
+
syncTaskModelDefault();
|
|
6483
6593
|
syncRelationshipOptions();
|
|
6484
6594
|
});
|
|
6485
6595
|
relationshipCharacterTrigger.addEventListener("click", () => {
|
|
@@ -7483,6 +7593,7 @@ $("#register-form").addEventListener("submit", async (event) => {
|
|
|
7483
7593
|
});
|
|
7484
7594
|
$("#settings-return").addEventListener("click", () => returnFromSettings().catch((error) => toast(error.message, "error")));
|
|
7485
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")));
|
|
7486
7597
|
$("#user-management-button").addEventListener("click", openUsersDialog);
|
|
7487
7598
|
$("#platform-ui-settings-button").addEventListener("click", openPlatformUiSettingsDialog);
|
|
7488
7599
|
$("#collaboration-button").addEventListener("click", () => openMembersDialog());
|
|
@@ -7492,7 +7603,9 @@ $("#presence-button").addEventListener("click", () => {
|
|
|
7492
7603
|
$("#presence-button").setAttribute("aria-expanded", String(open));
|
|
7493
7604
|
});
|
|
7494
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")));
|
|
7495
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")));
|
|
7496
7609
|
$("#platform-ui-settings-cancel").addEventListener("click", () => $("#platform-ui-settings-dialog").close());
|
|
7497
7610
|
$("#platform-ui-settings-form").addEventListener("submit", async (event) => {
|
|
7498
7611
|
event.preventDefault();
|
|
@@ -7522,6 +7635,7 @@ $("#platform-ui-settings-form").addEventListener("submit", async (event) => {
|
|
|
7522
7635
|
}
|
|
7523
7636
|
});
|
|
7524
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")));
|
|
7525
7639
|
$("#members-dialog").addEventListener("close", () => {
|
|
7526
7640
|
memberDialogWork = null;
|
|
7527
7641
|
memberDialogMembers = [];
|
|
@@ -7591,6 +7705,7 @@ function cleanupExpandedRelationshipMap() {
|
|
|
7591
7705
|
$("#relationship-map-close").addEventListener("click", () => $("#relationship-map-dialog").close());
|
|
7592
7706
|
$("#relationship-map-dialog").addEventListener("close", cleanupExpandedRelationshipMap);
|
|
7593
7707
|
$("#appearance-button").addEventListener("click", openAppearanceDialog);
|
|
7708
|
+
$("#appearance-settings-return").addEventListener("click", () => returnToSettingsHub("#appearance-button", "#appearance-dialog").catch((error) => toast(error.message, "error")));
|
|
7594
7709
|
$("#toggle-whitespace-appearance").addEventListener("click", toggleChapterWhitespaceVisibility);
|
|
7595
7710
|
$("#theme-toggle").addEventListener("click", () => {
|
|
7596
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": "关系冲突",
|
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-relationship-pinyin-v10"></script>
|
|
749
755
|
</body>
|
|
750
756
|
</html>
|