@musnows/scriverse 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +2 -2
- package/dist/app.js.map +1 -1
- package/dist/database.js +84 -2
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +75 -5
- package/dist/public/index.html +3 -2
- package/dist/public/styles.css +12 -8
- package/dist/store.js +18 -10
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +12 -10
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/public/app.js
CHANGED
|
@@ -1052,9 +1052,14 @@ function renderPresence() {
|
|
|
1052
1052
|
return;
|
|
1053
1053
|
}
|
|
1054
1054
|
const groups = groupedPresenceParticipants();
|
|
1055
|
+
const showControl = groups.length > 1;
|
|
1055
1056
|
const localKey = presencePageKey(presencePageForRoute());
|
|
1056
1057
|
syncChapterAutoSaveWithPresence();
|
|
1057
|
-
control.classList.
|
|
1058
|
+
control.classList.toggle("hidden", !showControl);
|
|
1059
|
+
if (!showControl) {
|
|
1060
|
+
$("#presence-panel").classList.add("hidden");
|
|
1061
|
+
$("#presence-button").setAttribute("aria-expanded", "false");
|
|
1062
|
+
}
|
|
1058
1063
|
$("#presence-count").textContent = `${groups.length} 人`;
|
|
1059
1064
|
$("#presence-list").innerHTML = groups.map((participant) => {
|
|
1060
1065
|
const isCurrent = participant.userId === state.user?.userId;
|
|
@@ -4735,6 +4740,7 @@ function uploadWithProgress(path, options = {}, onProgress = () => {}) {
|
|
|
4735
4740
|
request.upload.addEventListener("progress", (event) => {
|
|
4736
4741
|
onProgress(event.lengthComputable && event.total > 0 ? (event.loaded / event.total) * 100 : null);
|
|
4737
4742
|
});
|
|
4743
|
+
request.upload.addEventListener("load", () => onProgress(100));
|
|
4738
4744
|
request.addEventListener("error", () => {
|
|
4739
4745
|
updateSystemHealth({ status: "offline" });
|
|
4740
4746
|
reject(new Error("网络连接失败"));
|
|
@@ -5395,6 +5401,56 @@ function persistentToast(message, type = "info") {
|
|
|
5395
5401
|
return () => dismissToastElement(element);
|
|
5396
5402
|
}
|
|
5397
5403
|
|
|
5404
|
+
function createImportProgressToast(fileName) {
|
|
5405
|
+
const region = $("#import-progress-region");
|
|
5406
|
+
const element = document.createElement("div");
|
|
5407
|
+
element.className = "toast import-progress-toast";
|
|
5408
|
+
element.setAttribute("role", "status");
|
|
5409
|
+
element.setAttribute("aria-atomic", "true");
|
|
5410
|
+
const copy = document.createElement("div");
|
|
5411
|
+
copy.className = "import-progress-copy";
|
|
5412
|
+
const title = document.createElement("strong");
|
|
5413
|
+
title.textContent = `正在导入“${fileName}”`;
|
|
5414
|
+
const status = document.createElement("span");
|
|
5415
|
+
status.className = "import-progress-status";
|
|
5416
|
+
status.textContent = "正在上传 · 0%";
|
|
5417
|
+
const progress = document.createElement("progress");
|
|
5418
|
+
progress.max = 100;
|
|
5419
|
+
progress.value = 0;
|
|
5420
|
+
progress.setAttribute("aria-label", "书籍导入上传进度");
|
|
5421
|
+
copy.append(title, status);
|
|
5422
|
+
element.append(copy, progress);
|
|
5423
|
+
region.append(element);
|
|
5424
|
+
if (typeof region.showPopover === "function" && !region.matches(":popover-open")) region.showPopover();
|
|
5425
|
+
let closed = false;
|
|
5426
|
+
return {
|
|
5427
|
+
update(uploadProgress) {
|
|
5428
|
+
if (closed) return;
|
|
5429
|
+
if (Number.isFinite(uploadProgress) && uploadProgress >= 100) {
|
|
5430
|
+
progress.removeAttribute("value");
|
|
5431
|
+
status.textContent = "上传完成,正在解析并写入作品…";
|
|
5432
|
+
return;
|
|
5433
|
+
}
|
|
5434
|
+
if (Number.isFinite(uploadProgress)) {
|
|
5435
|
+
const percentage = Math.max(0, Math.min(99, Math.round(uploadProgress)));
|
|
5436
|
+
progress.value = percentage;
|
|
5437
|
+
status.textContent = `正在上传 · ${percentage}%`;
|
|
5438
|
+
} else {
|
|
5439
|
+
progress.removeAttribute("value");
|
|
5440
|
+
status.textContent = "正在上传…";
|
|
5441
|
+
}
|
|
5442
|
+
},
|
|
5443
|
+
close() {
|
|
5444
|
+
if (closed) return;
|
|
5445
|
+
closed = true;
|
|
5446
|
+
element.remove();
|
|
5447
|
+
if (!region.childElementCount && typeof region.hidePopover === "function" && region.matches(":popover-open")) {
|
|
5448
|
+
region.hidePopover();
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
};
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5398
5454
|
function restoreToastFocus(previousFocus) {
|
|
5399
5455
|
if (
|
|
5400
5456
|
previousFocus instanceof HTMLElement
|
|
@@ -18853,15 +18909,22 @@ $("#import-file").addEventListener("change", async (event) => {
|
|
|
18853
18909
|
body.append("file", file);
|
|
18854
18910
|
body.append("mode", mode);
|
|
18855
18911
|
body.append("expectedVersionNo", String(state.work.versionNo));
|
|
18912
|
+
const importProgress = createImportProgressToast(file.name);
|
|
18856
18913
|
try {
|
|
18857
|
-
const result = await
|
|
18914
|
+
const result = await uploadWithProgress(
|
|
18915
|
+
`/api/works/${state.work.id}/import`,
|
|
18916
|
+
{ method: "POST", body },
|
|
18917
|
+
(progress) => importProgress.update(progress)
|
|
18918
|
+
);
|
|
18858
18919
|
setSaveState(mode === "append" ? "已追加" : "已覆盖");
|
|
18859
18920
|
state.work = result.tree;
|
|
18860
18921
|
renderTree();
|
|
18861
18922
|
const completion = mode === "append" ? "正文追加完成" : "正文覆盖完成";
|
|
18862
|
-
toast(result.warnings.length ? `${completion}:${result.warnings.join(";")}` : completion);
|
|
18863
18923
|
if (result.firstImportedChapterId) await selectChapter(result.firstImportedChapterId);
|
|
18924
|
+
importProgress.close();
|
|
18925
|
+
toast(result.warnings.length ? `${completion}:${result.warnings.join(";")}` : completion);
|
|
18864
18926
|
} catch (error) {
|
|
18927
|
+
importProgress.close();
|
|
18865
18928
|
toast(error.message, "error");
|
|
18866
18929
|
if (state.dirty) scheduleChapterAutoSave();
|
|
18867
18930
|
}
|
|
@@ -18876,11 +18939,18 @@ $("#new-import-file").addEventListener("change", async (event) => {
|
|
|
18876
18939
|
body.append("title", metadata.title ?? "");
|
|
18877
18940
|
body.append("author", metadata.author ?? "");
|
|
18878
18941
|
body.append("description", metadata.description ?? "");
|
|
18942
|
+
const importProgress = createImportProgressToast(file.name);
|
|
18879
18943
|
try {
|
|
18880
|
-
const result = await
|
|
18881
|
-
|
|
18944
|
+
const result = await uploadWithProgress(
|
|
18945
|
+
"/api/works/import",
|
|
18946
|
+
{ method: "POST", body },
|
|
18947
|
+
(progress) => importProgress.update(progress)
|
|
18948
|
+
);
|
|
18882
18949
|
await loadWorks(result.work.id);
|
|
18950
|
+
importProgress.close();
|
|
18951
|
+
toast(result.warnings.length ? `作品已导入:${result.warnings.join(";")}` : "作品已导入");
|
|
18883
18952
|
} catch (error) {
|
|
18953
|
+
importProgress.close();
|
|
18884
18954
|
toast(error.message, "error");
|
|
18885
18955
|
} finally {
|
|
18886
18956
|
state.pendingImportMeta = null;
|
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=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1&feature=character-card-header-alignment-
|
|
13
|
+
<link rel="stylesheet" href="/styles.css?v=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1&feature=character-card-header-alignment-v6&feature=character-card-title-fit-v2&feature=book-import-progress-v1">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -476,6 +476,7 @@
|
|
|
476
476
|
</section>
|
|
477
477
|
|
|
478
478
|
<div id="toast-region" class="toast-region" data-position="bottom-right" aria-live="polite" popover="manual"></div>
|
|
479
|
+
<div id="import-progress-region" class="toast-region import-progress-region" data-position="bottom-right" aria-live="polite" popover="manual"></div>
|
|
479
480
|
<div id="chapter-type-menu" class="chapter-type-menu hidden" role="menu" aria-label="章节操作" data-testid="chapter-type-menu">
|
|
480
481
|
<strong>标记章节</strong>
|
|
481
482
|
<div>
|
|
@@ -1261,6 +1262,6 @@
|
|
|
1261
1262
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1262
1263
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1263
1264
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1264
|
-
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-roleplay-message-reference-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v1&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=entity-pin-icon-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1"></script>
|
|
1265
|
+
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-roleplay-message-reference-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v1&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=entity-pin-icon-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1&feature=book-import-progress-v1&feature=presence-multiple-users-v1"></script>
|
|
1265
1266
|
</body>
|
|
1266
1267
|
</html>
|
package/dist/public/styles.css
CHANGED
|
@@ -1589,22 +1589,19 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1589
1589
|
.module-row-span { grid-column: 1 / -1; }
|
|
1590
1590
|
.setting-row .card-actions, .module-row .card-actions { margin-top: 0; flex-wrap: wrap; justify-content: flex-end; }
|
|
1591
1591
|
.module-row .card-actions > .record-card-edit { position: static; }
|
|
1592
|
-
.character-card { cursor: pointer;
|
|
1592
|
+
.character-card { cursor: pointer; }
|
|
1593
1593
|
.character-row { grid-template-columns: minmax(140px, .28fr) minmax(0, 1fr) auto; }
|
|
1594
1594
|
.character-card-heading { display: flex; min-width: 0; align-items: center; gap: 7px; }
|
|
1595
1595
|
.character-card-heading .character-avatar { width: 32px; height: 32px; font-size: 12px; }
|
|
1596
1596
|
.character-card-heading h3 { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; }
|
|
1597
|
-
.record-card.has-card-edit .character-card-heading { padding-right: 70px; }
|
|
1597
|
+
.record-card.has-card-edit .character-card-heading { padding-right: 70px; transform: translateY(-3px); }
|
|
1598
1598
|
.record-card.has-card-edit.has-pin-control .character-card-heading { padding-right: 106px; }
|
|
1599
1599
|
.record-card.has-card-edit .character-card-heading h3 { padding-right: 0; }
|
|
1600
|
-
.character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top:
|
|
1600
|
+
.character-card.has-card-edit > .character-pin-button, .character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top: 18px; }
|
|
1601
1601
|
.module-row .character-favorite-button { position: static; }
|
|
1602
1602
|
.module-row .character-pin-button { position: static; }
|
|
1603
1603
|
.module-row .record-favorite-button { position: static; }
|
|
1604
1604
|
.module-row .record-pin-button { position: static; }
|
|
1605
|
-
@container character-card (max-width: 320px) {
|
|
1606
|
-
.record-card.has-card-edit.has-pin-control .character-card-heading { padding-top: 42px; padding-right: 0; }
|
|
1607
|
-
}
|
|
1608
1605
|
@media (max-width: 560px) {
|
|
1609
1606
|
.character-card-heading h3 { font-size: 15px; }
|
|
1610
1607
|
}
|
|
@@ -3042,8 +3039,15 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3042
3039
|
animation: toast-appear-top .18s ease;
|
|
3043
3040
|
pointer-events: auto;
|
|
3044
3041
|
}
|
|
3045
|
-
.toast:not(.toast-confirmation):not(.chapter-insight-toast) { cursor: pointer; }
|
|
3042
|
+
.toast:not(.toast-confirmation):not(.chapter-insight-toast):not(.import-progress-toast) { cursor: pointer; }
|
|
3046
3043
|
.toast.ai-new-conversation-toast { white-space: pre-line; }
|
|
3044
|
+
.import-progress-region,
|
|
3045
|
+
.import-progress-region:popover-open { top: auto; right: 24px; bottom: 24px; left: auto; }
|
|
3046
|
+
.import-progress-toast { display: grid; gap: 8px; width: min(360px, calc(100vw - 48px)); cursor: progress; }
|
|
3047
|
+
.import-progress-copy { display: grid; gap: 3px; }
|
|
3048
|
+
.import-progress-copy strong { font-size: 12px; }
|
|
3049
|
+
.import-progress-status { color: color-mix(in srgb, var(--toast-fg) 78%, transparent); font-size: 10px; }
|
|
3050
|
+
.import-progress-toast progress { width: 100%; height: 6px; accent-color: var(--accent); }
|
|
3047
3051
|
.toast-region[data-position="bottom-right"] .toast { animation-name: toast-appear-bottom; }
|
|
3048
3052
|
.toast.error {
|
|
3049
3053
|
border-color: color-mix(in srgb, var(--toast-error-fg) 16%, transparent);
|
|
@@ -4099,7 +4103,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
4099
4103
|
.module-content { padding: 18px 0 56px; }
|
|
4100
4104
|
.card-grid, .provider-card-grid { grid-template-columns: minmax(0, 1fr); }
|
|
4101
4105
|
.record-card { padding: 15px; }
|
|
4102
|
-
.character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top:
|
|
4106
|
+
.character-card.has-card-edit > .character-pin-button, .character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top: 8px; }
|
|
4103
4107
|
.card-actions, .task-row-actions, .module-header-actions { gap: 8px; }
|
|
4104
4108
|
.card-actions button, .task-row-actions button { flex: 1 1 auto; min-height: 38px; }
|
|
4105
4109
|
.setting-row, .module-row { padding: 13px; }
|
package/dist/store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CHARACTER_GENDERS, DRAFT_SETTING_MODULES } from "./domain.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { ENTITY_VERSION_BASELINE_MIGRATION_VERSION, PLATFORM_AI_WORK_ID } from "./database.js";
|
|
3
|
+
import { ENTITY_VERSION_BASELINE_MIGRATION_VERSION, PLATFORM_AI_WORK_ID, SYSTEM_USER_ID } from "./database.js";
|
|
4
4
|
import { exportWorkDocx } from "./docx-export.js";
|
|
5
5
|
import { createEpubArchive } from "./epub-export.js";
|
|
6
6
|
import { AppError, notFound } from "./errors.js";
|
|
@@ -800,11 +800,11 @@ export class Store {
|
|
|
800
800
|
this.getWork(workId);
|
|
801
801
|
if (type === "work") {
|
|
802
802
|
return this.db.transaction(() => {
|
|
803
|
-
const ownerUserId = typeof snapshot.ownerUserId === "string" ? snapshot.ownerUserId : null;
|
|
803
|
+
const ownerUserId = this.resolveWorkOwnerUserId(typeof snapshot.ownerUserId === "string" ? snapshot.ownerUserId : null, true);
|
|
804
804
|
const timestamp = now();
|
|
805
805
|
this.db.run(`INSERT INTO works (id, title, author, description, language, cover_url, tags_json, version_no, created_at, updated_at, owner_user_id)
|
|
806
806
|
VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`, entityId, String(snapshot.title ?? "未命名作品"), String(snapshot.author ?? ""), String(snapshot.description ?? ""), String(snapshot.language ?? "zh-CN"), snapshot.coverUrl ?? null, JSON.stringify(Array.isArray(snapshot.tags) ? snapshot.tags : []), timestamp, timestamp, ownerUserId);
|
|
807
|
-
if (ownerUserId) {
|
|
807
|
+
if (ownerUserId !== SYSTEM_USER_ID) {
|
|
808
808
|
this.db.run("INSERT INTO work_memberships (work_id, user_id, role, invited_by_user_id, created_at) VALUES (?, ?, 'owner', ?, ?)", entityId, ownerUserId, ownerUserId, timestamp);
|
|
809
809
|
}
|
|
810
810
|
const versionNo = this.recordEntityVersion("work", entityId, "restore", sourceRef, changeNote, timestamp);
|
|
@@ -863,21 +863,29 @@ export class Store {
|
|
|
863
863
|
}
|
|
864
864
|
}
|
|
865
865
|
}
|
|
866
|
-
createWork(input) {
|
|
866
|
+
createWork(input, ownerUserId = null) {
|
|
867
867
|
const workId = id("work");
|
|
868
868
|
const timestamp = now();
|
|
869
|
-
const
|
|
869
|
+
const resolvedOwnerUserId = this.resolveWorkOwnerUserId(ownerUserId);
|
|
870
870
|
this.db.transaction(() => {
|
|
871
871
|
this.db.run(`INSERT INTO works (id, title, author, description, language, cover_url, tags_json, created_at, updated_at, owner_user_id)
|
|
872
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, workId, input.title, input.author ?? "", input.description ?? "", input.language ?? "zh-CN", input.coverUrl ?? null, JSON.stringify(input.tags ?? []), timestamp, timestamp,
|
|
873
|
-
if (
|
|
874
|
-
this.db.run("INSERT INTO work_memberships (work_id, user_id, role, invited_by_user_id, created_at) VALUES (?, ?, 'owner', ?, ?)", workId,
|
|
872
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, workId, input.title, input.author ?? "", input.description ?? "", input.language ?? "zh-CN", input.coverUrl ?? null, JSON.stringify(input.tags ?? []), timestamp, timestamp, resolvedOwnerUserId);
|
|
873
|
+
if (resolvedOwnerUserId !== SYSTEM_USER_ID) {
|
|
874
|
+
this.db.run("INSERT INTO work_memberships (work_id, user_id, role, invited_by_user_id, created_at) VALUES (?, ?, 'owner', ?, ?)", workId, resolvedOwnerUserId, resolvedOwnerUserId, timestamp);
|
|
875
875
|
}
|
|
876
876
|
this.recordEntityVersion("work", workId, "create", null, "建立作品", timestamp);
|
|
877
877
|
this.audit(workId, "work.created", "work", workId);
|
|
878
878
|
});
|
|
879
879
|
return this.getWork(workId);
|
|
880
880
|
}
|
|
881
|
+
resolveWorkOwnerUserId(ownerUserId = null, allowUnknownFallback = false) {
|
|
882
|
+
const candidate = ownerUserId ?? currentRequestActor()?.userId ?? null;
|
|
883
|
+
if (candidate && this.db.get("SELECT 1 AS present FROM users WHERE id = ?", candidate))
|
|
884
|
+
return candidate;
|
|
885
|
+
if (candidate && !allowUnknownFallback)
|
|
886
|
+
throw new AppError(400, "WORK_OWNER_INVALID", "作品 Owner 用户不存在");
|
|
887
|
+
return SYSTEM_USER_ID;
|
|
888
|
+
}
|
|
881
889
|
listWorks() {
|
|
882
890
|
const actor = currentRequestActor();
|
|
883
891
|
if (!actor) {
|
|
@@ -1702,9 +1710,9 @@ export class Store {
|
|
|
1702
1710
|
this.db.transaction(() => { result = this.importNovelInTransaction(workId, fileName, fileType, parsed, mode, expectedVersionNo); });
|
|
1703
1711
|
return { ...result, tree: this.getWorkDirectory(workId) };
|
|
1704
1712
|
}
|
|
1705
|
-
createImportedWork(input, fileName, fileType, parsed) {
|
|
1713
|
+
createImportedWork(input, fileName, fileType, parsed, ownerUserId = null) {
|
|
1706
1714
|
return this.db.transaction(() => {
|
|
1707
|
-
const work = this.createWork(input);
|
|
1715
|
+
const work = this.createWork(input, ownerUserId);
|
|
1708
1716
|
const imported = this.importNovelInTransaction(String(work.id), fileName, fileType, parsed, undefined, undefined, false);
|
|
1709
1717
|
return { ...imported, work: this.getWork(String(work.id)) };
|
|
1710
1718
|
});
|