@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,408 @@
|
|
|
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
|
+
// 知识库协同快照种子化 helper(Yjs 多人协作 §5.2 双轨存储与首次种子化,
|
|
19
|
+
// 任务 14):业务列 KnowledgeDocument.content(编辑器 getHTML 产物存于
|
|
20
|
+
// content.html)→ Y.Doc → CollabDocument 快照落库。
|
|
21
|
+
//
|
|
22
|
+
// 任务 20 扩展:Story 描述种子化(seedStoryDoc)——Story.description 是
|
|
23
|
+
// Text 列存 serializeStoryDescription 的 JSON 字符串({html, markdown} 双轨),
|
|
24
|
+
// 编辑器与知识库同为 NotionDocEditor(扩展集一致),故种子 schema 复用
|
|
25
|
+
// 知识库同一组合;差异仅在内容提取(extractStoryDescriptionHtml)与
|
|
26
|
+
// room 名(entity:story:{id})。
|
|
27
|
+
//
|
|
28
|
+
// 普通服务端模块(非 serverFn 文件):getPrisma 惰性获取连接(只写
|
|
29
|
+
// CollabDocument;业务列由调用方读取后作参数传入,单次读取)+ 组装
|
|
30
|
+
// collab-core 默认集与 web 自定义追加集(knowledge-additions),由 serverFn
|
|
31
|
+
// (serverFns/collab-snapshot.ts)与未来 collab 回写复用。
|
|
32
|
+
//
|
|
33
|
+
// 三态语义(§5.2,权威口径在规格):
|
|
34
|
+
// - 无 CollabDocument 且业务列有内容 → 种子化(本模块)后返回快照
|
|
35
|
+
// - 有快照且业务列较新(旁路写入发生过,KnowledgeDocument.updatedAt vs
|
|
36
|
+
// CollabDocument.updatedAt)→ 重种子化(DB 内容覆盖 Y 快照)
|
|
37
|
+
// - 正常 → 直读快照
|
|
38
|
+
// 判定与编排在 serverFn;本模块专注「按 docId 种子化并落库」这一步。
|
|
39
|
+
//
|
|
40
|
+
// DOM 纪律:seedYDocFromHtml 的 generateJSON 依赖浏览器 DOM 全局;vitest
|
|
41
|
+
// jsdom 环境天然满足,生产 Node 运行时(serverFn SSR bundle)由
|
|
42
|
+
// ensureServerDom 懒加载 jsdom 注入全局(jsdom 为 apps/web 运行时依赖)。
|
|
43
|
+
|
|
44
|
+
import * as Y from "yjs";
|
|
45
|
+
import { getPrisma } from "@allbluecn/database/get-prisma";
|
|
46
|
+
import { buildRoomName } from "@allbluecn/collab-core/rooms";
|
|
47
|
+
import { seedYDocFromHtml } from "@allbluecn/collab-core/tiptap-export";
|
|
48
|
+
import { hasCanvasContent, seedYTreeFromCanvas } from "@allbluecn/collab-core/canvas-package";
|
|
49
|
+
import {
|
|
50
|
+
applyStepsToDoc,
|
|
51
|
+
type CollabStep,
|
|
52
|
+
} from "@allbluecn/collab-core/steps";
|
|
53
|
+
import { createKnowledgeExtensions } from "@allbluecn/collab-core/knowledge-schema";
|
|
54
|
+
import { createKnowledgeDocAdditions } from "./knowledge-additions";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 生产 Node 运行时的 DOM 全局注入(幂等)。generateJSON/generateHTML 走
|
|
58
|
+
* DOMParser/document 解析管线;缺失时 tiptap-export 会 fail-loud,这里
|
|
59
|
+
* 提前补齐。vitest jsdom 环境与浏览器端直接跳过。
|
|
60
|
+
*/
|
|
61
|
+
async function ensureServerDom(): Promise<void> {
|
|
62
|
+
if (typeof window !== "undefined") return;
|
|
63
|
+
const { JSDOM } = await import("jsdom");
|
|
64
|
+
const dom = new JSDOM("<!doctype html><html><body></body></html>");
|
|
65
|
+
const target = globalThis as unknown as Record<string, unknown>;
|
|
66
|
+
const source = dom.window as unknown as Record<string, unknown>;
|
|
67
|
+
for (const key of [
|
|
68
|
+
"window",
|
|
69
|
+
"document",
|
|
70
|
+
"DOMParser",
|
|
71
|
+
"XMLSerializer",
|
|
72
|
+
"Node",
|
|
73
|
+
"Element",
|
|
74
|
+
"HTMLElement",
|
|
75
|
+
"DocumentFragment",
|
|
76
|
+
"HTMLSpanElement",
|
|
77
|
+
"HTMLImageElement",
|
|
78
|
+
]) {
|
|
79
|
+
if (target[key] === undefined) target[key] = source[key];
|
|
80
|
+
}
|
|
81
|
+
// jsdom window 是 opaque origin(localStorage 访问抛 SecurityError)且不实现
|
|
82
|
+
// ResizeObserver;注入后同进程的 SSR 渲染树(React 组件读这些 API)会连环
|
|
83
|
+
// ReferenceError/SecurityError。补最小可渲染面:内存 localStorage + RO stub。
|
|
84
|
+
if (target["localStorage"] === undefined) {
|
|
85
|
+
const store = new Map<string, string>();
|
|
86
|
+
target["localStorage"] = {
|
|
87
|
+
getItem: (k: string) => (store.has(k) ? store.get(k)! : null),
|
|
88
|
+
setItem: (k: string, v: string) => void store.set(k, String(v)),
|
|
89
|
+
removeItem: (k: string) => void store.delete(k),
|
|
90
|
+
clear: () => void store.clear(),
|
|
91
|
+
key: (i: number) => Array.from(store.keys())[i] ?? null,
|
|
92
|
+
get length() {
|
|
93
|
+
return store.size;
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (target["ResizeObserver"] === undefined) {
|
|
98
|
+
target["ResizeObserver"] = class {
|
|
99
|
+
observe() {}
|
|
100
|
+
unobserve() {}
|
|
101
|
+
disconnect() {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 解析 KnowledgeDocument.content(Json 列)为可种子化的 HTML。
|
|
108
|
+
* 与 NotionDocEditor getInitialContent 读取口径同构:
|
|
109
|
+
* - { html, markdown } 双轨(编辑器 onChange/updateDocumentFn 存储形态)→ html
|
|
110
|
+
* - { text } → 包裹为单段落(同编辑器兜底)
|
|
111
|
+
* - string → 包裹为单段落(历史数据兜底)
|
|
112
|
+
* 返回 null 表示「业务列无内容」(空对象 / 空串 / 编辑器空文档产物
|
|
113
|
+
* `<p></p>`、`<p><br></p>`)——此时无需种子化,客户端直接建空 Y.Doc。
|
|
114
|
+
*/
|
|
115
|
+
export function extractKnowledgeDocHtml(content: unknown): string | null {
|
|
116
|
+
let html: string | null = null;
|
|
117
|
+
if (typeof content === "object" && content !== null) {
|
|
118
|
+
const record = content as Record<string, unknown>;
|
|
119
|
+
if ("html" in record && typeof record.html === "string") {
|
|
120
|
+
html = record.html;
|
|
121
|
+
} else if ("text" in record && typeof record.text === "string") {
|
|
122
|
+
html = `<p>${record.text}</p>`;
|
|
123
|
+
}
|
|
124
|
+
} else if (typeof content === "string") {
|
|
125
|
+
html = `<p>${content}</p>`;
|
|
126
|
+
}
|
|
127
|
+
if (!html) return null;
|
|
128
|
+
const trimmed = html.trim();
|
|
129
|
+
if (
|
|
130
|
+
trimmed === "" ||
|
|
131
|
+
trimmed === "<p></p>" ||
|
|
132
|
+
trimmed === "<p><br></p>"
|
|
133
|
+
) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return html;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 知识库文档服务端种子化(§5.2):默认集 + web 追加集构造 schema →
|
|
141
|
+
* seedYDocFromHtml → encodeStateAsUpdate → upsert CollabDocument。
|
|
142
|
+
* content 由调用方传入(serverFn 本次请求已读的业务列值,单次读取;
|
|
143
|
+
* 独立调用方可自行直查后传入)。返回快照 update 字节(调用方自行
|
|
144
|
+
* base64 编码给客户端 applyUpdate)。
|
|
145
|
+
*
|
|
146
|
+
* ⚠️ 仅限「协作开始前首次导入」与「旁路写入后的规格授权重种子化」(§5.2);
|
|
147
|
+
* 协作进行中调用会丢协作历史,调用方须遵守三态判定。
|
|
148
|
+
*/
|
|
149
|
+
export async function seedKnowledgeDoc(
|
|
150
|
+
docId: string,
|
|
151
|
+
content: unknown,
|
|
152
|
+
): Promise<Uint8Array> {
|
|
153
|
+
await ensureServerDom();
|
|
154
|
+
const html = extractKnowledgeDocHtml(content);
|
|
155
|
+
if (!html) {
|
|
156
|
+
throw new Error(`知识库文档无内容,无需种子化: ${docId}`);
|
|
157
|
+
}
|
|
158
|
+
const yDoc = seedYDocFromHtml(html, [
|
|
159
|
+
...createKnowledgeExtensions(),
|
|
160
|
+
...createKnowledgeDocAdditions(),
|
|
161
|
+
]);
|
|
162
|
+
// new Uint8Array 拷贝出 Uint8Array<ArrayBuffer>(Prisma Bytes 入参的
|
|
163
|
+
// 精确泛型要求;Y.encodeStateAsUpdate 返回 ArrayBufferLike 视图)
|
|
164
|
+
const state = new Uint8Array(Y.encodeStateAsUpdate(yDoc));
|
|
165
|
+
const prisma = await getPrisma();
|
|
166
|
+
await prisma.collabDocument.upsert({
|
|
167
|
+
where: { roomName: buildRoomName({ kind: "knowledge-doc", docId }) },
|
|
168
|
+
create: {
|
|
169
|
+
roomName: buildRoomName({ kind: "knowledge-doc", docId }),
|
|
170
|
+
state,
|
|
171
|
+
},
|
|
172
|
+
update: { state },
|
|
173
|
+
});
|
|
174
|
+
return state;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Story description 服务端种子化(§5.2,任务 20):extension 组合与知识库
|
|
179
|
+
* 种子化一致(Story 描述编辑器同为 NotionDocEditor,schema 等价);room 为
|
|
180
|
+
* entity:story:{storyId}。description 由调用方传入(serverFn 本次请求已读的
|
|
181
|
+
* Story.description Text 列值)。返回快照 update 字节。
|
|
182
|
+
*
|
|
183
|
+
* ⚠️ 仅限「协作开始前首次导入」与「旁路写入后的规格授权重种子化」(§5.2);
|
|
184
|
+
* 协作进行中调用会丢协作历史,调用方须遵守三态判定。
|
|
185
|
+
*/
|
|
186
|
+
export async function seedStoryDoc(
|
|
187
|
+
storyId: string,
|
|
188
|
+
description: string | null,
|
|
189
|
+
): Promise<Uint8Array> {
|
|
190
|
+
await ensureServerDom();
|
|
191
|
+
const html = extractStoryDescriptionHtml(description);
|
|
192
|
+
if (!html) {
|
|
193
|
+
throw new Error(`需求描述无内容,无需种子化: ${storyId}`);
|
|
194
|
+
}
|
|
195
|
+
const yDoc = seedYDocFromHtml(html, [
|
|
196
|
+
...createKnowledgeExtensions(),
|
|
197
|
+
...createKnowledgeDocAdditions(),
|
|
198
|
+
]);
|
|
199
|
+
const state = new Uint8Array(Y.encodeStateAsUpdate(yDoc));
|
|
200
|
+
const prisma = await getPrisma();
|
|
201
|
+
await prisma.collabDocument.upsert({
|
|
202
|
+
where: { roomName: buildRoomName({ kind: "entity-story", storyId }) },
|
|
203
|
+
create: {
|
|
204
|
+
roomName: buildRoomName({ kind: "entity-story", storyId }),
|
|
205
|
+
state,
|
|
206
|
+
},
|
|
207
|
+
update: { state },
|
|
208
|
+
});
|
|
209
|
+
return state;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 解析 Story.description(Text 列)为可种子化的 HTML。
|
|
214
|
+
* 存储形态:serializeStoryDescription 的 JSON 字符串({html, markdown},
|
|
215
|
+
* 与 components/story/types.ts parseStoryDescription 读取口径同构——服务端
|
|
216
|
+
* 不 import 组件模块,此处镜像其解析规则);历史裸串数据兜底为整串 html。
|
|
217
|
+
* 返回 null 表示「业务列无内容」(null / 空串 / 编辑器空文档产物
|
|
218
|
+
* `<p></p>`、`<p><br></p>`)——无需种子化,客户端直接建空 Y.Doc。
|
|
219
|
+
*/
|
|
220
|
+
export function extractStoryDescriptionHtml(
|
|
221
|
+
description: string | null,
|
|
222
|
+
): string | null {
|
|
223
|
+
let html: string | null = null;
|
|
224
|
+
if (description) {
|
|
225
|
+
try {
|
|
226
|
+
const parsed = JSON.parse(description) as { html?: unknown };
|
|
227
|
+
if (typeof parsed.html === "string") {
|
|
228
|
+
html = parsed.html;
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
html = description;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!html) return null;
|
|
235
|
+
const trimmed = html.trim();
|
|
236
|
+
if (trimmed === "" || trimmed === "<p></p>" || trimmed === "<p><br></p>") {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return html;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── entity-bug / entity-case 种子化(任务 22,§7.1 entity room 行)────────
|
|
243
|
+
//
|
|
244
|
+
// Bug/TestCase 的 Y 属主面 = description fragment(仅 Bug)+ fields Y.Map +
|
|
245
|
+
// steps Y.Array。种子化只覆盖「有正确单源初值」的面:
|
|
246
|
+
// - description(Bug):与 Story 同一 {html} 提取(Bug.description 与
|
|
247
|
+
// Story.description 存储同构);S12 裁决 UI 本期不接协同编辑,但 room 结构
|
|
248
|
+
// 自然预留——种子化照常(门卫转发的旁路写经 internal-apply 进 Y)。
|
|
249
|
+
// - steps:**服务端必种**——步骤是列表(非 per-key LWW 的字段 Map),客户端
|
|
250
|
+
// 各自从 DTO 初始化会以不同 clientID 重复插入同一批步骤(并发首写重复),
|
|
251
|
+
// 服务端单源种子化是唯一防重复路径(fields 沿用 Story 先例不种——单值
|
|
252
|
+
// per-key LWW 无此问题,Y 未写的 key 由 UI overlay 回退 loader 值)。
|
|
253
|
+
// - 空 Y.Array steps = 「Y 无意见」(业务 schema 步骤 min(1) 空集永不合法),
|
|
254
|
+
// Bug 无描述且无步骤 → 整体无内容可种 → 抛错由调用方判定(返回快照 null)。
|
|
255
|
+
|
|
256
|
+
/** 与 story/知识库种子化完全同一扩展组合(默认集 + 追加集) */
|
|
257
|
+
export function entityDocExtensions() {
|
|
258
|
+
return [...createKnowledgeExtensions(), ...createKnowledgeDocAdditions()];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** 把归一化步骤列表灌入 Y.Doc 的 steps Y.Array(种子化与测试共用) */
|
|
262
|
+
export function fillStepsYDoc(yDoc: Y.Doc, steps: CollabStep[]): void {
|
|
263
|
+
applyStepsToDoc(yDoc, steps);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Bug 服务端种子化(§5.2,任务 22):room 为 entity:bug:{bugId}。html 与
|
|
268
|
+
* steps 由调用方预提取(serverFn 本次请求已读的 Bug.description /
|
|
269
|
+
* reproduceSteps,单次读取);两者皆空抛错(调用方先判定「无内容」)。
|
|
270
|
+
* 返回快照 update 字节。
|
|
271
|
+
*
|
|
272
|
+
* ⚠️ 仅限「协作开始前首次导入」与「旁路写入后的规格授权重种子化」(§5.2);
|
|
273
|
+
* 协作进行中调用会丢协作历史,调用方须遵守三态判定。
|
|
274
|
+
*/
|
|
275
|
+
export async function seedBugDoc(
|
|
276
|
+
bugId: string,
|
|
277
|
+
face: {
|
|
278
|
+
/** 预提取的 description HTML(null = Bug.description 无内容) */
|
|
279
|
+
html: string | null;
|
|
280
|
+
/** 预归一化的步骤列表(null = 无步骤) */
|
|
281
|
+
steps: CollabStep[] | null;
|
|
282
|
+
},
|
|
283
|
+
): Promise<Uint8Array> {
|
|
284
|
+
await ensureServerDom();
|
|
285
|
+
if (!face.html && !face.steps) {
|
|
286
|
+
throw new Error(`BUG 无内容,无需种子化: ${bugId}`);
|
|
287
|
+
}
|
|
288
|
+
const yDoc = face.html ? seedYDocFromHtml(face.html, entityDocExtensions()) : new Y.Doc();
|
|
289
|
+
if (face.steps) fillStepsYDoc(yDoc, face.steps);
|
|
290
|
+
const state = new Uint8Array(Y.encodeStateAsUpdate(yDoc));
|
|
291
|
+
const prisma = await getPrisma();
|
|
292
|
+
await prisma.collabDocument.upsert({
|
|
293
|
+
where: { roomName: buildRoomName({ kind: "entity-bug", bugId }) },
|
|
294
|
+
create: {
|
|
295
|
+
roomName: buildRoomName({ kind: "entity-bug", bugId }),
|
|
296
|
+
state,
|
|
297
|
+
},
|
|
298
|
+
update: { state },
|
|
299
|
+
});
|
|
300
|
+
return state;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* TestCase 服务端种子化(§5.2,任务 22):room 为 entity:case:{caseId}。
|
|
305
|
+
* TestCase 无 description 列——Y 属主面只有 fields(不种,Story 先例)与
|
|
306
|
+
* steps(必种,防并发首写重复);steps 空抛错(调用方先判定「无内容」)。
|
|
307
|
+
* 返回快照 update 字节。
|
|
308
|
+
*
|
|
309
|
+
* ⚠️ 仅限「协作开始前首次导入」与「旁路写入后的规格授权重种子化」(§5.2)。
|
|
310
|
+
*/
|
|
311
|
+
export async function seedCaseDoc(
|
|
312
|
+
caseId: string,
|
|
313
|
+
steps: CollabStep[] | null,
|
|
314
|
+
): Promise<Uint8Array> {
|
|
315
|
+
await ensureServerDom();
|
|
316
|
+
if (!steps) {
|
|
317
|
+
throw new Error(`用例无步骤,无需种子化: ${caseId}`);
|
|
318
|
+
}
|
|
319
|
+
const yDoc = new Y.Doc();
|
|
320
|
+
fillStepsYDoc(yDoc, steps);
|
|
321
|
+
const state = new Uint8Array(Y.encodeStateAsUpdate(yDoc));
|
|
322
|
+
const prisma = await getPrisma();
|
|
323
|
+
await prisma.collabDocument.upsert({
|
|
324
|
+
where: { roomName: buildRoomName({ kind: "entity-case", caseId }) },
|
|
325
|
+
create: {
|
|
326
|
+
roomName: buildRoomName({ kind: "entity-case", caseId }),
|
|
327
|
+
state,
|
|
328
|
+
},
|
|
329
|
+
update: { state },
|
|
330
|
+
});
|
|
331
|
+
return state;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ── design-canvas 种子化(任务 29,§7.1 设计画布行)───────────────────────
|
|
335
|
+
//
|
|
336
|
+
// DesignPage 的 Y 属主面 = canvas Y 树(T26 canvas-tree 契约)+ 文档 fragment
|
|
337
|
+
// (同 room 双载体)。canvas 列为双格式存量(新 LeaferJSONPackage / 旧扁平
|
|
338
|
+
// SceneDocument——saveCanvasNow 优先 engine.getJSON() 回落 sceneDoc 的历史
|
|
339
|
+
// 产物),seedYTreeFromCanvas 双格式兼容(旧格式 = 惰性迁移,写回经导出器
|
|
340
|
+
// 自然升级为权威格式)。
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* 解析 DesignPage.content(Json 列,DocContent {html, markdown} 形态)为可
|
|
344
|
+
* 种子化的 HTML。与 use-design-page-load 的读取口径同构;历史 null / 空产物
|
|
345
|
+
* (`<p></p>`、`<p><br></p>`)→ null(文档面无内容,不种)。
|
|
346
|
+
*/
|
|
347
|
+
export function extractDesignDocHtml(content: unknown): string | null {
|
|
348
|
+
const html =
|
|
349
|
+
typeof content === "object" && content !== null && "html" in content
|
|
350
|
+
? (content as { html?: unknown }).html
|
|
351
|
+
: undefined;
|
|
352
|
+
if (typeof html !== "string") return null;
|
|
353
|
+
const trimmed = html.trim();
|
|
354
|
+
if (trimmed === "" || trimmed === "<p></p>" || trimmed === "<p><br></p>") {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
return html;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** DesignPage.canvas 有可种子化内容(新格式非空 board / 旧格式有用户节点) */
|
|
361
|
+
export function hasDesignCanvasContent(canvas: unknown): boolean {
|
|
362
|
+
return hasCanvasContent(canvas);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Design 画布服务端种子化(§5.2,任务 29):room 为 canvas:design:{pageId}。
|
|
367
|
+
* canvas Y 树 + 文档 fragment 双面可只种其一;皆无内容抛错(调用方先判定
|
|
368
|
+
* 「无内容」——canvas 经 hasDesignCanvasContent、文档经 extractDesignDocHtml)。
|
|
369
|
+
* 返回快照 update 字节。
|
|
370
|
+
*
|
|
371
|
+
* ⚠️ 仅限「协作开始前首次导入」与「旁路写入后的规格授权重种子化」(§5.2);
|
|
372
|
+
* 协作进行中调用会丢协作历史,调用方须遵守三态判定。
|
|
373
|
+
*/
|
|
374
|
+
export async function seedDesignPageDoc(
|
|
375
|
+
pageId: string,
|
|
376
|
+
face: {
|
|
377
|
+
/** DesignPage.canvas 列原值(双格式;null = 无画布面) */
|
|
378
|
+
canvas: unknown;
|
|
379
|
+
/** 预提取的文档 HTML(null = 文档面无内容) */
|
|
380
|
+
html: string | null;
|
|
381
|
+
},
|
|
382
|
+
): Promise<Uint8Array> {
|
|
383
|
+
await ensureServerDom();
|
|
384
|
+
const yDoc = new Y.Doc();
|
|
385
|
+
let seeded = false;
|
|
386
|
+
if (face.canvas !== null && face.canvas !== undefined) {
|
|
387
|
+
seeded = seedYTreeFromCanvas(face.canvas, yDoc) || seeded;
|
|
388
|
+
}
|
|
389
|
+
if (face.html) {
|
|
390
|
+
const docSource = seedYDocFromHtml(face.html, entityDocExtensions());
|
|
391
|
+
Y.applyUpdate(yDoc, Y.encodeStateAsUpdate(docSource));
|
|
392
|
+
seeded = true;
|
|
393
|
+
}
|
|
394
|
+
if (!seeded) {
|
|
395
|
+
throw new Error(`设计页面无内容,无需种子化: ${pageId}`);
|
|
396
|
+
}
|
|
397
|
+
const state = new Uint8Array(Y.encodeStateAsUpdate(yDoc));
|
|
398
|
+
const prisma = await getPrisma();
|
|
399
|
+
await prisma.collabDocument.upsert({
|
|
400
|
+
where: { roomName: buildRoomName({ kind: "design-canvas", pageId }) },
|
|
401
|
+
create: {
|
|
402
|
+
roomName: buildRoomName({ kind: "design-canvas", pageId }),
|
|
403
|
+
state,
|
|
404
|
+
},
|
|
405
|
+
update: { state },
|
|
406
|
+
});
|
|
407
|
+
return state;
|
|
408
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
// 协同 ticket 签发的工具函数(Yjs 多人协作 §4.2,serverFn 纯净纪律的拆分件):
|
|
19
|
+
// serverFns/collab.ts 只保留 createServerFn 产物,guest sub 生成 / 昵称兜底 /
|
|
20
|
+
// avatarBrief 投影 / COLLAB_SECRET 读取 / 分享密码门禁复验在此实现。
|
|
21
|
+
//
|
|
22
|
+
// 仅服务端:依赖 node:crypto 与 share 密码校验链(@allbluecn/database/lib/share-utils),
|
|
23
|
+
// 禁止任何客户端代码 import 本模块。
|
|
24
|
+
|
|
25
|
+
import { randomUUID } from "node:crypto";
|
|
26
|
+
import type { CollabAvatarBrief } from "@allbluecn/collab-core/types";
|
|
27
|
+
import { verifySharePassword } from "@allbluecn/database/lib/share-utils";
|
|
28
|
+
import { getClientIp } from "@allbluecn/kernel";
|
|
29
|
+
import {
|
|
30
|
+
isSharePasswordBlocked,
|
|
31
|
+
recordSharePasswordFailure,
|
|
32
|
+
clearSharePasswordFailures,
|
|
33
|
+
} from "@/server/middlewares/auth-rate-limit";
|
|
34
|
+
|
|
35
|
+
/** ticket 有效期(§4.2:now+60s 覆盖「签发→建连」窗口,重连由客户端重取) */
|
|
36
|
+
export const TICKET_TTL_SECONDS = 60;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 读取 HMAC 签名密钥。缺失/为空一律抛错(配置缺失必须显式失败,
|
|
40
|
+
* 禁止静默用空密钥签名——signTicket 同口径)。
|
|
41
|
+
*/
|
|
42
|
+
export function getCollabSecret(): string {
|
|
43
|
+
const secret = process.env.COLLAB_SECRET;
|
|
44
|
+
if (!secret) {
|
|
45
|
+
throw new Error("COLLAB_SECRET 未配置:无法签发协作 ticket");
|
|
46
|
+
}
|
|
47
|
+
return secret;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 访客主体 id(§4.2:sub 形如 guest:{random},每次签发独立,不落库) */
|
|
51
|
+
export function newGuestSub(): string {
|
|
52
|
+
return `guest:${randomUUID()}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 访客昵称兜底:trim 后为空(未填 / 纯空白)→「访客」(§4.2) */
|
|
56
|
+
export function normalizeNickname(raw?: string | null): string {
|
|
57
|
+
const trimmed = raw?.trim();
|
|
58
|
+
return trimmed ? trimmed : "访客";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* session user → ticket avatarBrief 投影(§4.2:取 session)。
|
|
63
|
+
* session callback 对已登录用户恒回填三字段(DB 或随机生成),此处的
|
|
64
|
+
* 空值兜底仅作类型层防御:avatarConfig→null(协议允许),shape/color→空串
|
|
65
|
+
* (CollabTicketPayload 要求字符串;渲染端按空值回退首字母头像)。
|
|
66
|
+
*/
|
|
67
|
+
export function avatarBriefFromSessionUser(user: {
|
|
68
|
+
avatarConfig?: Record<string, number> | null;
|
|
69
|
+
bgShape?: string | null;
|
|
70
|
+
bgColor?: string | null;
|
|
71
|
+
}): CollabAvatarBrief {
|
|
72
|
+
return {
|
|
73
|
+
avatarConfig: user?.avatarConfig ?? null,
|
|
74
|
+
bgShape: user?.bgShape ?? "",
|
|
75
|
+
bgColor: user?.bgColor ?? "",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 分享密码门禁的客户端 IP(取不到归入 unknown 桶,与 verifySharePasswordFn 同口径) */
|
|
80
|
+
export function shareGateClientIp(request: Request): string | null {
|
|
81
|
+
try {
|
|
82
|
+
return getClientIp(request);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 分享密码门禁复验(§4.2「密码已过」的服务端判定,与 verifySharePasswordFn
|
|
90
|
+
* 复用同一校验原语与 SEC-10 限流:blocked 短路不触达校验;失败计数、成功清零)。
|
|
91
|
+
* 密码缺失或 Share 无哈希存储一律 fail-closed 返回 false。
|
|
92
|
+
*/
|
|
93
|
+
export async function verifyShareGatePassword(opts: {
|
|
94
|
+
ip: string | null;
|
|
95
|
+
password?: string;
|
|
96
|
+
storedPassword: string | null;
|
|
97
|
+
}): Promise<boolean> {
|
|
98
|
+
if (isSharePasswordBlocked(opts.ip)) return false;
|
|
99
|
+
if (!opts.password || !opts.storedPassword) return false;
|
|
100
|
+
const ok = await verifySharePassword(opts.password, opts.storedPassword);
|
|
101
|
+
if (ok) {
|
|
102
|
+
clearSharePasswordFailures(opts.ip);
|
|
103
|
+
} else {
|
|
104
|
+
recordSharePasswordFailure(opts.ip);
|
|
105
|
+
}
|
|
106
|
+
return ok;
|
|
107
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
// R11 touch 不变式的共享实现(审查修复):web 侧对业务列的直写若**不含 Y
|
|
19
|
+
// 属主面**(forwarded 组合 patch 剥离后的纯元数据写、或从不归协同 room 的
|
|
20
|
+
// 字段写),@updatedAt 仍会被 bump——不回同步快照则 collab-snapshot 三态
|
|
21
|
+
// 判定(businessUpdatedAt <= snapshot.updatedAt → 直读)误判旁路写,下次
|
|
22
|
+
// 协同打开全量重种子化、Y 协作历史重置。同事务 touch 快照 updatedAt = 回读
|
|
23
|
+
// 业务行 updatedAt,保持「快照不落后于非协同写」。
|
|
24
|
+
//
|
|
25
|
+
// ⚠ 调用方判定:仅「生效直写不含 Y 属主面」时调用;含 Y 属主面的直写
|
|
26
|
+
// (pass-through/degraded 落 content/fields/steps/canvas)**刻意不 touch**
|
|
27
|
+
// ——旁路写必须让业务列反超快照,重种子化兜底才有正确输入。先例:design
|
|
28
|
+
// pages.ts updateDesignPageFn(web 侧镜像 T21 锚点 M② exporter touch)。
|
|
29
|
+
//
|
|
30
|
+
// 工具函数文件(非 serverFn):仅由 serverFn handler / 服务器模块引用;
|
|
31
|
+
// updateMany 对不存在快照计 0 天然跳过(不误建行,无快照文档零成本)。
|
|
32
|
+
|
|
33
|
+
import type { Prisma } from "@allbluecn/database";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 同事务 touch 协同快照 updatedAt = 业务行回读 updatedAt。
|
|
37
|
+
*
|
|
38
|
+
* @param tx 业务写入所在的 Prisma 事务客户端(必须与业务写同一事务——
|
|
39
|
+
* 分离提交时 crash 窗口会留下 businessUpdatedAt > snapshot.updatedAt,
|
|
40
|
+
* 触发伪旁路写重种子化)
|
|
41
|
+
* @param roomName §3.1 注册表 room 名(buildRoomName 产物)
|
|
42
|
+
* @param businessUpdatedAt 业务行写后回读的 updatedAt(@updatedAt bump 后值)
|
|
43
|
+
* @returns updateMany 计数(无快照 = 0,天然跳过)
|
|
44
|
+
*/
|
|
45
|
+
export async function touchCollabSnapshot(
|
|
46
|
+
tx: Prisma.TransactionClient,
|
|
47
|
+
roomName: string,
|
|
48
|
+
businessUpdatedAt: Date,
|
|
49
|
+
): Promise<number> {
|
|
50
|
+
const result = await tx.collabDocument.updateMany({
|
|
51
|
+
where: { roomName },
|
|
52
|
+
data: { updatedAt: businessUpdatedAt },
|
|
53
|
+
});
|
|
54
|
+
return result.count;
|
|
55
|
+
}
|