@allbluecn/web-app 0.18.2 → 0.19.0
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/package.json +66 -51
- package/src/__tests__/components/auth/auth-layout.test.tsx +13 -1
- package/src/__tests__/{knowledge-public → knowledge/public}/canonical-id.test.ts +1 -1
- package/src/__tests__/{knowledge-public → knowledge/public}/favicon.test.ts +1 -1
- package/src/__tests__/{knowledge-public → knowledge/public}/github-sync.test.ts +1 -1
- package/src/__tests__/{knowledge-public → knowledge/public}/share-merge.test.ts +7 -7
- package/src/__tests__/security/xss-sanitize.test.ts +9 -8
- package/src/components/ai/__tests__/chat-thread-id-stability.test.tsx +125 -0
- package/src/components/ai/ai-chat-prompt.tsx +8 -2
- package/src/components/ai/ai-prompt-input/index.tsx +2 -1
- package/src/components/ai/global-harness-sidebar/__tests__/global-ai-prompts.test.ts +1 -2
- package/src/components/ai/global-harness-sidebar/index.tsx +22 -10
- package/src/components/bug/__tests__/board-subgroups.test.tsx +512 -0
- package/src/components/bug/__tests__/board-view.test.tsx +350 -0
- package/src/components/bug/__tests__/bug-list-loading-defer.test.tsx +85 -0
- package/src/components/bug/__tests__/bug-toolbar.test.tsx +7 -1
- package/src/components/bug/__tests__/comment-reactions.test.tsx +182 -0
- package/src/components/bug/__tests__/date-and-lightbox.test.tsx +96 -0
- package/src/components/bug/__tests__/export-entry.test.tsx +217 -0
- package/src/components/bug/__tests__/filters-compat.test.tsx +157 -0
- package/src/components/bug/__tests__/fix-request-race.test.tsx +396 -0
- package/src/components/bug/__tests__/peek-modes.test.tsx +365 -0
- package/src/components/bug/__tests__/saved-views.test.tsx +309 -0
- package/src/components/bug/__tests__/story-bugs-panel.test.tsx +146 -0
- package/src/components/bug/__tests__/subgroup-header.test.tsx +150 -0
- package/src/components/bug/__tests__/toolbar-shell.test.tsx +369 -0
- package/src/components/bug/__tests__/use-paged-bug-groups.test.tsx +58 -0
- package/src/components/bug/__tests__/view-type.test.tsx +247 -0
- package/src/components/bug/bug-list-view.tsx +310 -13
- package/src/components/bug/bug-panel-header.tsx +84 -8
- package/src/components/bug/bug-toolbar.tsx +268 -116
- package/src/components/bug/create-bug-dialog.tsx +1 -1
- package/src/components/bug/detail/__tests__/activity-timeline.test.tsx +1 -0
- package/src/components/bug/detail/__tests__/bug-detail-collab.test.tsx +236 -0
- package/src/components/bug/detail/__tests__/repro-report-card.test.tsx +2 -1
- package/src/components/bug/detail/activity-timeline.tsx +1 -1
- package/src/components/bug/detail/bug-detail-body.tsx +316 -18
- package/src/components/bug/detail/comment-item.tsx +7 -2
- package/src/components/bug/detail/comment-reactions.tsx +138 -0
- package/src/components/bug/detail/error-context.tsx +29 -9
- package/src/components/bug/detail/props-section.tsx +31 -3
- package/src/components/bug/detail/repro-report-card.tsx +1 -1
- package/src/components/bug/display/constants.ts +4 -2
- package/src/components/bug/display/display-dropdown.tsx +76 -280
- package/src/components/bug/display/types.ts +24 -8
- package/src/components/bug/display/use-paged-bug-groups.ts +183 -32
- package/src/components/bug/filters/{filter-value-input.tsx → field-configs.tsx} +218 -106
- package/src/components/bug/filters/index.tsx +14 -5
- package/src/components/bug/inline-editors/assignee-editor.tsx +2 -2
- package/src/components/bug/inline-editors/labels-editor.tsx +1 -1
- package/src/components/bug/inline-editors/severity-editor.tsx +37 -18
- package/src/components/bug/inline-editors/state-editor.tsx +1 -1
- package/src/components/bug/peek-panel.tsx +153 -35
- package/src/components/bug/select-bug-dialog.tsx +500 -0
- package/src/components/bug/story-bugs-panel.tsx +1090 -0
- package/src/components/bug/types.ts +9 -2
- package/src/components/bug/views/board-view.tsx +661 -0
- package/src/components/bug/views/bug-list/batch-toolbar.tsx +25 -34
- package/src/components/bug/views/bug-list/group.tsx +28 -13
- package/src/components/bug/views/bug-list/index.tsx +223 -59
- package/src/components/bug/views/bug-list/row.tsx +7 -1
- package/src/components/bug/views/create-view-dialog.tsx +160 -0
- package/src/components/bug/views/view-list-section.tsx +140 -0
- package/src/components/bug/views/view-selector-button.tsx +145 -0
- package/src/components/bug/views/view-switcher.tsx +8 -6
- package/src/components/bug/views/view-types.ts +162 -0
- package/src/components/collab/__tests__/activity-attribution.test.tsx +211 -0
- package/src/components/collab/__tests__/canvas-collab-panel.test.tsx +118 -0
- package/src/components/collab/__tests__/collab-presence-header.test.tsx +335 -0
- package/src/components/collab/__tests__/entity-presence-badge.test.tsx +226 -0
- package/src/components/collab/activity-attribution.tsx +221 -0
- package/src/components/collab/canvas-collab-panel.tsx +104 -0
- package/src/components/collab/collab-presence-header.tsx +220 -0
- package/src/components/collab/entity-presence-badge.tsx +130 -0
- package/src/components/collab/field-editing-pill.tsx +137 -0
- package/src/components/command/command-palette.tsx +6 -6
- package/src/components/design/canvas/document/__tests__/canvas-tree-fixture-snapshot.ts +589 -0
- package/src/components/design/canvas/document/__tests__/canvas-tree-reconcile.test.ts +159 -0
- package/src/components/design/canvas/panels/toolbar.tsx +3 -2
- package/src/components/design/canvas/plugins/presence/__tests__/canvas-collab-wiring.test.tsx +660 -0
- package/src/components/design/canvas/plugins/presence/__tests__/layout.test.ts +292 -0
- package/src/components/design/canvas/plugins/presence/__tests__/presence-overlay.test.tsx +139 -0
- package/src/components/design/canvas/plugins/presence/layout.ts +222 -0
- package/src/components/design/canvas/plugins/presence/presence-layer.tsx +353 -0
- package/src/components/design/canvas/plugins/presence/presence-overlay.tsx +155 -0
- package/src/components/design/canvas/plugins/presence/viewer-badge.tsx +80 -0
- package/src/components/design/canvas/react/__tests__/hooks.test.tsx +4 -4
- package/src/components/design/editor/collaboration/__tests__/design-agent-panel-regenerate.test.tsx +3 -1
- package/src/components/design/editor/collaboration/__tests__/design-agent-panel-stream.test.tsx +3 -1
- package/src/components/design/editor/collaboration/design-agent-panel.tsx +3 -0
- package/src/components/design/editor/collaboration/design-comment-review-panel.tsx +5 -5
- package/src/components/design/editor/dashboard/create-design-dialog.tsx +1 -1
- package/src/components/design/editor/dashboard/design-dashboard.tsx +1 -10
- package/src/components/design/editor/layout/__tests__/collab-param-wiring.test.tsx +153 -0
- package/src/components/design/editor/layout/__tests__/no-page-save-toast.test.tsx +8 -0
- package/src/components/design/editor/layout/design-editor-layout.tsx +28 -1
- package/src/components/design/editor/layout/design-navigation.tsx +0 -4
- package/src/components/design/editor/layout/hooks/__tests__/use-design-auto-save-collab.test.tsx +130 -0
- package/src/components/design/editor/layout/hooks/use-design-auto-save.ts +18 -1
- package/src/components/design/editor/share/share-comment-panel.tsx +37 -20
- package/src/components/design/editor/share/share-layout.tsx +3 -0
- package/src/components/design/editor/share/share-nav-bar.tsx +7 -1
- package/src/components/design/editor/share/share-theme-toggle.tsx +10 -15
- package/src/components/design/editor/tiptap/node/paragraph-node/paragraph-node.scss +1 -1
- package/src/components/harness/__tests__/composer-draft.test.ts +126 -0
- package/src/components/harness/composer/composer-editor.tsx +64 -6
- package/src/components/harness/composer/harness-composer.tsx +1 -1
- package/src/components/harness/harness-layout.tsx +1 -1
- package/src/components/harness/harness-workbench-page.tsx +6 -1
- package/src/components/harness/turn-group.tsx +1 -1
- package/src/components/hotkeys/shortcut-badge.tsx +4 -4
- package/src/components/knowledge/dialog/knowledge-form-dialog.tsx +1 -1
- package/src/components/knowledge/editor/__tests__/knowledge-doc-editor-collab.test.tsx +542 -0
- package/src/components/knowledge/editor/doc-header.tsx +7 -49
- package/src/components/knowledge/editor/knowledge-doc-editor.tsx +264 -51
- package/src/components/knowledge/editor/mindmap-doc-editor.tsx +3 -19
- package/src/components/knowledge/knowledge-homepage.tsx +3 -3
- package/src/components/knowledge/knowledge-toolbar.tsx +8 -8
- package/src/components/{knowledge-public → knowledge/public}/bookmark-form-dialog.tsx +4 -4
- package/src/components/{knowledge-public → knowledge/public}/knowledge-card.tsx +1 -15
- package/src/components/{knowledge-public → knowledge/public}/knowledge-form-dialog.tsx +9 -9
- package/src/components/{knowledge-public → knowledge/public}/legal-compliance-editor.tsx +35 -28
- package/src/components/{knowledge-public → knowledge/public}/sharers-avatar-group.tsx +3 -2
- package/src/components/layout/layout-responsive-provider.tsx +79 -0
- package/src/components/layout/sidebar-03/__tests__/presence-avatar-stack.test.tsx +148 -0
- package/src/components/layout/sidebar-03/app-sidebar.tsx +9 -3
- package/src/components/layout/sidebar-03/index.tsx +19 -4
- package/src/components/layout/sidebar-03/nav-main.tsx +28 -10
- package/src/components/layout/sidebar-03/presence-avatar-stack.tsx +112 -0
- package/src/components/layout/sidebar-03/user-menu.tsx +3 -3
- package/src/components/project/create-project-dialog.tsx +1 -1
- package/src/components/project/invite-member-dialog.tsx +1 -1
- package/src/components/project/project-activity-list.tsx +2 -2
- package/src/components/project/project-member-manager.tsx +2 -2
- package/src/components/reui/__tests__/kanban-drop-indicator.test.ts +88 -0
- package/src/components/reui/__tests__/kanban-keyboard.test.ts +262 -0
- package/src/components/reui/event-calendar/event-calendar-agenda-view.tsx +236 -0
- package/src/components/reui/event-calendar/event-calendar-content.tsx +80 -0
- package/src/components/reui/event-calendar/event-calendar-dnd.tsx +1062 -0
- package/src/components/reui/event-calendar/event-calendar-event.tsx +555 -0
- package/src/components/reui/event-calendar/event-calendar-i18n.tsx +291 -0
- package/src/components/reui/event-calendar/event-calendar-lib.tsx +677 -0
- package/src/components/reui/event-calendar/event-calendar-month-view.tsx +1287 -0
- package/src/components/reui/event-calendar/event-calendar-nav.tsx +615 -0
- package/src/components/reui/event-calendar/event-calendar-recurrence.tsx +592 -0
- package/src/components/reui/event-calendar/event-calendar-resource-view.tsx +810 -0
- package/src/components/reui/event-calendar/event-calendar-time-grid.tsx +1366 -0
- package/src/components/reui/event-calendar/event-calendar-types.tsx +240 -0
- package/src/components/reui/event-calendar/event-calendar.tsx +1916 -0
- package/src/components/reui/gantt/__tests__/gantt-row-window.test.ts +243 -0
- package/src/components/reui/gantt/__tests__/gantt-view-virtualize.test.tsx +243 -0
- package/src/components/reui/gantt/gantt-bar.tsx +439 -0
- package/src/components/reui/gantt/gantt-dnd.tsx +1000 -0
- package/src/components/reui/gantt/gantt-i18n.tsx +287 -0
- package/src/components/reui/gantt/gantt-lib.tsx +695 -0
- package/src/components/reui/gantt/gantt-nav.tsx +446 -0
- package/src/components/reui/gantt/gantt-recurrence.tsx +402 -0
- package/src/components/reui/gantt/gantt-row-window.ts +119 -0
- package/src/components/reui/gantt/gantt-types.tsx +283 -0
- package/src/components/reui/gantt/gantt-view.tsx +4466 -0
- package/src/components/reui/gantt/gantt.tsx +1822 -0
- package/src/components/reui/gantt/use-gantt-row-window.ts +159 -0
- package/src/components/reui/icon-stack.tsx +90 -0
- package/src/components/reui/kanban-keyboard.ts +268 -0
- package/src/components/reui/kanban.tsx +1227 -0
- package/src/components/settings/personal-team/account/account-client.tsx +3 -2
- package/src/components/settings/personal-team/team/invitation-cards/invitation-history-list.tsx +5 -5
- package/src/components/settings/personal-team/team/team-client.tsx +2 -1
- package/src/components/settings/personal-team/team/team-member-list.tsx +4 -4
- package/src/components/share/share-presence-bar.tsx +182 -0
- package/src/components/shared-kit/auth/auth-client.ts +5 -0
- package/src/components/shared-kit/avatars/__tests__/avatar-shape.test.ts +44 -0
- package/src/components/shared-kit/avatars/__tests__/user-avatar.test.tsx +46 -0
- package/src/components/{tags/tags-collection.ts → shared-kit/avatars/avatar-shape.ts} +14 -20
- package/src/components/shared-kit/avatars/notion-avatar.tsx +62 -11
- package/src/components/shared-kit/avatars/user-avatar.tsx +7 -4
- package/src/components/shared-kit/list-kit/__tests__/batch-toolbar-shell.test.tsx +133 -0
- package/src/components/shared-kit/list-kit/__tests__/frozen-header.test.ts +89 -0
- package/src/components/shared-kit/list-kit/__tests__/level-badge.test.tsx +85 -0
- package/src/components/shared-kit/list-kit/__tests__/toolbar-shell.test.tsx +178 -0
- package/src/components/shared-kit/list-kit/batch-toolbar-shell.tsx +111 -0
- package/src/components/shared-kit/list-kit/csv-download.ts +46 -0
- package/src/components/shared-kit/list-kit/display/__tests__/dropdown.test.tsx +215 -0
- package/src/components/shared-kit/list-kit/display/dropdown.tsx +582 -0
- package/src/components/shared-kit/list-kit/display/types.ts +64 -0
- package/src/components/shared-kit/list-kit/display/values.ts +60 -0
- package/src/components/shared-kit/list-kit/filters/__tests__/create-filters-module.test.tsx +181 -0
- package/src/components/shared-kit/list-kit/filters/__tests__/types.test.ts +133 -0
- package/src/components/shared-kit/list-kit/filters/index.tsx +757 -0
- package/src/components/shared-kit/list-kit/filters/types.ts +68 -0
- package/src/components/shared-kit/list-kit/frozen-header.ts +45 -0
- package/src/components/shared-kit/list-kit/icon-semantics.ts +50 -0
- package/src/components/shared-kit/list-kit/kanban/__tests__/drop-layout.test.ts +139 -0
- package/src/components/shared-kit/list-kit/kanban/__tests__/frozen-header.test.ts +111 -0
- package/src/components/shared-kit/list-kit/kanban/__tests__/instance-key.test.ts +130 -0
- package/src/components/shared-kit/list-kit/kanban/__tests__/kanban-board-view.test.tsx +901 -0
- package/src/components/shared-kit/list-kit/kanban/__tests__/manual-order.test.ts +35 -0
- package/src/components/shared-kit/list-kit/kanban/drop-commit.ts +212 -0
- package/src/components/shared-kit/list-kit/kanban/drop-layout.ts +98 -0
- package/src/components/shared-kit/list-kit/kanban/frozen-header.ts +88 -0
- package/src/components/shared-kit/list-kit/kanban/instance-key.ts +83 -0
- package/src/components/shared-kit/list-kit/kanban/kanban-board-view.tsx +1414 -0
- package/src/components/shared-kit/list-kit/kanban/manual-order.ts +39 -0
- package/src/components/shared-kit/list-kit/level-badge.tsx +71 -0
- package/src/components/shared-kit/list-kit/toolbar-shell.tsx +103 -0
- package/src/components/shared-kit/loading/__tests__/use-deferred-flag.test.tsx +115 -0
- package/src/components/shared-kit/loading/use-deferred-flag.ts +35 -0
- package/src/components/shared-kit/peek/dock-slot.ts +62 -0
- package/src/components/shared-kit/peek/peek-mode.ts +34 -0
- package/src/components/shared-kit/theme/animated-theme-toggler.tsx +13 -7
- package/src/components/shared-kit/theme/theme-toggle.tsx +7 -18
- package/src/components/shared-kit/views/view-icon.tsx +49 -0
- package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +215 -0
- package/src/components/story/__tests__/story-list-loading-defer.test.tsx +88 -0
- package/src/components/story/__tests__/story-list-view-peek-sync.test.tsx +242 -0
- package/src/components/story/__tests__/story-list-view-restore.test.tsx +45 -3
- package/src/components/story/__tests__/story-list-view-ssr.test.tsx +0 -1
- package/src/components/story/ai/__tests__/action-registry.test.ts +11 -11
- package/src/components/story/ai/action-registry.ts +5 -7
- package/src/components/story/ai/handlers/stream-text.ts +1 -3
- package/src/components/story/ai/story-ai-toolbar.tsx +58 -16
- package/src/components/story/ai/use-toolbar-overflow.ts +4 -7
- package/src/components/story/columns.tsx +2 -2
- package/src/components/story/constants.ts +17 -1
- package/src/components/story/create-story-dialog.tsx +8 -7
- package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +38 -4
- package/src/components/story/detail/__tests__/categories.test.tsx +52 -26
- package/src/components/story/detail/__tests__/story-collab.test.tsx +897 -0
- package/src/components/story/detail/activity-tabs.tsx +5 -4
- package/src/components/story/detail/activity-timeline.tsx +15 -3
- package/src/components/story/detail/comment-input.tsx +2 -2
- package/src/components/story/detail/comment-item.tsx +6 -3
- package/src/components/story/detail/comment-thread.tsx +1 -0
- package/src/components/story/detail/reaction-bar.tsx +1 -1
- package/src/components/story/detail/story-detail-body.tsx +501 -76
- package/src/components/story/detail/story-detail-toolbar/__tests__/related-test-cases.test.tsx +173 -10
- package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +144 -8
- package/src/components/story/detail/story-detail-toolbar/categories.tsx +82 -38
- package/src/components/story/detail/story-detail-toolbar/children.tsx +254 -174
- package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +140 -58
- package/src/components/story/detail/story-detail-toolbar/index.tsx +613 -0
- package/src/components/story/detail/story-detail-toolbar/related-test-cases.tsx +1010 -87
- package/src/components/story/detail/story-detail-toolbar/scroll-hint.tsx +57 -0
- package/src/components/story/detail/story-detail-toolbar/use-panel-frozen-groups.ts +97 -0
- package/src/components/story/detail/story-properties.tsx +9 -5
- package/src/components/story/display/__tests__/display-dropdown-helpers.test.ts +17 -1
- package/src/components/story/display/__tests__/display-dropdown.test.tsx +9 -0
- package/src/components/story/display/__tests__/flatten-story-groups.test.ts +42 -0
- package/src/components/story/display/__tests__/group-fields.test.ts +81 -1
- package/src/components/story/display/__tests__/group-resolver.test.ts +153 -1
- package/src/components/story/display/__tests__/use-collapsed-subgroups.test.ts +158 -0
- package/src/components/story/display/__tests__/use-display-options.test.ts +32 -3
- package/src/components/story/display/__tests__/use-fit-count.test.ts +198 -0
- package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +77 -3
- package/src/components/story/display/constants.ts +23 -2
- package/src/components/story/display/display-dropdown.tsx +95 -397
- package/src/components/story/display/flatten-story-groups.ts +39 -0
- package/src/components/story/display/group-fields.ts +32 -1
- package/src/components/story/display/group-resolver.tsx +128 -5
- package/src/components/story/display/types.ts +23 -42
- package/src/components/story/display/use-collapsed-subgroups.ts +109 -0
- package/src/components/story/display/use-display-options.ts +21 -5
- package/src/components/story/display/use-fit-count.ts +124 -0
- package/src/components/story/display/use-paged-story-groups.ts +174 -31
- package/src/components/story/filters/field-configs.tsx +263 -0
- package/src/components/story/filters/index.tsx +14 -5
- package/src/components/story/inline-editors/__tests__/labels-editor.test.tsx +2 -0
- package/src/components/story/inline-editors/__tests__/priority-editor.test.tsx +15 -0
- package/src/components/story/inline-editors/assignee-editor.tsx +7 -5
- package/src/components/story/inline-editors/date-editor.tsx +7 -2
- package/src/components/story/inline-editors/inline-trigger.tsx +17 -4
- package/src/components/story/inline-editors/labels-editor.tsx +243 -35
- package/src/components/story/inline-editors/parent-story-editor.tsx +1 -1
- package/src/components/story/inline-editors/priority-editor.tsx +6 -4
- package/src/components/story/inline-editors/state-editor.tsx +6 -4
- package/src/components/story/inline-editors/story-select-dialog.tsx +56 -19
- package/src/components/story/peek-panel.tsx +234 -51
- package/src/components/story/relations/__tests__/design-select-dialog.test.tsx +131 -0
- package/src/components/story/relations/attachment-manage-dialog.tsx +1 -1
- package/src/components/story/relations/design-select-dialog.tsx +285 -0
- package/src/components/story/relations/knowledge-select-dialog.tsx +3 -3
- package/src/components/story/relations/link-manage-dialog.tsx +2 -2
- package/src/components/story/story-list-view.tsx +175 -17
- package/src/components/story/story-panel-header.tsx +18 -11
- package/src/components/story/story-toolbar.tsx +177 -126
- package/src/components/story/transfer-story-popover.tsx +1 -1
- package/src/components/story/types.ts +11 -2
- package/src/components/story/views/__tests__/calendar-view.test.tsx +262 -0
- package/src/components/story/views/__tests__/kanban-drop-subgroup.test.ts +68 -0
- package/src/components/story/views/__tests__/kanban-peek-sync.test.tsx +184 -0
- package/src/components/story/views/__tests__/kanban-view-membership-sync.test.tsx +179 -0
- package/src/components/story/views/__tests__/timeline-view.test.tsx +453 -0
- package/src/components/story/views/__tests__/view-icon.test.tsx +45 -0
- package/src/components/story/views/__tests__/view-types.test.ts +0 -8
- package/src/components/story/views/calendar/__tests__/mappers.test.ts +201 -0
- package/src/components/story/views/calendar/__tests__/slot-draft.test.ts +78 -0
- package/src/components/story/views/calendar/drag-blocked.ts +41 -0
- package/src/components/story/views/calendar/i18n.ts +91 -0
- package/src/components/story/views/calendar/mappers.ts +119 -0
- package/src/components/story/views/calendar/slot-draft.ts +48 -0
- package/src/components/story/views/calendar-view.tsx +490 -223
- package/src/components/story/views/create-view-dialog.tsx +6 -6
- package/src/components/story/views/kanban-view.tsx +618 -0
- package/src/components/story/views/{__tests__ → story-list/__tests__}/use-flat-rows.test.ts +1 -1
- package/src/components/story/views/story-list/batch-preview-dialog.tsx +3 -3
- package/src/components/story/views/story-list/batch-toolbar.tsx +17 -34
- package/src/components/story/views/story-list/group.tsx +4 -2
- package/src/components/story/views/story-list/index.tsx +142 -34
- package/src/components/story/views/story-list/row.tsx +4 -0
- package/src/components/story/views/{use-flat-rows.ts → story-list/use-flat-rows.ts} +2 -2
- package/src/components/story/views/timeline/__tests__/mappers.test.ts +742 -0
- package/src/components/story/views/timeline/__tests__/sentinel-geometry.test.ts +76 -0
- package/src/components/story/views/timeline/__tests__/use-date-commit.test.tsx +372 -0
- package/src/components/story/views/timeline/i18n.ts +96 -0
- package/src/components/story/views/timeline/mappers.ts +506 -0
- package/src/components/story/views/timeline/sentinel-geometry.ts +43 -0
- package/src/components/story/views/timeline/use-date-commit.ts +169 -0
- package/src/components/story/views/timeline-view.tsx +593 -222
- package/src/components/story/views/view-list-section.tsx +4 -4
- package/src/components/story/views/view-selector-button.tsx +9 -10
- package/src/components/story/views/view-switcher.tsx +7 -1
- package/src/components/story/views/view-types.ts +1 -5
- package/src/components/testcase/__tests__/batch-toolbar.test.tsx +245 -0
- package/src/components/testcase/__tests__/board-view.test.tsx +241 -0
- package/src/components/testcase/__tests__/case-row.test.tsx +24 -10
- package/src/components/testcase/__tests__/constants.test.ts +151 -0
- package/src/components/testcase/__tests__/create-dialog-scope.test.tsx +173 -0
- package/src/components/testcase/__tests__/detail-properties.test.tsx +19 -0
- package/src/components/testcase/__tests__/display-options.test.tsx +154 -0
- package/src/components/testcase/__tests__/export-entry.test.tsx +279 -0
- package/src/components/testcase/__tests__/filters.test.tsx +280 -0
- package/src/components/testcase/__tests__/list-view-scope.test.tsx +290 -0
- package/src/components/testcase/__tests__/list-view.test.tsx +552 -7
- package/src/components/testcase/__tests__/manual-move.test.ts +69 -0
- package/src/components/testcase/__tests__/peek-panel.test.tsx +89 -8
- package/src/components/testcase/__tests__/toolbar.test.tsx +302 -0
- package/src/components/testcase/__tests__/view-fallback.test.tsx +272 -0
- package/src/components/testcase/__tests__/view-management.test.tsx +179 -0
- package/src/components/testcase/ai/generate-dialog.tsx +1 -1
- package/src/components/testcase/constants.ts +83 -0
- package/src/components/testcase/create-testcase-dialog.tsx +76 -8
- package/src/components/testcase/detail/__tests__/select-cases-dialog.test.tsx +163 -0
- package/src/components/testcase/detail/case-detail-body.tsx +311 -30
- package/src/components/testcase/detail/select-cases-dialog.tsx +465 -0
- package/src/components/testcase/detail/story-link-editor.tsx +1 -1
- package/src/components/testcase/display/display-dropdown.tsx +147 -0
- package/src/components/testcase/display/field-configs.ts +80 -0
- package/src/components/testcase/display/use-testcase-display-options.ts +59 -0
- package/src/components/testcase/filters/field-configs.tsx +748 -0
- package/src/components/testcase/filters/index.tsx +35 -127
- package/src/components/testcase/inline-editors/assignee-editor.tsx +6 -6
- package/src/components/testcase/inline-editors/folder-editor.tsx +9 -9
- package/src/components/testcase/inline-editors/priority-editor.tsx +47 -31
- package/src/components/testcase/inline-editors/state-editor.tsx +14 -12
- package/src/components/testcase/inline-editors/type-editor.tsx +31 -20
- package/src/components/testcase/list/case-row.tsx +60 -2
- package/src/components/testcase/list/grouped-list.tsx +3 -2
- package/src/components/testcase/list-view.tsx +633 -137
- package/src/components/testcase/peek-panel.tsx +69 -11
- package/src/components/testcase/plans/plan-list.tsx +6 -1
- package/src/components/testcase/step-editor.tsx +34 -3
- package/src/components/testcase/testcase-toolbar.tsx +349 -94
- package/src/components/testcase/views/batch-toolbar.tsx +203 -0
- package/src/components/testcase/views/board-view.tsx +546 -0
- package/src/components/testcase/views/create-view-dialog.tsx +26 -14
- package/src/components/testcase/views/move.ts +56 -0
- package/src/components/testcase/views/view-selector-button.tsx +82 -30
- package/src/components/testcase/views/view-switcher.tsx +12 -5
- package/src/components/testcase/views/view-types.ts +50 -27
- package/src/components/tiptap/mindmap/lib/__tests__/shortcuts.test.ts +5 -4
- package/src/components/tiptap/mindmap/lib/shortcuts.ts +4 -3
- package/src/components/tiptap/node/doc-card-node-view.tsx +2 -1
- package/src/components/tiptap/notion-like/editor-extensions.ts +231 -0
- package/src/components/tiptap/notion-like/notion-like-editor.tsx +29 -156
- package/src/components/tiptap/ui/ai-menu/ai-menu.tsx +6 -1
- package/src/components/tiptap/ui/doc-reference-interaction/doc-reference-interaction.tsx +2 -1
- package/src/components/ui/checkbox.tsx +4 -4
- package/src/components/ui/popover.tsx +1 -1
- package/src/components/workspace/dialogs/edit-workspace-dialog.tsx +1 -1
- package/src/components/workspace/kb/workspace-kb-manage-dialog.tsx +1 -1
- package/src/components/workspace/kb/workspace-quick-doc-dialog.tsx +1 -1
- package/src/components/workspace/members/workspace-member-item.tsx +2 -2
- package/src/components/workspace/members/workspace-members-manager.tsx +2 -2
- package/src/config/sidebar-routes.tsx +11 -13
- package/src/design-engine/document/serialize-leafer-json.ts +2 -1
- package/src/design-engine/engine/__tests__/engine-split.test.ts +1 -1
- package/src/design-engine/engine/__tests__/engine.test.ts +20 -0
- package/src/design-engine/engine/app.ts +5 -0
- package/src/design-engine/engine/engine-base.ts +17 -0
- package/src/design-engine/engine/engine-doc.ts +32 -0
- package/src/hooks/__tests__/use-commands.test.tsx +17 -5
- package/src/hooks/use-commands.ts +4 -4
- package/src/hooks/use-toolbar-search.ts +7 -2
- package/src/lib/__tests__/datetime-local.test.ts +46 -0
- package/src/lib/__tests__/datetime.test.ts +7 -0
- package/src/lib/__tests__/layout-responsive.test.ts +123 -0
- package/src/lib/__tests__/sidebar-utils.test.ts +4 -4
- package/src/lib/ai/story-ai-stream.ts +1 -1
- package/src/lib/ai/use-selected-model.ts +15 -5
- package/src/lib/changelog/sdk-versions.ts +22 -22
- package/src/lib/collab/__tests__/bindings-fields.test.ts +496 -0
- package/src/lib/collab/__tests__/bindings-steps.test.ts +332 -0
- package/src/lib/collab/__tests__/presence-store.test.ts +414 -0
- package/src/lib/collab/__tests__/provider-handle-contract.test.ts +89 -0
- package/src/lib/collab/__tests__/provider.test.ts +504 -0
- package/src/lib/collab/__tests__/use-presence-list-refresh.test.tsx +309 -0
- package/src/lib/collab/__tests__/use-presence-report.test.ts +356 -0
- package/src/lib/collab/__tests__/use-share-presence.test.ts +555 -0
- package/src/lib/collab/__tests__/user-color.test.ts +53 -0
- package/src/lib/collab/bindings/__tests__/canvas-local.test.ts +583 -0
- package/src/lib/collab/bindings/__tests__/canvas-presence.test.ts +378 -0
- package/src/lib/collab/bindings/__tests__/canvas-remote.test.ts +454 -0
- package/src/lib/collab/bindings/__tests__/canvas-tree-perf.test.ts +134 -0
- package/src/lib/collab/bindings/canvas-local.ts +415 -0
- package/src/lib/collab/bindings/canvas-presence.ts +420 -0
- package/src/lib/collab/bindings/canvas-remote.ts +226 -0
- package/src/lib/collab/bindings/fields.ts +463 -0
- package/src/lib/collab/bindings/steps.ts +332 -0
- package/src/lib/collab/env.ts +33 -0
- package/src/lib/collab/module-map.ts +131 -0
- package/src/lib/collab/presence-store.ts +350 -0
- package/src/lib/collab/provider.ts +378 -0
- package/src/lib/collab/use-collab-doc.ts +100 -0
- package/src/lib/collab/use-presence-list-refresh.ts +142 -0
- package/src/lib/collab/use-presence-report.ts +176 -0
- package/src/lib/collab/use-self-profile-broadcast.ts +113 -0
- package/src/lib/collab/use-share-presence.ts +561 -0
- package/src/lib/collab/user-color.ts +40 -0
- package/src/lib/collections/index.ts +0 -1
- package/src/lib/collections/reference/bookmark-categories-collection.ts +1 -1
- package/src/lib/commands/__tests__/registry.test.ts +2 -5
- package/src/lib/commands/registry.ts +0 -3
- package/src/lib/commands/types.ts +1 -1
- package/src/lib/datetime.ts +20 -3
- package/src/lib/hooks/__tests__/use-synced-state.test.tsx +54 -0
- package/src/lib/hooks/use-story-panel-open.ts +134 -0
- package/src/lib/hooks/use-synced-state.ts +17 -0
- package/src/lib/hotkeys/__tests__/registry.test.ts +1 -1
- package/src/lib/hotkeys/registry.ts +0 -3
- package/src/lib/layout-responsive.ts +140 -0
- package/src/lib/pacer.ts +10 -1
- package/src/lib/peek-dock.ts +32 -0
- package/src/lib/plugin-runtime/__tests__/plugin-tool-schema-parity.test.ts +140 -0
- package/src/lib/plugin-runtime/plugin-client-tools.ts +28 -46
- package/src/lib/plugin-runtime/plugin-tool-defs.ts +152 -0
- package/src/lib/project-module-meta.ts +1 -8
- package/src/lib/rate-limit.ts +15 -6
- package/src/lib/sanitize.ts +122 -10
- package/src/lib/share-events.ts +20 -11
- package/src/lib/sidebar-utils.ts +1 -1
- package/src/lib/theme/__tests__/theme-mode.test.ts +96 -0
- package/src/lib/theme/theme-mode.ts +77 -0
- package/src/paraglide/messages/_index.js +5312 -1591
- package/src/paraglide/messages/en.js +1880 -685
- package/src/paraglide/messages/zh.js +1877 -682
- package/src/providers/index.tsx +4 -1
- package/src/providers/theme-mode-provider.tsx +135 -0
- package/src/routeTree.gen.ts +78 -101
- package/src/routes/$lang/docs/$/route.lazy.tsx +7 -2
- package/src/routes/-tests/chat-body-validation.test.ts +61 -3
- package/src/routes/__tests__/-edition-routing.test.tsx +12 -8
- package/src/routes/_nav/-components/nav-not-found.tsx +2 -2
- package/src/routes/_nav/bugs/$bugId/route.lazy.tsx +16 -2
- package/src/routes/_nav/bugs/$bugId/route.tsx +11 -2
- package/src/routes/_nav/dashboard/-components/avatar-group.tsx +2 -2
- package/src/routes/_nav/knowledge/$kbId/doc/$docId/route.lazy.tsx +31 -17
- package/src/routes/_nav/knowledge/$kbId/doc/$docId/route.tsx +12 -1
- package/src/routes/_nav/knowledge/$kbId/index.lazy.tsx +4 -2
- package/src/routes/_nav/knowledge/$kbId/route.lazy.tsx +7 -3
- package/src/routes/_nav/knowledge/bookmarks/$id.lazy.tsx +6 -6
- package/src/routes/_nav/knowledge/bookmarks/index.lazy.tsx +10 -10
- package/src/routes/_nav/knowledge/compliance/$id.lazy.tsx +9 -9
- package/src/routes/_nav/knowledge/compliance/edit/$id.lazy.tsx +1 -1
- package/src/routes/_nav/knowledge/compliance/index.lazy.tsx +8 -8
- package/src/routes/_nav/knowledge/dictionaries/$id.lazy.tsx +4 -4
- package/src/routes/_nav/knowledge/dictionaries/index.lazy.tsx +6 -6
- package/src/routes/_nav/knowledge/index.lazy.tsx +1 -1
- package/src/routes/_nav/knowledge/laws/$id.lazy.tsx +9 -9
- package/src/routes/_nav/knowledge/laws/edit/$id.lazy.tsx +1 -1
- package/src/routes/_nav/knowledge/laws/index.lazy.tsx +11 -11
- package/src/routes/_nav/knowledge/summary/index.lazy.tsx +1 -1
- package/src/routes/_nav/projects/$projectId/index.lazy.tsx +14 -6
- package/src/routes/_nav/route.lazy.tsx +11 -6
- package/src/routes/_nav/settings/account/route.tsx +3 -2
- package/src/routes/_nav/stories/$storyId/route.lazy.tsx +52 -8
- package/src/routes/_nav/stories/$storyId/route.tsx +11 -0
- package/src/routes/_nav/stories/-components/story-detail-skeleton.tsx +1 -1
- package/src/{components/tags/pages/tag-detail-page.tsx → routes/_nav/tasks/$taskId/route.lazy.tsx} +20 -10
- package/src/routes/_nav/tasks/$taskId/route.tsx +43 -0
- package/src/routes/_nav/tasks/-components/ai-task-detail.tsx +307 -0
- package/src/routes/_nav/tasks/-components/ai-task-list.tsx +217 -0
- package/src/routes/_nav/tasks/-components/ai-task-status-badge.tsx +46 -0
- package/src/routes/_nav/tasks/-components/assigned-list.tsx +157 -0
- package/src/routes/_nav/tasks/-components/create-ai-task-dialog.tsx +228 -0
- package/src/routes/_nav/tasks/-components/source-ai-exec-dialog.tsx +175 -0
- package/src/routes/_nav/tasks/-components/task-constants.ts +115 -0
- package/src/routes/_nav/tasks/-components/use-task-filter.ts +64 -0
- package/src/routes/_nav/tasks/-tests/create-ai-task-dialog.test.tsx +175 -0
- package/src/routes/_nav/tasks/-tests/task-filter.test.ts +134 -0
- package/src/routes/_nav/tasks/-tests/task-styles.test.tsx +119 -0
- package/src/routes/_nav/tasks/index.lazy.tsx +114 -0
- package/src/routes/_nav/tasks/index.tsx +47 -0
- package/src/routes/_nav/{tags → tasks}/route.tsx +3 -3
- package/src/routes/_nav/test-cases/$caseId/route.lazy.tsx +3 -1
- package/src/routes/_nav/test-cases/$caseId/route.tsx +11 -2
- package/src/routes/api/chat.ts +23 -19
- package/src/routes/api/design/prototype/route.ts +2 -4
- package/src/routes/{_nav/acceptance-criteria/route.tsx → api/internal/collab-reindex.ts} +11 -8
- package/src/routes/api/knowledge/sync-github.ts +1 -1
- package/src/routes/design-files/$projectId/$artboardId/$filename.ts +9 -0
- package/src/routes/invite/$token/route.lazy.tsx +2 -2
- package/src/routes/share/$versionId/route.lazy.tsx +8 -0
- package/src/routes/share/compliance/$id/route.lazy.tsx +2 -2
- package/src/routes/share/compliance/$id/route.tsx +1 -1
- package/src/routes/share/law/$id/route.lazy.tsx +2 -2
- package/src/routes/share/law/$id/route.tsx +1 -1
- package/src/server/__tests__/bug-groups-two-level.test.ts +263 -0
- package/src/server/__tests__/builtin-project.test.ts +45 -1
- package/src/server/__tests__/csv-export.test.ts +319 -0
- package/src/server/__tests__/share-version.test.ts +7 -2
- package/src/server/__tests__/task-assigned-query.test.ts +232 -0
- package/src/server/__tests__/task-context-builder.test.ts +175 -0
- package/src/server/__tests__/task-executor.test.ts +349 -0
- package/src/server/__tests__/task-priority-map.test.ts +41 -0
- package/src/server/__tests__/task-serverfn-crud.test.ts +639 -0
- package/src/server/__tests__/task-serverfn-queries.test.ts +403 -0
- package/src/server/__tests__/testcase-board.test.ts +301 -0
- package/src/server/__tests__/testcase-bulk.test.ts +155 -0
- package/src/server/__tests__/testcase-list-scope.test.ts +215 -0
- package/src/server/ai-gateway/__tests__/chat-handler-prompt-key.test.ts +106 -0
- package/src/server/ai-gateway/__tests__/chat-stream-builder-sandbox.test.ts +9 -3
- package/src/server/ai-gateway/__tests__/conformance.test.ts +4 -3
- package/src/server/ai-gateway/adapter.ts +4 -2
- package/src/server/ai-gateway/chat-handler.ts +13 -5
- package/src/server/ai-gateway/chat-stream-builder.ts +12 -7
- package/src/server/ai-gateway/sandbox/__tests__/harness-sandbox.test.ts +88 -0
- package/src/server/ai-gateway/sandbox/harness-sandbox.ts +1 -1
- package/src/server/ai-gateway/story-handler.ts +7 -2
- package/src/server/ai-gateway/tools/plugin-lifecycle-defs.ts +8 -99
- package/src/server/ai-gateway/tools/plugin-tools.ts +6 -10
- package/src/server/ai-task/assigned-query.ts +167 -0
- package/src/server/ai-task/context-builder.ts +182 -0
- package/src/server/ai-task/executor.ts +296 -0
- package/src/server/ai-task/priority-map.ts +37 -0
- package/src/server/ai-task/types.ts +15 -0
- package/src/server/bug-report/__tests__/report-handler.test.ts +55 -4
- package/src/server/bug-report/report-handler.ts +54 -8
- package/src/server/builtin-project.ts +24 -0
- package/src/server/cloudflare/prisma-wasm-base64-stub.ts +5 -0
- package/src/server/collab/__tests__/collab-permissions.test.ts +412 -0
- package/src/server/collab/__tests__/knowledge-additions.test.ts +121 -0
- package/src/server/collab/__tests__/reindex-endpoint.test.ts +168 -0
- package/src/server/collab/__tests__/resolve.test.ts +249 -0
- package/src/server/collab/__tests__/touch-snapshot.test.ts +58 -0
- package/src/server/collab/__tests__/write-guard-sdk.test.ts +501 -0
- package/src/server/collab/__tests__/write-guard.test.ts +373 -0
- package/src/server/collab/knowledge-additions.ts +37 -0
- package/src/server/collab/reindex-endpoint.ts +75 -0
- package/src/server/collab/resolve.ts +122 -0
- package/src/server/collab/seed.ts +408 -0
- package/src/server/collab/ticket-utils.ts +107 -0
- package/src/server/collab/touch-snapshot.ts +55 -0
- package/src/server/collab/write-guard.ts +258 -0
- package/src/server/collab-permissions.ts +248 -0
- package/src/server/csv.ts +61 -0
- package/src/server/{knowledge-public → knowledge/public}/github-sync.ts +12 -2
- package/src/server/{knowledge-public → knowledge/public}/share-merge.ts +31 -8
- package/src/server/lib/__tests__/sequence-number-retry.test.ts +160 -0
- package/src/server/lib/rate-limit.ts +13 -2
- package/src/server/lib/sequence-number-retry.ts +85 -0
- package/src/server/mcp/__tests__/tools.test.ts +10 -0
- package/src/server/mcp/tools.ts +26 -2
- package/src/server/middlewares/auth-rate-limit.ts +18 -9
- package/src/server/notifications/event-bus.ts +3 -0
- package/src/server/serverFns/__tests__/collab-snapshot.test.ts +665 -0
- package/src/server/serverFns/__tests__/collab.test.ts +459 -0
- package/src/server/serverFns/__tests__/knowledge-delete.test.ts +167 -0
- package/src/server/serverFns/__tests__/ownership-merge.test.ts +34 -2
- package/src/server/serverFns/__tests__/read-write-merge.test.ts +2 -2
- package/src/server/serverFns/__tests__/story-crud-archive-detail.test.ts +12 -2
- package/src/server/serverFns/__tests__/story-crud.test.ts +51 -11
- package/src/server/serverFns/__tests__/story-tree.test.ts +66 -34
- package/src/server/serverFns/__tests__/story-types.test.ts +14 -9
- package/src/server/serverFns/__tests__/update-bug-guard.test.ts +325 -0
- package/src/server/serverFns/__tests__/update-document-guard.test.ts +285 -0
- package/src/server/serverFns/__tests__/update-page-guard.test.ts +276 -0
- package/src/server/serverFns/__tests__/update-story-guard.test.ts +436 -0
- package/src/server/serverFns/__tests__/update-testcase-guard.test.ts +293 -0
- package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +25 -0
- package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +315 -15
- package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +36 -18
- package/src/server/serverFns/bug/__tests__/bug-list-query.test.ts +21 -0
- package/src/server/serverFns/bug/__tests__/bug-reactions.test.ts +335 -0
- package/src/server/serverFns/bug/__tests__/bug-reorder.test.ts +165 -0
- package/src/server/serverFns/bug/__tests__/bug-story.test.ts +4 -1
- package/src/server/serverFns/bug/bug-comments.ts +5 -0
- package/src/server/serverFns/bug/bug-crud.ts +321 -72
- package/src/server/serverFns/bug/bug-display-options.ts +11 -0
- package/src/server/serverFns/bug/bug-export.ts +137 -0
- package/src/server/serverFns/bug/bug-group-query.ts +16 -5
- package/src/server/serverFns/bug/bug-groups.ts +39 -15
- package/src/server/serverFns/bug/bug-list-query.ts +8 -1
- package/src/server/serverFns/bug/bug-reactions.ts +123 -0
- package/src/server/serverFns/bug/bug-reorder.ts +64 -0
- package/src/server/serverFns/bug/bug-story-bulk.ts +280 -0
- package/src/server/serverFns/bug/bug-story.ts +47 -39
- package/src/server/serverFns/bug/index.ts +6 -0
- package/src/server/serverFns/collab-presence.ts +119 -0
- package/src/server/serverFns/collab-snapshot.ts +201 -0
- package/src/server/serverFns/collab.ts +153 -0
- package/src/server/serverFns/design/__tests__/designs.test.ts +0 -2
- package/src/server/serverFns/design/designs.ts +0 -64
- package/src/server/serverFns/design/detail.ts +36 -2
- package/src/server/serverFns/design/pages.ts +73 -10
- package/src/server/serverFns/iteration/iteration-crud.ts +25 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/create.ts +3 -3
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/detail.ts +2 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/list.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/refresh-favicon.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/share.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/sync-github.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/update.ts +5 -4
- package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/create.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/detail.ts +2 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/list.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/share.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/update.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/create.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/detail.ts +2 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/list.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/share.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/update.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/create.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/detail.ts +2 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/list.ts +3 -2
- package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/share.ts +1 -1
- package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/update.ts +3 -2
- package/src/server/serverFns/knowledge.ts +135 -19
- package/src/server/serverFns/project/module-overview.ts +1 -2
- package/src/server/serverFns/share/version.ts +7 -1
- package/src/server/serverFns/story/__tests__/bulk.test.ts +6 -0
- package/src/server/serverFns/story/__tests__/create-story-internal.test.ts +112 -3
- package/src/server/serverFns/story/__tests__/story-assoc.test.ts +77 -6
- package/src/server/serverFns/story/__tests__/story-crud-date-key.test.ts +187 -0
- package/src/server/serverFns/story/__tests__/story-gantt-deps.test.ts +163 -0
- package/src/server/serverFns/story/__tests__/story-groups.test.ts +4 -0
- package/src/server/serverFns/story/__tests__/story-list-query.test.ts +20 -0
- package/src/server/serverFns/story/__tests__/story-search.test.ts +5 -0
- package/src/server/serverFns/story/__tests__/story-transfer.test.ts +64 -1
- package/src/server/serverFns/story/index.ts +2 -2
- package/src/server/serverFns/story/story-assoc.ts +44 -14
- package/src/server/serverFns/story/story-crud/archive.ts +52 -28
- package/src/server/serverFns/story/story-crud/bulk.ts +14 -4
- package/src/server/serverFns/story/story-crud/create-story-internal.ts +39 -21
- package/src/server/serverFns/story/story-crud.ts +283 -116
- package/src/server/serverFns/story/story-dependencies.ts +31 -0
- package/src/server/serverFns/story/story-groups.ts +15 -11
- package/src/server/serverFns/story/story-list-query.ts +16 -4
- package/src/server/serverFns/story/story-search.ts +9 -6
- package/src/server/serverFns/story/story-transfer.ts +99 -42
- package/src/server/serverFns/story/story-tree.ts +70 -48
- package/src/server/serverFns/story-display-options.ts +21 -10
- package/src/server/serverFns/story-view.ts +2 -2
- package/src/server/serverFns/tasks/task-crud.ts +264 -0
- package/src/server/serverFns/tasks/task-queries.ts +172 -0
- package/src/server/serverFns/testcase/__tests__/generation.test.ts +3 -0
- package/src/server/serverFns/testcase/__tests__/stats.test.ts +18 -4
- package/src/server/serverFns/testcase/__tests__/testcase-board.test.ts +113 -0
- package/src/server/serverFns/testcase/__tests__/testcase-crud.test.ts +62 -14
- package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +32 -10
- package/src/server/serverFns/testcase/bulk.ts +195 -0
- package/src/server/serverFns/testcase/generation.ts +10 -0
- package/src/server/serverFns/testcase/stats.ts +22 -8
- package/src/server/serverFns/testcase/testcase-board.ts +198 -0
- package/src/server/serverFns/testcase/testcase-crud.ts +165 -33
- package/src/server/serverFns/testcase/testcase-display-options.ts +166 -0
- package/src/server/serverFns/testcase/testcase-export.ts +125 -0
- package/src/server/serverFns/testcase/testcase-filter-views.ts +36 -9
- package/src/server/serverFns/testcase/testcase-groups.ts +27 -23
- package/src/server/serverFns/testcase/testcase-reorder.ts +66 -0
- package/src/server/story/__tests__/date-key.test.ts +57 -0
- package/src/server/story/date-key.ts +36 -0
- package/src/server/story/types.ts +3 -7
- package/src/server/testcase/__tests__/list-query.test.ts +269 -0
- package/src/server/testcase/list-query.ts +145 -0
- package/src/server-entry.ts +32 -0
- package/src/shared/bug/__tests__/constants.test.ts +44 -0
- package/src/shared/bug/constants.ts +37 -1
- package/src/start.ts +7 -2
- package/src/styles/globals.css +24 -3
- package/src/types/project.ts +2 -3
- package/vite.shared.ts +110 -3
- package/src/__tests__/tag-badge.test.tsx +0 -41
- package/src/components/bug/__tests__/bug-list-table.test.tsx +0 -289
- package/src/components/bug/bug-list-table.tsx +0 -187
- package/src/components/bug/columns.tsx +0 -193
- package/src/components/bug/filters/add-filter-dropdown.tsx +0 -70
- package/src/components/bug/filters/applied-filters.tsx +0 -214
- package/src/components/bug/filters/filter-condition.tsx +0 -151
- package/src/components/bug/filters/filter-row.tsx +0 -104
- package/src/components/bug/filters/filter-toggle.tsx +0 -61
- package/src/components/bug/filters/filter-types.ts +0 -146
- package/src/components/story/detail/story-detail-toolbar.tsx +0 -382
- package/src/components/story/filters/add-filter-dropdown.tsx +0 -70
- package/src/components/story/filters/applied-filters.tsx +0 -225
- package/src/components/story/filters/filter-condition.tsx +0 -152
- package/src/components/story/filters/filter-row.tsx +0 -116
- package/src/components/story/filters/filter-toggle.tsx +0 -60
- package/src/components/story/filters/filter-types.ts +0 -120
- package/src/components/story/filters/filter-value-input.tsx +0 -182
- package/src/components/story/views/kanban-board.tsx +0 -464
- package/src/components/story/views/kanban-card.tsx +0 -100
- package/src/components/tags/__tests__/tags.test.ts +0 -388
- package/src/components/tags/nav-tag.tsx +0 -121
- package/src/components/tags/pages/tag-settings-page.tsx +0 -32
- package/src/components/tags/pages/tags-index-page.tsx +0 -32
- package/src/components/tags/tag-badge.tsx +0 -56
- package/src/components/tags/tag-color-classes.ts +0 -61
- package/src/components/tags/tag-color-picker.tsx +0 -54
- package/src/components/tags/tag-components-index.ts +0 -23
- package/src/components/tags/tag-manager.tsx +0 -208
- package/src/components/tags/tag-selector.tsx +0 -164
- package/src/components/testcase/__tests__/table-view.test.tsx +0 -328
- package/src/components/testcase/plans/add-to-plan-bar.tsx +0 -64
- package/src/components/testcase/table/columns.tsx +0 -170
- package/src/components/testcase/table/table-view.tsx +0 -152
- package/src/lib/tag-colors.ts +0 -35
- package/src/routes/_nav/tags/$id/route.lazy.tsx +0 -23
- package/src/routes/_nav/tags/$id/route.tsx +0 -20
- package/src/routes/_nav/tags/index.lazy.tsx +0 -23
- package/src/routes/_nav/tags/index.tsx +0 -20
- package/src/routes/_nav/tags/settings/route.lazy.tsx +0 -23
- package/src/routes/_nav/tags/settings/route.tsx +0 -23
- package/src/server/serverFns/tags.ts +0 -138
- /package/src/components/{knowledge-public → knowledge/public}/bookmark-card.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/category-icon.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/chip-utils.ts +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/knowledge-card-grid.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/knowledge-filters.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/knowledge-search-bar.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/markdown-editor.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/markdown-view.tsx +0 -0
- /package/src/components/{knowledge-public → knowledge/public}/tag-filter.tsx +0 -0
- /package/src/components/{story/detail → shared-kit/comments}/emoji-picker.tsx +0 -0
- /package/src/components/story/{schemas/story-create.ts → story-create-schema.ts} +0 -0
- /package/src/server/{knowledge-public → knowledge/public}/canonical-id.ts +0 -0
- /package/src/server/{knowledge-public → knowledge/public}/favicon.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/categories.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/delete.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/favorite.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/unshare.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/delete.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/public.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/unshare.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/delete.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/unshare.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/entity-tags.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/favorite.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/delete.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/public.ts +0 -0
- /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/unshare.ts +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
// 画布 awareness 绑定层(P4a,任务 25,规格 §7.2「画布桥」awareness-only 部分)。
|
|
19
|
+
//
|
|
20
|
+
// ⚠ P4a 边界(与 P4b 编辑协同的任务 26-29 分界):本模块只做「只读远端在场」
|
|
21
|
+
// 的 awareness 读写——选中集 + 视口位置 + user brief。不建 Y 树(room 的
|
|
22
|
+
// Y.Doc 保持空文档)、不碰画布序列化、不回放任何远端 patch 到本地画布。
|
|
23
|
+
//
|
|
24
|
+
// ── awareness schema(任务 27/28 复用契约,禁止另起 key 约定)──────────────
|
|
25
|
+
//
|
|
26
|
+
// room:`canvas:design:{pageId}`(§3.1,buildRoomName({ kind: 'design-canvas' }))。
|
|
27
|
+
// awareness local state(每个客户端各自写入,跨端经 awareness 协议同步):
|
|
28
|
+
//
|
|
29
|
+
// state['canvas'](本模块独占 key,摘除外科手术式、不碰 profile 兄弟字段):
|
|
30
|
+
// {
|
|
31
|
+
// selection: string[] // 元素 id 数组(engine 权威选中态原样上报)
|
|
32
|
+
// viewport: { x, y, zoom } // 视口中心(board 坐标)+ 缩放——P4a 精度低
|
|
33
|
+
// //(仅「谁在看此页」展示),不承诺实时跟随
|
|
34
|
+
// // 平移;P4b 远端视口指示器复用此字段
|
|
35
|
+
// user: { id, name } // user brief(署名:options.identity →
|
|
36
|
+
// // awareness profile → `client:{clientID}` 降级,
|
|
37
|
+
// // 空名降级先例同协同头像/fields editing)
|
|
38
|
+
// ts: number // 写入时刻(Date.now(),新鲜度/排序)
|
|
39
|
+
// }
|
|
40
|
+
// state['profile'](既有 use-self-profile-broadcast 广播,本模块不写只读):
|
|
41
|
+
// 头像摘要 { id, username, avatarConfig?, bgShape?, bgColor? }——远端
|
|
42
|
+
// 渲染侧可用来补头像;身份键以 canvas.user.id 为准。
|
|
43
|
+
//
|
|
44
|
+
// 生命周期:engine 选中变化 / setViewport → 50ms 节流写入(交互级高频走
|
|
45
|
+
// awareness,绝不产生 Y 增量——同 fields editing 的性能红线口径)/ offline
|
|
46
|
+
// 自动摘除(对端靠 awareness 超时+removed 感知离场)/ synced 重放最新缓存 /
|
|
47
|
+
// 冻结期(setFrozen,拖拽/变换预留接口)仅暂停上报不销毁订阅,解冻补报 /
|
|
48
|
+
// 卸载 destroy 摘除。presence-layer 另挂 15s setViewport 心跳:覆盖平移
|
|
49
|
+
// 漂移(pan 不触发 zoom 快照变化)+ ts 新鲜度续报。
|
|
50
|
+
//
|
|
51
|
+
// drag 子字段(P4b 任务 28 起写入、任务 30 渲染消费):canvas key 内可选
|
|
52
|
+
// `drag: { items: [{id,x,y}], ts }`——拖拽会话中本地桥外科手术式写入(不碰
|
|
53
|
+
// selection/viewport/user),presence flush 的整键写不含 drag(冻结接力保证
|
|
54
|
+
// 拖拽期不被抹掉);readRemotes 透传解析后的 drag 供影子渲染,畸形丢弃。
|
|
55
|
+
//
|
|
56
|
+
// 分层:无 React / 无 Leafer 依赖(engine 以结构子集注入,可脱离组件树单测
|
|
57
|
+
// ——双客户端真实 Y.Doc + Awareness 直连验证,同 bindings-fields 先例)。
|
|
58
|
+
|
|
59
|
+
import type {
|
|
60
|
+
CollabAwareness,
|
|
61
|
+
CollabConnectionStatus,
|
|
62
|
+
} from '../provider'
|
|
63
|
+
|
|
64
|
+
/** awareness state 中本模块独占的 key(任务 27/28 复用) */
|
|
65
|
+
export const CANVAS_AWARENESS_KEY = 'canvas'
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 上报节流窗口:选中集变化是交互级高频(拖拽框选逐帧触发),落在性能红线
|
|
69
|
+
* 30-60ms 档(约 1-2 帧,肉眼无感延迟)。leading 首报即时 + 窗口内合并尾随。
|
|
70
|
+
*/
|
|
71
|
+
export const CANVAS_PRESENCE_THROTTLE_MS = 50
|
|
72
|
+
|
|
73
|
+
/** 视口位置(board 坐标系中心 + 缩放;P4a 低精度,见 schema 注释) */
|
|
74
|
+
export interface CanvasViewport {
|
|
75
|
+
x: number
|
|
76
|
+
y: number
|
|
77
|
+
zoom: number
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** awareness `canvas` key 的值形态(任务 27/28 复用) */
|
|
81
|
+
export interface CanvasPresenceState {
|
|
82
|
+
selection: string[]
|
|
83
|
+
viewport: CanvasViewport
|
|
84
|
+
user: { id: string; name: string }
|
|
85
|
+
ts: number
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 一个远端协作者的在场快照(渲染插件数据面:选中框 + 名牌 + 查看徽标) */
|
|
89
|
+
export interface RemoteCanvasPresence {
|
|
90
|
+
/** awareness clientID(多 tab 同人区分;React key / 排序稳定键) */
|
|
91
|
+
clientId: number
|
|
92
|
+
userId: string
|
|
93
|
+
name: string
|
|
94
|
+
selection: string[]
|
|
95
|
+
viewport: CanvasViewport
|
|
96
|
+
ts: number
|
|
97
|
+
/**
|
|
98
|
+
* 拖拽影子(任务 30,P4b):canvas key 的 drag 子字段(本地桥拖拽会话
|
|
99
|
+
* 写入),null/缺省 = 未在拖拽。渲染插件据此画半透明轮廓。
|
|
100
|
+
*/
|
|
101
|
+
drag?: CanvasDragShadow | null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ===== drag 子字段(任务 28 本地桥写入面,schema 归 awareness 侧管理;任务 30
|
|
105
|
+
// 渲染消费经本模块透传,禁止渲染层再解析 awareness 裸状态)=====================
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* awareness canvas key 内影子子字段的 key(T28 本地桥 + 任务 30 渲染共用,
|
|
109
|
+
* 禁止硬编码 'drag' 字面量)。
|
|
110
|
+
*/
|
|
111
|
+
export const CANVAS_DRAG_FIELD = 'drag'
|
|
112
|
+
|
|
113
|
+
/** 单个拖拽影子的位置快照(board 坐标系内节点的父相对 x/y,远端渲染插件按
|
|
114
|
+
* 本地 doc 父链补齐绝对偏移后投影定位画幽灵框) */
|
|
115
|
+
export interface CanvasDragShadowItem {
|
|
116
|
+
id: string
|
|
117
|
+
x: number
|
|
118
|
+
y: number
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 拖拽 awareness 影子(T25 canvas key 的 drag 子字段值形态):
|
|
123
|
+
* 一次拖拽会话的全量位置快照(非增量 delta——远端迟到加入直接可用)。
|
|
124
|
+
*/
|
|
125
|
+
export interface CanvasDragShadow {
|
|
126
|
+
items: CanvasDragShadowItem[]
|
|
127
|
+
ts: number
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 校验一份远端 awareness drag 子字段(fail-closed:畸形丢弃不抛错,
|
|
132
|
+
* 同 parseCanvasPresence 口径)。畸形仅影响影子,不影响 presence 主体。
|
|
133
|
+
*/
|
|
134
|
+
export function parseCanvasDragShadow(raw: unknown): CanvasDragShadow | null {
|
|
135
|
+
if (typeof raw !== 'object' || raw === null) return null
|
|
136
|
+
const record = raw as Record<string, unknown>
|
|
137
|
+
if (!Array.isArray(record.items)) return null
|
|
138
|
+
const items: CanvasDragShadowItem[] = []
|
|
139
|
+
for (const item of record.items) {
|
|
140
|
+
if (typeof item !== 'object' || item === null) return null
|
|
141
|
+
const entry = item as Record<string, unknown>
|
|
142
|
+
if (typeof entry.id !== 'string' || entry.id.length === 0) return null
|
|
143
|
+
if (!isFiniteNumber(entry.x) || !isFiniteNumber(entry.y)) return null
|
|
144
|
+
items.push({ id: entry.id, x: entry.x, y: entry.y })
|
|
145
|
+
}
|
|
146
|
+
if (!isFiniteNumber(record.ts)) return null
|
|
147
|
+
return { items, ts: record.ts }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* engine 结构子集(真实 LeaferEngine 兼容直传:getSelection 返回权威选中
|
|
152
|
+
* 态、onSelectionChange 订阅选中变化;测试注入 fake)。
|
|
153
|
+
*/
|
|
154
|
+
export interface CanvasSelectionSource {
|
|
155
|
+
getSelection(): string[]
|
|
156
|
+
onSelectionChange(cb: (ids: string[]) => void): () => void
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface CanvasPresenceBindingSource {
|
|
160
|
+
/** noop 模式(协同不可用)为 null:全链路安全空操作 */
|
|
161
|
+
readonly awareness: CollabAwareness | null
|
|
162
|
+
/** 连接状态订阅(真实 handle 契约:订阅即回放当前状态) */
|
|
163
|
+
onStatusChange(listener: (status: CollabConnectionStatus) => void): () => void
|
|
164
|
+
readonly selection: CanvasSelectionSource
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** 上报署名(缺省走 awareness profile → clientID 降级,同 fields 先例) */
|
|
168
|
+
export interface CanvasPresenceIdentity {
|
|
169
|
+
userId: string
|
|
170
|
+
name: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface CanvasPresenceBindingOptions {
|
|
174
|
+
/** 显式署名;仅在 createCanvasPresenceBinding 时生效(同 provider getTicket 惯例) */
|
|
175
|
+
identity?: CanvasPresenceIdentity
|
|
176
|
+
/** 节流窗口覆盖(单测用;缺省 CANVAS_PRESENCE_THROTTLE_MS) */
|
|
177
|
+
throttleMs?: number
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface CanvasPresenceBinding {
|
|
181
|
+
/** 单调递增变更计数(React useSyncExternalStore 快照缓存键) */
|
|
182
|
+
version(): number
|
|
183
|
+
/**
|
|
184
|
+
* 上报视口(board 坐标中心 + zoom;低频:挂载 / zoom 快照变化 / 15s 心跳)。
|
|
185
|
+
* 走与选中集相同的节流通道(挂起时与下一次 flush 合并)。
|
|
186
|
+
*/
|
|
187
|
+
setViewport(viewport: CanvasViewport): void
|
|
188
|
+
/**
|
|
189
|
+
* 交互冻结(P4b 拖拽/变换期间纪律的先行落地,规格 §7.2):冻结期仅暂停
|
|
190
|
+
* awareness 上报、不销毁任何订阅(引擎选中照常缓存为最新值),解冻即补报。
|
|
191
|
+
* P4a 只上报不应用远端 patch,故本端不激活此开关(接线随 P4b 落地)。
|
|
192
|
+
*/
|
|
193
|
+
setFrozen(frozen: boolean): void
|
|
194
|
+
/**
|
|
195
|
+
* 远端在场快照(自身除外;畸形 fail-closed 丢弃;按 clientID 升序稳定
|
|
196
|
+
* 排序——徽标/名牌顺序不随上报频率抖动)。
|
|
197
|
+
*/
|
|
198
|
+
readRemotes(): RemoteCanvasPresence[]
|
|
199
|
+
/** 订阅变更(远端或本端写入触发的 awareness update;不立即回调) */
|
|
200
|
+
subscribe(listener: () => void): () => void
|
|
201
|
+
/** 清理:摘 observer、摘本端 canvas key(保留 profile 兄弟字段)、断订阅、幂等 */
|
|
202
|
+
destroy(): void
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isFiniteNumber(v: unknown): v is number {
|
|
206
|
+
return typeof v === 'number' && Number.isFinite(v)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 校验一份远端 awareness `canvas` state(fail-closed:畸形丢弃不抛错,
|
|
211
|
+
* 同 fields parseEditor / presence-store 口径)。
|
|
212
|
+
*/
|
|
213
|
+
export function parseCanvasPresence(
|
|
214
|
+
raw: unknown,
|
|
215
|
+
): Omit<RemoteCanvasPresence, 'clientId'> | null {
|
|
216
|
+
if (typeof raw !== 'object' || raw === null) return null
|
|
217
|
+
const record = raw as Record<string, unknown>
|
|
218
|
+
if (!Array.isArray(record.selection)) return null
|
|
219
|
+
if (!record.selection.every((id) => typeof id === 'string')) return null
|
|
220
|
+
if (typeof record.viewport !== 'object' || record.viewport === null) return null
|
|
221
|
+
const viewport = record.viewport as Record<string, unknown>
|
|
222
|
+
if (!isFiniteNumber(viewport.x) || !isFiniteNumber(viewport.y) || !isFiniteNumber(viewport.zoom)) {
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
if (typeof record.user !== 'object' || record.user === null) return null
|
|
226
|
+
const user = record.user as Record<string, unknown>
|
|
227
|
+
if (typeof user.id !== 'string' || user.id.length === 0) return null
|
|
228
|
+
if (typeof user.name !== 'string') return null
|
|
229
|
+
if (!isFiniteNumber(record.ts)) return null
|
|
230
|
+
// drag 子字段(任务 30):可选;畸形仅丢弃影子,不影响 presence 主体
|
|
231
|
+
const drag = record.drag === undefined ? null : parseCanvasDragShadow(record.drag)
|
|
232
|
+
return {
|
|
233
|
+
userId: user.id,
|
|
234
|
+
name: user.name,
|
|
235
|
+
selection: record.selection as string[],
|
|
236
|
+
viewport: { x: viewport.x, y: viewport.y, zoom: viewport.zoom },
|
|
237
|
+
ts: record.ts,
|
|
238
|
+
drag,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** 从 awareness local state 读 profile 署名(use-self-profile-broadcast 广播) */
|
|
243
|
+
function profileIdentity(awareness: CollabAwareness): CanvasPresenceIdentity | null {
|
|
244
|
+
const profile = awareness.getLocalState()?.profile as
|
|
245
|
+
| { id?: unknown; username?: unknown }
|
|
246
|
+
| undefined
|
|
247
|
+
if (
|
|
248
|
+
typeof profile?.id === 'string' &&
|
|
249
|
+
profile.id.length > 0 &&
|
|
250
|
+
typeof profile.username === 'string'
|
|
251
|
+
) {
|
|
252
|
+
return { userId: profile.id, name: profile.username }
|
|
253
|
+
}
|
|
254
|
+
return null
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** 外科手术式摘除本端 canvas key(保留 profile 等兄弟字段,同 fields 先例) */
|
|
258
|
+
function removeCanvasKey(awareness: CollabAwareness): void {
|
|
259
|
+
const local = awareness.getLocalState()
|
|
260
|
+
if (!local) return
|
|
261
|
+
const next: Record<string, unknown> = { ...local }
|
|
262
|
+
if (!(CANVAS_AWARENESS_KEY in next)) return
|
|
263
|
+
delete next[CANVAS_AWARENESS_KEY]
|
|
264
|
+
awareness.setLocalState(next)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createCanvasPresenceBinding(
|
|
268
|
+
source: CanvasPresenceBindingSource,
|
|
269
|
+
options: CanvasPresenceBindingOptions = {},
|
|
270
|
+
): CanvasPresenceBinding {
|
|
271
|
+
const { awareness } = source
|
|
272
|
+
const throttleMs = options.throttleMs ?? CANVAS_PRESENCE_THROTTLE_MS
|
|
273
|
+
|
|
274
|
+
let version = 0
|
|
275
|
+
let destroyed = false
|
|
276
|
+
let frozen = false
|
|
277
|
+
let status: CollabConnectionStatus = 'connecting'
|
|
278
|
+
const listeners = new Set<() => void>()
|
|
279
|
+
/** 最新本地态缓存(冻结/offline 期间照常更新,仅不上报) */
|
|
280
|
+
let latestSelection: string[] = []
|
|
281
|
+
let latestViewport: CanvasViewport = { x: 0, y: 0, zoom: 1 }
|
|
282
|
+
|
|
283
|
+
let flushTimer: ReturnType<typeof setTimeout> | null = null
|
|
284
|
+
let lastFlushAt = 0
|
|
285
|
+
|
|
286
|
+
const notify = () => {
|
|
287
|
+
version += 1
|
|
288
|
+
for (const listener of listeners) listener()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const resolveIdentity = (): CanvasPresenceIdentity => {
|
|
292
|
+
if (options.identity) return options.identity
|
|
293
|
+
if (awareness) {
|
|
294
|
+
const fromProfile = profileIdentity(awareness)
|
|
295
|
+
if (fromProfile) return fromProfile
|
|
296
|
+
return { userId: `client:${awareness.clientID}`, name: '' }
|
|
297
|
+
}
|
|
298
|
+
return { userId: 'client:0', name: '' }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const flush = () => {
|
|
302
|
+
if (!awareness || destroyed || frozen || status === 'offline') return
|
|
303
|
+
const identity = resolveIdentity()
|
|
304
|
+
const state: CanvasPresenceState = {
|
|
305
|
+
selection: [...latestSelection],
|
|
306
|
+
viewport: { ...latestViewport },
|
|
307
|
+
// schema 序列化口径 user:{id,name}(与 fields editing 的 userId 键区分:
|
|
308
|
+
// canvas state 自包含远端可解析,消费方无需二次映射)
|
|
309
|
+
user: { id: identity.userId, name: identity.name },
|
|
310
|
+
ts: Date.now(),
|
|
311
|
+
}
|
|
312
|
+
awareness.setLocalStateField(CANVAS_AWARENESS_KEY, state)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** leading 即时 + 窗口内合并尾随(50ms 交互级节流) */
|
|
316
|
+
const scheduleFlush = () => {
|
|
317
|
+
if (destroyed || !awareness) return
|
|
318
|
+
if (frozen || status === 'offline') return
|
|
319
|
+
const elapsed = Date.now() - lastFlushAt
|
|
320
|
+
if (elapsed >= throttleMs) {
|
|
321
|
+
lastFlushAt = Date.now()
|
|
322
|
+
flush()
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
if (flushTimer) return
|
|
326
|
+
flushTimer = setTimeout(() => {
|
|
327
|
+
flushTimer = null
|
|
328
|
+
// 冻结/offline 在窗口内翻转:落地前再查一次(仅暂停不销毁语义)
|
|
329
|
+
if (destroyed || frozen || status === 'offline') return
|
|
330
|
+
lastFlushAt = Date.now()
|
|
331
|
+
flush()
|
|
332
|
+
}, throttleMs - elapsed)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const onEngineSelection = (ids: string[]) => {
|
|
336
|
+
if (destroyed) return
|
|
337
|
+
latestSelection = ids
|
|
338
|
+
scheduleFlush()
|
|
339
|
+
}
|
|
340
|
+
const unsubscribeEngine = source.selection.onSelectionChange(onEngineSelection)
|
|
341
|
+
// 订阅即取一次权威态(挂载前已有选中的场景)
|
|
342
|
+
latestSelection = source.selection.getSelection()
|
|
343
|
+
|
|
344
|
+
const onAwarenessUpdate = () => notify()
|
|
345
|
+
if (awareness) awareness.on('update', onAwarenessUpdate)
|
|
346
|
+
|
|
347
|
+
const unsubscribeStatus = source.onStatusChange((next) => {
|
|
348
|
+
if (destroyed) return
|
|
349
|
+
const prev = status
|
|
350
|
+
status = next
|
|
351
|
+
if (next === 'offline') {
|
|
352
|
+
// 断连自动清:对端靠 awareness 超时/removed 感知离场;缓存保留供重放;
|
|
353
|
+
// 挂起的尾随上报作废(offline 期间 scheduleFlush/flush 均短路)
|
|
354
|
+
if (flushTimer) {
|
|
355
|
+
clearTimeout(flushTimer)
|
|
356
|
+
flushTimer = null
|
|
357
|
+
}
|
|
358
|
+
if (awareness && prev !== 'offline') removeCanvasKey(awareness)
|
|
359
|
+
} else if (prev === 'offline' && next === 'synced') {
|
|
360
|
+
// 恢复重放最新缓存(detach/attach 往返可能丢 local state,同 fields 防御)
|
|
361
|
+
scheduleFlush()
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
version: () => version,
|
|
367
|
+
|
|
368
|
+
setViewport: (viewport) => {
|
|
369
|
+
if (destroyed) return
|
|
370
|
+
latestViewport = viewport
|
|
371
|
+
scheduleFlush()
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
setFrozen: (next) => {
|
|
375
|
+
if (destroyed || frozen === next) return
|
|
376
|
+
frozen = next
|
|
377
|
+
if (!next) scheduleFlush() // 解冻补报最新缓存
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
readRemotes: () => {
|
|
381
|
+
if (!awareness) return []
|
|
382
|
+
const remotes: RemoteCanvasPresence[] = []
|
|
383
|
+
for (const [clientId, state] of awareness.getStates()) {
|
|
384
|
+
if (clientId === awareness.clientID) continue // 只显示他人
|
|
385
|
+
const parsed = parseCanvasPresence(
|
|
386
|
+
(state ?? {})[CANVAS_AWARENESS_KEY],
|
|
387
|
+
)
|
|
388
|
+
if (!parsed) continue
|
|
389
|
+
remotes.push({ clientId, ...parsed })
|
|
390
|
+
}
|
|
391
|
+
// clientID 升序:稳定排序(徽标/名牌不随上报节奏抖动)
|
|
392
|
+
remotes.sort((a, b) => a.clientId - b.clientId)
|
|
393
|
+
return remotes
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
subscribe: (listener) => {
|
|
397
|
+
if (destroyed) return () => {}
|
|
398
|
+
listeners.add(listener)
|
|
399
|
+
return () => {
|
|
400
|
+
listeners.delete(listener)
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
destroy: () => {
|
|
405
|
+
if (destroyed) return
|
|
406
|
+
destroyed = true
|
|
407
|
+
if (flushTimer) {
|
|
408
|
+
clearTimeout(flushTimer)
|
|
409
|
+
flushTimer = null
|
|
410
|
+
}
|
|
411
|
+
unsubscribeEngine()
|
|
412
|
+
if (awareness) {
|
|
413
|
+
awareness.off('update', onAwarenessUpdate)
|
|
414
|
+
removeCanvasKey(awareness)
|
|
415
|
+
}
|
|
416
|
+
unsubscribeStatus()
|
|
417
|
+
listeners.clear()
|
|
418
|
+
},
|
|
419
|
+
}
|
|
420
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
// 画布远端变更增量应用桥(P4b,任务 27,规格 §7.2「画布桥」远端方向)。
|
|
19
|
+
//
|
|
20
|
+
// 数据流(规格逐字):Y observe → 组装最新 SceneDocument(纯函数 yTreeToScene)
|
|
21
|
+
// → applyRemoteScene(引擎内复用 planSceneDiff 的增量管线)→ 失败兜底
|
|
22
|
+
// rebuildRemoteScene(现有 rebuildScene 回退路径,appliedDoc 基线随重建重置)。
|
|
23
|
+
//
|
|
24
|
+
// ── 基线归属(与 §7.2「复用 planSceneDiff」的对接口径)─────────────────────
|
|
25
|
+
//
|
|
26
|
+
// diff 基线(「当前已应用场景」)由引擎持有(appliedDoc),不在桥上维护:
|
|
27
|
+
// 增量应用必须以「引擎真正渲染中的树」为 prev 才安全——若桥自持基线,本地
|
|
28
|
+
// 编辑(引擎侧变更)会让桥基线过期,算出的计划与引擎状态错位(重复挂载/
|
|
29
|
+
// 漏删),树直接损坏。引擎 applyRemoteScene 内部以 appliedDoc 重跑
|
|
30
|
+
// planSceneDiff,本地/远端并发编辑天然正确;桥只负责把最新 Y 态送进引擎并
|
|
31
|
+
// 依返回值决定是否全量兜底。§7.2「粒度适配」(toPatch 整节点 Y set 等)是
|
|
32
|
+
// 任务 28 本地→Y 方向对同一 diff 计划的适配,与本桥无涉。
|
|
33
|
+
//
|
|
34
|
+
// ── 行为契约 ────────────────────────────────────────────────────────────────
|
|
35
|
+
//
|
|
36
|
+
// - 观察:根 Y.Map(CANVAS_ROOT_KEY)observeDeep——只对画布树变更响应,
|
|
37
|
+
// 同 room 的 TipTap Design 文档(default XmlFragment 双载体)击键不触发对账
|
|
38
|
+
// (observeDeep 先例同 bindings/steps)。
|
|
39
|
+
// - 防抖:连续 update 合并到窗口尾(CANVAS_REMOTE_DEBOUNCE_MS),每次 flush
|
|
40
|
+
// 对「当前 Y 态」反序列化一次——中间态天然丢弃。
|
|
41
|
+
// - 冻结:setFrozen(true)(拖拽/变换中)挂起应用不销毁订阅;解冻后对最新
|
|
42
|
+
// 快照一次 diff 应用(非队列回放,同 canvas-presence 冻结语义)。
|
|
43
|
+
// - 兜底:applyRemoteScene 返回 false 或抛异常 → rebuildRemoteScene(同一
|
|
44
|
+
// 快照直通,引擎内 rebuildScene + appliedDoc 重置);重建再抛(引擎未挂载
|
|
45
|
+
// /已销毁)吞掉等下一次 update 重试——桥绝不向上抛错炸页面。
|
|
46
|
+
// - 初始种子:绑定创建即调度一次对账——room 已有内容(中途加入/快照加载前
|
|
47
|
+
// 到达的 update)时引擎收敛到 Y 态;引擎未就绪(未挂载)安全跳过,由
|
|
48
|
+
// wiring 侧在引擎就绪后调 reconcileNow() 补种。
|
|
49
|
+
// - 空树:yTreeToScene 返回 null(room 未种子化)跳过,不触碰引擎。
|
|
50
|
+
// - 回声:本端经任务 28 写 Y 的事务带 CANVAS_LOCAL_ORIGIN(canvas-local 写入
|
|
51
|
+
// 收敛在该 origin 内)——observeDeep 按 origin 跳过本地回声(引擎是写入源,
|
|
52
|
+
// appliedDoc 已是该态,回声对账是纯浪费);远端到达(provider applyUpdate)
|
|
53
|
+
// 与种子回放 origin 不同,不受过滤。
|
|
54
|
+
//
|
|
55
|
+
// 分层:无 React / 无 Leafer 依赖(引擎以结构子集注入,可脱离组件树单测
|
|
56
|
+
// ——同 canvas-presence/fields 先例)。
|
|
57
|
+
|
|
58
|
+
import type * as Y from 'yjs'
|
|
59
|
+
import type { SceneDocument } from '@/components/design/canvas/document/types'
|
|
60
|
+
import {
|
|
61
|
+
CANVAS_ROOT_KEY,
|
|
62
|
+
yTreeToScene,
|
|
63
|
+
type CollabSceneDocument,
|
|
64
|
+
} from '@allbluecn/collab-core/canvas-tree'
|
|
65
|
+
import { CANVAS_LOCAL_ORIGIN } from './canvas-local'
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 远端 update → flush 防抖窗口:远端画布编辑是手柄释放级低频(§7.1:拖拽中
|
|
69
|
+
* 只走 awareness,Y 只记最终态),100ms 合并多节点操作(粘贴/编组)的连发
|
|
70
|
+
* update,同时保持远端到达的可感延迟低于一帧余。
|
|
71
|
+
*/
|
|
72
|
+
export const CANVAS_REMOTE_DEBOUNCE_MS = 100
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 引擎结构子集(真实 DocEngine 兼容直传:applyRemoteScene 增量优先、
|
|
76
|
+
* rebuildRemoteScene 全量兜底——两者是引擎 §7.2 远端方向的公开门面;
|
|
77
|
+
* 测试注入记录调用的 fake)。
|
|
78
|
+
*/
|
|
79
|
+
export interface CanvasRemoteEngineTarget {
|
|
80
|
+
/** 增量应用(引擎内以已应用场景为基线 planSceneDiff);false/抛错 = 增量失败 */
|
|
81
|
+
applyRemoteScene(doc: SceneDocument): boolean
|
|
82
|
+
/** 全量重建兜底(现有 rebuildScene 回退门面;appliedDoc 基线随重建重置) */
|
|
83
|
+
rebuildRemoteScene(doc: SceneDocument): void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface CanvasRemoteBindingSource {
|
|
87
|
+
/** canvas room 的 Y.Doc(本端实例,远端变更经 provider applyUpdate 到达) */
|
|
88
|
+
readonly doc: Y.Doc
|
|
89
|
+
readonly engine: CanvasRemoteEngineTarget
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CanvasRemoteBindingOptions {
|
|
93
|
+
/** 防抖窗口覆盖(单测用;缺省 CANVAS_REMOTE_DEBOUNCE_MS) */
|
|
94
|
+
debounceMs?: number
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CanvasRemoteBinding {
|
|
98
|
+
/**
|
|
99
|
+
* 交互冻结(规格 §7.2:拖拽/变换中本地元素不回放远端 patch):冻结期挂起
|
|
100
|
+
* 应用、不销毁订阅;解冻若存在挂起变更,**同步**对最新快照一次 diff 应用
|
|
101
|
+
* (返回时引擎已收敛——任务 28 释放写序依赖此保证,见 canvas-local
|
|
102
|
+
* CanvasFreezeTarget 契约)。
|
|
103
|
+
*/
|
|
104
|
+
setFrozen(frozen: boolean): void
|
|
105
|
+
/** 立即对账一次(绕过防抖;仍尊重冻结)。wiring 在引擎就绪后补种初始态。 */
|
|
106
|
+
reconcileNow(): void
|
|
107
|
+
/** 清理:摘 observer、取消挂起 timer;幂等 */
|
|
108
|
+
destroy(): void
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** collab-core 结构镜像 → web SceneDocument(T26 canvas-tree 对账测试锁定字段集合严格一致;运行时防御由引擎入口 sanitizeScene 承担) */
|
|
112
|
+
function toSceneDocument(scene: CollabSceneDocument): SceneDocument {
|
|
113
|
+
return scene as unknown as SceneDocument
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function createCanvasRemoteBinding(
|
|
117
|
+
source: CanvasRemoteBindingSource,
|
|
118
|
+
options: CanvasRemoteBindingOptions = {},
|
|
119
|
+
): CanvasRemoteBinding {
|
|
120
|
+
const debounceMs = options.debounceMs ?? CANVAS_REMOTE_DEBOUNCE_MS
|
|
121
|
+
// 根 Y.Map 实例在 doc 生命周期内稳定(sceneToYTree/yTreeToScene 同例);
|
|
122
|
+
// observeDeep 覆盖 nodes/order/extensions/boardMeta 全部嵌套变更
|
|
123
|
+
const rootMap = source.doc.getMap<unknown>(CANVAS_ROOT_KEY)
|
|
124
|
+
|
|
125
|
+
let destroyed = false
|
|
126
|
+
let frozen = false
|
|
127
|
+
// 冻结期有 update 到达(或 flush 被冻结打断)→ 解冻后需补一次对账
|
|
128
|
+
let pending = false
|
|
129
|
+
let timer: ReturnType<typeof setTimeout> | null = null
|
|
130
|
+
|
|
131
|
+
const flush = () => {
|
|
132
|
+
timer = null
|
|
133
|
+
if (destroyed) return
|
|
134
|
+
if (frozen) {
|
|
135
|
+
pending = true
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
pending = false
|
|
139
|
+
let scene: CollabSceneDocument | null = null
|
|
140
|
+
try {
|
|
141
|
+
scene = yTreeToScene(source.doc)
|
|
142
|
+
} catch {
|
|
143
|
+
// 反序列化异常(远端写坏 Y 树):跳过本轮,下一次 update 再对账
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if (!scene) return // room 未种子化,不触碰引擎
|
|
147
|
+
|
|
148
|
+
const next = toSceneDocument(scene)
|
|
149
|
+
let applied = false
|
|
150
|
+
try {
|
|
151
|
+
applied = source.engine.applyRemoteScene(next)
|
|
152
|
+
} catch {
|
|
153
|
+
applied = false
|
|
154
|
+
}
|
|
155
|
+
if (applied) return
|
|
156
|
+
// 增量失败 → 全量兜底(同一快照直通;引擎内 rebuildScene + appliedDoc
|
|
157
|
+
// 重置)。引擎未挂载/已销毁时 assertUsable 抛错吞掉——wiring 时序问题,
|
|
158
|
+
// 下一次 update 或 reconcileNow 重试
|
|
159
|
+
try {
|
|
160
|
+
source.engine.rebuildRemoteScene(next)
|
|
161
|
+
} catch {
|
|
162
|
+
// 引擎不可用:跳过本轮
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const schedule = () => {
|
|
167
|
+
if (destroyed || timer) return
|
|
168
|
+
timer = setTimeout(flush, debounceMs)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// observeDeep 回调签名经 Parameters 提取(YEvent 泛型约束 AbstractType,
|
|
172
|
+
// 手写泛型实参会 tsc 报错;transaction.origin 为本地回声过滤的依据)
|
|
173
|
+
const onCanvasChange: Parameters<Y.Map<unknown>['observeDeep']>[0] = (
|
|
174
|
+
_events,
|
|
175
|
+
transaction,
|
|
176
|
+
) => {
|
|
177
|
+
if (destroyed) return
|
|
178
|
+
// 本地回声抑制(任务 28 验收点):本端写 Y 的事务按 origin 跳过——引擎
|
|
179
|
+
// 是写入源,无需回灌;远端到达与种子回放 origin 各异,照常对账
|
|
180
|
+
if (transaction.origin === CANVAS_LOCAL_ORIGIN) return
|
|
181
|
+
pending = true
|
|
182
|
+
schedule()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
rootMap.observeDeep(onCanvasChange)
|
|
186
|
+
// 挂载初始种子:room 已有内容(中途加入)时首次对账;引擎未就绪安全跳过
|
|
187
|
+
schedule()
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
setFrozen: (next) => {
|
|
191
|
+
if (destroyed || frozen === next) return
|
|
192
|
+
frozen = next
|
|
193
|
+
if (!next && pending) {
|
|
194
|
+
// 解冻即同步补应用(对最新快照一次 diff,非队列回放;同时关掉挂起
|
|
195
|
+
// timer 防二次):任务 28 本地桥的释放写序依赖「setFrozen(false) 返回
|
|
196
|
+
// 时冻结期到达的远端变更已收敛进引擎」——先收敛再写 Y,否则全量对账
|
|
197
|
+
// 会回滚拖拽期间到达的远端变更(审查 C1)。即时化同时收口 T27 审查
|
|
198
|
+
// minor②「解冻补应用过防抖窗」
|
|
199
|
+
if (timer) {
|
|
200
|
+
clearTimeout(timer)
|
|
201
|
+
timer = null
|
|
202
|
+
}
|
|
203
|
+
flush()
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
reconcileNow: () => {
|
|
208
|
+
if (destroyed) return
|
|
209
|
+
if (timer) {
|
|
210
|
+
clearTimeout(timer)
|
|
211
|
+
timer = null
|
|
212
|
+
}
|
|
213
|
+
flush()
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
destroy: () => {
|
|
217
|
+
if (destroyed) return
|
|
218
|
+
destroyed = true
|
|
219
|
+
if (timer) {
|
|
220
|
+
clearTimeout(timer)
|
|
221
|
+
timer = null
|
|
222
|
+
}
|
|
223
|
+
rootMap.unobserveDeep(onCanvasChange)
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
}
|