@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,35 @@
|
|
|
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
|
+
import { describe, expect, it } from "vitest";
|
|
19
|
+
import { manualOrderSwitchPatch } from "../manual-order";
|
|
20
|
+
|
|
21
|
+
describe("manualOrderSwitchPatch", () => {
|
|
22
|
+
it("拖拽落库后按非手动排序展示时,切换为手动排序", () => {
|
|
23
|
+
expect(manualOrderSwitchPatch("created", true)).toEqual({ orderBy: "manual" });
|
|
24
|
+
expect(manualOrderSwitchPatch("updated", true)).toEqual({ orderBy: "manual" });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("已是手动排序时不重复切换(避免 toast 噪音)", () => {
|
|
28
|
+
expect(manualOrderSwitchPatch("manual", true)).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("没有实际落库排序时不切换", () => {
|
|
32
|
+
expect(manualOrderSwitchPatch("created", false)).toBeNull();
|
|
33
|
+
expect(manualOrderSwitchPatch("manual", false)).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,212 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* 看板拖拽提交的决策层(UI 对齐二期设计 §4.5 / 复审 P1-8)。
|
|
20
|
+
*
|
|
21
|
+
* 从 story/views/kanban-view.tsx 的 drop 处理逻辑逐段参数化抽取:
|
|
22
|
+
* - resolveDropCommit:提交元数据 → 决策(跨列 move / 列内 reorder / 忽略)
|
|
23
|
+
* - commitDropDecision:决策 → 落库管线(成功延迟刷新 / 失败回滚 + toast)
|
|
24
|
+
* jsdom 不模拟 dnd-kit 真拖,拖拽语义测试走本文件导出的纯函数(任务 26/33/42 消费)。
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { toast } from "@/components/ui/toast";
|
|
28
|
+
import { toastError } from "@/lib/toast-error";
|
|
29
|
+
import { m } from "@/paraglide/messages.js";
|
|
30
|
+
import { storyIdFromInstanceKey } from "./instance-key";
|
|
31
|
+
|
|
32
|
+
/** 列内排序落库产物:目标列全列索引(boardOrder = 提交后数组下标) */
|
|
33
|
+
export interface ReorderEntry {
|
|
34
|
+
id: string;
|
|
35
|
+
boardOrder: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type DropCommitDecision =
|
|
39
|
+
| { kind: "ignore"; reason: "notDroppable" | "noTarget" }
|
|
40
|
+
/** 跨列移动:onDropToColumn(itemKey, columnKey) 产物;subKey 为目标子分组(启用子分组时) */
|
|
41
|
+
| {
|
|
42
|
+
kind: "move";
|
|
43
|
+
itemKey: string;
|
|
44
|
+
columnKey: string;
|
|
45
|
+
subKey?: string;
|
|
46
|
+
orders: ReorderEntry[];
|
|
47
|
+
}
|
|
48
|
+
/** 列内排序:manualOrder 开启时触发 onReorder(全列索引);
|
|
49
|
+
* subKey = 同列跨子分组时按提交后排列解析出的子组归属(任务 42 必带③,蓝本
|
|
50
|
+
* story kanban-view 同列拖子组也写子组字段),经 onSubGroupMove 落库 */
|
|
51
|
+
| { kind: "reorder"; itemKey: string; columnKey: string; subKey?: string; orders: ReorderEntry[] };
|
|
52
|
+
|
|
53
|
+
export interface DropCommitParams {
|
|
54
|
+
/** dnd-kit active id(实例 key 或裸条目 id,均可解析) */
|
|
55
|
+
activeId: string;
|
|
56
|
+
activeContainer: string;
|
|
57
|
+
overContainer: string;
|
|
58
|
+
/** 提交后的乐观列(被拖卡已在新位置,取其排列推导 boardOrder 与子分组归属) */
|
|
59
|
+
nextValue: Record<string, ReadonlyArray<{ id: string }>>;
|
|
60
|
+
/**
|
|
61
|
+
* 列是否可放置(复审 P1-1,缺省 true)。false 的列上的任何 drop 提交
|
|
62
|
+
* (含列内)都被忽略——labels 等多值维度分组禁拖用。
|
|
63
|
+
*/
|
|
64
|
+
columnDroppable?: (key: string) => boolean;
|
|
65
|
+
/** 子分组成员表(提交后的本地值);缺省 = 未启用子分组,不解析 subKey */
|
|
66
|
+
membership?: Record<string, string>;
|
|
67
|
+
/** 拖拽落点 hover 的实例 id(lastOver 通道,供子分组归属判定) */
|
|
68
|
+
overItemId?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 落点所属子分组(回落规则):取「紧随其后的卡片」所属子分组(提交后的排列里被拖卡也在其中),
|
|
73
|
+
* 追加到列尾(其后无卡)时取前一张,最后回落到该卡自身。
|
|
74
|
+
*
|
|
75
|
+
* 提交时必须用这个而不是「成员表里的旧值」:成员表由渲染期同步,drop 那一帧还是旧值
|
|
76
|
+
* → 跨子分组拖动时判定「目标分区 == 原分区」而不发 patch(story kanban-drop-subgroup 回归)。
|
|
77
|
+
*/
|
|
78
|
+
export function resolveDropSubKey(
|
|
79
|
+
items: ReadonlyArray<{ id: string }>,
|
|
80
|
+
itemId: string,
|
|
81
|
+
membership: Record<string, string>,
|
|
82
|
+
): string | undefined {
|
|
83
|
+
const index = items.findIndex((story) => story.id === itemId);
|
|
84
|
+
if (index === -1) return undefined;
|
|
85
|
+
return (
|
|
86
|
+
(items[index + 1] ? membership[items[index + 1].id] : undefined) ??
|
|
87
|
+
(items[index - 1] ? membership[items[index - 1].id] : undefined) ??
|
|
88
|
+
membership[itemId]
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 落点子分组优先取「hover 到的那张卡」所属分组:
|
|
94
|
+
* hover 在某分组最后一张卡的下半区 → 插入位等于下一分组首张卡的下标,
|
|
95
|
+
* 但语义是追加到 hover 卡所在分组,否则会落进下一个分组(小分组尤其明显)。
|
|
96
|
+
* 没有 hover 卡(落在列空白/列尾)时回落到 resolveDropSubKey 相邻卡推断。
|
|
97
|
+
*/
|
|
98
|
+
export function resolveSubKeyForDrop(
|
|
99
|
+
items: ReadonlyArray<{ id: string }>,
|
|
100
|
+
itemId: string,
|
|
101
|
+
membership: Record<string, string>,
|
|
102
|
+
overKey: string,
|
|
103
|
+
): string | undefined {
|
|
104
|
+
const overItemId = overKey ? storyIdFromInstanceKey(overKey) : "";
|
|
105
|
+
if (overItemId && membership[overItemId] != null) return membership[overItemId];
|
|
106
|
+
return resolveDropSubKey(items, itemId, membership);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 从提交元数据解析 drop 决策(纯函数,不改任何状态) */
|
|
110
|
+
export function resolveDropCommit(params: DropCommitParams): DropCommitDecision {
|
|
111
|
+
const itemKey = storyIdFromInstanceKey(params.activeId);
|
|
112
|
+
const destKey = params.overContainer;
|
|
113
|
+
if (!destKey) return { kind: "ignore", reason: "noTarget" };
|
|
114
|
+
if (params.columnDroppable && !params.columnDroppable(destKey)) {
|
|
115
|
+
return { kind: "ignore", reason: "notDroppable" };
|
|
116
|
+
}
|
|
117
|
+
const destItems = params.nextValue[destKey] ?? [];
|
|
118
|
+
const orders: ReorderEntry[] = destItems.map((entry, index) => ({
|
|
119
|
+
id: entry.id,
|
|
120
|
+
boardOrder: index,
|
|
121
|
+
}));
|
|
122
|
+
if (params.activeContainer !== destKey) {
|
|
123
|
+
const subKey = params.membership
|
|
124
|
+
? resolveSubKeyForDrop(
|
|
125
|
+
destItems,
|
|
126
|
+
itemKey,
|
|
127
|
+
params.membership,
|
|
128
|
+
params.overItemId ?? "",
|
|
129
|
+
)
|
|
130
|
+
: undefined;
|
|
131
|
+
return { kind: "move", itemKey, columnKey: destKey, subKey, orders };
|
|
132
|
+
}
|
|
133
|
+
// 同列(reorder)也解析子组归属:moveAcross 维度下同列跨子分组拖拽同样要落
|
|
134
|
+
// 子组字段(蓝本 story kanban-view 口径,任务 42 必带③);纯组内排序解析出的
|
|
135
|
+
// subKey == 本地成员表现值,消费方权威比对后跳写
|
|
136
|
+
const subKey = params.membership
|
|
137
|
+
? resolveSubKeyForDrop(destItems, itemKey, params.membership, params.overItemId ?? "")
|
|
138
|
+
: undefined;
|
|
139
|
+
return { kind: "reorder", itemKey, columnKey: destKey, subKey, orders };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface CommitDropDeps<T extends { id: string }> {
|
|
143
|
+
decision: DropCommitDecision;
|
|
144
|
+
/** meta.previousValue:忽略 / 失败时的乐观列回滚值 */
|
|
145
|
+
previousValue: Record<string, T[]>;
|
|
146
|
+
setColumns: (value: Record<string, T[]>) => void;
|
|
147
|
+
onDropToColumn: (
|
|
148
|
+
itemKey: string,
|
|
149
|
+
columnKey: string,
|
|
150
|
+
meta?: { subKey?: string },
|
|
151
|
+
) => Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* 同列跨子分组拖拽(reorder 决策带 subKey)的子组字段落库通道
|
|
154
|
+
* (任务 42 必带③)。在 onReorder 之前调用(蓝本:字段 patch 先于排序落库);
|
|
155
|
+
* subKey 已与组件本地乐观成员表自洽,是否真写(与权威成员表同值跳写)与
|
|
156
|
+
* 成功 toast 由消费方负责。缺省不落库。
|
|
157
|
+
*/
|
|
158
|
+
onSubGroupMove?: (itemKey: string, columnKey: string, subKey: string) => Promise<void>;
|
|
159
|
+
manualOrder?: {
|
|
160
|
+
onReorder: (orders: ReorderEntry[]) => Promise<void>;
|
|
161
|
+
onAutoSwitchManual?: () => Promise<void>;
|
|
162
|
+
};
|
|
163
|
+
/** 成功后的刷新(组件传 900ms 延迟静默包装,见 KanbanBoardView) */
|
|
164
|
+
onSuccess?: () => void;
|
|
165
|
+
/** 失败时的立即权威刷新 */
|
|
166
|
+
onRefresh?: () => void;
|
|
167
|
+
/** move 成功 toast 的落点文案(列 label,含子分组时「列-子组」) */
|
|
168
|
+
targetLabel?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 提交管线(蓝本 handleValueCommit 的参数化形态):决策 → 落库 → 成功刷新 / 失败回滚。
|
|
173
|
+
*
|
|
174
|
+
* - move:onDropToColumn → 成功 toast → manualOrder.onReorder(全列索引)→ onAutoSwitchManual
|
|
175
|
+
* (是否需要切手动排序 + 提示由消费方判断,共享层不掌握排序键)
|
|
176
|
+
* - 失败:乐观列回滚 previousValue(即刻)+ onRefresh(权威源)+ toast.error
|
|
177
|
+
* - ignore:仅回滚(拖拽途中可能已把卡乐观移入禁拖列)
|
|
178
|
+
*/
|
|
179
|
+
export async function commitDropDecision<T extends { id: string }>(
|
|
180
|
+
deps: CommitDropDeps<T>,
|
|
181
|
+
): Promise<void> {
|
|
182
|
+
const { decision } = deps;
|
|
183
|
+
if (decision.kind === "ignore") {
|
|
184
|
+
deps.setColumns(deps.previousValue);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
if (decision.kind === "move") {
|
|
189
|
+
await deps.onDropToColumn(
|
|
190
|
+
decision.itemKey,
|
|
191
|
+
decision.columnKey,
|
|
192
|
+
decision.subKey != null ? { subKey: decision.subKey } : undefined,
|
|
193
|
+
);
|
|
194
|
+
toast.success(
|
|
195
|
+
m.list_kit_kanban_movedTo({ target: deps.targetLabel ?? decision.columnKey }),
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (decision.kind === "reorder" && decision.subKey != null && deps.onSubGroupMove) {
|
|
199
|
+
// 同列跨子分组:子组字段先落库(蓝本顺序),失败走统一回滚分支
|
|
200
|
+
await deps.onSubGroupMove(decision.itemKey, decision.columnKey, decision.subKey);
|
|
201
|
+
}
|
|
202
|
+
if (deps.manualOrder && decision.orders.length > 0) {
|
|
203
|
+
await deps.manualOrder.onReorder(decision.orders);
|
|
204
|
+
await deps.manualOrder.onAutoSwitchManual?.();
|
|
205
|
+
}
|
|
206
|
+
deps.onSuccess?.();
|
|
207
|
+
} catch (e) {
|
|
208
|
+
deps.setColumns(deps.previousValue);
|
|
209
|
+
deps.onRefresh?.();
|
|
210
|
+
toastError(e, m.list_kit_kanban_moveFailed());
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
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 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
|
+
/** 落点布局的纯计算(与渲染无关,便于单测) */
|
|
19
|
+
|
|
20
|
+
export interface DropLayoutRow {
|
|
21
|
+
/** 行 key:卡片用 itemKey、子分组标题用 `h:<runKey>`(与渲染行一致) */
|
|
22
|
+
key: string;
|
|
23
|
+
/**
|
|
24
|
+
* 行类型。子分组标题也必须参与让位,否则落点在下半区时卡片会与标题重合
|
|
25
|
+
* (用户反馈:拖动时标题没跟着卡片走)。
|
|
26
|
+
*/
|
|
27
|
+
kind: "card" | "header";
|
|
28
|
+
/** 被折叠的子分组:卡片不渲染,但仍占用数据下标(与 primitive 的 insertIndex 对齐) */
|
|
29
|
+
hidden?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DropLayout {
|
|
33
|
+
/** 需要让位的行(卡片/标题)→ translateY 位移(px) */
|
|
34
|
+
shifts: Map<string, number>;
|
|
35
|
+
/** 落点槽渲染在其上方的卡片 */
|
|
36
|
+
slotBefore: Set<string>;
|
|
37
|
+
/** 落点槽渲染在其下方的卡片(落点在末位时使用;无可见卡则为 null) */
|
|
38
|
+
slotAfter: string | null;
|
|
39
|
+
/** 落点槽渲染在其上方的子分组标题(「追加到上一子分组末尾」时,槽应在标题之上) */
|
|
40
|
+
slotBeforeHeader: string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 计算拖拽落点的让位与插槽挂点。
|
|
45
|
+
*
|
|
46
|
+
* 让位语义:数据下标 >= insertIndex 的卡片统一下移 `step`(= 拖拽卡高 + 卡间距),
|
|
47
|
+
* 于是 `insertIndex` 处让出一块刚好容下落点槽的空隙。不区分跨列/同列——同列内
|
|
48
|
+
* 源卡也参与让位,视觉上等价于「卡片依次让开」,且不会出现重叠。
|
|
49
|
+
*/
|
|
50
|
+
export interface DropLayoutOptions {
|
|
51
|
+
/**
|
|
52
|
+
* 落点归属「上一子分组」(hover 在某分组最后一张卡的下半区 → 追加到该分组末尾)。
|
|
53
|
+
* 此时插入位恰好是下一分组首张卡的下标,但语义上应插在下一分组的**标题之前**,
|
|
54
|
+
* 所以该边界标题也要让位、落点槽挂在它上方(否则槽位与最终落位差一个标题行)。
|
|
55
|
+
*/
|
|
56
|
+
appendToPreviousRun?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function computeDropLayout(
|
|
60
|
+
rows: DropLayoutRow[],
|
|
61
|
+
drop: { insertIndex: number } | null,
|
|
62
|
+
stepPx: number,
|
|
63
|
+
options?: DropLayoutOptions,
|
|
64
|
+
): DropLayout {
|
|
65
|
+
const appendToPreviousRun = options?.appendToPreviousRun ?? false;
|
|
66
|
+
const shifts = new Map<string, number>();
|
|
67
|
+
const slotBefore = new Set<string>();
|
|
68
|
+
let slotAfter: string | null = null;
|
|
69
|
+
let slotBeforeHeader: string | null = null;
|
|
70
|
+
if (!drop) return { shifts, slotBefore, slotAfter, slotBeforeHeader };
|
|
71
|
+
|
|
72
|
+
let cardIndex = 0;
|
|
73
|
+
let lastVisibleKey: string | null = null;
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
if (row.kind === "header") {
|
|
76
|
+
// 标题位于插入点「之后」(其前方卡片数 > 插入位)才让位:插到某标题下第一张卡之前时,
|
|
77
|
+
// 该标题留在原位,让出的空隙正好在标题与卡片之间。
|
|
78
|
+
// 例外:落点归属上一子分组(追加到其末尾)时,边界标题(cardIndex === 插入位)也要让位,
|
|
79
|
+
// 空隙与落点槽都在该标题之上。
|
|
80
|
+
if (cardIndex > drop.insertIndex) {
|
|
81
|
+
shifts.set(row.key, stepPx);
|
|
82
|
+
} else if (appendToPreviousRun && cardIndex === drop.insertIndex) {
|
|
83
|
+
shifts.set(row.key, stepPx);
|
|
84
|
+
slotBeforeHeader = row.key;
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (cardIndex >= drop.insertIndex) shifts.set(row.key, stepPx);
|
|
89
|
+
// 归属上一子分组末尾时,落点槽由边界标题承担(见上方 header 分支);
|
|
90
|
+
// 这里若同时给锚卡加槽,会出现两个淡化卡上下错位叠在一起。
|
|
91
|
+
if (cardIndex === drop.insertIndex && !appendToPreviousRun) slotBefore.add(row.key);
|
|
92
|
+
if (!row.hidden) lastVisibleKey = row.key;
|
|
93
|
+
cardIndex += 1;
|
|
94
|
+
}
|
|
95
|
+
// 落点在末位(含拖到空列 / 列尾):槽挂在最后一张可见卡下方
|
|
96
|
+
if (drop.insertIndex >= cardIndex) slotAfter = lastVisibleKey;
|
|
97
|
+
return { shifts, slotBefore, slotAfter, slotBeforeHeader };
|
|
98
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 看板列内「吸附子分组头」判定。
|
|
20
|
+
*
|
|
21
|
+
* 列内只有一级子分组(父分组 = 列头 CardHeader,恒定可见、不参与列内滚动),
|
|
22
|
+
* 但行是绝对定位的虚拟化行,CSS sticky 不生效,故用「等价换算」得到原生
|
|
23
|
+
* sticky 的观感。
|
|
24
|
+
*
|
|
25
|
+
* **锚点必须是滚动容器顶边(scrollport top),不是内容盒顶**:滚动容器有
|
|
26
|
+
* p-2 上内边距,行测量起点(内容盒坐标系)比容器坐标系低
|
|
27
|
+
* SUB_HEADER_STICKY_PAD(8)px。活跃阈值 / 吸附位 / 顶出量三者必须同锚 ——
|
|
28
|
+
* 历史实现的锚点在内容盒顶(吸附位、阈值、顶出量都少算 8px),等价于把吸附层
|
|
29
|
+
* 下移 8px:组头本身已随内容上滑 8px 时吸附层仍停在原位,于是在「几乎没滚动」
|
|
30
|
+
* 的区间里就把紧贴组头底边的首张卡上边框盖掉(用户反馈「上边框还是被裁了」)。
|
|
31
|
+
*
|
|
32
|
+
* - 组头未滚到容器顶边时返回 null(在流渲染,自己自然上滑,吸附层完全不参与)
|
|
33
|
+
* - 越过容器顶边后吸附在容器顶(offset = 0)
|
|
34
|
+
* - 下一个组头逼近时被顶出(offset 为负),与原生 sticky 被顶开的位移一致
|
|
35
|
+
*
|
|
36
|
+
* 边界(容器坐标恰好 0)不留容差:此刻吸附层与在流组头**像素重合**,切换无视觉跳变;
|
|
37
|
+
* 留 1px 容差反而会让吸附层比在流组头高 1px(同一行画两遍、错开 1px)。
|
|
38
|
+
*
|
|
39
|
+
* @param scrollTop 列内滚动容器的 scrollTop
|
|
40
|
+
* @param measurements 虚拟化行测量(start 以内容盒顶端为原点,size 为行高)
|
|
41
|
+
* @param subHeaderIndexes 子分组头行在 rows 中的下标(升序)
|
|
42
|
+
* @returns 活跃子分组头的行下标与位移;未越过容器顶边 / 无可用测量时返回 null
|
|
43
|
+
*/
|
|
44
|
+
export interface FrozenSubHeader {
|
|
45
|
+
/** 活跃子分组头的行下标 */
|
|
46
|
+
index: number;
|
|
47
|
+
/** 相对容器顶边的纵向位移(px,<= 0):被下一个组头顶出时为负 */
|
|
48
|
+
offset: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 列内滚动容器上内边距(= kanban-view.tsx 滚动容器的 p-2 与吸附层内容盒的 px-2)。
|
|
52
|
+
* 吸附层挂在容器坐标系(top-0 = 容器顶边),行测量 start 在内容盒坐标系,
|
|
53
|
+
* 判定阈值 / 吸附位 / 顶出量必须同时计入本值,否则吸附层早 8px 落下、裁掉首卡上边框。 */
|
|
54
|
+
export const SUB_HEADER_STICKY_PAD = 8;
|
|
55
|
+
|
|
56
|
+
export function computeFrozenSubHeader(
|
|
57
|
+
scrollTop: number,
|
|
58
|
+
measurements: ReadonlyArray<{ start: number; size: number } | undefined>,
|
|
59
|
+
subHeaderIndexes: readonly number[],
|
|
60
|
+
): FrozenSubHeader | null {
|
|
61
|
+
// overscroll(macOS 橡皮筋)下 scrollTop 可能为负,钳制避免首个子组头失效
|
|
62
|
+
const top = Math.max(0, scrollTop);
|
|
63
|
+
// 换算到内容盒坐标系:组头上缘到达容器顶边 ⇔ m.start <= top - SUB_HEADER_STICKY_PAD
|
|
64
|
+
const threshold = top - SUB_HEADER_STICKY_PAD;
|
|
65
|
+
|
|
66
|
+
let active = -1;
|
|
67
|
+
let activePos = -1;
|
|
68
|
+
for (let k = 0; k < subHeaderIndexes.length; k++) {
|
|
69
|
+
const m = measurements[subHeaderIndexes[k]];
|
|
70
|
+
if (m && m.start <= threshold) {
|
|
71
|
+
active = subHeaderIndexes[k];
|
|
72
|
+
activePos = k;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (active < 0) return null;
|
|
76
|
+
|
|
77
|
+
const activeSize = measurements[active]?.size ?? 0;
|
|
78
|
+
let offset = 0;
|
|
79
|
+
for (let k = activePos + 1; k < subHeaderIndexes.length; k++) {
|
|
80
|
+
const m = measurements[subHeaderIndexes[k]];
|
|
81
|
+
if (!m) continue;
|
|
82
|
+
// 下一个组头上缘(容器坐标 start - threshold)进入「容器顶边 + 当前组头高」时开始上推
|
|
83
|
+
offset = Math.min(0, m.start - threshold - activeSize);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { index: active, offset };
|
|
88
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
/** 同一条目可同时落在多个标签列,而 dnd-kit 要求 droppable/draggable id 全局唯一。
|
|
19
|
+
* 渲染层用「列 key + 条目 id」合成实例 id,拖拽提交时再解析回条目 id。
|
|
20
|
+
* 泛型化(UI 对齐二期设计 §4.5):条目类型只约束 `id: string`,与 story 解耦,
|
|
21
|
+
* 供 testcase / bug 等模块的看板复用(函数名沿用 story 时期的口径)。 */
|
|
22
|
+
export type KanbanStoryItem<T extends { id: string } = { id: string }> = T & {
|
|
23
|
+
__instanceKey?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const INSTANCE_SEP = "::";
|
|
27
|
+
|
|
28
|
+
export function instanceKeyOf(columnKey: string, itemId: string): string {
|
|
29
|
+
return `${columnKey}${INSTANCE_SEP}${itemId}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 列 key 本身可能含 `::`(如 `label::xxx`),故从最后一个分隔符切分 */
|
|
33
|
+
export function storyIdFromInstanceKey(id: string): string {
|
|
34
|
+
const at = id.lastIndexOf(INSTANCE_SEP);
|
|
35
|
+
return at === -1 ? id : id.slice(at + INSTANCE_SEP.length);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 渲染层 id:优先用合成实例 id */
|
|
39
|
+
export function itemKey<T extends { id: string }>(item: T): string {
|
|
40
|
+
return (item as KanbanStoryItem<T>).__instanceKey ?? item.id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 按当前列归属重写实例 id:对象已归属正确列时保持原引用(卡片 memo 依赖引用稳定)。
|
|
44
|
+
* `frozenKey` 为拖拽中的活动卡片实例 id——vendored Kanban 在 onDragOver 就提交跨列移动,
|
|
45
|
+
* 此刻若跟着重算,dnd-kit 手里的 active id(拖拽开始时捕获)会从目标列 SortableContext 中
|
|
46
|
+
* 消失(其余卡片不让位),overlay 也查不到条目(浮动卡片消失)。故拖拽期冻结该卡的 id。 */
|
|
47
|
+
export function applyInstanceKeys<T extends { id: string }>(
|
|
48
|
+
columns: Record<string, T[]>,
|
|
49
|
+
frozenKey?: string | null,
|
|
50
|
+
): Record<string, KanbanStoryItem<T>[]> {
|
|
51
|
+
const keyed: Record<string, KanbanStoryItem<T>[]> = {};
|
|
52
|
+
for (const [columnKey, items] of Object.entries(columns)) {
|
|
53
|
+
let changed = false;
|
|
54
|
+
const mapped: KanbanStoryItem<T>[] = items.map((item) => {
|
|
55
|
+
const keyedItem = item as KanbanStoryItem<T>;
|
|
56
|
+
const instanceKey =
|
|
57
|
+
frozenKey != null && keyedItem.__instanceKey === frozenKey
|
|
58
|
+
? frozenKey
|
|
59
|
+
: instanceKeyOf(columnKey, item.id);
|
|
60
|
+
if (keyedItem.__instanceKey === instanceKey) return keyedItem;
|
|
61
|
+
changed = true;
|
|
62
|
+
return { ...item, __instanceKey: instanceKey };
|
|
63
|
+
});
|
|
64
|
+
// 未发生改写的列保留原数组引用:下游按列 memo 与 SortableContext 都以此为 bailout 依据
|
|
65
|
+
keyed[columnKey] = changed ? mapped : (items as KanbanStoryItem<T>[]);
|
|
66
|
+
}
|
|
67
|
+
return keyed;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** overlay 按 dnd-kit 的 active id 反查卡片:需同时索引原始条目 id 与实例 id
|
|
71
|
+
* (拖拽期活动卡片的实例 id 被冻结为出发列 key,已不等于其当前所在列的 key) */
|
|
72
|
+
export function buildStoryIndex<T extends { id: string }>(
|
|
73
|
+
keyed: Record<string, KanbanStoryItem<T>[]>,
|
|
74
|
+
): Record<string, T> {
|
|
75
|
+
const map: Record<string, T> = {};
|
|
76
|
+
for (const items of Object.values(keyed)) {
|
|
77
|
+
for (const item of items) {
|
|
78
|
+
map[item.id] = item;
|
|
79
|
+
map[itemKey(item)] = item;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return map;
|
|
83
|
+
}
|