kennel 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +9 -6
  2. package/dist/cli/src/plugins/external.d.ts +3 -0
  3. package/dist/cli/src/plugins/external.js +24 -12
  4. package/dist/cli/src/site/hot-viewer.js +45 -38
  5. package/dist/cli/src/site/server-plugins.d.ts +2 -2
  6. package/dist/cli/src/site/viewer.js +13 -8
  7. package/dist/core/api-client.js +576 -10317
  8. package/dist/core/core-folders.js +2313 -12088
  9. package/dist/core/runtime.js +7899 -17525
  10. package/dist/package/preview.js +9838 -19692
  11. package/dist/plugins/external.d.ts +3 -0
  12. package/dist/plugins/external.js +24 -12
  13. package/dist/site/hot-viewer.js +45 -38
  14. package/dist/site/server-plugins.d.ts +2 -2
  15. package/dist/site/server-plugins.js +9 -9
  16. package/dist/site/viewer.js +13 -8
  17. package/kernel/apps/web/package.json +1 -1
  18. package/kernel/apps/web/scripts/dev.mjs +4 -0
  19. package/kernel/apps/web/src/api/events.ts +60 -24
  20. package/kernel/apps/web/src/external-plugins.ts +1 -1
  21. package/kernel/apps/web/src/folder-filter-icons.tsx +1 -1
  22. package/kernel/apps/web/src/folder-plugin-view.tsx +1 -1
  23. package/kernel/apps/web/src/plugins.ts +1 -1
  24. package/kernel/apps/web/vite.config.ts +3 -1
  25. package/kernel/packages/agents/package.json +1 -1
  26. package/kernel/packages/agents/src/agent-card.tsx +1 -1
  27. package/kernel/packages/agents/src/client.tsx +1 -1
  28. package/kernel/packages/agents/src/manifest.ts +1 -1
  29. package/kernel/packages/agents/src/server.ts +1 -1
  30. package/kernel/packages/agents/src/viewer.tsx +1 -1
  31. package/kernel/packages/api/package.json +1 -1
  32. package/kernel/packages/api/src/server/acp-live-plugin.ts +1 -1
  33. package/kernel/packages/api/src/server/api/create-agent-file.ts +2 -2
  34. package/kernel/packages/api/src/server/api/document-edit.ts +2 -2
  35. package/kernel/packages/api/src/server/api/open-path.ts +2 -2
  36. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +4 -4
  37. package/kernel/packages/api/src/server/plugins.ts +3 -3
  38. package/kernel/packages/api/src/shared/viewer-route.ts +1 -1
  39. package/kernel/packages/api/test/core.test.ts +1 -1
  40. package/kernel/packages/notes/package.json +1 -1
  41. package/kernel/packages/notes/src/client.tsx +1 -1
  42. package/kernel/packages/notes/src/manifest.ts +1 -1
  43. package/kernel/packages/notes/src/pages/notes-page.tsx +34 -1
  44. package/kernel/packages/notes/src/server.ts +1 -1
  45. package/kernel/packages/notes/src/ui/folder-notes-list.tsx +10 -74
  46. package/kernel/packages/plugins/SKILL.md +39 -0
  47. package/kernel/packages/{folder-plugin → plugins}/package.json +65 -11
  48. package/kernel/packages/plugins/src/index.ts +52 -0
  49. package/kernel/packages/plugins/src/page.ts +17 -0
  50. package/kernel/packages/{folder-plugin → plugins}/tsconfig.json +5 -1
  51. package/kernel/packages/plugins/tsup.config.ts +25 -0
  52. package/kernel/packages/ui/src/components/content-issues.tsx +1 -1
  53. package/kernel/packages/ui/tsconfig.json +1 -1
  54. package/package.json +2 -3
  55. package/web/assets/{abnfDiagram-VRR7QNED-xd_g1uK3.js → abnfDiagram-VRR7QNED-CDr-U31F.js} +1 -1
  56. package/web/assets/app-DHGorUqZ.js +2 -0
  57. package/web/assets/app-options-CtkiC8GR.js +25 -0
  58. package/web/assets/{app-view-header-CNk5KnA-.js → app-view-header-DbQuD6_F.js} +1 -1
  59. package/web/assets/{arc-B0u5yBJA.js → arc-Cwz5OeLQ.js} +1 -1
  60. package/web/assets/{architectureDiagram-ZJ3FMSHR-DfxOAo8P.js → architectureDiagram-ZJ3FMSHR-igGC0b-J.js} +1 -1
  61. package/web/assets/{ask-tray-SscJecDY.js → ask-tray-y8nkSKNG.js} +1 -1
  62. package/web/assets/{blockDiagram-677ZJIJ3-Ci3Usxh6.js → blockDiagram-677ZJIJ3-D4SDltQ_.js} +1 -1
  63. package/web/assets/{c4Diagram-LMCZKHZV-BqgwIVgh.js → c4Diagram-LMCZKHZV-Br15uN-s.js} +1 -1
  64. package/web/assets/channel-DRnAhH31.js +1 -0
  65. package/web/assets/{chunk-2Q5K7J3B-CfX69l2U.js → chunk-2Q5K7J3B-B0RqrvJA.js} +1 -1
  66. package/web/assets/{chunk-32BRIVSS-CuFlzZkl.js → chunk-32BRIVSS-De9VBEyE.js} +1 -1
  67. package/web/assets/{chunk-5VM5RSS4-DidCMlJa.js → chunk-5VM5RSS4-YIMlZtW3.js} +1 -1
  68. package/web/assets/{chunk-EX3LRPZG-B5CDm1dS.js → chunk-EX3LRPZG-y9kGcrYi.js} +1 -1
  69. package/web/assets/{chunk-JWPE2WC7-D5fGV_sH.js → chunk-JWPE2WC7-D0A13HAu.js} +1 -1
  70. package/web/assets/{chunk-MOJQB5TN-ayhH5Q-H.js → chunk-MOJQB5TN-8v4jL8iu.js} +1 -1
  71. package/web/assets/{chunk-RYQCIY6F-C4BfOOZn.js → chunk-RYQCIY6F-Ri_iNmIp.js} +1 -1
  72. package/web/assets/{chunk-V7JOEXUC-DpUEIM9V.js → chunk-V7JOEXUC-b0mIbG_X.js} +1 -1
  73. package/web/assets/{chunk-VR4S4FIN-DoQuFFRh.js → chunk-VR4S4FIN-BkTZWt1n.js} +1 -1
  74. package/web/assets/{chunk-XXDRQBXY-CI9eiLwn.js → chunk-XXDRQBXY-D7muOXcd.js} +1 -1
  75. package/web/assets/classDiagram-OUVF2IWQ-BPkIpFlc.js +1 -0
  76. package/web/assets/classDiagram-v2-EOCWNBFH-BPkIpFlc.js +1 -0
  77. package/web/assets/{client-Dk-NBvJR.js → client-BMso7lHb.js} +2 -2
  78. package/web/assets/client-Bkl3gwZ5.js +17 -0
  79. package/web/assets/client-soK_bLWT.js +5 -0
  80. package/web/assets/{command-zkzH4hVz.js → command-CsN8TfJg.js} +1 -1
  81. package/web/assets/{copy-context-D5oiWrYC.js → copy-context-DmBNpffM.js} +1 -1
  82. package/web/assets/{cose-bilkent-JH36ORCC-BHRk1mO3.js → cose-bilkent-JH36ORCC-7hnQZylB.js} +1 -1
  83. package/web/assets/{cynefin-VYW2F7L2-C1B09TRE.js → cynefin-VYW2F7L2-CTbqXuvk.js} +1 -1
  84. package/web/assets/{cynefinDiagram-TSTJHNR4-B27qXQbv.js → cynefinDiagram-TSTJHNR4-CmIJtocX.js} +1 -1
  85. package/web/assets/{dagre-VKFMJZFB-B1qEi32g.js → dagre-VKFMJZFB-BLPHNs2Y.js} +1 -1
  86. package/web/assets/{diagram-FQU43EPY-BMkkgADD.js → diagram-FQU43EPY-L6FVWAuj.js} +1 -1
  87. package/web/assets/{diagram-G47NLZAW-Cb8_g1A5.js → diagram-G47NLZAW-Bb26dbIM.js} +1 -1
  88. package/web/assets/{diagram-NH7WQ7WH-B51wuc5m.js → diagram-NH7WQ7WH-BBauwdU-.js} +1 -1
  89. package/web/assets/{diagram-OA4YK3LP-xnsecbbI.js → diagram-OA4YK3LP-Dj_9FE7R.js} +1 -1
  90. package/web/assets/{diagram-WEI45ONY-DSZdyaAV.js → diagram-WEI45ONY-Co1MS5BO.js} +1 -1
  91. package/web/assets/{document-Dc2dftTW.js → document-_ETc5epn.js} +3 -3
  92. package/web/assets/{ebnfDiagram-CCIWWBDH-aTHmcUGy.js → ebnfDiagram-CCIWWBDH-COGAgW5z.js} +1 -1
  93. package/web/assets/{entities-CCUtpskY.js → entities-ByGFSWnQ.js} +1 -1
  94. package/web/assets/{erDiagram-Q63AITRT-CUUPBcvo.js → erDiagram-Q63AITRT-CRMKCr0m.js} +1 -1
  95. package/web/assets/{file-viewer-DJ3rsmef.js → file-viewer-Dqx7DbTw.js} +1 -1
  96. package/web/assets/{flowDiagram-23GEKE2U-aTQXx2Mh.js → flowDiagram-23GEKE2U-Drv72N5g.js} +1 -1
  97. package/web/assets/folder-filter-bar-Bs_uyU0g.js +4 -0
  98. package/web/assets/folder-image-BBNRcu1j.js +1 -0
  99. package/web/assets/{folder-landing-DCnzYXXY.js → folder-landing-0EXAzEpD.js} +3 -3
  100. package/web/assets/{folder-list-DvVkVjiR.js → folder-list-Do8Kl8uk.js} +1 -1
  101. package/web/assets/{folder-metadata-C9hMMzIH.js → folder-metadata-DncP3Vx2.js} +1 -1
  102. package/web/assets/folder-mobile-bar-D-XXKrip.js +1 -0
  103. package/web/assets/folder-plugin-view-DoRQa8Dz.js +3 -0
  104. package/web/assets/{ganttDiagram-NO4QXBWP-DiwdUp7u.js → ganttDiagram-NO4QXBWP-DHEt2_9K.js} +1 -1
  105. package/web/assets/{gitGraphDiagram-IHSO6WYX-BL2gG_IS.js → gitGraphDiagram-IHSO6WYX-Bt6XsYTq.js} +1 -1
  106. package/web/assets/{index-CUTtMSyJ.js → index-7JCizcu7.js} +2 -2
  107. package/web/assets/index-DY0VGBht.js +1 -0
  108. package/web/assets/{infoDiagram-FWYZ7A6U-B3n0zZpt.js → infoDiagram-FWYZ7A6U-961DCJAH.js} +1 -1
  109. package/web/assets/{ishikawaDiagram-FXEZZL3T-D7YytbEv.js → ishikawaDiagram-FXEZZL3T-ibSYK50l.js} +1 -1
  110. package/web/assets/{journeyDiagram-5HDEW3XC-BYwqEl3C.js → journeyDiagram-5HDEW3XC-Cd0ZPqw2.js} +1 -1
  111. package/web/assets/{kanban-definition-HUTT4EX6-BQHNJgXZ.js → kanban-definition-HUTT4EX6-Bu5skY8G.js} +1 -1
  112. package/web/assets/{linear-BPiQCLNk.js → linear-Cf8Xflqn.js} +1 -1
  113. package/web/assets/{mermaid.core-CD-KvBjV.js → mermaid.core-C9iHkrxr.js} +4 -4
  114. package/web/assets/{metadata-COJ7fZul.js → metadata-CLq-ZGmh.js} +1 -1
  115. package/web/assets/{mindmap-definition-LN4V7U3C-C1L_gyoj.js → mindmap-definition-LN4V7U3C-C5bK3O7k.js} +1 -1
  116. package/web/assets/{pegDiagram-2B236MQR-BDAfItqS.js → pegDiagram-2B236MQR-5rN2nkjz.js} +1 -1
  117. package/web/assets/{pieDiagram-ENE6RG2P-CkZYH3Vz.js → pieDiagram-ENE6RG2P-DnSBFuIb.js} +1 -1
  118. package/web/assets/popover-D7MTK-sz.js +9 -0
  119. package/web/assets/{project-search-dialog-B2oqMT6h.js → project-search-dialog-Bo1JWdqR.js} +1 -1
  120. package/web/assets/{quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js → quadrantDiagram-ABIIQ3AL-xinqVpVM.js} +1 -1
  121. package/web/assets/{railroadDiagram-RFXS5EU6-Dd9_yo7B.js → railroadDiagram-RFXS5EU6-GsASFEVa.js} +1 -1
  122. package/web/assets/{requirementDiagram-TGXJPOKE-DPhIUsbs.js → requirementDiagram-TGXJPOKE-DS5CqNNo.js} +1 -1
  123. package/web/assets/{sankeyDiagram-HTMAVEWB-B2QkYtub.js → sankeyDiagram-HTMAVEWB-DsnvjCXC.js} +1 -1
  124. package/web/assets/{search-C1J0-xiB.js → search-BA4r_Yoa.js} +1 -1
  125. package/web/assets/select-D4TpCgnU.js +30 -0
  126. package/web/assets/{selection-popup-qhlUFzGq.js → selection-popup-DIME-60e.js} +3 -3
  127. package/web/assets/{sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js → sequenceDiagram-DBY2YBRQ-PI0WgJrf.js} +1 -1
  128. package/web/assets/{sizeCapture-X5ZJPWSS-CIXIhl_A.js → sizeCapture-X5ZJPWSS-Bpul3MSt.js} +1 -1
  129. package/web/assets/{stateDiagram-2N3HPSRC-DCrdAflB.js → stateDiagram-2N3HPSRC-DZEEP9Z2.js} +1 -1
  130. package/web/assets/stateDiagram-v2-6OUMAXLB-WAp7-BWX.js +1 -0
  131. package/web/assets/{swimlanes-5IMT3BWC-DA2vc7iZ.js → swimlanes-5IMT3BWC-_qVjNiCh.js} +2 -2
  132. package/web/assets/swimlanesDiagram-G3AALYLV-Dy4wBDSi.js +8 -0
  133. package/web/assets/{timeline-definition-FHXFAJF6-lKfJiV9V.js → timeline-definition-FHXFAJF6-CJev7rIB.js} +1 -1
  134. package/web/assets/use-folder-filter-hA6fa7nm.js +1 -0
  135. package/web/assets/{vennDiagram-L72KCM5P-DO1q2KzG.js → vennDiagram-L72KCM5P-BrTVvREy.js} +1 -1
  136. package/web/assets/{view-context-DXKM3-l1.js → view-context-Co01ON81.js} +35 -35
  137. package/web/assets/{viewer-capabilities-Cig_xM9u.js → viewer-capabilities-C_kjcYcl.js} +1 -1
  138. package/web/assets/{wardleyDiagram-EHGQE667-Dal9LkuZ.js → wardleyDiagram-EHGQE667-XwMSgrT5.js} +1 -1
  139. package/web/assets/{xychartDiagram-FW5EYKEG-B3dPLPII.js → xychartDiagram-FW5EYKEG-Be1kFxeC.js} +1 -1
  140. package/web/index.html +1 -1
  141. package/kernel/packages/folder-plugin/src/index.ts +0 -15
  142. package/web/assets/app-CX20nNIa.js +0 -2
  143. package/web/assets/app-options-CkPJE5ep.js +0 -25
  144. package/web/assets/channel-X7WHyxxT.js +0 -1
  145. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +0 -1
  146. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +0 -1
  147. package/web/assets/client-7OWw5kW2.js +0 -6
  148. package/web/assets/client-DYj_td4Q.js +0 -17
  149. package/web/assets/folder-image-Bnv43tzP.js +0 -1
  150. package/web/assets/folder-mobile-bar-VoTL1GS0.js +0 -1
  151. package/web/assets/folder-plugin-view-Cvd804ZU.js +0 -6
  152. package/web/assets/popover-0oA2lLUV.js +0 -9
  153. package/web/assets/select-CtXC_PLC.js +0 -30
  154. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +0 -1
  155. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +0 -8
  156. /package/kernel/packages/{folder-plugin → plugins}/eslint.config.js +0 -0
  157. /package/kernel/packages/{folder-plugin → plugins}/src/client.ts +0 -0
  158. /package/kernel/packages/{folder-plugin → plugins}/src/endpoint.ts +0 -0
  159. /package/kernel/packages/{folder-plugin → plugins}/src/server.ts +0 -0
  160. /package/kernel/packages/{folder-plugin → plugins}/src/subpath.ts +0 -0
  161. /package/kernel/packages/{folder-plugin → plugins}/src/types.ts +0 -0
  162. /package/kernel/packages/{folder-plugin → plugins}/src/validation.ts +0 -0
  163. /package/kernel/packages/{folder-plugin → plugins}/test/contract.test.mjs +0 -0
@@ -1,11 +1,11 @@
1
- import type { FolderPluginContext } from "@raggle-ai/folder-plugin/server";
2
- import type { FolderPluginServer } from "@raggle-ai/folder-plugin/server";
1
+ import type { FolderPluginContext } from "kennel-plugins/server";
2
+ import type { FolderPluginServer } from "kennel-plugins/server";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { existsSync } from "node:fs";
5
5
  import { mkdir, stat, writeFile } from "node:fs/promises";
6
6
  import { dirname, relative, resolve, sep } from "node:path";
7
7
  import { createAgentsPlugin } from "@raggle-ai/agents/server";
8
- import { FolderPluginFailure } from "@raggle-ai/folder-plugin/server";
8
+ import { FolderPluginFailure } from "kennel-plugins/server";
9
9
  import { createNotesPlugin } from "@raggle-ai/notes/server";
10
10
 
11
11
  export { FolderPluginFailure };
@@ -33,7 +33,7 @@ const sectionTabs: Record<ViewerSection, FolderTab> = {
33
33
  skills: "skills",
34
34
  };
35
35
 
36
- import { folderPathId as encodeBase64Url } from "@raggle-ai/folder-plugin/client";
36
+ import { folderPathId as encodeBase64Url } from "kennel-plugins/client";
37
37
 
38
38
  function decodeBase64Url(value: string) {
39
39
  const padded = `${value}${"=".repeat((4 - (value.length % 4)) % 4)}`;
@@ -9,7 +9,7 @@ import {
9
9
  configuredFolderServerRegistry,
10
10
  scaffoldFolderPlugins,
11
11
  } from "../src/server/plugins";
12
- import type { FolderPluginServer } from "~/folder-plugin/server";
12
+ import type { FolderPluginServer } from "kennel-plugins/server";
13
13
  import { createCoreFolderIndex } from "@raggle-ai/schemas/node";
14
14
 
15
15
  const cleanup: (() => Promise<unknown>)[] = [];
@@ -39,7 +39,7 @@
39
39
  "publish:public:dry-run": "npm --@raggle-ai:registry=https://registry.npmjs.org publish --dry-run --access public --registry=https://registry.npmjs.org"
40
40
  },
41
41
  "dependencies": {
42
- "@raggle-ai/folder-plugin": "workspace:*",
42
+ "kennel-plugins": "workspace:*",
43
43
  "@raggle-ai/ui": "workspace:*",
44
44
  "@tanstack/react-query": "catalog:tanstack",
45
45
  "lucide-react": "^0.562.0"
@@ -1,4 +1,4 @@
1
- import type { FolderPluginClient } from "@raggle-ai/folder-plugin/client"
1
+ import type { FolderPluginClient } from "kennel-plugins/client"
2
2
 
3
3
  import { notesManifest } from "./manifest.ts"
4
4
  import { NotesPage } from "./pages/notes-page.tsx"
@@ -1,4 +1,4 @@
1
- import type { FolderPluginManifest } from "@raggle-ai/folder-plugin"
1
+ import type { FolderPluginManifest } from "kennel-plugins"
2
2
 
3
3
  export const notesManifest = {
4
4
  id: "notes",
@@ -1,6 +1,10 @@
1
1
  import type { ReactElement } from "react"
2
2
  import { useState } from "react"
3
3
 
4
+ import type { FolderFile } from "@raggle-ai/ui/lib/folder-activity"
5
+ import { FolderFilterBar } from "@raggle-ai/ui/components/folder-filter-bar"
6
+ import { useFolderFilter } from "@raggle-ai/ui/hooks/use-folder-filter"
7
+
4
8
  import { useAiInput } from "../data/ai-input.ts"
5
9
  import {
6
10
  folderDocumentHref,
@@ -15,6 +19,19 @@ import {
15
19
  } from "../ui/ai-composer-controls.tsx"
16
20
  import { FolderNotesList } from "../ui/folder-notes-list.tsx"
17
21
 
22
+ /** Folder that directly contains a note, including a note below the view root. */
23
+ function noteFolder(file: FolderFile, viewedFolder?: string): string | null {
24
+ const root = file.folder ?? viewedFolder
25
+ if (!root) return null
26
+ const parent = file.relativePath
27
+ .replaceAll("\\", "/")
28
+ .split("/")
29
+ .slice(0, -1)
30
+ .filter(Boolean)
31
+ .join("/")
32
+ return parent ? `${root.replace(/[\\/]+$/, "")}/${parent}` : root
33
+ }
34
+
18
35
  /** Folder Notes page: local and linked Markdown notes plus the AI composer. */
19
36
  export function NotesPage(): ReactElement {
20
37
  const [composerOpen, setComposerOpen] = useState(false)
@@ -24,6 +41,11 @@ export function NotesPage(): ReactElement {
24
41
  const folderId = folder ? folderPathId(folder) : "none"
25
42
  // Metadata events are handled once by the shared shell.
26
43
  const { local: query, linked, files, retryLinked } = useFolderNotes(href)
44
+ const filter = useFolderFilter(files ?? [], {
45
+ allowOutside: true,
46
+ getFolder: (file) => noteFolder(file, folder),
47
+ rootFolder: folder ?? "",
48
+ })
27
49
 
28
50
  return (
29
51
  <>
@@ -44,7 +66,18 @@ export function NotesPage(): ReactElement {
44
66
  : file.relativePath
45
67
  }}
46
68
  error={query.error ? String(query.error) : ""}
47
- files={files}
69
+ files={filter.visibleItems}
70
+ filter={
71
+ filter.choices.length > 1 ? (
72
+ <FolderFilterBar
73
+ ariaLabel="Filter notes by folder"
74
+ choices={filter.choices}
75
+ onSelect={filter.setSelected}
76
+ selected={filter.effectiveSelected}
77
+ total={filter.total}
78
+ />
79
+ ) : undefined
80
+ }
48
81
  folder={folder}
49
82
  folderHref={(source) => folderSectionHref(source, "notes")}
50
83
  loading={query.isLoading}
@@ -6,7 +6,7 @@ import type {
6
6
  FolderPluginData,
7
7
  FolderPluginDetection,
8
8
  FolderPluginServer,
9
- } from "@raggle-ai/folder-plugin/server"
9
+ } from "kennel-plugins/server"
10
10
 
11
11
  import { notesManifest } from "./manifest.ts"
12
12
 
@@ -1,20 +1,8 @@
1
1
  import type { ReactNode } from "react"
2
- import { useState } from "react"
3
- import { Link2 } from "lucide-react"
4
2
 
5
3
  import type { FolderFile } from "@raggle-ai/ui/lib/folder-activity"
6
- import { Button } from "@raggle-ai/ui/components/button"
7
4
  import { NoteCard } from "@raggle-ai/ui/components/note-card"
8
5
 
9
- function folderName(folder: string) {
10
- return (
11
- folder
12
- .replace(/[\\/]+$/, "")
13
- .split(/[\\/]/)
14
- .at(-1) ?? folder
15
- )
16
- }
17
-
18
6
  /**
19
7
  * Note rows for the main Notes tab, backed by the shared NoteCard so linked
20
8
  * notes render the same way everywhere.
@@ -24,6 +12,7 @@ export function FolderNotesList({
24
12
  documentHref,
25
13
  error,
26
14
  files = [],
15
+ filter,
27
16
  folder,
28
17
  folderHref,
29
18
  loading,
@@ -32,41 +21,18 @@ export function FolderNotesList({
32
21
  documentHref: (file: FolderFile) => string
33
22
  error: string
34
23
  files?: FolderFile[]
24
+ filter?: ReactNode
35
25
  folder?: string
36
26
  folderHref: (folder: string) => string
37
27
  loading: boolean
38
28
  }) {
39
- const [filter, setFilter] = useState("all")
40
- const sources = [
41
- ...new Set(files.flatMap((file) => (file.folder ? [file.folder] : []))),
42
- ]
43
- const filters = [
44
- { id: "all", label: "All", count: files.length },
45
- {
46
- id: "local",
47
- label: "This folder",
48
- count: files.filter((file) => !file.folder).length,
49
- },
50
- ...sources.map((source) => ({
51
- id: source,
52
- label: folderName(source),
53
- count: files.filter((file) => file.folder === source).length,
54
- })),
55
- ]
56
- const selected = filters.some((item) => item.id === filter) ? filter : "all"
57
- const visible = files
58
- .filter(
59
- (file) =>
60
- selected === "all" ||
61
- (selected === "local" ? !file.folder : file.folder === selected),
62
- )
63
- .sort(
64
- (left, right) =>
65
- right.activityAt - left.activityAt ||
66
- `${left.folder ?? ""}/${left.relativePath}`.localeCompare(
67
- `${right.folder ?? ""}/${right.relativePath}`,
68
- ),
69
- )
29
+ const visible = files.toSorted(
30
+ (left, right) =>
31
+ right.activityAt - left.activityAt ||
32
+ `${left.folder ?? ""}/${left.relativePath}`.localeCompare(
33
+ `${right.folder ?? ""}/${right.relativePath}`,
34
+ ),
35
+ )
70
36
 
71
37
  return (
72
38
  <div className="mx-auto grid w-full max-w-3xl gap-4 px-4 py-5">
@@ -74,37 +40,7 @@ export function FolderNotesList({
74
40
  <h1 className="m-0 text-2xl font-semibold text-[#233025]">Notes</h1>
75
41
  {action}
76
42
  </div>
77
- {sources.length ? (
78
- <div
79
- role="group"
80
- aria-label="Filter notes by source"
81
- className="flex flex-wrap gap-2"
82
- >
83
- {filters.map((item) => (
84
- <Button
85
- key={item.id}
86
- type="button"
87
- size="sm"
88
- variant={selected === item.id ? "secondary" : "outline"}
89
- className="h-auto min-h-8 max-w-full rounded-full
90
- whitespace-normal"
91
- aria-pressed={selected === item.id}
92
- onClick={() => setFilter(item.id)}
93
- title={
94
- item.id === "local"
95
- ? folder
96
- : sources.includes(item.id)
97
- ? item.id
98
- : undefined
99
- }
100
- >
101
- {sources.includes(item.id) ? <Link2 aria-hidden="true" /> : null}
102
- <span className="break-all">{item.label}</span>
103
- <span className="text-muted-foreground">{item.count}</span>
104
- </Button>
105
- ))}
106
- </div>
107
- ) : null}
43
+ {filter}
108
44
  {loading ? <p className="m-0 text-sm text-[#6c7a6d]">Loading…</p> : null}
109
45
  {error ? <p className="m-0 text-sm text-[#9c3a3a]">{error}</p> : null}
110
46
  {!loading && !error && !visible.length ? (
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: kennel-plugins
3
+ description: Install, register, or build a trusted Kennel folder plugin. Use for `knl plugin add`, external plugin kernels, plugin tabs, or deciding where plugin code belongs.
4
+ ---
5
+
6
+ # Kennel Plugin Management
7
+
8
+ ## Ownership
9
+
10
+ Keep each concern in its owner:
11
+
12
+ | Concern | Location |
13
+ | ----------------------------------------------------------- | -------------------------------------- |
14
+ | Plugin contract and public client/server types | `packages/plugins` (`kennel-plugins`) |
15
+ | Generic reusable viewer UI | `packages/ui` |
16
+ | Folder metadata API and server registry | `packages/api` |
17
+ | Browser route and static external-plugin registry | `apps/web` |
18
+ | Install, pin, cache, validate, and compose external plugins | `cli/src/plugins/external.ts` |
19
+ | Built-in Agents and Notes feature code | `packages/agents` and `packages/notes` |
20
+ | External plugin feature code | Its own repository |
21
+
22
+ Keep new plugin feature code in its own repository. Add to `packages/plugins` only when every plugin needs a contract change. Add to `packages/ui` only when every viewer can use the UI component.
23
+
24
+ ## External plugin install
25
+
26
+ 1. Inspect the plugin `package.json`, `src/manifest.ts`, `src/client.tsx`, and `src/server.ts`.
27
+ 2. Require `package.json > kennel.plugin` with a unique non-core `id`. The conventional `./manifest`, `./client`, `./server`, and optional `./api` entries must default-export their values. Add `trigger` only when a file can make an otherwise unavailable plugin visible.
28
+ 3. Keep the manifest id and package id equal. The client and server must spread or otherwise return that manifest id.
29
+ 4. Ensure the repository TypeScript config works outside the Kennel workspace. Do not require unpublished workspace config files during kernel bundling.
30
+ 5. Install with `knl plugin add <git-url>`. It pins the resolved commit, validates exports, stores source in `~/.cache/kennel/plugins/sources/`, records the plugin in `~/.config/kennel/config.json`, and composes a static kernel in `~/.cache/kennel/kernels/`.
31
+ 6. Use `knl view <folder>` after installation. External plugins require the composed static viewer; a plain Vite hot viewer cannot show their tabs.
32
+
33
+ ## Change workflow
34
+
35
+ - Start with the smallest plugin repository change. Do not copy plugin feature code into Kennel.
36
+ - Change `packages/plugins`, `packages/api`, `apps/web`, and `cli` only when the plugin contract or trusted-kernel behavior needs it.
37
+ - Keep plugin ids stable. Do not use core ids: `agents`, `calendar`, `mail`, `notes`, `projects`, or `tasks`.
38
+ - Verify a real install in an isolated `XDG_CONFIG_HOME` and `XDG_CACHE_HOME` first. Then verify the tab and its selected route in the composed viewer.
39
+ - A CLI behavior change requires a new `cli/package.json` version. The publish workflow releases it after merge to `main`.
@@ -1,21 +1,61 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "name": "@raggle-ai/folder-plugin",
4
- "version": "0.1.0",
3
+ "name": "kennel-plugins",
4
+ "version": "0.1.1",
5
5
  "description": "Small shared contract for trusted folder view plugins in the Kennel web viewer.",
6
6
  "type": "module",
7
- "private": true,
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/raggle-ai/kennel.git",
11
+ "directory": "packages/plugins"
12
+ },
13
+ "publishConfig": {
14
+ "registry": "https://registry.npmjs.org",
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
8
21
  "prettier": "@raggle-ai/prettier-config",
9
22
  "exports": {
10
- ".": "./src/index.ts",
11
- "./client": "./src/client.ts",
12
- "./endpoint": "./src/endpoint.ts",
13
- "./server": "./src/server.ts",
14
- "./subpath": "./src/subpath.ts",
15
- "./types": "./src/types.ts",
16
- "./validation": "./src/validation.ts"
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ },
27
+ "./client": {
28
+ "types": "./dist/client.d.ts",
29
+ "import": "./dist/client.js"
30
+ },
31
+ "./endpoint": {
32
+ "types": "./dist/endpoint.d.ts",
33
+ "import": "./dist/endpoint.js"
34
+ },
35
+ "./page": {
36
+ "types": "./dist/page.d.ts",
37
+ "import": "./dist/page.js"
38
+ },
39
+ "./server": {
40
+ "types": "./dist/server.d.ts",
41
+ "import": "./dist/server.js"
42
+ },
43
+ "./subpath": {
44
+ "types": "./dist/subpath.d.ts",
45
+ "import": "./dist/subpath.js"
46
+ },
47
+ "./types": {
48
+ "types": "./dist/types.d.ts",
49
+ "import": "./dist/types.js"
50
+ },
51
+ "./validation": {
52
+ "types": "./dist/validation.d.ts",
53
+ "import": "./dist/validation.js"
54
+ }
17
55
  },
18
56
  "scripts": {
57
+ "build": "tsup",
58
+ "prepack": "bun run build",
19
59
  "test": "bun test test/*.test.mjs",
20
60
  "typecheck": "tsc --noEmit",
21
61
  "format": "prettier . --check --cache --cache-location .cache/.prettiercache --ignore-path ../../.prettierignore",
@@ -24,8 +64,19 @@
24
64
  "lint:fix": "eslint . --fix --cache --cache-location .cache/.eslintcache",
25
65
  "clean": "git clean -xdf dist node_modules .cache .turbo/cache"
26
66
  },
67
+ "dependencies": {
68
+ "class-variance-authority": "^0.7.1",
69
+ "clsx": "^2.1.1",
70
+ "cmdk": "^1.1.1",
71
+ "lucide-react": "^0.562.0",
72
+ "next-themes": "^0.4.6",
73
+ "radix-ui": "^1.6.7",
74
+ "sonner": "^2.0.7",
75
+ "tailwind-merge": "^3.4.0"
76
+ },
27
77
  "peerDependencies": {
28
- "react": ">=18.3.0"
78
+ "react": ">=18.3.0",
79
+ "react-dom": ">=18.3.0"
29
80
  },
30
81
  "devDependencies": {
31
82
  "@eslint/compat": "catalog:eslint",
@@ -35,6 +86,8 @@
35
86
  "@raggle-ai/eslint-config": "workspace:*",
36
87
  "@raggle-ai/prettier-config": "workspace:*",
37
88
  "@raggle-ai/tsconfig": "workspace:*",
89
+ "@raggle-ai/ui": "workspace:*",
90
+ "kennel-client": "workspace:*",
38
91
  "@types/node": "catalog:",
39
92
  "@types/react": "catalog:react19",
40
93
  "eslint": "catalog:eslint",
@@ -44,6 +97,7 @@
44
97
  "prettier-plugin-merge": "catalog:prettier",
45
98
  "prettier-plugin-tailwindcss": "catalog:prettier",
46
99
  "react": "catalog:react19",
100
+ "tsup": "8.5.1",
47
101
  "typescript": "catalog:",
48
102
  "typescript-eslint": "catalog:eslint"
49
103
  }
@@ -0,0 +1,52 @@
1
+ // The complete public surface for external folder plugins. Plugins should
2
+ // import this module only; it deliberately exposes selected shared UI, not
3
+ // the underlying @raggle-ai/ui package.
4
+ export {
5
+ CopyContextButton,
6
+ FilePreview,
7
+ FilePreviewList,
8
+ FolderFilterBar,
9
+ PluginPage,
10
+ PluginPageError,
11
+ PluginPageStatus,
12
+ useFolderFilter,
13
+ } from "./page.ts"
14
+ export { folderPathId } from "./client.ts"
15
+ export {
16
+ apiError,
17
+ ApiError,
18
+ jsonError,
19
+ jsonResponse,
20
+ parseJsonBody,
21
+ readBoundedBody,
22
+ runApiRoute,
23
+ sameOrigin,
24
+ } from "./endpoint.ts"
25
+ export { FolderPluginFailure } from "./server.ts"
26
+ export { isPluginPayloadError } from "./types.ts"
27
+ export type {
28
+ FolderPluginDataset,
29
+ FolderPluginDetection,
30
+ FolderPluginId,
31
+ FolderPluginManifest,
32
+ FolderPluginPayload,
33
+ FolderPluginPayloadError,
34
+ } from "./types.ts"
35
+ export type {
36
+ FolderPluginClient,
37
+ FolderPluginHostActions,
38
+ FolderPluginViewProps,
39
+ } from "./client.ts"
40
+ export type { ApiErrorCode, ApiRouteHandle, ApiRouteSpec } from "./endpoint.ts"
41
+ export type {
42
+ FolderPluginContext,
43
+ FolderPluginData,
44
+ FolderPluginInitFile,
45
+ FolderPluginInitResult,
46
+ FolderPluginServer,
47
+ } from "./server.ts"
48
+ export type {
49
+ FolderFilterChoice,
50
+ FolderFilterMode,
51
+ FolderSelection,
52
+ } from "./page.ts"
@@ -0,0 +1,17 @@
1
+ export {
2
+ EntityError as PluginPageError,
3
+ EntityPage as PluginPage,
4
+ EntityStatus as PluginPageStatus,
5
+ } from "@raggle-ai/ui/pages/entity-page"
6
+ export { CopyContextButton } from "@raggle-ai/ui/components/copy-context"
7
+ export {
8
+ FilePreview,
9
+ FilePreviewList,
10
+ } from "@raggle-ai/ui/components/file-preview"
11
+ export { FolderFilterBar } from "@raggle-ai/ui/components/folder-filter-bar"
12
+ export { useFolderFilter } from "@raggle-ai/ui/hooks/use-folder-filter"
13
+ export type {
14
+ FolderFilterChoice,
15
+ FolderFilterMode,
16
+ FolderSelection,
17
+ } from "@raggle-ai/ui/lib/folder-filter"
@@ -9,7 +9,11 @@
9
9
  "allowImportingTsExtensions": true,
10
10
  // local copy: bun doesn't read paths inherited via `extends`
11
11
  "paths": {
12
- "~/*": ["${configDir}/../../packages/*/src"]
12
+ "~/*": ["${configDir}/../../packages/*/src"],
13
+ "kennel-plugins": ["./src/index.ts"],
14
+ "kennel-plugins/*": ["./src/*"],
15
+ "@raggle-ai/ui/*": ["../ui/src/*"],
16
+ "kennel-client": ["../api-client/src/index.ts"]
13
17
  }
14
18
  },
15
19
  "include": ["src"],
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from "tsup"
2
+
3
+ export default defineConfig({
4
+ entry: [
5
+ "src/index.ts",
6
+ "src/client.ts",
7
+ "src/endpoint.ts",
8
+ "src/page.ts",
9
+ "src/server.ts",
10
+ "src/subpath.ts",
11
+ "src/types.ts",
12
+ "src/validation.ts",
13
+ ],
14
+ format: ["esm"],
15
+ target: "es2022",
16
+ platform: "neutral",
17
+ clean: true,
18
+ esbuildOptions(options) {
19
+ options.jsx = "automatic"
20
+ },
21
+ splitting: true,
22
+ dts: { resolve: [/^@raggle-ai\/ui(?:\/|$)/, /^kennel-client(?:\/|$)/] },
23
+ noExternal: [/^@raggle-ai\/ui(?:\/|$)/, /^kennel-client(?:\/|$)/],
24
+ external: ["react", "react-dom", "react/jsx-runtime"],
25
+ })
@@ -2,7 +2,7 @@ import type { ReactNode } from "react"
2
2
 
3
3
  /**
4
4
  * One precise content problem, structurally identical to
5
- * `ContentIssue` from `@raggle-ai/folder-plugin` (kept local so the ui
5
+ * `ContentIssue` from `kennel-plugins` (kept local so the ui
6
6
  * package does not depend on the plugin contract's runtime).
7
7
  */
8
8
  export type ContentIssue = {
@@ -6,7 +6,7 @@
6
6
  "lib": ["dom", "dom.iterable", "esnext"],
7
7
  "jsx": "preserve",
8
8
  "baseUrl": ".",
9
- // Packages whose sources ui compile reach folder-plugin entries that
9
+ // Packages whose sources ui compile reach kennel-plugins entries that
10
10
  // import sibling files with .ts extensions (Bundler resolution, noEmit).
11
11
  "allowImportingTsExtensions": true
12
12
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kennel",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Open-source pop-up previews for AI output: markdown, HTML artifacts, email, images, code, tables, notes, and calendars, from the terminal or your agent session.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -67,7 +67,6 @@
67
67
  "@lexical/rich-text": "0.49.0",
68
68
  "@libsql/client": "^0.17.3",
69
69
  "@raggle-ai/barkdown": "0.6.1",
70
- "@raggle-ai/folder-plugin": "^0.1.0",
71
70
  "@raggle-ai/kennel-knowledge": "^0.1.0",
72
71
  "@raggle-ai/kennel-mcp": "^0.1.8",
73
72
  "@raggle-ai/kennel-preview": "^0.1.0",
@@ -116,7 +115,7 @@
116
115
  "zod": "^4.3.6"
117
116
  },
118
117
  "devDependencies": {
119
- "@raggle-ai/folder-plugin": "^0.1.0",
118
+ "kennel-plugins": "0.1.1",
120
119
  "@types/node": "^22.13.4",
121
120
  "typescript": "^5.9.3"
122
121
  },
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-ayhH5Q-H.js";import{p as f}from"./chunk-JWPE2WC7-D5fGV_sH.js";import{_ as n,l as o}from"./mermaid.core-CD-KvBjV.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-C1B09TRE.js";import"./index-CUTtMSyJ.js";import"./react-B9jn2M18.js";import"./markdown-Bw9RgUGR.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},I={parser:R,db:a,renderer:u,styles:p};export{I as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-8v4jL8iu.js";import{p as f}from"./chunk-JWPE2WC7-D0A13HAu.js";import{_ as n,l as o}from"./mermaid.core-C9iHkrxr.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-CTbqXuvk.js";import"./index-7JCizcu7.js";import"./react-B9jn2M18.js";import"./markdown-Bw9RgUGR.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},I={parser:R,db:a,renderer:u,styles:p};export{I as diagram};
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ask-tray-y8nkSKNG.js","assets/react-B9jn2M18.js","assets/markdown-Bw9RgUGR.js","assets/view-context-Co01ON81.js","assets/index-7JCizcu7.js","assets/index-Dwhf8F_U.css","assets/tanstack-YzX_Mi37.js","assets/button-C6arH7zt.js","assets/utils-DCaHk0Gm.js","assets/folder-plugin-view-DoRQa8Dz.js","assets/folder-filter-bar-Bs_uyU0g.js","assets/command-CsN8TfJg.js","assets/index-CCXE8zc9.js","assets/index-CegvVQ1C.js","assets/popover-D7MTK-sz.js","assets/index-DY0VGBht.js","assets/folder-image-BBNRcu1j.js","assets/viewer-capabilities-C_kjcYcl.js","assets/entities-ByGFSWnQ.js","assets/search-BA4r_Yoa.js","assets/search-BOHZTLC-.css","assets/folder-landing-0EXAzEpD.js","assets/folder-metadata-DncP3Vx2.js","assets/copy-context-DmBNpffM.js","assets/index-Bp9DidgD.js","assets/app-options-CtkiC8GR.js","assets/index-CXhBn-ar.js","assets/folder-list-Do8Kl8uk.js","assets/folder-activity-Cv1X2ezj.js","assets/folder-mobile-bar-D-XXKrip.js","assets/app-view-header-DbQuD6_F.js","assets/file-viewer-Dqx7DbTw.js","assets/metadata-CLq-ZGmh.js","assets/project-search-dialog-Bo1JWdqR.js","assets/client-BMso7lHb.js","assets/select-D4TpCgnU.js","assets/client-soK_bLWT.js","assets/use-folder-filter-hA6fa7nm.js"])))=>i.map(i=>d[i]);
2
+ import{_ as g,i as ne,f as T,c as re,e as ae,p as S,l as je,g as Re,r as _e,b as be}from"./index-7JCizcu7.js";import{c as s,j as r}from"./react-B9jn2M18.js";import{m as x,c as f}from"./tanstack-YzX_Mi37.js";import{b as ke,c as Ee,q as Ie,L as Te,T as Se,v as Q,V as De,i as se,s as ie,a as M,t as ce,g as Ae,h as Oe,j as E,k as F,m as Fe}from"./view-context-Co01ON81.js";function N(e){const t=new TextEncoder().encode(e);let o="";for(const c of t)o+=String.fromCharCode(c);return btoa(o).replaceAll("+","-").replaceAll("/","_").replaceAll("=","")}function Le(e){const t=new URL(e,window.location.origin),o=t.pathname.split("/").filter(Boolean),c=o.indexOf("folder"),d=c>=0?o[c+1]:void 0;let i=t.searchParams.get("folder")??void 0;if(!i&&d){const l=d.replaceAll("-","+").replaceAll("_","/"),a=l.padEnd(Math.ceil(l.length/4)*4,"="),u=atob(a),m=Uint8Array.from(u,j=>j.charCodeAt(0));i=new TextDecoder().decode(m)}return{folder:i}}function xt(e,t){return`/folder/${N(e)}/${t}`}function jt(e,t){return`/folder/${N(e)}/document/${N(t)}`}async function Ne(e,t,o={}){const c=new URL(e,window.location.origin),d=new URL("/api/metadata",window.location.origin),i=c.pathname.split("/").filter(Boolean),l=i[0]==="folder"?i[1]:void 0,a=c.searchParams.get("folder");if(a)d.searchParams.set("path",a);else if(l){const m=l.replaceAll("-","+").replaceAll("_","/"),j=m.padEnd(Math.ceil(m.length/4)*4,"="),y=atob(j),C=Uint8Array.from(y,V=>V.charCodeAt(0));d.searchParams.set("path",new TextDecoder().decode(C))}d.searchParams.set("part",t),o.notesScope&&d.searchParams.set("notesScope",o.notesScope);const u=await fetch(d,{headers:o.headers,signal:o.signal});if(!u.ok)throw new Error(await u.text());return await u.json()}function Ce(e,t=!0){const o=s.useRef(!1),c=Le(e).folder,d=c?N(c):"none",i=m=>({enabled:t&&!!c,gcTime:5*6e4,queryFn:({signal:j})=>{const y=m==="linked"&&o.current;return y&&(o.current=!1),Ne(e,"notes",{headers:y?{"x-kennel-retry-linked":"1"}:void 0,notesScope:m,signal:j})},queryKey:["folder",d,"notes",m],staleTime:15e3}),l=x.useQuery(i("local")),a=x.useQuery(i("linked")),u=s.useMemo(()=>l.data||a.data?[...l.data?.files.items??[],...a.data?.files.items??[]]:void 0,[l.data,a.data]);return{local:l,linked:a,files:u,retryLinked:()=>{o.current=!0,a.refetch()}}}const Ve=s.lazy(async()=>({default:(await g(async()=>{const{AskTray:e}=await import("./ask-tray-y8nkSKNG.js");return{AskTray:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8]))).AskTray}));function $e({folder:e}){const{enabled:t}=ke(),{draft:o}=Ee(e);return!t&&!o.comments.length&&!o.attachments.length?null:r.jsx(s.Suspense,{fallback:null,children:r.jsx(Ve,{folder:e})})}function Ke(e){const t=e.slice(e.lastIndexOf(".")).toLowerCase();if(t===".htm"||t===".html")return"html";if(t===".md"||t===".markdown")return"markdown";if(t===".text"||t===".txt")return"text"}async function le(e,t=""){const o=[];for await(const c of e.values()){const d=t?`${t}/${c.name}`:c.name;if(c.kind==="directory"){o.push(...await le(c,d));continue}const i=Ke(c.name);if(!i)continue;const l=await c.getFile();o.push({path:d,content:await l.text(),kind:i})}return o}async function qe(e){const t=await le(e);return t.sort((o,c)=>o.path.localeCompare(c.path)),{root:e.name,documents:t}}async function He(e){for(const t of e){if(t.kind!=="file")continue;const o=t.getAsFileSystemHandle;if(!o)throw new Error("This browser cannot read dropped folders.");const c=await o.call(t);if(c?.kind==="directory")return c}throw new Error("Drop one folder to open it.")}const Qe="kennel-folder-query-cache",z="queries",U=1,Me=10080*60*1e3;function ze(){return new Promise((e,t)=>{const o=indexedDB.open(Qe,U);o.onupgradeneeded=()=>{o.result.createObjectStore(z)},o.onerror=()=>t(o.error),o.onsuccess=()=>e(o.result)})}async function de(e,t){const o=await ze();return new Promise((c,d)=>{const i=o.transaction(z,e),l=t(i.objectStore(z));l.onerror=()=>d(l.error),l.onsuccess=()=>c(l.result),i.oncomplete=()=>o.close(),i.onerror=()=>{o.close(),d(i.error)}})}async function ee(e){const t=await de("readonly",o=>o.get(JSON.stringify(e))).catch(()=>{});if(t?.version===U&&!(Date.now()-t.savedAt>Me))return t.data}async function te(e,t){t&&await de("readwrite",o=>o.put({data:t,savedAt:Date.now(),version:U},JSON.stringify(e))).catch(()=>{})}const ue=s.lazy(async()=>({default:(await g(async()=>{const{FolderPluginPage:e}=await import("./folder-plugin-view-DoRQa8Dz.js").then(t=>t.f);return{FolderPluginPage:e}},__vite__mapDeps([9,1,2,6,3,4,5,10,7,8,11,12,13,14,15,16,17]))).FolderPluginPage})),Be=s.lazy(async()=>({default:(await g(async()=>{const{EntitySearchPage:e}=await import("./entities-ByGFSWnQ.js");return{EntitySearchPage:e}},__vite__mapDeps([18,1,2,6,3,4,5]))).EntitySearchPage})),Ue=s.lazy(async()=>({default:(await g(async()=>{const{ProjectSearchPage:e}=await import("./search-BA4r_Yoa.js");return{ProjectSearchPage:e}},__vite__mapDeps([19,1,2,6,3,4,5,20]))).ProjectSearchPage}));function I(){return r.jsx("p",{role:"status",className:"text-muted-foreground p-4 text-sm",children:"Loading page…"})}const D=f.createRootRoute({component:f.Outlet}),oe=s.lazy(async()=>({default:(await g(()=>import("./folder-landing-0EXAzEpD.js"),__vite__mapDeps([21,1,2,3,4,5,6,7,8,22,23,12,13,15,24,25,26,27,28,16]))).FolderLanding})),Ye=s.lazy(async()=>({default:(await g(()=>import("./folder-mobile-bar-D-XXKrip.js"),__vite__mapDeps([29,1,2,25,3,4,5,6,23,7,8,12,26,15,30,16,14]))).FolderMobileBar})),Je=s.lazy(async()=>({default:(await g(()=>import("./file-viewer-Dqx7DbTw.js"),__vite__mapDeps([31,1,2,3,4,5,6,32]))).FileViewer})),We=s.lazy(async()=>({default:(await g(()=>import("./project-search-dialog-Bo1JWdqR.js"),__vite__mapDeps([33,1,2,6,3,4,5,11,12,8,13]))).ProjectSearchDialog})),Ge=s.lazy(async()=>{const e=await g(()=>import("./client-BMso7lHb.js"),__vite__mapDeps([34,1,2,6,25,3,4,5,23,7,8,12,26,15,30,16,14,35,24,27]));return{default:function(){const o=f.useLocation(),c=`${window.location.origin}${o.href}`;return r.jsx(e.CollectionPage,{href:c,request:(d,i,l)=>M.metadata(d,i,l),subscribeApiEvent:ie,parseRoute:S,collectionHref:ae,documentHref:re,folderHref:B,homeHref:d=>T(d,"notes"),openFolder:d=>{M.openPath(d).catch(i=>ce.error(i instanceof Error?i.message:"Could not open the default app."))}})}}}),Xe=s.lazy(async()=>({default:(await g(()=>import("./client-soK_bLWT.js"),__vite__mapDeps([36,1,2,10,3,4,5,6,7,8,11,12,13,14,15,37,23,28]))).NotesPage})),Ze=f.createRoute({getParentRoute:()=>D,path:"/",component:fe}),b=f.createRoute({getParentRoute:()=>D,path:"folder/$folderId",component:fe}),et=f.createRoute({getParentRoute:()=>b,path:"skills"}),tt=f.createRoute({getParentRoute:()=>b,path:"views/$pluginId",component:ue}),ot=f.createRoute({getParentRoute:()=>b,path:"views/$pluginId/$subpath",component:ue}),nt=f.createRoute({getParentRoute:()=>b,path:"collections/$collectionId",component:()=>r.jsx(s.Suspense,{fallback:r.jsx("p",{role:"status",children:"Loading collection…"}),children:r.jsx(Ge,{})})}),rt=f.createRoute({getParentRoute:()=>b,path:"notes",component:Xe}),at=f.createRoute({getParentRoute:()=>b,path:"projects"}),st=f.createRoute({getParentRoute:()=>D,path:"entities",component:Be}),it=f.createRoute({getParentRoute:()=>D,path:"search",component:Ue}),ct=D.addChildren([Ze,st,it,b.addChildren([rt,nt,et,tt,ot,at])]),lt=f.createRouter({routeTree:ct});function dt(){const e=S(window.location.href).folder;return e?T(e,"notes"):"/"}function B(e){return T(e,"notes")}function ut(e){const t=S(window.location.href).folder;return t?re(t,e):e}function ft(e){const t=S(window.location.href).folder;return t?e==="files"?T(t,"notes"):T(t,e):"/"}function pt(e){const t=S(window.location.href).folder;return t?be(t,e):"/"}function mt({href:e,...t}){return!e?.startsWith("/")||ne(e)?r.jsx("a",{href:e,...t}):r.jsx(f.Link,{to:e,...t})}function ht(){return r.jsx(x.QueryClientProvider,{client:Ie,children:r.jsxs(Te,{component:mt,children:[r.jsx(s.Suspense,{fallback:r.jsx(I,{}),children:r.jsx(f.RouterProvider,{router:lt})}),r.jsx(Se,{})]})})}const L={discovery:Number.POSITIVE_INFINITY,identity:Number.POSITIVE_INFINITY,plugins:Number.POSITIVE_INFINITY,skills:15e3};function P(e,t){return["folder",e,t]}function fe(){const e=f.useLocation(),t=`${window.location.origin}${e.href}`,o=Q(t);return r.jsx(De,{kind:"folder",folder:se(t)?void 0:o.folder,children:r.jsx(yt,{})})}function yt(){const e=Ae(),t=je(window.location.href);t&&window.history.replaceState(null,"",t);const o=x.useQueryClient(),c=f.useNavigate(),d=f.useLocation(),i=`${window.location.origin}${d.href}`,l=se(i),a=Q(i),u=a.folder?Re(a.folder):"none",[m,j]=s.useState(()=>window.matchMedia("(max-width: 767px)").matches),[y,C]=s.useState(),[V,$]=s.useState(!1),[Y,J]=s.useState(!1),[pe,me]=s.useState(!1),W=pe&&!a.document,A=s.useRef(0),k=!l&&!y,R=k&&!!a.folder,he=P(u,"identity"),ye=P(u,"discovery");Oe(i,k);const ge=s.useMemo(()=>y?{read:async()=>y}:l?{read:E.read}:{...E},[y,l]),v=x.useQuery({enabled:!!R,gcTime:Number.POSITIVE_INFINITY,queryFn:({signal:n})=>F(i,"identity",{signal:n}),queryKey:he,staleTime:L.identity}),h=x.useQuery({enabled:!!(R&&a.section==="projects"),gcTime:Number.POSITIVE_INFINITY,queryFn:({signal:n})=>F(i,"discovery",{signal:n}),queryKey:ye,staleTime:L.discovery}),G=x.useQuery({enabled:!!R,gcTime:Number.POSITIVE_INFINITY,queryFn:({signal:n})=>F(i,"plugins",{signal:n}),queryKey:P(u,"plugins"),staleTime:L.plugins}),X=Ce(i,R&&a.section==="notes"),Z=x.useQuery({enabled:!!(R&&a.section==="skills"),gcTime:5*6e4,queryFn:({signal:n})=>F(i,"skills",{signal:n}),queryKey:P(u,"skills"),staleTime:L.skills}),_=s.useMemo(()=>{if(v.data)return{assets:{...h.data?.assets,...v.data.assets},files:{items:X.files??[]},folder:v.data.folder,iconAssetKey:v.data.iconAssetKey,plugins:G.data?.plugins??{items:[]},projects:h.data?.projects??{items:[]},workspace:e.workspace??h.data?.workspace,sessions:{items:[]},skills:Z.data?.skills??{items:[]}}},[h.data,v.data,X.files,G.data,Z.data,e.workspace]),K=v.error??(a.section==="projects"?h.error:void 0)??void 0,we=K?K instanceof Error?K.message:"Could not load folder activity.":"",ve=(_?.folder??y?.root??a.folder??"Kennel").replace(/\/+$/,"").split("/").pop()??"Kennel",Pe=_?.iconAssetKey?_.assets[_.iconAssetKey]:void 0;s.useEffect(()=>{const n=window.matchMedia("(max-width: 767px)"),p=()=>j(n.matches);return n.addEventListener("change",p),()=>n.removeEventListener("change",p)},[]),s.useEffect(()=>ie("kennel:metadata-changed",()=>{o.invalidateQueries({queryKey:["folder",u]})}),[u,o]),s.useEffect(()=>Fe("kennel:linked-folders-changed",a.folder,()=>{for(const n of[["notes","linked"],["plugins"]])o.invalidateQueries({queryKey:["folder",u,...n]},{cancelRefetch:!1})}),[u,a.folder,o]),s.useEffect(()=>{if(!R)return;const n=P(u,"identity"),p=P(u,"discovery");ee(n).then(w=>{w&&!o.getQueryData(n)&&o.setQueryData(n,w)}),ee(p).then(w=>{w&&!o.getQueryData(p)&&o.setQueryData(p,w)})},[u,R,o]),s.useEffect(()=>{te(P(u,"identity"),v.data)},[u,v.data]),s.useEffect(()=>{te(P(u,"discovery"),h.data)},[h.data,u]);const xe=n=>{const p=new URL(n,window.location.origin);Q(p.href),c({to:`${p.pathname}${p.search}`})},q=a.view||a.collectionId||a.section==="notes"?r.jsx(s.Suspense,{fallback:r.jsx(I,{}),children:r.jsx(f.Outlet,{})}):a.section==="projects"&&!h.data&&(h.isPending||h.error)?r.jsx("div",{className:"p-4",role:h.error?"alert":"status",children:h.error?r.jsxs(r.Fragment,{children:["Projects could not load."," ",r.jsx("button",{onClick:()=>{h.refetch()},children:"Retry projects"})]}):"Loading projects…"}):null,O=!!a.collectionId,H={data:_,collectionId:a.collectionId,collectionHref:n=>a.folder?ae(a.folder,n):"/",documentHref:ut,error:we,folder:a.folder,folderHref:B,onFindProject:()=>J(!0),onOpenFolder:()=>{E.openRoot?.()},onOpenPath:n=>{M.openPath(n).catch(p=>ce.error(p instanceof Error?p.message:"Could not open the default app."))},pluginHref:pt,tab:_e(a),tabHref:ft,view:a.view};return r.jsxs("div",{className:"folder-drop-zone","data-mobile-landing":k&&m?"true":void 0,onClickCapture:n=>{if(n.defaultPrevented||n.button!==0||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||!(n.target instanceof Element))return;const w=n.target.closest("a[href]")?.getAttribute("href");if(w?.startsWith("/folder/")&&!ne(w)){n.preventDefault(),xe(w);return}},onDragEnter:n=>{l||[...n.dataTransfer.items].some(p=>p.kind==="file")&&(n.preventDefault(),A.current+=1,$(!0))},onDragLeave:()=>{A.current-=1,A.current<=0&&$(!1)},onDragOver:n=>{l||(n.preventDefault(),n.dataTransfer.dropEffect="copy")},onDrop:n=>{l||(n.preventDefault(),A.current=0,$(!1),He(n.dataTransfer.items).then(qe).then(p=>{window.history.replaceState(null,"",window.location.pathname),C(p)}))},children:[O?null:r.jsx(s.Suspense,{fallback:null,children:r.jsx(Ye,{canOpen:!l&&!y,homeHref:dt(),icon:Pe,name:ve,folder:_?.folder??a.folder,folderHref:B,collectionHref:H.collectionHref,workspace:e.workspace??_?.workspace,onOpen:()=>{if(m){me(!0);return}E.openRoot?.()},onOpenFolder:()=>{E.openRoot?.()}})}),k&&O?r.jsx(s.Suspense,{fallback:r.jsx(I,{}),children:q}):null,k&&!O&&!m?r.jsx(s.Suspense,{fallback:r.jsx(I,{}),children:r.jsx(oe,{...H,children:q})}):null,k&&!O&&m?r.jsx("div",{className:"mobile-landing-content",children:r.jsx(s.Suspense,{fallback:r.jsx(I,{}),children:r.jsx(oe,{...H,children:q})})}):null,W?r.jsx(s.Suspense,{fallback:null,children:r.jsx(Je,{source:ge})}):null,a.folder&&!W?r.jsx($e,{folder:a.folder},a.folder):null,V?r.jsx("div",{className:"folder-drop-overlay",children:"Drop folder to open"}):null,Y?r.jsx(s.Suspense,{fallback:null,children:r.jsx(We,{open:Y,onOpenChange:J})}):null]})}const Rt=Object.freeze(Object.defineProperty({__proto__:null,App:ht},Symbol.toStringTag,{value:"Module"}));export{xt as a,jt as b,Rt as c,N as f,Le as p,Ce as u};