kennel 0.2.3 → 0.2.6

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 (274) 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 +77 -23
  9. package/dist/cli/src/site/server-plugins.d.ts +4 -9
  10. package/dist/cli/src/site/viewer.js +1 -0
  11. package/dist/cli/usage.js +1 -1
  12. package/dist/core/api-client.js +576 -10317
  13. package/dist/core/command.js +1 -0
  14. package/dist/core/core-folders.js +2313 -12088
  15. package/dist/core/init.js +5 -5
  16. package/dist/core/runtime.js +8237 -20713
  17. package/dist/package/preview.js +19617 -33352
  18. package/dist/plugins/external.d.ts +28 -2
  19. package/dist/plugins/external.js +77 -23
  20. package/dist/site/server-plugins.d.ts +4 -9
  21. package/dist/site/server-plugins.js +557 -10452
  22. package/dist/site/viewer.js +1 -0
  23. package/kernel/apps/web/package.json +2 -6
  24. package/kernel/apps/web/scripts/dev.mjs +53 -0
  25. package/kernel/apps/web/src/app.css +0 -3
  26. package/kernel/apps/web/src/external-plugins.ts +1 -1
  27. package/kernel/apps/web/src/folder-filter-icons.tsx +1 -1
  28. package/kernel/apps/web/src/folder-plugin-view.tsx +1 -1
  29. package/kernel/apps/web/src/main.tsx +0 -1
  30. package/kernel/apps/web/src/plugins.ts +1 -31
  31. package/kernel/apps/web/tsconfig.json +1 -2
  32. package/kernel/apps/web/vite.config.ts +32 -14
  33. package/kernel/packages/agents/package.json +1 -1
  34. package/kernel/packages/agents/src/agent-card.tsx +1 -1
  35. package/kernel/packages/agents/src/client.tsx +1 -1
  36. package/kernel/packages/agents/src/manifest.ts +1 -1
  37. package/kernel/packages/agents/src/server.ts +1 -1
  38. package/kernel/packages/agents/src/viewer.tsx +1 -1
  39. package/kernel/packages/api/package.json +1 -5
  40. package/kernel/packages/api/src/server/acp-live-plugin.ts +1 -1
  41. package/kernel/packages/api/src/server/api/create-agent-file.ts +2 -2
  42. package/kernel/packages/api/src/server/api/document-edit.ts +2 -2
  43. package/kernel/packages/api/src/server/api/open-path.ts +2 -2
  44. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +17 -42
  45. package/kernel/packages/api/src/server/index.ts +0 -1
  46. package/kernel/packages/api/src/server/plugins.ts +52 -19
  47. package/kernel/packages/api/src/shared/viewer-route.ts +1 -1
  48. package/kernel/packages/api/test/core.test.ts +14 -26
  49. package/kernel/packages/markdown/README.md +1 -1
  50. package/kernel/packages/notes/package.json +1 -1
  51. package/kernel/packages/notes/src/client.tsx +1 -1
  52. package/kernel/packages/notes/src/manifest.ts +1 -1
  53. package/kernel/packages/notes/src/server.ts +1 -1
  54. package/kernel/packages/plugins/SKILL.md +39 -0
  55. package/kernel/{plugins/mail → packages/plugins}/package.json +53 -28
  56. package/kernel/packages/plugins/src/index.ts +52 -0
  57. package/kernel/packages/plugins/src/page.ts +17 -0
  58. package/kernel/{plugins/projects → packages/plugins}/tsconfig.json +5 -1
  59. package/kernel/packages/plugins/tsup.config.ts +25 -0
  60. package/kernel/packages/ui/src/components/content-issues.tsx +1 -1
  61. package/kernel/packages/ui/tsconfig.json +1 -1
  62. package/package.json +2 -3
  63. package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
  64. package/web/assets/app-CX20nNIa.js +2 -0
  65. package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
  66. package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
  67. package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
  68. package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
  69. package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
  70. package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
  71. package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
  72. package/web/assets/channel-X7WHyxxT.js +1 -0
  73. package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
  74. package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
  75. package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
  76. package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
  77. package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
  78. package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
  79. package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
  80. package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
  81. package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
  82. package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
  83. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
  84. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
  85. package/web/assets/client-7OWw5kW2.js +6 -0
  86. package/web/assets/client-DYj_td4Q.js +17 -0
  87. package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
  88. package/web/assets/command-zkzH4hVz.js +38 -0
  89. package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
  90. package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
  91. package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
  92. package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
  93. package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
  94. package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
  95. package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
  96. package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
  97. package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
  98. package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
  99. package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
  100. package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
  101. package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
  102. package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
  103. package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
  104. package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
  105. package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
  106. package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
  107. package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
  108. package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
  109. package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
  110. package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
  111. package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
  112. package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
  113. package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
  114. package/web/assets/index-CegvVQ1C.js +1 -0
  115. package/web/assets/index-Dwhf8F_U.css +1 -0
  116. package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
  117. package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
  118. package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
  119. package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
  120. package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
  121. package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
  122. package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
  123. package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
  124. package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
  125. package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
  126. package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
  127. package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
  128. package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
  129. package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
  130. package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
  131. package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
  132. package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
  133. package/web/assets/select-CtXC_PLC.js +30 -0
  134. package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
  135. package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
  136. package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
  137. package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
  138. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
  139. package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
  140. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
  141. package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
  142. package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
  143. package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
  144. package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
  145. package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
  146. package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
  147. package/web/index.html +2 -2
  148. package/kernel/packages/api/test/partial-loading.test.ts +0 -43
  149. package/kernel/packages/folder-plugin/package.json +0 -50
  150. package/kernel/packages/folder-plugin/src/index.ts +0 -15
  151. package/kernel/packages/folder-plugin/tsconfig.json +0 -17
  152. package/kernel/plugins/calendar/SKILL.md +0 -173
  153. package/kernel/plugins/calendar/eslint.config.js +0 -10
  154. package/kernel/plugins/calendar/package.json +0 -73
  155. package/kernel/plugins/calendar/schema.json +0 -137
  156. package/kernel/plugins/calendar/src/calendar.tsx +0 -328
  157. package/kernel/plugins/calendar/src/client.tsx +0 -139
  158. package/kernel/plugins/calendar/src/context.ts +0 -48
  159. package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
  160. package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
  161. package/kernel/plugins/calendar/src/index.ts +0 -4
  162. package/kernel/plugins/calendar/src/manifest.ts +0 -7
  163. package/kernel/plugins/calendar/src/parse.ts +0 -610
  164. package/kernel/plugins/calendar/src/server.ts +0 -160
  165. package/kernel/plugins/calendar/src/utils.ts +0 -80
  166. package/kernel/plugins/calendar/src/views.tsx +0 -418
  167. package/kernel/plugins/calendar/test/context.test.mjs +0 -60
  168. package/kernel/plugins/calendar/test/package.test.mjs +0 -43
  169. package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
  170. package/kernel/plugins/calendar/test/server.test.mjs +0 -126
  171. package/kernel/plugins/calendar/tsconfig.json +0 -17
  172. package/kernel/plugins/mail/SKILL.md +0 -97
  173. package/kernel/plugins/mail/eslint.config.js +0 -10
  174. package/kernel/plugins/mail/src/api.ts +0 -359
  175. package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
  176. package/kernel/plugins/mail/src/client.tsx +0 -14
  177. package/kernel/plugins/mail/src/flock.ts +0 -137
  178. package/kernel/plugins/mail/src/folders.ts +0 -37
  179. package/kernel/plugins/mail/src/history.ts +0 -485
  180. package/kernel/plugins/mail/src/index.ts +0 -3
  181. package/kernel/plugins/mail/src/mail-card.tsx +0 -123
  182. package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
  183. package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
  184. package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
  185. package/kernel/plugins/mail/src/mail-page.tsx +0 -109
  186. package/kernel/plugins/mail/src/mail-request.ts +0 -78
  187. package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
  188. package/kernel/plugins/mail/src/mailos.ts +0 -723
  189. package/kernel/plugins/mail/src/manifest.ts +0 -7
  190. package/kernel/plugins/mail/src/node.ts +0 -171
  191. package/kernel/plugins/mail/src/parse.ts +0 -139
  192. package/kernel/plugins/mail/src/server.ts +0 -125
  193. package/kernel/plugins/mail/src/types.ts +0 -10
  194. package/kernel/plugins/mail/src/viewer.tsx +0 -147
  195. package/kernel/plugins/mail/test/flock.test.mjs +0 -156
  196. package/kernel/plugins/mail/test/folders.test.mjs +0 -138
  197. package/kernel/plugins/mail/test/history.test.mjs +0 -460
  198. package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
  199. package/kernel/plugins/mail/test/node.test.mjs +0 -207
  200. package/kernel/plugins/mail/test/package.test.mjs +0 -54
  201. package/kernel/plugins/mail/test/parse.test.mjs +0 -32
  202. package/kernel/plugins/mail/test/server.test.mjs +0 -116
  203. package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
  204. package/kernel/plugins/mail/tsconfig.json +0 -17
  205. package/kernel/plugins/projects/SKILL.md +0 -73
  206. package/kernel/plugins/projects/eslint.config.js +0 -10
  207. package/kernel/plugins/projects/package.json +0 -70
  208. package/kernel/plugins/projects/src/client.tsx +0 -17
  209. package/kernel/plugins/projects/src/data/model.ts +0 -129
  210. package/kernel/plugins/projects/src/index.ts +0 -17
  211. package/kernel/plugins/projects/src/manifest.ts +0 -7
  212. package/kernel/plugins/projects/src/project-context.ts +0 -41
  213. package/kernel/plugins/projects/src/server.ts +0 -276
  214. package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
  215. package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
  216. package/kernel/plugins/projects/test/package.test.mjs +0 -51
  217. package/kernel/plugins/tasks/SKILL.md +0 -341
  218. package/kernel/plugins/tasks/eslint.config.js +0 -10
  219. package/kernel/plugins/tasks/package.json +0 -74
  220. package/kernel/plugins/tasks/schema.json +0 -939
  221. package/kernel/plugins/tasks/src/api.ts +0 -212
  222. package/kernel/plugins/tasks/src/client.tsx +0 -23
  223. package/kernel/plugins/tasks/src/data/parse.ts +0 -544
  224. package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
  225. package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
  226. package/kernel/plugins/tasks/src/index.ts +0 -34
  227. package/kernel/plugins/tasks/src/manifest.ts +0 -7
  228. package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
  229. package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
  230. package/kernel/plugins/tasks/src/server.ts +0 -447
  231. package/kernel/plugins/tasks/src/styles.css +0 -9
  232. package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
  233. package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
  234. package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
  235. package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
  236. package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
  237. package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
  238. package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
  239. package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
  240. package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
  241. package/kernel/plugins/tasks/test/api.test.mjs +0 -99
  242. package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
  243. package/kernel/plugins/tasks/test/package.test.mjs +0 -54
  244. package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
  245. package/kernel/plugins/tasks/test/server.test.mjs +0 -386
  246. package/kernel/plugins/tasks/tsconfig.json +0 -17
  247. package/web/assets/app-DoGr2Zi7.js +0 -2
  248. package/web/assets/channel-uMEjV8qc.js +0 -1
  249. package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
  250. package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
  251. package/web/assets/client-C6Nxdfif.js +0 -2
  252. package/web/assets/client-CXYfBko5.js +0 -17
  253. package/web/assets/client-CoKtOq-X.js +0 -7
  254. package/web/assets/client-DyRQz5RU.js +0 -53
  255. package/web/assets/client-OY6HUzuD.js +0 -98
  256. package/web/assets/client-YOYWZIFi.js +0 -34
  257. package/web/assets/command-BugZBGXE.js +0 -38
  258. package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
  259. package/web/assets/index-BaRnbYRw.js +0 -1
  260. package/web/assets/index-BexmLxIP.css +0 -1
  261. package/web/assets/index-DDirZMgx.js +0 -1
  262. package/web/assets/note-card-DBfX6gW1.js +0 -6
  263. package/web/assets/select-D8cqNgcp.js +0 -30
  264. package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
  265. package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
  266. package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
  267. /package/kernel/packages/{folder-plugin → plugins}/eslint.config.js +0 -0
  268. /package/kernel/packages/{folder-plugin → plugins}/src/client.ts +0 -0
  269. /package/kernel/packages/{folder-plugin → plugins}/src/endpoint.ts +0 -0
  270. /package/kernel/packages/{folder-plugin → plugins}/src/server.ts +0 -0
  271. /package/kernel/packages/{folder-plugin → plugins}/src/subpath.ts +0 -0
  272. /package/kernel/packages/{folder-plugin → plugins}/src/types.ts +0 -0
  273. /package/kernel/packages/{folder-plugin → plugins}/src/validation.ts +0 -0
  274. /package/kernel/packages/{folder-plugin → plugins}/test/contract.test.mjs +0 -0
@@ -1,207 +0,0 @@
1
- import { mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises"
2
- import { tmpdir } from "node:os"
3
- import { join, resolve } from "node:path"
4
- import { afterAll, beforeAll, describe, expect, test } from "bun:test"
5
-
6
- import { isFolderWithin, listMailForFolder, readMailById } from "../src/node.ts"
7
-
8
- let tmpBase
9
- let root
10
- let sibling
11
- let outside
12
-
13
- beforeAll(async () => {
14
- tmpBase = await mkdtemp(join(tmpdir(), "kennel-mail-nested-"))
15
- root = join(tmpBase, "project")
16
- sibling = join(tmpBase, "project-x")
17
- outside = join(tmpBase, "outside")
18
- await mkdir(join(root, "clients", "client-one"), { recursive: true })
19
- await mkdir(join(root, "client-one"), { recursive: true })
20
- await mkdir(sibling, { recursive: true })
21
- await mkdir(outside, { recursive: true })
22
- // Symlink escape: a "folder" inside root that realpaths outside it.
23
- await mkdir(join(root, "clients"), { recursive: true })
24
- await symlink(outside, join(root, "clients", "escape"))
25
- })
26
-
27
- afterAll(async () => {
28
- await rm(tmpBase, { recursive: true, force: true })
29
- })
30
-
31
- describe("isFolderWithin", () => {
32
- test("exact folder is within itself", () => {
33
- expect(isFolderWithin(root, root)).toBe(true)
34
- })
35
-
36
- test("real descendant is within", () => {
37
- expect(isFolderWithin(root, join(root, "clients", "client-one"))).toBe(true)
38
- expect(isFolderWithin(root, join(root, "clients"))).toBe(true)
39
- })
40
-
41
- test("sibling prefix path is rejected", () => {
42
- expect(isFolderWithin(root, sibling)).toBe(false)
43
- })
44
-
45
- test("outside ancestor is rejected", () => {
46
- expect(isFolderWithin(root, tmpBase)).toBe(false)
47
- expect(isFolderWithin(root, tmpdir())).toBe(false)
48
- })
49
-
50
- test("traversal escapes collapse and are resolved against real paths", () => {
51
- expect(isFolderWithin(root, join(root, "clients", "..", ".."))).toBe(false)
52
- expect(
53
- isFolderWithin(root, join(root, "clients", "..", "client-one")),
54
- ).toBe(true)
55
- })
56
-
57
- test("symlink escape is rejected", () => {
58
- expect(isFolderWithin(root, join(root, "clients", "escape"))).toBe(false)
59
- })
60
-
61
- test("unresolved (missing) candidate is rejected", () => {
62
- expect(isFolderWithin(root, join(root, "not-created-yet"))).toBe(false)
63
- expect(isFolderWithin(join(root, "not-created-yet"), root)).toBe(false)
64
- })
65
-
66
- test("null or undefined candidate is rejected", () => {
67
- expect(isFolderWithin(root, null)).toBe(false)
68
- expect(isFolderWithin(root, undefined)).toBe(false)
69
- })
70
- })
71
-
72
- function draftFile(folder, id, subject = `Subject ${id}`) {
73
- const folderLine = folder ? `folder: ${folder}` : ""
74
- return `---\nid: ${id}\n${folderLine}\nstatus: working\nsubject: ${subject}\n---\n\nBody of ${id}\n`
75
- }
76
-
77
- describe("listMailForFolder", () => {
78
- test("lists exact and descendant folders only", async () => {
79
- const mailDir = join(tmpBase, "drafts")
80
- await mkdir(mailDir, { recursive: true })
81
- await writeFile(
82
- join(mailDir, "d_root.md"),
83
- draftFile(resolve(root), "d_root"),
84
- )
85
- await writeFile(
86
- join(mailDir, "d_nested.md"),
87
- draftFile(resolve(root, "clients", "client-one"), "d_nested"),
88
- )
89
- await writeFile(
90
- join(mailDir, "d_nested2.md"),
91
- draftFile(resolve(root, "clients"), "d_nested2"),
92
- )
93
- await writeFile(
94
- join(mailDir, "d_sibling.md"),
95
- draftFile(resolve(sibling), "d_sibling"),
96
- )
97
- await writeFile(
98
- join(mailDir, "d_outside.md"),
99
- draftFile(resolve(outside), "d_outside"),
100
- )
101
- await writeFile(
102
- join(mailDir, "d_escape.md"),
103
- draftFile(resolve(root, "clients", "escape"), "d_escape"),
104
- )
105
- await writeFile(
106
- join(mailDir, "d_nofolder.md"),
107
- draftFile(null, "d_nofolder"),
108
- )
109
-
110
- const items = await listMailForFolder({ mailDir, folder: root })
111
- const ids = items.map((item) => item.id).sort()
112
- expect(ids).toEqual(["d_nested", "d_nested2", "d_root"])
113
-
114
- const nested = items.find((item) => item.id === "d_nested")
115
- expect(nested?.folder).toBe(resolve(root, "clients", "client-one"))
116
- expect(nested?.subject).toBe("Subject d_nested")
117
- })
118
-
119
- test("nested folder scope works for a subfolder row", async () => {
120
- const mailDir = join(tmpBase, "drafts")
121
- const items = await listMailForFolder({
122
- mailDir,
123
- folder: resolve(root, "clients"),
124
- })
125
- const ids = items.map((item) => item.id).sort()
126
- expect(ids).toContain("d_nested")
127
- expect(ids).toContain("d_nested2")
128
- expect(ids).not.toContain("d_root")
129
- })
130
-
131
- test("reads only working and ready drafts from the mail folder", async () => {
132
- const mailDir = join(tmpBase, "active-drafts")
133
- await mkdir(mailDir, { recursive: true })
134
-
135
- const statusDraft = (id, status) =>
136
- `---\nid: ${id}\nfolder: ${resolve(root)}\nstatus: ${status}\nsubject: ${id}\n---\n\nBody of ${id}\n`
137
- await writeFile(
138
- join(mailDir, "d_aaaaaa.md"),
139
- statusDraft("d_aaaaaa", "working"),
140
- )
141
- await writeFile(
142
- join(mailDir, "d_ccccc1.md"),
143
- statusDraft("d_ccccc1", "ready"),
144
- )
145
- await writeFile(
146
- join(mailDir, "d_bbbbbb.md"),
147
- statusDraft("d_bbbbbb", "sent"),
148
- )
149
- await writeFile(
150
- join(mailDir, "d_dddddd.md"),
151
- statusDraft("d_dddddd", "archived"),
152
- )
153
- await writeFile(join(mailDir, "d_eeeeee.md"), `# No status\n`)
154
-
155
- const items = await listMailForFolder({ mailDir, folder: root })
156
- const ids = items.map((item) => item.id).sort()
157
- expect(ids).toEqual(["d_aaaaaa", "d_ccccc1"])
158
- })
159
- })
160
-
161
- describe("readMailById", () => {
162
- test("reads the authoritative draft file by id", async () => {
163
- const mailDir = join(tmpBase, "direct-path")
164
- await mkdir(mailDir, { recursive: true })
165
-
166
- const path = join(mailDir, "d_cccccc.md")
167
- await writeFile(path, draftFile(resolve(root), "d_cccccc"))
168
-
169
- const mail = await readMailById("d_cccccc", mailDir)
170
-
171
- expect(mail?.id).toBe("d_cccccc")
172
- expect(mail?.filePath).toBe(path)
173
- expect(mail?.body).toBe(`Body of d_cccccc\n`)
174
- })
175
-
176
- test("supports MailOS descriptive draft ids", async () => {
177
- const mailDir = join(tmpBase, "descriptive-ids")
178
- await mkdir(mailDir, { recursive: true })
179
-
180
- const id = "draft_20251129_215834_Test-Draft"
181
- await writeFile(join(mailDir, `${id}.md`), draftFile(resolve(root), id))
182
-
183
- const mail = await readMailById(id, mailDir)
184
- expect(mail?.id).toBe(id)
185
- })
186
-
187
- test("rejects drafts that are no longer active", async () => {
188
- const mailDir = join(tmpBase, "inactive-drafts")
189
- await mkdir(mailDir, { recursive: true })
190
-
191
- await writeFile(
192
- join(mailDir, "d_bbbbbb.md"),
193
- `---\nid: d_bbbbbb\nfolder: ${resolve(root)}\nstatus: sent\nsubject: Sent\n---\n\nBody\n`,
194
- )
195
-
196
- const mail = await readMailById("d_bbbbbb", mailDir)
197
- expect(mail).toBeNull()
198
- })
199
-
200
- test("rejects traversal ids", () => {
201
- const mailDir = join(tmpBase, "traversal-drafts")
202
- expect(() => readMailById("../d_aaaaaa", mailDir)).toThrow(
203
- "Invalid mail id",
204
- )
205
- expect(() => readMailById("a/b", mailDir)).toThrow("Invalid mail id")
206
- })
207
- })
@@ -1,54 +0,0 @@
1
- import assert from "node:assert/strict"
2
- import { readFile } from "node:fs/promises"
3
- import test from "node:test"
4
-
5
- const packageJson = JSON.parse(
6
- await readFile(new URL("../package.json", import.meta.url), "utf8"),
7
- )
8
-
9
- test("mail package is ready for public npm publishing", () => {
10
- assert.equal(packageJson.private, undefined)
11
- assert.equal(packageJson.version, "0.1.0")
12
- assert.equal(packageJson.license, "MIT")
13
- assert.equal(packageJson.publishConfig?.access, "public")
14
- assert.equal(packageJson.repository?.directory, "plugins/mail")
15
- assert.deepEqual(packageJson.files, ["src"])
16
- })
17
-
18
- test("mail package ships the standard plugin script set", () => {
19
- for (const script of [
20
- "test",
21
- "typecheck",
22
- "format",
23
- "lint",
24
- "clean",
25
- "pack:check",
26
- "publish:public:dry-run",
27
- ]) {
28
- assert.ok(
29
- typeof packageJson.scripts?.[script] === "string",
30
- `missing script "${script}"`,
31
- )
32
- }
33
- })
34
-
35
- test("mail package exposes the core plugin deep exports", () => {
36
- const keys = Object.keys(packageJson.exports).sort()
37
- for (const required of [
38
- ".",
39
- "./client",
40
- "./manifest",
41
- "./parse",
42
- "./server",
43
- ]) {
44
- assert.ok(keys.includes(required), `missing export "${required}"`)
45
- }
46
- })
47
-
48
- test("mail client entry does not import the Node server entry", async () => {
49
- const clientSource = await readFile(
50
- new URL("../src/client.tsx", import.meta.url),
51
- "utf8",
52
- )
53
- assert.doesNotMatch(clientSource, /node:|\/server"/)
54
- })
@@ -1,32 +0,0 @@
1
- import { describe, expect, test } from "bun:test"
2
-
3
- import { parseMailMarkdown } from "../src/parse.ts"
4
-
5
- function baseMail(body = "Hello") {
6
- return `---\nid: d_abcdef\nfolder: /tmp/project\n---\n\n${body}\n`
7
- }
8
-
9
- describe("parseMailMarkdown", () => {
10
- test("parses normal frontmatter", () => {
11
- const parsed = parseMailMarkdown(baseMail("Body"))
12
- expect(parsed.id).toBe("d_abcdef")
13
- expect(parsed.folder).toBe("/tmp/project")
14
- expect(parsed.body).toBe("Body\n")
15
- })
16
-
17
- test("empty frontmatter does not throw and returns defaults", () => {
18
- const content = `---\n---\n\nBody\n`
19
- const parsed = parseMailMarkdown(content)
20
- expect(parsed.id).toBe(null)
21
- expect(parsed.folder).toBe(null)
22
- expect(parsed.body).toBe("Body\n")
23
- })
24
-
25
- test("non-object frontmatter does not throw and returns defaults", () => {
26
- const content = `---\n[]\n---\n\nBody\n`
27
- const parsed = parseMailMarkdown(content)
28
- expect(parsed.id).toBe(null)
29
- expect(parsed.folder).toBe(null)
30
- expect(parsed.body).toBe("Body\n")
31
- })
32
- })
@@ -1,116 +0,0 @@
1
- import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"
2
- import { tmpdir } from "node:os"
3
- import { join } from "node:path"
4
- import { afterAll, beforeAll, expect, test } from "bun:test"
5
-
6
- import { createMailPlugin } from "../src/server.ts"
7
-
8
- function draftFile(folder, id) {
9
- return `---\nid: ${id}\nfolder: ${folder}\nstatus: working\nsubject: ${id}\n---\n\nBody of ${id}\n`
10
- }
11
-
12
- let base
13
- let root
14
- let mailDir
15
-
16
- beforeAll(async () => {
17
- base = await mkdtemp(join(tmpdir(), "kennel-mail-plugin-"))
18
- root = join(base, "project")
19
- mailDir = join(base, "drafts")
20
- await mkdir(join(root, "clients"), { recursive: true })
21
- await mkdir(mailDir, { recursive: true })
22
- })
23
-
24
- afterAll(async () => {
25
- await rm(base, { recursive: true, force: true })
26
- })
27
-
28
- test("detect is unavailable with no active drafts", async () => {
29
- const plugin = createMailPlugin({ mailDir })
30
- const detection = await plugin.detect({ folder: root })
31
- expect(detection.status).toBe("unavailable")
32
- })
33
-
34
- test("detect reports available with a count of matching active drafts", async () => {
35
- const nested = join(root, "clients", "nested")
36
- await mkdir(nested, { recursive: true })
37
- await writeFile(join(mailDir, "d_root.md"), draftFile(root, "d_root"))
38
- await writeFile(join(mailDir, "d_nested.md"), draftFile(nested, "d_nested"))
39
-
40
- const plugin = createMailPlugin({ mailDir })
41
- const detection = await plugin.detect({ folder: root })
42
- expect(detection.status).toBe("available")
43
- expect(detection.count).toBe(2)
44
- })
45
-
46
- test("handle list returns only folder-and-descendant active drafts", async () => {
47
- const plugin = createMailPlugin({ mailDir })
48
- const result = await plugin.handle({ folder: root }, "")
49
- expect(result.kind).toBe("available")
50
- if (result.kind !== "available") return
51
- const payload = result.data
52
- expect(payload.status).toBe("available")
53
- const ids = payload.items.map((item) => item.id).sort()
54
- expect(ids).toEqual(["d_nested", "d_root"])
55
- })
56
-
57
- test("handle detail returns the mail and injected preview", async () => {
58
- const calls = []
59
- const plugin = createMailPlugin({
60
- mailDir,
61
- previewer: async (options) => {
62
- calls.push(options.id)
63
- return { body: "COMPOSED BODY", signature: "--\nSig" }
64
- },
65
- })
66
- const result = await plugin.handle({ folder: root }, "d_root")
67
- expect(result.kind).toBe("available")
68
- if (result.kind !== "available") return
69
- expect(result.data.mail.id).toBe("d_root")
70
- expect(result.data.preview.body).toBe("COMPOSED BODY")
71
- expect(calls).toEqual(["d_root"])
72
- })
73
-
74
- test("handle detail rejects a folder mismatch before preview", async () => {
75
- const other = join(base, "other")
76
- await mkdir(other, { recursive: true })
77
- let called = false
78
- const plugin = createMailPlugin({
79
- mailDir,
80
- previewer: async () => {
81
- called = true
82
- return { body: "x", signature: null }
83
- },
84
- })
85
- await expect(plugin.handle({ folder: other }, "d_root")).rejects.toThrow(
86
- "Mail not found for this folder",
87
- )
88
- expect(called).toBe(false)
89
- })
90
-
91
- test("handle detail maps a preview read failure to a server failure", async () => {
92
- const plugin = createMailPlugin({
93
- mailDir,
94
- previewer: async () => {
95
- throw new Error("mailos parse failed")
96
- },
97
- })
98
- await expect(plugin.handle({ folder: root }, "d_root")).rejects.toMatchObject(
99
- {
100
- name: "FolderPluginFailure",
101
- },
102
- )
103
- await expect(plugin.handle({ folder: root }, "d_root")).rejects.toThrow(
104
- "Mail preview failed",
105
- )
106
- })
107
-
108
- test("handle detail rejects traversal ids", async () => {
109
- const plugin = createMailPlugin({ mailDir })
110
- await expect(plugin.handle({ folder: root }, "../x")).rejects.toThrow(
111
- "Invalid mail id",
112
- )
113
- await expect(plugin.handle({ folder: root }, "a/b")).rejects.toThrow(
114
- "Invalid mail id",
115
- )
116
- })
@@ -1,192 +0,0 @@
1
- import { createElement as h } from "react"
2
- import { describe, expect, test } from "bun:test"
3
- import { renderToStaticMarkup } from "react-dom/server"
4
-
5
- import { folderChoices, folderLabel } from "../src/folders.ts"
6
- import { MailFolderFilter, mailItemContext, MailList } from "../src/viewer.tsx"
7
-
8
- const WORKSPACE = "/tmp/kennel-example/example-workspace"
9
- const CLIENT_ONE = `${WORKSPACE}/clients/client-one`
10
- const CLIENT_TWO = `${WORKSPACE}/clients/client-two`
11
- const CLIENT_THREE = `${WORKSPACE}/clients/client-three`
12
-
13
- function mailItem({ folder, id, subject }) {
14
- return {
15
- bcc: [],
16
- cc: [],
17
- createdAt: "2026-09-18T08:23:23.805127Z",
18
- excerpt: "Draft excerpt",
19
- filePath: `/tmp/${id}.md`,
20
- folder,
21
- id,
22
- status: "working",
23
- subject,
24
- tags: [],
25
- to: ["ed@example.com"],
26
- updatedAt: null,
27
- }
28
- }
29
-
30
- function renderMailList(items) {
31
- return renderToStaticMarkup(
32
- h(MailList, {
33
- folderLabel: (folder) => folderLabel(folder, WORKSPACE),
34
- items,
35
- mailHref: (id) => `/folder/x/mail/${id}`,
36
- }),
37
- )
38
- }
39
-
40
- describe("MailList cards", () => {
41
- test("each card shows its owning folder and keeps a stable link", () => {
42
- const html = renderMailList([
43
- mailItem({ folder: WORKSPACE, id: "d_root0", subject: "Root draft" }),
44
- mailItem({
45
- folder: CLIENT_ONE,
46
- id: "d_client-one0",
47
- subject: "Client One draft",
48
- }),
49
- mailItem({ folder: null, id: "d_none0", subject: "No folder draft" }),
50
- ])
51
-
52
- expect(html).toContain("Root draft")
53
- expect(html).toContain("Client One draft")
54
- expect(html).toContain("No folder draft")
55
- // Stable per-draft links survive filtering.
56
- expect(html).toContain('href="/folder/x/mail/d_root0"')
57
- expect(html).toContain('href="/folder/x/mail/d_client-one0"')
58
- expect(html).toContain('href="/folder/x/mail/d_none0"')
59
- expect(html).toContain(">example-workspace<")
60
- expect(html).toContain(">clients/client-one<")
61
- expect(html).toContain(`title="${CLIENT_ONE}"`)
62
- expect(html.match(/lucide-folder /g)).toHaveLength(2)
63
- expect(html).toContain('class="truncate">clients/client-one</span>')
64
- })
65
-
66
- test("shows the To domain favicon and a truncated message preview", () => {
67
- const html = renderMailList([
68
- {
69
- ...mailItem({ folder: CLIENT_ONE, id: "d_preview", subject: "Draft" }),
70
- to: ["Ed <ed@example.com>"],
71
- },
72
- ])
73
-
74
- expect(html).toContain(
75
- "https://www.google.com/s2/favicons?domain=example.com&amp;sz=32",
76
- )
77
- expect(html).not.toContain("domain=ed")
78
- expect(html).toContain('referrerPolicy="no-referrer"')
79
- expect(html).toContain("grid-cols-[2rem_minmax(0,1fr)]")
80
- expect(html).toMatch(/<p class="[^"]*truncate[^"]*">Draft excerpt<\/p>/)
81
- })
82
-
83
- test("uses a mail icon without a favicon request for missing or invalid To addresses", () => {
84
- for (const to of [[], ["invalid"], ["user@localhost"]]) {
85
- const html = renderMailList([
86
- {
87
- ...mailItem({ folder: null, id: "d_fallback", subject: "Draft" }),
88
- to,
89
- cc: ["cc@example.com"],
90
- excerpt: "",
91
- },
92
- ])
93
-
94
- expect(html).toContain("lucide-mail")
95
- expect(html).not.toContain("s2/favicons")
96
- expect(html).not.toMatch(/<p[\s>]/)
97
- }
98
- })
99
-
100
- test("time precedes the recipient in a left-aligned row", () => {
101
- const html = renderMailList([
102
- {
103
- ...mailItem({ folder: CLIENT_ONE, id: "d_time", subject: "Draft" }),
104
- updatedAt: new Date().toISOString(),
105
- },
106
- ])
107
-
108
- expect(html).toContain("just now")
109
- expect(html.indexOf("just now")).toBeLessThan(html.indexOf("To:"))
110
- expect(html).toContain('class="flex items-start gap-2"')
111
- })
112
- })
113
-
114
- describe("mailItemContext", () => {
115
- test("formats one draft's fields into an agent-friendly prompt", () => {
116
- const item = mailItem({
117
- folder: WORKSPACE,
118
- id: "d_ctx",
119
- subject: "Pricing follow-up",
120
- })
121
- const text = mailItemContext(item, "Hi Ed,\n\nConfirming the numbers.")
122
-
123
- expect(text).toContain("Instructions for the LLM:")
124
- expect(text).toContain("- Subject: Pricing follow-up")
125
- expect(text).toContain("- To: ed@example.com")
126
- expect(text).toContain("- Status: working")
127
- expect(text).toContain(`- Draft file: /tmp/d_ctx.md`)
128
- expect(text).toContain("Draft body:")
129
- expect(text).toContain("Confirming the numbers.")
130
- })
131
-
132
- test("falls back to the excerpt and a no-subject placeholder", () => {
133
- const item = mailItem({ folder: null, id: "d_none", subject: null })
134
- const text = mailItemContext(item)
135
-
136
- expect(text).toContain("Subject: (no subject)")
137
- expect(text).toContain("Draft excerpt:")
138
- expect(text).toContain("Draft excerpt")
139
- expect(text).not.toContain("Draft body:")
140
- })
141
- })
142
-
143
- describe("MailFolderFilter markup", () => {
144
- const drafts = [
145
- WORKSPACE,
146
- WORKSPACE,
147
- CLIENT_ONE,
148
- CLIENT_TWO,
149
- CLIENT_THREE,
150
- ].map((folder, index) => ({ folder, id: `d_${index}` }))
151
- const choices = folderChoices(drafts, WORKSPACE)
152
-
153
- test("one trigger button shows the total when nothing is selected", () => {
154
- const html = renderToStaticMarkup(
155
- h(MailFolderFilter, {
156
- choices,
157
- onSelect: () => {},
158
- selected: [],
159
- total: drafts.length,
160
- }),
161
- )
162
-
163
- // Single trigger with accessible name and the unfiltered total badge.
164
- expect(html).toContain('aria-label="Filter drafts by folder"')
165
- expect(html).toContain("lucide-folder-search")
166
- expect(html).toContain(">Folder<")
167
- expect(html).toContain(">5<")
168
- // The All folders row lives inside the portal, so no SSR markup carries it.
169
- expect(html).not.toContain("All folders")
170
- // No pressed chips outside the portal dropdown.
171
- const pressed = html.match(/aria-pressed=/g) ?? []
172
- expect(pressed.length).toBe(0)
173
- })
174
-
175
- test("a single selection shows the count on the trigger", () => {
176
- const html = renderToStaticMarkup(
177
- h(MailFolderFilter, {
178
- choices,
179
- onSelect: () => {},
180
- selected: [CLIENT_ONE],
181
- total: drafts.length,
182
- }),
183
- )
184
-
185
- // Same single trigger; the badge shows the selected count.
186
- expect(html).toContain('aria-label="Filter drafts by folder"')
187
- expect(html).toContain(">Folder<")
188
- expect(html).toContain(">1<")
189
- expect(html).not.toContain("lucide-x")
190
- expect(html).not.toContain("All folders")
191
- })
192
- })
@@ -1,17 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "extends": "@raggle-ai/tsconfig/base.json",
4
- "compilerOptions": {
5
- "target": "ES2022",
6
- "lib": ["dom", "dom.iterable", "esnext"],
7
- "jsx": "preserve",
8
- "baseUrl": ".",
9
- "allowImportingTsExtensions": true,
10
- // local copy: bun doesn't read paths inherited via `extends`
11
- "paths": {
12
- "~/*": ["${configDir}/../../packages/*/src"]
13
- }
14
- },
15
- "include": ["src"],
16
- "exclude": ["node_modules", "dist", "eslint.config.mjs"]
17
- }