@musnows/scriverse 0.8.4 → 0.8.6
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/README.md +1 -0
- package/dist/ai-model-pricing.js +295 -0
- package/dist/ai-model-pricing.js.map +1 -0
- package/dist/ai-protocol.js +335 -15
- package/dist/ai-protocol.js.map +1 -1
- package/dist/ai-retry.js +1 -1
- package/dist/ai-retry.js.map +1 -1
- package/dist/ai-stream-timeout.js +9 -6
- package/dist/ai-stream-timeout.js.map +1 -1
- package/dist/ai.js +1103 -142
- package/dist/ai.js.map +1 -1
- package/dist/app.js +230 -28
- package/dist/app.js.map +1 -1
- package/dist/attachment-download.js +26 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachment-storage.js +8 -6
- package/dist/attachment-storage.js.map +1 -1
- package/dist/cli-contract.js +6 -4
- package/dist/cli-contract.js.map +1 -1
- package/dist/database.js +352 -3
- package/dist/database.js.map +1 -1
- package/dist/public/ai-image-attachments.d.ts +21 -0
- package/dist/public/ai-image-attachments.js +42 -0
- package/dist/public/ai-usage.d.ts +1 -0
- package/dist/public/ai-usage.js +11 -0
- package/dist/public/app.js +1125 -150
- package/dist/public/display-labels.d.ts +2 -1
- package/dist/public/display-labels.js +6 -6
- package/dist/public/index.html +42 -7
- package/dist/public/model-config.d.ts +3 -1
- package/dist/public/model-config.js +9 -2
- package/dist/public/styles.css +120 -6
- package/dist/s3-backup.js +21 -0
- package/dist/s3-backup.js.map +1 -1
- package/dist/security.js +2 -1
- package/dist/security.js.map +1 -1
- package/dist/server-runtime.js +10 -2
- package/dist/server-runtime.js.map +1 -1
- package/dist/store.js +562 -47
- package/dist/store.js.map +1 -1
- package/dist/upload-limits.js +10 -4
- package/dist/upload-limits.js.map +1 -1
- package/dist/user-auth.js +3 -0
- 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/dist/writing-progress-time.js +15 -0
- package/dist/writing-progress-time.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,8 @@ 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
|
|
13
|
+
export type ProviderProtocolOption = { value: string; label: string };
|
|
14
|
+
export function providerProtocolLabel(value: unknown, protocolOptions?: ReadonlyArray<ProviderProtocolOption>): string;
|
|
14
15
|
export function chapterVersionSourceLabel(value: unknown): string;
|
|
15
16
|
export function occurrenceRoleLabel(value: unknown): string;
|
|
16
17
|
export function searchResultTypeLabel(value: unknown): string;
|
|
@@ -65,12 +65,12 @@ export function providerConnectionLabel(value) {
|
|
|
65
65
|
return enumLabel({ unchecked: "未测试", success: "连接正常", failed: "连接失败" }, value, "未知连接状态");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function providerProtocolLabel(value) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
68
|
+
export function providerProtocolLabel(value, protocolOptions = []) {
|
|
69
|
+
const normalized = String(value ?? "").trim();
|
|
70
|
+
const option = Array.isArray(protocolOptions)
|
|
71
|
+
? protocolOptions.find((candidate) => candidate?.value === normalized)
|
|
72
|
+
: null;
|
|
73
|
+
return typeof option?.label === "string" ? option.label : enumLabel({}, normalized, "未知接口协议");
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export function chapterVersionSourceLabel(value) {
|
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">
|
|
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">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
<button type="button" data-module="races"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/></svg>种族</button>
|
|
135
135
|
<button class="ai-analysis-entry" type="button" data-module="tasks"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/></svg>AI 分析</button>
|
|
136
136
|
<button id="module-more-button" type="button" aria-expanded="false"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg><span class="nav-label">更多</span></button>
|
|
137
|
-
<button id="ai-assistant-entry" class="module-nav-secondary hidden ai-assistant-entry" type="button" aria-controls="ai-panel" aria-expanded="false"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
137
|
+
<button id="ai-assistant-entry" class="module-nav-secondary hidden ai-assistant-entry" type="button" aria-controls="ai-panel" aria-expanded="false"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5.25a3.25 3.25 0 0 0-5.95 1.82A3.74 3.74 0 0 0 4.7 13.5a3.25 3.25 0 0 0 4.55 4.53A3.6 3.6 0 0 0 12 19.5"></path><path d="M12 5.25a3.25 3.25 0 0 1 5.95 1.82 3.74 3.74 0 0 1 1.35 6.43 3.25 3.25 0 0 1-4.55 4.53A3.6 3.6 0 0 1 12 19.5"></path><path d="M12 5.25v14.25"></path><path d="M8 9.1c1.1.1 2 .9 2.25 1.95"></path><path d="M16 9.1c-1.1.1-2 .9-2.25 1.95"></path></svg>创作助手</button>
|
|
138
138
|
<button id="reader-open-button" class="module-nav-secondary hidden" type="button" aria-label="打开沉浸式阅读预览" title="阅读预览"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 5.5A2.5 2.5 0 0 1 5.5 3H11v16H5.5A2.5 2.5 0 0 0 3 21.5Z"/><path d="M21 5.5A2.5 2.5 0 0 0 18.5 3H13v16h5.5a2.5 2.5 0 0 1 2.5 2.5Z"/></svg>阅读预览</button>
|
|
139
139
|
<button class="module-nav-secondary hidden" type="button" data-module="comments"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4Z"/><path d="M8 9h8M8 13h5"/></svg><span class="nav-label">正文评论</span></button>
|
|
140
140
|
<button class="module-nav-secondary hidden" type="button" data-module="outlines"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg><span class="nav-label">大纲/伏笔</span></button>
|
|
@@ -187,6 +187,7 @@
|
|
|
187
187
|
<div class="settings-detail-actions">
|
|
188
188
|
<button id="platform-usage-return" class="ghost-button settings-parent-button" type="button">返回设置</button>
|
|
189
189
|
<button id="platform-usage-refresh" class="ghost-button" type="button">刷新</button>
|
|
190
|
+
<button id="platform-usage-pricing-refresh" class="ghost-button hidden" type="button">刷新模型价格</button>
|
|
190
191
|
</div>
|
|
191
192
|
</div>
|
|
192
193
|
<div id="platform-usage-content" class="module-content usage-page-content"></div>
|
|
@@ -214,6 +215,7 @@
|
|
|
214
215
|
<button id="platform-ai-button" class="settings-hub-card hidden" type="button" data-settings-action="ai"><span class="settings-card-mark">AI</span><strong>AI 管理</strong><small>供应商、模型、上下文与全局提示词</small></button>
|
|
215
216
|
<button id="platform-usage-button" class="settings-hub-card hidden" type="button"><span class="settings-card-mark">T</span><strong>Token 用量</strong><small>总消耗、缓存命中率、每日网格与作品明细</small></button>
|
|
216
217
|
<button id="user-management-button" class="settings-hub-card hidden" type="button"><span class="settings-card-mark">人</span><strong>用户管理</strong><small>管理员、普通用户与账户状态</small></button>
|
|
218
|
+
<button id="admin-ai-conversations-button" class="settings-hub-card hidden" type="button"><span class="settings-card-mark">话</span><strong>Chat 对话</strong><small>查看所有作品、所有用户的对话记录</small></button>
|
|
217
219
|
<button id="platform-ui-settings-button" class="settings-hub-card hidden" type="button"><span class="settings-card-mark">界</span><strong>界面与分页</strong><small>设置通知位置、银河图性能及各模块的单页数量</small></button>
|
|
218
220
|
<button id="s3-backup-button" class="settings-hub-card hidden" type="button"><span class="settings-card-mark">S3</span><strong>S3 备份</strong><small>将整个系统的数据库和图片同步到多个目标</small></button>
|
|
219
221
|
<button id="collaboration-button" class="settings-hub-card" type="button"><span class="settings-card-mark">协</span><strong>作品协作</strong><small>邀请注册用户共同编辑当前作品</small></button>
|
|
@@ -330,7 +332,6 @@
|
|
|
330
332
|
<div class="ai-heading-title"><span id="ai-status-dot" class="status-dot" role="status" aria-label="创作助手状态:正常" title="创作助手状态:正常"></span><strong>创作助手</strong></div>
|
|
331
333
|
<div class="ai-heading-actions">
|
|
332
334
|
<button id="ai-conversation-switcher" class="ai-conversation-switcher" type="button" aria-label="切换打开的对话" aria-controls="ai-conversation-switcher-menu" aria-expanded="false" aria-haspopup="dialog" title="切换打开的对话"><span id="ai-conversation-title">新对话</span><small id="ai-open-conversation-count">1</small><svg viewBox="0 0 12 8" aria-hidden="true" focusable="false"><path d="M1 1.5 6 6.5l5-5"></path></svg></button>
|
|
333
|
-
<button id="ai-workspace-close" class="hidden" type="button" aria-label="退出多会话工作台">退出</button>
|
|
334
335
|
<button id="ai-history-toggle" type="button" aria-label="历史记录" aria-controls="ai-history-dialog" aria-expanded="false" aria-haspopup="dialog" title="历史记录"><svg class="ai-heading-action-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"></path><path d="M3 3v5h5M12 7v5l3.5 2"></path></svg></button>
|
|
335
336
|
<button id="ai-new-conversation" type="button" aria-label="新建对话" title="新建对话">+</button>
|
|
336
337
|
</div>
|
|
@@ -384,6 +385,9 @@
|
|
|
384
385
|
</select>
|
|
385
386
|
</div>
|
|
386
387
|
<div class="prompt-composer">
|
|
388
|
+
<div id="ai-image-attachments" class="ai-image-attachments hidden" aria-label="待发送的图片附件" aria-live="polite"></div>
|
|
389
|
+
<button id="ai-attachment-button" class="ai-attachment-button hidden" type="button" aria-label="添加图片附件" title="添加图片附件"><svg class="ai-image-button-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="3.5" y="4.5" width="17" height="15" rx="2"></rect><circle cx="9" cy="10" r="1.5"></circle><path d="m5.5 17 4.4-4.4 3.3 3.1 2.2-2.2 3.1 3.5"></path></svg></button>
|
|
390
|
+
<input id="ai-attachment-input" class="ai-attachment-input" type="file" accept="image/png,image/jpeg,.jpg,.jpeg" multiple aria-label="选择图片附件">
|
|
387
391
|
<div id="ai-prompt" class="ai-prompt" contenteditable="true" role="textbox" aria-multiline="true" aria-autocomplete="list" aria-controls="ai-mention-menu" aria-haspopup="listbox" aria-expanded="false" aria-keyshortcuts="Enter" title="Enter 发送,Shift+Enter 换行" data-placeholder="告诉 AI 你想讨论或修改什么……"></div>
|
|
388
392
|
<div id="ai-mention-menu" class="ai-mention-menu hidden" role="listbox" aria-label="引用角色、设定、章节或上下文能力"></div>
|
|
389
393
|
<div class="prompt-composer-actions">
|
|
@@ -395,7 +399,8 @@
|
|
|
395
399
|
<button id="ai-model-picker" class="ai-model-picker" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="ai-model-popover" aria-label="选择实际使用模型" title="选择实际使用模型"><svg class="ai-model-picker-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5.25a3.25 3.25 0 0 0-5.95 1.82A3.74 3.74 0 0 0 4.7 13.5a3.25 3.25 0 0 0 4.55 4.53A3.6 3.6 0 0 0 12 19.5"></path><path d="M12 5.25a3.25 3.25 0 0 1 5.95 1.82 3.74 3.74 0 0 1 1.35 6.43 3.25 3.25 0 0 1-4.55 4.53A3.6 3.6 0 0 1 12 19.5"></path><path d="M12 5.25v14.25"></path><path d="M8 9.1c1.1.1 2 .9 2.25 1.95"></path><path d="M16 9.1c-1.1.1-2 .9-2.25 1.95"></path><path d="M8.3 15.3c1.15-.05 2.1-.85 2.3-1.95"></path><path d="M15.7 15.3c-1.15-.05-2.1-.85-2.3-1.95"></path></svg></button>
|
|
396
400
|
<section id="ai-model-popover" class="ai-model-popover hidden" role="dialog" aria-labelledby="ai-model-popover-title">
|
|
397
401
|
<label id="ai-model-popover-title" for="ai-model">实际使用模型</label>
|
|
398
|
-
<select id="ai-model" aria-label="实际使用模型"><option value="">使用创作助手时加载模型</option></select>
|
|
402
|
+
<select id="ai-model" class="ai-model-native-select" aria-label="实际使用模型"><option value="">使用创作助手时加载模型</option></select>
|
|
403
|
+
<div id="ai-model-options" class="ai-model-options" role="listbox" aria-labelledby="ai-model-popover-title"></div>
|
|
399
404
|
</section>
|
|
400
405
|
<button id="ai-send" class="ai-send-button" type="button" data-state="send" aria-label="发送消息" title="发送消息"><svg class="ai-send-button-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M21 3 9.5 14.5M21 3l-7 18-4.5-6.5L3 10l18-7Z"></path></svg></button>
|
|
401
406
|
</div>
|
|
@@ -499,7 +504,7 @@
|
|
|
499
504
|
</div>
|
|
500
505
|
<input id="new-import-file" class="hidden" type="file" accept=".txt,.docx">
|
|
501
506
|
<input id="cover-file" class="hidden" type="file" accept="image/png,image/jpeg,image/webp">
|
|
502
|
-
<input id="avatar-file" class="hidden" type="file" accept="image/png,image/jpeg,image/webp
|
|
507
|
+
<input id="avatar-file" class="hidden" type="file" accept="image/png,image/jpeg,image/webp">
|
|
503
508
|
|
|
504
509
|
<dialog id="background-task-dialog" class="dialog wide-dialog background-task-dialog" aria-labelledby="background-task-dialog-title">
|
|
505
510
|
<form method="dialog">
|
|
@@ -921,6 +926,28 @@
|
|
|
921
926
|
</div>
|
|
922
927
|
</dialog>
|
|
923
928
|
|
|
929
|
+
<dialog id="admin-ai-conversations-dialog" class="dialog wide-dialog admin-ai-conversations-dialog" aria-labelledby="admin-ai-conversations-title">
|
|
930
|
+
<div class="dialog-header">
|
|
931
|
+
<div><span class="eyebrow">系统管理员</span><h2 id="admin-ai-conversations-title">Chat 对话列表</h2><p class="dialog-header-meta">只读查看平台内所有作品和用户的对话。</p></div>
|
|
932
|
+
<div class="settings-dialog-header-actions">
|
|
933
|
+
<button id="admin-ai-conversations-filter-toggle" class="module-filter-toggle" type="button" aria-label="筛选 Chat 对话" aria-controls="admin-ai-conversations-filter-panel" aria-expanded="false" title="筛选 Chat 对话"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 5h16l-6.5 7.2v5.3l-3 1.5v-6.8L4 5Z"></path></svg></button>
|
|
934
|
+
<button id="admin-ai-conversations-settings-return" class="ghost-button settings-parent-button" type="button">返回设置</button>
|
|
935
|
+
<button id="admin-ai-conversations-close" class="dialog-close" aria-label="关闭 Chat 对话列表" type="button">×</button>
|
|
936
|
+
</div>
|
|
937
|
+
</div>
|
|
938
|
+
<div class="admin-ai-conversations-body">
|
|
939
|
+
<form id="admin-ai-conversations-filter-panel" class="admin-ai-conversations-filters hidden" method="get">
|
|
940
|
+
<label>关键词<input id="admin-ai-conversations-query" name="q" type="search" maxlength="200" placeholder="对话、作品或用户"></label>
|
|
941
|
+
<label>作品<select id="admin-ai-conversations-work" name="workId"><option value="">全部作品</option></select></label>
|
|
942
|
+
<label>用户<select id="admin-ai-conversations-user" name="userId"><option value="">全部用户</option></select></label>
|
|
943
|
+
<div class="admin-ai-conversations-filter-actions"><button id="admin-ai-conversations-reset" class="ghost-button" type="button">重置</button><button class="primary-button" type="submit">应用筛选</button></div>
|
|
944
|
+
</form>
|
|
945
|
+
<p id="admin-ai-conversations-summary" class="admin-ai-conversations-summary" role="status" aria-live="polite">正在读取对话……</p>
|
|
946
|
+
<div id="admin-ai-conversations-list" class="admin-ai-conversations-list" aria-live="polite"></div>
|
|
947
|
+
</div>
|
|
948
|
+
<div class="dialog-actions"><button id="admin-ai-conversations-refresh" class="ghost-button" type="button">刷新</button><button id="admin-ai-conversations-load-more" class="primary-button hidden" type="button">加载更多</button></div>
|
|
949
|
+
</dialog>
|
|
950
|
+
|
|
924
951
|
<dialog id="chapter-annotations-dialog" class="dialog wide-dialog chapter-annotations-dialog" aria-labelledby="chapter-annotations-title">
|
|
925
952
|
<div class="dialog-header"><div><span class="eyebrow">正文协作</span><h2 id="chapter-annotations-title">正文评论</h2><p id="chapter-annotations-meta" class="dialog-header-meta">在正文行上点击右键即可添加评论。</p></div><button id="chapter-annotations-close" class="dialog-close" aria-label="关闭正文评论" type="button">×</button></div>
|
|
926
953
|
<div id="chapter-annotations-list" class="chapter-annotations-list" aria-live="polite"></div>
|
|
@@ -1066,7 +1093,7 @@
|
|
|
1066
1093
|
<dialog id="avatar-crop-dialog" class="dialog avatar-crop-dialog" aria-labelledby="avatar-crop-dialog-title" aria-describedby="avatar-crop-dialog-description">
|
|
1067
1094
|
<div class="dialog-header">
|
|
1068
1095
|
<div>
|
|
1069
|
-
<span class="eyebrow">个人账户</span>
|
|
1096
|
+
<span id="avatar-crop-dialog-eyebrow" class="eyebrow">个人账户</span>
|
|
1070
1097
|
<h2 id="avatar-crop-dialog-title">裁剪头像</h2>
|
|
1071
1098
|
<p id="avatar-crop-dialog-description" class="dialog-header-meta">拖动选区或四角手柄调整范围,圆形预览即最终显示效果。</p>
|
|
1072
1099
|
</div>
|
|
@@ -1144,6 +1171,14 @@
|
|
|
1144
1171
|
</div>
|
|
1145
1172
|
</dialog>
|
|
1146
1173
|
|
|
1174
|
+
<dialog id="ai-image-preview-dialog" class="dialog ai-image-preview-dialog" aria-labelledby="ai-image-preview-title">
|
|
1175
|
+
<div class="dialog-header">
|
|
1176
|
+
<div><span id="ai-image-preview-title" class="eyebrow">图片附件</span><p id="ai-image-preview-meta" class="dialog-header-meta"></p></div>
|
|
1177
|
+
<button id="ai-image-preview-close" class="dialog-close" aria-label="关闭图片预览" type="button">×</button>
|
|
1178
|
+
</div>
|
|
1179
|
+
<div class="ai-image-preview-body"><img id="ai-image-preview-image" alt=""></div>
|
|
1180
|
+
</dialog>
|
|
1181
|
+
|
|
1147
1182
|
<dialog id="ai-history-dialog" class="dialog wide-dialog ai-history-dialog" aria-labelledby="ai-history-dialog-title">
|
|
1148
1183
|
<div class="dialog-header">
|
|
1149
1184
|
<div><span class="eyebrow">对话管理</span><h2 id="ai-history-dialog-title">历史记录</h2></div>
|
|
@@ -1210,6 +1245,6 @@
|
|
|
1210
1245
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1211
1246
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1212
1247
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1213
|
-
<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-v1&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-
|
|
1248
|
+
<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-v1&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-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=phone-client-entry-v1&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-v1&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"></script>
|
|
1214
1249
|
</body>
|
|
1215
1250
|
</html>
|
|
@@ -2,7 +2,8 @@ export const MODEL_PURPOSE_OPTIONS: ReadonlyArray<readonly [string, string]>;
|
|
|
2
2
|
export const MODEL_THINKING_EFFORT_OPTIONS: ReadonlyArray<readonly ["default" | "low" | "medium" | "high" | "xhigh" | "max", string]>;
|
|
3
3
|
export const MIN_MODEL_CONTEXT_WINDOW: number;
|
|
4
4
|
export const RECOMMENDED_MODEL_CONTEXT_WINDOW: number;
|
|
5
|
-
export
|
|
5
|
+
export type ModelProviderProtocolOption = { value: string; supportsMultimodal?: boolean };
|
|
6
|
+
export function supportsMultimodalModelProtocol(protocol: string | null | undefined, protocolOptions?: ReadonlyArray<ModelProviderProtocolOption>): boolean;
|
|
6
7
|
|
|
7
8
|
export type ModelFormValues = {
|
|
8
9
|
displayName: string;
|
|
@@ -24,3 +25,4 @@ export function modelContextWindowGuidance(value: unknown): { belowMinimum: bool
|
|
|
24
25
|
export function modelFormValues(model?: Record<string, unknown> | null): ModelFormValues;
|
|
25
26
|
export function modelPayload(values: ModelFormValues, existingPreset?: Record<string, unknown>): Record<string, unknown> & { thinkingEnabled: boolean; thinkingEffort: "default" | "low" | "medium" | "high" | "xhigh" | "max" };
|
|
26
27
|
export function modelOptionLabel(model: Record<string, unknown> | null | undefined): string;
|
|
28
|
+
export function modelThinkingEffortLabel(model: Record<string, unknown> | null | undefined): string;
|
|
@@ -23,8 +23,9 @@ const modelThinkingEfforts = new Set(MODEL_THINKING_EFFORT_OPTIONS.map(([value])
|
|
|
23
23
|
export const MIN_MODEL_CONTEXT_WINDOW = 32_768;
|
|
24
24
|
export const RECOMMENDED_MODEL_CONTEXT_WINDOW = 128_000;
|
|
25
25
|
|
|
26
|
-
export function supportsMultimodalModelProtocol(protocol) {
|
|
27
|
-
return
|
|
26
|
+
export function supportsMultimodalModelProtocol(protocol, protocolOptions = []) {
|
|
27
|
+
return Array.isArray(protocolOptions)
|
|
28
|
+
&& protocolOptions.some((option) => option?.value === protocol && option?.supportsMultimodal === true);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const purposeAliases = new Map([
|
|
@@ -99,3 +100,9 @@ export function modelOptionLabel(model) {
|
|
|
99
100
|
const modelName = String(model?.displayName ?? model?.modelId ?? "").trim();
|
|
100
101
|
return [providerName, modelName].filter(Boolean).join(" · ");
|
|
101
102
|
}
|
|
103
|
+
|
|
104
|
+
export function modelThinkingEffortLabel(model) {
|
|
105
|
+
if (model?.thinkingEnabled !== true) return "";
|
|
106
|
+
const thinkingEffort = String(model?.thinkingEffort ?? "").trim().toLowerCase();
|
|
107
|
+
return thinkingEffort && thinkingEffort !== "default" ? thinkingEffort : "auto";
|
|
108
|
+
}
|
package/dist/public/styles.css
CHANGED
|
@@ -482,6 +482,9 @@ input[type="checkbox"][hidden] { display: none; }
|
|
|
482
482
|
.user-avatar { position: relative; display: inline-grid; flex: 0 0 auto; place-items: center; width: 36px; height: 36px; overflow: hidden; border-radius: 50%; background: var(--accent); color: white; font-size: 13px; font-weight: 650; line-height: 1; }
|
|
483
483
|
.user-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
|
|
484
484
|
.user-avatar-fallback { display: grid; width: 100%; height: 100%; place-items: center; }
|
|
485
|
+
.character-avatar { position: relative; display: inline-grid; flex: 0 0 auto; place-items: center; width: 36px; height: 36px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line)); border-radius: 50%; background: color-mix(in srgb, var(--accent) 14%, var(--surface)); color: var(--accent-dark); font-size: 13px; font-weight: 650; line-height: 1; }
|
|
486
|
+
.character-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
|
|
487
|
+
.character-avatar-fallback { display: grid; width: 100%; height: 100%; place-items: center; }
|
|
485
488
|
.account-button > .account-avatar { width: 26px; height: 26px; font-size: 11px; }
|
|
486
489
|
.account-button > span:last-child { max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; }
|
|
487
490
|
.account-menu { position: absolute; z-index: 80; top: 48px; right: 84px; display: grid; width: 180px; min-width: 0; gap: 4px; padding: 12px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface); box-shadow: var(--shadow); }
|
|
@@ -676,7 +679,10 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
|
|
|
676
679
|
background: var(--surface-soft);
|
|
677
680
|
}
|
|
678
681
|
.usage-stat.is-primary { border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); background: color-mix(in srgb, var(--accent) 6%, var(--surface-soft)); }
|
|
682
|
+
.usage-stat-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
|
|
679
683
|
.usage-stat > span { color: var(--muted); font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
|
|
684
|
+
.usage-stat-label > span:first-child { min-width: 0; overflow: hidden; color: var(--muted); font-size: 10px; font-weight: 600; letter-spacing: .06em; text-overflow: ellipsis; text-transform: uppercase; white-space: nowrap; }
|
|
685
|
+
.usage-cost-bubble { flex: 0 0 auto; max-width: 100%; padding: 6px 10px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--accent) 62%, var(--line)); border-radius: 999px; background: color-mix(in srgb, var(--accent) 22%, var(--surface)); box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-dark); font: 700 10px/1.1 var(--font-latin), var(--font-cjk), monospace; letter-spacing: .02em; text-overflow: ellipsis; white-space: nowrap; }
|
|
680
686
|
.usage-stat strong { margin: 10px 0 5px; overflow: hidden; color: var(--ink); font: 600 clamp(23px, 2.8vw, 34px)/1 var(--font-latin), ui-monospace, monospace; letter-spacing: -.035em; text-overflow: ellipsis; white-space: nowrap; }
|
|
681
687
|
.usage-stat.is-primary strong { color: var(--accent-dark); }
|
|
682
688
|
.usage-stat small { overflow-wrap: anywhere; color: var(--muted); font-size: 9px; line-height: 1.45; }
|
|
@@ -806,6 +812,28 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
|
|
|
806
812
|
cursor: not-allowed;
|
|
807
813
|
opacity: .46;
|
|
808
814
|
}
|
|
815
|
+
.dialog.admin-ai-conversations-dialog { width: min(1040px, 94vw); height: min(760px, calc(100dvh - 24px)); max-height: calc(100dvh - 24px); overflow: hidden; }
|
|
816
|
+
.admin-ai-conversations-dialog { grid-template-rows: auto minmax(0, 1fr) auto; }
|
|
817
|
+
.admin-ai-conversations-dialog[open] { display: grid; }
|
|
818
|
+
.admin-ai-conversations-body { display: grid; grid-template-rows: auto auto minmax(0, 1fr); gap: 12px; min-height: 0; padding: 16px 24px 0; }
|
|
819
|
+
.admin-ai-conversations-filters { display: grid; grid-template-columns: minmax(220px, 1.4fr) repeat(2, minmax(160px, 1fr)) auto; align-items: end; gap: 10px; padding: 13px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-soft); }
|
|
820
|
+
.admin-ai-conversations-filters label { display: grid; gap: 6px; min-width: 0; color: var(--muted); font-size: 10px; font-weight: 650; }
|
|
821
|
+
.admin-ai-conversations-filters :is(input, select) { width: 100%; min-height: 36px; padding: 7px 10px; border: 1px solid var(--line); border-radius: 4px; background-color: var(--surface); color: var(--ink); font-size: 12px; }
|
|
822
|
+
.admin-ai-conversations-filter-actions { display: flex; gap: 7px; }
|
|
823
|
+
.admin-ai-conversations-filter-actions button { min-height: 36px; white-space: nowrap; }
|
|
824
|
+
.admin-ai-conversations-summary { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.5; }
|
|
825
|
+
.admin-ai-conversations-list { display: grid; align-content: start; gap: 9px; min-height: 0; overflow-y: auto; padding: 0 2px 16px 0; }
|
|
826
|
+
.admin-ai-conversation-row { display: grid; gap: 11px; min-width: 0; padding: 14px 15px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-soft); }
|
|
827
|
+
.admin-ai-conversation-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; min-width: 0; }
|
|
828
|
+
.admin-ai-conversation-heading > div { display: grid; gap: 4px; min-width: 0; }
|
|
829
|
+
.admin-ai-conversation-heading strong { overflow: hidden; color: var(--ink); font-size: 13px; font-weight: 650; line-height: 1.4; text-overflow: ellipsis; white-space: nowrap; }
|
|
830
|
+
.admin-ai-conversation-heading small, .admin-ai-conversation-heading > span { color: var(--muted); font-size: 9px; line-height: 1.5; }
|
|
831
|
+
.admin-ai-conversation-heading > span { flex: 0 0 auto; padding: 3px 7px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); white-space: nowrap; }
|
|
832
|
+
.admin-ai-conversation-meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; margin: 0; }
|
|
833
|
+
.admin-ai-conversation-meta > div { display: grid; gap: 3px; min-width: 0; padding: 7px 9px; border-radius: 5px; background: var(--surface); }
|
|
834
|
+
.admin-ai-conversation-meta dt { color: var(--muted); font-size: 8px; }
|
|
835
|
+
.admin-ai-conversation-meta dd { overflow: hidden; margin: 0; color: var(--ink); font-size: 10px; line-height: 1.45; text-overflow: ellipsis; white-space: nowrap; }
|
|
836
|
+
.admin-ai-conversation-row > p { display: -webkit-box; overflow: hidden; margin: 0; color: var(--muted); font-size: 10px; line-height: 1.6; overflow-wrap: anywhere; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
|
809
837
|
.dialog.s3-backup-dialog { width: min(900px, 94vw); }
|
|
810
838
|
.s3-backup-body { display: grid; gap: 18px; max-height: calc(88vh - 86px); overflow-y: auto; padding: 18px 24px 24px; }
|
|
811
839
|
.s3-backup-encryption { display: grid; gap: 12px; min-width: 0; margin: 0; padding: 15px 16px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-soft); }
|
|
@@ -1082,6 +1110,9 @@ body.work-viewer-mode .character-editor-actions { display: none !important; }
|
|
|
1082
1110
|
.avatar-settings-actions { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
1083
1111
|
.avatar-settings-actions button { min-height: 32px; padding: 6px 11px; font-size: 11px; }
|
|
1084
1112
|
.avatar-settings-actions button:disabled { cursor: wait; opacity: .5; }
|
|
1113
|
+
.character-avatar-settings { grid-column: 1 / -1; }
|
|
1114
|
+
.character-avatar-editor-preview { grid-row: 1 / 3; width: 76px; height: 76px; border-width: 2px; font-size: 26px; }
|
|
1115
|
+
.character-avatar-settings .avatar-settings-copy small { max-width: 520px; }
|
|
1085
1116
|
.avatar-crop-dialog { width: min(720px, 94vw); }
|
|
1086
1117
|
.avatar-crop-body { display: grid; grid-template-columns: minmax(0, 1fr) 96px; gap: 16px; align-items: start; padding: 18px 24px 8px; }
|
|
1087
1118
|
.avatar-crop-stage {
|
|
@@ -1484,6 +1515,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1484
1515
|
.character-card { cursor: pointer; }
|
|
1485
1516
|
.character-row { grid-template-columns: minmax(140px, .28fr) minmax(0, 1fr) auto; }
|
|
1486
1517
|
.character-card-heading { display: flex; min-width: 0; align-items: center; gap: 7px; }
|
|
1518
|
+
.character-card-heading .character-avatar { width: 32px; height: 32px; font-size: 12px; }
|
|
1487
1519
|
.character-card-heading h3 { min-width: 0; }
|
|
1488
1520
|
.record-card.has-card-edit .character-card-heading { padding-right: 34px; }
|
|
1489
1521
|
.record-card.has-card-edit .character-card-heading h3 { padding-right: 0; }
|
|
@@ -1598,6 +1630,11 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1598
1630
|
.platform-image-tool-field { display: grid; flex: 1 1 360px; gap: 7px; min-width: 0; color: var(--muted); font-size: 10px; }
|
|
1599
1631
|
.platform-image-tool-field select { width: 100%; height: 36px; min-height: 36px; padding: 8px 10px; font-size: 12px; }
|
|
1600
1632
|
.platform-image-tool-panel .config-save-button { flex: 0 0 auto; min-height: 36px; padding: 8px 14px; }
|
|
1633
|
+
.platform-stream-timeout-section { margin-bottom: 0; }
|
|
1634
|
+
.platform-stream-timeout-panel { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; padding: 16px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
|
|
1635
|
+
.platform-stream-timeout-field { display: grid; flex: 0 1 260px; gap: 7px; min-width: 0; color: var(--muted); font-size: 10px; }
|
|
1636
|
+
.platform-stream-timeout-field input { width: 100%; height: 36px; min-height: 36px; padding: 8px 10px; font-size: 12px; }
|
|
1637
|
+
.platform-stream-timeout-panel .config-save-button { flex: 0 0 auto; min-height: 36px; padding: 8px 14px; }
|
|
1601
1638
|
.task-auto-run-panel {
|
|
1602
1639
|
position: relative;
|
|
1603
1640
|
display: grid;
|
|
@@ -2533,19 +2570,36 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2533
2570
|
.context-compact-threshold-field { width: min(100%, 280px); }
|
|
2534
2571
|
.agent-tool-call-limit-field { width: min(100%, 280px); }
|
|
2535
2572
|
.daily-token-quota-field { width: min(100%, 144px); }
|
|
2573
|
+
.monthly-token-quota-field { width: min(100%, 144px); }
|
|
2536
2574
|
.agent-tool-call-global-multiplier-field { width: auto; min-width: 0; }
|
|
2537
2575
|
.book-summary-context-percent-field input, .context-compact-threshold-field input, .agent-tool-call-limit-field input, .daily-token-quota-field input { width: 64px; min-height: 32px; padding: 5px 8px; font-size: 13px; font-family: var(--font-latin), monospace; }
|
|
2538
2576
|
.daily-token-quota-field input { width: 144px; }
|
|
2577
|
+
.monthly-token-quota-field input { width: 144px; }
|
|
2578
|
+
.monthly-token-quota-field input { min-height: 32px; padding: 5px 8px; font-size: 13px; font-family: var(--font-latin), monospace; }
|
|
2579
|
+
.token-quota-warning, .provider-token-quota-warning { display: block; color: var(--accent-dark); font-size: 10px; font-weight: 600; line-height: 1.5; }
|
|
2580
|
+
.token-quota-warning.hidden, .provider-token-quota-warning.hidden { display: none; }
|
|
2539
2581
|
.config-inline-save { display: flex; align-items: flex-end; gap: 10px; margin: 14px 0 4px; flex-wrap: wrap; }
|
|
2540
2582
|
.config-inline-save .book-summary-context-percent-field,
|
|
2541
2583
|
.config-inline-save .context-compact-threshold-field,
|
|
2542
2584
|
.config-inline-save .agent-tool-call-limit-field,
|
|
2543
2585
|
.config-inline-save .daily-token-quota-field { display: grid; gap: 6px; width: 64px; margin: 0; color: var(--muted); font-size: 10px; }
|
|
2586
|
+
.config-inline-save .monthly-token-quota-field { display: grid; gap: 6px; width: 64px; margin: 0; color: var(--muted); font-size: 10px; }
|
|
2544
2587
|
.config-inline-save .book-summary-context-percent-field,
|
|
2545
2588
|
.config-inline-save .context-compact-threshold-field { grid-template-columns: minmax(110px, 130px) 88px; align-items: center; column-gap: 12px; width: min(100%, 230px); }
|
|
2546
2589
|
.config-inline-save .book-summary-context-percent-field input,
|
|
2547
2590
|
.config-inline-save .context-compact-threshold-field input { width: 88px; }
|
|
2548
2591
|
.config-inline-save .daily-token-quota-field { width: 144px; }
|
|
2592
|
+
.config-inline-save .monthly-token-quota-field { width: 144px; }
|
|
2593
|
+
@media (min-width: 641px) {
|
|
2594
|
+
.config-inline-save .daily-token-quota-field,
|
|
2595
|
+
.config-inline-save .monthly-token-quota-field { flex: 0 1 360px; width: min(100%, 360px); min-width: 240px; }
|
|
2596
|
+
.config-inline-save .daily-token-quota-field input,
|
|
2597
|
+
.config-inline-save .monthly-token-quota-field input { width: 100%; }
|
|
2598
|
+
.config-inline-save > .token-quota-warning { flex: 1 1 220px; width: min(100%, 260px); min-width: 200px; align-self: flex-end; margin-bottom: 8px; }
|
|
2599
|
+
}
|
|
2600
|
+
@media (max-width: 640px) {
|
|
2601
|
+
.config-inline-save > .token-quota-warning { flex: 1 1 106px; width: auto; min-width: 0; align-self: center; }
|
|
2602
|
+
}
|
|
2549
2603
|
.config-inline-save .agent-tool-call-global-multiplier-field { display: grid; gap: 6px; width: auto; margin: 0; color: var(--muted); font-size: 10px; }
|
|
2550
2604
|
.config-inline-save > .config-checkbox-field { display: inline-flex !important; align-items: center; gap: 8px; min-height: 32px; margin: 0; color: var(--muted); font-size: calc(11px * var(--ui-font-scale)); line-height: 1.4; white-space: nowrap; }
|
|
2551
2605
|
.config-inline-save > .config-checkbox-field input[type="checkbox"] { width: 18px; min-width: 18px; height: 18px; }
|
|
@@ -2580,6 +2634,10 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2580
2634
|
.platform-image-tool-field { flex: 0 0 auto; }
|
|
2581
2635
|
.platform-image-tool-field select, .platform-image-tool-panel .config-save-button { height: 40px; min-height: 40px; }
|
|
2582
2636
|
.platform-image-tool-panel .config-save-button { width: 100%; }
|
|
2637
|
+
.platform-stream-timeout-panel { align-items: stretch; flex-direction: column; }
|
|
2638
|
+
.platform-stream-timeout-field { flex: 0 0 auto; }
|
|
2639
|
+
.platform-stream-timeout-field input, .platform-stream-timeout-panel .config-save-button { height: 40px; min-height: 40px; }
|
|
2640
|
+
.platform-stream-timeout-panel .config-save-button { width: 100%; }
|
|
2583
2641
|
}
|
|
2584
2642
|
@media (max-width: 640px) {
|
|
2585
2643
|
.config-inline-save .book-summary-context-percent-field,
|
|
@@ -2608,7 +2666,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2608
2666
|
.assistant-message.is-search-target, .user-message.is-search-target { outline: 2px solid var(--accent); outline-offset: 3px; }
|
|
2609
2667
|
.assistant-message.is-error { border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line)); border-left: 3px solid var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--paper-deep)); }
|
|
2610
2668
|
.assistant-message.has-message-actions, .user-message.has-message-actions { margin-bottom: 42px; }
|
|
2611
|
-
.user-message { margin-left:
|
|
2669
|
+
.user-message { width: fit-content; max-width: 60%; margin-left: auto; background: var(--accent); color: #fff; }
|
|
2612
2670
|
.assistant-message > .message-heading, .user-message > .message-heading { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 5px; opacity: .65; font-size: 9px; }
|
|
2613
2671
|
.message-heading > span { letter-spacing: .12em; }.message-heading time { flex: 0 0 auto; font-variant-numeric: tabular-nums; letter-spacing: .03em; }
|
|
2614
2672
|
.assistant-message.is-error > .message-heading { color: var(--accent-dark); opacity: 1; }
|
|
@@ -2669,7 +2727,10 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2669
2727
|
.ai-process-list { display: grid; gap: 7px; padding: 9px; }
|
|
2670
2728
|
.ai-process-step { display: grid; min-width: 0; gap: 5px; }
|
|
2671
2729
|
.ai-process-step > small { color: var(--muted); font: 8px/1.4 var(--font-latin), monospace; }
|
|
2672
|
-
.ai-process-step-body { max-height: 220px; overflow: auto; padding: 8px 9px; border-left: 2px solid color-mix(in srgb, var(--accent) 46%, var(--line)); background: var(--paper-deep); color: var(--muted); font-size: 10px; }
|
|
2730
|
+
.ai-process-step-body { max-height: 220px; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-color: var(--muted) var(--surface-soft); scrollbar-gutter: stable; scrollbar-width: thin; -webkit-overflow-scrolling: touch; padding: 8px 9px; border-left: 2px solid color-mix(in srgb, var(--accent) 46%, var(--line)); background: var(--paper-deep); color: var(--muted); font-size: 10px; }
|
|
2731
|
+
.ai-process-step-body::-webkit-scrollbar { width: 8px; }
|
|
2732
|
+
.ai-process-step-body::-webkit-scrollbar-track { background: var(--surface-soft); }
|
|
2733
|
+
.ai-process-step-body::-webkit-scrollbar-thumb { border-radius: 4px; background: var(--muted); }
|
|
2673
2734
|
.ai-process-intermediate-step .ai-process-step-body { border-left-color: var(--muted); }
|
|
2674
2735
|
.ai-process-tool-step { padding-top: 1px; }
|
|
2675
2736
|
@keyframes ai-process-pulse { 0%, 100% { opacity: .45; transform: scale(.8); } 50% { opacity: 1; transform: scale(1); } }
|
|
@@ -2717,6 +2778,25 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2717
2778
|
.prompt-options .ai-roleplay-user-character { grid-column: 1 / -1; }
|
|
2718
2779
|
.ai-panel.is-roleplaying .ai-roleplay-character { border-color: color-mix(in srgb, var(--accent) 52%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); }
|
|
2719
2780
|
.prompt-composer { position: relative; }
|
|
2781
|
+
.ai-image-attachments { display: flex; gap: 5px; max-height: 38px; margin-bottom: 6px; overflow-x: auto; overflow-y: hidden; padding: 5px 0 0; }
|
|
2782
|
+
.ai-image-attachment { display: inline-flex; flex: 0 0 auto; align-items: center; width: auto; height: 28px; margin: 0; padding: 2px 3px 2px 4px; border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--line)); border-radius: 5px; background: var(--paper); }
|
|
2783
|
+
.ai-image-attachment-preview { display: inline-flex; align-items: center; gap: 4px; width: auto; height: 22px; min-width: 0; padding: 0 3px 0 0; border: 0; background: transparent; color: var(--ink); font: 9px/1 var(--font-cjk), sans-serif; white-space: nowrap; cursor: zoom-in; }
|
|
2784
|
+
.ai-image-attachment-preview:hover, .ai-image-attachment-preview:focus-visible { color: var(--accent-dark); outline: 2px solid color-mix(in srgb, var(--accent) 72%, transparent); outline-offset: 1px; }
|
|
2785
|
+
.ai-image-attachment-preview img { display: block; flex: 0 0 20px; width: 20px; height: 20px; object-fit: cover; border-radius: 2px; }
|
|
2786
|
+
.ai-image-attachment-label { overflow: hidden; text-overflow: ellipsis; }
|
|
2787
|
+
.ai-image-attachment-remove { display: grid; flex: 0 0 17px; place-items: center; width: 17px; height: 20px; padding: 0; border: 0; border-left: 1px solid color-mix(in srgb, var(--accent) 24%, var(--line)); background: transparent; color: var(--muted); font: 13px/1 var(--font-latin), sans-serif; }
|
|
2788
|
+
.ai-image-attachment-remove:hover, .ai-image-attachment-remove:focus-visible { color: var(--accent); outline: 0; }
|
|
2789
|
+
.ai-image-preview-dialog { width: min(900px, 94vw); max-width: calc(100vw - 24px); }
|
|
2790
|
+
.ai-image-preview-dialog .dialog-header { padding: 12px 16px 9px; }
|
|
2791
|
+
.ai-image-preview-dialog .dialog-header .eyebrow { font-size: 9px; }
|
|
2792
|
+
.ai-image-preview-dialog .dialog-header-meta { margin-top: 4px; font-size: 10px; }
|
|
2793
|
+
.ai-image-preview-body { display: grid; place-items: center; min-height: 180px; max-height: calc(88vh - 106px); padding: 16px 20px 20px; overflow: auto; background: var(--paper); }
|
|
2794
|
+
.ai-image-preview-body img { display: block; max-width: 100%; max-height: calc(88vh - 150px); width: auto; height: auto; object-fit: contain; border-radius: 3px; }
|
|
2795
|
+
.ai-attachment-input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }
|
|
2796
|
+
.ai-attachment-button { position: absolute; bottom: 8px; left: 8px; z-index: 2; display: grid; place-items: center; width: 32px; min-width: 32px; min-height: 32px; height: 32px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
2797
|
+
.ai-attachment-button:hover, .ai-attachment-button:focus-visible { border-color: color-mix(in srgb, var(--accent) 56%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); outline: 0; }
|
|
2798
|
+
.ai-attachment-button:disabled { cursor: wait; opacity: .56; }
|
|
2799
|
+
.ai-image-button-icon { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
2720
2800
|
.ai-mention-menu { position: absolute; z-index: 20; left: 0; right: 0; bottom: calc(100% + 6px); max-height: 230px; overflow-y: auto; padding: 6px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); box-shadow: 0 12px 34px rgba(49,42,32,.18); }
|
|
2721
2801
|
.ai-mention-option { display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 8px; width: 100%; padding: 7px 8px; border: 0; border-radius: 4px; background: transparent; text-align: left; }
|
|
2722
2802
|
.ai-mention-option:hover, .ai-mention-option:focus-visible, .ai-mention-option.is-active { background: var(--paper-deep); }
|
|
@@ -2754,8 +2834,18 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2754
2834
|
.ai-model-popover.hidden { display: none; }
|
|
2755
2835
|
.ai-model-popover::after { position: absolute; right: 49px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
2756
2836
|
.ai-model-popover > label { color: var(--muted); font-size: 10px; font-weight: 600; }
|
|
2757
|
-
.ai-model-
|
|
2758
|
-
.ai-model-
|
|
2837
|
+
.ai-model-native-select { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
|
|
2838
|
+
.ai-model-options { display: grid; max-height: 240px; gap: 3px; overflow-y: auto; }
|
|
2839
|
+
.ai-model-option { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; min-height: 42px; padding: 7px 8px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--ink); text-align: left; }
|
|
2840
|
+
.ai-model-option:hover, .ai-model-option:focus-visible, .ai-model-option[aria-selected="true"] { border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); outline: 0; }
|
|
2841
|
+
.ai-model-option:disabled { cursor: wait; opacity: .56; }
|
|
2842
|
+
.ai-model-option-copy { display: grid; min-width: 0; gap: 3px; }
|
|
2843
|
+
.ai-model-option-copy strong, .ai-model-option-copy small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2844
|
+
.ai-model-option-copy strong { color: var(--ink); font-size: 10px; font-weight: 600; }
|
|
2845
|
+
.ai-model-option-copy small { color: var(--muted); font: 9px/1.2 var(--font-latin), var(--font-cjk), sans-serif; }
|
|
2846
|
+
.ai-model-option-image { display: grid; flex: 0 0 20px; place-items: center; width: 20px; height: 20px; border-radius: 4px; background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent-dark); }
|
|
2847
|
+
.ai-model-option-image-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
2848
|
+
.ai-model-options-empty { margin: 0; padding: 10px 8px; color: var(--muted); font-size: 10px; text-align: center; }
|
|
2759
2849
|
.ai-context-distribution { display: grid; gap: 10px; }
|
|
2760
2850
|
.ai-context-distribution-row { display: grid; gap: 5px; }
|
|
2761
2851
|
.ai-context-distribution-label { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font: 10px/1.3 var(--font-latin), sans-serif; }
|
|
@@ -2774,6 +2864,10 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2774
2864
|
.ai-send-button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent), 0 4px 12px rgba(139,61,44,.2); }
|
|
2775
2865
|
.ai-send-button:disabled { cursor: wait; opacity: .62; }
|
|
2776
2866
|
.ai-send-button-icon { width: 17px; height: 17px; overflow: visible; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
|
|
2867
|
+
.ai-message-images { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
2868
|
+
.ai-message-image-preview { display: block; width: 68px; height: 68px; padding: 0; overflow: hidden; border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--line)); border-radius: 5px; background: var(--paper); cursor: zoom-in; }
|
|
2869
|
+
.ai-message-image-preview:hover, .ai-message-image-preview:focus-visible { outline: 2px solid color-mix(in srgb, var(--accent) 72%, transparent); outline-offset: 1px; }
|
|
2870
|
+
.ai-message-image-preview img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
2777
2871
|
.ai-send-button.is-stop .ai-send-button-icon { width: 15px; height: 15px; fill: currentColor; stroke: none; }
|
|
2778
2872
|
.ai-citations { display: grid; gap: 6px; max-height: 190px; overflow-y: auto; margin-bottom: 8px; }
|
|
2779
2873
|
.ai-citation-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; border: 1px solid var(--line); border-radius: 5px; background: var(--paper); overflow: hidden; }
|
|
@@ -2805,7 +2899,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2805
2899
|
display: grid;
|
|
2806
2900
|
gap: 8px;
|
|
2807
2901
|
z-index: 2147483647;
|
|
2808
|
-
pointer-events:
|
|
2902
|
+
pointer-events: auto;
|
|
2809
2903
|
box-shadow: none;
|
|
2810
2904
|
}
|
|
2811
2905
|
.toast-region::backdrop { display: none; }
|
|
@@ -2824,7 +2918,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2824
2918
|
font-size: 12px;
|
|
2825
2919
|
line-height: 1.55;
|
|
2826
2920
|
animation: toast-appear-top .18s ease;
|
|
2921
|
+
pointer-events: auto;
|
|
2827
2922
|
}
|
|
2923
|
+
.toast:not(.toast-confirmation):not(.chapter-insight-toast) { cursor: pointer; }
|
|
2828
2924
|
.toast.ai-new-conversation-toast { white-space: pre-line; }
|
|
2829
2925
|
.toast-region[data-position="bottom-right"] .toast { animation-name: toast-appear-bottom; }
|
|
2830
2926
|
.toast.error {
|
|
@@ -2993,6 +3089,12 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2993
3089
|
.background-task-actions #background-task-open-analysis { margin-right: auto; }
|
|
2994
3090
|
.merge-dialog-note { margin: 0; padding: 10px 12px; border-left: 2px solid var(--accent); background: var(--surface-soft); color: var(--muted); font-size: 11px; line-height: 1.6; }
|
|
2995
3091
|
.form-field { display: grid; gap: 6px; color: var(--muted); font-size: 11px; }
|
|
3092
|
+
.provider-token-quota-fields { gap: 9px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
|
|
3093
|
+
.provider-token-quota-fields > small { color: var(--muted); font-size: 9px; line-height: 1.5; }
|
|
3094
|
+
.provider-token-quota-row { display: grid; grid-template-columns: minmax(132px, .7fr) minmax(0, 1fr); align-items: end; gap: 10px; padding: 9px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface); }
|
|
3095
|
+
.provider-token-quota-toggle { color: var(--ink) !important; font-size: 10px !important; }
|
|
3096
|
+
.provider-token-quota-input { display: grid; gap: 5px; min-width: 0; }
|
|
3097
|
+
.provider-token-quota-input input:disabled { opacity: .58; }
|
|
2996
3098
|
.model-multimodal-fields { gap: 9px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
|
|
2997
3099
|
.model-multimodal-heading { color: var(--ink); font-size: 12px; font-weight: 600; }
|
|
2998
3100
|
.model-multimodal-fields .model-capability-option { display: grid !important; grid-template-columns: 16px minmax(0, 1fr); align-items: start; gap: 9px; margin: 0; padding: 10px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface); color: var(--ink) !important; transition: border-color .15s ease, background-color .15s ease; }
|
|
@@ -3565,6 +3667,17 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3565
3667
|
.member-permission-form .primary-button { justify-self: stretch; }
|
|
3566
3668
|
.access-row { grid-template-columns: minmax(0, 1fr); align-items: stretch; }
|
|
3567
3669
|
.access-row select, .access-row button { width: 100%; }
|
|
3670
|
+
.admin-ai-conversations-dialog .dialog-header { gap: 10px; }
|
|
3671
|
+
.admin-ai-conversations-dialog .dialog-header > div:first-child { min-width: 0; }
|
|
3672
|
+
.admin-ai-conversations-dialog .dialog-header-meta { display: none; }
|
|
3673
|
+
.admin-ai-conversations-dialog .settings-dialog-header-actions { flex-wrap: wrap; justify-content: flex-end; }
|
|
3674
|
+
.admin-ai-conversations-dialog .settings-parent-button { order: 3; width: 100%; }
|
|
3675
|
+
.admin-ai-conversations-body { padding: 12px 16px 0; }
|
|
3676
|
+
.admin-ai-conversations-filters { grid-template-columns: minmax(0, 1fr); align-items: stretch; }
|
|
3677
|
+
.admin-ai-conversations-filter-actions button { flex: 1 1 0; }
|
|
3678
|
+
.admin-ai-conversation-heading { align-items: stretch; flex-direction: column; gap: 7px; }
|
|
3679
|
+
.admin-ai-conversation-heading > span { align-self: flex-start; }
|
|
3680
|
+
.admin-ai-conversation-meta { grid-template-columns: minmax(0, 1fr); }
|
|
3568
3681
|
.work-access-field { align-items: stretch; flex-direction: column; }
|
|
3569
3682
|
.onboarding-popover { width: calc(100vw - 24px); }
|
|
3570
3683
|
.onboarding-popover-header { padding-inline: 15px; }
|
|
@@ -3646,6 +3759,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3646
3759
|
.account-settings-form, .password-settings-form, .api-key-settings, .api-key-result { grid-template-columns: minmax(0, 1fr); }
|
|
3647
3760
|
.avatar-settings { grid-template-columns: 64px minmax(0, 1fr); }
|
|
3648
3761
|
.profile-avatar-preview { width: 64px; height: 64px; }
|
|
3762
|
+
.character-avatar-editor-preview { width: 64px; height: 64px; }
|
|
3649
3763
|
.avatar-crop-body { grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 14px 16px 4px; }
|
|
3650
3764
|
.avatar-crop-side { grid-template-columns: auto 1fr; align-items: center; justify-items: start; gap: 10px; padding-top: 0; }
|
|
3651
3765
|
.avatar-crop-preview { width: 64px; height: 64px; }
|
|
@@ -4013,7 +4127,7 @@ body { font-size: var(--ui-font-size); }
|
|
|
4013
4127
|
.guard-card strong, .guard-card p, .message-actions button { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4014
4128
|
.ai-context-warning strong { font-size: calc(11px * var(--ai-font-scale)); }
|
|
4015
4129
|
.ai-context-warning p, .ai-context-warning button { font-size: calc(9px * var(--ai-font-scale)); }
|
|
4016
|
-
.prompt-options select, .ai-model-popover > label, .ai-model-
|
|
4130
|
+
.prompt-options select, .ai-model-popover > label, .ai-model-option-copy strong, .ai-model-options-empty, .ai-mention-option strong, .ai-mention-empty { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4017
4131
|
.ai-mention-option small { font-size: calc(8px * var(--ai-font-scale)); }
|
|
4018
4132
|
.ai-prompt { font-size: var(--ai-font-size); }
|
|
4019
4133
|
.ai-prompt-reference { font-size: calc(10px * var(--ai-font-scale)); }
|
package/dist/s3-backup.js
CHANGED
|
@@ -221,6 +221,7 @@ export class S3BackupManager {
|
|
|
221
221
|
now;
|
|
222
222
|
log;
|
|
223
223
|
encryptionConfirmationTtlMs;
|
|
224
|
+
characterAvatarStorage;
|
|
224
225
|
queuedTargetIds = new Set();
|
|
225
226
|
executionChain = Promise.resolve();
|
|
226
227
|
schedulerTimer = null;
|
|
@@ -245,6 +246,7 @@ export class S3BackupManager {
|
|
|
245
246
|
this.encryptionConfirmationTtlMs = Number.isFinite(encryptionConfirmationTtlMs) && encryptionConfirmationTtlMs > 0
|
|
246
247
|
? Math.floor(encryptionConfirmationTtlMs)
|
|
247
248
|
: 10 * 60_000;
|
|
249
|
+
this.characterAvatarStorage = options.characterAvatarStorage ?? null;
|
|
248
250
|
}
|
|
249
251
|
listTargets() {
|
|
250
252
|
return this.database.all("SELECT * FROM s3_backup_targets ORDER BY sort_order, created_at, id").map((row) => this.mapTarget(row));
|
|
@@ -751,6 +753,25 @@ export class S3BackupManager {
|
|
|
751
753
|
});
|
|
752
754
|
}
|
|
753
755
|
}
|
|
756
|
+
const characterAvatarStorage = this.characterAvatarStorage;
|
|
757
|
+
for (const row of this.database.all("SELECT storage_key, mime_type, sha256 FROM character_avatars ORDER BY storage_key")) {
|
|
758
|
+
const storageKey = requiredString(row, "storage_key");
|
|
759
|
+
const contentType = requiredString(row, "mime_type");
|
|
760
|
+
const sha256 = requiredString(row, "sha256");
|
|
761
|
+
const objectKey = `${rootPrefix}/img/character-avatars/${storageKey}`;
|
|
762
|
+
if (!sources.has(objectKey)) {
|
|
763
|
+
sources.set(objectKey, {
|
|
764
|
+
objectKey,
|
|
765
|
+
contentType,
|
|
766
|
+
sha256,
|
|
767
|
+
read: async () => {
|
|
768
|
+
if (!characterAvatarStorage)
|
|
769
|
+
throw new AppError(500, "BACKUP_CHARACTER_AVATAR_STORAGE_UNAVAILABLE", "角色头像存储不可用");
|
|
770
|
+
return characterAvatarStorage.read(storageKey);
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
754
775
|
return [...sources.values()];
|
|
755
776
|
}
|
|
756
777
|
databaseImageMetadata(row, label) {
|