@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
@@ -18,7 +18,15 @@
18
18
  // BUG 详情主体(规格 §5.4 分区骨架)。peek 侧滑与 /bugs/$bugId 独立路由共用。
19
19
  // 本任务交付 1-6 分区(6=AI 复现报告卡片,任务 27)+ 7(AI 修复时间线,任务 22)+ 8。
20
20
  // 活动/评论 tab 数据由任务 12 接入。
21
- import { lazy, Suspense, useEffect, useRef, useState, type LazyExoticComponent, type ComponentType } from "react";
21
+ //
22
+ // 协同双模式(Yjs 多人协作任务 22,§7.1 entity:bug 行;结构对齐任务 20 的
23
+ // story-detail-body):collab=true 时字段(title/state/severity/assigneeId/
24
+ // labelIds/env)接 useCollabFields、步骤(reproduceSteps.steps)接 useCollabSteps
25
+ // 只读视图、全部保存通道短路——Y 是唯一写手;offline 降级恢复 DB 链路(轻量版
26
+ // 取舍同 T20:降级期不做本地补写队列,DB 直写经 §5.3 门卫路由)。
27
+ // S12 裁决:description 本期不接协同编辑——只读渲染恒走 loader DTO,不挂
28
+ // Collaboration 扩展(room 结构自然预留:种子化/门卫转发/exporter 回写照常)。
29
+ import { lazy, Suspense, useEffect, useMemo, useRef, useState, type LazyExoticComponent, type ComponentType, type ReactNode } from "react";
22
30
  import { Archive, CalendarClock, Hash, LoaderCircle, Sparkles } from "lucide-react";
23
31
  import { Input } from "@/components/ui/input";
24
32
  import { Button } from "@/components/ui/button";
@@ -29,9 +37,16 @@ import { toast } from "@/components/ui/toast";
29
37
  import { requestBugFixFn, updateBugFn } from "@/server/serverFns/bug";
30
38
  import { formatTimestamp } from "@/lib/datetime";
31
39
  import { SOURCE_LABEL } from "@/shared/bug/constants";
32
- import type { BugDetail, BugStoryBrief } from "../types";
40
+ import type { BugDetail, BugStoryBrief, ReproduceSteps } from "../types";
33
41
  import type { UserBrief } from "@/components/story/types";
34
42
  import { parseStoryDescription } from "@/components/story/types";
43
+ import { buildRoomName } from "@allbluecn/collab-core/rooms";
44
+ import { useSession } from "@/components/shared-kit/auth/auth-client";
45
+ import { useCollabDoc } from "@/lib/collab/use-collab-doc";
46
+ import type { CollabProviderHandle } from "@/lib/collab/provider";
47
+ import { useCollabFields, type UseCollabFieldsResult } from "@/lib/collab/bindings/fields";
48
+ import { useCollabSteps } from "@/lib/collab/bindings/steps";
49
+ import { useSelfProfileBroadcast } from "@/lib/collab/use-self-profile-broadcast";
35
50
  import { PropsSection } from "./props-section";
36
51
  import { ReproduceStepsView } from "./reproduce-steps-view";
37
52
  import { ErrorContext } from "./error-context";
@@ -46,28 +61,188 @@ import type { NotionDocEditorProps } from "@/components/tiptap/notion-like";
46
61
  import { toastError } from "@/lib/toast-error";
47
62
  const NotionDocEditor = lazy(() => import("@/components/tiptap/notion-like").then((mod) => ({ default: mod.NotionDocEditor }))) as LazyExoticComponent<ComponentType<NotionDocEditorProps>>;
48
63
 
64
+ /**
65
+ * entity room 的 Y 属主字段清单(任务 22)——与服务端
66
+ * serverFns/bug/bug-crud.ts 的 BUG_Y_OWNED_FIELDS 镜像,两侧必须同步维护
67
+ * (漏字段 = 旁路写互吞/协同态丢写)。
68
+ */
69
+ const BUG_COLLAB_FIELDS = [
70
+ "title",
71
+ "state",
72
+ "severity",
73
+ "assigneeId",
74
+ "labelIds",
75
+ "env",
76
+ ] as const;
77
+
78
+ /** 协同运行时状态(BugCollabGate render-prop 产物;结构对齐任务 20 口径) */
79
+ interface BugCollabRuntimeState {
80
+ /** 会话是否就绪 */
81
+ ready: boolean;
82
+ /** provider offline(降级单机模式,提示条 + 旧保存链路) */
83
+ offline: boolean;
84
+ /** provider connecting(建连窗口,轻提示条;窗口内 Y 是唯一写手) */
85
+ connecting: boolean;
86
+ /** 协同写手活跃(connecting/synced 均算——Y 是唯一写手,DB 链路短路) */
87
+ collabActive: boolean;
88
+ /** room 连接句柄(字段/步骤 binding 数据源;null = 非协同/noop) */
89
+ handle: CollabProviderHandle | null;
90
+ /** 字段 editing 标记署名(session brief;null 走 binding 内 profile/clientID 降级) */
91
+ identity: { userId: string; name: string } | null;
92
+ }
93
+
94
+ /** 非协同分支的协同状态常量(稳定引用,行为与既有单机链路逐字一致) */
95
+ const NON_COLLAB_STATE: BugCollabRuntimeState = {
96
+ ready: true,
97
+ offline: false,
98
+ connecting: false,
99
+ collabActive: false,
100
+ handle: null,
101
+ identity: null,
102
+ };
103
+
104
+ interface BugCollabGateProps {
105
+ bugId: string;
106
+ snapshot: string | null;
107
+ children: (state: BugCollabRuntimeState) => ReactNode;
108
+ }
109
+
110
+ /**
111
+ * 协同运行时装配(条件挂载于 collab 分支,非协同路径不产生任何协同 hook
112
+ * 观察/连接——结构对齐任务 20 StoryCollabGate;S12:无 TipTap Collaboration
113
+ * 配置,description 只读渲染不挂协同扩展)。
114
+ */
115
+ function BugCollabGate({ bugId, snapshot, children }: BugCollabGateProps) {
116
+ const { data: session, isPending } = useSession();
117
+ const roomName = useMemo(
118
+ () => buildRoomName({ kind: "entity-bug", bugId }),
119
+ [bugId],
120
+ );
121
+ const { handle, status } = useCollabDoc({ roomName, snapshot });
122
+ // 自身 profile 广播:字段 binding 的 identity 缺省路径读 awareness profile
123
+ // 兜底署名(同 StoryCollabGate;Bug 详情无 presence 头像组,广播自持)
124
+ useSelfProfileBroadcast(handle);
125
+
126
+ // 会话就绪但拿不到 user(_nav beforeLoad 守卫下不应发生)时不进协同态:
127
+ // collabActive=false 让 DB 保存链路兜底,防「Y 无写手 + DB 短路」双失能
128
+ const sessionUsable = isPending || session?.user?.id != null;
129
+
130
+ return (
131
+ <>
132
+ {children({
133
+ ready: !isPending,
134
+ offline: status === "offline",
135
+ connecting: status === "connecting",
136
+ collabActive: sessionUsable && status !== null && status !== "offline",
137
+ handle,
138
+ identity:
139
+ session?.user?.id
140
+ ? { userId: session.user.id, name: session.user.name || session.user.id }
141
+ : null,
142
+ })}
143
+ </>
144
+ );
145
+ }
146
+
49
147
  interface BugDetailBodyProps {
50
148
  bug: BugDetail;
51
149
  users: UserBrief[];
52
150
  labels: { id: string; name: string; color: string }[];
151
+ /** peek 紧凑元信息:ID+来源 左、累计+最近发生 右(justify-between) */
152
+ compactMeta?: boolean;
153
+ /** 二级页(面包屑已含 ID):隐藏整个元信息行 */
154
+ hideIdentifierRow?: boolean;
53
155
  /** 任一属性编辑成功后回传服务端返回的完整 bug(peek/路由层同步本地状态) */
54
156
  onBugUpdate: (bug: BugDetail) => void;
157
+ /**
158
+ * 协同模式(Yjs 多人协作任务 22,§7.1 Bug 行):true 时字段接
159
+ * useCollabFields、steps 接 useCollabSteps(只读视图),title/props/env
160
+ * 保存通道短路——Y 是唯一写手;offline 降级恢复 DB 链路。缺省 false =
161
+ * 现有单机链路逐字保留(peek 侧滑不传,恒单机)。
162
+ */
163
+ collab?: boolean;
164
+ /** SSR 协同快照(loader 并行取 getCollabSnapshotFn;collab=true 时必传,null = 空文档) */
165
+ collabSnapshot?: string | null;
55
166
  }
56
167
 
57
- export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBodyProps) {
168
+ export function BugDetailBody(props: BugDetailBodyProps) {
169
+ const { collab, collabSnapshot, ...bodyProps } = props;
170
+ // 协同双模式:collab=true 时 gate 组件持有 useSession/useCollabDoc(条件
171
+ // 挂载——非协同路径不产生任何协同 hook 观察/连接);其余完全同分支。
172
+ if (collab) {
173
+ return (
174
+ <BugCollabGate bugId={bodyProps.bug.id} snapshot={collabSnapshot ?? null}>
175
+ {(collabState) => (
176
+ <BugDetailBodyInner {...bodyProps} collabEnabled collabState={collabState} />
177
+ )}
178
+ </BugCollabGate>
179
+ );
180
+ }
181
+ return <BugDetailBodyInner {...bodyProps} collabEnabled={false} collabState={NON_COLLAB_STATE} />;
182
+ }
183
+
184
+ function BugDetailBodyInner({
185
+ bug,
186
+ users,
187
+ labels,
188
+ onBugUpdate,
189
+ compactMeta = false,
190
+ hideIdentifierRow = false,
191
+ collabEnabled,
192
+ collabState,
193
+ }: BugDetailBodyProps & { collabEnabled: boolean; collabState: BugCollabRuntimeState }) {
58
194
  const [title, setTitle] = useState(bug.title);
59
195
  const titleRef = useRef<HTMLInputElement>(null);
196
+ const titleFocusedRef = useRef(false);
60
197
  // 「AI 修复」请求成功后递增:驱动 FixTimeline 与活动流重取(fix_requested/run 落库)
61
198
  const [fixRefreshKey, setFixRefreshKey] = useState(0);
62
199
  const [fixRequesting, setFixRequesting] = useState(false);
200
+ // 「AI 修复」竞态守卫(BUG 模块交付时遗留竞态收口):peek 上下条导航/路由跳转
201
+ // 会销毁旧详情实例(消费方 key={bug.id}),旧请求的迟到响应不得再 toast/setState
202
+ // ——否则「已发起」提示会弹在切换后的 BUG 上。三重校验:
203
+ // fixSeqRef——同实例双击窗口内后发请求序号更大,旧响应整体作废;
204
+ // bugIdRef——实例被复用且 bug 已变(防御性;key 约定下换 id 必重建);
205
+ // fixUnmountedRef——实例已卸载。命中任一:整体丢弃(不 setState 不 toast),
206
+ // fixRequesting 不回置由实例销毁兜底。
207
+ const fixSeqRef = useRef(0);
208
+ const fixBugIdRef = useRef(bug.id);
209
+ fixBugIdRef.current = bug.id;
210
+ const fixUnmountedRef = useRef(false);
211
+ useEffect(() => () => { fixUnmountedRef.current = true; }, []);
63
212
  // 「AI 复现」run 结束后递增:驱动活动流重取(ai_repro_completed 落库)
64
213
  const [reproRefreshKey, setReproRefreshKey] = useState(0);
65
214
 
215
+ // ── 协同态运行时(任务 22)──────────────────────────────────────────
216
+ // 核心不变式(对齐任务 15/20):任一时刻只有一条保存链路活跃。
217
+ // collabActive(connecting/synced)→ Y 唯一写手,DB 全通道短路;
218
+ // offline(降级单机)→ DB 通道恢复,Y 仅作展示源。
219
+ const fields = useCollabFields(
220
+ collabState.handle,
221
+ BUG_COLLAB_FIELDS,
222
+ collabState.identity ? { identity: collabState.identity } : undefined,
223
+ );
224
+ const collabSteps = useCollabSteps(collabState.handle);
225
+ const collabActiveRef = useRef(collabState.collabActive);
226
+ collabActiveRef.current = collabState.collabActive;
227
+
228
+ // 标题输入协同值流:Y 已写值实时回写本地输入(失焦时——聚焦期不打字),
229
+ // 未写 key 回退 loader 值。展示与保存通道共用 titleValue。
230
+ const fieldsValues = fields.values;
231
+ const yTitle = typeof fieldsValues.title === "string" ? fieldsValues.title : null;
66
232
  useEffect(() => {
67
- setTitle(bug.title);
68
- }, [bug.title]);
233
+ if (!collabEnabled) return;
234
+ if (yTitle === null) return;
235
+ if (titleFocusedRef.current) return;
236
+ setTitle((prev) => (prev === yTitle ? prev : yTitle));
237
+ }, [collabEnabled, yTitle]);
238
+
239
+ const titleValue =
240
+ collabEnabled && collabActiveRef.current && yTitle !== null ? yTitle : title;
69
241
 
70
242
  const saveTitle = async () => {
243
+ fields.setEditing("title", false);
244
+ // 协同写手活跃:Y 唯一写手,DB 通道短路(值已随输入即时写 Y)
245
+ if (collabEnabled && collabActiveRef.current) return;
71
246
  const next = title.trim();
72
247
  if (!next || next === bug.title) {
73
248
  setTitle(bug.title);
@@ -83,17 +258,33 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
83
258
  }
84
259
  };
85
260
 
261
+ // 协同标题通道:失焦写 Y(值流 onChange 已即时写——见 onChange 分派)
262
+ const handleTitleChange = (next: string) => {
263
+ setTitle(next);
264
+ if (collabEnabled && collabActiveRef.current) {
265
+ fields.setValue("title", next);
266
+ }
267
+ };
268
+
86
269
  const handleStoryChange = (story: BugStoryBrief | null) => {
87
270
  onBugUpdate({ ...bug, storyId: story?.id ?? null, story });
88
271
  };
89
272
 
90
273
  // 「AI 修复」(§10.3 手动触发):created → toast + 时间线/活动流重取;
91
- // conflict → 结构化返回提示已存在(服务端不抛错)
274
+ // conflict → 结构化返回提示已存在(服务端不抛错)。
275
+ // 竞态守卫:响应返回时实例已卸载 / bug 已切换 / 序号被后发请求超越则整体丢弃
92
276
  const handleRequestFix = async () => {
93
277
  if (fixRequesting) return;
94
278
  setFixRequesting(true);
279
+ const seq = ++fixSeqRef.current;
280
+ const requestBugId = bug.id;
281
+ const stale = () =>
282
+ fixUnmountedRef.current ||
283
+ fixBugIdRef.current !== requestBugId ||
284
+ seq !== fixSeqRef.current;
95
285
  try {
96
- const res = await requestBugFixFn({ data: { bugId: bug.id } });
286
+ const res = await requestBugFixFn({ data: { bugId: requestBugId } });
287
+ if (stale()) return;
97
288
  if (res.status === "created") {
98
289
  toast.success(m.bug_fix_requested_toast());
99
290
  setFixRefreshKey((k) => k + 1);
@@ -106,9 +297,12 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
106
297
  );
107
298
  }
108
299
  } catch (e) {
300
+ if (stale()) return;
109
301
  toastError(e, m.bug_fix_action_failed());
110
302
  } finally {
111
- setFixRequesting(false);
303
+ // 丢弃分支不回置(严格「不 setState」):按钮态随实例销毁重置;
304
+ // 仅同实例最新请求(seq 未被超越且未卸载未换 bug)有权回置
305
+ if (!stale()) setFixRequesting(false);
112
306
  }
113
307
  };
114
308
 
@@ -116,12 +310,81 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
116
310
  const hasErrorContext = bug.source !== "manual" || !!bug.stackSummary || !!bug.url || !!bug.userAgent;
117
311
  const sourceLabel = SOURCE_LABEL[bug.source]?.() ?? bug.source;
118
312
 
313
+ // 协同步骤展示流:Y 已写步骤(种子化/远端/本地写入)覆盖 loader DTO——
314
+ // 远端步骤变化实时回写只读视图(§7.1「不再手动 F5」)。空 Y.Array = Y 无
315
+ // 意见(业务 schema 步骤 min(1)),回退 DTO;expectedResult/actualResult
316
+ // 不归 Y,恒取 DTO。非协同恒等 DTO。
317
+ const effectiveSteps: ReproduceSteps | null = useMemo(() => {
318
+ if (!collabEnabled || collabSteps.steps.length === 0) return bug.reproduceSteps;
319
+ return {
320
+ steps: collabSteps.steps.map((step, index) => ({
321
+ order: index + 1,
322
+ action: step.action,
323
+ ...(step.expected !== "" && { expected: step.expected }),
324
+ })),
325
+ expectedResult: bug.reproduceSteps?.expectedResult,
326
+ actualResult: bug.reproduceSteps?.actualResult,
327
+ };
328
+ }, [collabEnabled, collabSteps.steps, bug.reproduceSteps]);
329
+
330
+ // 协同展示覆盖:Y 已写字段(非 undefined)覆盖 loader bug 值——远端字段
331
+ // 变化实时回写属性区(§7.1)。非协同恒等 bug。
332
+ const effectiveBug: BugDetail = useMemo(() => {
333
+ if (!collabEnabled) return bug;
334
+ const next: BugDetail = { ...bug };
335
+ if (typeof fieldsValues.title === "string") next.title = fieldsValues.title;
336
+ if (typeof fieldsValues.state === "string") next.state = fieldsValues.state as BugDetail["state"];
337
+ if (typeof fieldsValues.severity === "string") next.severity = fieldsValues.severity as BugDetail["severity"];
338
+ if (fieldsValues.assigneeId !== undefined) {
339
+ next.assignee =
340
+ fieldsValues.assigneeId == null
341
+ ? null
342
+ : users.find((u) => u.id === fieldsValues.assigneeId) ?? null;
343
+ }
344
+ if (Array.isArray(fieldsValues.labelIds)) {
345
+ const ids = fieldsValues.labelIds.filter((id): id is string => typeof id === "string");
346
+ next.labels = ids
347
+ .map((id) => labels.find((l) => l.id === id))
348
+ .filter((l): l is { id: string; name: string; color: string } => l != null);
349
+ }
350
+ if (fieldsValues.env !== undefined) {
351
+ next.env = fieldsValues.env == null ? null : String(fieldsValues.env);
352
+ }
353
+ return next;
354
+ }, [collabEnabled, bug, fieldsValues, users, labels]);
355
+
119
356
  return (
120
357
  <div className="flex min-h-0 flex-1 flex-col">
121
- <div className="min-h-0 flex-1 overflow-y-auto" style={{ scrollbarGutter: "stable" } as React.CSSProperties}>
358
+ {collabEnabled && collabState.connecting && (
359
+ <div
360
+ data-testid="collab-connecting-banner"
361
+ role="status"
362
+ className="bg-sky-500/10 text-sky-700 dark:text-sky-400 border-b border-sky-500/20 text-xs px-4 py-1.5 text-center"
363
+ >
364
+ {m.collab_connectingBanner()}
365
+ </div>
366
+ )}
367
+ {collabEnabled && collabState.offline && (
368
+ <div
369
+ data-testid="collab-offline-banner"
370
+ role="status"
371
+ className="bg-amber-500/10 text-amber-700 dark:text-amber-400 border-b border-amber-500/20 text-xs px-4 py-1.5 text-center"
372
+ >
373
+ {m.collab_offlineBanner()}
374
+ </div>
375
+ )}
376
+ {/* data-slot:BugPanelHeader 顶栏滚动渐显标题的滚动源(story-detail-scroll 同款,capture 监听) */}
377
+ <div data-slot="bug-detail-scroll" className="min-h-0 flex-1 overflow-y-auto" style={{ scrollbarGutter: "stable" } as React.CSSProperties}>
122
378
  {/* ① 头部:identifier + 来源/计数元信息 + 标题行内编辑 */}
123
379
  <header className="space-y-1 px-6 pt-4 pb-3">
124
- <div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
380
+ <div
381
+ className={
382
+ compactMeta
383
+ ? "flex items-center justify-between gap-2 text-xs text-muted-foreground"
384
+ : "flex flex-wrap items-center gap-2 text-xs text-muted-foreground"
385
+ }
386
+ >
387
+ {!hideIdentifierRow && (
125
388
  <Tooltip>
126
389
  <TooltipTrigger
127
390
  render={
@@ -141,14 +404,19 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
141
404
  </TooltipTrigger>
142
405
  <TooltipContent>{m.story_detail_clickToCopy()}</TooltipContent>
143
406
  </Tooltip>
407
+ )}
144
408
  <Badge variant="outline" className="px-1.5 py-0 text-xs font-normal">
145
409
  {sourceLabel}
146
410
  </Badge>
411
+ {hideIdentifierRow ? null : (
412
+ <>
147
413
  <span className="inline-flex items-center gap-1">
148
414
  <CalendarClock className="size-3" />
149
415
  {m.bug_list_occurrences({ count: bug.totalCount })}
150
416
  </span>
151
417
  <span>{m.bug_list_last_occur()} {formatTimestamp(bug.lastOccurAt)}</span>
418
+ </>
419
+ )}
152
420
  {bug.archived && (
153
421
  <Badge variant="outline" className="gap-1 px-1.5 py-0 text-xs font-normal">
154
422
  <Archive className="size-3" />
@@ -159,9 +427,16 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
159
427
  <div className="flex items-start gap-2">
160
428
  <Input
161
429
  ref={titleRef}
162
- value={title}
163
- onChange={(e) => setTitle(e.target.value)}
164
- onBlur={() => void saveTitle()}
430
+ value={titleValue}
431
+ onChange={(e) => handleTitleChange(e.target.value)}
432
+ onFocus={() => {
433
+ titleFocusedRef.current = true;
434
+ fields.setEditing("title", true);
435
+ }}
436
+ onBlur={() => {
437
+ titleFocusedRef.current = false;
438
+ void saveTitle();
439
+ }}
165
440
  onKeyDown={(e) => {
166
441
  if (e.key === "Enter") {
167
442
  e.preventDefault();
@@ -191,10 +466,25 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
191
466
  </div>
192
467
  </header>
193
468
 
194
- {/* ② 属性区 */}
195
- <PropsSection bug={bug} users={users} labels={labels} onBugUpdate={onBugUpdate} onStoryChange={handleStoryChange} />
469
+ {/* ② 属性区(协同态字段写手分派见 PropsSection collabProps) */}
470
+ <PropsSection
471
+ bug={effectiveBug}
472
+ users={users}
473
+ labels={labels}
474
+ onBugUpdate={onBugUpdate}
475
+ onStoryChange={handleStoryChange}
476
+ collabProps={
477
+ collabEnabled
478
+ ? {
479
+ collabActive: collabActiveRef.current,
480
+ setValue: fields.setValue,
481
+ setEditing: fields.setEditing,
482
+ }
483
+ : undefined
484
+ }
485
+ />
196
486
 
197
- {/* ③ 描述(TipTap 只读渲染) */}
487
+ {/* ③ 描述(TipTap 只读渲染;S12 裁决:协同期同样只读,不接 Collaboration) */}
198
488
  <section className="space-y-2 border-t px-6 py-4">
199
489
  <h3 className="text-sm font-semibold">{m.bug_create_description_label()}</h3>
200
490
  {descContent.markdown || descContent.html ? (
@@ -225,8 +515,8 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
225
515
  )}
226
516
  </section>
227
517
 
228
- {/* ④ 复现步骤视图 */}
229
- <ReproduceStepsView steps={bug.reproduceSteps} />
518
+ {/* ④ 复现步骤视图(协同态由 Y steps 驱动——远端变更实时刷新;只读) */}
519
+ <ReproduceStepsView steps={effectiveSteps} />
230
520
 
231
521
  {/* ⑤ 错误上下文(SDK/扩展/收集页来源)+ 事件样本时间线 */}
232
522
  {hasErrorContext && (
@@ -262,3 +552,11 @@ export function BugDetailBody({ bug, users, labels, onBugUpdate }: BugDetailBody
262
552
  </div>
263
553
  );
264
554
  }
555
+
556
+ /** PropsSection 协同写手分派入参(collabEnabled 时由 BugDetailBody 注入) */
557
+ export interface PropsSectionCollabProps {
558
+ /** 协同写手活跃(false = 降级走 DB 通道) */
559
+ collabActive: boolean;
560
+ setValue: UseCollabFieldsResult["setValue"];
561
+ setEditing: UseCollabFieldsResult["setEditing"];
562
+ }
@@ -35,6 +35,7 @@ import { toast } from "@/components/ui/toast";
35
35
  import type { BugCommentWithReplies } from "../types";
36
36
  import { formatRelative } from "./activity-timeline";
37
37
  import { CommentInput } from "./comment-input";
38
+ import { CommentReactions } from "./comment-reactions";
38
39
  import { m } from "@/paraglide/messages.js";
39
40
 
40
41
  interface CommentItemProps {
@@ -96,11 +97,12 @@ export function CommentItem({ bugId, comment, onAppendComment, onRemoveComment }
96
97
 
97
98
  return (
98
99
  <div className="space-y-3">
99
- <div className="flex gap-3">
100
+ {/* group/comment:hover 评论主体(不含回复区)时显出 CommentReactions 的快捷按钮 */}
101
+ <div className="group/comment flex gap-3">
100
102
  <UserAvatar
101
103
  name={comment.user.username}
102
104
  avatarConfig={comment.user.avatarConfig}
103
- bgShape={comment.user.bgShape as "circle" | "rounded" | "square" | null}
105
+ bgShape={comment.user.bgShape}
104
106
  bgColor={comment.user.bgColor}
105
107
  size="sm"
106
108
  className="shrink-0"
@@ -159,6 +161,9 @@ export function CommentItem({ bugId, comment, onAppendComment, onRemoveComment }
159
161
  <p className="text-sm whitespace-pre-wrap">{displayContent}</p>
160
162
  )}
161
163
 
164
+ {/* 评论级表情回应(chips + hover 快捷 + picker);空聚合时不占视觉位 */}
165
+ <CommentReactions commentId={comment.id} reactions={comment.reactions} />
166
+
162
167
  <div className="flex items-center gap-3">
163
168
  <button
164
169
  type="button"
@@ -0,0 +1,138 @@
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
+ // BUG 评论级表情回应(任务 39,BUG 侧首个真实接线——story 的 reaction-bar 是零
19
+ // 消费者死代码、story comment-item 的 handleReaction 无 JSX 调用点,均未照抄)。
20
+ // 三段式:chips 渲染 listCommentsFn 聚合的 ReactionGroup(emoji+count+reacted
21
+ // 高亮)+ hover 快捷常用 emoji(CSS group-hover 显隐,禁 React state)+
22
+ // EmojiPicker 完整网格。乐观更新:点击即按 reacted 标记本地翻转(userIds 不动,
23
+ // UI 不消费该字段),toggleBugCommentReactionFn 的权威聚合返回后整体覆盖;
24
+ // 失败回滚 + toast。由 comment-item.tsx JSX 实际调用(死代码教训:必须有调用点)。
25
+ import { useRef, useState } from "react";
26
+ import { SmilePlus } from "lucide-react";
27
+ import { EmojiPicker } from "@/components/shared-kit/comments/emoji-picker";
28
+ import { toast } from "@/components/ui/toast";
29
+ import { toggleBugCommentReactionFn } from "@/server/serverFns/bug";
30
+ import type { ReactionGroup } from "@/components/story/types";
31
+ import { m } from "@/paraglide/messages.js";
32
+
33
+ /** hover 快捷回应(常用 3 个;完整选择走 picker) */
34
+ const QUICK_EMOJIS = ["👍", "❤️", "🎉"];
35
+
36
+ interface CommentReactionsProps {
37
+ commentId: string;
38
+ reactions: ReactionGroup[];
39
+ }
40
+
41
+ /** 本地乐观翻转一组聚合:未表态 +1 高亮、已表态 -1(减到 0 移除该 chip)、
42
+ * 全新 emoji 追加。userIds 保持原样/空数组——UI 只消费 emoji/count/reacted,
43
+ * 权威结果返回后整体覆盖 */
44
+ function toggleGroupsLocally(groups: ReactionGroup[], emoji: string): ReactionGroup[] {
45
+ const idx = groups.findIndex((g) => g.emoji === emoji);
46
+ if (idx === -1) {
47
+ return [...groups, { emoji, count: 1, userIds: [], reacted: true }];
48
+ }
49
+ const group = groups[idx]!;
50
+ if (group.reacted) {
51
+ if (group.count <= 1) return groups.filter((g) => g.emoji !== emoji);
52
+ return groups.map((g, i) => (i === idx ? { ...g, count: g.count - 1, reacted: false } : g));
53
+ }
54
+ return groups.map((g, i) => (i === idx ? { ...g, count: g.count + 1, reacted: true } : g));
55
+ }
56
+
57
+ export function CommentReactions({ commentId, reactions }: CommentReactionsProps) {
58
+ // 乐观覆盖态:null 跟随 props;toggle 后先本地翻转、权威聚合返回再覆盖
59
+ const [override, setOverride] = useState<ReactionGroup[] | null>(null);
60
+ // 同一 emoji 连点防抖(story 级 pendingReactionsRef 同款守卫,实例级隔离)
61
+ const pendingRef = useRef<Set<string>>(new Set());
62
+ const display = override ?? reactions;
63
+
64
+ const toggle = async (emoji: string) => {
65
+ if (pendingRef.current.has(emoji)) return;
66
+ pendingRef.current.add(emoji);
67
+ const base = override ?? reactions;
68
+ setOverride(toggleGroupsLocally(base, emoji));
69
+ try {
70
+ const groups = await toggleBugCommentReactionFn({
71
+ data: { commentId, emoji },
72
+ });
73
+ setOverride(groups);
74
+ } catch (err) {
75
+ // 回滚到点击前状态。并发多 emoji 期间前者失败会连带重置后者的乐观态,
76
+ // 由后者的权威响应自愈——单表情失败是常态路径,不为极端并发加复杂度
77
+ setOverride(base);
78
+ toast.error(m.bug_comment_reactionFailed(), {
79
+ description: err instanceof Error ? err.message : m.bug_comment_retry(),
80
+ });
81
+ } finally {
82
+ pendingRef.current.delete(emoji);
83
+ }
84
+ };
85
+
86
+ const sorted = display.slice().sort((a, b) => b.count - a.count);
87
+
88
+ return (
89
+ // hover 域挂在评论主体(comment-item 的 avatar+内容行 group/comment):
90
+ // 快捷按钮与 picker 触发器 hover 评论时显出,纯 CSS 无 React state
91
+ <div className="flex items-center gap-1">
92
+ {sorted.map((group) => (
93
+ <button
94
+ key={group.emoji}
95
+ type="button"
96
+ data-testid={`reaction-chip-${group.emoji}`}
97
+ aria-pressed={group.reacted}
98
+ onClick={() => void toggle(group.emoji)}
99
+ className={`flex h-6 items-center gap-1 rounded-full border px-2 text-xs transition-colors ${
100
+ group.reacted
101
+ ? "border-primary/40 bg-primary/10 text-foreground hover:bg-primary/20"
102
+ : "border-input bg-muted/50 text-muted-foreground hover:bg-muted"
103
+ }`}
104
+ >
105
+ <span>{group.emoji}</span>
106
+ <span className="text-[10px]">{group.count}</span>
107
+ </button>
108
+ ))}
109
+ <span
110
+ data-testid="quick-reactions"
111
+ // 触屏降级:hover:none 设备 group-hover 永不触发,快捷栏/picker 入口
112
+ // 恒隐藏不可达——媒体查询常显。Tailwind v4 无 hover:none 标准变体,
113
+ // 任意值变体为唯一表达(项目内首处,后续同场景照抄)
114
+ className="hidden items-center gap-0.5 group-hover/comment:inline-flex [@media(hover:none)]:inline-flex"
115
+ >
116
+ {QUICK_EMOJIS.map((emoji) => (
117
+ <button
118
+ key={emoji}
119
+ type="button"
120
+ onClick={() => void toggle(emoji)}
121
+ className="flex h-6 w-6 items-center justify-center rounded-full text-sm transition-colors hover:bg-muted"
122
+ >
123
+ {emoji}
124
+ </button>
125
+ ))}
126
+ <EmojiPicker onPick={(emoji) => void toggle(emoji)} width={320} height={380}>
127
+ <button
128
+ type="button"
129
+ aria-label={m.bug_comment_addReaction()}
130
+ className="flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
131
+ >
132
+ <SmilePlus className="size-3.5" />
133
+ </button>
134
+ </EmojiPicker>
135
+ </span>
136
+ </div>
137
+ );
138
+ }