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.
- package/README.md +23 -13
- package/dist/cli/dispatch.js +16 -3
- package/dist/cli/src/cli/dispatch.js +16 -3
- package/dist/cli/src/cli/usage.js +1 -1
- package/dist/cli/src/core/command.js +1 -0
- package/dist/cli/src/core/init.js +5 -5
- package/dist/cli/src/plugins/external.d.ts +28 -2
- package/dist/cli/src/plugins/external.js +77 -23
- package/dist/cli/src/site/server-plugins.d.ts +4 -9
- package/dist/cli/src/site/viewer.js +1 -0
- package/dist/cli/usage.js +1 -1
- package/dist/core/api-client.js +576 -10317
- package/dist/core/command.js +1 -0
- package/dist/core/core-folders.js +2313 -12088
- package/dist/core/init.js +5 -5
- package/dist/core/runtime.js +8237 -20713
- package/dist/package/preview.js +19617 -33352
- package/dist/plugins/external.d.ts +28 -2
- package/dist/plugins/external.js +77 -23
- package/dist/site/server-plugins.d.ts +4 -9
- package/dist/site/server-plugins.js +557 -10452
- package/dist/site/viewer.js +1 -0
- package/kernel/apps/web/package.json +2 -6
- package/kernel/apps/web/scripts/dev.mjs +53 -0
- package/kernel/apps/web/src/app.css +0 -3
- package/kernel/apps/web/src/external-plugins.ts +1 -1
- package/kernel/apps/web/src/folder-filter-icons.tsx +1 -1
- package/kernel/apps/web/src/folder-plugin-view.tsx +1 -1
- package/kernel/apps/web/src/main.tsx +0 -1
- package/kernel/apps/web/src/plugins.ts +1 -31
- package/kernel/apps/web/tsconfig.json +1 -2
- package/kernel/apps/web/vite.config.ts +32 -14
- package/kernel/packages/agents/package.json +1 -1
- package/kernel/packages/agents/src/agent-card.tsx +1 -1
- package/kernel/packages/agents/src/client.tsx +1 -1
- package/kernel/packages/agents/src/manifest.ts +1 -1
- package/kernel/packages/agents/src/server.ts +1 -1
- package/kernel/packages/agents/src/viewer.tsx +1 -1
- package/kernel/packages/api/package.json +1 -5
- package/kernel/packages/api/src/server/acp-live-plugin.ts +1 -1
- package/kernel/packages/api/src/server/api/create-agent-file.ts +2 -2
- package/kernel/packages/api/src/server/api/document-edit.ts +2 -2
- package/kernel/packages/api/src/server/api/open-path.ts +2 -2
- package/kernel/packages/api/src/server/folder-metadata-plugin.ts +17 -42
- package/kernel/packages/api/src/server/index.ts +0 -1
- package/kernel/packages/api/src/server/plugins.ts +52 -19
- package/kernel/packages/api/src/shared/viewer-route.ts +1 -1
- package/kernel/packages/api/test/core.test.ts +14 -26
- package/kernel/packages/markdown/README.md +1 -1
- package/kernel/packages/notes/package.json +1 -1
- package/kernel/packages/notes/src/client.tsx +1 -1
- package/kernel/packages/notes/src/manifest.ts +1 -1
- package/kernel/packages/notes/src/server.ts +1 -1
- package/kernel/packages/plugins/SKILL.md +39 -0
- package/kernel/{plugins/mail → packages/plugins}/package.json +53 -28
- package/kernel/packages/plugins/src/index.ts +52 -0
- package/kernel/packages/plugins/src/page.ts +17 -0
- package/kernel/{plugins/projects → packages/plugins}/tsconfig.json +5 -1
- package/kernel/packages/plugins/tsup.config.ts +25 -0
- package/kernel/packages/ui/src/components/content-issues.tsx +1 -1
- package/kernel/packages/ui/tsconfig.json +1 -1
- package/package.json +2 -3
- package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
- package/web/assets/app-CX20nNIa.js +2 -0
- package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
- package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
- package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
- package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
- package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
- package/web/assets/channel-X7WHyxxT.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
- package/web/assets/client-7OWw5kW2.js +6 -0
- package/web/assets/client-DYj_td4Q.js +17 -0
- package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
- package/web/assets/command-zkzH4hVz.js +38 -0
- package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
- package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
- package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
- package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
- package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
- package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
- package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
- package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
- package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
- package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
- package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
- package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
- package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
- package/web/assets/index-CegvVQ1C.js +1 -0
- package/web/assets/index-Dwhf8F_U.css +1 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
- package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
- package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
- package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
- package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
- package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
- package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
- package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
- package/web/assets/select-CtXC_PLC.js +30 -0
- package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
- package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
- package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
- package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
- package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
- package/web/index.html +2 -2
- package/kernel/packages/api/test/partial-loading.test.ts +0 -43
- package/kernel/packages/folder-plugin/package.json +0 -50
- package/kernel/packages/folder-plugin/src/index.ts +0 -15
- package/kernel/packages/folder-plugin/tsconfig.json +0 -17
- package/kernel/plugins/calendar/SKILL.md +0 -173
- package/kernel/plugins/calendar/eslint.config.js +0 -10
- package/kernel/plugins/calendar/package.json +0 -73
- package/kernel/plugins/calendar/schema.json +0 -137
- package/kernel/plugins/calendar/src/calendar.tsx +0 -328
- package/kernel/plugins/calendar/src/client.tsx +0 -139
- package/kernel/plugins/calendar/src/context.ts +0 -48
- package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
- package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
- package/kernel/plugins/calendar/src/index.ts +0 -4
- package/kernel/plugins/calendar/src/manifest.ts +0 -7
- package/kernel/plugins/calendar/src/parse.ts +0 -610
- package/kernel/plugins/calendar/src/server.ts +0 -160
- package/kernel/plugins/calendar/src/utils.ts +0 -80
- package/kernel/plugins/calendar/src/views.tsx +0 -418
- package/kernel/plugins/calendar/test/context.test.mjs +0 -60
- package/kernel/plugins/calendar/test/package.test.mjs +0 -43
- package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
- package/kernel/plugins/calendar/test/server.test.mjs +0 -126
- package/kernel/plugins/calendar/tsconfig.json +0 -17
- package/kernel/plugins/mail/SKILL.md +0 -97
- package/kernel/plugins/mail/eslint.config.js +0 -10
- package/kernel/plugins/mail/src/api.ts +0 -359
- package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
- package/kernel/plugins/mail/src/client.tsx +0 -14
- package/kernel/plugins/mail/src/flock.ts +0 -137
- package/kernel/plugins/mail/src/folders.ts +0 -37
- package/kernel/plugins/mail/src/history.ts +0 -485
- package/kernel/plugins/mail/src/index.ts +0 -3
- package/kernel/plugins/mail/src/mail-card.tsx +0 -123
- package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
- package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
- package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
- package/kernel/plugins/mail/src/mail-page.tsx +0 -109
- package/kernel/plugins/mail/src/mail-request.ts +0 -78
- package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
- package/kernel/plugins/mail/src/mailos.ts +0 -723
- package/kernel/plugins/mail/src/manifest.ts +0 -7
- package/kernel/plugins/mail/src/node.ts +0 -171
- package/kernel/plugins/mail/src/parse.ts +0 -139
- package/kernel/plugins/mail/src/server.ts +0 -125
- package/kernel/plugins/mail/src/types.ts +0 -10
- package/kernel/plugins/mail/src/viewer.tsx +0 -147
- package/kernel/plugins/mail/test/flock.test.mjs +0 -156
- package/kernel/plugins/mail/test/folders.test.mjs +0 -138
- package/kernel/plugins/mail/test/history.test.mjs +0 -460
- package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
- package/kernel/plugins/mail/test/node.test.mjs +0 -207
- package/kernel/plugins/mail/test/package.test.mjs +0 -54
- package/kernel/plugins/mail/test/parse.test.mjs +0 -32
- package/kernel/plugins/mail/test/server.test.mjs +0 -116
- package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
- package/kernel/plugins/mail/tsconfig.json +0 -17
- package/kernel/plugins/projects/SKILL.md +0 -73
- package/kernel/plugins/projects/eslint.config.js +0 -10
- package/kernel/plugins/projects/package.json +0 -70
- package/kernel/plugins/projects/src/client.tsx +0 -17
- package/kernel/plugins/projects/src/data/model.ts +0 -129
- package/kernel/plugins/projects/src/index.ts +0 -17
- package/kernel/plugins/projects/src/manifest.ts +0 -7
- package/kernel/plugins/projects/src/project-context.ts +0 -41
- package/kernel/plugins/projects/src/server.ts +0 -276
- package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
- package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
- package/kernel/plugins/projects/test/package.test.mjs +0 -51
- package/kernel/plugins/tasks/SKILL.md +0 -341
- package/kernel/plugins/tasks/eslint.config.js +0 -10
- package/kernel/plugins/tasks/package.json +0 -74
- package/kernel/plugins/tasks/schema.json +0 -939
- package/kernel/plugins/tasks/src/api.ts +0 -212
- package/kernel/plugins/tasks/src/client.tsx +0 -23
- package/kernel/plugins/tasks/src/data/parse.ts +0 -544
- package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
- package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
- package/kernel/plugins/tasks/src/index.ts +0 -34
- package/kernel/plugins/tasks/src/manifest.ts +0 -7
- package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
- package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
- package/kernel/plugins/tasks/src/server.ts +0 -447
- package/kernel/plugins/tasks/src/styles.css +0 -9
- package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
- package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
- package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
- package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
- package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
- package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
- package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
- package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
- package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
- package/kernel/plugins/tasks/test/api.test.mjs +0 -99
- package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
- package/kernel/plugins/tasks/test/package.test.mjs +0 -54
- package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
- package/kernel/plugins/tasks/test/server.test.mjs +0 -386
- package/kernel/plugins/tasks/tsconfig.json +0 -17
- package/web/assets/app-DoGr2Zi7.js +0 -2
- package/web/assets/channel-uMEjV8qc.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
- package/web/assets/client-C6Nxdfif.js +0 -2
- package/web/assets/client-CXYfBko5.js +0 -17
- package/web/assets/client-CoKtOq-X.js +0 -7
- package/web/assets/client-DyRQz5RU.js +0 -53
- package/web/assets/client-OY6HUzuD.js +0 -98
- package/web/assets/client-YOYWZIFi.js +0 -34
- package/web/assets/command-BugZBGXE.js +0 -38
- package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
- package/web/assets/index-BaRnbYRw.js +0 -1
- package/web/assets/index-BexmLxIP.css +0 -1
- package/web/assets/index-DDirZMgx.js +0 -1
- package/web/assets/note-card-DBfX6gW1.js +0 -6
- package/web/assets/select-D8cqNgcp.js +0 -30
- package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
- package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
- /package/kernel/packages/{folder-plugin → plugins}/eslint.config.js +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/client.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/endpoint.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/server.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/subpath.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/types.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/src/validation.ts +0 -0
- /package/kernel/packages/{folder-plugin → plugins}/test/contract.test.mjs +0 -0
|
@@ -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
|
-
}
|