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,153 +0,0 @@
|
|
|
1
|
-
import { RefreshCw } from "lucide-react"
|
|
2
|
-
|
|
3
|
-
import { BarkdownMarkdown } from "@raggle-ai/markdown"
|
|
4
|
-
|
|
5
|
-
import type { ConversationMessagePayload } from "./mail-hooks.js"
|
|
6
|
-
import { conversationPanelState, useMailConversation } from "./mail-hooks.js"
|
|
7
|
-
import { mailErrorText } from "./mail-request.js"
|
|
8
|
-
|
|
9
|
-
function notice(message: string, onRetry: () => void) {
|
|
10
|
-
return (
|
|
11
|
-
<p className="m-0 flex flex-wrap items-center gap-2 text-sm text-[#6c7a6d]">
|
|
12
|
-
<span className="break-words">{message}</span>
|
|
13
|
-
<button
|
|
14
|
-
type="button"
|
|
15
|
-
className="inline-flex items-center gap-1.5 rounded-md border
|
|
16
|
-
border-[#dde1dc] bg-white px-2.5 py-1 text-[0.82rem] text-[#4a554b]
|
|
17
|
-
hover:bg-[#f3f5f2]"
|
|
18
|
-
onClick={onRetry}
|
|
19
|
-
>
|
|
20
|
-
<RefreshCw className="size-3.5" />
|
|
21
|
-
Retry
|
|
22
|
-
</button>
|
|
23
|
-
</p>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function ConversationMessages({
|
|
28
|
-
items,
|
|
29
|
-
}: {
|
|
30
|
-
items: ConversationMessagePayload[]
|
|
31
|
-
}) {
|
|
32
|
-
return (
|
|
33
|
-
<ol className="m-0 grid list-none gap-0 p-0">
|
|
34
|
-
{items.map((message, index) => (
|
|
35
|
-
<li
|
|
36
|
-
className={
|
|
37
|
-
index === 0 ? "px-4 py-4" : "border-t border-[#e7ebe7] px-4 py-4"
|
|
38
|
-
}
|
|
39
|
-
key={message.id}
|
|
40
|
-
>
|
|
41
|
-
<div className="mb-2 grid gap-1">
|
|
42
|
-
<p className="m-0 font-semibold break-words text-[#233025]">
|
|
43
|
-
{message.subject ?? "(no subject)"}
|
|
44
|
-
</p>
|
|
45
|
-
<p className="m-0 text-[0.82rem] text-[#6c7a6d]">
|
|
46
|
-
{message.from ?? "Unknown sender"}
|
|
47
|
-
{message.date ? ` · ${message.date}` : ""}
|
|
48
|
-
</p>
|
|
49
|
-
</div>
|
|
50
|
-
<div className="prose prose-neutral max-w-none">
|
|
51
|
-
<BarkdownMarkdown value={message.body} />
|
|
52
|
-
</div>
|
|
53
|
-
</li>
|
|
54
|
-
))}
|
|
55
|
-
</ol>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The previous-conversation panel. A disabled query is hidden rather than
|
|
61
|
-
* stuck loading; partial/not-found/timeout outcomes surface inline errors
|
|
62
|
-
* with a retry instead of silently masquerading as an empty conversation.
|
|
63
|
-
*/
|
|
64
|
-
export function MailConversationPanel({
|
|
65
|
-
enabled,
|
|
66
|
-
folder,
|
|
67
|
-
mailId,
|
|
68
|
-
}: {
|
|
69
|
-
enabled: boolean
|
|
70
|
-
folder: string | null | undefined
|
|
71
|
-
mailId: string | null | undefined
|
|
72
|
-
}) {
|
|
73
|
-
const query = useMailConversation(folder, mailId, enabled)
|
|
74
|
-
const state = conversationPanelState({
|
|
75
|
-
data: query.data,
|
|
76
|
-
enabled,
|
|
77
|
-
error: query.error,
|
|
78
|
-
isPending: query.isPending,
|
|
79
|
-
})
|
|
80
|
-
const retry = () => void query.refetch()
|
|
81
|
-
|
|
82
|
-
if (state.variant === "hidden" || state.variant === "empty") return null
|
|
83
|
-
if (state.variant === "loading") {
|
|
84
|
-
return <p className="m-0 text-sm text-[#6c7a6d]">Loading conversation…</p>
|
|
85
|
-
}
|
|
86
|
-
if (state.variant === "error") {
|
|
87
|
-
return (
|
|
88
|
-
<p
|
|
89
|
-
className="m-0 flex flex-wrap items-center gap-2 text-sm text-[#9c3a3a]"
|
|
90
|
-
>
|
|
91
|
-
<span className="break-words">
|
|
92
|
-
Could not load the conversation. {mailErrorText(query.error)}
|
|
93
|
-
</span>
|
|
94
|
-
<RetryInline onRetry={retry} />
|
|
95
|
-
</p>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const items = query.data?.items ?? []
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<section
|
|
103
|
-
className="overflow-hidden rounded-xl border border-[#e7ebe7] bg-white
|
|
104
|
-
shadow-[0_1px_2px_rgb(31_41_55/0.05)]"
|
|
105
|
-
>
|
|
106
|
-
<header className="border-b border-[#e7ebe7] bg-[#fbfcfb] px-4 py-3">
|
|
107
|
-
<h2
|
|
108
|
-
className="m-0 text-[0.75rem] font-medium tracking-wide text-[#6c7a6d]
|
|
109
|
-
uppercase"
|
|
110
|
-
>
|
|
111
|
-
Previous conversation
|
|
112
|
-
</h2>
|
|
113
|
-
</header>
|
|
114
|
-
|
|
115
|
-
{items.length ? <ConversationMessages items={items} /> : null}
|
|
116
|
-
|
|
117
|
-
<footer className="border-t border-[#e7ebe7] bg-[#fbfcfb] px-4 py-3">
|
|
118
|
-
{state.variant === "partial"
|
|
119
|
-
? notice(
|
|
120
|
-
`Some earlier messages could not be loaded (${state.missing}).`,
|
|
121
|
-
retry,
|
|
122
|
-
)
|
|
123
|
-
: null}
|
|
124
|
-
{state.variant === "not-found"
|
|
125
|
-
? notice("Earlier messages could not be found.", retry)
|
|
126
|
-
: null}
|
|
127
|
-
{state.variant === "timeout"
|
|
128
|
-
? notice("Loading earlier messages timed out.", retry)
|
|
129
|
-
: null}
|
|
130
|
-
{state.variant === "ok" && items.length ? (
|
|
131
|
-
<p className="m-0 text-[0.82rem] text-[#6c7a6d]">
|
|
132
|
-
Thread loaded from references.
|
|
133
|
-
</p>
|
|
134
|
-
) : null}
|
|
135
|
-
</footer>
|
|
136
|
-
</section>
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function RetryInline({ onRetry }: { onRetry: () => void }) {
|
|
141
|
-
return (
|
|
142
|
-
<button
|
|
143
|
-
type="button"
|
|
144
|
-
className="inline-flex items-center gap-1.5 rounded-md border
|
|
145
|
-
border-[#dde1dc] bg-white px-2.5 py-1 text-[0.82rem] text-[#4a554b]
|
|
146
|
-
hover:bg-[#f3f5f2]"
|
|
147
|
-
onClick={onRetry}
|
|
148
|
-
>
|
|
149
|
-
<RefreshCw className="size-3.5" />
|
|
150
|
-
Retry
|
|
151
|
-
</button>
|
|
152
|
-
)
|
|
153
|
-
}
|
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react"
|
|
2
|
-
import {
|
|
3
|
-
Archive,
|
|
4
|
-
ArrowLeft,
|
|
5
|
-
ExternalLink,
|
|
6
|
-
RefreshCw,
|
|
7
|
-
Trash2,
|
|
8
|
-
X,
|
|
9
|
-
} from "lucide-react"
|
|
10
|
-
|
|
11
|
-
import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
|
|
12
|
-
import { BarkdownMarkdown } from "@raggle-ai/markdown"
|
|
13
|
-
import { CopyContextButton } from "@raggle-ai/ui/components/copy-context"
|
|
14
|
-
import { FilePreview } from "@raggle-ai/ui/components/file-preview"
|
|
15
|
-
import { toast } from "@raggle-ai/ui/components/sonner"
|
|
16
|
-
|
|
17
|
-
import type { MailPreview } from "./types.js"
|
|
18
|
-
import type { MailDetail } from "./viewer.js"
|
|
19
|
-
import { MailConversationPanel } from "./mail-conversation.js"
|
|
20
|
-
import { useMailAction, useMailDetail, useMailSender } from "./mail-hooks.js"
|
|
21
|
-
import { mailErrorText } from "./mail-request.js"
|
|
22
|
-
import { MailSendBox } from "./mail-send-box.js"
|
|
23
|
-
import { mailItemContext } from "./viewer.js"
|
|
24
|
-
|
|
25
|
-
function displayTimestamp(value: string | null) {
|
|
26
|
-
if (!value) return "—"
|
|
27
|
-
const parsed = Date.parse(value)
|
|
28
|
-
if (Number.isNaN(parsed)) return value
|
|
29
|
-
return new Date(parsed).toLocaleString(undefined, {
|
|
30
|
-
dateStyle: "medium",
|
|
31
|
-
timeStyle: "short",
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function MailMetaRow({ label, value }: { label: string; value: ReactNode }) {
|
|
36
|
-
return (
|
|
37
|
-
<div className="grid grid-cols-[88px_1fr] items-start gap-x-3 gap-y-1">
|
|
38
|
-
<span
|
|
39
|
-
className="pt-0.5 text-[0.75rem] font-medium tracking-wide
|
|
40
|
-
text-[#6c7a6d] uppercase"
|
|
41
|
-
>
|
|
42
|
-
{label}
|
|
43
|
-
</span>
|
|
44
|
-
<span className="min-w-0 text-sm break-words">{value}</span>
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function RecipientItems({ items, max }: { items: string[]; max?: number }) {
|
|
50
|
-
if (!items.length) {
|
|
51
|
-
return <span className="text-[#6c7a6d]">—</span>
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const visible = max ? items.slice(0, max) : items
|
|
55
|
-
const extra = max && items.length > max ? items.length - max : 0
|
|
56
|
-
const pillClassName =
|
|
57
|
-
"rounded-md bg-[#f3f5f2] px-2 py-1 text-[0.85rem] text-[#2f382f]"
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<span className="flex min-w-0 flex-wrap gap-1">
|
|
61
|
-
{visible.map((recipient) => (
|
|
62
|
-
<span key={recipient} className={pillClassName} title={recipient}>
|
|
63
|
-
<span className="break-all">{recipient}</span>
|
|
64
|
-
</span>
|
|
65
|
-
))}
|
|
66
|
-
{extra ? (
|
|
67
|
-
<span className={pillClassName} title={`${extra} more recipients`}>
|
|
68
|
-
+{extra}
|
|
69
|
-
</span>
|
|
70
|
-
) : null}
|
|
71
|
-
</span>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Attachment cards with an optional per-card remove control. The remove
|
|
77
|
-
* button only unlinks the file from the draft's frontmatter `file:` list;
|
|
78
|
-
* the file stays on disk.
|
|
79
|
-
*/
|
|
80
|
-
function AttachmentList({
|
|
81
|
-
paths,
|
|
82
|
-
pending,
|
|
83
|
-
onRemove,
|
|
84
|
-
}: {
|
|
85
|
-
paths: string[]
|
|
86
|
-
pending?: boolean
|
|
87
|
-
onRemove?: (attachment: string) => void
|
|
88
|
-
}) {
|
|
89
|
-
return (
|
|
90
|
-
<span className="flex min-w-0 flex-wrap gap-2">
|
|
91
|
-
{paths.map((path) => {
|
|
92
|
-
const name = path.slice(path.lastIndexOf("/") + 1)
|
|
93
|
-
return (
|
|
94
|
-
<span key={path} className="relative">
|
|
95
|
-
<FilePreview path={path} />
|
|
96
|
-
{onRemove ? (
|
|
97
|
-
<button
|
|
98
|
-
type="button"
|
|
99
|
-
aria-label={`Remove ${name} from this draft`}
|
|
100
|
-
title={`Remove ${name} from this draft}`}
|
|
101
|
-
className="absolute top-1.5 right-1.5 z-10 flex size-6
|
|
102
|
-
cursor-pointer items-center justify-center rounded-full border
|
|
103
|
-
border-[#e5c4c4] bg-white text-[#9c3a3a] shadow-xs
|
|
104
|
-
hover:bg-[#fbf3f3] disabled:pointer-events-none
|
|
105
|
-
disabled:opacity-50"
|
|
106
|
-
onClick={() => onRemove(path)}
|
|
107
|
-
disabled={pending}
|
|
108
|
-
>
|
|
109
|
-
<X className="size-3.5" />
|
|
110
|
-
</button>
|
|
111
|
-
) : null}
|
|
112
|
-
</span>
|
|
113
|
-
)
|
|
114
|
-
})}
|
|
115
|
-
</span>
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function MailDetailView({
|
|
120
|
-
mail,
|
|
121
|
-
error,
|
|
122
|
-
preview,
|
|
123
|
-
refreshing,
|
|
124
|
-
onRefresh,
|
|
125
|
-
onArchive,
|
|
126
|
-
onDelete,
|
|
127
|
-
actionPending,
|
|
128
|
-
onOpenPath,
|
|
129
|
-
onRemoveAttachment,
|
|
130
|
-
}: {
|
|
131
|
-
mail: MailDetail
|
|
132
|
-
error?: string
|
|
133
|
-
preview?: MailPreview
|
|
134
|
-
refreshing: boolean
|
|
135
|
-
onRefresh: () => void
|
|
136
|
-
/** Archive this draft; omit to hide the Archive button. */
|
|
137
|
-
onArchive?: () => void
|
|
138
|
-
/** Delete this draft; omit to hide the Delete button. */
|
|
139
|
-
onDelete?: () => void
|
|
140
|
-
/** True while an archive or delete request is running. */
|
|
141
|
-
actionPending?: boolean
|
|
142
|
-
/** Open a physical file in the host's default app; omit to hide the button. */
|
|
143
|
-
onOpenPath?: (path: string) => Promise<unknown>
|
|
144
|
-
/** Unlink one attachment from this draft; omit to hide the remove button. */
|
|
145
|
-
onRemoveAttachment?: (attachment: string) => void
|
|
146
|
-
}) {
|
|
147
|
-
return (
|
|
148
|
-
<div className="grid min-w-0 grid-cols-1 gap-4">
|
|
149
|
-
<header className="flex flex-wrap items-start justify-between gap-3">
|
|
150
|
-
<div className="min-w-0">
|
|
151
|
-
<h1 className="m-0 text-2xl font-semibold break-words text-[#233025]">
|
|
152
|
-
{mail.subject ?? "(no subject)"}
|
|
153
|
-
</h1>
|
|
154
|
-
<p className="m-0 mt-1 text-sm text-[#6c7a6d]">
|
|
155
|
-
Updated:{" "}
|
|
156
|
-
{(mail.updatedAt ?? mail.createdAt) ? (
|
|
157
|
-
<time
|
|
158
|
-
dateTime={mail.updatedAt ?? mail.createdAt ?? undefined}
|
|
159
|
-
title={mail.updatedAt ?? mail.createdAt ?? undefined}
|
|
160
|
-
>
|
|
161
|
-
{displayTimestamp(mail.updatedAt ?? mail.createdAt)}
|
|
162
|
-
</time>
|
|
163
|
-
) : (
|
|
164
|
-
"—"
|
|
165
|
-
)}
|
|
166
|
-
</p>
|
|
167
|
-
</div>
|
|
168
|
-
|
|
169
|
-
<div className="flex shrink-0 flex-wrap items-center gap-2">
|
|
170
|
-
<CopyContextButton
|
|
171
|
-
className="border bg-white text-[#4a554b] shadow-xs
|
|
172
|
-
hover:bg-[#f3f5f2]"
|
|
173
|
-
context={() => mailItemContext(mail, preview?.body ?? mail.body)}
|
|
174
|
-
label={`Copy full context for ${mail.subject ?? "this draft"}`}
|
|
175
|
-
size="sm"
|
|
176
|
-
variant="ghost"
|
|
177
|
-
/>
|
|
178
|
-
<CopyContextButton
|
|
179
|
-
className="border bg-white text-[#4a554b] shadow-xs
|
|
180
|
-
hover:bg-[#f3f5f2]"
|
|
181
|
-
context={mail.filePath}
|
|
182
|
-
label="Copy draft path"
|
|
183
|
-
size="sm"
|
|
184
|
-
variant="ghost"
|
|
185
|
-
/>
|
|
186
|
-
<button
|
|
187
|
-
type="button"
|
|
188
|
-
className="inline-flex items-center gap-2 rounded-md border px-3
|
|
189
|
-
py-1.5 text-sm"
|
|
190
|
-
onClick={onRefresh}
|
|
191
|
-
disabled={refreshing}
|
|
192
|
-
>
|
|
193
|
-
<RefreshCw
|
|
194
|
-
className={refreshing ? "size-4 animate-spin" : "size-4"}
|
|
195
|
-
/>
|
|
196
|
-
Refresh
|
|
197
|
-
</button>
|
|
198
|
-
{onOpenPath ? (
|
|
199
|
-
<button
|
|
200
|
-
type="button"
|
|
201
|
-
className="inline-flex cursor-pointer items-center gap-2
|
|
202
|
-
rounded-md border px-3 py-1.5 text-sm"
|
|
203
|
-
onClick={() => void onOpenPath(mail.filePath)}
|
|
204
|
-
>
|
|
205
|
-
<ExternalLink className="size-4" />
|
|
206
|
-
Open
|
|
207
|
-
</button>
|
|
208
|
-
) : null}
|
|
209
|
-
{onArchive && mail.status !== "sent" ? (
|
|
210
|
-
<button
|
|
211
|
-
type="button"
|
|
212
|
-
className="inline-flex items-center gap-2 rounded-md border px-3
|
|
213
|
-
py-1.5 text-sm disabled:pointer-events-none disabled:opacity-50"
|
|
214
|
-
onClick={onArchive}
|
|
215
|
-
disabled={actionPending}
|
|
216
|
-
>
|
|
217
|
-
<Archive className="size-4" />
|
|
218
|
-
Archive
|
|
219
|
-
</button>
|
|
220
|
-
) : null}
|
|
221
|
-
{onDelete ? (
|
|
222
|
-
<button
|
|
223
|
-
type="button"
|
|
224
|
-
className="inline-flex items-center gap-2 rounded-md border
|
|
225
|
-
border-[#e5c4c4] px-3 py-1.5 text-sm text-[#9c3a3a]
|
|
226
|
-
hover:bg-[#fbf3f3] disabled:pointer-events-none
|
|
227
|
-
disabled:opacity-50"
|
|
228
|
-
onClick={onDelete}
|
|
229
|
-
disabled={actionPending}
|
|
230
|
-
>
|
|
231
|
-
<Trash2 className="size-4" />
|
|
232
|
-
Delete
|
|
233
|
-
</button>
|
|
234
|
-
) : null}
|
|
235
|
-
</div>
|
|
236
|
-
</header>
|
|
237
|
-
|
|
238
|
-
<div
|
|
239
|
-
className="grid grid-cols-1 gap-3 rounded-xl border border-[#e7ebe7]
|
|
240
|
-
bg-white px-4 py-4 shadow-[0_1px_2px_rgb(31_41_55/0.05)]"
|
|
241
|
-
>
|
|
242
|
-
{mail.from ? <MailMetaRow label="From" value={mail.from} /> : null}
|
|
243
|
-
{mail.subject ? (
|
|
244
|
-
<MailMetaRow label="Subject" value={mail.subject} />
|
|
245
|
-
) : null}
|
|
246
|
-
<MailMetaRow label="To" value={<RecipientItems items={mail.to} />} />
|
|
247
|
-
<MailMetaRow label="CC" value={<RecipientItems items={mail.cc} />} />
|
|
248
|
-
<MailMetaRow label="BCC" value={<RecipientItems items={mail.bcc} />} />
|
|
249
|
-
{mail.noSignature ? (
|
|
250
|
-
<MailMetaRow label="Signature" value="none" />
|
|
251
|
-
) : null}
|
|
252
|
-
{mail.attachments.length ? (
|
|
253
|
-
<MailMetaRow
|
|
254
|
-
label="Attachments"
|
|
255
|
-
value={
|
|
256
|
-
<AttachmentList
|
|
257
|
-
paths={mail.attachments}
|
|
258
|
-
pending={actionPending}
|
|
259
|
-
onRemove={onRemoveAttachment}
|
|
260
|
-
/>
|
|
261
|
-
}
|
|
262
|
-
/>
|
|
263
|
-
) : null}
|
|
264
|
-
</div>
|
|
265
|
-
|
|
266
|
-
{error ? <p className="m-0 text-sm text-[#9c3a3a]">{error}</p> : null}
|
|
267
|
-
|
|
268
|
-
{preview ? (
|
|
269
|
-
<article
|
|
270
|
-
className="overflow-hidden rounded-xl border border-[#e7ebe7] bg-white
|
|
271
|
-
shadow-[0_1px_2px_rgb(31_41_55/0.05)]"
|
|
272
|
-
>
|
|
273
|
-
<section className="px-4 py-4">
|
|
274
|
-
{!preview.body.trim() && !preview.signature ? (
|
|
275
|
-
<p className="m-0 text-sm text-[#6c7a6d]">(empty body)</p>
|
|
276
|
-
) : (
|
|
277
|
-
<div className="prose prose-neutral max-w-none">
|
|
278
|
-
<BarkdownMarkdown
|
|
279
|
-
value={
|
|
280
|
-
[preview.body, preview.signature]
|
|
281
|
-
.filter(Boolean)
|
|
282
|
-
.join("\n\n") as string
|
|
283
|
-
}
|
|
284
|
-
/>
|
|
285
|
-
</div>
|
|
286
|
-
)}
|
|
287
|
-
</section>
|
|
288
|
-
</article>
|
|
289
|
-
) : null}
|
|
290
|
-
</div>
|
|
291
|
-
)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function MailDetailPage({
|
|
295
|
-
folder,
|
|
296
|
-
subpath: mailId,
|
|
297
|
-
listHref,
|
|
298
|
-
hostActions,
|
|
299
|
-
}: Pick<
|
|
300
|
-
FolderPluginViewProps,
|
|
301
|
-
"folder" | "subpath" | "listHref" | "hostActions"
|
|
302
|
-
>) {
|
|
303
|
-
const query = useMailDetail(folder, mailId)
|
|
304
|
-
const action = useMailAction(folder)
|
|
305
|
-
|
|
306
|
-
async function handleArchive() {
|
|
307
|
-
if (!mail) return
|
|
308
|
-
try {
|
|
309
|
-
await action.mutateAsync({
|
|
310
|
-
action: "archive",
|
|
311
|
-
id: mailId,
|
|
312
|
-
path: folder ?? "",
|
|
313
|
-
revision: mail.revision,
|
|
314
|
-
})
|
|
315
|
-
void query.refetch()
|
|
316
|
-
} catch (error) {
|
|
317
|
-
toast.error(mailErrorText(error))
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async function handleRemoveAttachment(attachment: string) {
|
|
322
|
-
if (!mail) return
|
|
323
|
-
if (
|
|
324
|
-
!window.confirm(
|
|
325
|
-
`Remove "${attachment}" from this draft? The file is kept on disk.`,
|
|
326
|
-
)
|
|
327
|
-
)
|
|
328
|
-
return
|
|
329
|
-
try {
|
|
330
|
-
await action.mutateAsync({
|
|
331
|
-
action: "remove-attachment",
|
|
332
|
-
id: mailId,
|
|
333
|
-
path: folder ?? "",
|
|
334
|
-
revision: mail.revision,
|
|
335
|
-
attachment,
|
|
336
|
-
})
|
|
337
|
-
void query.refetch()
|
|
338
|
-
} catch (error) {
|
|
339
|
-
toast.error(mailErrorText(error))
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async function handleDelete() {
|
|
344
|
-
if (!mail) return
|
|
345
|
-
if (
|
|
346
|
-
!window.confirm(
|
|
347
|
-
"Delete this draft? It will be moved to draft history first.",
|
|
348
|
-
)
|
|
349
|
-
)
|
|
350
|
-
return
|
|
351
|
-
try {
|
|
352
|
-
await action.mutateAsync({
|
|
353
|
-
action: "delete",
|
|
354
|
-
id: mailId,
|
|
355
|
-
path: folder ?? "",
|
|
356
|
-
revision: mail.revision,
|
|
357
|
-
})
|
|
358
|
-
window.location.assign(listHref)
|
|
359
|
-
} catch (error) {
|
|
360
|
-
toast.error(mailErrorText(error))
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const senderNeeded = Boolean(query.data?.mail && !query.data.mail.from)
|
|
365
|
-
const fromQuery = useMailSender(folder, mailId, senderNeeded)
|
|
366
|
-
|
|
367
|
-
const resolvedFrom =
|
|
368
|
-
query.data?.mail?.from ??
|
|
369
|
-
(fromQuery.data?.name && fromQuery.data?.from
|
|
370
|
-
? `${fromQuery.data.name} <${fromQuery.data.from}>`
|
|
371
|
-
: (fromQuery.data?.from ?? null))
|
|
372
|
-
const mail = query.data?.mail
|
|
373
|
-
? { ...query.data.mail, from: resolvedFrom }
|
|
374
|
-
: undefined
|
|
375
|
-
|
|
376
|
-
const conversationEnabled = Boolean(
|
|
377
|
-
query.data?.mail &&
|
|
378
|
-
(query.data.mail.inReplyTo ?? query.data.mail.references.length > 0),
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
if (!folder || !mailId) {
|
|
382
|
-
return (
|
|
383
|
-
<div
|
|
384
|
-
className="mx-auto grid w-full max-w-3xl min-w-0 grid-cols-1 gap-4 px-4
|
|
385
|
-
py-5"
|
|
386
|
-
>
|
|
387
|
-
<p className="m-0 text-sm text-[#6c7a6d]">
|
|
388
|
-
Select a mail from the list.
|
|
389
|
-
</p>
|
|
390
|
-
<a
|
|
391
|
-
className="w-fit cursor-pointer rounded-md border border-[#dde1dc]
|
|
392
|
-
bg-white px-3 py-1.5 text-[0.85rem] text-[#4a554b]
|
|
393
|
-
hover:bg-[#f3f5f2]"
|
|
394
|
-
href={listHref}
|
|
395
|
-
>
|
|
396
|
-
Back
|
|
397
|
-
</a>
|
|
398
|
-
</div>
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return (
|
|
403
|
-
<div
|
|
404
|
-
className="mx-auto grid w-full max-w-3xl min-w-0 grid-cols-1 gap-4 px-4
|
|
405
|
-
py-5"
|
|
406
|
-
>
|
|
407
|
-
<a
|
|
408
|
-
className="inline-flex w-fit items-center gap-2 rounded-md border
|
|
409
|
-
border-[#dde1dc] bg-white px-3 py-1.5 text-sm text-[#4a554b]
|
|
410
|
-
no-underline hover:bg-[#f3f5f2]"
|
|
411
|
-
href={listHref}
|
|
412
|
-
>
|
|
413
|
-
<ArrowLeft className="size-4" />
|
|
414
|
-
Back to Mail
|
|
415
|
-
</a>
|
|
416
|
-
|
|
417
|
-
{query.isPending ? (
|
|
418
|
-
<p className="m-0 text-sm text-[#6c7a6d]">Loading…</p>
|
|
419
|
-
) : null}
|
|
420
|
-
|
|
421
|
-
{query.error && !query.data?.mail ? (
|
|
422
|
-
<p className="m-0 text-sm text-[#9c3a3a]">
|
|
423
|
-
Could not load mail preview.
|
|
424
|
-
</p>
|
|
425
|
-
) : null}
|
|
426
|
-
|
|
427
|
-
{mail ? (
|
|
428
|
-
<MailDetailView
|
|
429
|
-
mail={mail}
|
|
430
|
-
preview={query.error ? undefined : query.data?.preview}
|
|
431
|
-
error={
|
|
432
|
-
query.error
|
|
433
|
-
? "Could not load mail preview."
|
|
434
|
-
: query.data?.preview
|
|
435
|
-
? undefined
|
|
436
|
-
: "Could not load mail preview."
|
|
437
|
-
}
|
|
438
|
-
refreshing={query.isFetching}
|
|
439
|
-
onRefresh={() => void query.refetch()}
|
|
440
|
-
onArchive={
|
|
441
|
-
mail.status === "sent" ? undefined : () => void handleArchive()
|
|
442
|
-
}
|
|
443
|
-
onDelete={() => void handleDelete()}
|
|
444
|
-
actionPending={action.isPending}
|
|
445
|
-
onOpenPath={hostActions?.openPath}
|
|
446
|
-
onRemoveAttachment={(attachment) =>
|
|
447
|
-
void handleRemoveAttachment(attachment)
|
|
448
|
-
}
|
|
449
|
-
/>
|
|
450
|
-
) : null}
|
|
451
|
-
|
|
452
|
-
<MailConversationPanel
|
|
453
|
-
enabled={conversationEnabled}
|
|
454
|
-
folder={folder}
|
|
455
|
-
mailId={mailId}
|
|
456
|
-
/>
|
|
457
|
-
|
|
458
|
-
{mail ? (
|
|
459
|
-
<MailSendBox folder={folder} mailId={mailId} status={mail.status} />
|
|
460
|
-
) : null}
|
|
461
|
-
|
|
462
|
-
{!query.isPending && !query.error && !query.data?.mail ? (
|
|
463
|
-
<p className="m-0 text-sm text-[#6c7a6d]">Mail not found.</p>
|
|
464
|
-
) : null}
|
|
465
|
-
</div>
|
|
466
|
-
)
|
|
467
|
-
}
|