kennel 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +75 -21
- 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 +28 -2
- package/dist/plugins/external.js +75 -21
- 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,160 +0,0 @@
|
|
|
1
|
-
import { readdir, readFile } from "node:fs/promises"
|
|
2
|
-
import { basename, dirname, relative, resolve, sep } from "node:path"
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
FolderPluginContext,
|
|
6
|
-
FolderPluginData,
|
|
7
|
-
FolderPluginDetection,
|
|
8
|
-
} from "@raggle-ai/folder-plugin/server"
|
|
9
|
-
|
|
10
|
-
import type { CalendarSource } from "./parse.ts"
|
|
11
|
-
import { calendarManifest } from "./manifest.ts"
|
|
12
|
-
import {
|
|
13
|
-
calendarIssueSummary,
|
|
14
|
-
eventCount,
|
|
15
|
-
mergeCalendars,
|
|
16
|
-
parseCalendarJson,
|
|
17
|
-
parseCalendarSource,
|
|
18
|
-
starterCalendarSource,
|
|
19
|
-
} from "./parse.ts"
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Recursively discover valid `calendar.json` sources under a folder.
|
|
23
|
-
* Hidden directories and `node_modules` are skipped, and directory symlinks
|
|
24
|
-
* are not followed, so discovered files stay contained inside the folder.
|
|
25
|
-
*
|
|
26
|
-
* Returns every found file with its validated issues (empty for valid files)
|
|
27
|
-
* plus the tolerated calendar for valid files. A file that triggers
|
|
28
|
-
* validation issues still parses tolerantly when possible, so the view can
|
|
29
|
-
* show the good events while listing what is wrong.
|
|
30
|
-
*/
|
|
31
|
-
type CalendarSourceLoad = {
|
|
32
|
-
issues: Array<{
|
|
33
|
-
file: string
|
|
34
|
-
problems: Array<{ path: string; message: string }>
|
|
35
|
-
}>
|
|
36
|
-
sources: CalendarSource[]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function loadSources(folder: string): Promise<CalendarSourceLoad> {
|
|
40
|
-
const visit = async (current: string): Promise<string[]> => {
|
|
41
|
-
const entries = await readdir(current, { withFileTypes: true }).catch(
|
|
42
|
-
() => [],
|
|
43
|
-
)
|
|
44
|
-
const paths: string[] = []
|
|
45
|
-
for (const entry of entries) {
|
|
46
|
-
if (entry.isFile() && entry.name === "calendar.json") {
|
|
47
|
-
paths.push(resolve(current, entry.name))
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
if (
|
|
51
|
-
!entry.isDirectory() ||
|
|
52
|
-
entry.name.startsWith(".") ||
|
|
53
|
-
entry.name === "node_modules"
|
|
54
|
-
)
|
|
55
|
-
continue
|
|
56
|
-
paths.push(...(await visit(resolve(current, entry.name))))
|
|
57
|
-
}
|
|
58
|
-
return paths
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const paths = (await visit(folder)).sort((left, right) =>
|
|
62
|
-
relative(folder, left).localeCompare(relative(folder, right)),
|
|
63
|
-
)
|
|
64
|
-
const issues: CalendarSourceLoad["issues"] = []
|
|
65
|
-
const sources: CalendarSource[] = []
|
|
66
|
-
await Promise.all(
|
|
67
|
-
paths.map(async (path) => {
|
|
68
|
-
const sourceText = await readFile(path, "utf8")
|
|
69
|
-
const file = relative(folder, path).split(sep).join("/")
|
|
70
|
-
const validation = parseCalendarSource(sourceText)
|
|
71
|
-
if (!validation.ok) {
|
|
72
|
-
issues.push({ file, problems: validation.issues })
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
const calendar = parseCalendarJson(sourceText)
|
|
76
|
-
if (!calendar) {
|
|
77
|
-
issues.push({
|
|
78
|
-
file,
|
|
79
|
-
problems: [{ path: "#", message: "could not be read as a calendar" }],
|
|
80
|
-
})
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
const directory = dirname(file)
|
|
84
|
-
sources.push({
|
|
85
|
-
calendar,
|
|
86
|
-
folder: dirname(path),
|
|
87
|
-
group: directory === "." ? basename(folder) : directory,
|
|
88
|
-
path: file,
|
|
89
|
-
})
|
|
90
|
-
}),
|
|
91
|
-
)
|
|
92
|
-
issues.sort((left, right) => left.file.localeCompare(right.file))
|
|
93
|
-
return { issues, sources }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function flattenIssues(load: CalendarSourceLoad) {
|
|
97
|
-
return load.issues.flatMap(({ file, problems }) =>
|
|
98
|
-
problems.map((problem) => ({
|
|
99
|
-
path: problem.path === "#" ? file : `${file}: ${problem.path}`,
|
|
100
|
-
message: problem.message,
|
|
101
|
-
})),
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function detectionFor(load: CalendarSourceLoad) {
|
|
106
|
-
const noContent = load.sources.length === 0 && load.issues.length === 0
|
|
107
|
-
if (noContent) return { status: "unavailable" as const }
|
|
108
|
-
if (load.issues.length > 0) {
|
|
109
|
-
return {
|
|
110
|
-
status: "error" as const,
|
|
111
|
-
error: calendarIssueSummary(flattenIssues(load)),
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return {
|
|
115
|
-
count: eventCount(mergeCalendars(load.sources)),
|
|
116
|
-
status: "available" as const,
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function createCalendarPlugin() {
|
|
121
|
-
return {
|
|
122
|
-
...calendarManifest,
|
|
123
|
-
async detect(context: FolderPluginContext): Promise<FolderPluginDetection> {
|
|
124
|
-
return detectionFor(await loadSources(context.folder))
|
|
125
|
-
},
|
|
126
|
-
async handle(
|
|
127
|
-
context: FolderPluginContext,
|
|
128
|
-
subpath: string,
|
|
129
|
-
): Promise<FolderPluginData> {
|
|
130
|
-
if (subpath !== "") return { kind: "unavailable" }
|
|
131
|
-
|
|
132
|
-
const load = await loadSources(context.folder)
|
|
133
|
-
const detection = detectionFor(load)
|
|
134
|
-
if (detection.status === "unavailable") return { kind: "unavailable" }
|
|
135
|
-
const calendar = mergeCalendars(load.sources)
|
|
136
|
-
const issues = flattenIssues(load)
|
|
137
|
-
return {
|
|
138
|
-
kind: "available",
|
|
139
|
-
data: {
|
|
140
|
-
calendar: {
|
|
141
|
-
count: calendar.events.length,
|
|
142
|
-
items: calendar.events,
|
|
143
|
-
startDate: calendar.startDate,
|
|
144
|
-
title: calendar.title,
|
|
145
|
-
},
|
|
146
|
-
folder: context.folder,
|
|
147
|
-
issues: issues.length > 0 ? issues : undefined,
|
|
148
|
-
// Broken files are flagged but excluded from the rendered dataset;
|
|
149
|
-
// valid sibling files still render next to the error panel.
|
|
150
|
-
status: issues.length > 0 ? "error" : "available",
|
|
151
|
-
},
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
async init(context: FolderPluginContext) {
|
|
155
|
-
return {
|
|
156
|
-
files: [{ path: "calendar.json", content: starterCalendarSource() }],
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
}
|
|
160
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { CalendarColor, CalendarEvent } from "./parse"
|
|
2
|
-
|
|
3
|
-
type CalendarTone = {
|
|
4
|
-
badge: string
|
|
5
|
-
dot: string
|
|
6
|
-
selected: string
|
|
7
|
-
soft: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const tones: Record<CalendarColor, CalendarTone> = {
|
|
11
|
-
blue: {
|
|
12
|
-
badge:
|
|
13
|
-
"border-blue-300 bg-blue-50 text-blue-800 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-200",
|
|
14
|
-
dot: "bg-blue-500",
|
|
15
|
-
selected:
|
|
16
|
-
"border-blue-600 bg-blue-600 !text-white shadow-sm hover:bg-blue-700",
|
|
17
|
-
soft: "border-blue-200 bg-blue-50 text-blue-800 hover:bg-blue-100 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-200",
|
|
18
|
-
},
|
|
19
|
-
green: {
|
|
20
|
-
badge:
|
|
21
|
-
"border-emerald-300 bg-emerald-50 text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-200",
|
|
22
|
-
dot: "bg-emerald-500",
|
|
23
|
-
selected:
|
|
24
|
-
"border-emerald-600 bg-emerald-600 !text-white shadow-sm hover:bg-emerald-700",
|
|
25
|
-
soft: "border-emerald-200 bg-emerald-50 text-emerald-800 hover:bg-emerald-100 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200",
|
|
26
|
-
},
|
|
27
|
-
orange: {
|
|
28
|
-
badge:
|
|
29
|
-
"border-orange-300 bg-orange-50 text-orange-800 dark:border-orange-800 dark:bg-orange-950 dark:text-orange-200",
|
|
30
|
-
dot: "bg-orange-500",
|
|
31
|
-
selected:
|
|
32
|
-
"border-orange-600 bg-orange-600 !text-white shadow-sm hover:bg-orange-700",
|
|
33
|
-
soft: "border-orange-200 bg-orange-50 text-orange-800 hover:bg-orange-100 dark:border-orange-900 dark:bg-orange-950 dark:text-orange-200",
|
|
34
|
-
},
|
|
35
|
-
purple: {
|
|
36
|
-
badge:
|
|
37
|
-
"border-purple-300 bg-purple-50 text-purple-800 dark:border-purple-800 dark:bg-purple-950 dark:text-purple-200",
|
|
38
|
-
dot: "bg-purple-500",
|
|
39
|
-
selected:
|
|
40
|
-
"border-purple-600 bg-purple-600 !text-white shadow-sm hover:bg-purple-700",
|
|
41
|
-
soft: "border-purple-200 bg-purple-50 text-purple-800 hover:bg-purple-100 dark:border-purple-900 dark:bg-purple-950 dark:text-purple-200",
|
|
42
|
-
},
|
|
43
|
-
red: {
|
|
44
|
-
badge:
|
|
45
|
-
"border-red-300 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200",
|
|
46
|
-
dot: "bg-red-500",
|
|
47
|
-
selected:
|
|
48
|
-
"border-red-600 bg-red-600 !text-white shadow-sm hover:bg-red-700",
|
|
49
|
-
soft: "border-red-200 bg-red-50 text-red-800 hover:bg-red-100 dark:border-red-900 dark:bg-red-950 dark:text-red-200",
|
|
50
|
-
},
|
|
51
|
-
yellow: {
|
|
52
|
-
badge:
|
|
53
|
-
"border-amber-300 bg-amber-50 text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100",
|
|
54
|
-
dot: "bg-amber-500",
|
|
55
|
-
selected:
|
|
56
|
-
"border-amber-500 bg-amber-500 !text-amber-950 shadow-sm hover:bg-amber-600",
|
|
57
|
-
soft: "border-amber-200 bg-amber-50 text-amber-900 hover:bg-amber-100 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100",
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function calendarTone(color?: CalendarColor) {
|
|
62
|
-
return tones[color ?? "blue"]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function eventTone(event: CalendarEvent) {
|
|
66
|
-
return calendarTone(event.color ?? event.groupColor)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function cn(...values: Array<false | null | string | undefined>) {
|
|
70
|
-
return values.filter(Boolean).join(" ")
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function eventSpan(event: CalendarEvent) {
|
|
74
|
-
return {
|
|
75
|
-
allDay:
|
|
76
|
-
event.allDay ?? event.end.getTime() - event.start.getTime() >= 86_399_000,
|
|
77
|
-
end: event.end,
|
|
78
|
-
start: event.start,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react"
|
|
2
|
-
import { useEffect, useRef } from "react"
|
|
3
|
-
import {
|
|
4
|
-
addDays,
|
|
5
|
-
format,
|
|
6
|
-
isSameDay,
|
|
7
|
-
isSameMonth,
|
|
8
|
-
startOfDay,
|
|
9
|
-
startOfMonth,
|
|
10
|
-
startOfWeek,
|
|
11
|
-
} from "date-fns"
|
|
12
|
-
import { CalendarDays } from "lucide-react"
|
|
13
|
-
|
|
14
|
-
import type { CalendarEvent } from "./parse"
|
|
15
|
-
import { EventDialog } from "./event-dialog"
|
|
16
|
-
import { cn, eventSpan, eventTone } from "./utils"
|
|
17
|
-
|
|
18
|
-
const weekdays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
|
|
19
|
-
const weekStart = { weekStartsOn: 1 as const }
|
|
20
|
-
const hourHeight = 48
|
|
21
|
-
|
|
22
|
-
type LaneItem = {
|
|
23
|
-
endCol: number // inclusive last covered column
|
|
24
|
-
event: CalendarEvent
|
|
25
|
-
lane: number
|
|
26
|
-
startCol: number
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Place events intersecting one week row into horizontal lanes. Multi-day events
|
|
30
|
-
// keep one lane across their covered columns so they render as a single bar.
|
|
31
|
-
function layoutLanes(days: Date[], events: CalendarEvent[]): LaneItem[] {
|
|
32
|
-
if (days.length === 0) return []
|
|
33
|
-
const first = startOfDay(days[0]!)
|
|
34
|
-
const last = addDays(startOfDay(days[days.length - 1]!), 1)
|
|
35
|
-
const relevant = events
|
|
36
|
-
.filter((event) => event.end > first && event.start < last)
|
|
37
|
-
.sort(
|
|
38
|
-
(left, right) =>
|
|
39
|
-
left.start.getTime() - right.start.getTime() ||
|
|
40
|
-
right.end.getTime() -
|
|
41
|
-
right.start.getTime() -
|
|
42
|
-
(left.end.getTime() - left.start.getTime()),
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
const laneEnds: number[] = []
|
|
46
|
-
const items: LaneItem[] = []
|
|
47
|
-
for (const event of relevant) {
|
|
48
|
-
const startCol = Math.max(
|
|
49
|
-
0,
|
|
50
|
-
days.findIndex(
|
|
51
|
-
(day) => day.getTime() >= startOfDay(event.start).getTime(),
|
|
52
|
-
),
|
|
53
|
-
)
|
|
54
|
-
let endCol = startCol
|
|
55
|
-
for (let index = startCol; index < days.length; index++) {
|
|
56
|
-
if (days[index]!.getTime() >= event.end.getTime()) break
|
|
57
|
-
endCol = index
|
|
58
|
-
}
|
|
59
|
-
let lane = laneEnds.findIndex((end) => end <= startCol)
|
|
60
|
-
if (lane === -1) {
|
|
61
|
-
lane = laneEnds.length
|
|
62
|
-
laneEnds.push(-1)
|
|
63
|
-
}
|
|
64
|
-
laneEnds[lane] = endCol + 1
|
|
65
|
-
items.push({ endCol, event, lane, startCol })
|
|
66
|
-
}
|
|
67
|
-
return items
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function LaneGrid({ columns, items }: { columns: number; items: LaneItem[] }) {
|
|
71
|
-
if (items.length === 0) return null
|
|
72
|
-
const lanes = Math.max(...items.map((item) => item.lane)) + 1
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<div
|
|
76
|
-
className="relative grid"
|
|
77
|
-
style={{
|
|
78
|
-
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
79
|
-
gridTemplateRows: `repeat(${lanes}, auto)`,
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
{items.map((item) => (
|
|
83
|
-
<div
|
|
84
|
-
className="min-w-0 pr-0.5 pb-0.5"
|
|
85
|
-
key={item.event.id}
|
|
86
|
-
style={{
|
|
87
|
-
gridColumn: `${item.startCol + 1} / ${item.endCol + 2}`,
|
|
88
|
-
gridRow: item.lane + 1,
|
|
89
|
-
}}
|
|
90
|
-
>
|
|
91
|
-
<EventDialog
|
|
92
|
-
event={item.event}
|
|
93
|
-
variant={item.endCol > item.startCol ? "bar" : "chip"}
|
|
94
|
-
/>
|
|
95
|
-
</div>
|
|
96
|
-
))}
|
|
97
|
-
</div>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function MonthView({
|
|
102
|
-
date,
|
|
103
|
-
events,
|
|
104
|
-
}: {
|
|
105
|
-
date: Date
|
|
106
|
-
events: CalendarEvent[]
|
|
107
|
-
}) {
|
|
108
|
-
const monthStart = startOfMonth(date)
|
|
109
|
-
const start = startOfWeek(monthStart, weekStart)
|
|
110
|
-
const weeks = Array.from({ length: 6 }, (_, week) =>
|
|
111
|
-
Array.from({ length: 7 }, (_, day) => addDays(start, week * 7 + day)),
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<div>
|
|
116
|
-
<div className="grid grid-cols-7">
|
|
117
|
-
{weekdays.map((label) => (
|
|
118
|
-
<div
|
|
119
|
-
className="text-muted-foreground border-b py-1 text-center text-xs
|
|
120
|
-
font-medium"
|
|
121
|
-
key={label}
|
|
122
|
-
>
|
|
123
|
-
{label}
|
|
124
|
-
</div>
|
|
125
|
-
))}
|
|
126
|
-
</div>
|
|
127
|
-
{weeks.map((days) => (
|
|
128
|
-
<MonthWeek
|
|
129
|
-
days={days}
|
|
130
|
-
events={events}
|
|
131
|
-
key={days[0]!.toISOString()}
|
|
132
|
-
monthStart={monthStart}
|
|
133
|
-
/>
|
|
134
|
-
))}
|
|
135
|
-
</div>
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function MonthWeek({
|
|
140
|
-
days,
|
|
141
|
-
events,
|
|
142
|
-
monthStart,
|
|
143
|
-
}: {
|
|
144
|
-
days: Date[]
|
|
145
|
-
events: CalendarEvent[]
|
|
146
|
-
monthStart: Date
|
|
147
|
-
}) {
|
|
148
|
-
const items = layoutLanes(days, events)
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<div className="min-h-20 border-b last:border-b-0">
|
|
152
|
-
<div className="grid grid-cols-7">
|
|
153
|
-
{days.map((day) => {
|
|
154
|
-
const isCurrentMonth = isSameMonth(day, monthStart)
|
|
155
|
-
const isToday = isSameDay(day, new Date())
|
|
156
|
-
return (
|
|
157
|
-
<div
|
|
158
|
-
className={cn(
|
|
159
|
-
"flex justify-end border-r px-1.5 py-1 last:border-r-0",
|
|
160
|
-
!isCurrentMonth && "bg-muted/30",
|
|
161
|
-
)}
|
|
162
|
-
key={day.toISOString()}
|
|
163
|
-
>
|
|
164
|
-
<span
|
|
165
|
-
className={cn(
|
|
166
|
-
"flex size-5 items-center justify-center rounded-full text-xs",
|
|
167
|
-
isToday
|
|
168
|
-
? "bg-primary text-primary-foreground font-semibold"
|
|
169
|
-
: "text-muted-foreground",
|
|
170
|
-
)}
|
|
171
|
-
>
|
|
172
|
-
{format(day, "d")}
|
|
173
|
-
</span>
|
|
174
|
-
</div>
|
|
175
|
-
)
|
|
176
|
-
})}
|
|
177
|
-
</div>
|
|
178
|
-
<div className="relative min-h-14 pt-0.5">
|
|
179
|
-
<div
|
|
180
|
-
aria-hidden="true"
|
|
181
|
-
className="pointer-events-none absolute inset-0 grid grid-cols-7"
|
|
182
|
-
>
|
|
183
|
-
{days.map((day) => (
|
|
184
|
-
<div
|
|
185
|
-
className={cn(
|
|
186
|
-
"border-r last:border-r-0",
|
|
187
|
-
!isSameMonth(day, monthStart) && "bg-muted/30",
|
|
188
|
-
)}
|
|
189
|
-
key={day.toISOString()}
|
|
190
|
-
/>
|
|
191
|
-
))}
|
|
192
|
-
</div>
|
|
193
|
-
<LaneGrid columns={days.length} items={items} />
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function eventsByHour(events: CalendarEvent[], day: Date) {
|
|
200
|
-
const start = startOfDay(day)
|
|
201
|
-
const next = addDays(start, 1)
|
|
202
|
-
const hours = new Map<number, CalendarEvent[]>()
|
|
203
|
-
for (const event of events.filter(
|
|
204
|
-
(item) => item.end > start && item.start < next && !eventSpan(item).allDay,
|
|
205
|
-
)) {
|
|
206
|
-
const first = Math.max(0, Math.floor(event.start.getHours()))
|
|
207
|
-
const last = Math.min(23, event.end.getHours())
|
|
208
|
-
for (let hour = first; hour <= last; hour++) {
|
|
209
|
-
const list = hours.get(hour) ?? []
|
|
210
|
-
list.push(event)
|
|
211
|
-
hours.set(hour, list)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return hours
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function HourGrid({
|
|
218
|
-
columns,
|
|
219
|
-
columnHeaders,
|
|
220
|
-
events,
|
|
221
|
-
}: {
|
|
222
|
-
columns: Date[]
|
|
223
|
-
columnHeaders?: ReactNode[]
|
|
224
|
-
events: CalendarEvent[]
|
|
225
|
-
}) {
|
|
226
|
-
const area = useRef<HTMLDivElement>(null)
|
|
227
|
-
const grid = columns.map((column) => ({
|
|
228
|
-
hours: eventsByHour(events, column),
|
|
229
|
-
}))
|
|
230
|
-
const allDayItems = layoutLanes(
|
|
231
|
-
columns,
|
|
232
|
-
events.filter((event) => eventSpan(event).allDay),
|
|
233
|
-
)
|
|
234
|
-
const hasAllDay = allDayItems.length > 0
|
|
235
|
-
const first = events
|
|
236
|
-
.filter((event) => !eventSpan(event).allDay)
|
|
237
|
-
.sort((left, right) => left.start.getTime() - right.start.getTime())[0]
|
|
238
|
-
|
|
239
|
-
useEffect(() => {
|
|
240
|
-
const frame = requestAnimationFrame(() => {
|
|
241
|
-
const viewport = area.current?.querySelector<HTMLElement>(
|
|
242
|
-
'[data-slot="scroll-area-viewport"]',
|
|
243
|
-
)
|
|
244
|
-
const event = viewport?.querySelector<HTMLElement>(
|
|
245
|
-
'[data-calendar-first-event="true"]',
|
|
246
|
-
)
|
|
247
|
-
if (!viewport || !event) return
|
|
248
|
-
|
|
249
|
-
const viewportRect = viewport.getBoundingClientRect()
|
|
250
|
-
const eventRect = event.getBoundingClientRect()
|
|
251
|
-
viewport.scrollTo({
|
|
252
|
-
behavior: "auto",
|
|
253
|
-
top: Math.max(
|
|
254
|
-
0,
|
|
255
|
-
viewport.scrollTop +
|
|
256
|
-
eventRect.top -
|
|
257
|
-
viewportRect.top -
|
|
258
|
-
viewport.clientHeight / 2 +
|
|
259
|
-
eventRect.height / 2,
|
|
260
|
-
),
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
return () => cancelAnimationFrame(frame)
|
|
265
|
-
}, [first?.id, first?.start])
|
|
266
|
-
|
|
267
|
-
return (
|
|
268
|
-
<div className="h-[calc(100vh-240px)] min-h-80" ref={area}>
|
|
269
|
-
<div
|
|
270
|
-
className="size-full overflow-auto rounded-[inherit]"
|
|
271
|
-
data-slot="scroll-area-viewport"
|
|
272
|
-
>
|
|
273
|
-
<div className="min-w-[600px]">
|
|
274
|
-
{columnHeaders ? (
|
|
275
|
-
<div
|
|
276
|
-
className="grid border-b"
|
|
277
|
-
style={{
|
|
278
|
-
gridTemplateColumns: `52px repeat(${columns.length}, 1fr)`,
|
|
279
|
-
}}
|
|
280
|
-
>
|
|
281
|
-
<div />
|
|
282
|
-
{columnHeaders.map((header, index) => (
|
|
283
|
-
<div
|
|
284
|
-
className="border-l px-2 py-2 font-medium"
|
|
285
|
-
key={columns[index]?.toISOString()}
|
|
286
|
-
>
|
|
287
|
-
{header}
|
|
288
|
-
</div>
|
|
289
|
-
))}
|
|
290
|
-
</div>
|
|
291
|
-
) : null}
|
|
292
|
-
{hasAllDay ? (
|
|
293
|
-
<div
|
|
294
|
-
className="grid border-b"
|
|
295
|
-
style={{
|
|
296
|
-
gridTemplateColumns: `52px 1fr`,
|
|
297
|
-
}}
|
|
298
|
-
>
|
|
299
|
-
<div
|
|
300
|
-
className="text-muted-foreground px-1 py-1 text-right
|
|
301
|
-
text-[0.65rem] uppercase"
|
|
302
|
-
>
|
|
303
|
-
All day
|
|
304
|
-
</div>
|
|
305
|
-
<div className="relative min-h-8 py-0.5">
|
|
306
|
-
<div
|
|
307
|
-
aria-hidden="true"
|
|
308
|
-
className="pointer-events-none absolute inset-0 grid"
|
|
309
|
-
style={{
|
|
310
|
-
gridTemplateColumns: `repeat(${columns.length}, 1fr)`,
|
|
311
|
-
}}
|
|
312
|
-
>
|
|
313
|
-
{columns.map((column) => (
|
|
314
|
-
<div className="border-l" key={column.toISOString()} />
|
|
315
|
-
))}
|
|
316
|
-
</div>
|
|
317
|
-
<LaneGrid columns={columns.length} items={allDayItems} />
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
) : null}
|
|
321
|
-
<div
|
|
322
|
-
className="grid"
|
|
323
|
-
style={{
|
|
324
|
-
gridTemplateColumns: `52px repeat(${columns.length}, 1fr)`,
|
|
325
|
-
}}
|
|
326
|
-
>
|
|
327
|
-
<div>
|
|
328
|
-
{Array.from({ length: 24 }, (_, hour) => (
|
|
329
|
-
<div
|
|
330
|
-
className="text-muted-foreground relative pr-2 text-right
|
|
331
|
-
text-[0.65rem]"
|
|
332
|
-
key={hour}
|
|
333
|
-
style={{ height: hourHeight }}
|
|
334
|
-
>
|
|
335
|
-
<span className="absolute top-0 right-1 -translate-y-1/2">
|
|
336
|
-
{hour === 0 ? "" : format(new Date(2020, 0, 1, hour), "ha")}
|
|
337
|
-
</span>
|
|
338
|
-
</div>
|
|
339
|
-
))}
|
|
340
|
-
</div>
|
|
341
|
-
{grid.map((column, index) => (
|
|
342
|
-
<div className="border-l" key={columns[index]?.toISOString()}>
|
|
343
|
-
{Array.from({ length: 24 }, (_, hour) => (
|
|
344
|
-
<div
|
|
345
|
-
className="relative border-t px-1"
|
|
346
|
-
key={hour}
|
|
347
|
-
style={{ height: hourHeight }}
|
|
348
|
-
>
|
|
349
|
-
{column.hours.get(hour)?.map((event) => (
|
|
350
|
-
<div
|
|
351
|
-
className={cn(
|
|
352
|
-
"mb-0.5 truncate rounded border-l-2 text-xs last:mb-0",
|
|
353
|
-
eventTone(event).badge,
|
|
354
|
-
)}
|
|
355
|
-
data-calendar-first-event={
|
|
356
|
-
event.id === first?.id ? "true" : undefined
|
|
357
|
-
}
|
|
358
|
-
key={event.id}
|
|
359
|
-
>
|
|
360
|
-
<EventDialog event={event} />
|
|
361
|
-
</div>
|
|
362
|
-
)) ?? null}
|
|
363
|
-
</div>
|
|
364
|
-
))}
|
|
365
|
-
</div>
|
|
366
|
-
))}
|
|
367
|
-
</div>
|
|
368
|
-
</div>
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
)
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
export function DayView({
|
|
375
|
-
date,
|
|
376
|
-
events,
|
|
377
|
-
}: {
|
|
378
|
-
date: Date
|
|
379
|
-
events: CalendarEvent[]
|
|
380
|
-
}) {
|
|
381
|
-
return (
|
|
382
|
-
<HourGrid
|
|
383
|
-
columnHeaders={[
|
|
384
|
-
<div className="flex items-center gap-1.5" key="day">
|
|
385
|
-
<CalendarDays aria-hidden="true" className="size-4" />
|
|
386
|
-
{format(date, "EEE MMM d")}
|
|
387
|
-
</div>,
|
|
388
|
-
]}
|
|
389
|
-
columns={[startOfDay(date)]}
|
|
390
|
-
events={events}
|
|
391
|
-
/>
|
|
392
|
-
)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export function WeekView({
|
|
396
|
-
date,
|
|
397
|
-
events,
|
|
398
|
-
}: {
|
|
399
|
-
date: Date
|
|
400
|
-
events: CalendarEvent[]
|
|
401
|
-
}) {
|
|
402
|
-
const start = startOfWeek(date, weekStart)
|
|
403
|
-
const days = Array.from({ length: 7 }, (_, index) => addDays(start, index))
|
|
404
|
-
const headers = days.map((day) => (
|
|
405
|
-
<div
|
|
406
|
-
className={cn(
|
|
407
|
-
"leading-tight",
|
|
408
|
-
isSameDay(day, new Date()) && "text-primary",
|
|
409
|
-
)}
|
|
410
|
-
key={day.toISOString()}
|
|
411
|
-
>
|
|
412
|
-
<strong>{format(day, "EEE")}</strong>{" "}
|
|
413
|
-
<span className="text-muted-foreground">{format(day, "d")}</span>
|
|
414
|
-
</div>
|
|
415
|
-
))
|
|
416
|
-
|
|
417
|
-
return <HourGrid columnHeaders={headers} columns={days} events={events} />
|
|
418
|
-
}
|