@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,420 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ // 画布 awareness 绑定层(P4a,任务 25,规格 §7.2「画布桥」awareness-only 部分)。
19
+ //
20
+ // ⚠ P4a 边界(与 P4b 编辑协同的任务 26-29 分界):本模块只做「只读远端在场」
21
+ // 的 awareness 读写——选中集 + 视口位置 + user brief。不建 Y 树(room 的
22
+ // Y.Doc 保持空文档)、不碰画布序列化、不回放任何远端 patch 到本地画布。
23
+ //
24
+ // ── awareness schema(任务 27/28 复用契约,禁止另起 key 约定)──────────────
25
+ //
26
+ // room:`canvas:design:{pageId}`(§3.1,buildRoomName({ kind: 'design-canvas' }))。
27
+ // awareness local state(每个客户端各自写入,跨端经 awareness 协议同步):
28
+ //
29
+ // state['canvas'](本模块独占 key,摘除外科手术式、不碰 profile 兄弟字段):
30
+ // {
31
+ // selection: string[] // 元素 id 数组(engine 权威选中态原样上报)
32
+ // viewport: { x, y, zoom } // 视口中心(board 坐标)+ 缩放——P4a 精度低
33
+ // //(仅「谁在看此页」展示),不承诺实时跟随
34
+ // // 平移;P4b 远端视口指示器复用此字段
35
+ // user: { id, name } // user brief(署名:options.identity →
36
+ // // awareness profile → `client:{clientID}` 降级,
37
+ // // 空名降级先例同协同头像/fields editing)
38
+ // ts: number // 写入时刻(Date.now(),新鲜度/排序)
39
+ // }
40
+ // state['profile'](既有 use-self-profile-broadcast 广播,本模块不写只读):
41
+ // 头像摘要 { id, username, avatarConfig?, bgShape?, bgColor? }——远端
42
+ // 渲染侧可用来补头像;身份键以 canvas.user.id 为准。
43
+ //
44
+ // 生命周期:engine 选中变化 / setViewport → 50ms 节流写入(交互级高频走
45
+ // awareness,绝不产生 Y 增量——同 fields editing 的性能红线口径)/ offline
46
+ // 自动摘除(对端靠 awareness 超时+removed 感知离场)/ synced 重放最新缓存 /
47
+ // 冻结期(setFrozen,拖拽/变换预留接口)仅暂停上报不销毁订阅,解冻补报 /
48
+ // 卸载 destroy 摘除。presence-layer 另挂 15s setViewport 心跳:覆盖平移
49
+ // 漂移(pan 不触发 zoom 快照变化)+ ts 新鲜度续报。
50
+ //
51
+ // drag 子字段(P4b 任务 28 起写入、任务 30 渲染消费):canvas key 内可选
52
+ // `drag: { items: [{id,x,y}], ts }`——拖拽会话中本地桥外科手术式写入(不碰
53
+ // selection/viewport/user),presence flush 的整键写不含 drag(冻结接力保证
54
+ // 拖拽期不被抹掉);readRemotes 透传解析后的 drag 供影子渲染,畸形丢弃。
55
+ //
56
+ // 分层:无 React / 无 Leafer 依赖(engine 以结构子集注入,可脱离组件树单测
57
+ // ——双客户端真实 Y.Doc + Awareness 直连验证,同 bindings-fields 先例)。
58
+
59
+ import type {
60
+ CollabAwareness,
61
+ CollabConnectionStatus,
62
+ } from '../provider'
63
+
64
+ /** awareness state 中本模块独占的 key(任务 27/28 复用) */
65
+ export const CANVAS_AWARENESS_KEY = 'canvas'
66
+
67
+ /**
68
+ * 上报节流窗口:选中集变化是交互级高频(拖拽框选逐帧触发),落在性能红线
69
+ * 30-60ms 档(约 1-2 帧,肉眼无感延迟)。leading 首报即时 + 窗口内合并尾随。
70
+ */
71
+ export const CANVAS_PRESENCE_THROTTLE_MS = 50
72
+
73
+ /** 视口位置(board 坐标系中心 + 缩放;P4a 低精度,见 schema 注释) */
74
+ export interface CanvasViewport {
75
+ x: number
76
+ y: number
77
+ zoom: number
78
+ }
79
+
80
+ /** awareness `canvas` key 的值形态(任务 27/28 复用) */
81
+ export interface CanvasPresenceState {
82
+ selection: string[]
83
+ viewport: CanvasViewport
84
+ user: { id: string; name: string }
85
+ ts: number
86
+ }
87
+
88
+ /** 一个远端协作者的在场快照(渲染插件数据面:选中框 + 名牌 + 查看徽标) */
89
+ export interface RemoteCanvasPresence {
90
+ /** awareness clientID(多 tab 同人区分;React key / 排序稳定键) */
91
+ clientId: number
92
+ userId: string
93
+ name: string
94
+ selection: string[]
95
+ viewport: CanvasViewport
96
+ ts: number
97
+ /**
98
+ * 拖拽影子(任务 30,P4b):canvas key 的 drag 子字段(本地桥拖拽会话
99
+ * 写入),null/缺省 = 未在拖拽。渲染插件据此画半透明轮廓。
100
+ */
101
+ drag?: CanvasDragShadow | null
102
+ }
103
+
104
+ // ===== drag 子字段(任务 28 本地桥写入面,schema 归 awareness 侧管理;任务 30
105
+ // 渲染消费经本模块透传,禁止渲染层再解析 awareness 裸状态)=====================
106
+
107
+ /**
108
+ * awareness canvas key 内影子子字段的 key(T28 本地桥 + 任务 30 渲染共用,
109
+ * 禁止硬编码 'drag' 字面量)。
110
+ */
111
+ export const CANVAS_DRAG_FIELD = 'drag'
112
+
113
+ /** 单个拖拽影子的位置快照(board 坐标系内节点的父相对 x/y,远端渲染插件按
114
+ * 本地 doc 父链补齐绝对偏移后投影定位画幽灵框) */
115
+ export interface CanvasDragShadowItem {
116
+ id: string
117
+ x: number
118
+ y: number
119
+ }
120
+
121
+ /**
122
+ * 拖拽 awareness 影子(T25 canvas key 的 drag 子字段值形态):
123
+ * 一次拖拽会话的全量位置快照(非增量 delta——远端迟到加入直接可用)。
124
+ */
125
+ export interface CanvasDragShadow {
126
+ items: CanvasDragShadowItem[]
127
+ ts: number
128
+ }
129
+
130
+ /**
131
+ * 校验一份远端 awareness drag 子字段(fail-closed:畸形丢弃不抛错,
132
+ * 同 parseCanvasPresence 口径)。畸形仅影响影子,不影响 presence 主体。
133
+ */
134
+ export function parseCanvasDragShadow(raw: unknown): CanvasDragShadow | null {
135
+ if (typeof raw !== 'object' || raw === null) return null
136
+ const record = raw as Record<string, unknown>
137
+ if (!Array.isArray(record.items)) return null
138
+ const items: CanvasDragShadowItem[] = []
139
+ for (const item of record.items) {
140
+ if (typeof item !== 'object' || item === null) return null
141
+ const entry = item as Record<string, unknown>
142
+ if (typeof entry.id !== 'string' || entry.id.length === 0) return null
143
+ if (!isFiniteNumber(entry.x) || !isFiniteNumber(entry.y)) return null
144
+ items.push({ id: entry.id, x: entry.x, y: entry.y })
145
+ }
146
+ if (!isFiniteNumber(record.ts)) return null
147
+ return { items, ts: record.ts }
148
+ }
149
+
150
+ /**
151
+ * engine 结构子集(真实 LeaferEngine 兼容直传:getSelection 返回权威选中
152
+ * 态、onSelectionChange 订阅选中变化;测试注入 fake)。
153
+ */
154
+ export interface CanvasSelectionSource {
155
+ getSelection(): string[]
156
+ onSelectionChange(cb: (ids: string[]) => void): () => void
157
+ }
158
+
159
+ export interface CanvasPresenceBindingSource {
160
+ /** noop 模式(协同不可用)为 null:全链路安全空操作 */
161
+ readonly awareness: CollabAwareness | null
162
+ /** 连接状态订阅(真实 handle 契约:订阅即回放当前状态) */
163
+ onStatusChange(listener: (status: CollabConnectionStatus) => void): () => void
164
+ readonly selection: CanvasSelectionSource
165
+ }
166
+
167
+ /** 上报署名(缺省走 awareness profile → clientID 降级,同 fields 先例) */
168
+ export interface CanvasPresenceIdentity {
169
+ userId: string
170
+ name: string
171
+ }
172
+
173
+ export interface CanvasPresenceBindingOptions {
174
+ /** 显式署名;仅在 createCanvasPresenceBinding 时生效(同 provider getTicket 惯例) */
175
+ identity?: CanvasPresenceIdentity
176
+ /** 节流窗口覆盖(单测用;缺省 CANVAS_PRESENCE_THROTTLE_MS) */
177
+ throttleMs?: number
178
+ }
179
+
180
+ export interface CanvasPresenceBinding {
181
+ /** 单调递增变更计数(React useSyncExternalStore 快照缓存键) */
182
+ version(): number
183
+ /**
184
+ * 上报视口(board 坐标中心 + zoom;低频:挂载 / zoom 快照变化 / 15s 心跳)。
185
+ * 走与选中集相同的节流通道(挂起时与下一次 flush 合并)。
186
+ */
187
+ setViewport(viewport: CanvasViewport): void
188
+ /**
189
+ * 交互冻结(P4b 拖拽/变换期间纪律的先行落地,规格 §7.2):冻结期仅暂停
190
+ * awareness 上报、不销毁任何订阅(引擎选中照常缓存为最新值),解冻即补报。
191
+ * P4a 只上报不应用远端 patch,故本端不激活此开关(接线随 P4b 落地)。
192
+ */
193
+ setFrozen(frozen: boolean): void
194
+ /**
195
+ * 远端在场快照(自身除外;畸形 fail-closed 丢弃;按 clientID 升序稳定
196
+ * 排序——徽标/名牌顺序不随上报频率抖动)。
197
+ */
198
+ readRemotes(): RemoteCanvasPresence[]
199
+ /** 订阅变更(远端或本端写入触发的 awareness update;不立即回调) */
200
+ subscribe(listener: () => void): () => void
201
+ /** 清理:摘 observer、摘本端 canvas key(保留 profile 兄弟字段)、断订阅、幂等 */
202
+ destroy(): void
203
+ }
204
+
205
+ function isFiniteNumber(v: unknown): v is number {
206
+ return typeof v === 'number' && Number.isFinite(v)
207
+ }
208
+
209
+ /**
210
+ * 校验一份远端 awareness `canvas` state(fail-closed:畸形丢弃不抛错,
211
+ * 同 fields parseEditor / presence-store 口径)。
212
+ */
213
+ export function parseCanvasPresence(
214
+ raw: unknown,
215
+ ): Omit<RemoteCanvasPresence, 'clientId'> | null {
216
+ if (typeof raw !== 'object' || raw === null) return null
217
+ const record = raw as Record<string, unknown>
218
+ if (!Array.isArray(record.selection)) return null
219
+ if (!record.selection.every((id) => typeof id === 'string')) return null
220
+ if (typeof record.viewport !== 'object' || record.viewport === null) return null
221
+ const viewport = record.viewport as Record<string, unknown>
222
+ if (!isFiniteNumber(viewport.x) || !isFiniteNumber(viewport.y) || !isFiniteNumber(viewport.zoom)) {
223
+ return null
224
+ }
225
+ if (typeof record.user !== 'object' || record.user === null) return null
226
+ const user = record.user as Record<string, unknown>
227
+ if (typeof user.id !== 'string' || user.id.length === 0) return null
228
+ if (typeof user.name !== 'string') return null
229
+ if (!isFiniteNumber(record.ts)) return null
230
+ // drag 子字段(任务 30):可选;畸形仅丢弃影子,不影响 presence 主体
231
+ const drag = record.drag === undefined ? null : parseCanvasDragShadow(record.drag)
232
+ return {
233
+ userId: user.id,
234
+ name: user.name,
235
+ selection: record.selection as string[],
236
+ viewport: { x: viewport.x, y: viewport.y, zoom: viewport.zoom },
237
+ ts: record.ts,
238
+ drag,
239
+ }
240
+ }
241
+
242
+ /** 从 awareness local state 读 profile 署名(use-self-profile-broadcast 广播) */
243
+ function profileIdentity(awareness: CollabAwareness): CanvasPresenceIdentity | null {
244
+ const profile = awareness.getLocalState()?.profile as
245
+ | { id?: unknown; username?: unknown }
246
+ | undefined
247
+ if (
248
+ typeof profile?.id === 'string' &&
249
+ profile.id.length > 0 &&
250
+ typeof profile.username === 'string'
251
+ ) {
252
+ return { userId: profile.id, name: profile.username }
253
+ }
254
+ return null
255
+ }
256
+
257
+ /** 外科手术式摘除本端 canvas key(保留 profile 等兄弟字段,同 fields 先例) */
258
+ function removeCanvasKey(awareness: CollabAwareness): void {
259
+ const local = awareness.getLocalState()
260
+ if (!local) return
261
+ const next: Record<string, unknown> = { ...local }
262
+ if (!(CANVAS_AWARENESS_KEY in next)) return
263
+ delete next[CANVAS_AWARENESS_KEY]
264
+ awareness.setLocalState(next)
265
+ }
266
+
267
+ export function createCanvasPresenceBinding(
268
+ source: CanvasPresenceBindingSource,
269
+ options: CanvasPresenceBindingOptions = {},
270
+ ): CanvasPresenceBinding {
271
+ const { awareness } = source
272
+ const throttleMs = options.throttleMs ?? CANVAS_PRESENCE_THROTTLE_MS
273
+
274
+ let version = 0
275
+ let destroyed = false
276
+ let frozen = false
277
+ let status: CollabConnectionStatus = 'connecting'
278
+ const listeners = new Set<() => void>()
279
+ /** 最新本地态缓存(冻结/offline 期间照常更新,仅不上报) */
280
+ let latestSelection: string[] = []
281
+ let latestViewport: CanvasViewport = { x: 0, y: 0, zoom: 1 }
282
+
283
+ let flushTimer: ReturnType<typeof setTimeout> | null = null
284
+ let lastFlushAt = 0
285
+
286
+ const notify = () => {
287
+ version += 1
288
+ for (const listener of listeners) listener()
289
+ }
290
+
291
+ const resolveIdentity = (): CanvasPresenceIdentity => {
292
+ if (options.identity) return options.identity
293
+ if (awareness) {
294
+ const fromProfile = profileIdentity(awareness)
295
+ if (fromProfile) return fromProfile
296
+ return { userId: `client:${awareness.clientID}`, name: '' }
297
+ }
298
+ return { userId: 'client:0', name: '' }
299
+ }
300
+
301
+ const flush = () => {
302
+ if (!awareness || destroyed || frozen || status === 'offline') return
303
+ const identity = resolveIdentity()
304
+ const state: CanvasPresenceState = {
305
+ selection: [...latestSelection],
306
+ viewport: { ...latestViewport },
307
+ // schema 序列化口径 user:{id,name}(与 fields editing 的 userId 键区分:
308
+ // canvas state 自包含远端可解析,消费方无需二次映射)
309
+ user: { id: identity.userId, name: identity.name },
310
+ ts: Date.now(),
311
+ }
312
+ awareness.setLocalStateField(CANVAS_AWARENESS_KEY, state)
313
+ }
314
+
315
+ /** leading 即时 + 窗口内合并尾随(50ms 交互级节流) */
316
+ const scheduleFlush = () => {
317
+ if (destroyed || !awareness) return
318
+ if (frozen || status === 'offline') return
319
+ const elapsed = Date.now() - lastFlushAt
320
+ if (elapsed >= throttleMs) {
321
+ lastFlushAt = Date.now()
322
+ flush()
323
+ return
324
+ }
325
+ if (flushTimer) return
326
+ flushTimer = setTimeout(() => {
327
+ flushTimer = null
328
+ // 冻结/offline 在窗口内翻转:落地前再查一次(仅暂停不销毁语义)
329
+ if (destroyed || frozen || status === 'offline') return
330
+ lastFlushAt = Date.now()
331
+ flush()
332
+ }, throttleMs - elapsed)
333
+ }
334
+
335
+ const onEngineSelection = (ids: string[]) => {
336
+ if (destroyed) return
337
+ latestSelection = ids
338
+ scheduleFlush()
339
+ }
340
+ const unsubscribeEngine = source.selection.onSelectionChange(onEngineSelection)
341
+ // 订阅即取一次权威态(挂载前已有选中的场景)
342
+ latestSelection = source.selection.getSelection()
343
+
344
+ const onAwarenessUpdate = () => notify()
345
+ if (awareness) awareness.on('update', onAwarenessUpdate)
346
+
347
+ const unsubscribeStatus = source.onStatusChange((next) => {
348
+ if (destroyed) return
349
+ const prev = status
350
+ status = next
351
+ if (next === 'offline') {
352
+ // 断连自动清:对端靠 awareness 超时/removed 感知离场;缓存保留供重放;
353
+ // 挂起的尾随上报作废(offline 期间 scheduleFlush/flush 均短路)
354
+ if (flushTimer) {
355
+ clearTimeout(flushTimer)
356
+ flushTimer = null
357
+ }
358
+ if (awareness && prev !== 'offline') removeCanvasKey(awareness)
359
+ } else if (prev === 'offline' && next === 'synced') {
360
+ // 恢复重放最新缓存(detach/attach 往返可能丢 local state,同 fields 防御)
361
+ scheduleFlush()
362
+ }
363
+ })
364
+
365
+ return {
366
+ version: () => version,
367
+
368
+ setViewport: (viewport) => {
369
+ if (destroyed) return
370
+ latestViewport = viewport
371
+ scheduleFlush()
372
+ },
373
+
374
+ setFrozen: (next) => {
375
+ if (destroyed || frozen === next) return
376
+ frozen = next
377
+ if (!next) scheduleFlush() // 解冻补报最新缓存
378
+ },
379
+
380
+ readRemotes: () => {
381
+ if (!awareness) return []
382
+ const remotes: RemoteCanvasPresence[] = []
383
+ for (const [clientId, state] of awareness.getStates()) {
384
+ if (clientId === awareness.clientID) continue // 只显示他人
385
+ const parsed = parseCanvasPresence(
386
+ (state ?? {})[CANVAS_AWARENESS_KEY],
387
+ )
388
+ if (!parsed) continue
389
+ remotes.push({ clientId, ...parsed })
390
+ }
391
+ // clientID 升序:稳定排序(徽标/名牌不随上报节奏抖动)
392
+ remotes.sort((a, b) => a.clientId - b.clientId)
393
+ return remotes
394
+ },
395
+
396
+ subscribe: (listener) => {
397
+ if (destroyed) return () => {}
398
+ listeners.add(listener)
399
+ return () => {
400
+ listeners.delete(listener)
401
+ }
402
+ },
403
+
404
+ destroy: () => {
405
+ if (destroyed) return
406
+ destroyed = true
407
+ if (flushTimer) {
408
+ clearTimeout(flushTimer)
409
+ flushTimer = null
410
+ }
411
+ unsubscribeEngine()
412
+ if (awareness) {
413
+ awareness.off('update', onAwarenessUpdate)
414
+ removeCanvasKey(awareness)
415
+ }
416
+ unsubscribeStatus()
417
+ listeners.clear()
418
+ },
419
+ }
420
+ }
@@ -0,0 +1,226 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ // 画布远端变更增量应用桥(P4b,任务 27,规格 §7.2「画布桥」远端方向)。
19
+ //
20
+ // 数据流(规格逐字):Y observe → 组装最新 SceneDocument(纯函数 yTreeToScene)
21
+ // → applyRemoteScene(引擎内复用 planSceneDiff 的增量管线)→ 失败兜底
22
+ // rebuildRemoteScene(现有 rebuildScene 回退路径,appliedDoc 基线随重建重置)。
23
+ //
24
+ // ── 基线归属(与 §7.2「复用 planSceneDiff」的对接口径)─────────────────────
25
+ //
26
+ // diff 基线(「当前已应用场景」)由引擎持有(appliedDoc),不在桥上维护:
27
+ // 增量应用必须以「引擎真正渲染中的树」为 prev 才安全——若桥自持基线,本地
28
+ // 编辑(引擎侧变更)会让桥基线过期,算出的计划与引擎状态错位(重复挂载/
29
+ // 漏删),树直接损坏。引擎 applyRemoteScene 内部以 appliedDoc 重跑
30
+ // planSceneDiff,本地/远端并发编辑天然正确;桥只负责把最新 Y 态送进引擎并
31
+ // 依返回值决定是否全量兜底。§7.2「粒度适配」(toPatch 整节点 Y set 等)是
32
+ // 任务 28 本地→Y 方向对同一 diff 计划的适配,与本桥无涉。
33
+ //
34
+ // ── 行为契约 ────────────────────────────────────────────────────────────────
35
+ //
36
+ // - 观察:根 Y.Map(CANVAS_ROOT_KEY)observeDeep——只对画布树变更响应,
37
+ // 同 room 的 TipTap Design 文档(default XmlFragment 双载体)击键不触发对账
38
+ // (observeDeep 先例同 bindings/steps)。
39
+ // - 防抖:连续 update 合并到窗口尾(CANVAS_REMOTE_DEBOUNCE_MS),每次 flush
40
+ // 对「当前 Y 态」反序列化一次——中间态天然丢弃。
41
+ // - 冻结:setFrozen(true)(拖拽/变换中)挂起应用不销毁订阅;解冻后对最新
42
+ // 快照一次 diff 应用(非队列回放,同 canvas-presence 冻结语义)。
43
+ // - 兜底:applyRemoteScene 返回 false 或抛异常 → rebuildRemoteScene(同一
44
+ // 快照直通,引擎内 rebuildScene + appliedDoc 重置);重建再抛(引擎未挂载
45
+ // /已销毁)吞掉等下一次 update 重试——桥绝不向上抛错炸页面。
46
+ // - 初始种子:绑定创建即调度一次对账——room 已有内容(中途加入/快照加载前
47
+ // 到达的 update)时引擎收敛到 Y 态;引擎未就绪(未挂载)安全跳过,由
48
+ // wiring 侧在引擎就绪后调 reconcileNow() 补种。
49
+ // - 空树:yTreeToScene 返回 null(room 未种子化)跳过,不触碰引擎。
50
+ // - 回声:本端经任务 28 写 Y 的事务带 CANVAS_LOCAL_ORIGIN(canvas-local 写入
51
+ // 收敛在该 origin 内)——observeDeep 按 origin 跳过本地回声(引擎是写入源,
52
+ // appliedDoc 已是该态,回声对账是纯浪费);远端到达(provider applyUpdate)
53
+ // 与种子回放 origin 不同,不受过滤。
54
+ //
55
+ // 分层:无 React / 无 Leafer 依赖(引擎以结构子集注入,可脱离组件树单测
56
+ // ——同 canvas-presence/fields 先例)。
57
+
58
+ import type * as Y from 'yjs'
59
+ import type { SceneDocument } from '@/components/design/canvas/document/types'
60
+ import {
61
+ CANVAS_ROOT_KEY,
62
+ yTreeToScene,
63
+ type CollabSceneDocument,
64
+ } from '@allbluecn/collab-core/canvas-tree'
65
+ import { CANVAS_LOCAL_ORIGIN } from './canvas-local'
66
+
67
+ /**
68
+ * 远端 update → flush 防抖窗口:远端画布编辑是手柄释放级低频(§7.1:拖拽中
69
+ * 只走 awareness,Y 只记最终态),100ms 合并多节点操作(粘贴/编组)的连发
70
+ * update,同时保持远端到达的可感延迟低于一帧余。
71
+ */
72
+ export const CANVAS_REMOTE_DEBOUNCE_MS = 100
73
+
74
+ /**
75
+ * 引擎结构子集(真实 DocEngine 兼容直传:applyRemoteScene 增量优先、
76
+ * rebuildRemoteScene 全量兜底——两者是引擎 §7.2 远端方向的公开门面;
77
+ * 测试注入记录调用的 fake)。
78
+ */
79
+ export interface CanvasRemoteEngineTarget {
80
+ /** 增量应用(引擎内以已应用场景为基线 planSceneDiff);false/抛错 = 增量失败 */
81
+ applyRemoteScene(doc: SceneDocument): boolean
82
+ /** 全量重建兜底(现有 rebuildScene 回退门面;appliedDoc 基线随重建重置) */
83
+ rebuildRemoteScene(doc: SceneDocument): void
84
+ }
85
+
86
+ export interface CanvasRemoteBindingSource {
87
+ /** canvas room 的 Y.Doc(本端实例,远端变更经 provider applyUpdate 到达) */
88
+ readonly doc: Y.Doc
89
+ readonly engine: CanvasRemoteEngineTarget
90
+ }
91
+
92
+ export interface CanvasRemoteBindingOptions {
93
+ /** 防抖窗口覆盖(单测用;缺省 CANVAS_REMOTE_DEBOUNCE_MS) */
94
+ debounceMs?: number
95
+ }
96
+
97
+ export interface CanvasRemoteBinding {
98
+ /**
99
+ * 交互冻结(规格 §7.2:拖拽/变换中本地元素不回放远端 patch):冻结期挂起
100
+ * 应用、不销毁订阅;解冻若存在挂起变更,**同步**对最新快照一次 diff 应用
101
+ * (返回时引擎已收敛——任务 28 释放写序依赖此保证,见 canvas-local
102
+ * CanvasFreezeTarget 契约)。
103
+ */
104
+ setFrozen(frozen: boolean): void
105
+ /** 立即对账一次(绕过防抖;仍尊重冻结)。wiring 在引擎就绪后补种初始态。 */
106
+ reconcileNow(): void
107
+ /** 清理:摘 observer、取消挂起 timer;幂等 */
108
+ destroy(): void
109
+ }
110
+
111
+ /** collab-core 结构镜像 → web SceneDocument(T26 canvas-tree 对账测试锁定字段集合严格一致;运行时防御由引擎入口 sanitizeScene 承担) */
112
+ function toSceneDocument(scene: CollabSceneDocument): SceneDocument {
113
+ return scene as unknown as SceneDocument
114
+ }
115
+
116
+ export function createCanvasRemoteBinding(
117
+ source: CanvasRemoteBindingSource,
118
+ options: CanvasRemoteBindingOptions = {},
119
+ ): CanvasRemoteBinding {
120
+ const debounceMs = options.debounceMs ?? CANVAS_REMOTE_DEBOUNCE_MS
121
+ // 根 Y.Map 实例在 doc 生命周期内稳定(sceneToYTree/yTreeToScene 同例);
122
+ // observeDeep 覆盖 nodes/order/extensions/boardMeta 全部嵌套变更
123
+ const rootMap = source.doc.getMap<unknown>(CANVAS_ROOT_KEY)
124
+
125
+ let destroyed = false
126
+ let frozen = false
127
+ // 冻结期有 update 到达(或 flush 被冻结打断)→ 解冻后需补一次对账
128
+ let pending = false
129
+ let timer: ReturnType<typeof setTimeout> | null = null
130
+
131
+ const flush = () => {
132
+ timer = null
133
+ if (destroyed) return
134
+ if (frozen) {
135
+ pending = true
136
+ return
137
+ }
138
+ pending = false
139
+ let scene: CollabSceneDocument | null = null
140
+ try {
141
+ scene = yTreeToScene(source.doc)
142
+ } catch {
143
+ // 反序列化异常(远端写坏 Y 树):跳过本轮,下一次 update 再对账
144
+ return
145
+ }
146
+ if (!scene) return // room 未种子化,不触碰引擎
147
+
148
+ const next = toSceneDocument(scene)
149
+ let applied = false
150
+ try {
151
+ applied = source.engine.applyRemoteScene(next)
152
+ } catch {
153
+ applied = false
154
+ }
155
+ if (applied) return
156
+ // 增量失败 → 全量兜底(同一快照直通;引擎内 rebuildScene + appliedDoc
157
+ // 重置)。引擎未挂载/已销毁时 assertUsable 抛错吞掉——wiring 时序问题,
158
+ // 下一次 update 或 reconcileNow 重试
159
+ try {
160
+ source.engine.rebuildRemoteScene(next)
161
+ } catch {
162
+ // 引擎不可用:跳过本轮
163
+ }
164
+ }
165
+
166
+ const schedule = () => {
167
+ if (destroyed || timer) return
168
+ timer = setTimeout(flush, debounceMs)
169
+ }
170
+
171
+ // observeDeep 回调签名经 Parameters 提取(YEvent 泛型约束 AbstractType,
172
+ // 手写泛型实参会 tsc 报错;transaction.origin 为本地回声过滤的依据)
173
+ const onCanvasChange: Parameters<Y.Map<unknown>['observeDeep']>[0] = (
174
+ _events,
175
+ transaction,
176
+ ) => {
177
+ if (destroyed) return
178
+ // 本地回声抑制(任务 28 验收点):本端写 Y 的事务按 origin 跳过——引擎
179
+ // 是写入源,无需回灌;远端到达与种子回放 origin 各异,照常对账
180
+ if (transaction.origin === CANVAS_LOCAL_ORIGIN) return
181
+ pending = true
182
+ schedule()
183
+ }
184
+
185
+ rootMap.observeDeep(onCanvasChange)
186
+ // 挂载初始种子:room 已有内容(中途加入)时首次对账;引擎未就绪安全跳过
187
+ schedule()
188
+
189
+ return {
190
+ setFrozen: (next) => {
191
+ if (destroyed || frozen === next) return
192
+ frozen = next
193
+ if (!next && pending) {
194
+ // 解冻即同步补应用(对最新快照一次 diff,非队列回放;同时关掉挂起
195
+ // timer 防二次):任务 28 本地桥的释放写序依赖「setFrozen(false) 返回
196
+ // 时冻结期到达的远端变更已收敛进引擎」——先收敛再写 Y,否则全量对账
197
+ // 会回滚拖拽期间到达的远端变更(审查 C1)。即时化同时收口 T27 审查
198
+ // minor②「解冻补应用过防抖窗」
199
+ if (timer) {
200
+ clearTimeout(timer)
201
+ timer = null
202
+ }
203
+ flush()
204
+ }
205
+ },
206
+
207
+ reconcileNow: () => {
208
+ if (destroyed) return
209
+ if (timer) {
210
+ clearTimeout(timer)
211
+ timer = null
212
+ }
213
+ flush()
214
+ },
215
+
216
+ destroy: () => {
217
+ if (destroyed) return
218
+ destroyed = true
219
+ if (timer) {
220
+ clearTimeout(timer)
221
+ timer = null
222
+ }
223
+ rootMap.unobserveDeep(onCanvasChange)
224
+ },
225
+ }
226
+ }