@allbluecn/web-app 0.22.4 → 0.23.1

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 (500) hide show
  1. package/package.json +4 -14
  2. package/src/__tests__/integration/login-email-acceptance.test.ts +16 -0
  3. package/src/__tests__/story-view.test.ts +33 -1
  4. package/src/components/agents/__tests__/agent-bindings-tab.test.tsx +221 -0
  5. package/src/components/agents/__tests__/agents-page.test.tsx +284 -0
  6. package/src/components/agents/agent-bindings-tab.tsx +59 -45
  7. package/src/components/agents/agent-form-dialog.tsx +3 -3
  8. package/src/components/agents/agent-selector.tsx +2 -3
  9. package/src/components/agents/agents-page.tsx +66 -31
  10. package/src/components/agents/slash-skill-menu.tsx +2 -3
  11. package/src/components/auth/__tests__/session-guard.test.tsx +74 -0
  12. package/src/components/auth/session-guard.tsx +8 -0
  13. package/src/components/billing/login-prompt-dialog.tsx +1 -1
  14. package/src/components/billing/subscription-provider.tsx +2 -16
  15. package/src/components/bug/__tests__/peek-modes.test.tsx +8 -0
  16. package/src/components/bug/__tests__/subgroup-header.test.tsx +202 -40
  17. package/src/components/bug/peek-panel.tsx +5 -3
  18. package/src/components/bug/views/bug-list/group.tsx +12 -2
  19. package/src/components/bug/views/bug-list/index.tsx +94 -67
  20. package/src/components/command/command-palette.tsx +10 -9
  21. package/src/components/design/canvas/document/frame-custom-presets.ts +9 -1
  22. package/src/components/design/canvas/panels/toolbar.tsx +21 -22
  23. package/src/components/design/canvas/react/__tests__/canvas-context-load-echo.test.tsx +199 -0
  24. package/src/components/design/canvas/react/__tests__/hooks.test.tsx +17 -3
  25. package/src/components/design/canvas/react/canvas-context.tsx +47 -26
  26. package/src/components/design/canvas/react/hooks.ts +39 -34
  27. package/src/components/design/editor/layout/__tests__/collab-param-wiring.test.tsx +17 -0
  28. package/src/components/design/editor/layout/__tests__/context-menu.test.tsx +17 -1
  29. package/src/components/design/editor/layout/__tests__/no-page-save-toast.test.tsx +17 -0
  30. package/src/components/design/editor/layout/__tests__/persistence-restore.test.tsx +17 -1
  31. package/src/components/design/editor/layout/__tests__/ssr-localstorage-smoke.test.tsx +17 -1
  32. package/src/components/design/editor/layout/design-editor-layout.tsx +13 -2
  33. package/src/components/design/editor/layout/hooks/use-design-auto-save.ts +3 -19
  34. package/src/components/devtools/devtools-panel.tsx +56 -0
  35. package/src/components/forms/__tests__/create-app-form.test.tsx +131 -0
  36. package/src/components/forms/app-field-error.tsx +21 -1
  37. package/src/components/forms/app-field.tsx +8 -8
  38. package/src/components/forms/app-submit.tsx +12 -9
  39. package/src/components/forms/create-app-form.tsx +124 -0
  40. package/src/components/forms/index.ts +2 -3
  41. package/src/components/harness/__tests__/gallery-behavior.test.tsx +37 -1
  42. package/src/components/harness/__tests__/harness-workbench-first-send.test.tsx +4 -1
  43. package/src/components/harness/__tests__/message-actions.test.tsx +7 -6
  44. package/src/components/harness/__tests__/use-video-generation.test.tsx +165 -0
  45. package/src/components/harness/ai-chat-history/index.tsx +5 -3
  46. package/src/components/harness/gallery/gallery-card.tsx +57 -35
  47. package/src/components/harness/gallery/gallery-grid.tsx +32 -15
  48. package/src/components/harness/gallery/gallery-types.ts +19 -0
  49. package/src/components/harness/gallery/gallery-video-card.tsx +54 -0
  50. package/src/components/harness/gallery/types.ts +2 -0
  51. package/src/components/harness/gallery/use-video-generation.ts +95 -0
  52. package/src/components/harness/harness-workbench-page.tsx +2 -3
  53. package/src/components/harness/message-actions.tsx +5 -3
  54. package/src/components/hotkeys/shortcuts-panel.tsx +28 -13
  55. package/src/components/iteration/__tests__/burndown-chart.test.tsx +118 -0
  56. package/src/components/iteration/burndown-chart.tsx +54 -23
  57. package/src/components/knowledge/editor/__tests__/knowledge-doc-editor-collab.test.tsx +96 -0
  58. package/src/components/knowledge/editor/knowledge-doc-editor.tsx +56 -1
  59. package/src/components/knowledge/editor/mindmap-doc-editor.tsx +12 -0
  60. package/src/components/knowledge/editor/use-auto-save-channels.ts +3 -1
  61. package/src/components/layout/sidebar-03/__tests__/nav-workspace.test.tsx +0 -4
  62. package/src/components/layout/sidebar-03/nav-knowledge.tsx +11 -11
  63. package/src/components/layout/sidebar-03/nav-main.tsx +12 -15
  64. package/src/components/layout/sidebar-03/nav-notifications.tsx +5 -8
  65. package/src/components/layout/sidebar-03/nav-workspace.tsx +11 -9
  66. package/src/components/notifications/__tests__/settings-notifications.test.tsx +158 -0
  67. package/src/components/notifications/settings-notifications.tsx +37 -13
  68. package/src/components/notifications/team-notifications.tsx +10 -14
  69. package/src/components/settings/personal-team/account/__tests__/account-client.test.tsx +170 -0
  70. package/src/components/settings/personal-team/account/account-client.tsx +44 -7
  71. package/src/components/settings/personal-team/team/team-client.tsx +17 -19
  72. package/src/components/shared-kit/list-kit/__tests__/frozen-header.test.ts +210 -41
  73. package/src/components/shared-kit/list-kit/frozen-header.ts +134 -21
  74. package/src/components/shared-kit/list-kit/kanban/frozen-header.ts +20 -27
  75. package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +8 -0
  76. package/src/components/story/__tests__/story-list-table.test.tsx +214 -0
  77. package/src/components/story/columns.tsx +11 -2
  78. package/src/components/story/peek-panel.tsx +4 -2
  79. package/src/components/story/story-list-table.tsx +16 -1
  80. package/src/components/story/views/story-list/__tests__/frozen-headers.test.tsx +186 -0
  81. package/src/components/story/views/story-list/group.tsx +12 -1
  82. package/src/components/story/views/story-list/index.tsx +101 -68
  83. package/src/components/testcase/__tests__/case-row.test.tsx +134 -2
  84. package/src/components/testcase/__tests__/peek-panel.test.tsx +8 -0
  85. package/src/components/testcase/__tests__/step-editor.test.tsx +106 -0
  86. package/src/components/testcase/list/grouped-list.tsx +50 -22
  87. package/src/components/testcase/peek-panel.tsx +7 -4
  88. package/src/components/testcase/step-editor.tsx +196 -150
  89. package/src/components/tiptap/notion-like/editor-extensions.ts +0 -3
  90. package/src/components/tiptap/notion-like/notion-like-editor-header.tsx +1 -31
  91. package/src/components/tiptap/notion-like/notion-like-editor.tsx +32 -1
  92. package/src/components/workspace/__tests__/dialogs/create-workspace-dialog.test.tsx +2 -28
  93. package/src/components/workspace/__tests__/dialogs/edit-workspace-dialog.test.tsx +2 -28
  94. package/src/components/workspace/dialogs/create-workspace-dialog.tsx +45 -43
  95. package/src/components/workspace/dialogs/edit-workspace-dialog.tsx +38 -36
  96. package/src/design-engine/engine/engine-decor.ts +2 -0
  97. package/src/design-engine/engine/engine-path.ts +2 -0
  98. package/src/hooks/__tests__/use-unsaved-guard.test.tsx +180 -0
  99. package/src/hooks/use-notifications.ts +9 -16
  100. package/src/hooks/use-unsaved-guard.tsx +87 -0
  101. package/src/lib/__tests__/pacer.test.ts +33 -96
  102. package/src/lib/__tests__/query-defaults.test.ts +169 -0
  103. package/src/lib/__tests__/query-global-error.test.ts +256 -0
  104. package/src/lib/anyhub/anyhub-store.ts +2 -2
  105. package/src/lib/changelog/sdk-versions.ts +2 -10
  106. package/src/lib/collab/presence-store.ts +5 -5
  107. package/src/lib/collab/use-presence-list-refresh.ts +2 -2
  108. package/src/lib/collab/use-share-presence.ts +2 -2
  109. package/src/lib/collections/query-keys.ts +7 -2
  110. package/src/lib/commands/__tests__/command-palette-state.test.ts +12 -14
  111. package/src/lib/commands/__tests__/registry.test.ts +6 -3
  112. package/src/lib/commands/command-palette-state.ts +8 -31
  113. package/src/lib/hotkeys/__tests__/registry.test.ts +71 -18
  114. package/src/lib/hotkeys/registry.ts +188 -85
  115. package/src/lib/hotkeys/types.ts +1 -13
  116. package/src/lib/pacer.ts +1 -79
  117. package/src/lib/queries/README.md +67 -0
  118. package/src/lib/queries/__tests__/agents-queries.test.ts +101 -0
  119. package/src/lib/queries/__tests__/compliance-queries.test.ts +136 -0
  120. package/src/lib/queries/__tests__/notifications-queries.test.ts +175 -0
  121. package/src/lib/queries/__tests__/team-queries.test.ts +87 -0
  122. package/src/lib/queries/agents.ts +40 -0
  123. package/src/lib/queries/compliance.ts +69 -0
  124. package/src/lib/queries/notifications.ts +63 -0
  125. package/src/lib/queries/team.ts +39 -0
  126. package/src/lib/query-defaults.ts +8 -0
  127. package/src/lib/query-global-error.ts +128 -0
  128. package/src/lib/remote-auth-client.ts +16 -0
  129. package/src/paraglide/README.md +1 -1
  130. package/src/paraglide/messages/_index.d.ts +11 -0
  131. package/src/paraglide/messages/_index.js +5659 -88285
  132. package/src/paraglide/messages/common_error_generic.d.ts +16 -0
  133. package/src/paraglide/messages/common_error_generic.js +29 -0
  134. package/src/paraglide/messages/common_unsaved_body.d.ts +16 -0
  135. package/src/paraglide/messages/common_unsaved_body.js +29 -0
  136. package/src/paraglide/messages/common_unsaved_leave.d.ts +16 -0
  137. package/src/paraglide/messages/common_unsaved_leave.js +29 -0
  138. package/src/paraglide/messages/common_unsaved_stay.d.ts +16 -0
  139. package/src/paraglide/messages/common_unsaved_stay.js +29 -0
  140. package/src/paraglide/messages/common_unsaved_title.d.ts +16 -0
  141. package/src/paraglide/messages/common_unsaved_title.js +29 -0
  142. package/src/paraglide/messages/gallery_videofailed1.d.ts +17 -0
  143. package/src/paraglide/messages/gallery_videofailed1.js +30 -0
  144. package/src/paraglide/messages/gallery_videogenerating1.d.ts +17 -0
  145. package/src/paraglide/messages/gallery_videogenerating1.js +30 -0
  146. package/src/paraglide/messages/gallery_videomissingprompt2.d.ts +17 -0
  147. package/src/paraglide/messages/gallery_videomissingprompt2.js +30 -0
  148. package/src/paraglide/messages/gallery_videoprogress1.d.ts +19 -0
  149. package/src/paraglide/messages/gallery_videoprogress1.js +30 -0
  150. package/src/paraglide/messages/gallery_videostop1.d.ts +17 -0
  151. package/src/paraglide/messages/gallery_videostop1.js +30 -0
  152. package/src/paraglide/messages/story_table_resizecolumn1.d.ts +17 -0
  153. package/src/paraglide/messages/story_table_resizecolumn1.js +30 -0
  154. package/src/providers/index.tsx +5 -50
  155. package/src/routeTree.gen.ts +21 -0
  156. package/src/router.tsx +21 -8
  157. package/src/routes/__root.tsx +2 -1
  158. package/src/routes/__tests__/-edition-routing.test.tsx +6 -1
  159. package/src/routes/_login/forgot-password/route.lazy.tsx +7 -6
  160. package/src/routes/_login/login/route.lazy.tsx +10 -9
  161. package/src/routes/_login/register/route.lazy.tsx +19 -18
  162. package/src/routes/_login/reset-password/route.lazy.tsx +10 -9
  163. package/src/routes/_nav/knowledge/compliance/$id.lazy.tsx +9 -12
  164. package/src/routes/_nav/knowledge/compliance/index.lazy.tsx +16 -13
  165. package/src/routes/_nav/knowledge/laws/$id.lazy.tsx +1 -1
  166. package/src/routes/_nav/notifications/route.tsx +6 -16
  167. package/src/routes/_nav/settings/-settings-tabs.tsx +9 -12
  168. package/src/routes/api/generate/__tests__/media.test.ts +378 -0
  169. package/src/routes/api/generate/media.ts +179 -0
  170. package/src/server/__tests__/agent-client-graph.test.ts +0 -1
  171. package/src/server/__tests__/agent-crud.test.ts +37 -4
  172. package/src/server/__tests__/bug-groups-two-level.test.ts +34 -1
  173. package/src/server/__tests__/chat-conversations.test.ts +37 -4
  174. package/src/server/__tests__/chat-list-messages.test.ts +40 -7
  175. package/src/server/__tests__/csv-export.test.ts +36 -3
  176. package/src/server/__tests__/desktop-bootstrap.test.ts +189 -0
  177. package/src/server/__tests__/remote-auth.test.ts +72 -0
  178. package/src/server/__tests__/skill-crud.test.ts +37 -4
  179. package/src/server/__tests__/sso-domain.test.ts +1 -1
  180. package/src/server/__tests__/sso-rate-limit-singleton.test.ts +123 -0
  181. package/src/server/__tests__/task-serverfn-crud.test.ts +35 -2
  182. package/src/server/__tests__/task-serverfn-queries.test.ts +35 -2
  183. package/src/server/__tests__/testcase-board.test.ts +38 -5
  184. package/src/server/__tests__/testcase-bulk.test.ts +38 -5
  185. package/src/server/__tests__/testcase-list-scope.test.ts +38 -5
  186. package/src/server/ai-gateway/__tests__/ai-output-guard.test.ts +126 -2
  187. package/src/server/ai-gateway/__tests__/chat-stream-builder-code-mode.test.ts +4 -0
  188. package/src/server/ai-gateway/__tests__/chat-stream-builder-memory.test.ts +4 -0
  189. package/src/server/ai-gateway/__tests__/chat-stream-builder-sandbox.test.ts +4 -0
  190. package/src/server/ai-gateway/__tests__/chat-stream-builder-skills.test.ts +4 -0
  191. package/src/server/ai-gateway/__tests__/pgvector-memory-adapter.test.ts +30 -0
  192. package/src/server/ai-gateway/__tests__/pgvector-memory-conformance.test.ts +92 -0
  193. package/src/server/ai-gateway/__tests__/source-mode.test.ts +49 -8
  194. package/src/server/ai-gateway/ai-output-guard.ts +50 -2
  195. package/src/server/ai-gateway/chat-stream-builder.ts +3 -0
  196. package/src/server/ai-gateway/media/index.ts +36 -29
  197. package/src/server/ai-gateway/media/trim-input.ts +52 -0
  198. package/src/server/ai-gateway/memory/pgvector-adapter.ts +27 -13
  199. package/src/server/ai-gateway/sandbox/__tests__/harness-sandbox.test.ts +252 -41
  200. package/src/server/ai-gateway/sandbox/harness-sandbox.ts +6 -67
  201. package/src/server/ai-gateway/source-mode.ts +24 -8
  202. package/src/server/desktop-bootstrap.ts +74 -0
  203. package/src/server/lib/__tests__/rate-limit.test.ts +0 -1
  204. package/src/server/lib/rate-limit.ts +0 -2
  205. package/src/server/middlewares/__tests__/auth-context.test.ts +156 -0
  206. package/src/server/middlewares/auth-context.ts +62 -0
  207. package/src/server/remote-auth.ts +58 -31
  208. package/src/server/serverFns/__tests__/ai-hosted-models.test.ts +47 -15
  209. package/src/server/serverFns/__tests__/ai-source-mode.test.ts +45 -15
  210. package/src/server/serverFns/__tests__/billing.test.ts +37 -2
  211. package/src/server/serverFns/__tests__/collab-snapshot.test.ts +41 -14
  212. package/src/server/serverFns/__tests__/collab.test.ts +41 -14
  213. package/src/server/serverFns/__tests__/compliance.test.ts +33 -1
  214. package/src/server/serverFns/__tests__/email-verification.test.ts +35 -1
  215. package/src/server/serverFns/__tests__/knowledge-breadcrumb.test.ts +46 -6
  216. package/src/server/serverFns/__tests__/knowledge-delete.test.ts +42 -14
  217. package/src/server/serverFns/__tests__/knowledge-kb-projects.test.ts +46 -6
  218. package/src/server/serverFns/__tests__/knowledge-public-compliance.test.ts +36 -2
  219. package/src/server/serverFns/__tests__/knowledge-readable-kb.test.ts +42 -10
  220. package/src/server/serverFns/__tests__/knowledge-search.test.ts +45 -9
  221. package/src/server/serverFns/__tests__/legal-code-canonical.test.ts +36 -2
  222. package/src/server/serverFns/__tests__/link-preview.test.ts +38 -4
  223. package/src/server/serverFns/__tests__/notifications-list.test.ts +36 -3
  224. package/src/server/serverFns/__tests__/ownership-merge.test.ts +38 -4
  225. package/src/server/serverFns/__tests__/project-delete-transaction.test.ts +39 -2
  226. package/src/server/serverFns/__tests__/project-kb.test.ts +34 -1
  227. package/src/server/serverFns/__tests__/project-member-idor.test.ts +34 -1
  228. package/src/server/serverFns/__tests__/project-team-assign.test.ts +34 -1
  229. package/src/server/serverFns/__tests__/read-write-merge.test.ts +38 -4
  230. package/src/server/serverFns/__tests__/story-comments.test.ts +38 -4
  231. package/src/server/serverFns/__tests__/story-crud-archive-detail.test.ts +38 -4
  232. package/src/server/serverFns/__tests__/story-crud-labels-users.test.ts +37 -3
  233. package/src/server/serverFns/__tests__/story-crud.test.ts +36 -3
  234. package/src/server/serverFns/__tests__/story-links.test.ts +38 -4
  235. package/src/server/serverFns/__tests__/story-reactions.test.ts +38 -4
  236. package/src/server/serverFns/__tests__/story-tree.test.ts +38 -4
  237. package/src/server/serverFns/__tests__/team-accept.test.ts +34 -1
  238. package/src/server/serverFns/__tests__/team-invitation.test.ts +34 -1
  239. package/src/server/serverFns/__tests__/team-join-request.test.ts +34 -1
  240. package/src/server/serverFns/__tests__/team-members.test.ts +34 -1
  241. package/src/server/serverFns/__tests__/team-ownership-merge.test.ts +34 -1
  242. package/src/server/serverFns/__tests__/update-bug-guard.test.ts +37 -3
  243. package/src/server/serverFns/__tests__/update-document-guard.test.ts +38 -4
  244. package/src/server/serverFns/__tests__/update-page-guard.test.ts +40 -4
  245. package/src/server/serverFns/__tests__/update-seats.test.ts +33 -1
  246. package/src/server/serverFns/__tests__/update-story-guard.test.ts +36 -3
  247. package/src/server/serverFns/__tests__/update-testcase-guard.test.ts +37 -3
  248. package/src/server/serverFns/__tests__/workspace-kb-idor.test.ts +36 -2
  249. package/src/server/serverFns/__tests__/workspace-kb.test.ts +36 -2
  250. package/src/server/serverFns/__tests__/workspace.test.ts +34 -1
  251. package/src/server/serverFns/activity.ts +10 -14
  252. package/src/server/serverFns/agents/agent-crud.ts +16 -15
  253. package/src/server/serverFns/agents/bindings.ts +12 -13
  254. package/src/server/serverFns/agents/skill-crud.ts +16 -15
  255. package/src/server/serverFns/ai/code-mode-readiness.ts +3 -4
  256. package/src/server/serverFns/ai/search-capability.ts +3 -4
  257. package/src/server/serverFns/anyhub/app.ts +10 -14
  258. package/src/server/serverFns/anyhub/index.ts +7 -10
  259. package/src/server/serverFns/anyhub/install.ts +4 -4
  260. package/src/server/serverFns/anyhub/permissions.ts +10 -14
  261. package/src/server/serverFns/anyhub/proxy.ts +4 -6
  262. package/src/server/serverFns/auth/email-verification.ts +4 -5
  263. package/src/server/serverFns/auth/session.ts +9 -0
  264. package/src/server/serverFns/billing.ts +18 -28
  265. package/src/server/serverFns/blob-upload.ts +6 -9
  266. package/src/server/serverFns/bug/__tests__/bug-access-tokens.test.ts +34 -1
  267. package/src/server/serverFns/bug/__tests__/bug-activities.test.ts +34 -1
  268. package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +34 -1
  269. package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +34 -1
  270. package/src/server/serverFns/bug/__tests__/bug-fix.test.ts +34 -1
  271. package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +34 -1
  272. package/src/server/serverFns/bug/__tests__/bug-reactions.test.ts +34 -1
  273. package/src/server/serverFns/bug/__tests__/bug-reorder.test.ts +39 -5
  274. package/src/server/serverFns/bug/__tests__/bug-repro.test.ts +34 -1
  275. package/src/server/serverFns/bug/__tests__/bug-story.test.ts +34 -1
  276. package/src/server/serverFns/bug/__tests__/bug-webhooks.test.ts +34 -1
  277. package/src/server/serverFns/bug/bug-access-tokens.ts +16 -22
  278. package/src/server/serverFns/bug/bug-activities.ts +4 -6
  279. package/src/server/serverFns/bug/bug-collect-tokens.ts +13 -18
  280. package/src/server/serverFns/bug/bug-comments.ts +13 -18
  281. package/src/server/serverFns/bug/bug-crud/detail.ts +10 -14
  282. package/src/server/serverFns/bug/bug-crud.ts +28 -38
  283. package/src/server/serverFns/bug/bug-display-options.ts +8 -11
  284. package/src/server/serverFns/bug/bug-export.ts +5 -7
  285. package/src/server/serverFns/bug/bug-fix.ts +10 -14
  286. package/src/server/serverFns/bug/bug-groups.ts +10 -14
  287. package/src/server/serverFns/bug/bug-reactions.ts +10 -14
  288. package/src/server/serverFns/bug/bug-reorder.ts +4 -6
  289. package/src/server/serverFns/bug/bug-repro.ts +8 -11
  290. package/src/server/serverFns/bug/bug-story-bulk.ts +13 -18
  291. package/src/server/serverFns/bug/bug-story.ts +10 -14
  292. package/src/server/serverFns/bug/bug-views.ts +12 -18
  293. package/src/server/serverFns/bug/bug-webhooks.ts +13 -18
  294. package/src/server/serverFns/bug/test-bug-confirm.ts +7 -10
  295. package/src/server/serverFns/changelog/__tests__/fetch.test.ts +40 -1
  296. package/src/server/serverFns/changelog/fetch.ts +4 -6
  297. package/src/server/serverFns/chat/conversations.ts +17 -28
  298. package/src/server/serverFns/chat/list-messages.ts +4 -7
  299. package/src/server/serverFns/collab-presence.ts +11 -13
  300. package/src/server/serverFns/collab-snapshot.ts +6 -7
  301. package/src/server/serverFns/collab.ts +7 -7
  302. package/src/server/serverFns/compliance.ts +8 -8
  303. package/src/server/serverFns/design/__tests__/comments.test.ts +54 -10
  304. package/src/server/serverFns/design/__tests__/designs.test.ts +39 -16
  305. package/src/server/serverFns/design/__tests__/page-versions.test.ts +52 -14
  306. package/src/server/serverFns/design/__tests__/shares-password.test.ts +44 -14
  307. package/src/server/serverFns/design/__tests__/shares.test.ts +44 -14
  308. package/src/server/serverFns/design/__tests__/versions.test.ts +44 -14
  309. package/src/server/serverFns/design/ai/__tests__/agent-conversations.test.ts +50 -21
  310. package/src/server/serverFns/design/ai/__tests__/providers.test.ts +42 -8
  311. package/src/server/serverFns/design/ai/agent-conversations.ts +19 -26
  312. package/src/server/serverFns/design/ai/fill-annotation.ts +5 -7
  313. package/src/server/serverFns/design/ai/generate-prototype.ts +5 -7
  314. package/src/server/serverFns/design/ai/hosted-models.ts +4 -5
  315. package/src/server/serverFns/design/ai/providers.ts +30 -39
  316. package/src/server/serverFns/design/ai/source-mode.ts +10 -12
  317. package/src/server/serverFns/design/comments.ts +14 -13
  318. package/src/server/serverFns/design/designs.ts +13 -19
  319. package/src/server/serverFns/design/page-versions.ts +17 -18
  320. package/src/server/serverFns/design/pages.ts +17 -18
  321. package/src/server/serverFns/design/reviews.ts +8 -9
  322. package/src/server/serverFns/design/shares-password.ts +13 -19
  323. package/src/server/serverFns/design/shares.ts +22 -31
  324. package/src/server/serverFns/design/versions.ts +8 -9
  325. package/src/server/serverFns/harness/__tests__/attach-prototype.spec.ts +52 -2
  326. package/src/server/serverFns/harness/__tests__/attach-to-kb.spec.ts +52 -2
  327. package/src/server/serverFns/harness/__tests__/attach-to-story-ssrf.spec.ts +52 -2
  328. package/src/server/serverFns/harness/__tests__/attach-to-story.spec.ts +52 -2
  329. package/src/server/serverFns/harness/__tests__/cancel-run.test.ts +40 -15
  330. package/src/server/serverFns/harness/__tests__/extract-attachment.spec.ts +52 -2
  331. package/src/server/serverFns/harness/__tests__/gallery.spec.ts +76 -12
  332. package/src/server/serverFns/harness/__tests__/github-workspace.spec.ts +38 -6
  333. package/src/server/serverFns/harness/__tests__/media-prototype.spec.ts +50 -6
  334. package/src/server/serverFns/harness/__tests__/media.spec.ts +55 -11
  335. package/src/server/serverFns/harness/__tests__/sandbox-policy.test.ts +41 -16
  336. package/src/server/serverFns/harness/__tests__/workspace-files.test.ts +41 -16
  337. package/src/server/serverFns/harness/attach-prototype.ts +4 -4
  338. package/src/server/serverFns/harness/attach-to-kb.ts +4 -4
  339. package/src/server/serverFns/harness/attach-to-story.ts +4 -4
  340. package/src/server/serverFns/harness/cancel-run.ts +4 -6
  341. package/src/server/serverFns/harness/extract-attachment.ts +3 -4
  342. package/src/server/serverFns/harness/gallery.ts +19 -23
  343. package/src/server/serverFns/harness/github-workspace.ts +14 -21
  344. package/src/server/serverFns/harness/media.ts +18 -117
  345. package/src/server/serverFns/harness/sandbox-policy.ts +11 -10
  346. package/src/server/serverFns/harness/search-mention-candidates.ts +4 -6
  347. package/src/server/serverFns/harness/workspace-files.ts +19 -26
  348. package/src/server/serverFns/iteration/__tests__/iteration-auto-schedule.test.ts +35 -3
  349. package/src/server/serverFns/iteration/__tests__/iteration-crud.test.ts +35 -3
  350. package/src/server/serverFns/iteration/__tests__/iteration-stories.test.ts +35 -3
  351. package/src/server/serverFns/iteration/__tests__/iteration-transfer.test.ts +35 -3
  352. package/src/server/serverFns/iteration/iteration-auto-schedule.ts +7 -10
  353. package/src/server/serverFns/iteration/iteration-burndown.ts +4 -6
  354. package/src/server/serverFns/iteration/iteration-crud.ts +26 -35
  355. package/src/server/serverFns/iteration/iteration-stories.ts +10 -14
  356. package/src/server/serverFns/iteration/iteration-transfer.ts +4 -6
  357. package/src/server/serverFns/knowledge/public/bookmarks/categories.ts +11 -15
  358. package/src/server/serverFns/knowledge/public/bookmarks/create.ts +4 -6
  359. package/src/server/serverFns/knowledge/public/bookmarks/delete.ts +5 -7
  360. package/src/server/serverFns/knowledge/public/bookmarks/detail.ts +4 -6
  361. package/src/server/serverFns/knowledge/public/bookmarks/favorite.ts +4 -6
  362. package/src/server/serverFns/knowledge/public/bookmarks/list.ts +4 -6
  363. package/src/server/serverFns/knowledge/public/bookmarks/refresh-favicon.ts +4 -6
  364. package/src/server/serverFns/knowledge/public/bookmarks/share.ts +4 -6
  365. package/src/server/serverFns/knowledge/public/bookmarks/sync-github.ts +5 -7
  366. package/src/server/serverFns/knowledge/public/bookmarks/unshare.ts +4 -6
  367. package/src/server/serverFns/knowledge/public/bookmarks/update.ts +6 -8
  368. package/src/server/serverFns/knowledge/public/compliance/create.ts +4 -6
  369. package/src/server/serverFns/knowledge/public/compliance/delete.ts +5 -7
  370. package/src/server/serverFns/knowledge/public/compliance/detail.ts +4 -6
  371. package/src/server/serverFns/knowledge/public/compliance/list.ts +4 -6
  372. package/src/server/serverFns/knowledge/public/compliance/share.ts +4 -6
  373. package/src/server/serverFns/knowledge/public/compliance/unshare.ts +4 -6
  374. package/src/server/serverFns/knowledge/public/compliance/update.ts +5 -7
  375. package/src/server/serverFns/knowledge/public/dictionary/create.ts +4 -6
  376. package/src/server/serverFns/knowledge/public/dictionary/delete.ts +5 -7
  377. package/src/server/serverFns/knowledge/public/dictionary/detail.ts +4 -6
  378. package/src/server/serverFns/knowledge/public/dictionary/list.ts +4 -6
  379. package/src/server/serverFns/knowledge/public/dictionary/share.ts +4 -6
  380. package/src/server/serverFns/knowledge/public/dictionary/unshare.ts +4 -6
  381. package/src/server/serverFns/knowledge/public/dictionary/update.ts +5 -7
  382. package/src/server/serverFns/knowledge/public/entity-tags.ts +4 -6
  383. package/src/server/serverFns/knowledge/public/favorite.ts +7 -10
  384. package/src/server/serverFns/knowledge/public/legal/create.ts +4 -6
  385. package/src/server/serverFns/knowledge/public/legal/delete.ts +5 -7
  386. package/src/server/serverFns/knowledge/public/legal/detail.ts +4 -6
  387. package/src/server/serverFns/knowledge/public/legal/list.ts +4 -6
  388. package/src/server/serverFns/knowledge/public/legal/share.ts +4 -6
  389. package/src/server/serverFns/knowledge/public/legal/unshare.ts +4 -6
  390. package/src/server/serverFns/knowledge/public/legal/update.ts +5 -7
  391. package/src/server/serverFns/knowledge-index.ts +12 -16
  392. package/src/server/serverFns/knowledge.ts +50 -67
  393. package/src/server/serverFns/notifications.ts +25 -34
  394. package/src/server/serverFns/plugins/harness-plugins.ts +16 -23
  395. package/src/server/serverFns/project/module-overview.ts +7 -10
  396. package/src/server/serverFns/project.ts +48 -68
  397. package/src/server/serverFns/references/create.ts +4 -6
  398. package/src/server/serverFns/references/resolve.ts +4 -6
  399. package/src/server/serverFns/references/search.ts +4 -6
  400. package/src/server/serverFns/share/design/[designId]/events.ts +4 -6
  401. package/src/server/serverFns/sso.ts +1 -1
  402. package/src/server/serverFns/story/__tests__/bulk.test.ts +34 -1
  403. package/src/server/serverFns/story/__tests__/story-assoc.test.ts +34 -1
  404. package/src/server/serverFns/story/__tests__/story-attachments.test.ts +34 -1
  405. package/src/server/serverFns/story/__tests__/story-crud-date-key.test.ts +42 -17
  406. package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +34 -1
  407. package/src/server/serverFns/story/__tests__/story-gantt-deps.test.ts +34 -1
  408. package/src/server/serverFns/story/__tests__/story-groups.test.ts +34 -1
  409. package/src/server/serverFns/story/__tests__/story-read-path-authz.test.ts +40 -4
  410. package/src/server/serverFns/story/__tests__/story-search.test.ts +34 -1
  411. package/src/server/serverFns/story/__tests__/story-transfer.test.ts +34 -1
  412. package/src/server/serverFns/story/link-preview.ts +5 -7
  413. package/src/server/serverFns/story/story-assoc.ts +28 -38
  414. package/src/server/serverFns/story/story-attachments.ts +7 -10
  415. package/src/server/serverFns/story/story-children.ts +4 -6
  416. package/src/server/serverFns/story/story-comments.ts +13 -18
  417. package/src/server/serverFns/story/story-crud/activities.ts +4 -6
  418. package/src/server/serverFns/story/story-crud/archive.ts +7 -10
  419. package/src/server/serverFns/story/story-crud/bulk.ts +10 -14
  420. package/src/server/serverFns/story/story-crud/detail.ts +7 -10
  421. package/src/server/serverFns/story/story-crud/label-suggest.ts +4 -6
  422. package/src/server/serverFns/story/story-crud/labels.ts +6 -9
  423. package/src/server/serverFns/story/story-crud/user-brief.ts +4 -6
  424. package/src/server/serverFns/story/story-crud.ts +13 -18
  425. package/src/server/serverFns/story/story-dependencies.ts +16 -22
  426. package/src/server/serverFns/story/story-groups.ts +10 -14
  427. package/src/server/serverFns/story/story-links.ts +13 -18
  428. package/src/server/serverFns/story/story-reactions.ts +13 -18
  429. package/src/server/serverFns/story/story-search.ts +7 -10
  430. package/src/server/serverFns/story/story-transfer.ts +10 -14
  431. package/src/server/serverFns/story/story-tree.ts +7 -10
  432. package/src/server/serverFns/story-display-options.ts +7 -8
  433. package/src/server/serverFns/story-view.ts +12 -18
  434. package/src/server/serverFns/tasks/task-crud.ts +13 -17
  435. package/src/server/serverFns/tasks/task-queries.ts +12 -18
  436. package/src/server/serverFns/team.ts +46 -66
  437. package/src/server/serverFns/testcase/__tests__/automation.test.ts +38 -5
  438. package/src/server/serverFns/testcase/__tests__/evidence-upload.test.ts +37 -3
  439. package/src/server/serverFns/testcase/__tests__/generation.test.ts +38 -5
  440. package/src/server/serverFns/testcase/__tests__/plans.test.ts +36 -3
  441. package/src/server/serverFns/testcase/__tests__/regression.test.ts +36 -3
  442. package/src/server/serverFns/testcase/__tests__/run-cases.test.ts +36 -3
  443. package/src/server/serverFns/testcase/__tests__/runs.test.ts +36 -3
  444. package/src/server/serverFns/testcase/__tests__/stats.test.ts +36 -3
  445. package/src/server/serverFns/testcase/__tests__/testcase-board.test.ts +39 -5
  446. package/src/server/serverFns/testcase/__tests__/testcase-crud.test.ts +36 -3
  447. package/src/server/serverFns/testcase/__tests__/testcase-folders.test.ts +36 -3
  448. package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +38 -5
  449. package/src/server/serverFns/testcase/automation.ts +13 -17
  450. package/src/server/serverFns/testcase/bulk.ts +10 -14
  451. package/src/server/serverFns/testcase/evidence-upload.ts +5 -7
  452. package/src/server/serverFns/testcase/generation.ts +10 -13
  453. package/src/server/serverFns/testcase/plans.ts +23 -31
  454. package/src/server/serverFns/testcase/regression.ts +7 -10
  455. package/src/server/serverFns/testcase/run-cases.ts +13 -18
  456. package/src/server/serverFns/testcase/runs.ts +13 -18
  457. package/src/server/serverFns/testcase/stats.ts +13 -18
  458. package/src/server/serverFns/testcase/testcase-activities.ts +4 -6
  459. package/src/server/serverFns/testcase/testcase-board.ts +4 -6
  460. package/src/server/serverFns/testcase/testcase-crud.ts +20 -27
  461. package/src/server/serverFns/testcase/testcase-display-options.ts +8 -11
  462. package/src/server/serverFns/testcase/testcase-export.ts +5 -7
  463. package/src/server/serverFns/testcase/testcase-filter-views.ts +12 -18
  464. package/src/server/serverFns/testcase/testcase-folders.ts +16 -22
  465. package/src/server/serverFns/testcase/testcase-groups.ts +4 -6
  466. package/src/server/serverFns/testcase/testcase-reorder.ts +4 -6
  467. package/src/server/serverFns/translation.ts +4 -6
  468. package/src/server/serverFns/user/avatar.ts +4 -6
  469. package/src/server/serverFns/user/settings.ts +4 -6
  470. package/src/server/serverFns/user/update-locale.ts +4 -6
  471. package/src/server/serverFns/workspace-kb.ts +26 -35
  472. package/src/server/serverFns/workspace.ts +43 -59
  473. package/src/server/serverFns/ws.ts +4 -6
  474. package/src/server/serverfn-public-allowlist.ts +117 -0
  475. package/src/server/story/types.ts +0 -1
  476. package/src/server/sync/__tests__/scopes.test.ts +64 -0
  477. package/src/server/sync/scopes.ts +7 -0
  478. package/src/server/testcase/__tests__/auto-bug.test.ts +44 -12
  479. package/src/__tests__/bench/baselines/format-timestamp.json +0 -44
  480. package/src/__typeprobe__.d.ts +0 -4
  481. package/src/components/forms/app-form-dialog.tsx +0 -126
  482. package/src/components/forms/hooks/use-form-submit.ts +0 -57
  483. package/src/components/forms/types.ts +0 -34
  484. package/src/components/harness/code/workspace-picker.tsx +0 -239
  485. package/src/components/tiptap/extensions/local-file/insert-local-file.ts +0 -35
  486. package/src/components/tiptap/extensions/local-file/local-file-node.ts +0 -71
  487. package/src/devframe-docks/ai-chat-iframe.tsx +0 -29
  488. package/src/devframe-docks/session-iframe.tsx +0 -90
  489. package/src/lib/__tests__/desktop-detect.test.ts +0 -19
  490. package/src/lib/collections/index.ts +0 -22
  491. package/src/lib/collections/knowledge/index.ts +0 -18
  492. package/src/lib/collections/knowledge/knowledge-bases-collection.ts +0 -34
  493. package/src/lib/collections/reference/bookmark-categories-collection.ts +0 -31
  494. package/src/lib/collections/reference/labels-collection.ts +0 -31
  495. package/src/lib/collections/reference/projects-collection.ts +0 -31
  496. package/src/lib/collections/reference/users-brief-collection.ts +0 -35
  497. package/src/lib/collections/reference/workspaces-collection.ts +0 -34
  498. package/src/lib/desktop-detect.ts +0 -29
  499. package/src/paraglide/messages/en.js +0 -28260
  500. package/src/paraglide/messages/zh.js +0 -28258
@@ -13,33 +13,146 @@
13
13
  // GNU Affero General Public License for more details.
14
14
  //
15
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/>.
16
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- export interface FrozenHeader {
19
- groupIdx: number;
20
- subIdx: number | null;
18
+ /**
19
+ * 虚拟化列表「吸附组头」统一判定(任务 22:story 列表 / bug 列表 / 看板列内三处共享)。
20
+ *
21
+ * 行是绝对定位的虚拟化行,CSS sticky 不生效,故用「等价换算」得到原生 sticky 的
22
+ * 观感。行为标杆是看板列内吸附子分组头(2026-10-17 用户五轮裁决版,AGENTS.md
23
+ * 「虚拟化列表内的子分组标题吸附」条目),本函数把它泛化为两层:
24
+ *
25
+ * - **主组头层**:吸附在滚动容器顶边(scrollport top,offset 基准 0)。
26
+ * - **子组头层**:吸附在吸附主组头正下方(offset 基准 subPinTop = 组头行高)。
27
+ *
28
+ * **锚点必须是滚动容器顶边(scrollport top),不是内容盒顶**:滚动容器若有上内边距,
29
+ * 行测量起点(内容盒坐标系)比容器坐标系低 pad px。活跃阈值 / 吸附位 / 顶出量三者
30
+ * 必须同锚——历史实现锚在内容盒顶会整体偏移 pad px,在「几乎没滚动」的区间里就把
31
+ * 紧贴组头底边的首行上边框盖掉(看板五轮裁决的「上边框还是被裁了」)。
32
+ *
33
+ * 每层语义(与原生 sticky 逐条等价):
34
+ * - 组头未滚到吸附位时该层返回 null(在流渲染,自己自然上滑,吸附层完全不参与)
35
+ * - 越过吸附位后吸附(offset = 0)
36
+ * - 下一个组头逼近时被逐像素顶出(offset 为负):主组头被下一个主组头(= 本组
37
+ * 区段结束边界)顶出;子组头被「下一个子组头」与「下一个主组头(区段结束)」
38
+ * 中更近者顶出——组切换时子组头随本组区段一起滑出,与原生 sticky 的区段
39
+ * 语义一致,切换瞬间旧头恰好完全离场、新头恰好就位,零跳变
40
+ * - 边界(容器坐标恰好等于吸附位)不留容差:此刻吸附层与在流组头**像素重合**,
41
+ * 切换无视觉跳变;留 1px 容差反而会让吸附层与在流组头错开 1px(同一行画两遍)
42
+ *
43
+ * 子组头活跃判定带 parent 归属门(parentGroupOfSub):主组头切换后,上一个组的
44
+ * 子组头即使测量上早已越过也不得再吸附(否则新组头下方钉着旧组的子组头)。
45
+ * parentGroupOfSub 缺项的子组头不做归属约束(看板列内只有一层、无主组头行)。
46
+ *
47
+ * @param scrollTop 滚动容器的 scrollTop
48
+ * @param measurements 虚拟化行测量(start 以内容盒顶端为原点,size 为行高)
49
+ * @param levels 两层组头行下标与吸附参数
50
+ * @returns 两层各自的活跃组头行下标与位移;未越过吸附位 / 无可用测量时该层为 null
51
+ */
52
+
53
+ /** 单层吸附态:活跃组头行下标 + 相对该层吸附位的纵向位移(px,<= 0,被顶出时为负) */
54
+ export interface FrozenHeaderAnchor {
55
+ index: number;
56
+ offset: number;
57
+ }
58
+
59
+ export interface FrozenHeaderResult {
60
+ /** 活跃主组头;无主组层(如看板列内)或未越过容器顶边时为 null */
61
+ group: FrozenHeaderAnchor | null;
62
+ /** 活跃子组头;无子组越过吸附位(主组头下方 groupHeight 处)时为 null */
63
+ sub: FrozenHeaderAnchor | null;
64
+ }
65
+
66
+ export interface FrozenHeaderLevels {
67
+ /** 主组头行在 rows 中的下标(升序) */
68
+ groupIndexes: readonly number[];
69
+ /** 子组头行在 rows 中的下标(升序) */
70
+ subIndexes: readonly number[];
71
+ /** 子组头行下标 → 所属主组头行下标 */
72
+ parentGroupOfSub: ReadonlyMap<number, number>;
73
+ /**
74
+ * 滚动容器上内边距(px):行测量在内容盒坐标系,容器顶边在内容盒坐标 -pad 处。
75
+ * 看板列内滚动容器 p-2 → 8(SUB_HEADER_STICKY_PAD);story/bug 列表滚动容器
76
+ * 无内边距 → 0(缺省)。
77
+ */
78
+ pad?: number;
79
+ /**
80
+ * 子组头吸附位距容器顶边的偏移(px)= 吸附主组头的行高。story/bug 列表
81
+ * 组头恒 36px(HEADER_ROW_HEIGHT);看板列内无主组头行 → 0(缺省)。
82
+ */
83
+ subPinTop?: number;
21
84
  }
22
85
 
23
86
  export function computeFrozenHeader(
24
87
  scrollTop: number,
25
- measurements: ReadonlyArray<{ start: number } | undefined>,
26
- groupHeaderIndexes: readonly number[],
27
- subGroupHeaderIndexes: readonly number[],
28
- parentGroupOfSub: ReadonlyMap<number, number>,
29
- ): FrozenHeader | null {
30
- // overscroll(macOS 橡皮筋)下 scrollTop 可能为负,钳制后保证首个组头不被误判为「无活跃组头」
31
- const threshold = Math.max(0, scrollTop) + 1;
32
- let groupIdx: number | null = null;
33
- for (const i of groupHeaderIndexes) {
34
- const m = measurements[i];
35
- if (m && m.start <= threshold) groupIdx = i;
88
+ measurements: ReadonlyArray<{ start: number; size: number } | undefined>,
89
+ levels: FrozenHeaderLevels,
90
+ ): FrozenHeaderResult {
91
+ const { groupIndexes, subIndexes, parentGroupOfSub } = levels;
92
+ const pad = levels.pad ?? 0;
93
+ const subPinTop = levels.subPinTop ?? 0;
94
+ // overscroll(macOS 橡皮筋)下 scrollTop 可能为负,钳制避免首个组头失效
95
+ const top = Math.max(0, scrollTop);
96
+ // 容器顶边在内容盒坐标系的位置;组头上缘到达容器顶边 ⇔ m.start <= threshold
97
+ const threshold = top - pad;
98
+
99
+ /* ---- 主组头层:吸附位 = 容器顶边(0),被下一个主组头顶出 ---- */
100
+ let group: FrozenHeaderAnchor | null = null;
101
+ let groupPos = -1;
102
+ for (let k = 0; k < groupIndexes.length; k++) {
103
+ const m = measurements[groupIndexes[k]];
104
+ if (m && m.start <= threshold) {
105
+ group = { index: groupIndexes[k], offset: 0 };
106
+ groupPos = k;
107
+ }
108
+ }
109
+ if (group) {
110
+ const groupSize = measurements[group.index]?.size ?? 0;
111
+ let groupOffset = 0;
112
+ for (let k = groupPos + 1; k < groupIndexes.length; k++) {
113
+ const m = measurements[groupIndexes[k]];
114
+ if (!m) continue;
115
+ // 下一个主组头上缘(容器坐标 m.start - threshold)触及本组头底边时开始上推
116
+ groupOffset = Math.min(0, m.start - threshold - groupSize);
117
+ break;
118
+ }
119
+ group = { index: group.index, offset: groupOffset };
36
120
  }
37
- if (groupIdx == null) return null;
38
121
 
39
- let subIdx: number | null = null;
40
- for (const i of subGroupHeaderIndexes) {
41
- const m = measurements[i];
42
- if (m && m.start <= threshold && parentGroupOfSub.get(i) === groupIdx) subIdx = i;
122
+ /* ---- 子组头层:吸附位 = subPinTop,顶出源取更近的同类头/区段结束 ---- */
123
+ let sub: FrozenHeaderAnchor | null = null;
124
+ let subPos = -1;
125
+ const subThreshold = threshold + subPinTop;
126
+ for (let k = 0; k < subIndexes.length; k++) {
127
+ const idx = subIndexes[k];
128
+ const m = measurements[idx];
129
+ if (!m || m.start > subThreshold) continue;
130
+ // parent 归属门:归属缺失或归属非当前活跃主组头的子组头不吸附
131
+ const parent = parentGroupOfSub.get(idx);
132
+ if (parent !== undefined && (!group || parent !== group.index)) continue;
133
+ sub = { index: idx, offset: 0 };
134
+ subPos = k;
43
135
  }
44
- return { groupIdx, subIdx };
136
+ if (sub) {
137
+ const subSize = measurements[sub.index]?.size ?? 0;
138
+ // 顶出源 = 下一个子组头 / 下一个主组头(区段结束)中容器坐标更小(更近)者
139
+ let pusherCoord = Infinity;
140
+ for (let k = subPos + 1; k < subIndexes.length; k++) {
141
+ const m = measurements[subIndexes[k]];
142
+ if (!m) continue;
143
+ pusherCoord = m.start - threshold;
144
+ break;
145
+ }
146
+ for (let k = groupPos + 1; group && k < groupIndexes.length; k++) {
147
+ const m = measurements[groupIndexes[k]];
148
+ if (!m) continue;
149
+ pusherCoord = Math.min(pusherCoord, m.start - threshold);
150
+ break;
151
+ }
152
+ const subOffset =
153
+ pusherCoord === Infinity ? 0 : Math.min(0, pusherCoord - subPinTop - subSize);
154
+ sub = { index: sub.index, offset: subOffset };
155
+ }
156
+
157
+ return { group, sub };
45
158
  }
@@ -22,6 +22,13 @@
22
22
  * 但行是绝对定位的虚拟化行,CSS sticky 不生效,故用「等价换算」得到原生
23
23
  * sticky 的观感。
24
24
  *
25
+ * 任务 22 起算法本体抽到共享 `list-kit/frozen-header.ts` 的 computeFrozenHeader
26
+ * (story 列表 / bug 列表 / 看板列内三处统一),本文件保留看板侧单层签名作适配:
27
+ * 无主组头层(groupIndexes 空)、pad = SUB_HEADER_STICKY_PAD、subPinTop = 0
28
+ * (子组头直接吸附在容器顶边 = 列头 CardHeader 正下方)。本文件既有单测
29
+ * (__tests__/frozen-header.test.ts,2026-10-17 用户五轮裁决语义)原样保留并
30
+ * 全绿——即「切到共享实现后看板行为零变化」的回归证明。
31
+ *
25
32
  * **锚点必须是滚动容器顶边(scrollport top),不是内容盒顶**:滚动容器有
26
33
  * p-2 上内边距,行测量起点(内容盒坐标系)比容器坐标系低
27
34
  * SUB_HEADER_STICKY_PAD(8)px。活跃阈值 / 吸附位 / 顶出量三者必须同锚 ——
@@ -41,6 +48,9 @@
41
48
  * @param subHeaderIndexes 子分组头行在 rows 中的下标(升序)
42
49
  * @returns 活跃子分组头的行下标与位移;未越过容器顶边 / 无可用测量时返回 null
43
50
  */
51
+
52
+ import { computeFrozenHeader } from "../frozen-header";
53
+
44
54
  export interface FrozenSubHeader {
45
55
  /** 活跃子分组头的行下标 */
46
56
  index: number;
@@ -53,36 +63,19 @@ export interface FrozenSubHeader {
53
63
  * 判定阈值 / 吸附位 / 顶出量必须同时计入本值,否则吸附层早 8px 落下、裁掉首卡上边框。 */
54
64
  export const SUB_HEADER_STICKY_PAD = 8;
55
65
 
66
+ /** 看板列内无主组头层:子组头不做归属约束(parentGroupOfSub 恒空表,模块级复用) */
67
+ const EMPTY_PARENT_OF_SUB = new Map<number, number>();
68
+
56
69
  export function computeFrozenSubHeader(
57
70
  scrollTop: number,
58
71
  measurements: ReadonlyArray<{ start: number; size: number } | undefined>,
59
72
  subHeaderIndexes: readonly number[],
60
73
  ): FrozenSubHeader | null {
61
- // overscroll(macOS 橡皮筋)下 scrollTop 可能为负,钳制避免首个子组头失效
62
- const top = Math.max(0, scrollTop);
63
- // 换算到内容盒坐标系:组头上缘到达容器顶边 ⇔ m.start <= top - SUB_HEADER_STICKY_PAD
64
- const threshold = top - SUB_HEADER_STICKY_PAD;
65
-
66
- let active = -1;
67
- let activePos = -1;
68
- for (let k = 0; k < subHeaderIndexes.length; k++) {
69
- const m = measurements[subHeaderIndexes[k]];
70
- if (m && m.start <= threshold) {
71
- active = subHeaderIndexes[k];
72
- activePos = k;
73
- }
74
- }
75
- if (active < 0) return null;
76
-
77
- const activeSize = measurements[active]?.size ?? 0;
78
- let offset = 0;
79
- for (let k = activePos + 1; k < subHeaderIndexes.length; k++) {
80
- const m = measurements[subHeaderIndexes[k]];
81
- if (!m) continue;
82
- // 下一个组头上缘(容器坐标 start - threshold)进入「容器顶边 + 当前组头高」时开始上推
83
- offset = Math.min(0, m.start - threshold - activeSize);
84
- break;
85
- }
86
-
87
- return { index: active, offset };
74
+ return computeFrozenHeader(scrollTop, measurements, {
75
+ groupIndexes: [],
76
+ subIndexes: subHeaderIndexes,
77
+ parentGroupOfSub: EMPTY_PARENT_OF_SUB,
78
+ pad: SUB_HEADER_STICKY_PAD,
79
+ subPinTop: 0,
80
+ }).sub;
88
81
  }
@@ -90,6 +90,14 @@ vi.mock("../detail/story-detail-body", () => ({
90
90
  ),
91
91
  }));
92
92
 
93
+ // useAppHotkeys 需要 HotkeysProvider appHotkeys 挂载;单测无全局 Provider,
94
+ import { appHotkeys } from "@/lib/hotkeys/registry"
95
+ // 局部替换该 hook 返回应用声明,其余(useHotkey/useHotkeys)保持官方真实实现
96
+ vi.mock("@tanstack/react-hotkeys", async (importOriginal) => {
97
+ const actual = await importOriginal<typeof import("@tanstack/react-hotkeys")>()
98
+ return { ...actual, useAppHotkeys: () => appHotkeys }
99
+ })
100
+
93
101
  import { StoryPeekPanel } from "../peek-panel";
94
102
  import type { StoryDetail } from "@/shared/story/types";
95
103
 
@@ -0,0 +1,214 @@
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
+ // 任务 24(阶段 8):story 表三项验证——
19
+ // ① columnResizingFeature 拖拽手柄:拖拽后 columnSizing 状态提交且列宽即时生效;
20
+ // ② sortFns 按需注册(tree-shaking):注册表仅含 alphanumeric/text,
21
+ // 未注册内置名不被引用时行为不受影响(basic 由库内部兜底,非注册表消费)。
22
+ // 虚拟化在 jsdom 无布局,桩返回全量虚拟 items(列宽/排序断言不依赖窗口化)。
23
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
+ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
25
+ import { useMemo } from "react";
26
+ import { useTable } from "@tanstack/react-table";
27
+
28
+ // 虚拟化桩:全量 items(断言列宽/行序契约,不测窗口化)
29
+ vi.mock("@tanstack/react-virtual", async (importOriginal) => {
30
+ const actual = await importOriginal<typeof import("@tanstack/react-virtual")>();
31
+ return {
32
+ ...actual,
33
+ useVirtualizer: (opts: Parameters<typeof actual.useVirtualizer>[0]) => ({
34
+ getVirtualItems: () =>
35
+ Array.from({ length: opts.count }, (_, i) => ({
36
+ index: i,
37
+ key: i,
38
+ start: i * 44,
39
+ size: 44,
40
+ })),
41
+ getTotalSize: () => opts.count * 44,
42
+ measure: () => {},
43
+ scrollToIndex: () => {},
44
+ }),
45
+ };
46
+ });
47
+
48
+ // 无限滚动哨兵裁掉(IntersectionObserver 依赖,与本任务无关)
49
+ vi.mock("@/components/infinite-scroll-sentinel", () => ({
50
+ InfiniteScrollSentinel: () => null,
51
+ }));
52
+
53
+ import { StoryListTable } from "../story-list-table";
54
+ import { createStoryColumns, storyTableFeatures } from "../columns";
55
+ import type { StoryListItem } from "@/shared/story/types";
56
+ import type { DisplayProperty } from "../display/types";
57
+
58
+ function makeStory(overrides: Partial<StoryListItem> & Pick<StoryListItem, "id" | "identifier">): StoryListItem {
59
+ const user = {
60
+ id: "u1",
61
+ username: "alice",
62
+ email: "alice@example.com",
63
+ avatarConfig: null,
64
+ bgShape: null,
65
+ bgColor: null,
66
+ };
67
+ return {
68
+ name: `story ${overrides.identifier}`,
69
+ description: null,
70
+ state: "todo",
71
+ priority: "none",
72
+ archived: false,
73
+ startDate: null,
74
+ targetDate: null,
75
+ boardOrder: 0,
76
+ projectId: "p1",
77
+ parentId: null,
78
+ parent: null,
79
+ assignee: user,
80
+ createdBy: user,
81
+ labels: [],
82
+ createdAt: "2026-01-01T00:00:00.000Z",
83
+ updatedAt: "2026-01-02T00:00:00.000Z",
84
+ ...overrides,
85
+ };
86
+ }
87
+
88
+ const baseProps = {
89
+ onOpenStory: () => {},
90
+ properties: ["id", "priority", "state"] as DisplayProperty[],
91
+ orderBy: "manual" as const,
92
+ onLoadMore: () => {},
93
+ hasMore: false,
94
+ };
95
+
96
+ // jsdom 的 rAF 是异步帧,拖拽 move 走 rAF 节流;置为立即执行保证断言确定性。
97
+ beforeEach(() => {
98
+ vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => {
99
+ cb(performance.now());
100
+ return 1;
101
+ });
102
+ vi.stubGlobal("cancelAnimationFrame", () => {});
103
+ });
104
+
105
+ afterEach(() => {
106
+ cleanup();
107
+ vi.unstubAllGlobals();
108
+ });
109
+
110
+ describe("story 列宽拖拽(columnResizingFeature)", () => {
111
+ it("拖拽手柄提交 columnSizing 且列宽即时生效", () => {
112
+ const data = [
113
+ makeStory({ id: "s1", identifier: "S-1" }),
114
+ makeStory({ id: "s2", identifier: "S-2" }),
115
+ ];
116
+ const { container } = render(<StoryListTable data={data} {...baseProps} />);
117
+
118
+ // identifier 列定义 size=110;首列手柄 = 第一个 separator
119
+ const firstHeaderCell = screen.getByText("需求 ID") as HTMLElement;
120
+ expect(firstHeaderCell.style.width).toBe("110px");
121
+ const handle = firstHeaderCell.querySelector('[role="separator"]') as HTMLElement;
122
+ expect(handle).not.toBeNull();
123
+
124
+ // mousedown(100) → mousemove(150):delta +50 → 110 → 160(onChange 模式实时提交)
125
+ fireEvent.mouseDown(handle, { clientX: 100 });
126
+ // 拖拽中:columnResizing.isResizingColumn = identifier
127
+ expect(handle.dataset.resizing).toBe("true");
128
+ fireEvent.mouseMove(document, { clientX: 150 });
129
+ expect(firstHeaderCell.style.width).toBe("160px");
130
+ fireEvent.mouseUp(document, { clientX: 150 });
131
+ // 拖拽结束:瞬时 resizing 态复位
132
+ expect(handle.dataset.resizing).toBeUndefined();
133
+
134
+ // 列宽生效到行单元格(getSize 消费同一 columnSizing 状态)
135
+ const bodyCell = container.querySelector("button .overflow-hidden") as HTMLElement | null;
136
+ expect(bodyCell?.style.width).toBe("160px");
137
+ });
138
+
139
+ it("双击手柄复位列宽(resetSize 回列定义 size)", () => {
140
+ const data = [makeStory({ id: "s1", identifier: "S-1" })];
141
+ render(<StoryListTable data={data} {...baseProps} />);
142
+ const firstHeaderCell = screen.getByText("需求 ID") as HTMLElement;
143
+ const handle = firstHeaderCell.querySelector('[role="separator"]') as HTMLElement;
144
+
145
+ fireEvent.mouseDown(handle, { clientX: 100 });
146
+ fireEvent.mouseMove(document, { clientX: 200 });
147
+ fireEvent.mouseUp(document, { clientX: 200 });
148
+ expect(firstHeaderCell.style.width).toBe("210px");
149
+
150
+ fireEvent.doubleClick(handle);
151
+ expect(firstHeaderCell.style.width).toBe("110px");
152
+ });
153
+ });
154
+
155
+ describe("sortFns 按需注册", () => {
156
+ it("注册表仅含 alphanumeric/text(整包 sortFns 已移除)", () => {
157
+ expect(Object.keys(storyTableFeatures.sortFns ?? {}).sort()).toEqual([
158
+ "alphanumeric",
159
+ "text",
160
+ ]);
161
+ });
162
+
163
+ it("auto 解析命中已注册的 alphanumeric(编号列自然排序 2 < 10)", () => {
164
+ const data = [
165
+ makeStory({ id: "s2", identifier: "S-2" }),
166
+ makeStory({ id: "s10", identifier: "S-10" }),
167
+ makeStory({ id: "s1", identifier: "S-1" }),
168
+ ];
169
+ render(<SortHarness data={data} />);
170
+ // 首次点击 = asc:alphanumeric 给出 S-1, S-2, S-10;
171
+ // 若回落 basic(未注册时的库内兜底)则为字典序 S-1, S-10, S-2,可区分注册是否生效
172
+ fireEvent.click(screen.getByRole("button", { name: "identifier" }));
173
+ const ids = screen
174
+ .getAllByTestId(/^row-/)
175
+ .map((el) => el.getAttribute("data-testid"));
176
+ expect(ids).toEqual(["row-S-1", "row-S-2", "row-S-10"]);
177
+ });
178
+
179
+ it("auto 解析命中已注册的 text(纯文字列字典排序)", () => {
180
+ const data = [
181
+ makeStory({ id: "s1", identifier: "S-1", state: "todo" }),
182
+ makeStory({ id: "s2", identifier: "S-2", state: "done" }),
183
+ ];
184
+ render(<SortHarness data={data} />);
185
+ fireEvent.click(screen.getByRole("button", { name: "state" }));
186
+ const ids = screen
187
+ .getAllByTestId(/^row-/)
188
+ .map((el) => el.getAttribute("data-testid"));
189
+ expect(ids).toEqual(["row-S-2", "row-S-1"]);
190
+ });
191
+ });
192
+
193
+ function SortHarness({ data }: { data: StoryListItem[] }) {
194
+ const columns = useMemo(() => createStoryColumns(), []);
195
+ const table = useTable({
196
+ features: storyTableFeatures,
197
+ data,
198
+ columns,
199
+ });
200
+ return (
201
+ <div>
202
+ {table.getHeaderGroups()[0].headers.map((h) => (
203
+ <button key={h.id} onClick={h.column.getToggleSortingHandler()}>
204
+ {h.id}
205
+ </button>
206
+ ))}
207
+ {table.getRowModel().rows.map((r) => (
208
+ <div key={r.id} data-testid={`row-${r.original.identifier}`}>
209
+ {r.original.identifier}
210
+ </div>
211
+ ))}
212
+ </div>
213
+ );
214
+ }
@@ -17,11 +17,13 @@
17
17
 
18
18
  import {
19
19
  columnPinningFeature,
20
+ columnResizingFeature,
20
21
  columnSizingFeature,
21
22
  columnVisibilityFeature,
22
23
  createSortedRowModel,
23
24
  rowSortingFeature,
24
- sortFns,
25
+ sortFn_alphanumeric,
26
+ sortFn_text,
25
27
  tableFeatures,
26
28
  type ColumnDef,
27
29
  } from "@tanstack/react-table";
@@ -58,8 +60,15 @@ export const storyTableFeatures = tableFeatures({
58
60
  rowSortingFeature,
59
61
  columnPinningFeature,
60
62
  columnSizingFeature,
63
+ columnResizingFeature,
61
64
  columnVisibilityFeature,
62
- sortFns,
65
+ // 按需注册替代 sortFns 整包(tree-shaking):本表全部排序走 sortFn:'auto',
66
+ // 自动解析只会命中 alphanumeric(ISO 日期/编号等含数字字符串)与 text(纯文字),
67
+ // 未注册的内置名在 dev 下 warn 并回落 sortFn_basic(库内部兜底,不依赖注册表)。
68
+ sortFns: {
69
+ alphanumeric: sortFn_alphanumeric,
70
+ text: sortFn_text,
71
+ },
63
72
  sortedRowModel: createSortedRowModel(),
64
73
  });
65
74
 
@@ -18,7 +18,7 @@
18
18
  import { useCallback, useEffect, useMemo, useRef, useState, type UIEvent } from "react";
19
19
  import { createPortal } from "react-dom";
20
20
  import { useNavigate } from "@tanstack/react-router";
21
- import { useHotkey } from "@tanstack/react-hotkeys";
21
+ import { useAppHotkeys, useHotkey } from "@tanstack/react-hotkeys";
22
22
  import { Skeleton } from "@/components/ui/skeleton";
23
23
  import { Dialog, DialogContent } from "@/components/ui/dialog";
24
24
  import {
@@ -319,7 +319,9 @@ export function StoryPeekPanel({
319
319
  };
320
320
  }, [effectiveStoryId]);
321
321
 
322
- useHotkey("Escape", onClose, {
322
+ // 声明即绑定:close-panel 条目单一来源(lib/hotkeys/registry.ts)
323
+ const hk = useAppHotkeys();
324
+ useHotkey(hk["close-panel"], onClose, {
323
325
  enabled: mode === "side-peek" && history.length === 0,
324
326
  });
325
327
 
@@ -78,6 +78,9 @@ export function StoryListTable({ data, onOpenStory, properties, orderBy, onLoadM
78
78
  columns,
79
79
  state: { sorting, columnVisibility },
80
80
  onSortingChange: setSorting,
81
+ // onChange:拖拽过程实时提交 columnSizing(列宽随手柄即时生效)。
82
+ // 列宽持久化为内存态(P2 再接 localStorage),刷新后回到列定义 size。
83
+ columnResizeMode: "onChange",
81
84
  initialState: {
82
85
  columnPinning: { start: ["identifier", "name"], end: [] },
83
86
  },
@@ -113,7 +116,7 @@ export function StoryListTable({ data, onOpenStory, properties, orderBy, onLoadM
113
116
  <div
114
117
  key={header.id}
115
118
  style={{ width: header.getSize() }}
116
- className="flex h-full items-center gap-1 px-3 text-xs font-medium text-muted-foreground"
119
+ className="relative flex h-full items-center gap-1 px-3 text-xs font-medium text-muted-foreground"
117
120
  >
118
121
  {flexRender(header.column.columnDef.header, header.getContext())}
119
122
  {canSort && (
@@ -126,6 +129,18 @@ export function StoryListTable({ data, onOpenStory, properties, orderBy, onLoadM
126
129
  : <ChevronDown className="size-3" />}
127
130
  </button>
128
131
  )}
132
+ {header.column.getCanResize() && (
133
+ <div
134
+ role="separator"
135
+ aria-orientation="vertical"
136
+ aria-label={`${m.story_table_resizeColumn()}: ${header.id}`}
137
+ data-resizing={header.column.getIsResizing() || undefined}
138
+ onDoubleClick={() => header.column.resetSize()}
139
+ onMouseDown={header.getResizeHandler()}
140
+ onTouchStart={header.getResizeHandler()}
141
+ className="absolute inset-y-0 right-0 w-1 cursor-col-resize touch-none select-none bg-transparent hover:bg-border data-resizing:bg-primary/50"
142
+ />
143
+ )}
129
144
  </div>
130
145
  );
131
146
  })}