@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,258 @@
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
+ // 写入路由协议门卫(Yjs 多人协作 §5.3,任务 17)——serverFn 写路径的统一
19
+ // 前置封装。协议语义(复审定稿):
20
+ //
21
+ // 1. 门卫检查:查 CollabDocument(roomName 唯一键)。不存在 → 照旧直写
22
+ // (pass-through,零行为变化)。元数据列(title/tags/icon/properties)
23
+ // 不归 Y(协同 room 只拥有 content),patch 无 content 时连查询都省——
24
+ // 直写即正确路由,无互吞面。
25
+ // 2. 快照存在 + content → **一律转发** collab 内部通道
26
+ // `POST ${COLLAB_INTERNAL_URL}/internal/apply`(信封 HMAC,域
27
+ // collab-internal-apply:v1)。**活跃判定在 collab 侧**(复审修正:web
28
+ // 进程不做活跃判定)——room 在线由 collab 应用到 Y 并广播(整文档
29
+ // replace,DB 旁路写是权威时刻);room 离线由 collab 直接落库业务列
30
+ // (视为旁路写,重种子化兜底)。两种情形 web 都不再直写 content。
31
+ // 3. collab 不可达 → 内存排队重试(**请求内同步**:await 重试直至 TTL,
32
+ // 缺省 3s——规格 §5.3.2),TTL 后降级直写 DB(degraded,信任重种子化
33
+ // 兜底)。刻意不做「先降级后后台重试」:降级直写之后的迟到应用会把
34
+ // collab 侧旧态广播回 Y,正是 §5.3 要防的互吞。
35
+ //
36
+ // 工具函数文件(非 serverFn):仅由 serverFn handler 引用;serverFn 纯净
37
+ // 纪律下客户端 split 不会保留本模块(无值导出消费)。
38
+ //
39
+ // 故障口径:COLLAB_INTERNAL_URL / COLLAB_SECRET 未配置 → warn-once 降级直写
40
+ // (可用性优先,配置缺失日志显式);转发异常/非 2xx → 重试至 TTL 后降级。
41
+
42
+ import { getPrisma } from "@allbluecn/database/get-prisma";
43
+ import {
44
+ INTERNAL_DOMAIN_APPLY,
45
+ signInternalRequest,
46
+ } from "@allbluecn/collab-core/internal";
47
+ import type { CollabStep } from "@allbluecn/collab-core/steps";
48
+
49
+ /** 门卫裁决(调用方按 kind 分支) */
50
+ export type CollabGuardOutcome =
51
+ | {
52
+ kind: "pass-through";
53
+ reason: "no-y-owned-field" | "no-snapshot" | "unsupported-content";
54
+ }
55
+ | { kind: "forwarded" }
56
+ | { kind: "degraded"; reason: "no-url" | "no-secret" | "unreachable" };
57
+
58
+ /** 门卫入参 patch(content = 富文本 Y 属主面;fields = entity room 字段 Y 属主面) */
59
+ export interface CollabGuardPatch {
60
+ content?: unknown;
61
+ /**
62
+ * entity room 的字段 patch(任务 21,Story 8 字段;任务 22 扩 bug/case 6
63
+ * 字段——键值与客户端 Y 写端同形:JSON 兼容原值)。仅对象非空才视为 Y
64
+ * 属主写入;知识库不传。
65
+ */
66
+ fields?: Record<string, unknown>;
67
+ /**
68
+ * entity room 的步骤 patch(任务 22,§7.1 steps 行):门卫归一化列表
69
+ * (normalizeCollabSteps 产物——数组顺序即步骤顺序,order 已剥)。仅非空
70
+ * 数组才视为 Y 属主写入(业务 schema 步骤 min(1),空列表直写保持原行为)。
71
+ */
72
+ steps?: CollabStep[];
73
+ /**
74
+ * design-canvas room 的画布 patch(任务 29,§7.1 设计画布行):LeaferJSONPackage
75
+ * / 旧扁平 SceneDocument 原值(web 直写 canvas 列的同形值),应用语义 =
76
+ * 整场景 replace(collab 侧 seedYTreeFromCanvas 覆写 Y 树)。仅 undefined
77
+ * 之外才视为 Y 属主写入(内容形状由 collab 应用侧 fail-closed 把关)。
78
+ */
79
+ canvas?: unknown;
80
+ /**
81
+ * 语义活动 hint(entity-bug 机器/旁路写入,如 reopened_by_sdk):透传至
82
+ * collab /internal/apply——live 路径入队随落库成行、离线路径随直落活动行。
83
+ * 仅随 Y 属主面一起转发;action 必须为非空字符串(collab 侧 fail-closed)。
84
+ */
85
+ activity?: { action: string; field?: unknown; oldValue?: unknown; newValue?: unknown };
86
+ }
87
+
88
+ export interface WithCollabGuardOptions {
89
+ /** §3.1 注册表 room 名(buildRoomName 产物) */
90
+ roomName: string;
91
+ /** 业务实体 id(与 roomName 互相印证,collab 侧复核) */
92
+ docId: string;
93
+ /** 写入者(session user id;collab 侧系统 client 归属用它) */
94
+ actor: string;
95
+ patch: CollabGuardPatch;
96
+ /**
97
+ * 属主预检(C1,任务 17 复审修复):快照命中后、转发前执行,抛错即短路
98
+ * (不触网络、不排队)。**用户会话调用点必须传入**——requirePermission
99
+ * 是模块级权限(普通用户默认 write),不构成资源属主校验;不预检则
100
+ * 「collab 已应用、web 侧才报无权限」的越权窗口成立。回调内做只读
101
+ * assertOwned;直写路径(pass-through/degraded)不经此回调,属主校验
102
+ * 保持合并在写库 WHERE。
103
+ *
104
+ * 机器路径豁免口径(任务 23):SDK/MCP 机器写入调用点可省略本回调,条件
105
+ * 是资源定位已绑定凭证 scope、token 即授权主体——SDK 上报经 X-Bug-Key
106
+ * (token 含 projectId)+ projectId_fingerprint 定位 BUG,MCP 经
107
+ * requireBug 的 findFirst({ id, projectId }) 结构性消除跨项目面。省略
108
+ * 前须做同等信任链分析并在调用点注释留痕(现豁免点:report-handler
109
+ * dedupeExistingBug、mcp/tools reportResultHandler);普通用户调用点
110
+ * (session userId)不适用本豁免,仍必须传入。
111
+ */
112
+ assertWriteAllowed?: () => Promise<void>;
113
+ /** 重试总预算(§5.3.2 TTL 3s);超时降级直写 */
114
+ ttlMs?: number;
115
+ /** 单次转发请求超时 */
116
+ requestTimeoutMs?: number;
117
+ /** 重试间隔(测试注入即时 sleep) */
118
+ sleep?: (ms: number) => Promise<void>;
119
+ }
120
+
121
+ /** §5.3.2 降级 TTL(ms) */
122
+ export const COLLAB_GUARD_TTL_MS = 3000;
123
+ /** 单次转发请求超时(ms) */
124
+ export const COLLAB_GUARD_REQUEST_TIMEOUT_MS = 2000;
125
+ /** 重试退避序列(指数;TTL 截断) */
126
+ const RETRY_BACKOFF_MS = [250, 500, 1000, 1000];
127
+
128
+ /** COLLAB_INTERNAL_URL 缺失的 warn-once 标记(进程级) */
129
+ let warnedNoUrl = false;
130
+ /** COLLAB_SECRET 缺失的 warn-once 标记(进程级) */
131
+ let warnedNoSecret = false;
132
+
133
+ /**
134
+ * 门卫主入口。返回裁决;「直写」类裁决(pass-through/degraded)由调用方
135
+ * 执行原 updateManyAndReturn,forwarded 时调用方须把 content 从直写数据中
136
+ * 剥离(content 已由 collab 接管)。
137
+ */
138
+ export async function withCollabGuard(opts: WithCollabGuardOptions): Promise<CollabGuardOutcome> {
139
+ // ① 无 Y 属主字段:patch 既无 content/canvas 也无非空 fields/steps(元数据
140
+ // 等不归协同 room)→ 直写即正确路由(省一次查询)
141
+ const hasFields =
142
+ opts.patch.fields !== undefined &&
143
+ typeof opts.patch.fields === "object" &&
144
+ Object.keys(opts.patch.fields).length > 0;
145
+ const hasSteps = Array.isArray(opts.patch.steps) && opts.patch.steps.length > 0;
146
+ const hasCanvas = opts.patch.canvas !== undefined;
147
+ if (opts.patch.content === undefined && !hasCanvas && !hasFields && !hasSteps) {
148
+ return { kind: "pass-through", reason: "no-y-owned-field" };
149
+ }
150
+ // ② content 形状守卫:仅 { html: string } 可同构进 Y(编辑器 onChange
151
+ // 产物);历史 {text}/string 形状照旧直写,重种子化兜底换算。
152
+ // content 缺席(纯 fields/canvas patch)跳过此守卫。
153
+ const html =
154
+ typeof opts.patch.content === "object" && opts.patch.content !== null
155
+ ? (opts.patch.content as Record<string, unknown>).html
156
+ : undefined;
157
+ if (opts.patch.content !== undefined && typeof html !== "string") {
158
+ return { kind: "pass-through", reason: "unsupported-content" };
159
+ }
160
+ // ③ 快照检查:无 CollabDocument = 该文档从未协同,照旧直写(零行为变化)
161
+ const prisma = await getPrisma();
162
+ const snapshot = await prisma.collabDocument.findUnique({
163
+ where: { roomName: opts.roomName },
164
+ select: { id: true },
165
+ });
166
+ if (!snapshot) {
167
+ return { kind: "pass-through", reason: "no-snapshot" };
168
+ }
169
+ // ③a 属主预检(C1):快照命中即可能转发——先校验属主,失败短路抛错,
170
+ // 杜绝「content 已进受害者 Y 态/业务列、web 才报无权限」的水平越权
171
+ if (opts.assertWriteAllowed) {
172
+ await opts.assertWriteAllowed();
173
+ }
174
+ // ④ 转发(活跃判定在 collab 侧;web 不判活跃)
175
+ const base = process.env.COLLAB_INTERNAL_URL;
176
+ if (!base) {
177
+ if (!warnedNoUrl) {
178
+ warnedNoUrl = true;
179
+ console.warn(
180
+ "[collab-write-guard] COLLAB_INTERNAL_URL 未配置:协同文档的旁路 content 写入无法转发," +
181
+ "已降级直写 DB(下次协同打开时重种子化兜底)。请补配该 env。",
182
+ );
183
+ }
184
+ return { kind: "degraded", reason: "no-url" };
185
+ }
186
+
187
+ // ③b secret 检查(R 审查修复 Minor):缺失与 COLLAB_INTERNAL_URL 缺失同
188
+ // 口径——warn-once 降级直写(可用性优先),不再抛错 500。行为变化:
189
+ // 「快照存在但 secret 缺失」原为 500(写入失败),现降级直写 DB,
190
+ // 重种子化兜底。
191
+ const secret = process.env.COLLAB_SECRET;
192
+ if (!secret) {
193
+ if (!warnedNoSecret) {
194
+ warnedNoSecret = true;
195
+ console.warn(
196
+ "[collab-write-guard] COLLAB_SECRET 未配置:协同文档的旁路写入无法签发内部通道信封," +
197
+ "已降级直写 DB(下次协同打开时重种子化兜底)。请补配该 env。",
198
+ );
199
+ }
200
+ return { kind: "degraded", reason: "no-secret" };
201
+ }
202
+
203
+ const signed = signInternalRequest({
204
+ secret,
205
+ domain: INTERNAL_DOMAIN_APPLY,
206
+ payload: {
207
+ roomName: opts.roomName,
208
+ docId: opts.docId,
209
+ actor: opts.actor,
210
+ patch: {
211
+ // 只携带出现的属主面:knowledge 仅 content;story 为 content/fields
212
+ // 组合;bug/case 为 content/fields/steps 组合(任务 22);design 为
213
+ // canvas/content 组合(任务 29)
214
+ ...(opts.patch.content !== undefined && { content: opts.patch.content }),
215
+ ...(hasCanvas && { canvas: opts.patch.canvas }),
216
+ ...(hasFields && { fields: opts.patch.fields }),
217
+ ...(hasSteps && { steps: opts.patch.steps }),
218
+ ...(opts.patch.activity !== undefined && { activity: opts.patch.activity }),
219
+ },
220
+ },
221
+ });
222
+
223
+ const ttlMs = opts.ttlMs ?? COLLAB_GUARD_TTL_MS;
224
+ const requestTimeoutMs = opts.requestTimeoutMs ?? COLLAB_GUARD_REQUEST_TIMEOUT_MS;
225
+ const sleep =
226
+ opts.sleep ??
227
+ ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
228
+ const startedAt = Date.now();
229
+ let attempt = 0;
230
+ // ⑤ 内存排队重试(请求内同步):失败即重试直至 TTL 耗尽——调用方在本
231
+ // 请求内拿到最终裁决,绝不出现「已降级直写、后台又迟到应用」的互吞。
232
+ while (true) {
233
+ attempt += 1;
234
+ try {
235
+ const response = await fetch(`${base.replace(/\/$/, "")}/internal/apply`, {
236
+ method: "POST",
237
+ headers: signed.headers,
238
+ body: signed.body,
239
+ signal: AbortSignal.timeout(requestTimeoutMs),
240
+ });
241
+ if (response.ok) {
242
+ return { kind: "forwarded" };
243
+ }
244
+ console.warn(
245
+ `[collab-write-guard] /internal/apply 响应 ${response.status}(room=${opts.roomName} 第 ${attempt} 次)`,
246
+ );
247
+ } catch (err) {
248
+ console.warn(
249
+ `[collab-write-guard] /internal/apply 不可达(room=${opts.roomName} 第 ${attempt} 次):`,
250
+ err instanceof Error ? err.message : err,
251
+ );
252
+ }
253
+ if (Date.now() - startedAt >= ttlMs) {
254
+ return { kind: "degraded", reason: "unreachable" };
255
+ }
256
+ await sleep(RETRY_BACKOFF_MS[Math.min(attempt - 1, RETRY_BACKOFF_MS.length - 1)]!);
257
+ }
258
+ }
@@ -0,0 +1,248 @@
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
+ // 协同授权统一入口(Yjs 多人协作 §4.1)+ room 名解析(§3.1 注册表)。
19
+ // assertCollabAccess(entityType, entityId, userId) 是协同 ticket 签发与协同态
20
+ // serverFn 的唯一授权裁决点;工作空间反查组合 collab/resolve.ts 的查询层。
21
+ //
22
+ // 裁决口径(2026-10-07 控制者裁决):
23
+ // - owner ≥ edit:owner 判定(实体 userId)独立于工作空间成员判定,
24
+ // owner 对自己实体永远拿到 edit、永不 Forbidden(私人项目 / private KB /
25
+ // 未关联 Design 的 owner 均不被锁外)。
26
+ // - WorkspaceMember role=viewer → view(只读);其他角色 → edit。
27
+ // - 空间外(归属链解析出的工作空间与该用户成员身份无交集)→ Forbidden。
28
+ // - 实体不存在 / room 解析失败一律 fail-closed 拒绝。
29
+ //
30
+ // 边界:presence:team 与 share: 两类 room 不是实体 room,本模块仅提供
31
+ // parseRoomName 解析(roomAccessTarget 映射为 null);其校验由 P1 任务各自实现。
32
+ // deleted/archived 过滤与 §4.1 口径一致暂不收紧(渐进策略,P1 ticket 层按需加守卫)。
33
+
34
+ import { prisma } from "@allbluecn/database";
35
+ import { ForbiddenError } from "@/server/auth-helpers";
36
+ import {
37
+ resolveEntityWorkspace,
38
+ resolveKbAccess,
39
+ resolveDesignWorkspaces,
40
+ type CollabEntityType,
41
+ } from "./collab/resolve";
42
+
43
+ /** 协同角色:edit 可编辑(ticket readOnly=false),view 只读 */
44
+ export type CollabRole = "edit" | "view";
45
+
46
+ /**
47
+ * assertCollabAccess 支持的实体类型(§3.1 七类 room 名映射的值域):
48
+ * doc:knowledge→knowledge、entity:*→story|bug|case、canvas:design→design。
49
+ * presence/share 两类 room 不在值域内(类型层面阻止越界实现)。
50
+ */
51
+ export type CollabAccessEntityType = CollabEntityType | "knowledge" | "design";
52
+
53
+ /** §3.1 七类 room 名的解析结果( discriminated union,供 collab-core 镜像对齐) */
54
+ export type CollabRoomName =
55
+ | { kind: "presence-team"; ownerId: string }
56
+ | { kind: "share"; shareToken: string }
57
+ | { kind: "knowledge-doc"; docId: string }
58
+ | { kind: "entity-story"; storyId: string }
59
+ | { kind: "entity-bug"; bugId: string }
60
+ | { kind: "entity-case"; caseId: string }
61
+ | { kind: "design-canvas"; pageId: string };
62
+
63
+ /**
64
+ * 解析 §3.1 注册表的七类 room 名;格式不符(缺段 / 空 id / 未知前缀 / 多余段)
65
+ * 返回 null(fail-closed:调用方对 null 一律拒绝签发 ticket,防越权复用)。
66
+ * id 段不含 `:`(实体 id 为 cuid;Share.token 为 nanoid 字母数字表),
67
+ * 故按严格段数切分,不做宽松截断。
68
+ */
69
+ export function parseRoomName(roomName: string): CollabRoomName | null {
70
+ const parts = roomName.split(":");
71
+ // share:{token} 两段;其余六类均为三段
72
+ if (parts.length === 2 && parts[0] === "share" && parts[1]) {
73
+ return { kind: "share", shareToken: parts[1] };
74
+ }
75
+ if (parts.length !== 3 || parts.some((part) => part.length === 0)) return null;
76
+ const [scope, kind, id] = parts;
77
+ if (scope === "presence" && kind === "team") {
78
+ return { kind: "presence-team", ownerId: id };
79
+ }
80
+ if (scope === "doc" && kind === "knowledge") {
81
+ return { kind: "knowledge-doc", docId: id };
82
+ }
83
+ if (scope === "entity" && kind === "story") {
84
+ return { kind: "entity-story", storyId: id };
85
+ }
86
+ if (scope === "entity" && kind === "bug") {
87
+ return { kind: "entity-bug", bugId: id };
88
+ }
89
+ if (scope === "entity" && kind === "case") {
90
+ return { kind: "entity-case", caseId: id };
91
+ }
92
+ if (scope === "canvas" && kind === "design") {
93
+ return { kind: "design-canvas", pageId: id };
94
+ }
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * room 权限映射(§3.1 七类 → 实体类型):实体类 room 返回
100
+ * assertCollabAccess 的入参对;presence/share 非实体 room → null
101
+ * (P1 ticket 流程对 null 拒绝,presence/share 校验由各自任务实现)。
102
+ */
103
+ export function roomAccessTarget(
104
+ room: CollabRoomName,
105
+ ): { entityType: CollabAccessEntityType; entityId: string } | null {
106
+ switch (room.kind) {
107
+ case "knowledge-doc":
108
+ return { entityType: "knowledge", entityId: room.docId };
109
+ case "entity-story":
110
+ return { entityType: "story", entityId: room.storyId };
111
+ case "entity-bug":
112
+ return { entityType: "bug", entityId: room.bugId };
113
+ case "entity-case":
114
+ return { entityType: "case", entityId: room.caseId };
115
+ case "design-canvas":
116
+ return { entityType: "design", entityId: room.pageId };
117
+ case "presence-team":
118
+ case "share":
119
+ return null;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * owner + 授权锚点查询:room 的 id 即实体 id(docId / pageId 亦然),
125
+ * knowledge 经文档反查 KB(owner=KnowledgeBase.userId,private KB 不协同
126
+ * 即仅 KB owner),design 经页反查 Design(owner=Design.userId)。
127
+ * 实体不存在 → ownerUserId 与锚点均为 null(后续路径 fail-closed)。
128
+ */
129
+ type EntityOwnership = {
130
+ ownerUserId: string | null;
131
+ kbId: string | null;
132
+ designId: string | null;
133
+ };
134
+
135
+ const findOwnership: Record<
136
+ CollabAccessEntityType,
137
+ (id: string) => Promise<EntityOwnership>
138
+ > = {
139
+ story: async (id) => {
140
+ const row = await prisma.story.findUnique({
141
+ where: { id },
142
+ select: { createdById: true },
143
+ });
144
+ return { ownerUserId: row?.createdById ?? null, kbId: null, designId: null };
145
+ },
146
+ bug: async (id) => {
147
+ const row = await prisma.bug.findUnique({
148
+ where: { id },
149
+ select: { createdById: true },
150
+ });
151
+ return { ownerUserId: row?.createdById ?? null, kbId: null, designId: null };
152
+ },
153
+ case: async (id) => {
154
+ const row = await prisma.testCase.findUnique({
155
+ where: { id },
156
+ select: { createdById: true },
157
+ });
158
+ return { ownerUserId: row?.createdById ?? null, kbId: null, designId: null };
159
+ },
160
+ knowledge: async (docId) => {
161
+ const doc = await prisma.knowledgeDocument.findUnique({
162
+ where: { id: docId },
163
+ select: { kbId: true, kb: { select: { userId: true } } },
164
+ });
165
+ return {
166
+ ownerUserId: doc?.kb.userId ?? null,
167
+ kbId: doc?.kbId ?? null,
168
+ designId: null,
169
+ };
170
+ },
171
+ design: async (pageId) => {
172
+ const page = await prisma.designPage.findUnique({
173
+ where: { id: pageId },
174
+ select: { designId: true, design: { select: { userId: true } } },
175
+ });
176
+ return {
177
+ ownerUserId: page?.design.userId ?? null,
178
+ kbId: null,
179
+ designId: page?.designId ?? null,
180
+ };
181
+ },
182
+ };
183
+
184
+ /**
185
+ * 实体的协同授权工作空间集合(组合 collab/resolve.ts 反查):
186
+ * - story/bug/case → 项目直挂的单个工作空间(未挂 → 空集)
187
+ * - knowledge → open KB 挂载的三条路径并集(private / 空 → 空集)
188
+ * - design → StoryDesignLink 间接反查(无关联 → 空集)
189
+ */
190
+ async function resolveAccessWorkspaces(
191
+ entityType: CollabAccessEntityType,
192
+ entityId: string,
193
+ ownership: EntityOwnership,
194
+ ): Promise<string[]> {
195
+ if (entityType === "knowledge") {
196
+ if (!ownership.kbId) return [];
197
+ const access = await resolveKbAccess(ownership.kbId);
198
+ return access.kind === "open" ? access.workspaceIds : [];
199
+ }
200
+ if (entityType === "design") {
201
+ if (!ownership.designId) return [];
202
+ return resolveDesignWorkspaces(ownership.designId);
203
+ }
204
+ const workspaceId = await resolveEntityWorkspace(entityType, entityId);
205
+ return workspaceId ? [workspaceId] : [];
206
+ }
207
+
208
+ /**
209
+ * 协同授权统一裁决(§4.1 单一入口)。
210
+ * 返回协同角色({role:'edit'}|{role:'view'});
211
+ * 无权限(空间外 / private-非 owner / 实体不存在)抛 ForbiddenError。
212
+ *
213
+ * - owner(实体 userId)→ 恒为 edit,不查成员表(owner ≥ edit 裁决)。
214
+ * - 其余用户:归属链解析工作空间 → active WorkspaceMember 全取 →
215
+ * 任一非 viewer 角色 → edit;仅有 viewer → view;无成员身份 → Forbidden。
216
+ */
217
+ export async function assertCollabAccess(
218
+ entityType: CollabAccessEntityType,
219
+ entityId: string,
220
+ userId: string,
221
+ ): Promise<{ role: CollabRole }> {
222
+ const ownership = await findOwnership[entityType](entityId);
223
+ if (ownership.ownerUserId && ownership.ownerUserId === userId) {
224
+ return { role: "edit" };
225
+ }
226
+
227
+ const workspaceIds = await resolveAccessWorkspaces(
228
+ entityType,
229
+ entityId,
230
+ ownership,
231
+ );
232
+ if (workspaceIds.length === 0) {
233
+ throw new ForbiddenError("无协同访问权限");
234
+ }
235
+
236
+ const memberships = await prisma.workspaceMember.findMany({
237
+ where: { workspaceId: { in: workspaceIds }, userId, status: "active" },
238
+ select: { role: true },
239
+ });
240
+ if (memberships.length === 0) {
241
+ throw new ForbiddenError("无协同访问权限");
242
+ }
243
+ return {
244
+ role: memberships.some((membership) => membership.role !== "viewer")
245
+ ? "edit"
246
+ : "view",
247
+ };
248
+ }
@@ -0,0 +1,61 @@
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
+ // CSV 序列化共享纯函数(四期 Q8 任务 44)。落 src/server/(serverFns 树外,
19
+ // 照 server/testcase/list-query.ts 先例——serverFn 文件只准导出 createServerFn
20
+ // 产物,纯 helper 须放树外客户端安全位置)。零依赖、零 IO;testcase/bug 两
21
+ // 导出 serverFn 消费,后续模块导出(story 等)复用同款编码规则。
22
+ //
23
+ // 编码规则(RFC 4180 + Excel 兼容加固):
24
+ // ① BOM:整体输出以 \uFEFF 前缀——Excel 无 BOM 按 ANSI 解码,中文乱码;
25
+ // ② 转义:值含 " , \n \r 任一字符时整格包裹双引号,内部 " 翻倍为 "";
26
+ // ③ 公式注入防护(CSV Injection / Excel formula injection):值首字符为
27
+ // = + - @ \t 时前置单引号 ',防导出文件被 Excel/WPS 当公式求值(设计 §8
28
+ // 审查重点;\t 为控制者补充——tab 开头同样触发公式解释器);
29
+ // ④ 行分隔符 CRLF(RFC 4180 口径,Excel/Numbers/WPS 全兼容);
30
+ // ⑤ null/undefined 收敛为空单元格。
31
+ //
32
+ // 表头为服务端硬编码中文(serverFn 无客户端 locale 上下文,与 serverFn 错误
33
+ // 文案「无权限操作…」同一口径);枚举值(type/priority/state…)输出 DB 原值
34
+ // 保持机器可读。若需 en 表头,后续可加 locale 入参(YAGNI 暂不做)。
35
+
36
+ /** 单次导出最大数据行数(设计 R10:上限截断 + 前端提示「已导出前 20000 行」) */
37
+ export const CSV_EXPORT_MAX_ROWS = 20000;
38
+
39
+ /** 公式注入防护前缀集:命中即前置 ' */
40
+ const CSV_INJECTION_PREFIXES = new Set(["=", "+", "-", "@", "\t"]);
41
+
42
+ /** CSV 单元格值(服务端行数据允许原始类型;日期等由调用方先转 ISO 字符串) */
43
+ export type CsvCell = string | number | null | undefined;
44
+
45
+ /** 单格编码:注入防护 → 含分隔符则包裹引号 */
46
+ function encodeCell(value: CsvCell): string {
47
+ let s = value === null || value === undefined ? "" : String(value);
48
+ if (s.length > 0 && CSV_INJECTION_PREFIXES.has(s[0]!)) {
49
+ s = `'${s}`;
50
+ }
51
+ if (/[",\n\r]/.test(s)) {
52
+ s = `"${s.replace(/"/g, '""')}"`;
53
+ }
54
+ return s;
55
+ }
56
+
57
+ /** 行数组 → CSV 文本(含 BOM 前缀;行间 CRLF,尾部无行分隔符) */
58
+ export function toCsv(rows: CsvCell[][]): string {
59
+ const body = rows.map((row) => row.map(encodeCell).join(",")).join("\r\n");
60
+ return `\uFEFF${body}`;
61
+ }
@@ -24,6 +24,16 @@ export interface GithubData {
24
24
  versionDate: Date | null
25
25
  }
26
26
 
27
+ interface GithubRepoJson {
28
+ stargazers_count?: number
29
+ updated_at?: string
30
+ }
31
+
32
+ interface GithubReleaseJson {
33
+ tag_name?: string
34
+ published_at?: string
35
+ }
36
+
27
37
  const API = 'https://api.github.com'
28
38
  const HEADERS: HeadersInit = {
29
39
  Accept: 'application/vnd.github+json',
@@ -53,7 +63,7 @@ export async function fetchGithubData(
53
63
  }
54
64
  const repoRes = await fetch(repoUrl, { headers: HEADERS, redirect: 'error' })
55
65
  if (!repoRes.ok) return null
56
- const repoJson: any = await repoRes.json()
66
+ const repoJson: GithubRepoJson = await repoRes.json()
57
67
 
58
68
  let latestVersion: string | null = null
59
69
  let versionDate: Date | null = null
@@ -64,7 +74,7 @@ export async function fetchGithubData(
64
74
  }
65
75
  const relRes = await fetch(relUrl, { headers: HEADERS, redirect: 'error' })
66
76
  if (relRes.ok) {
67
- const relJson: any = await relRes.json()
77
+ const relJson: GithubReleaseJson = await relRes.json()
68
78
  latestVersion = relJson.tag_name ?? null
69
79
  versionDate = relJson.published_at ? new Date(relJson.published_at) : null
70
80
  }
@@ -15,7 +15,7 @@
15
15
  // You should have received a copy of the GNU Affero General Public License
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
- import { prisma } from '@allbluecn/database'
18
+ import { Prisma, prisma } from '@allbluecn/database'
19
19
 
20
20
  type EntityType = 'legal' | 'compliance' | 'dictionary' | 'bookmark'
21
21
 
@@ -30,10 +30,35 @@ interface ShareInput {
30
30
 
31
31
  export type ShareResult = { action: 'created' | 'appended' | 'already' }
32
32
 
33
+ export interface PublicSharer {
34
+ id: string
35
+ username: string
36
+ // Prisma Json 列:用 JsonValue(可序列化)而非 unknown,过 serverFn 序列化校验
37
+ avatarConfig: Prisma.JsonValue
38
+ bgShape: string | null
39
+ bgColor: string | null
40
+ }
41
+
42
+ /** 四个公共库模型 delegate 的公共操作面(仅用到公共列,收窄 prisma 动态访问类型) */
43
+ interface PublicModelDelegate {
44
+ findFirst(args: {
45
+ where: { canonicalId: string; visibility: string; isDeleted: boolean }
46
+ select: { id: boolean; canonicalId: boolean }
47
+ }): Promise<{ id: string; canonicalId: string } | null>
48
+ update(args: { where: { id: string }; data: Record<string, unknown> }): Promise<unknown>
49
+ }
50
+
51
+ const modelDelegates = {
52
+ legalRegulation: prisma.legalRegulation,
53
+ industryCompliance: prisma.industryCompliance,
54
+ dictionaryTerm: prisma.dictionaryTerm,
55
+ websiteBookmark: prisma.websiteBookmark,
56
+ }
57
+
33
58
  export async function shareToPublic(input: ShareInput): Promise<ShareResult> {
34
59
  const { model, entityType, userId, cloneData } = input
35
60
  const canonicalId = String(cloneData.canonicalId)
36
- const table = (prisma as any)[model]
61
+ const table = modelDelegates[model] as unknown as PublicModelDelegate
37
62
 
38
63
  const existing = await table.findFirst({
39
64
  where: { canonicalId, visibility: 'public', isDeleted: false },
@@ -42,7 +67,7 @@ export async function shareToPublic(input: ShareInput): Promise<ShareResult> {
42
67
 
43
68
  if (existing) {
44
69
  const { canonicalId: _cid, visibility: _vis, userId: _uid, ...updateData } = cloneData
45
- await table.update({ where: { id: existing.id }, data: updateData as any })
70
+ await table.update({ where: { id: existing.id }, data: updateData })
46
71
  const already = await prisma.knowledgeShareRecord.findFirst({
47
72
  where: { entityType, canonicalId, userId },
48
73
  select: { id: true },
@@ -68,10 +93,8 @@ export async function shareToPublic(input: ShareInput): Promise<ShareResult> {
68
93
  export async function aggregateSharers(
69
94
  entityType: EntityType,
70
95
  canonicalIds: string[],
71
- ) {
72
- if (canonicalIds.length === 0) return new Map<string, Array<{
73
- id: string; username: string; avatarConfig: unknown; bgShape: string | null; bgColor: string | null
74
- }>>()
96
+ ): Promise<Map<string, PublicSharer[]>> {
97
+ if (canonicalIds.length === 0) return new Map()
75
98
  const records = await prisma.knowledgeShareRecord.findMany({
76
99
  where: { entityType, canonicalId: { in: canonicalIds } },
77
100
  select: {
@@ -82,7 +105,7 @@ export async function aggregateSharers(
82
105
  },
83
106
  orderBy: { sharedAt: 'asc' },
84
107
  })
85
- const map = new Map<string, typeof records[number]['user'][]>()
108
+ const map = new Map<string, PublicSharer[]>()
86
109
  for (const r of records) {
87
110
  const arr = map.get(r.canonicalId) ?? []
88
111
  arr.push(r.user)