kennel 0.2.3 → 0.2.5

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 (237) hide show
  1. package/README.md +23 -13
  2. package/dist/cli/dispatch.js +16 -3
  3. package/dist/cli/src/cli/dispatch.js +16 -3
  4. package/dist/cli/src/cli/usage.js +1 -1
  5. package/dist/cli/src/core/command.js +1 -0
  6. package/dist/cli/src/core/init.js +5 -5
  7. package/dist/cli/src/plugins/external.d.ts +28 -2
  8. package/dist/cli/src/plugins/external.js +75 -21
  9. package/dist/cli/src/site/server-plugins.d.ts +2 -7
  10. package/dist/cli/src/site/viewer.js +1 -0
  11. package/dist/cli/usage.js +1 -1
  12. package/dist/core/command.js +1 -0
  13. package/dist/core/init.js +5 -5
  14. package/dist/core/runtime.js +357 -3207
  15. package/dist/package/preview.js +1232 -5113
  16. package/dist/plugins/external.d.ts +28 -2
  17. package/dist/plugins/external.js +75 -21
  18. package/dist/site/server-plugins.d.ts +2 -7
  19. package/dist/site/server-plugins.js +552 -10447
  20. package/dist/site/viewer.js +1 -0
  21. package/kernel/apps/web/package.json +1 -5
  22. package/kernel/apps/web/scripts/dev.mjs +53 -0
  23. package/kernel/apps/web/src/app.css +0 -3
  24. package/kernel/apps/web/src/main.tsx +0 -1
  25. package/kernel/apps/web/src/plugins.ts +0 -30
  26. package/kernel/apps/web/tsconfig.json +1 -2
  27. package/kernel/apps/web/vite.config.ts +31 -13
  28. package/kernel/packages/api/package.json +0 -4
  29. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +13 -38
  30. package/kernel/packages/api/src/server/index.ts +0 -1
  31. package/kernel/packages/api/src/server/plugins.ts +49 -16
  32. package/kernel/packages/api/test/core.test.ts +13 -25
  33. package/kernel/packages/markdown/README.md +1 -1
  34. package/package.json +1 -1
  35. package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
  36. package/web/assets/app-CX20nNIa.js +2 -0
  37. package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
  38. package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
  39. package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
  40. package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
  41. package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
  42. package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
  43. package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
  44. package/web/assets/channel-X7WHyxxT.js +1 -0
  45. package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
  46. package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
  47. package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
  48. package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
  49. package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
  50. package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
  51. package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
  52. package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
  53. package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
  54. package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
  55. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
  56. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
  57. package/web/assets/client-7OWw5kW2.js +6 -0
  58. package/web/assets/client-DYj_td4Q.js +17 -0
  59. package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
  60. package/web/assets/command-zkzH4hVz.js +38 -0
  61. package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
  62. package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
  63. package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
  64. package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
  65. package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
  66. package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
  67. package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
  68. package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
  69. package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
  70. package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
  71. package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
  72. package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
  73. package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
  74. package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
  75. package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
  76. package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
  77. package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
  78. package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
  79. package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
  80. package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
  81. package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
  82. package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
  83. package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
  84. package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
  85. package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
  86. package/web/assets/index-CegvVQ1C.js +1 -0
  87. package/web/assets/index-Dwhf8F_U.css +1 -0
  88. package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
  89. package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
  90. package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
  91. package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
  92. package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
  93. package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
  94. package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
  95. package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
  96. package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
  97. package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
  98. package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
  99. package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
  100. package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
  101. package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
  102. package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
  103. package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
  104. package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
  105. package/web/assets/select-CtXC_PLC.js +30 -0
  106. package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
  107. package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
  108. package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
  109. package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
  110. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
  111. package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
  112. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
  113. package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
  114. package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
  115. package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
  116. package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
  117. package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
  118. package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
  119. package/web/index.html +2 -2
  120. package/kernel/packages/api/test/partial-loading.test.ts +0 -43
  121. package/kernel/plugins/calendar/SKILL.md +0 -173
  122. package/kernel/plugins/calendar/eslint.config.js +0 -10
  123. package/kernel/plugins/calendar/package.json +0 -73
  124. package/kernel/plugins/calendar/schema.json +0 -137
  125. package/kernel/plugins/calendar/src/calendar.tsx +0 -328
  126. package/kernel/plugins/calendar/src/client.tsx +0 -139
  127. package/kernel/plugins/calendar/src/context.ts +0 -48
  128. package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
  129. package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
  130. package/kernel/plugins/calendar/src/index.ts +0 -4
  131. package/kernel/plugins/calendar/src/manifest.ts +0 -7
  132. package/kernel/plugins/calendar/src/parse.ts +0 -610
  133. package/kernel/plugins/calendar/src/server.ts +0 -160
  134. package/kernel/plugins/calendar/src/utils.ts +0 -80
  135. package/kernel/plugins/calendar/src/views.tsx +0 -418
  136. package/kernel/plugins/calendar/test/context.test.mjs +0 -60
  137. package/kernel/plugins/calendar/test/package.test.mjs +0 -43
  138. package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
  139. package/kernel/plugins/calendar/test/server.test.mjs +0 -126
  140. package/kernel/plugins/calendar/tsconfig.json +0 -17
  141. package/kernel/plugins/mail/SKILL.md +0 -97
  142. package/kernel/plugins/mail/eslint.config.js +0 -10
  143. package/kernel/plugins/mail/package.json +0 -79
  144. package/kernel/plugins/mail/src/api.ts +0 -359
  145. package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
  146. package/kernel/plugins/mail/src/client.tsx +0 -14
  147. package/kernel/plugins/mail/src/flock.ts +0 -137
  148. package/kernel/plugins/mail/src/folders.ts +0 -37
  149. package/kernel/plugins/mail/src/history.ts +0 -485
  150. package/kernel/plugins/mail/src/index.ts +0 -3
  151. package/kernel/plugins/mail/src/mail-card.tsx +0 -123
  152. package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
  153. package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
  154. package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
  155. package/kernel/plugins/mail/src/mail-page.tsx +0 -109
  156. package/kernel/plugins/mail/src/mail-request.ts +0 -78
  157. package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
  158. package/kernel/plugins/mail/src/mailos.ts +0 -723
  159. package/kernel/plugins/mail/src/manifest.ts +0 -7
  160. package/kernel/plugins/mail/src/node.ts +0 -171
  161. package/kernel/plugins/mail/src/parse.ts +0 -139
  162. package/kernel/plugins/mail/src/server.ts +0 -125
  163. package/kernel/plugins/mail/src/types.ts +0 -10
  164. package/kernel/plugins/mail/src/viewer.tsx +0 -147
  165. package/kernel/plugins/mail/test/flock.test.mjs +0 -156
  166. package/kernel/plugins/mail/test/folders.test.mjs +0 -138
  167. package/kernel/plugins/mail/test/history.test.mjs +0 -460
  168. package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
  169. package/kernel/plugins/mail/test/node.test.mjs +0 -207
  170. package/kernel/plugins/mail/test/package.test.mjs +0 -54
  171. package/kernel/plugins/mail/test/parse.test.mjs +0 -32
  172. package/kernel/plugins/mail/test/server.test.mjs +0 -116
  173. package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
  174. package/kernel/plugins/mail/tsconfig.json +0 -17
  175. package/kernel/plugins/projects/SKILL.md +0 -73
  176. package/kernel/plugins/projects/eslint.config.js +0 -10
  177. package/kernel/plugins/projects/package.json +0 -70
  178. package/kernel/plugins/projects/src/client.tsx +0 -17
  179. package/kernel/plugins/projects/src/data/model.ts +0 -129
  180. package/kernel/plugins/projects/src/index.ts +0 -17
  181. package/kernel/plugins/projects/src/manifest.ts +0 -7
  182. package/kernel/plugins/projects/src/project-context.ts +0 -41
  183. package/kernel/plugins/projects/src/server.ts +0 -276
  184. package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
  185. package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
  186. package/kernel/plugins/projects/test/package.test.mjs +0 -51
  187. package/kernel/plugins/projects/tsconfig.json +0 -17
  188. package/kernel/plugins/tasks/SKILL.md +0 -341
  189. package/kernel/plugins/tasks/eslint.config.js +0 -10
  190. package/kernel/plugins/tasks/package.json +0 -74
  191. package/kernel/plugins/tasks/schema.json +0 -939
  192. package/kernel/plugins/tasks/src/api.ts +0 -212
  193. package/kernel/plugins/tasks/src/client.tsx +0 -23
  194. package/kernel/plugins/tasks/src/data/parse.ts +0 -544
  195. package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
  196. package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
  197. package/kernel/plugins/tasks/src/index.ts +0 -34
  198. package/kernel/plugins/tasks/src/manifest.ts +0 -7
  199. package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
  200. package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
  201. package/kernel/plugins/tasks/src/server.ts +0 -447
  202. package/kernel/plugins/tasks/src/styles.css +0 -9
  203. package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
  204. package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
  205. package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
  206. package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
  207. package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
  208. package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
  209. package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
  210. package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
  211. package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
  212. package/kernel/plugins/tasks/test/api.test.mjs +0 -99
  213. package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
  214. package/kernel/plugins/tasks/test/package.test.mjs +0 -54
  215. package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
  216. package/kernel/plugins/tasks/test/server.test.mjs +0 -386
  217. package/kernel/plugins/tasks/tsconfig.json +0 -17
  218. package/web/assets/app-DoGr2Zi7.js +0 -2
  219. package/web/assets/channel-uMEjV8qc.js +0 -1
  220. package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
  221. package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
  222. package/web/assets/client-C6Nxdfif.js +0 -2
  223. package/web/assets/client-CXYfBko5.js +0 -17
  224. package/web/assets/client-CoKtOq-X.js +0 -7
  225. package/web/assets/client-DyRQz5RU.js +0 -53
  226. package/web/assets/client-OY6HUzuD.js +0 -98
  227. package/web/assets/client-YOYWZIFi.js +0 -34
  228. package/web/assets/command-BugZBGXE.js +0 -38
  229. package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
  230. package/web/assets/index-BaRnbYRw.js +0 -1
  231. package/web/assets/index-BexmLxIP.css +0 -1
  232. package/web/assets/index-DDirZMgx.js +0 -1
  233. package/web/assets/note-card-DBfX6gW1.js +0 -6
  234. package/web/assets/select-D8cqNgcp.js +0 -30
  235. package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
  236. package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
  237. package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
@@ -1,494 +0,0 @@
1
- import {
2
- CircleDot,
3
- CirclePause,
4
- ExternalLink,
5
- FileText,
6
- GitBranch,
7
- GitCommitHorizontal,
8
- GitPullRequest,
9
- Link2,
10
- MessageSquare,
11
- } from "lucide-react"
12
-
13
- import { Badge } from "@raggle-ai/ui/components/badge"
14
- import { Button } from "@raggle-ai/ui/components/button"
15
- import { CopyContextButton } from "@raggle-ai/ui/components/copy-context"
16
- import {
17
- NoteCard,
18
- noteReferenceContext,
19
- } from "@raggle-ai/ui/components/note-card"
20
-
21
- import type {
22
- TodoItemData,
23
- TodoItemLink,
24
- TodoItemRelation,
25
- TodoItemStatus,
26
- TodoItemStep,
27
- } from "../data/todo-model.js"
28
-
29
- export type TodoItemProps = {
30
- item: TodoItemData
31
- canToggle?: boolean
32
- onToggle: (id: string) => void
33
- /**
34
- * Host document-route builder for local note references; when absent
35
- * (or for hosts that do not serve documents) note chips render as labels.
36
- */
37
- documentHref?: (folder: string, relativePath: string) => string | undefined
38
- }
39
-
40
- const priorityTone: Record<TodoItemData["priority"], string> = {
41
- 1: "border-red-300 bg-red-100 text-red-800",
42
- 2: "border-orange-300 bg-orange-100 text-orange-800",
43
- 3: "border-amber-300 bg-amber-100 text-amber-800",
44
- 4: "border-sky-300 bg-sky-100 text-sky-800",
45
- 5: "border-slate-300 bg-slate-100 text-slate-700",
46
- }
47
-
48
- const statusLabel: Record<TodoItemStatus, string> = {
49
- open: "Open",
50
- blocked: "Blocked",
51
- done: "Done",
52
- }
53
-
54
- const statusTone: Record<TodoItemStatus, string> = {
55
- open: "border-blue-200 bg-blue-50 text-blue-700",
56
- blocked: "border-violet-200 bg-violet-50 text-violet-700",
57
- done: "border-emerald-200 bg-emerald-50 text-emerald-700",
58
- }
59
-
60
- export function TodoItem({
61
- item,
62
- canToggle = true,
63
- onToggle,
64
- documentHref,
65
- }: TodoItemProps) {
66
- const isDone = item.status === "done"
67
- const isBlocked = item.status === "blocked"
68
- const isOverdue = Boolean(item.overdue && !isDone)
69
- // A description that is only check lines becomes a checklist instead of
70
- // prose; with explicit steps it stays intro text beside the Steps section.
71
- const descSteps =
72
- item.description && !item.steps?.length
73
- ? parseDescription(item.description)
74
- : null
75
- const plainDescription =
76
- item.description && descSteps?.kind !== "steps"
77
- ? item.description
78
- : undefined
79
- const dueDate = item.dueDate ? new Date(`${item.dueDate}T12:00:00`) : null
80
- const dueText =
81
- dueDate && !Number.isNaN(dueDate.valueOf())
82
- ? new Intl.DateTimeFormat(undefined, {
83
- day: "numeric",
84
- month: "short",
85
- }).format(dueDate)
86
- : item.dueDate
87
- const contextText = todoItemContext(item, dueText)
88
-
89
- return (
90
- <article
91
- className="min-w-0 overflow-hidden rounded-lg border border-slate-200
92
- bg-white shadow-sm"
93
- >
94
- <header
95
- className={[
96
- "border-b px-4 py-4 sm:px-5",
97
- isOverdue
98
- ? "border-red-200 bg-red-50/80"
99
- : isBlocked
100
- ? "border-violet-200 bg-violet-50/60"
101
- : "border-slate-100 bg-slate-50/70",
102
- ].join(" ")}
103
- >
104
- <div
105
- className="flex min-w-0 flex-wrap items-start justify-between gap-3
106
- sm:flex-nowrap"
107
- >
108
- <div className="min-w-0">
109
- {item.key ? (
110
- <p className="m-0 pb-1 font-mono text-xs text-slate-500">
111
- {item.key}
112
- </p>
113
- ) : null}
114
- <h2
115
- className={[
116
- "m-0 text-lg leading-tight font-semibold text-slate-950",
117
- isDone ? "text-slate-400 line-through" : "",
118
- ].join(" ")}
119
- >
120
- {item.title}
121
- </h2>
122
- {plainDescription ? (
123
- <p
124
- className="mt-1.5 mb-0 max-w-3xl text-sm leading-6
125
- whitespace-pre-wrap text-slate-600"
126
- >
127
- {plainDescription}
128
- </p>
129
- ) : null}
130
- </div>
131
- <div className="flex shrink-0 items-center gap-1.5">
132
- <Button
133
- type="button"
134
- className={
135
- isDone
136
- ? ""
137
- : `border-emerald-500 bg-emerald-500 text-white
138
- hover:bg-emerald-600`
139
- }
140
- size="sm"
141
- variant={isDone ? "outline" : "default"}
142
- disabled={!canToggle}
143
- onClick={() => onToggle(item.id)}
144
- >
145
- {isBlocked ? <CirclePause className="h-3.5 w-3.5" /> : "✓"}
146
- {isDone ? "Reopen" : isBlocked ? "Complete anyway" : "Complete"}
147
- </Button>
148
- <CopyContextButton
149
- className="text-slate-500 hover:text-slate-900"
150
- context={contextText}
151
- label={`Copy full context for ${item.title}`}
152
- />
153
- </div>
154
- </div>
155
- <div className="mt-3 flex flex-wrap items-center gap-1.5 text-xs">
156
- {item.type ? (
157
- <Badge variant="secondary" className="font-normal capitalize">
158
- {item.type}
159
- </Badge>
160
- ) : null}
161
- <Badge variant="outline" className={priorityTone[item.priority]}>
162
- P{item.priority}
163
- </Badge>
164
- {dueText ? (
165
- <Badge
166
- variant="outline"
167
- className={[
168
- isOverdue
169
- ? "border-red-300 bg-red-100 font-medium text-red-800"
170
- : "border-amber-200 bg-amber-50 text-amber-700",
171
- ].join(" ")}
172
- >
173
- Due {dueText}
174
- </Badge>
175
- ) : null}
176
- {item.source ? (
177
- <span className="text-slate-400" title={`${item.source}/todo.json`}>
178
- {item.source.split("/").pop()}
179
- </span>
180
- ) : null}
181
- {item.tags.map((tag) => (
182
- <Badge
183
- key={tag}
184
- variant="outline"
185
- className="border-indigo-100 bg-indigo-50 text-indigo-700"
186
- >
187
- #{tag}
188
- </Badge>
189
- ))}
190
- </div>
191
- </header>
192
- {item.links?.length ? (
193
- <div
194
- className="flex flex-wrap items-center gap-1.5 border-b
195
- border-slate-100 px-4 py-2.5 sm:px-5"
196
- >
197
- {item.links.map((link) => (
198
- <a
199
- key={link.id}
200
- className="inline-flex items-center gap-1.5 rounded-md border
201
- border-slate-200 bg-white px-2 py-1 text-xs text-slate-600
202
- no-underline transition-colors hover:bg-slate-50
203
- hover:text-slate-900"
204
- href={link.url}
205
- rel="noreferrer"
206
- target="_blank"
207
- title={linkLabel(link)}
208
- >
209
- <TodoLinkIcon link={link} />
210
- {link.title ?? link.repository ?? linkLabel(link)}
211
- </a>
212
- ))}
213
- </div>
214
- ) : null}
215
- <div className="grid grid-cols-1 divide-y divide-slate-100">
216
- {item.acceptanceCriteria?.length ? (
217
- <TodoChecklist
218
- label="Acceptance criteria"
219
- steps={item.acceptanceCriteria}
220
- />
221
- ) : null}
222
- {item.steps?.length ? (
223
- <TodoChecklist label="Steps" steps={item.steps} />
224
- ) : null}
225
- {descSteps?.kind === "steps" ? (
226
- <TodoChecklist
227
- label={descSteps.intro ?? "Steps"}
228
- steps={descSteps.steps}
229
- />
230
- ) : null}
231
- {item.relations?.length ? (
232
- <TodoRelations relations={item.relations} />
233
- ) : null}
234
- {item.notes?.length ? (
235
- <TodoNotes
236
- notes={item.notes}
237
- source={item.source}
238
- documentHref={documentHref}
239
- />
240
- ) : null}
241
- </div>
242
- </article>
243
- )
244
- }
245
-
246
- type DescriptionView =
247
- | { readonly kind: "plain"; readonly text: string }
248
- | {
249
- readonly kind: "steps"
250
- readonly intro?: string
251
- readonly steps: ReadonlyArray<TodoItemStep>
252
- }
253
-
254
- const stepPattern = /^[-*]\s+\[(x|X| )\]\s+(.+)$/
255
-
256
- const parseDescription = (description: string): DescriptionView => {
257
- const lines = description
258
- .split(/\r?\n/)
259
- .map((line) => line.trim())
260
- .filter(Boolean)
261
- const steps = lines
262
- .map((line, index) => {
263
- const match = stepPattern.exec(line)
264
- const marker = match?.[1]
265
- const text = match?.[2]
266
- return marker && text
267
- ? {
268
- id: `description-step-${index}`,
269
- done: marker.toLowerCase() === "x",
270
- text,
271
- }
272
- : null
273
- })
274
- .filter((step): step is TodoItemStep => step != null)
275
-
276
- if (steps.length === 0) {
277
- return { kind: "plain", text: description }
278
- }
279
-
280
- const intro = lines.find((line) => !stepPattern.test(line)) ?? "Steps"
281
- return { kind: "steps", intro: intro.replace(/:$/, ""), steps }
282
- }
283
-
284
- function TodoChecklist({
285
- label,
286
- steps,
287
- }: {
288
- label: string
289
- steps: ReadonlyArray<TodoItemStep>
290
- }) {
291
- const doneCount = steps.filter((step) => step.done).length
292
- return (
293
- <section className="px-4 py-3 sm:px-5">
294
- <div className="mb-2 flex items-baseline gap-2">
295
- <span
296
- className="text-xs font-semibold tracking-wide text-slate-500
297
- uppercase"
298
- >
299
- {label}
300
- </span>
301
- <span className="text-xs text-slate-400">
302
- {doneCount} of {steps.length}
303
- </span>
304
- </div>
305
- <ul className="m-0 list-none grid-cols-1 space-y-1.5 pl-0">
306
- {steps.map((step) => (
307
- <li
308
- key={step.id}
309
- className="grid grid-cols-[1.25rem_minmax(0,1fr)] items-start gap-2
310
- text-sm text-slate-600"
311
- >
312
- <span
313
- className={[
314
- "mt-0.5 grid h-4 w-4 place-items-center rounded-full border",
315
- step.done
316
- ? "border-emerald-500 bg-emerald-500 text-[10px] text-white"
317
- : "border-slate-300 bg-white text-transparent",
318
- ].join(" ")}
319
- aria-hidden="true"
320
- >
321
- ✓
322
- </span>
323
- <span className={step.done ? "text-slate-400 line-through" : ""}>
324
- {step.text}
325
- </span>
326
- </li>
327
- ))}
328
- </ul>
329
- </section>
330
- )
331
- }
332
-
333
- function TodoRelations({
334
- relations,
335
- }: {
336
- relations: ReadonlyArray<TodoItemRelation>
337
- }) {
338
- return (
339
- <section className="px-4 py-3 sm:px-5">
340
- <span
341
- className="text-xs font-semibold tracking-wide text-slate-500 uppercase"
342
- >
343
- Related tasks
344
- </span>
345
- <div className="mt-2 flex flex-wrap gap-1.5">
346
- {relations.map((relation, index) => (
347
- <Badge
348
- key={`${relation.type}-${relation.targetTaskId}-${index}`}
349
- className="max-w-full gap-1 font-normal outline-none"
350
- variant="outline"
351
- >
352
- {relation.type}
353
- <span className="font-mono text-[11px] text-slate-700">
354
- {relation.targetTaskId}
355
- </span>
356
- {relation.description ? (
357
- <span className="truncate text-slate-400">
358
- — {relation.description}
359
- </span>
360
- ) : null}
361
- </Badge>
362
- ))}
363
- </div>
364
- </section>
365
- )
366
- }
367
-
368
- function TodoNotes({
369
- notes,
370
- source,
371
- documentHref,
372
- }: {
373
- notes: ReadonlyArray<string>
374
- source: string
375
- documentHref?: (folder: string, relativePath: string) => string | undefined
376
- }) {
377
- return (
378
- <section className="px-4 py-3 sm:px-5">
379
- <span
380
- className="text-xs font-semibold tracking-wide text-slate-500 uppercase"
381
- >
382
- Notes
383
- </span>
384
- <div className="mt-2 grid gap-1.5">
385
- {notes.map((note) => {
386
- const isUrl = /^https?:\/\//i.test(note)
387
- const reference = isUrl ? note : (note.split("#", 1)[0] ?? "").trim()
388
- const href = isUrl
389
- ? note
390
- : reference && documentHref
391
- ? documentHref(source, reference)
392
- : undefined
393
- return (
394
- <NoteCard
395
- key={note}
396
- context={() => noteReferenceContext(note, source)}
397
- href={href}
398
- path={isUrl ? undefined : note}
399
- title={isUrl ? note.replace(/^https?:\/\//, "") : note}
400
- />
401
- )
402
- })}
403
- </div>
404
- </section>
405
- )
406
- }
407
-
408
- function TodoLinkIcon({ link }: { link: TodoItemLink }) {
409
- if (link.provider === "github" && link.kind === "pull_request")
410
- return <GitPullRequest className="h-3.5 w-3.5" />
411
- if (link.provider === "github" && link.kind === "commit")
412
- return <GitCommitHorizontal className="h-3.5 w-3.5" />
413
- if (link.provider === "github" && link.kind === "issue")
414
- return <CircleDot className="h-3.5 w-3.5" />
415
- if (link.provider === "github") return <GitBranch className="h-3.5 w-3.5" />
416
- if (link.kind === "document" || link.kind === "design")
417
- return <FileText className="h-3.5 w-3.5" />
418
- if (link.kind === "discussion")
419
- return <MessageSquare className="h-3.5 w-3.5" />
420
- if (link.provider === "web") return <ExternalLink className="h-3.5 w-3.5" />
421
- return <Link2 className="h-3.5 w-3.5" />
422
- }
423
-
424
- const linkLabel = (link: TodoItemLink) => {
425
- const provider = link.provider === "web" ? "external" : link.provider
426
- const title = link.title ?? link.repository ?? link.url
427
- return `${provider} ${link.kind.replace(/_/g, " ")}: ${title}`
428
- }
429
-
430
- export const todoItemContext = (item: TodoItemData, dueText?: string) =>
431
- [
432
- "You are helping complete a todo item. Use the context below to identify the next concrete action, gather or update any required evidence, and produce a concise fix plan.",
433
- "",
434
- "Instructions for the LLM:",
435
- "- Treat the todo title as the objective.",
436
- "- Use the description and steps as the authoritative task context.",
437
- "- Do not mark the task complete unless every unfinished step is resolved.",
438
- "- If information is missing, state the exact missing input or file needed.",
439
- "- Return: 1) current status, 2) next action, 3) commands/files to inspect or update, 4) completion criteria.",
440
- "",
441
- "Todo context:",
442
- item.key ? `- Key: ${item.key}` : null,
443
- `- Title: ${item.title}`,
444
- item.type ? `- Type: ${item.type}` : null,
445
- item.description ? `- Description: ${item.description}` : null,
446
- `- Status: ${statusLabel[item.status]}`,
447
- `- Priority: P${item.priority}`,
448
- dueText ? `- Due: ${dueText}` : null,
449
- `- Source JSON: ${item.source}`,
450
- item.acceptanceCriteria?.length ? "" : null,
451
- item.acceptanceCriteria?.length ? "Acceptance criteria:" : null,
452
- item.acceptanceCriteria?.length
453
- ? item.acceptanceCriteria
454
- .map(
455
- (criterion) =>
456
- `- [${criterion.done ? "x" : " "}] ${criterion.text}`,
457
- )
458
- .join("\n")
459
- : null,
460
- item.steps?.length ? "" : null,
461
- item.steps?.length ? "Steps:" : null,
462
- item.steps?.length
463
- ? item.steps
464
- .map((step) => `- [${step.done ? "x" : " "}] ${step.text}`)
465
- .join("\n")
466
- : null,
467
- item.relations?.length ? "" : null,
468
- item.relations?.length ? "Task relations:" : null,
469
- item.relations?.length
470
- ? item.relations
471
- .map(
472
- (relation) =>
473
- `- ${relation.type}: ${relation.targetTaskId}${relation.description ? ` - ${relation.description}` : ""}`,
474
- )
475
- .join("\n")
476
- : null,
477
- item.links?.length ? "" : null,
478
- item.links?.length ? "External links:" : null,
479
- item.links?.length
480
- ? item.links
481
- .map(
482
- (link) =>
483
- `- ${link.provider}/${link.kind}/${link.relation}: ${link.title ?? link.repository ?? link.id} (${link.url})`,
484
- )
485
- .join("\n")
486
- : null,
487
- item.notes?.length ? "" : null,
488
- item.notes?.length ? "Accompanying notes and file references:" : null,
489
- item.notes?.length
490
- ? item.notes.map((note) => `- ${note}`).join("\n")
491
- : null,
492
- ]
493
- .filter((line): line is string => line != null)
494
- .join("\n")
@@ -1,60 +0,0 @@
1
- import { useMemo } from "react"
2
-
3
- import { FilterSelect } from "@raggle-ai/ui/components/filter-select"
4
-
5
- import type { TodoDataset, TodoFilter } from "../data/todo-model.js"
6
- import { todoItems } from "../data/todo-model.js"
7
-
8
- const options: ReadonlyArray<{
9
- readonly label: string
10
- readonly value: TodoFilter
11
- }> = [
12
- { value: "all", label: "All" },
13
- { value: "open", label: "Open" },
14
- { value: "done", label: "Done" },
15
- { value: "blocked", label: "Blocked" },
16
- { value: "overdue", label: "Late" },
17
- ]
18
-
19
- export type TodoStatusFilterProps = {
20
- readonly dataset: TodoDataset | null
21
- readonly value: TodoFilter
22
- readonly onChange: (value: TodoFilter) => void
23
- readonly className?: string
24
- }
25
-
26
- /** Status filter dropdown: all/open/done/blocked/late with task counts.
27
- * The trigger shows the active value, so no label sits beside it. */
28
- export function TodoStatusFilter({
29
- dataset,
30
- value,
31
- onChange,
32
- className = "",
33
- }: TodoStatusFilterProps) {
34
- const items = useMemo(() => todoItems(dataset), [dataset])
35
- const counts = useMemo(
36
- () => ({
37
- all: items.length,
38
- open: items.filter((item) => item.status === "open").length,
39
- done: items.filter((item) => item.status === "done").length,
40
- blocked: items.filter((item) => item.status === "blocked").length,
41
- overdue: items.filter((item) => item.overdue && item.status !== "done")
42
- .length,
43
- }),
44
- [items],
45
- )
46
-
47
- return (
48
- <FilterSelect
49
- ariaLabel="Filter tasks by status"
50
- className={className}
51
- onChange={(next) => onChange(next as TodoFilter)}
52
- options={options.map((option) => ({
53
- ...option,
54
- count: counts[option.value],
55
- }))}
56
- triggerClassName="w-28"
57
- value={value}
58
- />
59
- )
60
- }
@@ -1,48 +0,0 @@
1
- import { useMemo } from "react"
2
-
3
- import { FilterChipPanel } from "@raggle-ai/ui/components/filter-chip-panel"
4
-
5
- import type { TodoDataset, TodoFilterSelection } from "../data/todo-model.js"
6
- import { todoItems } from "../data/todo-model.js"
7
-
8
- export type TodoTagFilterProps = {
9
- readonly dataset: TodoDataset | null
10
- /** Selected tags; empty shows tasks with any tags. */
11
- readonly value: TodoFilterSelection
12
- readonly onChange: (value: TodoFilterSelection) => void
13
- readonly className?: string
14
- }
15
-
16
- /** Tag filter: expand/collapse chip panel, one chip per tag, with counts. */
17
- export function TodoTagFilter({
18
- dataset,
19
- value: selectedTags,
20
- onChange,
21
- className = "",
22
- }: TodoTagFilterProps) {
23
- const tags = useMemo(() => {
24
- const items = todoItems(dataset)
25
- return [...new Set(items.flatMap((item) => item.tags).filter(Boolean))]
26
- .sort((a, b) => a.localeCompare(b))
27
- .map((tag) => ({
28
- tag,
29
- count: items.filter((item) => item.tags.includes(tag)).length,
30
- }))
31
- }, [dataset])
32
- if (!tags.length) return null
33
-
34
- return (
35
- <FilterChipPanel
36
- ariaLabel="Filter tasks by tag"
37
- className={className}
38
- label="Tag"
39
- onChange={onChange}
40
- options={tags.map((entry) => ({
41
- value: entry.tag,
42
- label: entry.tag,
43
- count: entry.count,
44
- }))}
45
- selected={selectedTags}
46
- />
47
- )
48
- }