@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,108 @@
1
+ import type { BoardTask } from "@contract/board.ts";
2
+ import type { ColumnWithCards } from "@/board/board-data/board-data.types.ts";
3
+
4
+ /** What one entry of the task's link list is. The icon is drawn from this, nothing else. */
5
+ /**
6
+ * ⚠️ **No `reference` kind yet, on purpose.** A plain reference is a link in the node graph and
7
+ * needs `node_link_list` — a second read this screen does not make. Carrying the kind here with no
8
+ * caller would be a path that looks built and is not; it is `#687` instead.
9
+ */
10
+ export type LinkKind = "subtask" | "blocker" | "blocked";
11
+
12
+ export interface TaskLink {
13
+ kind: LinkKind;
14
+ /** The linked node's id — its address, and the key of the row. */
15
+ id: string;
16
+ /** ⚠️ `null` when this answer holds no name for it. The VIEW decides what to say then; putting
17
+ * the id here would put it on screen. */
18
+ title: string | null;
19
+ /** The column the linked task sits in, so its state is readable without opening it. */
20
+ columnTitle: string | null;
21
+ /**
22
+ * The colour of that column (#725), so the row can show the state as a dot as well as a word.
23
+ *
24
+ * ⚠️ `undefined` is a real answer, not a gap: a board configured before colours existed has none,
25
+ * and a blocker outside this board has no column at all.
26
+ */
27
+ columnColor: string | undefined;
28
+ /** Set on a subtask only: whether it sits in a column the board calls terminal. */
29
+ done: boolean;
30
+ }
31
+
32
+ /**
33
+ * ⚠️ **The order is a DECISION, not an accident.** Subtasks, blockers and references stand mixed in
34
+ * one list, and a list whose order nobody can name reorders itself on every load. The key is, in
35
+ * this order:
36
+ *
37
+ * 1. **by kind** — subtasks, then what this task waits for, then what waits for it. Subtasks first
38
+ * because they carry the progress; what waits on this one last, because it says nothing about
39
+ * whether this task itself can move.
40
+ * 2. **by the board's own position** within a kind, so the list reads in the same order as the
41
+ * column it came from.
42
+ * 3. **by title**, only to break a tie — two cards may share a position after a filtered read.
43
+ */
44
+ const kindOrder: Record<LinkKind, number> = {
45
+ subtask: 0,
46
+ blocker: 1,
47
+ blocked: 2,
48
+ };
49
+
50
+ export function linksOf(task: BoardTask, columns: ColumnWithCards[]): TaskLink[] {
51
+ const cards = columns.flatMap((entry) =>
52
+ entry.cards.map((card) => ({ card, column: entry.column })),
53
+ );
54
+ const at = (id: string | null) =>
55
+ id === null ? undefined : cards.find((entry) => entry.card.id === id);
56
+ const position = (id: string) => at(id)?.card.position ?? Number.POSITIVE_INFINITY;
57
+
58
+ const links: TaskLink[] = [];
59
+
60
+ for (const { card, column } of cards) {
61
+ if (card.parentTaskId === task.id) {
62
+ links.push({
63
+ kind: "subtask",
64
+ id: card.id,
65
+ title: card.title,
66
+ columnTitle: column.title,
67
+ columnColor: column.color,
68
+ done: column.terminal,
69
+ });
70
+ }
71
+ // ⚠️ The other direction too. `dependsOn` is stored on the waiting card, so a task only learns
72
+ // what waits for IT by looking at every card — and that is exactly what somebody opening a
73
+ // blocker wants to know before they move it.
74
+ if (card.dependsOn === task.id && card.id !== task.id) {
75
+ links.push({
76
+ kind: "blocked",
77
+ id: card.id,
78
+ title: card.title,
79
+ columnTitle: column.title,
80
+ columnColor: column.color,
81
+ done: false,
82
+ });
83
+ }
84
+ }
85
+
86
+ if (task.dependsOn !== null) {
87
+ const blocking = at(task.dependsOn);
88
+ links.push({
89
+ kind: "blocker",
90
+ id: task.dependsOn,
91
+ // ⚠️ **`null`, never the id.** A blocker may be any node, not only a card of this board, so
92
+ // this answer often has no title for it — and an id put on screen in place of a name is the
93
+ // rule in `user-name.ts` (#258) broken with a node id instead of a principal id. The view
94
+ // says "something outside this board"; the id says nothing at all.
95
+ title: blocking?.card.title ?? null,
96
+ columnTitle: blocking?.column.title ?? null,
97
+ columnColor: blocking?.column.color,
98
+ done: false,
99
+ });
100
+ }
101
+
102
+ return links.sort(
103
+ (a, b) =>
104
+ kindOrder[a.kind] - kindOrder[b.kind] ||
105
+ position(a.id) - position(b.id) ||
106
+ (a.title ?? "").localeCompare(b.title ?? ""),
107
+ );
108
+ }
@@ -0,0 +1,335 @@
1
+ import type { BoardTask } from "@contract/board.ts";
2
+ import { Lock, Plus } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { AssigneeChip } from "@/board/board-assignee/board-assignee.tsx";
5
+ import { BoardChip } from "@/board/board-chip/board-chip.tsx";
6
+ import type { BoardHandle } from "@/board/board-data/board-data.types.ts";
7
+ import { DatesChip } from "@/board/board-dates/board-dates.tsx";
8
+ import { ColumnDot } from "@/board/board-kanban/board-kanban.tsx";
9
+ import { StatusChip } from "@/board/board-status/board-status.tsx";
10
+ import {
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuTrigger,
15
+ } from "@/components/ui/dropdown-menu.tsx";
16
+ import { useI18n } from "@/i18n/i18n-context.tsx";
17
+ import { linksOf, type TaskLink } from "./board-task.ts";
18
+
19
+ /** One entry of the link list. The icon carries the kind; nothing writes the word out. */
20
+ function LinkRow({ link, onOpen }: { link: TaskLink; onOpen(id: string): void }) {
21
+ const i18n = useI18n();
22
+ /**
23
+ * ⚠️ **A subtask shows its column as a DOT, not a tick** (#725). Jack's report of 2026-08-21 was
24
+ * that the open subtasks carry a checkmark in front of them that cannot be ticked. A checkbox
25
+ * promises an action this list does not have, and a card is finished by being moved, not by being
26
+ * ticked here.
27
+ *
28
+ * ⚠️ **The lock stays.** A blocker outside this board is not a state on a scale, it is something
29
+ * the reader may not open, and a colour cannot say that.
30
+ */
31
+ /**
32
+ * ⚠️ **The lock is about READABILITY, not about the kind**, and keying it on the kind was wrong in
33
+ * both directions: `blocked` is always a card of this board and always readable, while `blocker`
34
+ * may be any node anywhere — the one case where this answer holds no title and no column at all.
35
+ *
36
+ * `title === null` is exactly that case, and it is the same condition the row already uses one
37
+ * line down to say "outside this board". A dot there would be a colour for a column that does not
38
+ * exist.
39
+ */
40
+ const unreadable = link.title === null;
41
+ return (
42
+ <li className="flex items-center gap-2 border-b py-2 last:border-0">
43
+ {unreadable ? (
44
+ <Lock aria-hidden="true" className="size-4 shrink-0 text-destructive" />
45
+ ) : (
46
+ <ColumnDot color={link.columnColor} />
47
+ )}
48
+ <button
49
+ type="button"
50
+ onClick={() => onOpen(link.id)}
51
+ className="flex-1 truncate text-left text-sm outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring"
52
+ >
53
+ {/* ⚠️ A name or a sentence, never the id. This answer holds no title for a node outside the
54
+ board, and a ulid on screen is a label nobody can read (`user-name.ts`, #258). */}
55
+ {link.title ?? i18n.t("board.link.outside")}
56
+ </button>
57
+ {/* ⚠️ The column of the LINKED task, so its state is readable without opening it — the whole
58
+ reason the list carries a right-hand column at all. */}
59
+ {/* ⚠️ Empty rather than a word, and no template over the kind. Only a BLOCKER can be without
60
+ a column — it may be any node, not a card of this board — and the title beside it already
61
+ says "outside this board". A `board.link.${kind}` lookup could reach exactly one of the
62
+ three keys, and kept the other two alive in all three catalogs for nobody. */}
63
+ <span className="shrink-0 text-xs text-muted-foreground">{link.columnTitle ?? ""}</span>
64
+ </li>
65
+ );
66
+ }
67
+
68
+ export function BoardTaskDocument({
69
+ task,
70
+ board,
71
+ onOpen,
72
+ body,
73
+ }: {
74
+ task: BoardTask;
75
+ board: BoardHandle;
76
+ onOpen(taskId: string): void;
77
+ /** The markdown surface, handed in so this component stays free of the editor's own loading. */
78
+ body: React.ReactNode;
79
+ }) {
80
+ const i18n = useI18n();
81
+
82
+ const links = linksOf(task, board.columns);
83
+ const columns = board.columns.filter((entry) => entry.unknown !== true).map((e) => e.column);
84
+ const column = columns.find((entry) => entry.id === task.status);
85
+
86
+ /**
87
+ * ⚠️ **Refused while a write is in flight, like every other surface of this board.** Labels make
88
+ * it worst: the whole list is REPLACED, so two quick clicks each send the list minus their own
89
+ * entry, the second answer wins, and one of the two removals is silently undone — both calls
90
+ * succeeded, so nothing anywhere reports a problem (#651).
91
+ */
92
+ const write = (input: Partial<Omit<Parameters<BoardHandle["updateTask"]>[0], "taskId">>) => {
93
+ if (board.isWriting) return;
94
+ void board.updateTask({
95
+ taskId: task.id,
96
+ ...input,
97
+ idempotencyKey: crypto.randomUUID(),
98
+ });
99
+ };
100
+
101
+ return (
102
+ <div
103
+ /* ⚠️ `pt-0`, and the 16px above the chips is the TITLE ROW's own `py-4` (#724). Adding padding
104
+ here as well would stack two gaps into 40px, which is what it was: the chips looked like a
105
+ section of their own instead of the second line of one head. `gap-6` below sets the 24px
106
+ that separates the head from the link list, which IS a section of its own. */
107
+ className="flex min-h-0 flex-1 flex-col gap-6 overflow-auto px-6 pt-0 pb-6"
108
+ aria-busy={board.isWriting}
109
+ >
110
+ {/* ⚠️ No heading and no way back HERE. Both live in the title row above since #692: the path
111
+ there ends in this card's name, so a heading would print it a second time, and an arrow
112
+ here would be a second control meaning what the one up there already means. */}
113
+ {/* ⚠️ ONE line of chips, and no field labels. "Status:" in front of a chip that says "Backlog"
114
+ says nothing the chip does not — and it turns a quiet head into a form. */}
115
+ <div className="flex flex-wrap items-center gap-2">
116
+ <StatusChip
117
+ status={task.status}
118
+ title={column?.title ?? task.status}
119
+ columns={columns}
120
+ write={write}
121
+ />
122
+
123
+ {/* ⚠️ ONE chip, since #724. It was two, `from …` and `due …` — two facts where a reader
124
+ sees one: a card runs from a day to a day. Each half is still edited and removed on its
125
+ own, but inside the popover, where the control can say which half it means. */}
126
+ <DatesChip startDate={task.startDate} dueDate={task.dueDate} write={write} />
127
+
128
+ {task.labels.map((entry) => (
129
+ <BoardChip
130
+ key={entry}
131
+ label={entry}
132
+ tone="inverted"
133
+ action={i18n.t("board.removeLabel")}
134
+ onClick={() => write({ labels: task.labels.filter((keep) => keep !== entry) })}
135
+ />
136
+ ))}
137
+
138
+ {/* ⚠️ **The last VALUE, and the plus comes after it** (#750, Jack's decision 2026-08-22). It
139
+ is the only round element in the row; standing between the chips it breaks the line. But
140
+ the plus is not a value, it is the control that adds one, and a control belongs behind
141
+ everything it can add to.
142
+
143
+ ⚠️ Drawn even when nobody has the card: an empty circle is the one control on the row
144
+ that says the card COULD be given to somebody, which is precisely what an unowned card
145
+ needs (#724). */}
146
+ <AssigneeChip
147
+ id={task.assigneeId}
148
+ boardId={board.boardId}
149
+ onPick={(assigneeId) => write({ assigneeId })}
150
+ />
151
+
152
+ <Adder task={task} board={board} write={write} />
153
+ </div>
154
+
155
+ {/* ⚠️ ONE list, not three blocks: the icon carries the kind. And it exists only when there is
156
+ something in it. An empty section with a heading and a hint tells the reader that something
157
+ is missing; nothing at all tells them there is nothing, which is the truth (#692). */}
158
+ {links.length === 0 ? null : (
159
+ <section aria-label={i18n.t("board.links")} className="flex flex-col gap-1">
160
+ {/* ⚠️ Secondary, and the counter is gone (#724). The heading is a label for the list
161
+ under it, not a section title competing with the card's name; and "0 of 2 done" was a
162
+ number nobody acts on — the rows themselves say which are done, and they say it
163
+ without arithmetic. */}
164
+ <h2 className="text-xs text-muted-foreground">{i18n.t("board.links")}</h2>
165
+ <ul className="flex flex-col">
166
+ {links.map((link) => (
167
+ <LinkRow key={`${link.kind}:${link.id}`} link={link} onOpen={onOpen} />
168
+ ))}
169
+ </ul>
170
+ </section>
171
+ )}
172
+
173
+ {/* The markdown text is the largest element of the page — everything above is the quiet part.
174
+ ⚠️ The slot is not decoration: a test that asks whether the BODY drew something has to be
175
+ able to say which part of the card it means. Unscoped, the same loading line elsewhere on
176
+ the card answers for it, and the assurance goes hollow without anybody touching it. */}
177
+ <div data-slot="task-body" className="min-h-0 flex-1">
178
+ {body}
179
+ </div>
180
+ </div>
181
+ );
182
+ }
183
+
184
+ /**
185
+ * ⚠️ **ONE `+ Verlinkung`, and the kind is chosen afterwards** — not two buttons labelled "subtask"
186
+ * and "waits for". That is `#371`: the reader decides WHAT they are linking once they know which
187
+ * card they mean, not before.
188
+ */
189
+
190
+ /**
191
+ * What the card can still be given. The order matches the chips it produces.
192
+ *
193
+ * ⚠️ **No `assignee` here, and that is a gap rather than a decision.** Setting one needs a person to
194
+ * pick from, and this application can only ever DISPLAY principals it already sees in a grant: there
195
+ * is no list to choose from anywhere in the data provider. An entry that opens nothing is worse than
196
+ * a missing one, because it looks like the feature is there (`#700`).
197
+ */
198
+ // ⚠️ **No `assignee` here** (#750). It stood first once, back when the circle was drawn only for
199
+ // somebody and an unowned card had nothing to press. Since #724 the circle is always there and opens
200
+ // the same picker, so an entry here would be a second way to the same thing, two lines apart — and
201
+ // the one somebody finds first would depend on where they happened to look.
202
+ const addable = ["label", "start", "due"] as const;
203
+ const linkable = ["subtask", "blocker"] as const;
204
+ type Addable = (typeof addable)[number] | (typeof linkable)[number];
205
+
206
+ /**
207
+ * ⚠️ **ONE plus for everything the card can carry**, and the kind is chosen afterwards. Two
208
+ * controls, one for fields and one for links, asked the reader to know which of the two a thing was
209
+ * before they could add it, and that is a question about our data model, not about their work.
210
+ */
211
+ function Adder({
212
+ task,
213
+ board,
214
+ write,
215
+ }: {
216
+ task: BoardTask;
217
+ board: BoardHandle;
218
+ write(input: Partial<Omit<Parameters<BoardHandle["updateTask"]>[0], "taskId">>): void;
219
+ }) {
220
+ const i18n = useI18n();
221
+ const [kind, setKind] = useState<Addable | null>(null);
222
+ const [draft, setDraft] = useState("");
223
+ const close = () => {
224
+ setKind(null);
225
+ setDraft("");
226
+ };
227
+
228
+ const candidates = board.columns
229
+ .flatMap((entry) => entry.cards)
230
+ .filter((card) => card.id !== task.id && card.parentTaskId !== task.id);
231
+
232
+ // ⚠️ The picker is not an `<input>`, so it cannot ride the `draft` path below. It gets its own
233
+ // branch rather than a special case inside `commit`, because a "value" that is picked from a
234
+ // remote list has nothing in common with one that is typed.
235
+ const commit = () => {
236
+ const value = draft.trim();
237
+ if (value.length === 0 || board.isWriting) return close();
238
+ if (kind === "label") {
239
+ // ⚠️ Tags are REPLACED, not merged, so the whole list travels, and a duplicate would be
240
+ // written as one more entry rather than refused.
241
+ if (!task.labels.includes(value)) write({ labels: [...task.labels, value] });
242
+ }
243
+ if (kind === "start") write({ startDate: `${value}T00:00:00.000Z` });
244
+ if (kind === "due") write({ dueDate: `${value}T00:00:00.000Z` });
245
+ if (kind === "subtask") {
246
+ void board
247
+ .createTask({
248
+ title: value,
249
+ // A subtask starts in the same column as the task it belongs to: the first column would
250
+ // claim work was reset that never started.
251
+ status: task.status,
252
+ parentTaskId: task.id,
253
+ assigneeId: null,
254
+ labels: [],
255
+ startDate: null,
256
+ dueDate: null,
257
+ dependsOn: null,
258
+ idempotencyKey: crypto.randomUUID(),
259
+ })
260
+ .then(close);
261
+ return;
262
+ }
263
+ close();
264
+ };
265
+
266
+ return (
267
+ <>
268
+ <DropdownMenu>
269
+ {/* ⚠️ No border, square, and the icon at full strength (#724). A bordered pill here read
270
+ as one more chip, so the row looked like it carried a value called "+". Without the
271
+ border it is what it is: a control, quiet until the pointer is on it. */}
272
+ <DropdownMenuTrigger
273
+ aria-label={i18n.t("board.add")}
274
+ className="flex size-6 shrink-0 items-center justify-center rounded-md text-foreground outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
275
+ >
276
+ <Plus aria-hidden="true" className="size-4" />
277
+ </DropdownMenuTrigger>
278
+ <DropdownMenuContent align="start">
279
+ {addable.map((entry) => (
280
+ <DropdownMenuItem key={entry} onSelect={() => setKind(entry)}>
281
+ {i18n.t(`board.add.${entry}`)}
282
+ </DropdownMenuItem>
283
+ ))}
284
+ {linkable.map((entry) => (
285
+ <DropdownMenuItem key={entry} onSelect={() => setKind(entry)}>
286
+ {i18n.t(`board.link.${entry}`)}
287
+ </DropdownMenuItem>
288
+ ))}
289
+ </DropdownMenuContent>
290
+ </DropdownMenu>
291
+
292
+ {kind === null || kind === "blocker" ? null : (
293
+ <input
294
+ // biome-ignore lint/a11y/noAutofocus: it opens on a deliberate click, never on load
295
+ autoFocus
296
+ type={kind === "start" || kind === "due" ? "date" : "text"}
297
+ value={draft}
298
+ aria-label={i18n.t(kind === "subtask" ? "board.link.subtask" : `board.add.${kind}`)}
299
+ onChange={(event) => setDraft(event.target.value)}
300
+ onKeyDown={(event) => {
301
+ if (event.key === "Escape") close();
302
+ if (event.key !== "Enter") return;
303
+ event.preventDefault();
304
+ commit();
305
+ }}
306
+ className="w-36 rounded-full border bg-background px-2.5 py-0.5 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring"
307
+ />
308
+ )}
309
+
310
+ {kind !== "blocker" ? null : (
311
+ <DropdownMenu open onOpenChange={(next) => !next && close()}>
312
+ <DropdownMenuTrigger
313
+ aria-label={i18n.t("board.link.blocker")}
314
+ className="rounded-full border px-2.5 py-0.5 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring"
315
+ >
316
+ {i18n.t("board.link.blocker")}
317
+ </DropdownMenuTrigger>
318
+ <DropdownMenuContent align="start">
319
+ {candidates.map((card) => (
320
+ <DropdownMenuItem
321
+ key={card.id}
322
+ onSelect={() => {
323
+ close();
324
+ write({ dependsOn: card.id });
325
+ }}
326
+ >
327
+ {card.title}
328
+ </DropdownMenuItem>
329
+ ))}
330
+ </DropdownMenuContent>
331
+ </DropdownMenu>
332
+ )}
333
+ </>
334
+ );
335
+ }
@@ -0,0 +1,66 @@
1
+ import type { Node } from "@contract/node.ts";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { crumbsOf } from "@/board/board-crumbs/board-crumbs.ts";
4
+ import { BoardCrumbs } from "@/board/board-crumbs/board-crumbs.tsx";
5
+ import { useBoard } from "@/board/board-data/board-data.ts";
6
+ import { useOpenTask } from "@/board/board-open-task/board-open-task.ts";
7
+ import { useIntelRouterContext } from "@/router/router-context.ts";
8
+ import { TitleRow } from "@/title-row/title-row.tsx";
9
+
10
+ /**
11
+ * The one line above a board, and above a card opened on it.
12
+ *
13
+ * ⚠️ **The SAME `TitleRow`, with the path in place of the name.** A second header component for the
14
+ * open card would look identical today and drift tomorrow: the order of the right-hand group, the
15
+ * shadow on scroll and the slots are decided in exactly one place, and this keeps it that way (#53).
16
+ *
17
+ * ⚠️ **The menu then belongs to the CARD, not to the board.** Rename, move and archive act on what
18
+ * the reader is looking at. That is also where renaming a card happens, since the path carries its
19
+ * name and there is no heading to edit (#692).
20
+ */
21
+ export function BoardTitleRow({ node }: { node: Node }) {
22
+ const { openId, open } = useOpenTask();
23
+ const board = useBoard(node.id);
24
+ const { data } = useIntelRouterContext();
25
+
26
+ // ⚠️ The same key the open card's body already uses, so this costs no second request.
27
+ const task = useQuery({
28
+ queryKey: ["node", openId],
29
+ queryFn: () => data.getNode(openId ?? ""),
30
+ enabled: openId !== null,
31
+ });
32
+
33
+ const tasks = board.columns.flatMap((entry) => entry.cards);
34
+ const openTask = tasks.find((card) => card.id === openId);
35
+
36
+ // ⚠️ Until the card is in the ANSWER, the board's own row stands. Drawing a path around a name
37
+ // nobody knows yet would flicker a wrong one into place for a moment.
38
+ //
39
+ // ⚠️ The card's NODE is a different wait, and it must not gate the path: it is only needed for the
40
+ // menu, and holding the whole row for it would show the board's name first and swap it a moment
41
+ // later — two different headers in sequence for one navigation.
42
+ if (openId === null || openTask === undefined) {
43
+ return <TitleRow title={node.title} target={{ type: "node", node }} />;
44
+ }
45
+
46
+ const crumbs = <BoardCrumbs crumbs={crumbsOf(openId, board.title, tasks)} onOpen={open} />;
47
+
48
+ /**
49
+ * ⚠️ **No menu at all while the card's node is missing**, rather than the board's. Rename, move
50
+ * and archive would otherwise act on the BOARD, and `archive` fires without asking: a control
51
+ * that is not there yet costs a moment, one that acts on the wrong thing costs the thing.
52
+ *
53
+ * ⚠️ **Missing covers two states, and deliberately treats them the same**: the read is still on
54
+ * its way, or it was refused. Neither gives this row a card to act on, and telling them apart
55
+ * here would only decide WHICH wrong menu to draw.
56
+ */
57
+ if (task.data === undefined) return <TitleRow title={openTask.title} crumbs={crumbs} />;
58
+
59
+ return (
60
+ <TitleRow
61
+ title={openTask.title}
62
+ crumbs={crumbs}
63
+ target={{ type: "node", node: task.data.node }}
64
+ />
65
+ );
66
+ }
@@ -0,0 +1,17 @@
1
+ import { BrainCircuit } from "lucide-react";
2
+ import { cn } from "@/lib/utils";
3
+ import { customLogoUrl } from "./custom-logo.ts";
4
+
5
+ export function AppLogo({ className }: { className?: string }) {
6
+ if (customLogoUrl !== null) return <img src={customLogoUrl} alt="" className={className} />;
7
+ return (
8
+ <span
9
+ className={cn(
10
+ "flex aspect-square items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground",
11
+ className,
12
+ )}
13
+ >
14
+ <BrainCircuit aria-hidden="true" className="size-5" />
15
+ </span>
16
+ );
17
+ }
@@ -0,0 +1 @@
1
+ export const customLogoUrl: string | null = null;
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <title>Intel</title>
3
+ <rect width="32" height="32" rx="8" fill="oklch(0.205 0 0)"/>
4
+ <path d="M10 10h12v12H10zM7 14h3m12 0h3M14 7v3m4-3v3M14 22v3m4-3v3" fill="none" stroke="oklch(0.985 0 0)" stroke-width="2" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <title>Intel</title>
3
+ <rect width="32" height="32" rx="8" fill="oklch(0.205 0 0)"/>
4
+ <path d="M10 10h12v12H10zM7 14h3m12 0h3M14 7v3m4-3v3M14 22v3m4-3v3" fill="none" stroke="oklch(0.985 0 0)" stroke-width="2" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,39 @@
1
+ import { Avatar as AvatarPrimitive } from "radix-ui";
2
+ import type * as React from "react";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ function Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
7
+ return (
8
+ <AvatarPrimitive.Root
9
+ data-slot="avatar"
10
+ className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
17
+ return (
18
+ <AvatarPrimitive.Image
19
+ data-slot="avatar-image"
20
+ className={cn("aspect-square size-full", className)}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ function AvatarFallback({
27
+ className,
28
+ ...props
29
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
30
+ return (
31
+ <AvatarPrimitive.Fallback
32
+ data-slot="avatar-fallback"
33
+ className={cn("flex size-full items-center justify-center rounded-full bg-muted", className)}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,64 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { Slot } from "radix-ui";
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ // The registry hard-codes the destructive label colour. `--destructive-foreground` exists
14
+ // and a customer theme has to be able to move it, so the token replaces the fixed colour.
15
+ destructive:
16
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
17
+ outline:
18
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
19
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
20
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
25
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
26
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
27
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28
+ icon: "size-9",
29
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
30
+ "icon-sm": "size-8",
31
+ "icon-lg": "size-10",
32
+ },
33
+ },
34
+ defaultVariants: {
35
+ variant: "default",
36
+ size: "default",
37
+ },
38
+ },
39
+ );
40
+
41
+ function Button({
42
+ className,
43
+ variant = "default",
44
+ size = "default",
45
+ asChild = false,
46
+ ...props
47
+ }: React.ComponentProps<"button"> &
48
+ VariantProps<typeof buttonVariants> & {
49
+ asChild?: boolean;
50
+ }) {
51
+ const Comp = asChild ? Slot.Root : "button";
52
+
53
+ return (
54
+ <Comp
55
+ data-slot="button"
56
+ data-variant={variant}
57
+ data-size={size}
58
+ className={cn(buttonVariants({ variant, size, className }))}
59
+ {...props}
60
+ />
61
+ );
62
+ }
63
+
64
+ export { Button, buttonVariants };
@@ -0,0 +1,20 @@
1
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui";
2
+ import type * as React from "react";
3
+
4
+ function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
5
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
6
+ }
7
+
8
+ function CollapsibleTrigger({
9
+ ...props
10
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
11
+ return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;
12
+ }
13
+
14
+ function CollapsibleContent({
15
+ ...props
16
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
17
+ return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
18
+ }
19
+
20
+ export { Collapsible, CollapsibleContent, CollapsibleTrigger };