@anchrd/intel 0.59.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 (364) hide show
  1. package/README.md +496 -0
  2. package/bin/intel.mjs +134 -0
  3. package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
  4. package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
  5. package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
  6. package/dist/adapters/cloudflare/cloudflare.js +416 -0
  7. package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
  8. package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
  9. package/dist/adapters/content/content.d.ts +3 -0
  10. package/dist/adapters/content/content.js +128 -0
  11. package/dist/adapters/content/content.types.d.ts +38 -0
  12. package/dist/adapters/content/content.types.js +1 -0
  13. package/dist/adapters/db/db-audit.d.ts +62 -0
  14. package/dist/adapters/db/db-audit.js +150 -0
  15. package/dist/adapters/db/db-boards.d.ts +6 -0
  16. package/dist/adapters/db/db-boards.js +324 -0
  17. package/dist/adapters/db/db-feed.d.ts +30 -0
  18. package/dist/adapters/db/db-feed.js +276 -0
  19. package/dist/adapters/db/db-flows.d.ts +42 -0
  20. package/dist/adapters/db/db-flows.js +1311 -0
  21. package/dist/adapters/db/db-grants.d.ts +133 -0
  22. package/dist/adapters/db/db-grants.js +269 -0
  23. package/dist/adapters/db/db-indexing.d.ts +3 -0
  24. package/dist/adapters/db/db-indexing.js +200 -0
  25. package/dist/adapters/db/db-oauth.d.ts +3 -0
  26. package/dist/adapters/db/db-oauth.js +26 -0
  27. package/dist/adapters/db/db-prompts.d.ts +46 -0
  28. package/dist/adapters/db/db-prompts.js +144 -0
  29. package/dist/adapters/db/db.d.ts +6 -0
  30. package/dist/adapters/db/db.js +1687 -0
  31. package/dist/adapters/db/db.types.d.ts +16 -0
  32. package/dist/adapters/db/db.types.js +1 -0
  33. package/dist/adapters/document-converter/document-converter.d.ts +2 -0
  34. package/dist/adapters/document-converter/document-converter.js +26 -0
  35. package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
  36. package/dist/adapters/document-converter/document-converter.types.js +1 -0
  37. package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
  38. package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
  39. package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
  40. package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
  41. package/dist/adapters/index-queue/index-queue.d.ts +10 -0
  42. package/dist/adapters/index-queue/index-queue.js +12 -0
  43. package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
  44. package/dist/adapters/index-queue/index-queue.types.js +1 -0
  45. package/dist/adapters/json-schema/json-schema.d.ts +2 -0
  46. package/dist/adapters/json-schema/json-schema.js +77 -0
  47. package/dist/adapters/openid/openid.d.ts +5 -0
  48. package/dist/adapters/openid/openid.js +301 -0
  49. package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
  50. package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
  51. package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
  52. package/dist/adapters/remote-tools/remote-tools.js +129 -0
  53. package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
  54. package/dist/adapters/semantic-index/semantic-index.js +139 -0
  55. package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
  56. package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
  57. package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
  58. package/dist/adapters/session-cookie/session-cookie.js +87 -0
  59. package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
  60. package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
  61. package/dist/audit/audit.d.ts +2 -0
  62. package/dist/audit/audit.js +87 -0
  63. package/dist/audit/audit.types.d.ts +23 -0
  64. package/dist/audit/audit.types.js +1 -0
  65. package/dist/auth/auth.d.ts +2 -0
  66. package/dist/auth/auth.js +284 -0
  67. package/dist/auth/auth.types.d.ts +108 -0
  68. package/dist/auth/auth.types.js +1 -0
  69. package/dist/boards/boards.d.ts +44 -0
  70. package/dist/boards/boards.js +447 -0
  71. package/dist/boards/boards.types.d.ts +117 -0
  72. package/dist/boards/boards.types.js +1 -0
  73. package/dist/build/build.d.ts +2 -0
  74. package/dist/build/build.js +192 -0
  75. package/dist/build/build.types.d.ts +17 -0
  76. package/dist/build/build.types.js +1 -0
  77. package/dist/bundle/bundle.d.ts +4 -0
  78. package/dist/bundle/bundle.js +1194 -0
  79. package/dist/bundle/bundle.types.d.ts +33 -0
  80. package/dist/bundle/bundle.types.js +1 -0
  81. package/dist/cli/cli.d.ts +4 -0
  82. package/dist/cli/cli.js +262 -0
  83. package/dist/cli/cli.types.d.ts +14 -0
  84. package/dist/cli/cli.types.js +1 -0
  85. package/dist/contract/audit.d.ts +50 -0
  86. package/dist/contract/audit.js +72 -0
  87. package/dist/contract/board.d.ts +150 -0
  88. package/dist/contract/board.js +262 -0
  89. package/dist/contract/bundle.d.ts +147 -0
  90. package/dist/contract/bundle.js +139 -0
  91. package/dist/contract/contract.d.ts +112 -0
  92. package/dist/contract/contract.js +143 -0
  93. package/dist/contract/feed.d.ts +126 -0
  94. package/dist/contract/feed.js +149 -0
  95. package/dist/contract/flow-run.d.ts +346 -0
  96. package/dist/contract/flow-run.js +194 -0
  97. package/dist/contract/flow.d.ts +1057 -0
  98. package/dist/contract/flow.js +552 -0
  99. package/dist/contract/node.d.ts +551 -0
  100. package/dist/contract/node.js +456 -0
  101. package/dist/contract/share.d.ts +216 -0
  102. package/dist/contract/share.js +134 -0
  103. package/dist/contract/table.d.ts +171 -0
  104. package/dist/contract/table.js +117 -0
  105. package/dist/contract/tool.d.ts +122 -0
  106. package/dist/contract/tool.js +172 -0
  107. package/dist/feed/feed.d.ts +2 -0
  108. package/dist/feed/feed.js +77 -0
  109. package/dist/feed/feed.types.d.ts +23 -0
  110. package/dist/feed/feed.types.js +1 -0
  111. package/dist/flows/flows.d.ts +41 -0
  112. package/dist/flows/flows.js +2216 -0
  113. package/dist/flows/flows.types.d.ts +339 -0
  114. package/dist/flows/flows.types.js +1 -0
  115. package/dist/http/http.d.ts +9 -0
  116. package/dist/http/http.js +874 -0
  117. package/dist/http/http.types.d.ts +22 -0
  118. package/dist/http/http.types.js +1 -0
  119. package/dist/indexing/indexing.d.ts +6 -0
  120. package/dist/indexing/indexing.js +239 -0
  121. package/dist/indexing/indexing.types.d.ts +11 -0
  122. package/dist/indexing/indexing.types.js +1 -0
  123. package/dist/intel/intel.d.ts +3 -0
  124. package/dist/intel/intel.js +194 -0
  125. package/dist/intel/intel.types.d.ts +24 -0
  126. package/dist/intel/intel.types.js +1 -0
  127. package/dist/mcp/mcp.d.ts +2 -0
  128. package/dist/mcp/mcp.js +1292 -0
  129. package/dist/mcp/mcp.types.d.ts +25 -0
  130. package/dist/mcp/mcp.types.js +1 -0
  131. package/dist/nodes/document-links/document-links.d.ts +20 -0
  132. package/dist/nodes/document-links/document-links.js +57 -0
  133. package/dist/nodes/nodes.d.ts +2 -0
  134. package/dist/nodes/nodes.js +1667 -0
  135. package/dist/nodes/nodes.types.d.ts +511 -0
  136. package/dist/nodes/nodes.types.js +1 -0
  137. package/dist/prepare/prepare.d.ts +4 -0
  138. package/dist/prepare/prepare.js +16 -0
  139. package/dist/prepare/prepare.types.d.ts +9 -0
  140. package/dist/prepare/prepare.types.js +1 -0
  141. package/dist/prompts/prompts.d.ts +2 -0
  142. package/dist/prompts/prompts.js +65 -0
  143. package/dist/prompts/prompts.types.d.ts +71 -0
  144. package/dist/prompts/prompts.types.js +1 -0
  145. package/dist/shared/callers/callers.d.ts +28 -0
  146. package/dist/shared/callers/callers.js +30 -0
  147. package/dist/shared/csv/csv.d.ts +13 -0
  148. package/dist/shared/csv/csv.js +85 -0
  149. package/dist/shared/document-text/document-text.d.ts +21 -0
  150. package/dist/shared/document-text/document-text.js +31 -0
  151. package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
  152. package/dist/shared/gate-authorization/gate-authorization.js +26 -0
  153. package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
  154. package/dist/shared/grant-expiry/grant-expiry.js +26 -0
  155. package/dist/shared/intel-error/intel-error.d.ts +22 -0
  156. package/dist/shared/intel-error/intel-error.js +27 -0
  157. package/dist/shared/plain-title/plain-title.d.ts +16 -0
  158. package/dist/shared/plain-title/plain-title.js +45 -0
  159. package/dist/shared/problem-details/problem-details.d.ts +30 -0
  160. package/dist/shared/problem-details/problem-details.js +18 -0
  161. package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
  162. package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
  163. package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
  164. package/dist/shared/safe-return-path/safe-return-path.js +11 -0
  165. package/dist/shared/sha256/sha256.d.ts +2 -0
  166. package/dist/shared/sha256/sha256.js +14 -0
  167. package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
  168. package/dist/tools/tool-servers/tool-servers.js +177 -0
  169. package/dist/tools/tools.d.ts +2 -0
  170. package/dist/tools/tools.js +207 -0
  171. package/dist/tools/tools.types.d.ts +56 -0
  172. package/dist/tools/tools.types.js +1 -0
  173. package/examples/branding/favicon.svg +5 -0
  174. package/examples/branding/logo.svg +5 -0
  175. package/examples/branding/theme.css +16 -0
  176. package/examples/dev.vars.example +7 -0
  177. package/examples/intel.json +9 -0
  178. package/migrations/0000_intel_foundation.sql +203 -0
  179. package/migrations/0001_portal_native_tools.sql +21 -0
  180. package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
  181. package/migrations/0003_folder_permissions.sql +211 -0
  182. package/migrations/0004_subflow_runs.sql +14 -0
  183. package/migrations/0005_flow_node_cleanup.sql +28 -0
  184. package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
  185. package/migrations/0006_links_are_written_in_the_text.sql +20 -0
  186. package/migrations/0007_no_node_waits.sql +69 -0
  187. package/migrations/0008_three_layers.sql +130 -0
  188. package/migrations/0009_no_context_policy.sql +48 -0
  189. package/migrations/0010_no_run_waits_a_year.sql +27 -0
  190. package/migrations/0011_one_name_for_the_tree.sql +53 -0
  191. package/migrations/0012_table_snapshots.sql +29 -0
  192. package/migrations/0017_a_vector_per_card.sql +38 -0
  193. package/migrations/0018_no_context_policy_at_last.sql +97 -0
  194. package/migrations/0019_one_name_for_the_grants.sql +52 -0
  195. package/migrations/0020_cascade_purge_replay.sql +11 -0
  196. package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
  197. package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
  198. package/migrations/0023_a_board_and_its_tasks.sql +116 -0
  199. package/migrations/0024_the_archive_is_a_column.sql +33 -0
  200. package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
  201. package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
  202. package/migrations/0027_the_runs_of_every_flow.sql +27 -0
  203. package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
  204. package/package.json +118 -0
  205. package/src/contract/CLAUDE.md +152 -0
  206. package/src/contract/README.md +102 -0
  207. package/src/contract/audit.ts +85 -0
  208. package/src/contract/board.ts +306 -0
  209. package/src/contract/bundle.ts +157 -0
  210. package/src/contract/contract.ts +161 -0
  211. package/src/contract/feed.ts +165 -0
  212. package/src/contract/flow-run.ts +241 -0
  213. package/src/contract/flow.ts +675 -0
  214. package/src/contract/node.ts +583 -0
  215. package/src/contract/share.ts +196 -0
  216. package/src/contract/table.ts +178 -0
  217. package/src/contract/tool.ts +202 -0
  218. package/ui/README.md +110 -0
  219. package/ui/components.json +21 -0
  220. package/ui/index.html +13 -0
  221. package/ui/package.json +11 -0
  222. package/ui/scripts/lint-tokens.mjs +187 -0
  223. package/ui/src/access-summary/access-summary.tsx +385 -0
  224. package/ui/src/app/action-slot/action-slot.tsx +27 -0
  225. package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
  226. package/ui/src/app/app-tree/app-tree.tsx +1175 -0
  227. package/ui/src/app/app.tsx +87 -0
  228. package/ui/src/app/header-search/header-search.tsx +294 -0
  229. package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
  230. package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
  231. package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
  232. package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
  233. package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
  234. package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
  235. package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
  236. package/ui/src/app/tree-move/tree-move.tsx +427 -0
  237. package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
  238. package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
  239. package/ui/src/app/user-footer/user-footer.tsx +188 -0
  240. package/ui/src/app/view-menu/view-menu.tsx +115 -0
  241. package/ui/src/app-root/app-root.tsx +109 -0
  242. package/ui/src/app-root/app-root.types.ts +27 -0
  243. package/ui/src/archive/archive.tsx +417 -0
  244. package/ui/src/attachment-detail/attachment-detail.ts +52 -0
  245. package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
  246. package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
  247. package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
  248. package/ui/src/board/board-assignee/board-assignee.ts +48 -0
  249. package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
  250. package/ui/src/board/board-chip/board-chip.tsx +46 -0
  251. package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
  252. package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
  253. package/ui/src/board/board-data/board-data.ts +195 -0
  254. package/ui/src/board/board-data/board-data.types.ts +38 -0
  255. package/ui/src/board/board-dates/board-dates.ts +40 -0
  256. package/ui/src/board/board-dates/board-dates.tsx +105 -0
  257. package/ui/src/board/board-kanban/board-kanban.ts +162 -0
  258. package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
  259. package/ui/src/board/board-open-task/board-open-task.ts +32 -0
  260. package/ui/src/board/board-panel/board-panel.tsx +259 -0
  261. package/ui/src/board/board-settings/board-settings.tsx +226 -0
  262. package/ui/src/board/board-status/board-status.tsx +60 -0
  263. package/ui/src/board/board-stripes/board-stripes.ts +128 -0
  264. package/ui/src/board/board-table/board-table.ts +53 -0
  265. package/ui/src/board/board-table/board-table.tsx +610 -0
  266. package/ui/src/board/board-task/board-task.ts +108 -0
  267. package/ui/src/board/board-task/board-task.tsx +335 -0
  268. package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
  269. package/ui/src/branding/branding.tsx +17 -0
  270. package/ui/src/branding/custom-logo.ts +1 -0
  271. package/ui/src/branding/favicon.default.svg +5 -0
  272. package/ui/src/branding/favicon.svg +5 -0
  273. package/ui/src/components/ui/avatar.tsx +39 -0
  274. package/ui/src/components/ui/button.tsx +64 -0
  275. package/ui/src/components/ui/collapsible.tsx +20 -0
  276. package/ui/src/components/ui/command.tsx +160 -0
  277. package/ui/src/components/ui/dialog.tsx +143 -0
  278. package/ui/src/components/ui/dropdown-menu.tsx +187 -0
  279. package/ui/src/components/ui/input.tsx +21 -0
  280. package/ui/src/components/ui/popover.tsx +41 -0
  281. package/ui/src/components/ui/select.tsx +163 -0
  282. package/ui/src/components/ui/separator.tsx +26 -0
  283. package/ui/src/components/ui/sheet.tsx +136 -0
  284. package/ui/src/components/ui/sidebar.tsx +693 -0
  285. package/ui/src/components/ui/skeleton.tsx +13 -0
  286. package/ui/src/components/ui/switch.tsx +25 -0
  287. package/ui/src/components/ui/table.tsx +82 -0
  288. package/ui/src/components/ui/tabs.tsx +52 -0
  289. package/ui/src/components/ui/tooltip.tsx +51 -0
  290. package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
  291. package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
  292. package/ui/src/data/query-client.ts +18 -0
  293. package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
  294. package/ui/src/data/request-refusal/request-refusal.ts +51 -0
  295. package/ui/src/data/sign-in/sign-in.ts +97 -0
  296. package/ui/src/data/sign-in/sign-in.types.ts +15 -0
  297. package/ui/src/document-link/document-link.tsx +114 -0
  298. package/ui/src/document-markdown/document-markdown.ts +195 -0
  299. package/ui/src/editor-schema/editor-schema.ts +23 -0
  300. package/ui/src/entry-picker/entry-picker.tsx +238 -0
  301. package/ui/src/feed/feed.tsx +344 -0
  302. package/ui/src/file-preview/file-preview-view.tsx +496 -0
  303. package/ui/src/file-preview/file-preview.tsx +28 -0
  304. package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
  305. package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
  306. package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
  307. package/ui/src/file-preview/word-file-preview.tsx +5 -0
  308. package/ui/src/flow-runs/flow-runs.tsx +238 -0
  309. package/ui/src/flows/flows.tsx +1646 -0
  310. package/ui/src/flows/node-icon/node-icon.ts +34 -0
  311. package/ui/src/flows/node-palette/node-palette.tsx +214 -0
  312. package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
  313. package/ui/src/folder-contents/folder-contents.tsx +321 -0
  314. package/ui/src/folder-index/folder-index.ts +55 -0
  315. package/ui/src/frontmatter/frontmatter.tsx +193 -0
  316. package/ui/src/graph-pane/graph-pane.tsx +53 -0
  317. package/ui/src/hooks/use-mobile.ts +19 -0
  318. package/ui/src/i18n/custom.json +4 -0
  319. package/ui/src/i18n/de.json +609 -0
  320. package/ui/src/i18n/en.json +609 -0
  321. package/ui/src/i18n/es.json +609 -0
  322. package/ui/src/i18n/i18n-context.tsx +65 -0
  323. package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
  324. package/ui/src/i18n/i18n.ts +32 -0
  325. package/ui/src/i18n/i18n.types.ts +49 -0
  326. package/ui/src/kind-icon.ts +97 -0
  327. package/ui/src/lib/utils.ts +6 -0
  328. package/ui/src/main.tsx +68 -0
  329. package/ui/src/modal/modal.tsx +53 -0
  330. package/ui/src/node-details/node-details.tsx +545 -0
  331. package/ui/src/node-editor/node-editor.tsx +304 -0
  332. package/ui/src/node-graph/graph-notice.tsx +21 -0
  333. package/ui/src/node-graph/node-graph.ts +214 -0
  334. package/ui/src/node-graph/node-graph.tsx +295 -0
  335. package/ui/src/node-import/node-import.tsx +418 -0
  336. package/ui/src/node-save-error.ts +46 -0
  337. package/ui/src/node-source/node-source.tsx +128 -0
  338. package/ui/src/node-table/node-table.tsx +107 -0
  339. package/ui/src/nodes/nodes.tsx +240 -0
  340. package/ui/src/rename-resource/rename-resource.ts +80 -0
  341. package/ui/src/resolved-names/resolved-names.tsx +67 -0
  342. package/ui/src/resource-error.ts +33 -0
  343. package/ui/src/resource-menu/resource-menu.tsx +1156 -0
  344. package/ui/src/router/router-context.ts +6 -0
  345. package/ui/src/router/router.tsx +67 -0
  346. package/ui/src/router/router.types.ts +8 -0
  347. package/ui/src/router/selection-search.ts +88 -0
  348. package/ui/src/save-button/save-button.tsx +94 -0
  349. package/ui/src/section-hint/section-hint.tsx +40 -0
  350. package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
  351. package/ui/src/styles.css +189 -0
  352. package/ui/src/table-columns/table-columns.tsx +379 -0
  353. package/ui/src/theme/custom.css +1 -0
  354. package/ui/src/theme/theme-context.tsx +89 -0
  355. package/ui/src/theme/theme.ts +70 -0
  356. package/ui/src/time/relative-time.tsx +41 -0
  357. package/ui/src/time/time-context.tsx +78 -0
  358. package/ui/src/time/time.ts +165 -0
  359. package/ui/src/title-row/title-row.tsx +185 -0
  360. package/ui/src/tools/tools.tsx +546 -0
  361. package/ui/src/user-name/user-name.ts +61 -0
  362. package/ui/src/verbatim/verbatim.tsx +68 -0
  363. package/ui/tsconfig.json +17 -0
  364. package/ui/vite.config.ts +129 -0
@@ -0,0 +1,38 @@
1
+ import type {
2
+ BoardColumn,
3
+ BoardTask,
4
+ BoardTaskCreateInput,
5
+ BoardTaskUpdateInput,
6
+ } from "@contract/board.ts";
7
+
8
+ // One column and the cards in it, in the order they are drawn.
9
+ export interface ColumnWithCards {
10
+ column: BoardColumn;
11
+ cards: BoardTask[];
12
+ // ⚠️ Set only on a column the board does not configure — cards whose status names nothing. It is
13
+ // drawn differently rather than hidden: a card nobody can see is a card somebody will look for.
14
+ unknown?: boolean;
15
+ }
16
+
17
+ export interface BoardHandle {
18
+ boardId: string;
19
+ title: string;
20
+ columns: ColumnWithCards[];
21
+ isPending: boolean;
22
+ isError: boolean;
23
+ isWriting: boolean;
24
+ // `boardId` is supplied by the handle, so a view cannot file a card on the wrong board by
25
+ // forgetting a field.
26
+ createTask(input: Omit<BoardTaskCreateInput, "boardId">): Promise<void>;
27
+ updateTask(input: BoardTaskUpdateInput): Promise<void>;
28
+ /** Whether the archive column is drawn (D68). The settings dialog draws the switch in both
29
+ * positions, so it needs the current value, not only the column list. */
30
+ archiveVisible: boolean;
31
+ setColumns(input: {
32
+ columns: BoardColumn[];
33
+ // ⚠️ Optional means "leave it": the dialog writes the columns and the switch in ONE call, and a
34
+ // caller who only reorders must not un-hide an archive somebody put away.
35
+ archiveVisible?: boolean;
36
+ idempotencyKey: string;
37
+ }): Promise<void>;
38
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The one place a card's two dates become the one thing a reader sees (#724).
3
+ *
4
+ * ⚠️ **Pure, and separate from the chip that draws it.** The rules here — which of the four
5
+ * combinations produces which text — are the part worth holding in a test without a DOM, and the
6
+ * part a second surface would otherwise re-derive slightly differently.
7
+ */
8
+
9
+ /**
10
+ * `TT.MM.JJJJ`, from the ISO timestamp the contract carries.
11
+ *
12
+ * ⚠️ Built from the date PART of the string, never from a `Date`. `new Date("2026-08-15T00:00:00Z")`
13
+ * is the 14th in every timezone west of London, and a card would show the day before the one
14
+ * somebody picked. The stored value is a calendar day; it is read as one.
15
+ */
16
+ export function germanDate(iso: string): string {
17
+ const [year, month, day] = iso.slice(0, 10).split("-");
18
+ return `${day}.${month}.${year}`;
19
+ }
20
+
21
+ /** What the `<input type="date">` in the popover needs: the same day, the other way round. */
22
+ export function isoDay(iso: string): string {
23
+ return iso.slice(0, 10);
24
+ }
25
+
26
+ /**
27
+ * The label of the single date chip, or `null` when there is nothing to say.
28
+ *
29
+ * ⚠️ **A range only when there IS one.** With one date set the chip carries that date alone: a
30
+ * range with an empty half ("15.08.2026 - ") reads as a value somebody forgot to finish, and a
31
+ * placeholder for the missing end is a placeholder for an absence (#692).
32
+ */
33
+ export function dateLabel(startDate: string | null, dueDate: string | null): string | null {
34
+ if (startDate !== null && dueDate !== null) {
35
+ return `${germanDate(startDate)} - ${germanDate(dueDate)}`;
36
+ }
37
+ if (startDate !== null) return germanDate(startDate);
38
+ if (dueDate !== null) return germanDate(dueDate);
39
+ return null;
40
+ }
@@ -0,0 +1,105 @@
1
+ import { CalendarDays } from "lucide-react";
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuTrigger,
6
+ } from "@/components/ui/dropdown-menu.tsx";
7
+ import { useI18n } from "@/i18n/i18n-context.tsx";
8
+ import { dateLabel, isoDay } from "./board-dates.ts";
9
+
10
+ /**
11
+ * When a card runs, as one chip (#724).
12
+ *
13
+ * ⚠️ **One chip, not two.** It used to be `from …` and `due …` side by side, and that was two facts
14
+ * where a reader sees one: a card runs from a day to a day. The two are still edited and removed
15
+ * separately, but inside, where the popover can say which half it is changing.
16
+ *
17
+ * ⚠️ **The chip is the trigger.** A menu with no anchor never appears — that is `#723`, and it cost
18
+ * a whole release. `DropdownMenuContent` without a `DropdownMenuTrigger` in the same `DropdownMenu`
19
+ * is a finding in this repository.
20
+ */
21
+ export function DatesChip({
22
+ startDate,
23
+ dueDate,
24
+ write,
25
+ }: {
26
+ startDate: string | null;
27
+ dueDate: string | null;
28
+ write(input: { startDate?: string | null; dueDate?: string | null }): void;
29
+ }) {
30
+ const i18n = useI18n();
31
+ const label = dateLabel(startDate, dueDate);
32
+ if (label === null) return null;
33
+
34
+ // ⚠️ Midnight UTC, the same shape the plus menu writes. A card's dates are calendar days here, and
35
+ // a second format would make two cards written by two paths sort against each other.
36
+ const asStored = (day: string) => (day === "" ? null : `${day}T00:00:00.000Z`);
37
+
38
+ return (
39
+ <DropdownMenu>
40
+ <DropdownMenuTrigger
41
+ aria-label={`${label}, ${i18n.t("board.dates.edit")}`}
42
+ className="flex shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs tabular-nums outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
43
+ >
44
+ <CalendarDays aria-hidden="true" className="size-3.5" />
45
+ {label}
46
+ </DropdownMenuTrigger>
47
+ <DropdownMenuContent align="start" className="w-60 p-2">
48
+ <div className="flex flex-col gap-2">
49
+ <Field
50
+ label={i18n.t("board.add.start")}
51
+ value={startDate}
52
+ clear={i18n.t("board.clearStart")}
53
+ onChange={(day) => write({ startDate: asStored(day) })}
54
+ />
55
+ <Field
56
+ label={i18n.t("board.add.due")}
57
+ value={dueDate}
58
+ clear={i18n.t("board.clearDue")}
59
+ onChange={(day) => write({ dueDate: asStored(day) })}
60
+ />
61
+ </div>
62
+ </DropdownMenuContent>
63
+ </DropdownMenu>
64
+ );
65
+ }
66
+
67
+ /**
68
+ * One half of the range.
69
+ *
70
+ * ⚠️ **The remove button only exists while there is something to remove.** A permanently visible
71
+ * one is a control that does nothing on most cards, and a reader learns to ignore it.
72
+ */
73
+ function Field({
74
+ label,
75
+ value,
76
+ clear,
77
+ onChange,
78
+ }: {
79
+ label: string;
80
+ value: string | null;
81
+ clear: string;
82
+ onChange(day: string): void;
83
+ }) {
84
+ return (
85
+ <label className="flex items-center gap-2 text-xs">
86
+ <span className="w-14 shrink-0 text-muted-foreground">{label}</span>
87
+ <input
88
+ type="date"
89
+ value={value === null ? "" : isoDay(value)}
90
+ onChange={(event) => onChange(event.target.value)}
91
+ className="min-w-0 flex-1 rounded-md border bg-background px-2 py-1 tabular-nums outline-none focus-visible:ring-2 focus-visible:ring-ring"
92
+ />
93
+ {value === null ? null : (
94
+ <button
95
+ type="button"
96
+ aria-label={`${label}, ${clear}`}
97
+ onClick={() => onChange("")}
98
+ className="shrink-0 rounded-md px-1 text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
99
+ >
100
+ ×
101
+ </button>
102
+ )}
103
+ </label>
104
+ );
105
+ }
@@ -0,0 +1,162 @@
1
+ import type { BoardTask } from "@contract/board.ts";
2
+ import { positionBetween } from "@/board/board-data/board-data.ts";
3
+ import type { ColumnWithCards } from "@/board/board-data/board-data.types.ts";
4
+
5
+ /**
6
+ * Where a dragged card was let go.
7
+ *
8
+ * ⚠️ **`index` counts into the column AS RENDERED — including the dragged card itself when it
9
+ * started there.** That is what a drop handler can actually measure: the card stays in the list
10
+ * while the gesture runs, and the browser reports the gap it hovers over. Reading it as an index
11
+ * into the list *without* the card is off by one for every downward move inside a column, and the
12
+ * symptom is a card landing one place short of where it was let go — which looks like a sloppy
13
+ * animation rather than a bug.
14
+ */
15
+ export interface Drop {
16
+ status: string;
17
+ index: number;
18
+ }
19
+
20
+ /**
21
+ * What a drop means as a write — the whole of the drag logic, kept out of the component so it can be
22
+ * proven without a browser.
23
+ *
24
+ * ⚠️ **`null` means "changed nothing", and the caller must not write.** A card let go where it
25
+ * already sits is the most common drop there is: somebody picks one up, thinks again, and puts it
26
+ * back. Writing anyway costs a round trip, a re-render, and — because the position would be
27
+ * recomputed — a card that visibly jumps for no reason.
28
+ *
29
+ * ⚠️ **The card itself is removed from the neighbour list first.** Without that, dragging a card
30
+ * one place down within its own column measures against its OWN old position: the midpoint lands
31
+ * between the card and its neighbour, which is where it already was. It would look like the drag
32
+ * did nothing, which is the same symptom as a broken drop handler and reads as one.
33
+ */
34
+ export function dropToWrite(
35
+ card: BoardTask,
36
+ columns: ColumnWithCards[],
37
+ drop: Drop,
38
+ ): { status: string; position: number } | null {
39
+ const target = columns.find((entry) => entry.column.id === drop.status);
40
+ if (target === undefined) return null;
41
+
42
+ const others = target.cards.filter((entry) => entry.id !== card.id);
43
+ const wasAt = target.cards.findIndex((entry) => entry.id === card.id);
44
+
45
+ // ⚠️ The card is counted out of its own gap. Dropped BELOW where it started, every card it passed
46
+ // has shifted up by one, so the index into the remaining cards is one lower. Above its start
47
+ // nothing moved and the index carries over unchanged.
48
+ const wanted = wasAt >= 0 && drop.index > wasAt ? drop.index - 1 : drop.index;
49
+ const index = Math.max(0, Math.min(wanted, others.length));
50
+
51
+ // Let go in its own gap: nothing moved, and writing anyway would recompute the position and make
52
+ // the card jump for no reason.
53
+ if (wasAt >= 0 && index === wasAt) return null;
54
+
55
+ return {
56
+ status: drop.status,
57
+ position: positionBetween(others[index - 1]?.position, others[index]?.position),
58
+ };
59
+ }
60
+
61
+ /**
62
+ * The card a keyboard move should land on, given the one it starts from.
63
+ *
64
+ * ⚠️ Keyboard reachability is part of the first version here, not polish — dragging is a mouse
65
+ * gesture and a board that can only be operated by dragging is a board half the people cannot use.
66
+ * Left and right move between columns and keep the row; up and down move within one.
67
+ */
68
+ export function neighbourDrop(
69
+ card: BoardTask,
70
+ columns: ColumnWithCards[],
71
+ direction: "left" | "right" | "up" | "down",
72
+ ): Drop | null {
73
+ const columnIndex = columns.findIndex((entry) => entry.column.id === card.status);
74
+ const column = columns[columnIndex];
75
+ if (column === undefined) return null;
76
+ const row = column.cards.findIndex((entry) => entry.id === card.id);
77
+
78
+ if (direction === "left" || direction === "right") {
79
+ const next = columns[columnIndex + (direction === "left" ? -1 : 1)];
80
+ // ⚠️ No wrapping. A card that jumps from the last column to the first because somebody pressed
81
+ // the arrow once too often is a move nobody asked for, and the one they did ask for is lost.
82
+ if (next === undefined) return null;
83
+ return { status: next.column.id, index: Math.min(row, next.cards.length) };
84
+ }
85
+ const landing = row + (direction === "up" ? -1 : 1);
86
+ if (landing < 0 || landing >= column.cards.length) return null;
87
+ // ⚠️ Down needs one more than the row it lands on. `index` is a GAP in the list AS RENDERED, and
88
+ // moving down one place means ending up BEHIND the neighbour — that is the gap after it. Naming
89
+ // the neighbour's own gap names the gap the card already occupies, and `dropToWrite` then
90
+ // correctly answers "nothing changed" and swallows the move. Upwards the two coincide, which is
91
+ // why this reads as an asymmetry and is not one.
92
+ return { status: column.column.id, index: direction === "down" ? landing + 1 : landing };
93
+ }
94
+
95
+ /**
96
+ * What a finished drag means as a `Drop` — translated from what `dnd-kit` reports.
97
+ *
98
+ * ⚠️ **`over` is either a CARD or a COLUMN, and the two mean different things.** Let go over a
99
+ * card, the gesture means "into that card's gap"; let go over the column itself — the free area
100
+ * below the last card — it means "append". Reading both as the same thing is how every drop ends
101
+ * up at the bottom of the column, which looks like a broken drag rather than like a missing case.
102
+ *
103
+ * ⚠️ **`after` is only consulted ACROSS columns.** Inside one column the direction of travel
104
+ * already says which gap is meant; between columns there is none, so which side of the hovered card
105
+ * the CARRIED card came to rest on decides — see `dropIsAfter`.
106
+ *
107
+ * ⚠️ **Downwards inside one column, the gap is one BEHIND the card hovered.** `Drop.index` counts
108
+ * into the column as rendered, with the dragged card still in it. Dragging a card down onto a
109
+ * neighbour means ending up behind that neighbour; naming the neighbour's own gap names the gap the
110
+ * card already occupies, `dropToWrite` then correctly answers "nothing changed", and the move is
111
+ * swallowed without a word. Upwards the two coincide, which is why this reads as an asymmetry and
112
+ * is not one.
113
+ */
114
+ export function dragEndToDrop(
115
+ activeId: string,
116
+ overId: string | null,
117
+ columns: ColumnWithCards[],
118
+ after = false,
119
+ ): Drop | null {
120
+ if (overId === null || overId === activeId) return null;
121
+
122
+ const asColumn = columns.find((entry) => entry.column.id === overId);
123
+ if (asColumn !== undefined) return { status: asColumn.column.id, index: asColumn.cards.length };
124
+
125
+ for (const entry of columns) {
126
+ const row = entry.cards.findIndex((card) => card.id === overId);
127
+ if (row < 0) continue;
128
+ const from = entry.cards.findIndex((card) => card.id === activeId);
129
+ // Inside one column the direction of travel already says it: downwards means behind.
130
+ if (from >= 0) return { status: entry.column.id, index: from < row ? row + 1 : row };
131
+ // ⚠️ Across columns there is no direction to read, so the half of the card decides — and
132
+ // without it the END of a foreign column is reachable only through the thin strip below its
133
+ // last card. That is the capability the half-of-the-card check carried before `dnd-kit`.
134
+ return { status: entry.column.id, index: after ? row + 1 : row };
135
+ }
136
+ // A card that is on no column of this board — an answer that arrived while the gesture ran.
137
+ return null;
138
+ }
139
+
140
+ /** A box, as far as this decision cares: where its top is and how tall it is. */
141
+ export interface DropBox {
142
+ top: number;
143
+ height: number;
144
+ }
145
+
146
+ /**
147
+ * Whether a carried card came to rest BELOW the middle of the card it was let go on.
148
+ *
149
+ * ⚠️ **This compares two CARDS, not a pointer against a card.** The gesture reports where the
150
+ * carried card ended up, not where the cursor is, and on cards of different heights those are two
151
+ * different answers. Naming it after the pointer would send the next reader looking for a
152
+ * coordinate that is not in this code.
153
+ *
154
+ * ⚠️ **A missing box means "before".** `translated` is `null` until the drag has actually moved
155
+ * something, and in jsdom every box is zero — so the fallback is the one that has to be named
156
+ * rather than fallen into: no measurement is not "at the end", it is "no information", and the
157
+ * front of the gap is where a card goes when nothing is known.
158
+ */
159
+ export function dropIsAfter(carried: DropBox | null, over: DropBox | undefined): boolean {
160
+ if (carried === null || over === undefined) return false;
161
+ return carried.top + carried.height / 2 > over.top + over.height / 2;
162
+ }