kennel 0.2.2 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +29 -3
- package/dist/cli/src/plugins/external.js +78 -24
- package/dist/cli/src/site/server-plugins.d.ts +2 -7
- package/dist/cli/src/site/viewer.js +1 -0
- package/dist/cli/usage.js +1 -1
- package/dist/core/command.js +1 -0
- package/dist/core/init.js +5 -5
- package/dist/core/runtime.js +357 -3207
- package/dist/package/preview.js +1232 -5113
- package/dist/plugins/external.d.ts +29 -3
- package/dist/plugins/external.js +78 -24
- package/dist/site/server-plugins.d.ts +2 -7
- package/dist/site/server-plugins.js +552 -10447
- package/dist/site/viewer.js +1 -0
- package/kernel/apps/web/package.json +1 -5
- package/kernel/apps/web/scripts/dev.mjs +53 -0
- package/kernel/apps/web/src/app.css +0 -3
- package/kernel/apps/web/src/main.tsx +0 -1
- package/kernel/apps/web/src/plugins.ts +0 -30
- package/kernel/apps/web/tsconfig.json +1 -2
- package/kernel/apps/web/vite.config.ts +31 -13
- package/kernel/packages/api/package.json +0 -4
- package/kernel/packages/api/src/server/folder-metadata-plugin.ts +13 -38
- package/kernel/packages/api/src/server/index.ts +0 -1
- package/kernel/packages/api/src/server/plugins.ts +49 -16
- package/kernel/packages/api/test/core.test.ts +13 -25
- package/kernel/packages/markdown/README.md +1 -1
- package/package.json +1 -1
- 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/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/package.json +0 -79
- 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/projects/tsconfig.json +0 -17
- 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
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { RefreshCw } from "lucide-react"
|
|
2
|
-
|
|
3
|
-
import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
|
|
4
|
-
import { folderLabel } from "@raggle-ai/mail/folders"
|
|
5
|
-
import {
|
|
6
|
-
MailFolderFilter,
|
|
7
|
-
mailItemContext,
|
|
8
|
-
MailList,
|
|
9
|
-
} from "@raggle-ai/mail/viewer"
|
|
10
|
-
import { CopyContextButton } from "@raggle-ai/ui/components/copy-context"
|
|
11
|
-
import { useFolderFilter } from "@raggle-ai/ui/hooks/use-folder-filter"
|
|
12
|
-
|
|
13
|
-
import { useMailList } from "./mail-hooks.js"
|
|
14
|
-
import { mailErrorText } from "./mail-request.js"
|
|
15
|
-
|
|
16
|
-
export function MailPage({
|
|
17
|
-
folder,
|
|
18
|
-
href,
|
|
19
|
-
}: Pick<FolderPluginViewProps, "folder" | "href">) {
|
|
20
|
-
const query = useMailList(folder)
|
|
21
|
-
|
|
22
|
-
const items = query.data?.items ?? []
|
|
23
|
-
const hasItems = Boolean(query.data?.items.length)
|
|
24
|
-
// Shared folder-filter state: derives the chips from the drafts' owning
|
|
25
|
-
// folders and filters the list. A selection whose folder no longer has
|
|
26
|
-
// drafts falls back to All folders rather than showing a dead-end list.
|
|
27
|
-
const { choices, effectiveSelected, setSelected, total, visibleItems } =
|
|
28
|
-
useFolderFilter(items, {
|
|
29
|
-
getFolder: (item) => item.folder,
|
|
30
|
-
rootFolder: folder ?? "",
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
if (!folder) {
|
|
34
|
-
return (
|
|
35
|
-
<div
|
|
36
|
-
className="mx-auto grid w-full max-w-3xl min-w-0 grid-cols-1 gap-4 px-4
|
|
37
|
-
py-5"
|
|
38
|
-
>
|
|
39
|
-
<p className="m-0 text-sm text-[#6c7a6d]">Open mail from a folder.</p>
|
|
40
|
-
</div>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div
|
|
46
|
-
className="mx-auto grid w-full max-w-3xl min-w-0 grid-cols-1 gap-4 px-4
|
|
47
|
-
py-5"
|
|
48
|
-
>
|
|
49
|
-
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
50
|
-
<h1 className="m-0 text-2xl font-semibold text-[#233025]">Mail</h1>
|
|
51
|
-
<button
|
|
52
|
-
type="button"
|
|
53
|
-
className="inline-flex items-center gap-2 rounded-md border px-3
|
|
54
|
-
py-1.5 text-sm disabled:pointer-events-none disabled:opacity-50"
|
|
55
|
-
onClick={() => void query.refetch()}
|
|
56
|
-
disabled={query.isFetching}
|
|
57
|
-
>
|
|
58
|
-
<RefreshCw
|
|
59
|
-
className={query.isFetching ? "size-4 animate-spin" : "size-4"}
|
|
60
|
-
/>
|
|
61
|
-
Refresh
|
|
62
|
-
</button>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
{hasItems ? (
|
|
66
|
-
<MailFolderFilter
|
|
67
|
-
choices={choices}
|
|
68
|
-
onSelect={setSelected}
|
|
69
|
-
selected={effectiveSelected}
|
|
70
|
-
total={total}
|
|
71
|
-
/>
|
|
72
|
-
) : null}
|
|
73
|
-
|
|
74
|
-
{query.isPending ? (
|
|
75
|
-
<p className="m-0 text-sm text-[#6c7a6d]">Loading…</p>
|
|
76
|
-
) : null}
|
|
77
|
-
{query.error ? (
|
|
78
|
-
<p className="m-0 text-sm text-[#9c3a3a]">
|
|
79
|
-
{mailErrorText(query.error)}
|
|
80
|
-
</p>
|
|
81
|
-
) : null}
|
|
82
|
-
|
|
83
|
-
{!query.isPending && !query.error && !query.data?.items.length ? (
|
|
84
|
-
<p className="m-0 text-sm text-[#6c7a6d]">No mail found.</p>
|
|
85
|
-
) : null}
|
|
86
|
-
|
|
87
|
-
{hasItems ? (
|
|
88
|
-
visibleItems.length ? (
|
|
89
|
-
<MailList
|
|
90
|
-
actions={(item) => (
|
|
91
|
-
<CopyContextButton
|
|
92
|
-
className="text-[#6c7a6d] hover:text-[#2f382f]"
|
|
93
|
-
context={() => mailItemContext(item)}
|
|
94
|
-
label={`Copy context for ${item.subject ?? "this draft"}`}
|
|
95
|
-
/>
|
|
96
|
-
)}
|
|
97
|
-
folderLabel={(mailFolder) => folderLabel(mailFolder, folder)}
|
|
98
|
-
items={visibleItems}
|
|
99
|
-
mailHref={href}
|
|
100
|
-
/>
|
|
101
|
-
) : (
|
|
102
|
-
<p className="m-0 text-sm text-[#6c7a6d]">
|
|
103
|
-
No drafts in this folder.
|
|
104
|
-
</p>
|
|
105
|
-
)
|
|
106
|
-
) : null}
|
|
107
|
-
</div>
|
|
108
|
-
)
|
|
109
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
export type MailRequestOptions = {
|
|
2
|
-
body?: unknown
|
|
3
|
-
method?: "GET" | "POST" | "DELETE"
|
|
4
|
-
search?: Record<string, string | undefined>
|
|
5
|
-
signal?: AbortSignal
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class MailRequestFailure extends Error {
|
|
9
|
-
readonly status: number
|
|
10
|
-
|
|
11
|
-
constructor(status: number, message: string) {
|
|
12
|
-
super(message)
|
|
13
|
-
this.name = "MailRequestFailure"
|
|
14
|
-
this.status = status
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function structuredMessage(text: string) {
|
|
19
|
-
try {
|
|
20
|
-
const parsed = JSON.parse(text) as {
|
|
21
|
-
error?: { code?: string; message?: string }
|
|
22
|
-
}
|
|
23
|
-
if (parsed.error?.message) return parsed.error.message
|
|
24
|
-
} catch {
|
|
25
|
-
// Non-JSON error bodies fall back to the raw text.
|
|
26
|
-
}
|
|
27
|
-
return text.trim() || "Mail request failed."
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Shared typed request helper for the mail API. GET requests never carry a
|
|
32
|
-
* body (the server forbids mutations over GET); mutations send JSON which the
|
|
33
|
-
* server's same-origin guard accepts only for same-site pages. JSON responses
|
|
34
|
-
* are enforced and structured server errors become readable messages.
|
|
35
|
-
*/
|
|
36
|
-
export async function mailRequest<T>(
|
|
37
|
-
path: string,
|
|
38
|
-
options: MailRequestOptions = {},
|
|
39
|
-
): Promise<T> {
|
|
40
|
-
const search = new URLSearchParams()
|
|
41
|
-
for (const [key, value] of Object.entries(options.search ?? {})) {
|
|
42
|
-
if (value !== undefined) search.set(key, value)
|
|
43
|
-
}
|
|
44
|
-
const query = search.toString()
|
|
45
|
-
const url = `${path}${query ? `?${query}` : ""}`
|
|
46
|
-
|
|
47
|
-
const headers: Record<string, string> = {}
|
|
48
|
-
let body: string | undefined
|
|
49
|
-
if (options.body !== undefined) {
|
|
50
|
-
headers["content-type"] = "application/json"
|
|
51
|
-
body = JSON.stringify(options.body)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const response = await fetch(url, {
|
|
55
|
-
body,
|
|
56
|
-
headers,
|
|
57
|
-
method: options.method ?? "GET",
|
|
58
|
-
signal: options.signal,
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
const text = await response.text()
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
throw new MailRequestFailure(response.status, structuredMessage(text))
|
|
64
|
-
}
|
|
65
|
-
if (!response.headers.get("content-type")?.includes("application/json")) {
|
|
66
|
-
throw new MailRequestFailure(
|
|
67
|
-
response.status,
|
|
68
|
-
"The server returned a non-JSON response.",
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
return JSON.parse(text) as T
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function mailErrorText(error: unknown) {
|
|
75
|
-
if (error instanceof MailRequestFailure) return error.message
|
|
76
|
-
if (error instanceof Error) return error.message
|
|
77
|
-
return "Mail request failed."
|
|
78
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react"
|
|
2
|
-
import { CheckCircle2, Loader2, Send, XCircle } from "lucide-react"
|
|
3
|
-
|
|
4
|
-
import type { MailSendPreviewPayload } from "./mail-hooks.js"
|
|
5
|
-
import { useMailSend } from "./mail-hooks.js"
|
|
6
|
-
import { mailErrorText } from "./mail-request.js"
|
|
7
|
-
|
|
8
|
-
type SendPhase =
|
|
9
|
-
"idle" | "previewing" | "confirm" | "sending" | "sent" | "failed"
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The preview-then-confirm send flow for one draft. State resets when the
|
|
13
|
-
* active draft changes so navigation never carries a stale preview, and every
|
|
14
|
-
* action disables the buttons while a request is in flight (double
|
|
15
|
-
* submissions cannot start a send twice).
|
|
16
|
-
*/
|
|
17
|
-
export function MailSendBox({
|
|
18
|
-
folder,
|
|
19
|
-
mailId,
|
|
20
|
-
status,
|
|
21
|
-
}: {
|
|
22
|
-
folder: string | null | undefined
|
|
23
|
-
mailId: string | null | undefined
|
|
24
|
-
status: string | null | undefined
|
|
25
|
-
}) {
|
|
26
|
-
const [phase, setPhase] = useState<SendPhase>("idle")
|
|
27
|
-
const [preview, setPreview] = useState<MailSendPreviewPayload | null>(null)
|
|
28
|
-
const [error, setError] = useState<string | null>(null)
|
|
29
|
-
const send = useMailSend(folder)
|
|
30
|
-
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
setPhase("idle")
|
|
33
|
-
setPreview(null)
|
|
34
|
-
setError(null)
|
|
35
|
-
}, [mailId])
|
|
36
|
-
|
|
37
|
-
const pending =
|
|
38
|
-
send.isPending || phase === "previewing" || phase === "sending"
|
|
39
|
-
|
|
40
|
-
async function runPreview() {
|
|
41
|
-
if (!mailId || pending) return
|
|
42
|
-
setError(null)
|
|
43
|
-
setPhase("previewing")
|
|
44
|
-
try {
|
|
45
|
-
const result = await send.mutateAsync({ dryRun: true, id: mailId })
|
|
46
|
-
if (!("revision" in result)) {
|
|
47
|
-
throw new Error("MailOS did not return a revision.")
|
|
48
|
-
}
|
|
49
|
-
setPreview(result)
|
|
50
|
-
setPhase("confirm")
|
|
51
|
-
} catch (cause) {
|
|
52
|
-
setError(mailErrorText(cause))
|
|
53
|
-
setPhase("failed")
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function runSend() {
|
|
58
|
-
if (!mailId || !preview || pending) return
|
|
59
|
-
setError(null)
|
|
60
|
-
setPhase("sending")
|
|
61
|
-
try {
|
|
62
|
-
await send.mutateAsync({
|
|
63
|
-
dryRun: false,
|
|
64
|
-
id: mailId,
|
|
65
|
-
revision: preview.revision,
|
|
66
|
-
})
|
|
67
|
-
setPhase("sent")
|
|
68
|
-
} catch (cause) {
|
|
69
|
-
setError(mailErrorText(cause))
|
|
70
|
-
setPhase("failed")
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<div
|
|
76
|
-
className="rounded-xl border border-[#e7ebe7] bg-white px-4 py-3
|
|
77
|
-
shadow-[0_1px_2px_rgb(31_41_55/0.05)]"
|
|
78
|
-
>
|
|
79
|
-
{phase === "idle" ? (
|
|
80
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
81
|
-
<button
|
|
82
|
-
type="button"
|
|
83
|
-
className="inline-flex w-fit items-center gap-2 rounded-md
|
|
84
|
-
bg-[#395641] px-3 py-1.5 text-sm text-white hover:bg-[#2f4b38]
|
|
85
|
-
disabled:pointer-events-none disabled:opacity-50"
|
|
86
|
-
onClick={() => void runPreview()}
|
|
87
|
-
disabled={status === "sent" || pending}
|
|
88
|
-
>
|
|
89
|
-
<Send className="size-4" />
|
|
90
|
-
{status === "sent" ? "Already sent" : "Send"}
|
|
91
|
-
</button>
|
|
92
|
-
<span className="text-[0.82rem] text-[#6c7a6d]">
|
|
93
|
-
Will preview for confirmation before delivering.
|
|
94
|
-
</span>
|
|
95
|
-
</div>
|
|
96
|
-
) : null}
|
|
97
|
-
|
|
98
|
-
{phase === "previewing" || phase === "sending" ? (
|
|
99
|
-
<p className="m-0 flex items-center gap-2 text-sm text-[#4a554b]">
|
|
100
|
-
<Loader2 className="size-4 animate-spin" />
|
|
101
|
-
{phase === "previewing" ? "Previewing…" : "Sending…"}
|
|
102
|
-
</p>
|
|
103
|
-
) : null}
|
|
104
|
-
|
|
105
|
-
{phase === "confirm" && preview ? (
|
|
106
|
-
<div className="grid gap-3">
|
|
107
|
-
<p className="m-0 text-sm text-[#4a554b]">
|
|
108
|
-
Ready to send this draft to its recipients.
|
|
109
|
-
</p>
|
|
110
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
111
|
-
<button
|
|
112
|
-
type="button"
|
|
113
|
-
className="inline-flex w-fit items-center gap-2 rounded-md
|
|
114
|
-
bg-[#395641] px-3 py-1.5 text-sm text-white hover:bg-[#2f4b38]
|
|
115
|
-
disabled:pointer-events-none disabled:opacity-50"
|
|
116
|
-
onClick={() => void runSend()}
|
|
117
|
-
disabled={pending}
|
|
118
|
-
>
|
|
119
|
-
<Send className="size-4" />
|
|
120
|
-
Send now
|
|
121
|
-
</button>
|
|
122
|
-
<button
|
|
123
|
-
type="button"
|
|
124
|
-
className="w-fit rounded-md border border-[#dde1dc] bg-white px-3
|
|
125
|
-
py-1.5 text-sm text-[#4a554b] hover:bg-[#f3f5f2]
|
|
126
|
-
disabled:pointer-events-none disabled:opacity-50"
|
|
127
|
-
onClick={() => {
|
|
128
|
-
setPhase("idle")
|
|
129
|
-
setPreview(null)
|
|
130
|
-
}}
|
|
131
|
-
disabled={pending}
|
|
132
|
-
>
|
|
133
|
-
Cancel
|
|
134
|
-
</button>
|
|
135
|
-
</div>
|
|
136
|
-
<details className="text-[0.82rem] text-[#6c7a6d]">
|
|
137
|
-
<summary className="cursor-pointer">Preview output</summary>
|
|
138
|
-
<pre
|
|
139
|
-
className="mt-2 max-h-48 overflow-auto rounded-md bg-[#f3f5f2] p-2
|
|
140
|
-
text-[0.75rem] whitespace-pre-wrap text-[#4a554b]"
|
|
141
|
-
>
|
|
142
|
-
{preview.output}
|
|
143
|
-
</pre>
|
|
144
|
-
</details>
|
|
145
|
-
</div>
|
|
146
|
-
) : null}
|
|
147
|
-
|
|
148
|
-
{phase === "sent" ? (
|
|
149
|
-
<p className="m-0 flex items-center gap-2 text-sm text-[#2f5b3a]">
|
|
150
|
-
<CheckCircle2 className="size-4" />
|
|
151
|
-
Sent.
|
|
152
|
-
</p>
|
|
153
|
-
) : null}
|
|
154
|
-
|
|
155
|
-
{phase === "failed" ? (
|
|
156
|
-
<div className="grid gap-3">
|
|
157
|
-
<p className="m-0 flex items-center gap-2 text-sm text-[#9c3a3a]">
|
|
158
|
-
<XCircle className="size-4 shrink-0" />
|
|
159
|
-
<span className="break-words">{error}</span>
|
|
160
|
-
</p>
|
|
161
|
-
<button
|
|
162
|
-
type="button"
|
|
163
|
-
className="inline-flex w-fit items-center gap-2 rounded-md border
|
|
164
|
-
border-[#dde1dc] bg-white px-3 py-1.5 text-sm text-[#4a554b]
|
|
165
|
-
hover:bg-[#f3f5f2]"
|
|
166
|
-
onClick={() => void runPreview()}
|
|
167
|
-
>
|
|
168
|
-
<Send className="size-4" />
|
|
169
|
-
Try again
|
|
170
|
-
</button>
|
|
171
|
-
</div>
|
|
172
|
-
) : null}
|
|
173
|
-
</div>
|
|
174
|
-
)
|
|
175
|
-
}
|