@musnows/scriverse 1.0.6 → 1.0.8
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/ai-tool-results.js +14 -8
- package/dist/ai-tool-results.js.map +1 -1
- package/dist/ai.js +37 -20
- package/dist/ai.js.map +1 -1
- package/dist/app.js +5 -2
- package/dist/app.js.map +1 -1
- package/dist/public/ai-context-meter.js +45 -4
- package/dist/public/ai-render-scheduler.js +30 -0
- package/dist/public/app.js +174 -76
- package/dist/public/icon-dev.svg +10 -0
- package/dist/public/index.html +6 -3
- package/dist/public/markdown.js +22 -10
- package/dist/public/stream-markdown.js +107 -0
- package/dist/public/stream-typewriter.d.ts +1 -0
- package/dist/public/stream-typewriter.js +33 -19
- package/dist/public/styles.css +14 -15
- package/dist/store.js +7 -7
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/public/index.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<script src="/theme-init.js?v=20260827-reader-prefetch-v1"></script>
|
|
10
10
|
<link rel="icon" href="/icon.svg?v=20260712" type="image/svg+xml">
|
|
11
11
|
<link rel="manifest" href="/site.webmanifest">
|
|
12
|
-
<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=chapter-comment-filters-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=ai-stream-character-count-five-digit-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&feature=editor-toolbar-compact-v2&feature=reader-first-frame-v1&feature=auth-compact-v2&feature=editor-preview-toggle-v2&feature=setting-title-chrome-v2&feature=entity-pin-icon-center-v1&feature=chapter-center-bottom-space-v1&feature=work-editor-preferences-v1&feature=work-editor-preference-checkbox-v1&feature=ai-roleplay-memory-v2&feature=ai-roleplay-memory-v3&feature=ai-roleplay-memory-v5&feature=character-editor-header-actions-v1&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=roleplay-memory-pin-border-v1&feature=ai-write-tools-v2&feature=ai-write-card-actions-compact-v1&feature=ai-write-plan-actions-footer-v1&feature=ai-question-actions-footer-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-answer-limit-v1&feature=ai-question-batch-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v1&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=ai-settings-textarea-font-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=ai-skill-slash-menu-v1&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=im-narration-contrast-v1&feature=im-member-add-plus-v2&feature=im-button-hierarchy-v1&feature=im-icon-button-size-v1&feature=compact-sidebar-directory-v5&feature=ai-model-config-dialog-v1&feature=system-prompt-override-v3&feature=continuation-guard-failure-details-v1&feature=entity-editor-back-icon-v1&feature=toast-stack-v1">
|
|
12
|
+
<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=ai-composer-compact-controls-v1&feature=ai-context-meter-ring-only-v1&feature=ai-context-cache-hit-v1&feature=annotation-line-counts-v1&feature=chapter-comment-filters-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=ai-stream-character-count-five-digit-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&feature=editor-toolbar-compact-v2&feature=reader-first-frame-v1&feature=auth-compact-v2&feature=editor-preview-toggle-v2&feature=setting-title-chrome-v2&feature=entity-pin-icon-center-v1&feature=chapter-center-bottom-space-v1&feature=work-editor-preferences-v1&feature=work-editor-preference-checkbox-v1&feature=ai-roleplay-memory-v2&feature=ai-roleplay-memory-v3&feature=ai-roleplay-memory-v5&feature=character-editor-header-actions-v1&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=roleplay-memory-pin-border-v1&feature=ai-write-tools-v2&feature=ai-write-card-actions-compact-v1&feature=ai-write-plan-actions-footer-v1&feature=ai-question-actions-footer-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-answer-limit-v1&feature=ai-question-batch-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v1&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=ai-settings-textarea-font-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=ai-skill-slash-menu-v1&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=im-narration-contrast-v1&feature=im-member-add-plus-v2&feature=im-button-hierarchy-v1&feature=im-icon-button-size-v1&feature=compact-sidebar-directory-v5&feature=ai-model-config-dialog-v1&feature=system-prompt-override-v3&feature=continuation-guard-failure-details-v1&feature=entity-editor-back-icon-v1&feature=toast-stack-v1">
|
|
13
13
|
</head>
|
|
14
14
|
<body class="auth-pending">
|
|
15
15
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -440,7 +440,7 @@
|
|
|
440
440
|
<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>
|
|
441
441
|
<div id="ai-mention-menu" class="ai-mention-menu hidden" role="listbox" aria-label="引用角色、设定、章节或上下文能力"></div>
|
|
442
442
|
<div class="prompt-composer-actions">
|
|
443
|
-
<button id="ai-context-meter" class="ai-context-meter is-empty" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="ai-context-popover" aria-live="polite" aria-label="当前上下文用量"
|
|
443
|
+
<button id="ai-context-meter" class="ai-context-meter is-empty" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="ai-context-popover" aria-live="polite" aria-label="当前上下文用量"></button>
|
|
444
444
|
<section id="ai-context-popover" class="ai-context-popover hidden" role="dialog" aria-labelledby="ai-context-popover-title" aria-describedby="ai-context-popover-description">
|
|
445
445
|
<header class="ai-context-popover-header"><div><strong id="ai-context-popover-title">Token 分布</strong><small id="ai-context-popover-description">按当前模型上下文窗口计算</small></div><button id="ai-context-popover-close" class="ai-context-popover-close" type="button" aria-label="关闭 Token 分布">×</button></header>
|
|
446
446
|
<div id="ai-context-distribution" class="ai-context-distribution" role="list" aria-label="当前 Token 分布"></div>
|
|
@@ -522,6 +522,8 @@
|
|
|
522
522
|
<button type="button" role="menuitemradio" data-chapter-type="设定">设定</button>
|
|
523
523
|
<button type="button" role="menuitemradio" data-chapter-type="作者的话">作者的话</button>
|
|
524
524
|
<button type="button" role="menuitemradio" data-chapter-type="其他">其他</button>
|
|
525
|
+
<div id="chapter-type-ai-reference-separator" class="line-citation-menu-separator hidden" role="separator" aria-orientation="horizontal"></div>
|
|
526
|
+
<button type="button" role="menuitem" data-add-chapter-ai-reference>添加到助手引用</button>
|
|
525
527
|
<button class="danger-button" type="button" role="menuitem" data-delete-chapter>删除章节</button>
|
|
526
528
|
</div>
|
|
527
529
|
</div>
|
|
@@ -1425,6 +1427,7 @@
|
|
|
1425
1427
|
</dialog>
|
|
1426
1428
|
|
|
1427
1429
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1428
|
-
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-history-rename-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-v2&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&feature=ai-context-input-output-v1&feature=editor-blank-lines-preserved-v1&feature=reader-first-frame-v1&feature=vditor-lazy-load-v1&feature=ui-module-preload-v1&feature=reader-initial-prefetch-v1&feature=editor-preview-toggle-v2&feature=vditor-fullscreen-disabled-v1&feature=chapter-auto-indent-v1&feature=chapter-centered-scroll-v1&feature=work-editor-preferences-v1&feature=ai-context-output-usage-v1&feature=ai-roleplay-memory-v4&feature=ai-roleplay-memory-v5&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-header-toolbar-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=annotation-line-anchor-v1&feature=stable-line-ids-v1&feature=live-annotation-anchors-v1&feature=chapter-comment-filters-v1&feature=ai-write-tools-v3&feature=ai-question-option-supplement-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-continuation-ui-v3&feature=ai-background-stream-v1&feature=ai-question-tool-result-v1&feature=ai-question-tool-summary-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-batch-v1&feature=ai-question-answer-limit-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v3&feature=ai-cancel-preserve-process-v2&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=chapter-batch-editor-refresh-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=semantic-search-rag-label-v1&feature=ai-skill-slash-menu-v1&feature=ai-roleplay-scene-bubble-v1&feature=ai-roleplay-scene-collapse-v1&feature=markdown-adjacent-blockquotes-v1&feature=chapter-save-shortcut-v2&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=ai-fork-progress-toast-v1&feature=im-settings-gear-v1&feature=compact-sidebar-directory-v2&feature=chapter-word-count-consistency-v1&feature=continuation-guard-failure-details-v1&feature=ai-all-message-references-v2&feature=ai-question-render-recovery-v3&feature=ai-suggestion-editor-boundary-v1&feature=setting-category-preservation-v1&feature=toast-stack-v1&feature=toast-stack-label-v2"></script>
|
|
1430
|
+
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-history-rename-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-v2&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&feature=ai-context-input-output-v1&feature=editor-blank-lines-preserved-v1&feature=reader-first-frame-v1&feature=vditor-lazy-load-v1&feature=ui-module-preload-v1&feature=reader-initial-prefetch-v1&feature=editor-preview-toggle-v2&feature=vditor-fullscreen-disabled-v1&feature=chapter-auto-indent-v1&feature=chapter-centered-scroll-v1&feature=work-editor-preferences-v1&feature=ai-context-output-usage-v1&feature=ai-context-meter-ring-only-v1&feature=ai-context-cache-hit-v1&feature=ai-roleplay-memory-v4&feature=ai-roleplay-memory-v5&feature=roleplay-memory-header-actions-v1&feature=roleplay-memory-header-toolbar-v1&feature=roleplay-memory-action-icons-v1&feature=roleplay-memory-action-colors-v1&feature=annotation-line-anchor-v1&feature=stable-line-ids-v1&feature=live-annotation-anchors-v1&feature=chapter-comment-filters-v1&feature=ai-write-tools-v3&feature=ai-question-option-supplement-v1&feature=ai-question-selection-highlight-v1&feature=ai-question-continuation-ui-v3&feature=ai-background-stream-v1&feature=ai-question-tool-result-v1&feature=ai-question-tool-summary-v1&feature=ai-question-submit-guidance-v1&feature=ai-question-batch-v1&feature=ai-question-answer-limit-v1&feature=semantic-search-v6&feature=chapter-title-renumber-v1&feature=ai-writing-skills-v3&feature=ai-cancel-preserve-process-v2&feature=remote-mcp-v1&feature=character-alias-chips-v2&feature=character-title-input-v1&feature=character-detail-value-wrap-v2&feature=chapter-batch-editor-refresh-v1&feature=ai-usage-stat-display-v1&feature=ai-usage-year-v1&feature=semantic-search-rag-label-v1&feature=ai-skill-slash-menu-v1&feature=ai-roleplay-scene-bubble-v1&feature=ai-roleplay-scene-collapse-v1&feature=markdown-adjacent-blockquotes-v1&feature=chapter-save-shortcut-v2&feature=ai-message-citation-popover-v1&feature=line-citation-menu-separator-v1&feature=global-im-v106&feature=im-sidebar-compact-v1&feature=ai-fork-progress-toast-v1&feature=im-settings-gear-v1&feature=compact-sidebar-directory-v2&feature=chapter-word-count-consistency-v1&feature=continuation-guard-failure-details-v1&feature=ai-all-message-references-v2&feature=ai-question-render-recovery-v3&feature=ai-suggestion-editor-boundary-v1&feature=setting-category-preservation-v1&feature=toast-stack-v1&feature=toast-stack-label-v2&feature=agent-tool-limits-300-v1&feature=server-dev-logo-v1&feature=chapter-directory-ai-reference-v3&feature=ai-error-origin-v1&feature=ai-stream-render-performance-v2"></script>
|
|
1431
|
+
|
|
1429
1432
|
</body>
|
|
1430
1433
|
</html>
|
package/dist/public/markdown.js
CHANGED
|
@@ -142,8 +142,20 @@ function renderMarkdownTable(headers, alignments, rows) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export function renderMarkdown(value) {
|
|
145
|
+
return renderMarkdownParts(value).map((part) => part.html).join("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function renderMarkdownParts(value) {
|
|
145
149
|
const lines = String(value ?? "").replace(/\r\n?/gu, "\n").split("\n");
|
|
146
150
|
const output = [];
|
|
151
|
+
const offsets = [];
|
|
152
|
+
let offset = 0;
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
offsets.push(offset);
|
|
155
|
+
offset += line.length + 1;
|
|
156
|
+
}
|
|
157
|
+
let lineIndex = 0;
|
|
158
|
+
const emit = (html, endLine = lineIndex) => output.push({ html, end: offsets[endLine] ?? offset - 1 });
|
|
147
159
|
let paragraph = [];
|
|
148
160
|
let list = null;
|
|
149
161
|
let quote = null;
|
|
@@ -151,12 +163,12 @@ export function renderMarkdown(value) {
|
|
|
151
163
|
|
|
152
164
|
const flushParagraph = () => {
|
|
153
165
|
if (!paragraph.length) return;
|
|
154
|
-
|
|
166
|
+
emit(`<p>${paragraph.map(renderInlineMarkdown).join("<br>")}</p>`);
|
|
155
167
|
paragraph = [];
|
|
156
168
|
};
|
|
157
169
|
const flushList = () => {
|
|
158
170
|
if (!list) return;
|
|
159
|
-
|
|
171
|
+
emit(`<${list.tag}>${list.items.map((item) => `<li class="markdown-depth-${item.depth}">${renderInlineMarkdown(item.text)}</li>`).join("")}</${list.tag}>`);
|
|
160
172
|
list = null;
|
|
161
173
|
};
|
|
162
174
|
const flushQuote = () => {
|
|
@@ -166,7 +178,7 @@ export function renderMarkdown(value) {
|
|
|
166
178
|
const html = content.split(/\n\s*\n/gu)
|
|
167
179
|
.map((part) => part.split("\n").map(renderInlineMarkdown).join("<br>"))
|
|
168
180
|
.join("<br><br>");
|
|
169
|
-
|
|
181
|
+
emit(`<blockquote>${html}</blockquote>`);
|
|
170
182
|
}
|
|
171
183
|
quote = null;
|
|
172
184
|
};
|
|
@@ -176,11 +188,11 @@ export function renderMarkdown(value) {
|
|
|
176
188
|
flushQuote();
|
|
177
189
|
};
|
|
178
190
|
|
|
179
|
-
for (
|
|
191
|
+
for (lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
180
192
|
const line = lines[lineIndex];
|
|
181
193
|
if (codeFence) {
|
|
182
194
|
if (/^\s*```/u.test(line)) {
|
|
183
|
-
|
|
195
|
+
emit(`<pre><code${codeFence.language ? ` class="language-${codeFence.language}"` : ""}>${escapeHtml(codeFence.lines.join("\n"))}</code></pre>`, lineIndex + 1);
|
|
184
196
|
codeFence = null;
|
|
185
197
|
} else {
|
|
186
198
|
codeFence.lines.push(line);
|
|
@@ -230,12 +242,12 @@ export function renderMarkdown(value) {
|
|
|
230
242
|
if (heading) {
|
|
231
243
|
flushBlocks();
|
|
232
244
|
const level = heading[1].length;
|
|
233
|
-
|
|
245
|
+
emit(`<h${level}>${renderInlineMarkdown(heading[2])}</h${level}>`, lineIndex + 1);
|
|
234
246
|
continue;
|
|
235
247
|
}
|
|
236
248
|
if (/^\s*(---+|___+|\*\*\*+)\s*$/u.test(line)) {
|
|
237
249
|
flushBlocks();
|
|
238
|
-
|
|
250
|
+
emit("<hr>", lineIndex + 1);
|
|
239
251
|
continue;
|
|
240
252
|
}
|
|
241
253
|
const tableHeaders = splitMarkdownTableRow(line);
|
|
@@ -251,7 +263,7 @@ export function renderMarkdown(value) {
|
|
|
251
263
|
lineIndex += 1;
|
|
252
264
|
}
|
|
253
265
|
lineIndex -= 1;
|
|
254
|
-
|
|
266
|
+
emit(renderMarkdownTable(tableHeaders, alignments, rows), lineIndex + 1);
|
|
255
267
|
continue;
|
|
256
268
|
}
|
|
257
269
|
const listItem = line.match(/^(\s*)([-+*]|\d+\.)\s+(.+)$/u);
|
|
@@ -266,7 +278,7 @@ export function renderMarkdown(value) {
|
|
|
266
278
|
if (list) flushList();
|
|
267
279
|
paragraph.push(line);
|
|
268
280
|
}
|
|
269
|
-
if (codeFence)
|
|
281
|
+
if (codeFence) emit(`<pre><code${codeFence.language ? ` class="language-${codeFence.language}"` : ""}>${escapeHtml(codeFence.lines.join("\n"))}</code></pre>`);
|
|
270
282
|
flushBlocks();
|
|
271
|
-
return output
|
|
283
|
+
return output;
|
|
272
284
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { renderMarkdownParts } from "./markdown.js?v=20260912-stream-render-v2";
|
|
2
|
+
|
|
3
|
+
// 保留末尾两个块,等待列表、引用和表格的后续行确定语义后再冻结前缀。
|
|
4
|
+
export function createMarkdownStreamState() {
|
|
5
|
+
let previous = "";
|
|
6
|
+
let committed = 0;
|
|
7
|
+
return {
|
|
8
|
+
update(value) {
|
|
9
|
+
const source = String(value ?? "").replace(/\r\n?/gu, "\n");
|
|
10
|
+
const reset = !source.startsWith(previous);
|
|
11
|
+
if (reset) committed = 0;
|
|
12
|
+
const parts = renderMarkdownParts(source.slice(committed));
|
|
13
|
+
const stableCount = Math.max(0, parts.length - 2);
|
|
14
|
+
if (stableCount) committed += parts[stableCount - 1].end;
|
|
15
|
+
previous = source;
|
|
16
|
+
return { reset, parts, stableCount };
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 每个节点都是可让出主线程的边界,长列表和表格也不会作为一个大块集中挂载。
|
|
22
|
+
function* patchMarkdownNode(current, next, parent) {
|
|
23
|
+
if (!current || current.nodeType !== next.nodeType || current.nodeName !== next.nodeName) {
|
|
24
|
+
const replacement = next.cloneNode(false);
|
|
25
|
+
if (current) current.replaceWith(replacement);
|
|
26
|
+
else parent.append(replacement);
|
|
27
|
+
current = replacement;
|
|
28
|
+
}
|
|
29
|
+
if (current.nodeType === 3) {
|
|
30
|
+
if (current.data === next.data) return current;
|
|
31
|
+
if (next.data.startsWith(current.data)) current.appendData(next.data.slice(current.data.length));
|
|
32
|
+
else if (current.data !== next.data) current.data = next.data;
|
|
33
|
+
yield;
|
|
34
|
+
return current;
|
|
35
|
+
}
|
|
36
|
+
if (current.nodeType !== 1) return current;
|
|
37
|
+
for (const attribute of [...current.attributes]) {
|
|
38
|
+
if (!next.hasAttribute(attribute.name)) current.removeAttribute(attribute.name);
|
|
39
|
+
}
|
|
40
|
+
for (const attribute of next.attributes) {
|
|
41
|
+
if (current.getAttribute(attribute.name) !== attribute.value) current.setAttribute(attribute.name, attribute.value);
|
|
42
|
+
}
|
|
43
|
+
yield;
|
|
44
|
+
const children = [...current.childNodes];
|
|
45
|
+
const nextChildren = [...next.childNodes];
|
|
46
|
+
for (const [index, child] of nextChildren.entries()) yield* patchMarkdownNode(children[index], child, current);
|
|
47
|
+
for (const child of children.slice(nextChildren.length)) { child.remove(); yield; }
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createStreamingMarkdownRenderer(host, {
|
|
52
|
+
enqueue = (callback) => requestAnimationFrame(callback),
|
|
53
|
+
onRender = () => {},
|
|
54
|
+
now = () => performance.now(),
|
|
55
|
+
budgetMs = 3,
|
|
56
|
+
nodeBudget = 120
|
|
57
|
+
} = {}) {
|
|
58
|
+
const state = createMarkdownStreamState();
|
|
59
|
+
let tail = [];
|
|
60
|
+
let requestedValue = null;
|
|
61
|
+
let appliedValue = null;
|
|
62
|
+
let work = null;
|
|
63
|
+
let queued = false;
|
|
64
|
+
function* apply(value) {
|
|
65
|
+
const update = state.update(value);
|
|
66
|
+
if (update.reset) {
|
|
67
|
+
for (const child of [...host.childNodes]) { child.remove(); yield; }
|
|
68
|
+
tail = [];
|
|
69
|
+
}
|
|
70
|
+
const next = [];
|
|
71
|
+
for (const [index, part] of update.parts.entries()) {
|
|
72
|
+
const previous = tail[index];
|
|
73
|
+
if (previous?.html === part.html) { next.push(previous); continue; }
|
|
74
|
+
const template = host.ownerDocument.createElement("template");
|
|
75
|
+
template.innerHTML = part.html;
|
|
76
|
+
const node = yield* patchMarkdownNode(previous?.node, template.content.firstChild, host);
|
|
77
|
+
next.push({ html: part.html, node });
|
|
78
|
+
}
|
|
79
|
+
for (const item of tail.slice(next.length)) { item.node.remove(); yield; }
|
|
80
|
+
tail = next.slice(update.stableCount);
|
|
81
|
+
appliedValue = value;
|
|
82
|
+
}
|
|
83
|
+
const drain = () => {
|
|
84
|
+
queued = false;
|
|
85
|
+
const started = now();
|
|
86
|
+
if (!work && requestedValue !== appliedValue) work = apply(requestedValue);
|
|
87
|
+
let nodes = 0;
|
|
88
|
+
while (work && nodes < nodeBudget && now() - started < budgetMs) {
|
|
89
|
+
if (work.next().done) { work = null; break; }
|
|
90
|
+
nodes += 1;
|
|
91
|
+
}
|
|
92
|
+
onRender();
|
|
93
|
+
if (work || requestedValue !== appliedValue) schedule();
|
|
94
|
+
};
|
|
95
|
+
const schedule = () => {
|
|
96
|
+
if (queued) return;
|
|
97
|
+
queued = true;
|
|
98
|
+
enqueue(drain);
|
|
99
|
+
};
|
|
100
|
+
return (value) => {
|
|
101
|
+
const normalized = String(value ?? "");
|
|
102
|
+
if (normalized === requestedValue) return false;
|
|
103
|
+
requestedValue = normalized;
|
|
104
|
+
schedule();
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -27,6 +27,7 @@ export function createStreamTypewriter<FrameHandle = number>(options: {
|
|
|
27
27
|
scheduleFrame?: (callback: () => void) => FrameHandle;
|
|
28
28
|
cancelFrame?: (handle: FrameHandle) => void;
|
|
29
29
|
reducedMotion?: boolean;
|
|
30
|
+
shouldAnimate?: () => boolean;
|
|
30
31
|
speedController?: StreamTypewriterSpeedController | null;
|
|
31
32
|
visibilitySource?: Pick<Document, "visibilityState" | "addEventListener" | "removeEventListener"> | null;
|
|
32
33
|
}): StreamTypewriter;
|
|
@@ -75,23 +75,34 @@ export function createStreamTypewriter({
|
|
|
75
75
|
cancelFrame = (handle) => window.cancelAnimationFrame(handle),
|
|
76
76
|
reducedMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true,
|
|
77
77
|
speedController = null,
|
|
78
|
+
shouldAnimate = () => true,
|
|
78
79
|
visibilitySource = typeof document === "undefined" ? null : document
|
|
79
80
|
}) {
|
|
80
81
|
if (typeof onRender !== "function") throw new TypeError("onRender must be a function");
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
let visibleText = "";
|
|
84
|
+
let visibleCharacterCount = 0;
|
|
85
|
+
let pendingCharacters = [];
|
|
86
|
+
let pendingOffset = 0;
|
|
87
|
+
const pendingCount = () => pendingCharacters.length - pendingOffset;
|
|
88
|
+
const takePending = (count) => {
|
|
89
|
+
const characters = pendingCharacters.slice(pendingOffset, pendingOffset + count);
|
|
90
|
+
visibleText += characters.join("");
|
|
91
|
+
visibleCharacterCount += characters.length;
|
|
92
|
+
pendingOffset += characters.length;
|
|
93
|
+
if (pendingOffset === pendingCharacters.length) { pendingCharacters = []; pendingOffset = 0; }
|
|
94
|
+
};
|
|
84
95
|
const idleResolvers = [];
|
|
85
96
|
let scheduledFrame = null;
|
|
86
97
|
let finishing = false;
|
|
87
98
|
let listeningForVisibility = false;
|
|
88
99
|
|
|
89
|
-
const snapshot = () =>
|
|
100
|
+
const snapshot = () => visibleText;
|
|
90
101
|
const visibilityChanged = () => {
|
|
91
|
-
if (
|
|
102
|
+
if (pendingCount()) typewriter.reveal();
|
|
92
103
|
};
|
|
93
104
|
const resolveIdle = () => {
|
|
94
|
-
if (
|
|
105
|
+
if (pendingCount() || scheduledFrame !== null) return;
|
|
95
106
|
if (listeningForVisibility) {
|
|
96
107
|
visibilitySource.removeEventListener("visibilitychange", visibilityChanged);
|
|
97
108
|
listeningForVisibility = false;
|
|
@@ -101,32 +112,33 @@ export function createStreamTypewriter({
|
|
|
101
112
|
};
|
|
102
113
|
const render = () => {
|
|
103
114
|
onRender(snapshot(), {
|
|
104
|
-
visibleCharacters:
|
|
105
|
-
pendingCharacters:
|
|
115
|
+
visibleCharacters: visibleCharacterCount,
|
|
116
|
+
pendingCharacters: pendingCount()
|
|
106
117
|
});
|
|
107
118
|
};
|
|
108
119
|
const schedule = () => {
|
|
109
|
-
if (visibilitySource?.visibilityState === "hidden") {
|
|
120
|
+
if (visibilitySource?.visibilityState === "hidden" || !shouldAnimate()) {
|
|
110
121
|
typewriter.reveal();
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
113
|
-
if (scheduledFrame !== null ||
|
|
124
|
+
if (scheduledFrame !== null || pendingCount() === 0) return;
|
|
114
125
|
if (visibilitySource && !listeningForVisibility) {
|
|
115
126
|
visibilitySource.addEventListener("visibilitychange", visibilityChanged);
|
|
116
127
|
listeningForVisibility = true;
|
|
117
128
|
}
|
|
118
129
|
scheduledFrame = scheduleFrame(() => {
|
|
119
130
|
scheduledFrame = null;
|
|
120
|
-
const batchSize = reducedMotion
|
|
121
|
-
?
|
|
131
|
+
const batchSize = reducedMotion || !shouldAnimate()
|
|
132
|
+
? pendingCount()
|
|
122
133
|
: streamTypewriterBatchSize(
|
|
123
|
-
|
|
134
|
+
pendingCount(),
|
|
124
135
|
finishing,
|
|
125
136
|
speedController?.charactersPerSecond?.()
|
|
126
137
|
);
|
|
127
|
-
|
|
138
|
+
const catchUp = pendingCount() > 4096 ? Math.min(2048, Math.ceil(pendingCount() / 8)) : batchSize;
|
|
139
|
+
takePending(Math.max(batchSize, catchUp));
|
|
128
140
|
render();
|
|
129
|
-
if (
|
|
141
|
+
if (pendingCount()) schedule();
|
|
130
142
|
else resolveIdle();
|
|
131
143
|
});
|
|
132
144
|
};
|
|
@@ -136,7 +148,7 @@ export function createStreamTypewriter({
|
|
|
136
148
|
const characters = Array.from(String(value ?? ""));
|
|
137
149
|
if (!characters.length) return;
|
|
138
150
|
speedController?.observe?.(characters.length);
|
|
139
|
-
pendingCharacters.push(
|
|
151
|
+
for (const character of characters) pendingCharacters.push(character);
|
|
140
152
|
schedule();
|
|
141
153
|
},
|
|
142
154
|
replace(value) {
|
|
@@ -144,15 +156,17 @@ export function createStreamTypewriter({
|
|
|
144
156
|
cancelFrame(scheduledFrame);
|
|
145
157
|
scheduledFrame = null;
|
|
146
158
|
}
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
visibleText = String(value ?? "");
|
|
160
|
+
visibleCharacterCount = Array.from(visibleText).length;
|
|
161
|
+
pendingCharacters = [];
|
|
162
|
+
pendingOffset = 0;
|
|
149
163
|
finishing = false;
|
|
150
164
|
render();
|
|
151
165
|
resolveIdle();
|
|
152
166
|
return snapshot();
|
|
153
167
|
},
|
|
154
168
|
finish() {
|
|
155
|
-
if (!
|
|
169
|
+
if (!pendingCount() && scheduledFrame === null) return Promise.resolve(snapshot());
|
|
156
170
|
finishing = true;
|
|
157
171
|
const completed = new Promise((resolve) => idleResolvers.push(resolve));
|
|
158
172
|
schedule();
|
|
@@ -163,7 +177,7 @@ export function createStreamTypewriter({
|
|
|
163
177
|
cancelFrame(scheduledFrame);
|
|
164
178
|
scheduledFrame = null;
|
|
165
179
|
}
|
|
166
|
-
|
|
180
|
+
takePending(pendingCount());
|
|
167
181
|
finishing = false;
|
|
168
182
|
render();
|
|
169
183
|
resolveIdle();
|
package/dist/public/styles.css
CHANGED
|
@@ -3172,11 +3172,11 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3172
3172
|
.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); }
|
|
3173
3173
|
.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; }
|
|
3174
3174
|
.ai-attachment-input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }
|
|
3175
|
-
.ai-attachment-button, .ai-scene-button { display: grid; place-items: center; width:
|
|
3175
|
+
.ai-attachment-button, .ai-scene-button { display: grid; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
3176
3176
|
.ai-attachment-button:hover, .ai-attachment-button:focus-visible, .ai-scene-button:hover, .ai-scene-button:focus-visible, .ai-scene-button[aria-expanded="true"], .ai-scene-button.is-active { 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; }
|
|
3177
3177
|
.ai-attachment-button:disabled, .ai-scene-button:disabled { cursor: wait; opacity: .56; }
|
|
3178
3178
|
.ai-scene-button.hidden { display: none; }
|
|
3179
|
-
.ai-image-button-icon, .ai-scene-button-icon { width:
|
|
3179
|
+
.ai-image-button-icon, .ai-scene-button-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
3180
3180
|
|
|
3181
3181
|
.character-roleplay-memory-surface { display: grid; grid-column: 1 / -1; gap: 14px; min-width: 0; }
|
|
3182
3182
|
.roleplay-memory-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
|
|
@@ -3230,7 +3230,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3230
3230
|
.ai-skill-option > span { display: grid; gap: 2px; min-width: 0; }
|
|
3231
3231
|
.ai-skill-option em { color: var(--muted); font-size: 9px; font-style: normal; line-height: 1.35; overflow-wrap: anywhere; }
|
|
3232
3232
|
.ai-mention-empty { margin: 0; padding: 10px; color: var(--muted); font-size: 10px; text-align: center; }
|
|
3233
|
-
.ai-prompt { display: block; width: 100%; min-height: 104px; margin: 0; padding: 10px 10px
|
|
3233
|
+
.ai-prompt { display: block; width: 100%; min-height: 104px; margin: 0; padding: 10px 10px 36px; overflow-y: auto; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--ink); font-size: 12px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; cursor: text; }
|
|
3234
3234
|
.ai-prompt:empty::before { color: var(--muted); content: attr(data-placeholder); pointer-events: none; }
|
|
3235
3235
|
.ai-prompt:focus { border-color: #a77768; box-shadow: 0 0 0 3px rgba(167,119,104,.12); outline: 0; }
|
|
3236
3236
|
.ai-prompt-reference { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; margin: 0 2px; padding: 2px 4px 2px 7px; border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--line)); border-radius: 999px; background: color-mix(in srgb, var(--accent) 13%, var(--paper)); color: var(--accent-dark); font-size: 10px; line-height: 1.25; vertical-align: baseline; }
|
|
@@ -3238,28 +3238,28 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3238
3238
|
.ai-prompt-reference button { width: 16px; height: 16px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; font: 14px/1 var(--font-latin), monospace; }
|
|
3239
3239
|
.ai-prompt-reference button:hover, .ai-prompt-reference button:focus-visible { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
|
|
3240
3240
|
.prompt-composer-actions { position: absolute; right: 8px; bottom: 8px; display: flex; gap: 6px; align-items: center; }
|
|
3241
|
-
.ai-context-meter { --context-usage: 0; --context-meter-color: var(--green); position: relative; display: grid; flex: 0 0
|
|
3242
|
-
.ai-context-meter::before { content: ""; position: absolute; inset:
|
|
3241
|
+
.ai-context-meter { --context-usage: 0; --context-meter-color: var(--green); position: relative; display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-height: 24px; height: 24px; overflow: visible; border: 0; border-radius: 50%; background: conic-gradient(var(--context-meter-color) calc(var(--context-usage) * 1%), rgba(143,132,116,.2) 0); color: var(--ink); font-family: var(--font-latin); line-height: 1; cursor: pointer; }
|
|
3242
|
+
.ai-context-meter::before { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--paper); box-shadow: inset 0 0 0 1px rgba(143,132,116,.12); }
|
|
3243
3243
|
.ai-context-meter:hover, .ai-context-meter:focus-visible, .ai-context-meter[aria-expanded="true"] { outline: 0; filter: brightness(.96); }
|
|
3244
3244
|
.ai-context-meter:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
|
|
3245
|
-
.ai-context-meter
|
|
3245
|
+
.ai-context-meter.is-warning { --context-meter-color: #bf6a35; }.ai-context-meter.is-danger { --context-meter-color: #ad463c; }.ai-context-meter.is-empty { opacity: .62; }
|
|
3246
3246
|
.ai-context-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 40; display: grid; width: min(320px, calc(100vw - 32px)); gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
3247
3247
|
.ai-context-popover.hidden { display: none; }
|
|
3248
|
-
.ai-context-popover::after { position: absolute; right:
|
|
3248
|
+
.ai-context-popover::after { position: absolute; right: 67px; 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); }
|
|
3249
3249
|
.ai-context-popover-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
|
|
3250
3250
|
.ai-context-popover-header > div { display: grid; gap: 3px; min-width: 0; }
|
|
3251
3251
|
.ai-context-popover-header strong { font-size: 12px; }
|
|
3252
3252
|
.ai-context-popover-header small { color: var(--muted); font: 9px/1.4 var(--font-latin), var(--font-cjk), sans-serif; }
|
|
3253
3253
|
.ai-context-popover-close { flex: 0 0 auto; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); font: 17px/1 var(--font-latin), sans-serif; }
|
|
3254
3254
|
.ai-context-popover-close:hover, .ai-context-popover-close:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: 0; }
|
|
3255
|
-
.ai-model-picker { display: grid; flex: 0 0
|
|
3255
|
+
.ai-model-picker { display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
3256
3256
|
.ai-model-picker:hover, .ai-model-picker:focus-visible, .ai-model-picker[aria-expanded="true"] { 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; }
|
|
3257
3257
|
.ai-model-picker:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
3258
3258
|
.ai-model-picker:disabled { cursor: wait; opacity: .56; }
|
|
3259
|
-
.ai-model-picker-icon { width:
|
|
3259
|
+
.ai-model-picker-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
3260
3260
|
.ai-model-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 41; display: grid; width: min(280px, calc(100vw - 32px)); gap: 7px; padding: 12px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
3261
3261
|
.ai-model-popover.hidden { display: none; }
|
|
3262
|
-
.ai-model-popover::after { position: absolute; right:
|
|
3262
|
+
.ai-model-popover::after { position: absolute; right: 37px; 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); }
|
|
3263
3263
|
.ai-model-popover > label { color: var(--muted); font-size: 10px; font-weight: 600; }
|
|
3264
3264
|
.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; }
|
|
3265
3265
|
.ai-model-options { display: grid; max-height: 240px; gap: 3px; overflow-y: auto; }
|
|
@@ -3287,16 +3287,16 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3287
3287
|
.ai-context-distribution-row[data-key="input"] { --distribution-color: var(--green); }
|
|
3288
3288
|
.ai-context-distribution-row[data-key="output"] { --distribution-color: #718a9f; }
|
|
3289
3289
|
.ai-context-distribution-row[data-key="left"] { --distribution-color: color-mix(in srgb, var(--muted) 55%, transparent); }
|
|
3290
|
-
.ai-send-button { display: grid; flex: 0 0
|
|
3290
|
+
.ai-send-button { display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 0; border-radius: 4px; background: var(--accent); color: #fff; box-shadow: 0 3px 8px rgba(139,61,44,.2); }
|
|
3291
3291
|
.ai-send-button:hover, .ai-send-button:focus-visible { background: var(--accent-dark); outline: 0; }
|
|
3292
|
-
.ai-send-button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent), 0
|
|
3292
|
+
.ai-send-button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent), 0 3px 8px rgba(139,61,44,.2); }
|
|
3293
3293
|
.ai-send-button:disabled { cursor: wait; opacity: .62; }
|
|
3294
|
-
.ai-send-button-icon { width:
|
|
3294
|
+
.ai-send-button-icon { width: 14px; height: 14px; overflow: visible; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
|
|
3295
3295
|
.ai-message-images { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
3296
3296
|
.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; }
|
|
3297
3297
|
.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; }
|
|
3298
3298
|
.ai-message-image-preview img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
3299
|
-
.ai-send-button.is-stop .ai-send-button-icon { width:
|
|
3299
|
+
.ai-send-button.is-stop .ai-send-button-icon { width: 12px; height: 12px; fill: currentColor; stroke: none; }
|
|
3300
3300
|
.ai-citations { display: grid; gap: 6px; max-height: 190px; overflow-y: auto; margin-bottom: 8px; }
|
|
3301
3301
|
.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; }
|
|
3302
3302
|
.ai-citation-main { min-width: 0; padding: 8px 9px; border: 0; background: transparent; text-align: left; }
|
|
@@ -4705,7 +4705,6 @@ body { font-size: var(--ui-font-size); }
|
|
|
4705
4705
|
.user-message-scene { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4706
4706
|
.user-message-scene-label { font-size: calc(8px * var(--ai-font-scale)); }
|
|
4707
4707
|
.ai-prompt-reference { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4708
|
-
.ai-context-meter b { font-size: calc(9px * var(--ai-font-scale)); }
|
|
4709
4708
|
.ai-context-popover-header strong { font-size: calc(12px * var(--ai-font-scale)); }
|
|
4710
4709
|
.ai-context-popover-header small, .ai-context-distribution-label { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4711
4710
|
.ai-context-distribution-label small, .ai-context-distribution-label strong { font-size: calc(9px * var(--ai-font-scale)); }
|
package/dist/store.js
CHANGED
|
@@ -11,7 +11,7 @@ import { currentRequestActor } from "./request-context.js";
|
|
|
11
11
|
import { canReadWorkModule, canWriteWorkModule, classifyWorkModulePermissions, emptyWorkModulePermissions, fullWorkModulePermissions, storedWorkModulePermissions } from "./work-permissions.js";
|
|
12
12
|
import { countWords, documentShortSearchTerms, escapeSqlLikePattern, id, json, normalizeDocumentSearchText, now, splitDocumentParagraphs } from "./utils.js";
|
|
13
13
|
import { buildWritingCalendar, writingDateKey } from "./writing-progress-time.js";
|
|
14
|
-
import { resolveMaxAgentToolCallLimit } from "./ai-tool-results.js";
|
|
14
|
+
import { DEFAULT_AGENT_TOOL_CALL_LIMIT, MIN_AGENT_TOOL_CALL_LIMIT, resolveMaxAgentToolCallLimit } from "./ai-tool-results.js";
|
|
15
15
|
import { DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS, normalizeAiStreamIdleTimeoutSeconds } from "./ai-stream-timeout.js";
|
|
16
16
|
import { normalizeRoleplayScenePin, roleplayUserTurnDisplayText, roleplayUserTurnTitleSource } from "./roleplay-turn.js";
|
|
17
17
|
import { chapterAnnotationLineHashes, createChapterLineIds, MAX_CHAPTER_LINE_IDS, parseChapterAnnotationLineIds, parseChapterAnnotationLineHashes, parseChapterLineIds, reconcileChapterLineIds, reanchorChapterAnnotations } from "./chapter-annotation-anchor.js";
|
|
@@ -1104,7 +1104,7 @@ export class Store {
|
|
|
1104
1104
|
bookSummaryContextPercent: Math.min(90, Math.max(1, Number(row?.book_summary_context_percent ?? 50) || 50)),
|
|
1105
1105
|
contextCompactThreshold: Math.min(90, Math.max(50, Number(row?.context_compact_threshold ?? 85) || 85)),
|
|
1106
1106
|
agentToolCallLimitMaximum: maximumAgentToolCallLimit,
|
|
1107
|
-
agentToolCallLimit: Math.min(maximumAgentToolCallLimit, Math.max(
|
|
1107
|
+
agentToolCallLimit: Math.min(maximumAgentToolCallLimit, Math.max(MIN_AGENT_TOOL_CALL_LIMIT, Number(row?.agent_tool_call_limit ?? DEFAULT_AGENT_TOOL_CALL_LIMIT) || DEFAULT_AGENT_TOOL_CALL_LIMIT)),
|
|
1108
1108
|
agentToolCallGlobalMultiplier: Math.min(6, Math.max(1, Number(row?.agent_tool_call_global_multiplier ?? 3) || 3)),
|
|
1109
1109
|
agentTools: normalizeWorkAgentTools(row?.agent_tools_json),
|
|
1110
1110
|
imageToolModelId: row?.image_tool_model_id === null || row?.image_tool_model_id === undefined
|
|
@@ -1190,7 +1190,7 @@ export class Store {
|
|
|
1190
1190
|
title_generation_model_id = excluded.title_generation_model_id,
|
|
1191
1191
|
image_tool_model_id = excluded.image_tool_model_id,
|
|
1192
1192
|
always_include_setting_info = excluded.always_include_setting_info,
|
|
1193
|
-
updated_at = excluded.updated_at`, workId, nextPrompt, nextSystemPromptOverride ? 1 : 0, nextDailyTokenQuota, nextMonthlyTokenQuota, nextEnabled ? 1 : 0, Math.min(8, Math.max(1, nextConcurrency)), Math.min(200, Math.max(1, nextBatchLimit)), Math.min(10_000, Math.max(0, nextDailyTaskLimit)), Math.min(10, Math.max(1, nextFailureThreshold)), Math.min(120, Math.max(1, nextStabilityDelayMinutes)), current.autoRunPaused ? 1 : 0, String(current.autoRunPauseReason ?? ""), current.autoRunResumeAt === null ? null : String(current.autoRunResumeAt), Math.max(0, Number(current.autoRunConsecutiveFailures) || 0), Math.min(90, Math.max(1, nextBookSummaryContextPercent)), Math.min(90, Math.max(50, nextContextCompactThreshold)), Math.min(maximumAgentToolCallLimit, Math.max(
|
|
1193
|
+
updated_at = excluded.updated_at`, workId, nextPrompt, nextSystemPromptOverride ? 1 : 0, nextDailyTokenQuota, nextMonthlyTokenQuota, nextEnabled ? 1 : 0, Math.min(8, Math.max(1, nextConcurrency)), Math.min(200, Math.max(1, nextBatchLimit)), Math.min(10_000, Math.max(0, nextDailyTaskLimit)), Math.min(10, Math.max(1, nextFailureThreshold)), Math.min(120, Math.max(1, nextStabilityDelayMinutes)), current.autoRunPaused ? 1 : 0, String(current.autoRunPauseReason ?? ""), current.autoRunResumeAt === null ? null : String(current.autoRunResumeAt), Math.max(0, Number(current.autoRunConsecutiveFailures) || 0), Math.min(90, Math.max(1, nextBookSummaryContextPercent)), Math.min(90, Math.max(50, nextContextCompactThreshold)), Math.min(maximumAgentToolCallLimit, Math.max(MIN_AGENT_TOOL_CALL_LIMIT, nextAgentToolCallLimit)), Math.min(6, Math.max(1, nextAgentToolCallGlobalMultiplier)), JSON.stringify(nextAgentTools), nextTitleGenerationModelId, nextImageToolModelId, nextAlwaysIncludeSettingInfo ? 1 : 0, timestamp);
|
|
1194
1194
|
this.audit(workId, "work.ai-settings.updated", "work-ai-settings", workId, {
|
|
1195
1195
|
systemPromptChanged: input.systemPrompt !== undefined,
|
|
1196
1196
|
systemPromptOverride: nextSystemPromptOverride,
|
|
@@ -1204,7 +1204,7 @@ export class Store {
|
|
|
1204
1204
|
autoRunStabilityDelayMinutes: Math.min(120, Math.max(1, nextStabilityDelayMinutes)),
|
|
1205
1205
|
bookSummaryContextPercent: Math.min(90, Math.max(1, nextBookSummaryContextPercent)),
|
|
1206
1206
|
contextCompactThreshold: Math.min(90, Math.max(50, nextContextCompactThreshold)),
|
|
1207
|
-
agentToolCallLimit: Math.min(maximumAgentToolCallLimit, Math.max(
|
|
1207
|
+
agentToolCallLimit: Math.min(maximumAgentToolCallLimit, Math.max(MIN_AGENT_TOOL_CALL_LIMIT, nextAgentToolCallLimit)),
|
|
1208
1208
|
agentToolCallGlobalMultiplier: Math.min(6, Math.max(1, nextAgentToolCallGlobalMultiplier)),
|
|
1209
1209
|
agentTools: nextAgentTools,
|
|
1210
1210
|
imageToolModelId: nextImageToolModelId,
|
|
@@ -1233,8 +1233,8 @@ export class Store {
|
|
|
1233
1233
|
this.db.run(`INSERT INTO work_ai_settings (
|
|
1234
1234
|
work_id, semantic_search_enabled, semantic_embedding_model_id, semantic_rerank_model_id,
|
|
1235
1235
|
semantic_vector_dimension, semantic_recall_limit, semantic_result_limit,
|
|
1236
|
-
semantic_budget_tokens, semantic_channel_weight, updated_at
|
|
1237
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1236
|
+
semantic_budget_tokens, semantic_channel_weight, updated_at, agent_tool_call_limit
|
|
1237
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1238
1238
|
ON CONFLICT(work_id) DO UPDATE SET
|
|
1239
1239
|
semantic_search_enabled = excluded.semantic_search_enabled,
|
|
1240
1240
|
semantic_embedding_model_id = excluded.semantic_embedding_model_id,
|
|
@@ -1244,7 +1244,7 @@ export class Store {
|
|
|
1244
1244
|
semantic_result_limit = excluded.semantic_result_limit,
|
|
1245
1245
|
semantic_budget_tokens = excluded.semantic_budget_tokens,
|
|
1246
1246
|
semantic_channel_weight = excluded.semantic_channel_weight,
|
|
1247
|
-
updated_at = excluded.updated_at`, workId, enabled ? 1 : 0, embeddingModelId, rerankModelId, vectorDimension, recallLimit, resultLimit, budgetTokens, channelWeight, timestamp);
|
|
1247
|
+
updated_at = excluded.updated_at`, workId, enabled ? 1 : 0, embeddingModelId, rerankModelId, vectorDimension, recallLimit, resultLimit, budgetTokens, channelWeight, timestamp, Number(current.agentToolCallLimit));
|
|
1248
1248
|
this.audit(workId, "semantic.settings.updated", "work-ai-settings", workId, {
|
|
1249
1249
|
enabled,
|
|
1250
1250
|
embeddingModelId,
|