@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.
Files changed (761) hide show
  1. package/package.json +66 -51
  2. package/src/__tests__/components/auth/auth-layout.test.tsx +13 -1
  3. package/src/__tests__/{knowledge-public → knowledge/public}/canonical-id.test.ts +1 -1
  4. package/src/__tests__/{knowledge-public → knowledge/public}/favicon.test.ts +1 -1
  5. package/src/__tests__/{knowledge-public → knowledge/public}/github-sync.test.ts +1 -1
  6. package/src/__tests__/{knowledge-public → knowledge/public}/share-merge.test.ts +7 -7
  7. package/src/__tests__/security/xss-sanitize.test.ts +9 -8
  8. package/src/components/ai/__tests__/chat-thread-id-stability.test.tsx +125 -0
  9. package/src/components/ai/ai-chat-prompt.tsx +8 -2
  10. package/src/components/ai/ai-prompt-input/index.tsx +2 -1
  11. package/src/components/ai/global-harness-sidebar/__tests__/global-ai-prompts.test.ts +1 -2
  12. package/src/components/ai/global-harness-sidebar/index.tsx +22 -10
  13. package/src/components/bug/__tests__/board-subgroups.test.tsx +512 -0
  14. package/src/components/bug/__tests__/board-view.test.tsx +350 -0
  15. package/src/components/bug/__tests__/bug-list-loading-defer.test.tsx +85 -0
  16. package/src/components/bug/__tests__/bug-toolbar.test.tsx +7 -1
  17. package/src/components/bug/__tests__/comment-reactions.test.tsx +182 -0
  18. package/src/components/bug/__tests__/date-and-lightbox.test.tsx +96 -0
  19. package/src/components/bug/__tests__/export-entry.test.tsx +217 -0
  20. package/src/components/bug/__tests__/filters-compat.test.tsx +157 -0
  21. package/src/components/bug/__tests__/fix-request-race.test.tsx +396 -0
  22. package/src/components/bug/__tests__/peek-modes.test.tsx +365 -0
  23. package/src/components/bug/__tests__/saved-views.test.tsx +309 -0
  24. package/src/components/bug/__tests__/story-bugs-panel.test.tsx +146 -0
  25. package/src/components/bug/__tests__/subgroup-header.test.tsx +150 -0
  26. package/src/components/bug/__tests__/toolbar-shell.test.tsx +369 -0
  27. package/src/components/bug/__tests__/use-paged-bug-groups.test.tsx +58 -0
  28. package/src/components/bug/__tests__/view-type.test.tsx +247 -0
  29. package/src/components/bug/bug-list-view.tsx +310 -13
  30. package/src/components/bug/bug-panel-header.tsx +84 -8
  31. package/src/components/bug/bug-toolbar.tsx +268 -116
  32. package/src/components/bug/create-bug-dialog.tsx +1 -1
  33. package/src/components/bug/detail/__tests__/activity-timeline.test.tsx +1 -0
  34. package/src/components/bug/detail/__tests__/bug-detail-collab.test.tsx +236 -0
  35. package/src/components/bug/detail/__tests__/repro-report-card.test.tsx +2 -1
  36. package/src/components/bug/detail/activity-timeline.tsx +1 -1
  37. package/src/components/bug/detail/bug-detail-body.tsx +316 -18
  38. package/src/components/bug/detail/comment-item.tsx +7 -2
  39. package/src/components/bug/detail/comment-reactions.tsx +138 -0
  40. package/src/components/bug/detail/error-context.tsx +29 -9
  41. package/src/components/bug/detail/props-section.tsx +31 -3
  42. package/src/components/bug/detail/repro-report-card.tsx +1 -1
  43. package/src/components/bug/display/constants.ts +4 -2
  44. package/src/components/bug/display/display-dropdown.tsx +76 -280
  45. package/src/components/bug/display/types.ts +24 -8
  46. package/src/components/bug/display/use-paged-bug-groups.ts +183 -32
  47. package/src/components/bug/filters/{filter-value-input.tsx → field-configs.tsx} +218 -106
  48. package/src/components/bug/filters/index.tsx +14 -5
  49. package/src/components/bug/inline-editors/assignee-editor.tsx +2 -2
  50. package/src/components/bug/inline-editors/labels-editor.tsx +1 -1
  51. package/src/components/bug/inline-editors/severity-editor.tsx +37 -18
  52. package/src/components/bug/inline-editors/state-editor.tsx +1 -1
  53. package/src/components/bug/peek-panel.tsx +153 -35
  54. package/src/components/bug/select-bug-dialog.tsx +500 -0
  55. package/src/components/bug/story-bugs-panel.tsx +1090 -0
  56. package/src/components/bug/types.ts +9 -2
  57. package/src/components/bug/views/board-view.tsx +661 -0
  58. package/src/components/bug/views/bug-list/batch-toolbar.tsx +25 -34
  59. package/src/components/bug/views/bug-list/group.tsx +28 -13
  60. package/src/components/bug/views/bug-list/index.tsx +223 -59
  61. package/src/components/bug/views/bug-list/row.tsx +7 -1
  62. package/src/components/bug/views/create-view-dialog.tsx +160 -0
  63. package/src/components/bug/views/view-list-section.tsx +140 -0
  64. package/src/components/bug/views/view-selector-button.tsx +145 -0
  65. package/src/components/bug/views/view-switcher.tsx +8 -6
  66. package/src/components/bug/views/view-types.ts +162 -0
  67. package/src/components/collab/__tests__/activity-attribution.test.tsx +211 -0
  68. package/src/components/collab/__tests__/canvas-collab-panel.test.tsx +118 -0
  69. package/src/components/collab/__tests__/collab-presence-header.test.tsx +335 -0
  70. package/src/components/collab/__tests__/entity-presence-badge.test.tsx +226 -0
  71. package/src/components/collab/activity-attribution.tsx +221 -0
  72. package/src/components/collab/canvas-collab-panel.tsx +104 -0
  73. package/src/components/collab/collab-presence-header.tsx +220 -0
  74. package/src/components/collab/entity-presence-badge.tsx +130 -0
  75. package/src/components/collab/field-editing-pill.tsx +137 -0
  76. package/src/components/command/command-palette.tsx +6 -6
  77. package/src/components/design/canvas/document/__tests__/canvas-tree-fixture-snapshot.ts +589 -0
  78. package/src/components/design/canvas/document/__tests__/canvas-tree-reconcile.test.ts +159 -0
  79. package/src/components/design/canvas/panels/toolbar.tsx +3 -2
  80. package/src/components/design/canvas/plugins/presence/__tests__/canvas-collab-wiring.test.tsx +660 -0
  81. package/src/components/design/canvas/plugins/presence/__tests__/layout.test.ts +292 -0
  82. package/src/components/design/canvas/plugins/presence/__tests__/presence-overlay.test.tsx +139 -0
  83. package/src/components/design/canvas/plugins/presence/layout.ts +222 -0
  84. package/src/components/design/canvas/plugins/presence/presence-layer.tsx +353 -0
  85. package/src/components/design/canvas/plugins/presence/presence-overlay.tsx +155 -0
  86. package/src/components/design/canvas/plugins/presence/viewer-badge.tsx +80 -0
  87. package/src/components/design/canvas/react/__tests__/hooks.test.tsx +4 -4
  88. package/src/components/design/editor/collaboration/__tests__/design-agent-panel-regenerate.test.tsx +3 -1
  89. package/src/components/design/editor/collaboration/__tests__/design-agent-panel-stream.test.tsx +3 -1
  90. package/src/components/design/editor/collaboration/design-agent-panel.tsx +3 -0
  91. package/src/components/design/editor/collaboration/design-comment-review-panel.tsx +5 -5
  92. package/src/components/design/editor/dashboard/create-design-dialog.tsx +1 -1
  93. package/src/components/design/editor/dashboard/design-dashboard.tsx +1 -10
  94. package/src/components/design/editor/layout/__tests__/collab-param-wiring.test.tsx +153 -0
  95. package/src/components/design/editor/layout/__tests__/no-page-save-toast.test.tsx +8 -0
  96. package/src/components/design/editor/layout/design-editor-layout.tsx +28 -1
  97. package/src/components/design/editor/layout/design-navigation.tsx +0 -4
  98. package/src/components/design/editor/layout/hooks/__tests__/use-design-auto-save-collab.test.tsx +130 -0
  99. package/src/components/design/editor/layout/hooks/use-design-auto-save.ts +18 -1
  100. package/src/components/design/editor/share/share-comment-panel.tsx +37 -20
  101. package/src/components/design/editor/share/share-layout.tsx +3 -0
  102. package/src/components/design/editor/share/share-nav-bar.tsx +7 -1
  103. package/src/components/design/editor/share/share-theme-toggle.tsx +10 -15
  104. package/src/components/design/editor/tiptap/node/paragraph-node/paragraph-node.scss +1 -1
  105. package/src/components/harness/__tests__/composer-draft.test.ts +126 -0
  106. package/src/components/harness/composer/composer-editor.tsx +64 -6
  107. package/src/components/harness/composer/harness-composer.tsx +1 -1
  108. package/src/components/harness/harness-layout.tsx +1 -1
  109. package/src/components/harness/harness-workbench-page.tsx +6 -1
  110. package/src/components/harness/turn-group.tsx +1 -1
  111. package/src/components/hotkeys/shortcut-badge.tsx +4 -4
  112. package/src/components/knowledge/dialog/knowledge-form-dialog.tsx +1 -1
  113. package/src/components/knowledge/editor/__tests__/knowledge-doc-editor-collab.test.tsx +542 -0
  114. package/src/components/knowledge/editor/doc-header.tsx +7 -49
  115. package/src/components/knowledge/editor/knowledge-doc-editor.tsx +264 -51
  116. package/src/components/knowledge/editor/mindmap-doc-editor.tsx +3 -19
  117. package/src/components/knowledge/knowledge-homepage.tsx +3 -3
  118. package/src/components/knowledge/knowledge-toolbar.tsx +8 -8
  119. package/src/components/{knowledge-public → knowledge/public}/bookmark-form-dialog.tsx +4 -4
  120. package/src/components/{knowledge-public → knowledge/public}/knowledge-card.tsx +1 -15
  121. package/src/components/{knowledge-public → knowledge/public}/knowledge-form-dialog.tsx +9 -9
  122. package/src/components/{knowledge-public → knowledge/public}/legal-compliance-editor.tsx +35 -28
  123. package/src/components/{knowledge-public → knowledge/public}/sharers-avatar-group.tsx +3 -2
  124. package/src/components/layout/layout-responsive-provider.tsx +79 -0
  125. package/src/components/layout/sidebar-03/__tests__/presence-avatar-stack.test.tsx +148 -0
  126. package/src/components/layout/sidebar-03/app-sidebar.tsx +9 -3
  127. package/src/components/layout/sidebar-03/index.tsx +19 -4
  128. package/src/components/layout/sidebar-03/nav-main.tsx +28 -10
  129. package/src/components/layout/sidebar-03/presence-avatar-stack.tsx +112 -0
  130. package/src/components/layout/sidebar-03/user-menu.tsx +3 -3
  131. package/src/components/project/create-project-dialog.tsx +1 -1
  132. package/src/components/project/invite-member-dialog.tsx +1 -1
  133. package/src/components/project/project-activity-list.tsx +2 -2
  134. package/src/components/project/project-member-manager.tsx +2 -2
  135. package/src/components/reui/__tests__/kanban-drop-indicator.test.ts +88 -0
  136. package/src/components/reui/__tests__/kanban-keyboard.test.ts +262 -0
  137. package/src/components/reui/event-calendar/event-calendar-agenda-view.tsx +236 -0
  138. package/src/components/reui/event-calendar/event-calendar-content.tsx +80 -0
  139. package/src/components/reui/event-calendar/event-calendar-dnd.tsx +1062 -0
  140. package/src/components/reui/event-calendar/event-calendar-event.tsx +555 -0
  141. package/src/components/reui/event-calendar/event-calendar-i18n.tsx +291 -0
  142. package/src/components/reui/event-calendar/event-calendar-lib.tsx +677 -0
  143. package/src/components/reui/event-calendar/event-calendar-month-view.tsx +1287 -0
  144. package/src/components/reui/event-calendar/event-calendar-nav.tsx +615 -0
  145. package/src/components/reui/event-calendar/event-calendar-recurrence.tsx +592 -0
  146. package/src/components/reui/event-calendar/event-calendar-resource-view.tsx +810 -0
  147. package/src/components/reui/event-calendar/event-calendar-time-grid.tsx +1366 -0
  148. package/src/components/reui/event-calendar/event-calendar-types.tsx +240 -0
  149. package/src/components/reui/event-calendar/event-calendar.tsx +1916 -0
  150. package/src/components/reui/gantt/__tests__/gantt-row-window.test.ts +243 -0
  151. package/src/components/reui/gantt/__tests__/gantt-view-virtualize.test.tsx +243 -0
  152. package/src/components/reui/gantt/gantt-bar.tsx +439 -0
  153. package/src/components/reui/gantt/gantt-dnd.tsx +1000 -0
  154. package/src/components/reui/gantt/gantt-i18n.tsx +287 -0
  155. package/src/components/reui/gantt/gantt-lib.tsx +695 -0
  156. package/src/components/reui/gantt/gantt-nav.tsx +446 -0
  157. package/src/components/reui/gantt/gantt-recurrence.tsx +402 -0
  158. package/src/components/reui/gantt/gantt-row-window.ts +119 -0
  159. package/src/components/reui/gantt/gantt-types.tsx +283 -0
  160. package/src/components/reui/gantt/gantt-view.tsx +4466 -0
  161. package/src/components/reui/gantt/gantt.tsx +1822 -0
  162. package/src/components/reui/gantt/use-gantt-row-window.ts +159 -0
  163. package/src/components/reui/icon-stack.tsx +90 -0
  164. package/src/components/reui/kanban-keyboard.ts +268 -0
  165. package/src/components/reui/kanban.tsx +1227 -0
  166. package/src/components/settings/personal-team/account/account-client.tsx +3 -2
  167. package/src/components/settings/personal-team/team/invitation-cards/invitation-history-list.tsx +5 -5
  168. package/src/components/settings/personal-team/team/team-client.tsx +2 -1
  169. package/src/components/settings/personal-team/team/team-member-list.tsx +4 -4
  170. package/src/components/share/share-presence-bar.tsx +182 -0
  171. package/src/components/shared-kit/auth/auth-client.ts +5 -0
  172. package/src/components/shared-kit/avatars/__tests__/avatar-shape.test.ts +44 -0
  173. package/src/components/shared-kit/avatars/__tests__/user-avatar.test.tsx +46 -0
  174. package/src/components/{tags/tags-collection.ts → shared-kit/avatars/avatar-shape.ts} +14 -20
  175. package/src/components/shared-kit/avatars/notion-avatar.tsx +62 -11
  176. package/src/components/shared-kit/avatars/user-avatar.tsx +7 -4
  177. package/src/components/shared-kit/list-kit/__tests__/batch-toolbar-shell.test.tsx +133 -0
  178. package/src/components/shared-kit/list-kit/__tests__/frozen-header.test.ts +89 -0
  179. package/src/components/shared-kit/list-kit/__tests__/level-badge.test.tsx +85 -0
  180. package/src/components/shared-kit/list-kit/__tests__/toolbar-shell.test.tsx +178 -0
  181. package/src/components/shared-kit/list-kit/batch-toolbar-shell.tsx +111 -0
  182. package/src/components/shared-kit/list-kit/csv-download.ts +46 -0
  183. package/src/components/shared-kit/list-kit/display/__tests__/dropdown.test.tsx +215 -0
  184. package/src/components/shared-kit/list-kit/display/dropdown.tsx +582 -0
  185. package/src/components/shared-kit/list-kit/display/types.ts +64 -0
  186. package/src/components/shared-kit/list-kit/display/values.ts +60 -0
  187. package/src/components/shared-kit/list-kit/filters/__tests__/create-filters-module.test.tsx +181 -0
  188. package/src/components/shared-kit/list-kit/filters/__tests__/types.test.ts +133 -0
  189. package/src/components/shared-kit/list-kit/filters/index.tsx +757 -0
  190. package/src/components/shared-kit/list-kit/filters/types.ts +68 -0
  191. package/src/components/shared-kit/list-kit/frozen-header.ts +45 -0
  192. package/src/components/shared-kit/list-kit/icon-semantics.ts +50 -0
  193. package/src/components/shared-kit/list-kit/kanban/__tests__/drop-layout.test.ts +139 -0
  194. package/src/components/shared-kit/list-kit/kanban/__tests__/frozen-header.test.ts +111 -0
  195. package/src/components/shared-kit/list-kit/kanban/__tests__/instance-key.test.ts +130 -0
  196. package/src/components/shared-kit/list-kit/kanban/__tests__/kanban-board-view.test.tsx +901 -0
  197. package/src/components/shared-kit/list-kit/kanban/__tests__/manual-order.test.ts +35 -0
  198. package/src/components/shared-kit/list-kit/kanban/drop-commit.ts +212 -0
  199. package/src/components/shared-kit/list-kit/kanban/drop-layout.ts +98 -0
  200. package/src/components/shared-kit/list-kit/kanban/frozen-header.ts +88 -0
  201. package/src/components/shared-kit/list-kit/kanban/instance-key.ts +83 -0
  202. package/src/components/shared-kit/list-kit/kanban/kanban-board-view.tsx +1414 -0
  203. package/src/components/shared-kit/list-kit/kanban/manual-order.ts +39 -0
  204. package/src/components/shared-kit/list-kit/level-badge.tsx +71 -0
  205. package/src/components/shared-kit/list-kit/toolbar-shell.tsx +103 -0
  206. package/src/components/shared-kit/loading/__tests__/use-deferred-flag.test.tsx +115 -0
  207. package/src/components/shared-kit/loading/use-deferred-flag.ts +35 -0
  208. package/src/components/shared-kit/peek/dock-slot.ts +62 -0
  209. package/src/components/shared-kit/peek/peek-mode.ts +34 -0
  210. package/src/components/shared-kit/theme/animated-theme-toggler.tsx +13 -7
  211. package/src/components/shared-kit/theme/theme-toggle.tsx +7 -18
  212. package/src/components/shared-kit/views/view-icon.tsx +49 -0
  213. package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +215 -0
  214. package/src/components/story/__tests__/story-list-loading-defer.test.tsx +88 -0
  215. package/src/components/story/__tests__/story-list-view-peek-sync.test.tsx +242 -0
  216. package/src/components/story/__tests__/story-list-view-restore.test.tsx +45 -3
  217. package/src/components/story/__tests__/story-list-view-ssr.test.tsx +0 -1
  218. package/src/components/story/ai/__tests__/action-registry.test.ts +11 -11
  219. package/src/components/story/ai/action-registry.ts +5 -7
  220. package/src/components/story/ai/handlers/stream-text.ts +1 -3
  221. package/src/components/story/ai/story-ai-toolbar.tsx +58 -16
  222. package/src/components/story/ai/use-toolbar-overflow.ts +4 -7
  223. package/src/components/story/columns.tsx +2 -2
  224. package/src/components/story/constants.ts +17 -1
  225. package/src/components/story/create-story-dialog.tsx +8 -7
  226. package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +38 -4
  227. package/src/components/story/detail/__tests__/categories.test.tsx +52 -26
  228. package/src/components/story/detail/__tests__/story-collab.test.tsx +897 -0
  229. package/src/components/story/detail/activity-tabs.tsx +5 -4
  230. package/src/components/story/detail/activity-timeline.tsx +15 -3
  231. package/src/components/story/detail/comment-input.tsx +2 -2
  232. package/src/components/story/detail/comment-item.tsx +6 -3
  233. package/src/components/story/detail/comment-thread.tsx +1 -0
  234. package/src/components/story/detail/reaction-bar.tsx +1 -1
  235. package/src/components/story/detail/story-detail-body.tsx +501 -76
  236. package/src/components/story/detail/story-detail-toolbar/__tests__/related-test-cases.test.tsx +173 -10
  237. package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +144 -8
  238. package/src/components/story/detail/story-detail-toolbar/categories.tsx +82 -38
  239. package/src/components/story/detail/story-detail-toolbar/children.tsx +254 -174
  240. package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +140 -58
  241. package/src/components/story/detail/story-detail-toolbar/index.tsx +613 -0
  242. package/src/components/story/detail/story-detail-toolbar/related-test-cases.tsx +1010 -87
  243. package/src/components/story/detail/story-detail-toolbar/scroll-hint.tsx +57 -0
  244. package/src/components/story/detail/story-detail-toolbar/use-panel-frozen-groups.ts +97 -0
  245. package/src/components/story/detail/story-properties.tsx +9 -5
  246. package/src/components/story/display/__tests__/display-dropdown-helpers.test.ts +17 -1
  247. package/src/components/story/display/__tests__/display-dropdown.test.tsx +9 -0
  248. package/src/components/story/display/__tests__/flatten-story-groups.test.ts +42 -0
  249. package/src/components/story/display/__tests__/group-fields.test.ts +81 -1
  250. package/src/components/story/display/__tests__/group-resolver.test.ts +153 -1
  251. package/src/components/story/display/__tests__/use-collapsed-subgroups.test.ts +158 -0
  252. package/src/components/story/display/__tests__/use-display-options.test.ts +32 -3
  253. package/src/components/story/display/__tests__/use-fit-count.test.ts +198 -0
  254. package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +77 -3
  255. package/src/components/story/display/constants.ts +23 -2
  256. package/src/components/story/display/display-dropdown.tsx +95 -397
  257. package/src/components/story/display/flatten-story-groups.ts +39 -0
  258. package/src/components/story/display/group-fields.ts +32 -1
  259. package/src/components/story/display/group-resolver.tsx +128 -5
  260. package/src/components/story/display/types.ts +23 -42
  261. package/src/components/story/display/use-collapsed-subgroups.ts +109 -0
  262. package/src/components/story/display/use-display-options.ts +21 -5
  263. package/src/components/story/display/use-fit-count.ts +124 -0
  264. package/src/components/story/display/use-paged-story-groups.ts +174 -31
  265. package/src/components/story/filters/field-configs.tsx +263 -0
  266. package/src/components/story/filters/index.tsx +14 -5
  267. package/src/components/story/inline-editors/__tests__/labels-editor.test.tsx +2 -0
  268. package/src/components/story/inline-editors/__tests__/priority-editor.test.tsx +15 -0
  269. package/src/components/story/inline-editors/assignee-editor.tsx +7 -5
  270. package/src/components/story/inline-editors/date-editor.tsx +7 -2
  271. package/src/components/story/inline-editors/inline-trigger.tsx +17 -4
  272. package/src/components/story/inline-editors/labels-editor.tsx +243 -35
  273. package/src/components/story/inline-editors/parent-story-editor.tsx +1 -1
  274. package/src/components/story/inline-editors/priority-editor.tsx +6 -4
  275. package/src/components/story/inline-editors/state-editor.tsx +6 -4
  276. package/src/components/story/inline-editors/story-select-dialog.tsx +56 -19
  277. package/src/components/story/peek-panel.tsx +234 -51
  278. package/src/components/story/relations/__tests__/design-select-dialog.test.tsx +131 -0
  279. package/src/components/story/relations/attachment-manage-dialog.tsx +1 -1
  280. package/src/components/story/relations/design-select-dialog.tsx +285 -0
  281. package/src/components/story/relations/knowledge-select-dialog.tsx +3 -3
  282. package/src/components/story/relations/link-manage-dialog.tsx +2 -2
  283. package/src/components/story/story-list-view.tsx +175 -17
  284. package/src/components/story/story-panel-header.tsx +18 -11
  285. package/src/components/story/story-toolbar.tsx +177 -126
  286. package/src/components/story/transfer-story-popover.tsx +1 -1
  287. package/src/components/story/types.ts +11 -2
  288. package/src/components/story/views/__tests__/calendar-view.test.tsx +262 -0
  289. package/src/components/story/views/__tests__/kanban-drop-subgroup.test.ts +68 -0
  290. package/src/components/story/views/__tests__/kanban-peek-sync.test.tsx +184 -0
  291. package/src/components/story/views/__tests__/kanban-view-membership-sync.test.tsx +179 -0
  292. package/src/components/story/views/__tests__/timeline-view.test.tsx +453 -0
  293. package/src/components/story/views/__tests__/view-icon.test.tsx +45 -0
  294. package/src/components/story/views/__tests__/view-types.test.ts +0 -8
  295. package/src/components/story/views/calendar/__tests__/mappers.test.ts +201 -0
  296. package/src/components/story/views/calendar/__tests__/slot-draft.test.ts +78 -0
  297. package/src/components/story/views/calendar/drag-blocked.ts +41 -0
  298. package/src/components/story/views/calendar/i18n.ts +91 -0
  299. package/src/components/story/views/calendar/mappers.ts +119 -0
  300. package/src/components/story/views/calendar/slot-draft.ts +48 -0
  301. package/src/components/story/views/calendar-view.tsx +490 -223
  302. package/src/components/story/views/create-view-dialog.tsx +6 -6
  303. package/src/components/story/views/kanban-view.tsx +618 -0
  304. package/src/components/story/views/{__tests__ → story-list/__tests__}/use-flat-rows.test.ts +1 -1
  305. package/src/components/story/views/story-list/batch-preview-dialog.tsx +3 -3
  306. package/src/components/story/views/story-list/batch-toolbar.tsx +17 -34
  307. package/src/components/story/views/story-list/group.tsx +4 -2
  308. package/src/components/story/views/story-list/index.tsx +142 -34
  309. package/src/components/story/views/story-list/row.tsx +4 -0
  310. package/src/components/story/views/{use-flat-rows.ts → story-list/use-flat-rows.ts} +2 -2
  311. package/src/components/story/views/timeline/__tests__/mappers.test.ts +742 -0
  312. package/src/components/story/views/timeline/__tests__/sentinel-geometry.test.ts +76 -0
  313. package/src/components/story/views/timeline/__tests__/use-date-commit.test.tsx +372 -0
  314. package/src/components/story/views/timeline/i18n.ts +96 -0
  315. package/src/components/story/views/timeline/mappers.ts +506 -0
  316. package/src/components/story/views/timeline/sentinel-geometry.ts +43 -0
  317. package/src/components/story/views/timeline/use-date-commit.ts +169 -0
  318. package/src/components/story/views/timeline-view.tsx +593 -222
  319. package/src/components/story/views/view-list-section.tsx +4 -4
  320. package/src/components/story/views/view-selector-button.tsx +9 -10
  321. package/src/components/story/views/view-switcher.tsx +7 -1
  322. package/src/components/story/views/view-types.ts +1 -5
  323. package/src/components/testcase/__tests__/batch-toolbar.test.tsx +245 -0
  324. package/src/components/testcase/__tests__/board-view.test.tsx +241 -0
  325. package/src/components/testcase/__tests__/case-row.test.tsx +24 -10
  326. package/src/components/testcase/__tests__/constants.test.ts +151 -0
  327. package/src/components/testcase/__tests__/create-dialog-scope.test.tsx +173 -0
  328. package/src/components/testcase/__tests__/detail-properties.test.tsx +19 -0
  329. package/src/components/testcase/__tests__/display-options.test.tsx +154 -0
  330. package/src/components/testcase/__tests__/export-entry.test.tsx +279 -0
  331. package/src/components/testcase/__tests__/filters.test.tsx +280 -0
  332. package/src/components/testcase/__tests__/list-view-scope.test.tsx +290 -0
  333. package/src/components/testcase/__tests__/list-view.test.tsx +552 -7
  334. package/src/components/testcase/__tests__/manual-move.test.ts +69 -0
  335. package/src/components/testcase/__tests__/peek-panel.test.tsx +89 -8
  336. package/src/components/testcase/__tests__/toolbar.test.tsx +302 -0
  337. package/src/components/testcase/__tests__/view-fallback.test.tsx +272 -0
  338. package/src/components/testcase/__tests__/view-management.test.tsx +179 -0
  339. package/src/components/testcase/ai/generate-dialog.tsx +1 -1
  340. package/src/components/testcase/constants.ts +83 -0
  341. package/src/components/testcase/create-testcase-dialog.tsx +76 -8
  342. package/src/components/testcase/detail/__tests__/select-cases-dialog.test.tsx +163 -0
  343. package/src/components/testcase/detail/case-detail-body.tsx +311 -30
  344. package/src/components/testcase/detail/select-cases-dialog.tsx +465 -0
  345. package/src/components/testcase/detail/story-link-editor.tsx +1 -1
  346. package/src/components/testcase/display/display-dropdown.tsx +147 -0
  347. package/src/components/testcase/display/field-configs.ts +80 -0
  348. package/src/components/testcase/display/use-testcase-display-options.ts +59 -0
  349. package/src/components/testcase/filters/field-configs.tsx +748 -0
  350. package/src/components/testcase/filters/index.tsx +35 -127
  351. package/src/components/testcase/inline-editors/assignee-editor.tsx +6 -6
  352. package/src/components/testcase/inline-editors/folder-editor.tsx +9 -9
  353. package/src/components/testcase/inline-editors/priority-editor.tsx +47 -31
  354. package/src/components/testcase/inline-editors/state-editor.tsx +14 -12
  355. package/src/components/testcase/inline-editors/type-editor.tsx +31 -20
  356. package/src/components/testcase/list/case-row.tsx +60 -2
  357. package/src/components/testcase/list/grouped-list.tsx +3 -2
  358. package/src/components/testcase/list-view.tsx +633 -137
  359. package/src/components/testcase/peek-panel.tsx +69 -11
  360. package/src/components/testcase/plans/plan-list.tsx +6 -1
  361. package/src/components/testcase/step-editor.tsx +34 -3
  362. package/src/components/testcase/testcase-toolbar.tsx +349 -94
  363. package/src/components/testcase/views/batch-toolbar.tsx +203 -0
  364. package/src/components/testcase/views/board-view.tsx +546 -0
  365. package/src/components/testcase/views/create-view-dialog.tsx +26 -14
  366. package/src/components/testcase/views/move.ts +56 -0
  367. package/src/components/testcase/views/view-selector-button.tsx +82 -30
  368. package/src/components/testcase/views/view-switcher.tsx +12 -5
  369. package/src/components/testcase/views/view-types.ts +50 -27
  370. package/src/components/tiptap/mindmap/lib/__tests__/shortcuts.test.ts +5 -4
  371. package/src/components/tiptap/mindmap/lib/shortcuts.ts +4 -3
  372. package/src/components/tiptap/node/doc-card-node-view.tsx +2 -1
  373. package/src/components/tiptap/notion-like/editor-extensions.ts +231 -0
  374. package/src/components/tiptap/notion-like/notion-like-editor.tsx +29 -156
  375. package/src/components/tiptap/ui/ai-menu/ai-menu.tsx +6 -1
  376. package/src/components/tiptap/ui/doc-reference-interaction/doc-reference-interaction.tsx +2 -1
  377. package/src/components/ui/checkbox.tsx +4 -4
  378. package/src/components/ui/popover.tsx +1 -1
  379. package/src/components/workspace/dialogs/edit-workspace-dialog.tsx +1 -1
  380. package/src/components/workspace/kb/workspace-kb-manage-dialog.tsx +1 -1
  381. package/src/components/workspace/kb/workspace-quick-doc-dialog.tsx +1 -1
  382. package/src/components/workspace/members/workspace-member-item.tsx +2 -2
  383. package/src/components/workspace/members/workspace-members-manager.tsx +2 -2
  384. package/src/config/sidebar-routes.tsx +11 -13
  385. package/src/design-engine/document/serialize-leafer-json.ts +2 -1
  386. package/src/design-engine/engine/__tests__/engine-split.test.ts +1 -1
  387. package/src/design-engine/engine/__tests__/engine.test.ts +20 -0
  388. package/src/design-engine/engine/app.ts +5 -0
  389. package/src/design-engine/engine/engine-base.ts +17 -0
  390. package/src/design-engine/engine/engine-doc.ts +32 -0
  391. package/src/hooks/__tests__/use-commands.test.tsx +17 -5
  392. package/src/hooks/use-commands.ts +4 -4
  393. package/src/hooks/use-toolbar-search.ts +7 -2
  394. package/src/lib/__tests__/datetime-local.test.ts +46 -0
  395. package/src/lib/__tests__/datetime.test.ts +7 -0
  396. package/src/lib/__tests__/layout-responsive.test.ts +123 -0
  397. package/src/lib/__tests__/sidebar-utils.test.ts +4 -4
  398. package/src/lib/ai/story-ai-stream.ts +1 -1
  399. package/src/lib/ai/use-selected-model.ts +15 -5
  400. package/src/lib/changelog/sdk-versions.ts +22 -22
  401. package/src/lib/collab/__tests__/bindings-fields.test.ts +496 -0
  402. package/src/lib/collab/__tests__/bindings-steps.test.ts +332 -0
  403. package/src/lib/collab/__tests__/presence-store.test.ts +414 -0
  404. package/src/lib/collab/__tests__/provider-handle-contract.test.ts +89 -0
  405. package/src/lib/collab/__tests__/provider.test.ts +504 -0
  406. package/src/lib/collab/__tests__/use-presence-list-refresh.test.tsx +309 -0
  407. package/src/lib/collab/__tests__/use-presence-report.test.ts +356 -0
  408. package/src/lib/collab/__tests__/use-share-presence.test.ts +555 -0
  409. package/src/lib/collab/__tests__/user-color.test.ts +53 -0
  410. package/src/lib/collab/bindings/__tests__/canvas-local.test.ts +583 -0
  411. package/src/lib/collab/bindings/__tests__/canvas-presence.test.ts +378 -0
  412. package/src/lib/collab/bindings/__tests__/canvas-remote.test.ts +454 -0
  413. package/src/lib/collab/bindings/__tests__/canvas-tree-perf.test.ts +134 -0
  414. package/src/lib/collab/bindings/canvas-local.ts +415 -0
  415. package/src/lib/collab/bindings/canvas-presence.ts +420 -0
  416. package/src/lib/collab/bindings/canvas-remote.ts +226 -0
  417. package/src/lib/collab/bindings/fields.ts +463 -0
  418. package/src/lib/collab/bindings/steps.ts +332 -0
  419. package/src/lib/collab/env.ts +33 -0
  420. package/src/lib/collab/module-map.ts +131 -0
  421. package/src/lib/collab/presence-store.ts +350 -0
  422. package/src/lib/collab/provider.ts +378 -0
  423. package/src/lib/collab/use-collab-doc.ts +100 -0
  424. package/src/lib/collab/use-presence-list-refresh.ts +142 -0
  425. package/src/lib/collab/use-presence-report.ts +176 -0
  426. package/src/lib/collab/use-self-profile-broadcast.ts +113 -0
  427. package/src/lib/collab/use-share-presence.ts +561 -0
  428. package/src/lib/collab/user-color.ts +40 -0
  429. package/src/lib/collections/index.ts +0 -1
  430. package/src/lib/collections/reference/bookmark-categories-collection.ts +1 -1
  431. package/src/lib/commands/__tests__/registry.test.ts +2 -5
  432. package/src/lib/commands/registry.ts +0 -3
  433. package/src/lib/commands/types.ts +1 -1
  434. package/src/lib/datetime.ts +20 -3
  435. package/src/lib/hooks/__tests__/use-synced-state.test.tsx +54 -0
  436. package/src/lib/hooks/use-story-panel-open.ts +134 -0
  437. package/src/lib/hooks/use-synced-state.ts +17 -0
  438. package/src/lib/hotkeys/__tests__/registry.test.ts +1 -1
  439. package/src/lib/hotkeys/registry.ts +0 -3
  440. package/src/lib/layout-responsive.ts +140 -0
  441. package/src/lib/pacer.ts +10 -1
  442. package/src/lib/peek-dock.ts +32 -0
  443. package/src/lib/plugin-runtime/__tests__/plugin-tool-schema-parity.test.ts +140 -0
  444. package/src/lib/plugin-runtime/plugin-client-tools.ts +28 -46
  445. package/src/lib/plugin-runtime/plugin-tool-defs.ts +152 -0
  446. package/src/lib/project-module-meta.ts +1 -8
  447. package/src/lib/rate-limit.ts +15 -6
  448. package/src/lib/sanitize.ts +122 -10
  449. package/src/lib/share-events.ts +20 -11
  450. package/src/lib/sidebar-utils.ts +1 -1
  451. package/src/lib/theme/__tests__/theme-mode.test.ts +96 -0
  452. package/src/lib/theme/theme-mode.ts +77 -0
  453. package/src/paraglide/messages/_index.js +5312 -1591
  454. package/src/paraglide/messages/en.js +1880 -685
  455. package/src/paraglide/messages/zh.js +1877 -682
  456. package/src/providers/index.tsx +4 -1
  457. package/src/providers/theme-mode-provider.tsx +135 -0
  458. package/src/routeTree.gen.ts +78 -101
  459. package/src/routes/$lang/docs/$/route.lazy.tsx +7 -2
  460. package/src/routes/-tests/chat-body-validation.test.ts +61 -3
  461. package/src/routes/__tests__/-edition-routing.test.tsx +12 -8
  462. package/src/routes/_nav/-components/nav-not-found.tsx +2 -2
  463. package/src/routes/_nav/bugs/$bugId/route.lazy.tsx +16 -2
  464. package/src/routes/_nav/bugs/$bugId/route.tsx +11 -2
  465. package/src/routes/_nav/dashboard/-components/avatar-group.tsx +2 -2
  466. package/src/routes/_nav/knowledge/$kbId/doc/$docId/route.lazy.tsx +31 -17
  467. package/src/routes/_nav/knowledge/$kbId/doc/$docId/route.tsx +12 -1
  468. package/src/routes/_nav/knowledge/$kbId/index.lazy.tsx +4 -2
  469. package/src/routes/_nav/knowledge/$kbId/route.lazy.tsx +7 -3
  470. package/src/routes/_nav/knowledge/bookmarks/$id.lazy.tsx +6 -6
  471. package/src/routes/_nav/knowledge/bookmarks/index.lazy.tsx +10 -10
  472. package/src/routes/_nav/knowledge/compliance/$id.lazy.tsx +9 -9
  473. package/src/routes/_nav/knowledge/compliance/edit/$id.lazy.tsx +1 -1
  474. package/src/routes/_nav/knowledge/compliance/index.lazy.tsx +8 -8
  475. package/src/routes/_nav/knowledge/dictionaries/$id.lazy.tsx +4 -4
  476. package/src/routes/_nav/knowledge/dictionaries/index.lazy.tsx +6 -6
  477. package/src/routes/_nav/knowledge/index.lazy.tsx +1 -1
  478. package/src/routes/_nav/knowledge/laws/$id.lazy.tsx +9 -9
  479. package/src/routes/_nav/knowledge/laws/edit/$id.lazy.tsx +1 -1
  480. package/src/routes/_nav/knowledge/laws/index.lazy.tsx +11 -11
  481. package/src/routes/_nav/knowledge/summary/index.lazy.tsx +1 -1
  482. package/src/routes/_nav/projects/$projectId/index.lazy.tsx +14 -6
  483. package/src/routes/_nav/route.lazy.tsx +11 -6
  484. package/src/routes/_nav/settings/account/route.tsx +3 -2
  485. package/src/routes/_nav/stories/$storyId/route.lazy.tsx +52 -8
  486. package/src/routes/_nav/stories/$storyId/route.tsx +11 -0
  487. package/src/routes/_nav/stories/-components/story-detail-skeleton.tsx +1 -1
  488. package/src/{components/tags/pages/tag-detail-page.tsx → routes/_nav/tasks/$taskId/route.lazy.tsx} +20 -10
  489. package/src/routes/_nav/tasks/$taskId/route.tsx +43 -0
  490. package/src/routes/_nav/tasks/-components/ai-task-detail.tsx +307 -0
  491. package/src/routes/_nav/tasks/-components/ai-task-list.tsx +217 -0
  492. package/src/routes/_nav/tasks/-components/ai-task-status-badge.tsx +46 -0
  493. package/src/routes/_nav/tasks/-components/assigned-list.tsx +157 -0
  494. package/src/routes/_nav/tasks/-components/create-ai-task-dialog.tsx +228 -0
  495. package/src/routes/_nav/tasks/-components/source-ai-exec-dialog.tsx +175 -0
  496. package/src/routes/_nav/tasks/-components/task-constants.ts +115 -0
  497. package/src/routes/_nav/tasks/-components/use-task-filter.ts +64 -0
  498. package/src/routes/_nav/tasks/-tests/create-ai-task-dialog.test.tsx +175 -0
  499. package/src/routes/_nav/tasks/-tests/task-filter.test.ts +134 -0
  500. package/src/routes/_nav/tasks/-tests/task-styles.test.tsx +119 -0
  501. package/src/routes/_nav/tasks/index.lazy.tsx +114 -0
  502. package/src/routes/_nav/tasks/index.tsx +47 -0
  503. package/src/routes/_nav/{tags → tasks}/route.tsx +3 -3
  504. package/src/routes/_nav/test-cases/$caseId/route.lazy.tsx +3 -1
  505. package/src/routes/_nav/test-cases/$caseId/route.tsx +11 -2
  506. package/src/routes/api/chat.ts +23 -19
  507. package/src/routes/api/design/prototype/route.ts +2 -4
  508. package/src/routes/{_nav/acceptance-criteria/route.tsx → api/internal/collab-reindex.ts} +11 -8
  509. package/src/routes/api/knowledge/sync-github.ts +1 -1
  510. package/src/routes/design-files/$projectId/$artboardId/$filename.ts +9 -0
  511. package/src/routes/invite/$token/route.lazy.tsx +2 -2
  512. package/src/routes/share/$versionId/route.lazy.tsx +8 -0
  513. package/src/routes/share/compliance/$id/route.lazy.tsx +2 -2
  514. package/src/routes/share/compliance/$id/route.tsx +1 -1
  515. package/src/routes/share/law/$id/route.lazy.tsx +2 -2
  516. package/src/routes/share/law/$id/route.tsx +1 -1
  517. package/src/server/__tests__/bug-groups-two-level.test.ts +263 -0
  518. package/src/server/__tests__/builtin-project.test.ts +45 -1
  519. package/src/server/__tests__/csv-export.test.ts +319 -0
  520. package/src/server/__tests__/share-version.test.ts +7 -2
  521. package/src/server/__tests__/task-assigned-query.test.ts +232 -0
  522. package/src/server/__tests__/task-context-builder.test.ts +175 -0
  523. package/src/server/__tests__/task-executor.test.ts +349 -0
  524. package/src/server/__tests__/task-priority-map.test.ts +41 -0
  525. package/src/server/__tests__/task-serverfn-crud.test.ts +639 -0
  526. package/src/server/__tests__/task-serverfn-queries.test.ts +403 -0
  527. package/src/server/__tests__/testcase-board.test.ts +301 -0
  528. package/src/server/__tests__/testcase-bulk.test.ts +155 -0
  529. package/src/server/__tests__/testcase-list-scope.test.ts +215 -0
  530. package/src/server/ai-gateway/__tests__/chat-handler-prompt-key.test.ts +106 -0
  531. package/src/server/ai-gateway/__tests__/chat-stream-builder-sandbox.test.ts +9 -3
  532. package/src/server/ai-gateway/__tests__/conformance.test.ts +4 -3
  533. package/src/server/ai-gateway/adapter.ts +4 -2
  534. package/src/server/ai-gateway/chat-handler.ts +13 -5
  535. package/src/server/ai-gateway/chat-stream-builder.ts +12 -7
  536. package/src/server/ai-gateway/sandbox/__tests__/harness-sandbox.test.ts +88 -0
  537. package/src/server/ai-gateway/sandbox/harness-sandbox.ts +1 -1
  538. package/src/server/ai-gateway/story-handler.ts +7 -2
  539. package/src/server/ai-gateway/tools/plugin-lifecycle-defs.ts +8 -99
  540. package/src/server/ai-gateway/tools/plugin-tools.ts +6 -10
  541. package/src/server/ai-task/assigned-query.ts +167 -0
  542. package/src/server/ai-task/context-builder.ts +182 -0
  543. package/src/server/ai-task/executor.ts +296 -0
  544. package/src/server/ai-task/priority-map.ts +37 -0
  545. package/src/server/ai-task/types.ts +15 -0
  546. package/src/server/bug-report/__tests__/report-handler.test.ts +55 -4
  547. package/src/server/bug-report/report-handler.ts +54 -8
  548. package/src/server/builtin-project.ts +24 -0
  549. package/src/server/cloudflare/prisma-wasm-base64-stub.ts +5 -0
  550. package/src/server/collab/__tests__/collab-permissions.test.ts +412 -0
  551. package/src/server/collab/__tests__/knowledge-additions.test.ts +121 -0
  552. package/src/server/collab/__tests__/reindex-endpoint.test.ts +168 -0
  553. package/src/server/collab/__tests__/resolve.test.ts +249 -0
  554. package/src/server/collab/__tests__/touch-snapshot.test.ts +58 -0
  555. package/src/server/collab/__tests__/write-guard-sdk.test.ts +501 -0
  556. package/src/server/collab/__tests__/write-guard.test.ts +373 -0
  557. package/src/server/collab/knowledge-additions.ts +37 -0
  558. package/src/server/collab/reindex-endpoint.ts +75 -0
  559. package/src/server/collab/resolve.ts +122 -0
  560. package/src/server/collab/seed.ts +408 -0
  561. package/src/server/collab/ticket-utils.ts +107 -0
  562. package/src/server/collab/touch-snapshot.ts +55 -0
  563. package/src/server/collab/write-guard.ts +258 -0
  564. package/src/server/collab-permissions.ts +248 -0
  565. package/src/server/csv.ts +61 -0
  566. package/src/server/{knowledge-public → knowledge/public}/github-sync.ts +12 -2
  567. package/src/server/{knowledge-public → knowledge/public}/share-merge.ts +31 -8
  568. package/src/server/lib/__tests__/sequence-number-retry.test.ts +160 -0
  569. package/src/server/lib/rate-limit.ts +13 -2
  570. package/src/server/lib/sequence-number-retry.ts +85 -0
  571. package/src/server/mcp/__tests__/tools.test.ts +10 -0
  572. package/src/server/mcp/tools.ts +26 -2
  573. package/src/server/middlewares/auth-rate-limit.ts +18 -9
  574. package/src/server/notifications/event-bus.ts +3 -0
  575. package/src/server/serverFns/__tests__/collab-snapshot.test.ts +665 -0
  576. package/src/server/serverFns/__tests__/collab.test.ts +459 -0
  577. package/src/server/serverFns/__tests__/knowledge-delete.test.ts +167 -0
  578. package/src/server/serverFns/__tests__/ownership-merge.test.ts +34 -2
  579. package/src/server/serverFns/__tests__/read-write-merge.test.ts +2 -2
  580. package/src/server/serverFns/__tests__/story-crud-archive-detail.test.ts +12 -2
  581. package/src/server/serverFns/__tests__/story-crud.test.ts +51 -11
  582. package/src/server/serverFns/__tests__/story-tree.test.ts +66 -34
  583. package/src/server/serverFns/__tests__/story-types.test.ts +14 -9
  584. package/src/server/serverFns/__tests__/update-bug-guard.test.ts +325 -0
  585. package/src/server/serverFns/__tests__/update-document-guard.test.ts +285 -0
  586. package/src/server/serverFns/__tests__/update-page-guard.test.ts +276 -0
  587. package/src/server/serverFns/__tests__/update-story-guard.test.ts +436 -0
  588. package/src/server/serverFns/__tests__/update-testcase-guard.test.ts +293 -0
  589. package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +25 -0
  590. package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +315 -15
  591. package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +36 -18
  592. package/src/server/serverFns/bug/__tests__/bug-list-query.test.ts +21 -0
  593. package/src/server/serverFns/bug/__tests__/bug-reactions.test.ts +335 -0
  594. package/src/server/serverFns/bug/__tests__/bug-reorder.test.ts +165 -0
  595. package/src/server/serverFns/bug/__tests__/bug-story.test.ts +4 -1
  596. package/src/server/serverFns/bug/bug-comments.ts +5 -0
  597. package/src/server/serverFns/bug/bug-crud.ts +321 -72
  598. package/src/server/serverFns/bug/bug-display-options.ts +11 -0
  599. package/src/server/serverFns/bug/bug-export.ts +137 -0
  600. package/src/server/serverFns/bug/bug-group-query.ts +16 -5
  601. package/src/server/serverFns/bug/bug-groups.ts +39 -15
  602. package/src/server/serverFns/bug/bug-list-query.ts +8 -1
  603. package/src/server/serverFns/bug/bug-reactions.ts +123 -0
  604. package/src/server/serverFns/bug/bug-reorder.ts +64 -0
  605. package/src/server/serverFns/bug/bug-story-bulk.ts +280 -0
  606. package/src/server/serverFns/bug/bug-story.ts +47 -39
  607. package/src/server/serverFns/bug/index.ts +6 -0
  608. package/src/server/serverFns/collab-presence.ts +119 -0
  609. package/src/server/serverFns/collab-snapshot.ts +201 -0
  610. package/src/server/serverFns/collab.ts +153 -0
  611. package/src/server/serverFns/design/__tests__/designs.test.ts +0 -2
  612. package/src/server/serverFns/design/designs.ts +0 -64
  613. package/src/server/serverFns/design/detail.ts +36 -2
  614. package/src/server/serverFns/design/pages.ts +73 -10
  615. package/src/server/serverFns/iteration/iteration-crud.ts +25 -1
  616. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/create.ts +3 -3
  617. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/detail.ts +2 -2
  618. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/list.ts +3 -2
  619. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/refresh-favicon.ts +1 -1
  620. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/share.ts +1 -1
  621. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/sync-github.ts +1 -1
  622. package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/update.ts +5 -4
  623. package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/create.ts +1 -1
  624. package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/detail.ts +2 -2
  625. package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/list.ts +3 -2
  626. package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/share.ts +1 -1
  627. package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/update.ts +3 -2
  628. package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/create.ts +1 -1
  629. package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/detail.ts +2 -2
  630. package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/list.ts +3 -2
  631. package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/share.ts +1 -1
  632. package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/update.ts +3 -2
  633. package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/create.ts +1 -1
  634. package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/detail.ts +2 -2
  635. package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/list.ts +3 -2
  636. package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/share.ts +1 -1
  637. package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/update.ts +3 -2
  638. package/src/server/serverFns/knowledge.ts +135 -19
  639. package/src/server/serverFns/project/module-overview.ts +1 -2
  640. package/src/server/serverFns/share/version.ts +7 -1
  641. package/src/server/serverFns/story/__tests__/bulk.test.ts +6 -0
  642. package/src/server/serverFns/story/__tests__/create-story-internal.test.ts +112 -3
  643. package/src/server/serverFns/story/__tests__/story-assoc.test.ts +77 -6
  644. package/src/server/serverFns/story/__tests__/story-crud-date-key.test.ts +187 -0
  645. package/src/server/serverFns/story/__tests__/story-gantt-deps.test.ts +163 -0
  646. package/src/server/serverFns/story/__tests__/story-groups.test.ts +4 -0
  647. package/src/server/serverFns/story/__tests__/story-list-query.test.ts +20 -0
  648. package/src/server/serverFns/story/__tests__/story-search.test.ts +5 -0
  649. package/src/server/serverFns/story/__tests__/story-transfer.test.ts +64 -1
  650. package/src/server/serverFns/story/index.ts +2 -2
  651. package/src/server/serverFns/story/story-assoc.ts +44 -14
  652. package/src/server/serverFns/story/story-crud/archive.ts +52 -28
  653. package/src/server/serverFns/story/story-crud/bulk.ts +14 -4
  654. package/src/server/serverFns/story/story-crud/create-story-internal.ts +39 -21
  655. package/src/server/serverFns/story/story-crud.ts +283 -116
  656. package/src/server/serverFns/story/story-dependencies.ts +31 -0
  657. package/src/server/serverFns/story/story-groups.ts +15 -11
  658. package/src/server/serverFns/story/story-list-query.ts +16 -4
  659. package/src/server/serverFns/story/story-search.ts +9 -6
  660. package/src/server/serverFns/story/story-transfer.ts +99 -42
  661. package/src/server/serverFns/story/story-tree.ts +70 -48
  662. package/src/server/serverFns/story-display-options.ts +21 -10
  663. package/src/server/serverFns/story-view.ts +2 -2
  664. package/src/server/serverFns/tasks/task-crud.ts +264 -0
  665. package/src/server/serverFns/tasks/task-queries.ts +172 -0
  666. package/src/server/serverFns/testcase/__tests__/generation.test.ts +3 -0
  667. package/src/server/serverFns/testcase/__tests__/stats.test.ts +18 -4
  668. package/src/server/serverFns/testcase/__tests__/testcase-board.test.ts +113 -0
  669. package/src/server/serverFns/testcase/__tests__/testcase-crud.test.ts +62 -14
  670. package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +32 -10
  671. package/src/server/serverFns/testcase/bulk.ts +195 -0
  672. package/src/server/serverFns/testcase/generation.ts +10 -0
  673. package/src/server/serverFns/testcase/stats.ts +22 -8
  674. package/src/server/serverFns/testcase/testcase-board.ts +198 -0
  675. package/src/server/serverFns/testcase/testcase-crud.ts +165 -33
  676. package/src/server/serverFns/testcase/testcase-display-options.ts +166 -0
  677. package/src/server/serverFns/testcase/testcase-export.ts +125 -0
  678. package/src/server/serverFns/testcase/testcase-filter-views.ts +36 -9
  679. package/src/server/serverFns/testcase/testcase-groups.ts +27 -23
  680. package/src/server/serverFns/testcase/testcase-reorder.ts +66 -0
  681. package/src/server/story/__tests__/date-key.test.ts +57 -0
  682. package/src/server/story/date-key.ts +36 -0
  683. package/src/server/story/types.ts +3 -7
  684. package/src/server/testcase/__tests__/list-query.test.ts +269 -0
  685. package/src/server/testcase/list-query.ts +145 -0
  686. package/src/server-entry.ts +32 -0
  687. package/src/shared/bug/__tests__/constants.test.ts +44 -0
  688. package/src/shared/bug/constants.ts +37 -1
  689. package/src/start.ts +7 -2
  690. package/src/styles/globals.css +24 -3
  691. package/src/types/project.ts +2 -3
  692. package/vite.shared.ts +110 -3
  693. package/src/__tests__/tag-badge.test.tsx +0 -41
  694. package/src/components/bug/__tests__/bug-list-table.test.tsx +0 -289
  695. package/src/components/bug/bug-list-table.tsx +0 -187
  696. package/src/components/bug/columns.tsx +0 -193
  697. package/src/components/bug/filters/add-filter-dropdown.tsx +0 -70
  698. package/src/components/bug/filters/applied-filters.tsx +0 -214
  699. package/src/components/bug/filters/filter-condition.tsx +0 -151
  700. package/src/components/bug/filters/filter-row.tsx +0 -104
  701. package/src/components/bug/filters/filter-toggle.tsx +0 -61
  702. package/src/components/bug/filters/filter-types.ts +0 -146
  703. package/src/components/story/detail/story-detail-toolbar.tsx +0 -382
  704. package/src/components/story/filters/add-filter-dropdown.tsx +0 -70
  705. package/src/components/story/filters/applied-filters.tsx +0 -225
  706. package/src/components/story/filters/filter-condition.tsx +0 -152
  707. package/src/components/story/filters/filter-row.tsx +0 -116
  708. package/src/components/story/filters/filter-toggle.tsx +0 -60
  709. package/src/components/story/filters/filter-types.ts +0 -120
  710. package/src/components/story/filters/filter-value-input.tsx +0 -182
  711. package/src/components/story/views/kanban-board.tsx +0 -464
  712. package/src/components/story/views/kanban-card.tsx +0 -100
  713. package/src/components/tags/__tests__/tags.test.ts +0 -388
  714. package/src/components/tags/nav-tag.tsx +0 -121
  715. package/src/components/tags/pages/tag-settings-page.tsx +0 -32
  716. package/src/components/tags/pages/tags-index-page.tsx +0 -32
  717. package/src/components/tags/tag-badge.tsx +0 -56
  718. package/src/components/tags/tag-color-classes.ts +0 -61
  719. package/src/components/tags/tag-color-picker.tsx +0 -54
  720. package/src/components/tags/tag-components-index.ts +0 -23
  721. package/src/components/tags/tag-manager.tsx +0 -208
  722. package/src/components/tags/tag-selector.tsx +0 -164
  723. package/src/components/testcase/__tests__/table-view.test.tsx +0 -328
  724. package/src/components/testcase/plans/add-to-plan-bar.tsx +0 -64
  725. package/src/components/testcase/table/columns.tsx +0 -170
  726. package/src/components/testcase/table/table-view.tsx +0 -152
  727. package/src/lib/tag-colors.ts +0 -35
  728. package/src/routes/_nav/tags/$id/route.lazy.tsx +0 -23
  729. package/src/routes/_nav/tags/$id/route.tsx +0 -20
  730. package/src/routes/_nav/tags/index.lazy.tsx +0 -23
  731. package/src/routes/_nav/tags/index.tsx +0 -20
  732. package/src/routes/_nav/tags/settings/route.lazy.tsx +0 -23
  733. package/src/routes/_nav/tags/settings/route.tsx +0 -23
  734. package/src/server/serverFns/tags.ts +0 -138
  735. /package/src/components/{knowledge-public → knowledge/public}/bookmark-card.tsx +0 -0
  736. /package/src/components/{knowledge-public → knowledge/public}/category-icon.tsx +0 -0
  737. /package/src/components/{knowledge-public → knowledge/public}/chip-utils.ts +0 -0
  738. /package/src/components/{knowledge-public → knowledge/public}/knowledge-card-grid.tsx +0 -0
  739. /package/src/components/{knowledge-public → knowledge/public}/knowledge-filters.tsx +0 -0
  740. /package/src/components/{knowledge-public → knowledge/public}/knowledge-search-bar.tsx +0 -0
  741. /package/src/components/{knowledge-public → knowledge/public}/markdown-editor.tsx +0 -0
  742. /package/src/components/{knowledge-public → knowledge/public}/markdown-view.tsx +0 -0
  743. /package/src/components/{knowledge-public → knowledge/public}/tag-filter.tsx +0 -0
  744. /package/src/components/{story/detail → shared-kit/comments}/emoji-picker.tsx +0 -0
  745. /package/src/components/story/{schemas/story-create.ts → story-create-schema.ts} +0 -0
  746. /package/src/server/{knowledge-public → knowledge/public}/canonical-id.ts +0 -0
  747. /package/src/server/{knowledge-public → knowledge/public}/favicon.ts +0 -0
  748. /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/categories.ts +0 -0
  749. /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/delete.ts +0 -0
  750. /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/favorite.ts +0 -0
  751. /package/src/server/serverFns/{knowledge-public → knowledge/public}/bookmarks/unshare.ts +0 -0
  752. /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/delete.ts +0 -0
  753. /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/public.ts +0 -0
  754. /package/src/server/serverFns/{knowledge-public → knowledge/public}/compliance/unshare.ts +0 -0
  755. /package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/delete.ts +0 -0
  756. /package/src/server/serverFns/{knowledge-public → knowledge/public}/dictionary/unshare.ts +0 -0
  757. /package/src/server/serverFns/{knowledge-public → knowledge/public}/entity-tags.ts +0 -0
  758. /package/src/server/serverFns/{knowledge-public → knowledge/public}/favorite.ts +0 -0
  759. /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/delete.ts +0 -0
  760. /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/public.ts +0 -0
  761. /package/src/server/serverFns/{knowledge-public → knowledge/public}/legal/unshare.ts +0 -0
@@ -0,0 +1,353 @@
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
+ // 画布协同挂载层(P4a 任务 25 presence;P4b 任务 30 装配点):本组件把
19
+ // 画布协同四件在此收敛成完整协同态——
20
+ // ① canvas-presence 绑定(本端选中/视口上报 + 远端在场快照,T25)
21
+ // ② canvas-remote 绑定(Y → 引擎增量应用,T27)
22
+ // ③ canvas-local 绑定(引擎 → Y 转写 + 拖拽 awareness 影子,T28),其
23
+ // freezeTargets 接力 [remoteBinding, presenceBinding](拖拽冻结纪律)
24
+ // ④ auto-save collab 参数由布局层注入本组件 collab prop(门控建连)并
25
+ // 直通 useDesignAutoSave(保存让位,T29)
26
+ // 装配时序(T27 交接锚点):绑定创建在引擎就绪后;remote 绑定创建即
27
+ // reconcileNow() 补种(room 已有 Y 树 → 引擎收敛到 Y 权威态);local 绑定
28
+ // 订阅在补种之后——初始 loadDoc 的 DB 态不再回写 Y。
29
+ //
30
+ // 编辑面双门控(审查 C1/I2):① 装载窗口——loadPage 先 setActivePageId 再
31
+ // 异步取页,窗口内引擎持旧页文档,远端桥/本地桥整体不装配(pageLoaded =
32
+ // canvasLoadedRef 镜像,与 auto-save 同口径),零 Y 写防旧页内容污染新
33
+ // room;② 预览——版本预览(readOnly)引擎持历史版本,reconcileNow/远端
34
+ // 回放会覆写历史,编辑面整体跳过(presence 只读在场保留)。
35
+ //
36
+ // loadDoc 防覆盖守卫(任务 30 装配新增):页装载完成时 DB 快照晚于 room
37
+ // 协同态到达(sceneDoc prop 换新触发 engine.loadDoc(DB))——若 room 内存在
38
+ // 尚未 flush 的协同编辑(Y 新于 DB),本地桥将把陈旧快照回写 Y、覆盖他人
39
+ // 在途编辑。守卫三件套:装配守卫预订阅(先于本地桥记录 lastEngineDoc)→
40
+ // sceneDoc prop ≠ lastEngineDoc 时武装(等价 CanvasProvider initialDoc ≠
41
+ // lastEmitted 的 loadDoc 判据;武装与 loadDoc 同 commit 相邻 effect,用户
42
+ // 编辑无法插入)→ 被武装消费的装载通知从本地桥抑制(其内容从引擎丢弃,
43
+ // queueMicrotask reconcileNow 以 Y 覆写——丢弃的正是陈旧 DB 快照,预期)。
44
+ // Y 树为空时放行(写 Y 是合法首种)。守卫仅消费一次,普通编辑/远端应用
45
+ // 不受影响。
46
+ //
47
+ // room 建连(getOrCreateProvider 单例 + createCollabTicketFn)→ 绑定层 →
48
+ // overlay(远端选中框/名牌/拖拽影子)+ ViewerBadge(谁在看此页)+
49
+ // CanvasCollabPanel(协作者面板,任务 30)。
50
+ //
51
+ // 挂载点:design-editor-layout 的 EditorShell(CanvasProvider 内,engine 与
52
+ // activePageId 就绪处)——collab 不可用(desktop / 未配置 WS / collab=false)
53
+ // 时 provider 为 noop(awareness null)→ binding 恒 null → 本组件渲染
54
+ // null,零侵入。切页(pageId 变化)换 room:旧连接 destroy、新连接重建
55
+ // (effect cleanup 先于新 effect 执行,lazy 双挂载场景安全——同
56
+ // use-collab-doc 口径)。
57
+
58
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react'
59
+ import { buildRoomName } from '@allbluecn/collab-core/rooms'
60
+ import { yTreeToScene } from '@allbluecn/collab-core/canvas-tree'
61
+ import { createCollabTicketFn } from '@/server/serverFns/collab'
62
+ import type { SceneDocument } from '../../document/types'
63
+ import {
64
+ createCanvasPresenceBinding,
65
+ type CanvasPresenceBinding,
66
+ type RemoteCanvasPresence,
67
+ } from '@/lib/collab/bindings/canvas-presence'
68
+ import { createCanvasLocalBinding } from '@/lib/collab/bindings/canvas-local'
69
+ import { createCanvasRemoteBinding, type CanvasRemoteBinding } from '@/lib/collab/bindings/canvas-remote'
70
+ import {
71
+ destroyProvider,
72
+ getOrCreateProvider,
73
+ type CollabProviderHandle,
74
+ } from '@/lib/collab/provider'
75
+ import { isCollabEnabled } from '@/lib/collab/env'
76
+ import { useSelfProfileBroadcast } from '@/lib/collab/use-self-profile-broadcast'
77
+ import { CanvasCollabPanel } from '@/components/collab/canvas-collab-panel'
78
+ import { useCanvasEngineOptional } from '../../react/hooks'
79
+ import { useCanvasSnapshotOptional } from '../../react/canvas-hooks'
80
+ import { projectIntoContainer } from './layout'
81
+ import { CanvasPresenceOverlay } from './presence-overlay'
82
+ import { ViewerBadge } from './viewer-badge'
83
+
84
+ /**
85
+ * 视口心跳(平移不触发 zoom 快照变化,15s 低频 setViewport 覆盖 pan 漂移
86
+ * 并续报 ts 新鲜度;对齐 use-presence-report 心跳节奏)。
87
+ */
88
+ const CANVAS_VIEWPORT_HEARTBEAT_MS = 15_000
89
+
90
+ const EMPTY_REMOTES: RemoteCanvasPresence[] = []
91
+
92
+ /** 远端在场快照订阅(version 缓存快照,同 useCollabFields 口径) */
93
+ function useCanvasRemotes(binding: CanvasPresenceBinding | null): RemoteCanvasPresence[] {
94
+ const subscribe = useCallback(
95
+ (onStoreChange: () => void) => binding?.subscribe(onStoreChange) ?? (() => {}),
96
+ [binding],
97
+ )
98
+ const cacheRef = useRef<{ version: number; remotes: RemoteCanvasPresence[] } | null>(null)
99
+ const getSnapshot = useCallback(() => {
100
+ if (!binding) return EMPTY_REMOTES
101
+ const version = binding.version()
102
+ if (cacheRef.current?.version !== version) {
103
+ cacheRef.current = { version, remotes: binding.readRemotes() }
104
+ }
105
+ return cacheRef.current.remotes
106
+ }, [binding])
107
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
108
+ }
109
+
110
+ export interface CanvasPresenceLayerProps {
111
+ /** 画布页 id(room 键;null = 未加载,不挂载) */
112
+ pageId: string | null
113
+ /**
114
+ * 协同入口参数(任务 30 装配:布局层按 isCollabEnabled() 注入并直通
115
+ * auto-save collab 参数);false 或 env 不可用 → 全短路零挂载。
116
+ */
117
+ collab?: boolean
118
+ /**
119
+ * DB 取页产出的场景文档(EditorShell 的 sceneDoc prop):loadDoc 防覆盖
120
+ * 守卫的武装判据(prop 换新 = 一次 engine.loadDoc(DB) 即将发生)。
121
+ */
122
+ loadedSceneDoc?: SceneDocument | null
123
+ /**
124
+ * 页面数据装载完成(布局层镜像 useDesignPageLoad 的 canvasLoadedRef,与
125
+ * auto-save 同口径,审查 C1):loadPage 先 setActivePageId 再异步取页,
126
+ * 「activePageId 先行、sceneDoc 未到」窗口内引擎仍持旧页文档——此窗口
127
+ * 编辑面(远端桥回放 + 本地桥回写)整体不装配,任何引擎变更零 Y 写,
128
+ * 防旧页内容污染新 room。缺省 false(fail-closed:漏传即不启用编辑面)。
129
+ */
130
+ pageLoaded?: boolean
131
+ }
132
+
133
+ export function CanvasPresenceLayer({
134
+ pageId,
135
+ collab = false,
136
+ loadedSceneDoc = null,
137
+ pageLoaded = false,
138
+ }: CanvasPresenceLayerProps) {
139
+ const engine = useCanvasEngineOptional()
140
+ const snapshot = useCanvasSnapshotOptional()
141
+ const layerRef = useRef<HTMLDivElement | null>(null)
142
+ // 预览态(版本预览 readOnly 引擎持历史版本):编辑面跳过(审查 I2)——
143
+ // reconcileNow/远端回放会把 Y 现行态覆写历史预览;原始类型快照隔离
144
+ // (snapshot 对象每次 store emit 重建,不可作 effect deps)
145
+ const previewing = snapshot?.readOnly ?? false
146
+
147
+ // room 建连(collab 不可用/未加载页 → null,全链路降级)
148
+ const roomName =
149
+ pageId && snapshot?.ready && collab && isCollabEnabled()
150
+ ? buildRoomName({ kind: 'design-canvas', pageId })
151
+ : null
152
+ const [handle, setHandle] = useState<CollabProviderHandle | null>(null)
153
+ useEffect(() => {
154
+ if (!roomName) {
155
+ setHandle(null)
156
+ return
157
+ }
158
+ const created = getOrCreateProvider(roomName, {
159
+ getTicket: async (name) => {
160
+ const { ticket } = await createCollabTicketFn({ data: { roomName: name } })
161
+ return ticket
162
+ },
163
+ })
164
+ setHandle(created)
165
+ return () => {
166
+ setHandle(null)
167
+ destroyProvider(roomName)
168
+ }
169
+ }, [roomName])
170
+
171
+ // profile 头像摘要广播(本端 canvas.user 署名 profile 兜底 + 远端头像数据)
172
+ useSelfProfileBroadcast(handle)
173
+
174
+ // ===== 装配域(任务 30):三绑定 + freezeTargets 接力 + loadDoc 守卫 =====
175
+
176
+ // loadDoc 守卫状态(ref 跨 effect 共享;guard 绑定与三绑定同生命周期)
177
+ const guardArmedRef = useRef(false)
178
+ const guardSuppressedRef = useRef<SceneDocument | null>(null)
179
+ const lastEngineDocRef = useRef<SceneDocument | null>(null)
180
+ const remoteBindingRef = useRef<CanvasRemoteBinding | null>(null)
181
+
182
+ const [binding, setBinding] = useState<CanvasPresenceBinding | null>(null)
183
+ useEffect(() => {
184
+ const awareness = handle?.awareness
185
+ if (!handle || !awareness || !engine) {
186
+ setBinding(null)
187
+ return
188
+ }
189
+
190
+ // 守卫预订阅(先于本地桥订阅:被武装通知先经此消费,本地桥的包装订阅
191
+ // 再按 guardSuppressedRef 跳过同一次通知)。
192
+ // 抑制语义(诚实口径,审查 I3):被武装通知若是装载通知(loadDoc 的
193
+ // 陈旧 DB 快照),内容从引擎**丢弃**——reconcileNow 以 Y 覆写引擎,被
194
+ // 丢弃的正是「打开时刻的 DB 快照」,属预期;武装到 loadDoc 在同一次
195
+ // commit 内相邻 effect 完成(子组件 effect 先于父组件),用户编辑无法
196
+ // 插入该窗口。抑制仅在 Y 树非空时生效:room 未种子化(yTreeToScene
197
+ // null)时无远端态可回滚,DB 装载写 Y 反而是合法首种——放行不抑制。
198
+ lastEngineDocRef.current = null
199
+ guardArmedRef.current = false
200
+ guardSuppressedRef.current = null
201
+ const unsubscribeGuard = engine.onDocChange((doc) => {
202
+ lastEngineDocRef.current = doc
203
+ if (guardArmedRef.current) {
204
+ guardArmedRef.current = false
205
+ let seeded = false
206
+ try {
207
+ seeded = yTreeToScene(handle.doc) !== null
208
+ } catch {
209
+ seeded = false
210
+ }
211
+ if (!seeded) return
212
+ guardSuppressedRef.current = doc
213
+ // 以 Y 权威覆写引擎(丢弃被抑制的装载内容):微任务避开 notify
214
+ // 派发中重入
215
+ queueMicrotask(() => {
216
+ remoteBindingRef.current?.reconcileNow()
217
+ })
218
+ }
219
+ })
220
+
221
+ // 本地桥专用引擎视图:包装 onDocChange,消费守卫抑制(被武装的装载
222
+ // 通知不进本地桥 → 陈旧快照永不回写 Y);其余订阅/读面直通
223
+ const suppressed = guardSuppressedRef
224
+ const localEngine = {
225
+ getDoc: () => engine.getDoc(),
226
+ getSelection: () => engine.getSelection(),
227
+ onDocChange: (cb: (doc: SceneDocument) => void) =>
228
+ engine.onDocChange((doc) => {
229
+ if (suppressed.current === doc) {
230
+ suppressed.current = null
231
+ return
232
+ }
233
+ cb(doc)
234
+ }),
235
+ onDragChange: (cb: (dragging: boolean) => void) => engine.onDragChange(cb),
236
+ }
237
+
238
+ // 装配时序(T27 交接锚点):remote 先行(Y observe)→ presence →
239
+ // 补种(引擎收敛 Y 权威态,本地桥订阅尚未建立——补种通知不回写 Y)→
240
+ // local(freezeTargets 接力 [remote, presence],拖拽冻结纪律)。
241
+ //
242
+ // 编辑面(remote + local)仅在「页已装载且非预览」时装配(审查 C1/I2):
243
+ // - C1 装载窗口:activePageId 先行、sceneDoc 未到期间引擎持旧页文档,
244
+ // 远端回放与本地回写都会污染新 room 的 Y——整面延迟到 pageLoaded;
245
+ // - I2 预览:readOnly 引擎持历史版本,reconcileNow/远端回放会把 Y
246
+ // 现行态覆写历史——编辑面整体跳过(预览本就不应回放远端变更)。
247
+ // presence(只读在场)恒建:预览/窗口期保留「谁在看此页」(T25 行为)。
248
+ const editPlaneReady = !previewing && pageLoaded
249
+ const remote = editPlaneReady
250
+ ? createCanvasRemoteBinding({ doc: handle.doc, engine })
251
+ : null
252
+ remoteBindingRef.current = remote
253
+ const presence = createCanvasPresenceBinding({
254
+ awareness,
255
+ onStatusChange: (listener) => handle.onStatusChange(listener),
256
+ selection: engine,
257
+ })
258
+ remote?.reconcileNow()
259
+ const local = editPlaneReady
260
+ ? createCanvasLocalBinding({
261
+ doc: handle.doc,
262
+ engine: localEngine,
263
+ awareness,
264
+ freezeTargets: [remote as CanvasRemoteBinding, presence],
265
+ })
266
+ : null
267
+ setBinding(presence)
268
+ return () => {
269
+ remoteBindingRef.current = null
270
+ // 销毁序:local 先(解订阅 + 拖拽中断线解冻接力)→ presence → remote
271
+ local?.destroy()
272
+ presence.destroy()
273
+ remote?.destroy()
274
+ unsubscribeGuard()
275
+ setBinding(null)
276
+ }
277
+ }, [handle, engine, previewing, pageLoaded])
278
+
279
+ // 守卫武装(loadDoc 判据):sceneDoc prop ≠ 引擎最近通知文档 → 一次
280
+ // loadDoc(DB) 即将发生(同 CanvasProvider initialDoc ≠ lastEmitted 口径),
281
+ // 武装下个引擎通知为抑制对象。仅页已装载且非预览时武装(编辑面在场的
282
+ // 前提,与 binding 门控一致);子组件 effect 先于父组件(CanvasProvider
283
+ // 的 initialDoc loadDoc effect)执行,武装先于通知。仅消费一次。
284
+ useEffect(() => {
285
+ guardArmedRef.current = false
286
+ if (!handle || !engine || !binding || !pageLoaded || previewing) return
287
+ if (loadedSceneDoc && loadedSceneDoc !== lastEngineDocRef.current) {
288
+ guardArmedRef.current = true
289
+ }
290
+ }, [loadedSceneDoc, handle, engine, binding, pageLoaded, previewing])
291
+
292
+ // 视口上报:绑定建立 + zoom 快照变化即时,15s 心跳覆盖 pan 漂移。
293
+ // ⚠ client 坐标口径:toCanvas 入参是浏览器视口坐标,必须带容器原点
294
+ // (cRect.left/top + 尺寸一半),不能用纯 clientWidth/2(容器原点 ≠ 视口
295
+ // 原点,左侧栏/顶部工具条偏移会系统性带偏 viewport 数据——C1 同源问题)
296
+ const reportViewport = useCallback(() => {
297
+ if (!binding || !engine) return
298
+ try {
299
+ const el = layerRef.current
300
+ const rect = el?.getBoundingClientRect()
301
+ const center = engine.toCanvas(
302
+ (rect?.left ?? 0) + (el?.clientWidth ?? 0) / 2,
303
+ (rect?.top ?? 0) + (el?.clientHeight ?? 0) / 2,
304
+ )
305
+ binding.setViewport({
306
+ x: center?.x ?? 0,
307
+ y: center?.y ?? 0,
308
+ zoom: engine.getZoom(),
309
+ })
310
+ } catch {
311
+ // 引擎 dispose 与 store 更新间的一帧竞态(getZoom/toCanvas 内部
312
+ // assertUsable 抛 LeaferEngine disposed):静默跳过本次上报
313
+ }
314
+ }, [binding, engine])
315
+ useEffect(() => {
316
+ reportViewport()
317
+ }, [reportViewport, snapshot?.zoom])
318
+ useEffect(() => {
319
+ if (!binding) return
320
+ const timer = setInterval(reportViewport, CANVAS_VIEWPORT_HEARTBEAT_MS)
321
+ return () => clearInterval(timer)
322
+ }, [binding, reportViewport])
323
+
324
+ // board→容器坐标投影(C1 容器偏移校正):engine.fromCanvas 产物是浏览器
325
+ // 视口坐标,而 overlay SVG 挂在本层容器(absolute inset-0)上——经
326
+ // projectIntoContainer 减去容器原点(annotation-overlay 的 -cRect.left
327
+ // 先例口径)。矩形每次现读(overlay 的 250ms tick 顺带覆盖容器 resize);
328
+ // 抛错(引擎 dispose 竞态)由 overlay 的布局 try/catch 承接
329
+ const fromCanvasInLayer = useCallback(
330
+ (x: number, y: number): { x: number; y: number } | null => {
331
+ const el = layerRef.current
332
+ if (!el || !engine) return null
333
+ const rect = el.getBoundingClientRect()
334
+ return projectIntoContainer(
335
+ (px, py) => engine.fromCanvas(px, py),
336
+ { left: rect.left, top: rect.top },
337
+ )(x, y)
338
+ },
339
+ [engine],
340
+ )
341
+
342
+ const remotes = useCanvasRemotes(binding)
343
+ if (!binding) return null
344
+
345
+ return (
346
+ <div ref={layerRef} className="pointer-events-none absolute inset-0 z-10">
347
+ <CanvasPresenceOverlay remotes={remotes} fromCanvas={fromCanvasInLayer} doc={snapshot?.doc ?? null} />
348
+ <ViewerBadge remotes={remotes} />
349
+ {/* 协作者面板(任务 30):头像组 + 元素级「X 正在编辑」提示 */}
350
+ <CanvasCollabPanel remotes={remotes} doc={snapshot?.doc ?? null} />
351
+ </div>
352
+ )
353
+ }
@@ -0,0 +1,155 @@
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
+ // 远端选中框 + 名牌 overlay(P4a,任务 25)。presentational 组件:投影函数
19
+ // 与 doc 由 presence-layer 注入(生产为含容器偏移校正的 engine.fromCanvas
20
+ // 包装;测试注入假实现——真 Leafer headless 限制下 DOM 层测试一律走假投影,
21
+ // memrec 纪律)。
22
+ //
23
+ // 只读渲染纪律:不写本地文档、不碰序列化、不建 Y 树;布局经 layout.ts
24
+ // 纯函数产出纯数值(无 ILeaf 进 React 层)。渲染形态对齐 annotation-overlay
25
+ // 先例(绝对定位 SVG + pointer-events-none),漂移校正用 250ms 低频 tick
26
+ // 而非连续 rAF(远端选中框 P4a 低精度,省持续帧预算;viewport 快照只覆盖
27
+ // zoom 变化,pan 漂移由此 tick 兜底)。
28
+
29
+ import { useEffect, useMemo, useState } from "react"
30
+ import { m } from "@/paraglide/messages.js"
31
+ import type { RemoteCanvasPresence } from "@/lib/collab/bindings/canvas-presence"
32
+ import type { SceneDocument } from "../../document/types"
33
+ import { computePresenceLayout, PRESENCE_TAG_HEIGHT_PX } from "./layout"
34
+
35
+ /**
36
+ * board→容器坐标投影(生产 = projectIntoContainer 包装的 engine.fromCanvas,
37
+ * 已减去画布容器原点;测试注入纯函数假实现)
38
+ */
39
+ export type PresenceFromCanvas = (x: number, y: number) => { x: number; y: number } | null
40
+
41
+ /** 漂移校正 tick 间隔(ms) */
42
+ const VIEWPORT_REFRESH_MS = 250
43
+
44
+ /** 名牌文案宽度估算:CJK 全宽 ~10px、半角 ~6px @10px 字号 + 内边距 */
45
+ function tagTextWidth(text: string): number {
46
+ let width = 12
47
+ for (const ch of text) width += ch.charCodeAt(0) > 0x2e80 ? 10 : 6
48
+ return width
49
+ }
50
+
51
+ export interface CanvasPresenceOverlayProps {
52
+ remotes: RemoteCanvasPresence[]
53
+ fromCanvas: PresenceFromCanvas | null
54
+ doc: SceneDocument | null
55
+ }
56
+
57
+ export function CanvasPresenceOverlay({ remotes, fromCanvas, doc }: CanvasPresenceOverlayProps) {
58
+ const [tick, setTick] = useState(0)
59
+
60
+ useEffect(() => {
61
+ if (remotes.length === 0) return
62
+ const timer = setInterval(() => setTick((t) => t + 1), VIEWPORT_REFRESH_MS)
63
+ return () => clearInterval(timer)
64
+ }, [remotes.length])
65
+
66
+ const layout = useMemo(() => {
67
+ if (!fromCanvas || !doc || remotes.length === 0) return []
68
+ try {
69
+ return computePresenceLayout({
70
+ doc,
71
+ remotes,
72
+ fromCanvas,
73
+ })
74
+ } catch {
75
+ // 引擎 dispose 与 store 更新间的一帧竞态(engine.fromCanvas 内部
76
+ // assertUsable 抛 LeaferEngine disposed):跳过本帧重投影
77
+ return []
78
+ }
79
+ // tick 仅作重投影触发器(pan/缩放漂移校正),不参与计算
80
+ }, [fromCanvas, doc, remotes, tick])
81
+
82
+ if (layout.length === 0) return null
83
+
84
+ return (
85
+ <svg
86
+ data-testid="canvas-presence-overlay"
87
+ className="pointer-events-none absolute inset-0 h-full w-full"
88
+ >
89
+ {layout.map((user) => (
90
+ <g key={user.clientId} data-presence-user={user.userId}>
91
+ {/* 拖拽影子(任务 30):半透明虚线轮廓——拖拽中的实时位置,不遮内容 */}
92
+ {user.shadows.map((box, index) => (
93
+ <rect
94
+ key={`shadow-${index}`}
95
+ data-presence-shadow={user.userId}
96
+ x={box.x}
97
+ y={box.y}
98
+ width={box.width}
99
+ height={box.height}
100
+ fill={user.color}
101
+ fillOpacity={0.14}
102
+ stroke={user.color}
103
+ strokeWidth={1.5}
104
+ strokeDasharray="5 4"
105
+ rx={2}
106
+ opacity={0.85}
107
+ />
108
+ ))}
109
+ {user.boxes.map((box, index) => (
110
+ <rect
111
+ key={index}
112
+ data-presence-box={user.userId}
113
+ x={box.x}
114
+ y={box.y}
115
+ width={box.width}
116
+ height={box.height}
117
+ fill="none"
118
+ stroke={user.color}
119
+ strokeWidth={1.5}
120
+ rx={2}
121
+ opacity={0.9}
122
+ />
123
+ ))}
124
+ {user.tag && (
125
+ <g data-presence-tag={user.userId}>
126
+ <rect
127
+ x={user.tag.x}
128
+ y={user.tag.y}
129
+ width={tagTextWidth(buildPresenceTagText(user.tag.name, user.tag.count))}
130
+ height={PRESENCE_TAG_HEIGHT_PX}
131
+ rx={3}
132
+ fill={user.color}
133
+ />
134
+ <text
135
+ x={user.tag.x + 6}
136
+ y={user.tag.y + 11.5}
137
+ fontSize={10}
138
+ fontWeight={600}
139
+ fill="#ffffff"
140
+ >
141
+ {buildPresenceTagText(user.tag.name, user.tag.count)}
142
+ </text>
143
+ </g>
144
+ )}
145
+ </g>
146
+ ))}
147
+ </svg>
148
+ )
149
+ }
150
+
151
+ /** 名牌文案(布局层数据 + 展示层兜底组装):空名降级 i18n「未知用户」,多选附计数 */
152
+ export function buildPresenceTagText(name: string, count: number): string {
153
+ const displayName = name.length > 0 ? name : m.collab_activityUnknownUser()
154
+ return count > 1 ? `${displayName} · ${count}` : displayName
155
+ }
@@ -0,0 +1,80 @@
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
+ // 「谁在看此页」徽标(P4a,任务 25):画布视口左上角的在场 pill——色点
19
+ // (getUserColor 哈希色)+ 人数,hover 列出每人姓名与选中数(名牌 tooltip
20
+ // 的 i18n 承载面)。presentational:remotes 由 presence-layer 注入。
21
+ //
22
+ // 精度口径(P4a):只表「在此 room」不表视口朝向——viewport 字段已入
23
+ // awareness schema 供 P4b 远端视口指示器升级。
24
+
25
+ import { m } from '@/paraglide/messages.js'
26
+ import type { RemoteCanvasPresence } from '@/lib/collab/bindings/canvas-presence'
27
+ import { getUserColor } from '@/lib/collab/user-color'
28
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
29
+
30
+ export interface ViewerBadgeProps {
31
+ remotes: RemoteCanvasPresence[]
32
+ }
33
+
34
+ export function ViewerBadge({ remotes }: ViewerBadgeProps) {
35
+ if (remotes.length === 0) return null
36
+ return (
37
+ <div className="pointer-events-auto absolute left-2 top-2 z-20" data-testid="canvas-presence-badge">
38
+ <Tooltip>
39
+ <TooltipTrigger
40
+ className="flex h-6 items-center gap-1.5 rounded-full border border-border bg-background/90 px-2 shadow-sm"
41
+ aria-label={m.collab_canvasViewersCount({ count: remotes.length })}
42
+ >
43
+ <span className="flex -space-x-1">
44
+ {remotes.map((remote) => (
45
+ <span
46
+ key={remote.clientId}
47
+ className="inline-block size-2.5 rounded-full border border-background"
48
+ style={{ backgroundColor: getUserColor(remote.userId) }}
49
+ />
50
+ ))}
51
+ </span>
52
+ {/* text-[10px]:对齐画布角标既有字号口径(CanvasToolbarBar 自动保存角标同款) */}
53
+ <span className="text-[10px] leading-none tabular-nums text-muted-foreground">
54
+ {m.collab_canvasViewersCount({ count: remotes.length })}
55
+ </span>
56
+ </TooltipTrigger>
57
+ <TooltipContent side="bottom" align="start">
58
+ <div className="flex flex-col gap-0.5">
59
+ {remotes.map((remote) => (
60
+ <span key={remote.clientId} className="flex items-center gap-1.5">
61
+ <span
62
+ className="inline-block size-2 shrink-0 rounded-full"
63
+ style={{ backgroundColor: getUserColor(remote.userId) }}
64
+ />
65
+ {remote.selection.length > 0
66
+ ? m.collab_canvasViewerRow({
67
+ name: remote.name || m.collab_activityUnknownUser(),
68
+ count: remote.selection.length,
69
+ })
70
+ : m.collab_canvasViewerIdle({
71
+ name: remote.name || m.collab_activityUnknownUser(),
72
+ })}
73
+ </span>
74
+ ))}
75
+ </div>
76
+ </TooltipContent>
77
+ </Tooltip>
78
+ </div>
79
+ )
80
+ }
@@ -321,8 +321,8 @@ describe("canvas hooks", () => {
321
321
  it("undo/redo 快捷键语法通过 @tanstack/hotkeys 校验", () => {
322
322
  for (const hotkey of [UNDO_HOTKEY, REDO_HOTKEY, GROUP_HOTKEY, UNGROUP_HOTKEY] as Hotkey[]) {
323
323
  const result = validateHotkey(hotkey)
324
- expect(result.errors, hotkey).toEqual([])
325
- expect(result.warnings, hotkey).toEqual([])
324
+ expect(result.errors, String(hotkey)).toEqual([])
325
+ expect(result.warnings, String(hotkey)).toEqual([])
326
326
  }
327
327
  })
328
328
 
@@ -337,8 +337,8 @@ describe("canvas hooks", () => {
337
337
  HIDE_HOTKEY,
338
338
  ] as Hotkey[]) {
339
339
  const result = validateHotkey(hotkey)
340
- expect(result.errors, hotkey).toEqual([])
341
- expect(result.warnings, hotkey).toEqual([])
340
+ expect(result.errors, String(hotkey)).toEqual([])
341
+ expect(result.warnings, String(hotkey)).toEqual([])
342
342
  }
343
343
  })
344
344
 
@@ -278,7 +278,9 @@ describe("DesignAgentPanel 组件级重生成(FS-8.4)", () => {
278
278
 
279
279
  await waitFor(() => expect(fetchMock).toHaveBeenCalled())
280
280
  const body = lastBody(fetchMock)
281
- const biz = (body.forwardedProps ?? body.data) as Record<string, unknown>
281
+ // connect 第 4 参 run context:wire threadId = 当前会话 id(1.0 threadId 必填语义)
282
+ expect(body.threadId).toBe("conv_1")
283
+ const biz = body.forwardedProps as Record<string, unknown>
282
284
  expect(biz.prompt).toBe("把主按钮改为描边样式")
283
285
  expect(biz.mode).toBe("regenerate")
284
286
  expect(biz.selectedFrameId).toBe("a_1")
@@ -428,7 +428,9 @@ describe("DesignAgentPanel 流式生成消费(FS-8.3)", () => {
428
428
 
429
429
  await waitFor(() => expect(fetchMock).toHaveBeenCalled())
430
430
  const body = lastBody(fetchMock)
431
- const biz = (body.forwardedProps ?? body.data) as Record<string, unknown>
431
+ // connect 第 4 参 run context:wire threadId = 当前会话 id(1.0 threadId 必填语义)
432
+ expect(body.threadId).toBe("conv_1")
433
+ const biz = body.forwardedProps as Record<string, unknown>
432
434
  expect(biz.prompt).toBe("在选中画板里加一个卡片")
433
435
  expect(biz.mode).toBe("generate")
434
436
  expect(biz.selectedFrameId).toBe("a_1")
@@ -372,6 +372,8 @@ export function DesignAgentPanel({ designId, onSceneGenerated, selectedModel, on
372
372
 
373
373
  try {
374
374
  const connection = fetchServerSentEvents(STREAM_URL)
375
+ // 第 4 参 run context:threadId 用本会话 id(convId 由调用方确保非空),
376
+ // runId 每次 connect 一个(一次 connect = 一次 run,与客户端自 mint 语义一致)
375
377
  const stream = connection.connect(
376
378
  [] as never[],
377
379
  {
@@ -384,6 +386,7 @@ export function DesignAgentPanel({ designId, onSceneGenerated, selectedModel, on
384
386
  : {}),
385
387
  },
386
388
  controller.signal,
389
+ { threadId: convId, runId: crypto.randomUUID() },
387
390
  )
388
391
  for await (const chunk of stream) {
389
392
  // 首事件已到解除首事件超时;每个事件重挂空闲超时——空闲窗口自最近