kennel 0.2.12 → 0.2.13

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 (164) hide show
  1. package/dist/cli/src/plugins/external.js +19 -7
  2. package/dist/cli/src/site/server-plugins.d.ts +1 -4
  3. package/dist/core/api-client.js +3 -11
  4. package/dist/core/runtime.js +84 -695
  5. package/dist/package/preview.js +99 -729
  6. package/dist/plugins/external.js +19 -7
  7. package/dist/site/server-plugins.d.ts +1 -4
  8. package/dist/site/server-plugins.js +28 -537
  9. package/kernel/apps/web/package.json +0 -1
  10. package/kernel/apps/web/src/app.css +0 -1
  11. package/kernel/apps/web/src/app.tsx +7 -24
  12. package/kernel/apps/web/src/folder-plugin-view.tsx +6 -3
  13. package/kernel/apps/web/src/plugins.ts +2 -14
  14. package/kernel/apps/web/vite.config.ts +16 -3
  15. package/kernel/packages/api/openapi.json +85 -224
  16. package/kernel/packages/api/package.json +0 -1
  17. package/kernel/packages/api/src/contracts/schemas.ts +1 -9
  18. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +9 -151
  19. package/kernel/packages/api/src/server/plugins.ts +11 -26
  20. package/kernel/packages/api/src/shared/viewer-route.ts +3 -5
  21. package/kernel/packages/api-client/src/generated.d.ts +16 -53
  22. package/kernel/packages/markdown/README.md +2 -2
  23. package/kernel/packages/notes/package.json +1 -0
  24. package/kernel/packages/notes/src/client.tsx +0 -1
  25. package/kernel/packages/notes/src/manifest.ts +7 -0
  26. package/kernel/packages/notes/src/pages/notes-page.tsx +1 -1
  27. package/kernel/packages/notes/src/ui/folder-notes-list.tsx +52 -7
  28. package/kernel/packages/plugins/SKILL.md +3 -2
  29. package/kernel/packages/plugins/package.json +5 -1
  30. package/kernel/packages/plugins/src/hooks.ts +24 -0
  31. package/kernel/packages/plugins/src/index.ts +1 -7
  32. package/kernel/packages/plugins/src/page.ts +0 -7
  33. package/kernel/packages/plugins/src/types.ts +8 -0
  34. package/kernel/packages/plugins/tsconfig.json +1 -1
  35. package/kernel/packages/plugins/tsup.config.ts +1 -0
  36. package/kernel/packages/ui/src/components/folder-activity.tsx +16 -98
  37. package/kernel/packages/ui/src/components/folder-landing.tsx +4 -0
  38. package/kernel/packages/ui/src/components/pagination.tsx +88 -2
  39. package/kernel/packages/ui/src/components/plugin-icon.tsx +19 -0
  40. package/kernel/packages/ui/src/lib/folder-activity.ts +4 -17
  41. package/kernel/packages/ui/src/styles/folder-activity.css +1 -31
  42. package/package.json +2 -2
  43. package/web/assets/{abnfDiagram-VRR7QNED-Brm1vcGG.js → abnfDiagram-VRR7QNED-Dt7QEF5H.js} +1 -1
  44. package/web/assets/app-C1j063QX.js +2 -0
  45. package/web/assets/app-options-98-a6ivv.js +25 -0
  46. package/web/assets/{app-view-header-BSOI-wB6.js → app-view-header-BJ9CWTE1.js} +1 -1
  47. package/web/assets/{arc-zngHsMKV.js → arc-PFB0H7oD.js} +1 -1
  48. package/web/assets/{architectureDiagram-ZJ3FMSHR-BLklLqRP.js → architectureDiagram-ZJ3FMSHR-BHLqEXlZ.js} +1 -1
  49. package/web/assets/{ask-tray-B4SPj-fH.js → ask-tray-C3gBesHJ.js} +1 -1
  50. package/web/assets/{blockDiagram-677ZJIJ3-yhC2fEtu.js → blockDiagram-677ZJIJ3-UbnYoYDg.js} +1 -1
  51. package/web/assets/{c4Diagram-LMCZKHZV-DMcweQme.js → c4Diagram-LMCZKHZV-4OSx1V4W.js} +1 -1
  52. package/web/assets/channel-BuvY5cQN.js +1 -0
  53. package/web/assets/{chunk-2Q5K7J3B-DD-S6n8h.js → chunk-2Q5K7J3B-CgxJ1aJb.js} +1 -1
  54. package/web/assets/{chunk-32BRIVSS-DFIAKRv_.js → chunk-32BRIVSS-Bfwxg2LM.js} +1 -1
  55. package/web/assets/{chunk-5VM5RSS4-B23APb3c.js → chunk-5VM5RSS4-BZN9a2hI.js} +1 -1
  56. package/web/assets/{chunk-EX3LRPZG-BSVEB7db.js → chunk-EX3LRPZG-CrH6hhqq.js} +1 -1
  57. package/web/assets/{chunk-JWPE2WC7-B6qGd0ng.js → chunk-JWPE2WC7-Ddn7ZSp8.js} +1 -1
  58. package/web/assets/{chunk-MOJQB5TN-N4T1-ew1.js → chunk-MOJQB5TN-9lJ7f_hI.js} +1 -1
  59. package/web/assets/{chunk-RYQCIY6F-6oc6i_Xw.js → chunk-RYQCIY6F-QrFo_00s.js} +1 -1
  60. package/web/assets/{chunk-V7JOEXUC-Dftgm9ck.js → chunk-V7JOEXUC-Dn0lQjVf.js} +1 -1
  61. package/web/assets/{chunk-VR4S4FIN-DfjqwEsO.js → chunk-VR4S4FIN-DXVPySkB.js} +1 -1
  62. package/web/assets/{chunk-XXDRQBXY-CsFsQYow.js → chunk-XXDRQBXY-CBiFDOdZ.js} +1 -1
  63. package/web/assets/classDiagram-OUVF2IWQ-DCYNCJhe.js +1 -0
  64. package/web/assets/classDiagram-v2-EOCWNBFH-DCYNCJhe.js +1 -0
  65. package/web/assets/client-DBJY3XDL.js +43 -0
  66. package/web/assets/client-DKJs0w4y.js +6 -0
  67. package/web/assets/{command-D_9V8ejS.js → command-onjTnuXT.js} +1 -1
  68. package/web/assets/{copy-context-CevDWX59.js → copy-context-CciRaLNN.js} +1 -1
  69. package/web/assets/{cose-bilkent-JH36ORCC-DzKsIuDb.js → cose-bilkent-JH36ORCC-CGRic2um.js} +1 -1
  70. package/web/assets/{cynefin-VYW2F7L2-AL45ZHbw.js → cynefin-VYW2F7L2-Blk0Lu0l.js} +1 -1
  71. package/web/assets/{cynefinDiagram-TSTJHNR4-DSbBegxZ.js → cynefinDiagram-TSTJHNR4-DpnnHRDK.js} +1 -1
  72. package/web/assets/{dagre-VKFMJZFB-BaG6MGUE.js → dagre-VKFMJZFB-llzMokKp.js} +1 -1
  73. package/web/assets/{diagram-FQU43EPY-WbDotTX5.js → diagram-FQU43EPY-DC6xHovR.js} +1 -1
  74. package/web/assets/{diagram-G47NLZAW-DMbouYVI.js → diagram-G47NLZAW-B8GVS3EA.js} +1 -1
  75. package/web/assets/{diagram-NH7WQ7WH-DoEuaWd_.js → diagram-NH7WQ7WH-DWUaARfh.js} +1 -1
  76. package/web/assets/{diagram-OA4YK3LP-BjGd-PoH.js → diagram-OA4YK3LP-9Uaes_Yi.js} +1 -1
  77. package/web/assets/{diagram-WEI45ONY-CXHi27jF.js → diagram-WEI45ONY-DPPTBWud.js} +1 -1
  78. package/web/assets/{document-CVJCfWiQ.js → document-D23WzFsW.js} +3 -3
  79. package/web/assets/{ebnfDiagram-CCIWWBDH-B5vMUDOs.js → ebnfDiagram-CCIWWBDH-BPrKneNw.js} +1 -1
  80. package/web/assets/{entities-BTKzWJ-r.js → entities-DPg0B8vd.js} +1 -1
  81. package/web/assets/{erDiagram-Q63AITRT-DhO7ayzF.js → erDiagram-Q63AITRT-CFqYBcnb.js} +1 -1
  82. package/web/assets/{file-viewer-D1HK9J3A.js → file-viewer-Ck73SsLA.js} +1 -1
  83. package/web/assets/{flowDiagram-23GEKE2U-CublTUlC.js → flowDiagram-23GEKE2U-C7RpYqaT.js} +1 -1
  84. package/web/assets/folder-filter-bar-DzejUj87.js +4 -0
  85. package/web/assets/{folder-image-L_AjkRfI.js → folder-image-BjIicmQ6.js} +1 -1
  86. package/web/assets/folder-landing-CCIXZfkp.js +55 -0
  87. package/web/assets/folder-list-TuRwuTss.js +1 -0
  88. package/web/assets/folder-mobile-bar-Bqnfwi-z.js +1 -0
  89. package/web/assets/folder-plugin-view-CrinCX1o.js +2 -0
  90. package/web/assets/{ganttDiagram-NO4QXBWP-BLMJWerM.js → ganttDiagram-NO4QXBWP-Cz2sxU8F.js} +1 -1
  91. package/web/assets/{gitGraphDiagram-IHSO6WYX-Br0E6qyZ.js → gitGraphDiagram-IHSO6WYX-BSuz0yF-.js} +1 -1
  92. package/web/assets/index-DLn_2LPp.js +2 -0
  93. package/web/assets/index-uY9jLiL5.css +1 -0
  94. package/web/assets/{infoDiagram-FWYZ7A6U-Dgz0npJO.js → infoDiagram-FWYZ7A6U-CpeKgqoS.js} +1 -1
  95. package/web/assets/input-Bz_puamS.js +9 -0
  96. package/web/assets/{ishikawaDiagram-FXEZZL3T-rY5pdS3b.js → ishikawaDiagram-FXEZZL3T-5c2Tmknb.js} +1 -1
  97. package/web/assets/{journeyDiagram-5HDEW3XC-CKZALyR6.js → journeyDiagram-5HDEW3XC-Cu-wRkR4.js} +1 -1
  98. package/web/assets/{kanban-definition-HUTT4EX6-EGPY9Vda.js → kanban-definition-HUTT4EX6-BunSqkPe.js} +1 -1
  99. package/web/assets/{linear-DkJW9OwI.js → linear-C8e8bGVv.js} +1 -1
  100. package/web/assets/{mermaid.core-DVEpXYiF.js → mermaid.core-DLgjY1g2.js} +4 -4
  101. package/web/assets/{metadata-Bu_ffLUd.js → metadata-DEfJKlnd.js} +1 -1
  102. package/web/assets/{mindmap-definition-LN4V7U3C-BEOUVnDN.js → mindmap-definition-LN4V7U3C-W9ENYFOD.js} +1 -1
  103. package/web/assets/{pegDiagram-2B236MQR-DThjl3eK.js → pegDiagram-2B236MQR-0vPlHCFp.js} +1 -1
  104. package/web/assets/{pieDiagram-ENE6RG2P-DzmoGgw2.js → pieDiagram-ENE6RG2P-CakaveRV.js} +1 -1
  105. package/web/assets/{project-search-dialog-e2Jmhvfq.js → project-search-dialog-BSKA4peX.js} +1 -1
  106. package/web/assets/{quadrantDiagram-ABIIQ3AL-BqT8U3FS.js → quadrantDiagram-ABIIQ3AL-jxHw_egj.js} +1 -1
  107. package/web/assets/{railroadDiagram-RFXS5EU6-1k1tfPk1.js → railroadDiagram-RFXS5EU6-lIjDXPZt.js} +1 -1
  108. package/web/assets/{requirementDiagram-TGXJPOKE-bM4RTd2J.js → requirementDiagram-TGXJPOKE-C-JiT4bc.js} +1 -1
  109. package/web/assets/{sankeyDiagram-HTMAVEWB-TCgfqDLy.js → sankeyDiagram-HTMAVEWB-SuLPAw9X.js} +1 -1
  110. package/web/assets/{search-DQiFH2up.js → search-CKujW14f.js} +1 -1
  111. package/web/assets/{selection-popup-bqfgVd-L.js → selection-popup-CtxT9SWn.js} +3 -3
  112. package/web/assets/{sequenceDiagram-DBY2YBRQ-BRhGHI0i.js → sequenceDiagram-DBY2YBRQ-o9UwTkZR.js} +1 -1
  113. package/web/assets/{sizeCapture-X5ZJPWSS-EeUWgn0N.js → sizeCapture-X5ZJPWSS-ClbWGybF.js} +1 -1
  114. package/web/assets/{stateDiagram-2N3HPSRC-BSOG758d.js → stateDiagram-2N3HPSRC-Ce4avoWx.js} +1 -1
  115. package/web/assets/stateDiagram-v2-6OUMAXLB-CsrPTZoJ.js +1 -0
  116. package/web/assets/{swimlanes-5IMT3BWC-DpCo4aEm.js → swimlanes-5IMT3BWC-DMZI4XZT.js} +2 -2
  117. package/web/assets/swimlanesDiagram-G3AALYLV-pShSVcTd.js +8 -0
  118. package/web/assets/{timeline-definition-FHXFAJF6-BpaObe6u.js → timeline-definition-FHXFAJF6-CPIpIgPN.js} +1 -1
  119. package/web/assets/{vennDiagram-L72KCM5P-ClvUzW8B.js → vennDiagram-L72KCM5P-_rha8uDZ.js} +1 -1
  120. package/web/assets/view-context-BDtIUHQy.js +229 -0
  121. package/web/assets/{viewer-capabilities-CFBwvpcI.js → viewer-capabilities-YYZqMs56.js} +1 -1
  122. package/web/assets/{wardleyDiagram-EHGQE667-Dg1CX0Fl.js → wardleyDiagram-EHGQE667-D4EAOS35.js} +1 -1
  123. package/web/assets/{xychartDiagram-FW5EYKEG-C0c9zQTr.js → xychartDiagram-FW5EYKEG-DR8PwXpe.js} +1 -1
  124. package/web/index.html +2 -2
  125. package/kernel/packages/agents/eslint.config.js +0 -10
  126. package/kernel/packages/agents/package.json +0 -75
  127. package/kernel/packages/agents/src/agent-card.tsx +0 -334
  128. package/kernel/packages/agents/src/client.tsx +0 -13
  129. package/kernel/packages/agents/src/index.ts +0 -18
  130. package/kernel/packages/agents/src/manifest.ts +0 -7
  131. package/kernel/packages/agents/src/node.ts +0 -447
  132. package/kernel/packages/agents/src/profiles.ts +0 -137
  133. package/kernel/packages/agents/src/resolve.ts +0 -69
  134. package/kernel/packages/agents/src/server.ts +0 -60
  135. package/kernel/packages/agents/src/types.ts +0 -82
  136. package/kernel/packages/agents/src/viewer.tsx +0 -370
  137. package/kernel/packages/agents/test/profiles.test.mjs +0 -88
  138. package/kernel/packages/agents/test/resolve.test.mjs +0 -729
  139. package/kernel/packages/agents/tsconfig.json +0 -17
  140. package/kernel/packages/api/test/agents-loading.test.ts +0 -94
  141. package/kernel/packages/ui/src/components/folder-skill-list.tsx +0 -62
  142. package/web/assets/app-Debw-lUT.js +0 -2
  143. package/web/assets/app-options-UU_JlJQv.js +0 -25
  144. package/web/assets/channel-BgYLn5Cm.js +0 -1
  145. package/web/assets/classDiagram-OUVF2IWQ-Fzhmfbkt.js +0 -1
  146. package/web/assets/classDiagram-v2-EOCWNBFH-Fzhmfbkt.js +0 -1
  147. package/web/assets/client-C1WdY7jQ.js +0 -22
  148. package/web/assets/client-D__L4Hl0.js +0 -17
  149. package/web/assets/client-DoMhF9Tv.js +0 -5
  150. package/web/assets/folder-filter-bar-BHz7s9Vb.js +0 -4
  151. package/web/assets/folder-landing-Byh8W67L.js +0 -54
  152. package/web/assets/folder-list-jMyo-7pV.js +0 -1
  153. package/web/assets/folder-metadata-bCocvxpP.js +0 -2
  154. package/web/assets/folder-mobile-bar-DvvyOS-a.js +0 -1
  155. package/web/assets/folder-plugin-view-sib0Dwfw.js +0 -3
  156. package/web/assets/index-4Xh9UKim.js +0 -1
  157. package/web/assets/index-Bu75S2qS.js +0 -1
  158. package/web/assets/index-CPcMcCUY.js +0 -2
  159. package/web/assets/index-CXxkvzNJ.css +0 -1
  160. package/web/assets/select-CkOwL81C.js +0 -30
  161. package/web/assets/stateDiagram-v2-6OUMAXLB-DUeGDz1r.js +0 -1
  162. package/web/assets/swimlanesDiagram-G3AALYLV-BGOaCfyV.js +0 -8
  163. package/web/assets/use-folder-filter-hA6fa7nm.js +0 -1
  164. package/web/assets/view-context-CVvDw2JM.js +0 -229
@@ -16,7 +16,8 @@ Keep each concern in its owner:
16
16
  | Folder metadata API and server registry | `packages/api` |
17
17
  | Browser route and static external-plugin registry | `apps/web` |
18
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` |
19
+ | Built-in Notes feature code | `packages/notes` |
20
+ | Skills and Agents feature code | `raggle-ai/kennel-starter-plugins`, under `plugins/skills` and `plugins/agents` |
20
21
  | External plugin feature code | Its own repository |
21
22
 
22
23
  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.
@@ -34,6 +35,6 @@ Keep new plugin feature code in its own repository. Add to `packages/plugins` on
34
35
 
35
36
  - Start with the smallest plugin repository change. Do not copy plugin feature code into Kennel.
36
37
  - 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
+ - Keep plugin ids stable. Only `notes` is reserved for the core. The starter plugins use `skills` and `kennel-agents`; both use `/views/<id>` routes.
38
39
  - 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
40
  - A CLI behavior change requires a new `cli/package.json` version. The publish workflow releases it after merge to `main`.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "kennel-plugins",
4
- "version": "0.1.3",
4
+ "version": "0.1.4",
5
5
  "description": "Small shared contract for trusted folder view plugins in the Kennel web viewer.",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -32,6 +32,10 @@
32
32
  "types": "./dist/endpoint.d.ts",
33
33
  "import": "./dist/endpoint.js"
34
34
  },
35
+ "./hooks": {
36
+ "types": "./dist/hooks.d.ts",
37
+ "import": "./dist/hooks.js"
38
+ },
35
39
  "./page": {
36
40
  "types": "./dist/page.d.ts",
37
41
  "import": "./dist/page.js"
@@ -0,0 +1,24 @@
1
+ import { createContext, createElement, useContext } from "react"
2
+ import type { ReactNode } from "react"
3
+
4
+ const FolderPathContext = createContext<string | undefined>(undefined)
5
+
6
+ /** Host boundary. The host and plugin must resolve this module once. */
7
+ export function FolderPathProvider({
8
+ folder,
9
+ children,
10
+ }: {
11
+ folder: string
12
+ children: ReactNode
13
+ }) {
14
+ return createElement(FolderPathContext.Provider, { value: folder }, children)
15
+ }
16
+
17
+ /** Current viewed folder. This is not a filter selection or a permission check. */
18
+ export function useFolderPath(): string {
19
+ const folder = useContext(FolderPathContext)
20
+ if (folder === undefined) {
21
+ throw new Error("useFolderPath must be used inside FolderPathProvider")
22
+ }
23
+ return folder
24
+ }
@@ -5,11 +5,9 @@ export {
5
5
  CopyContextButton,
6
6
  FilePreview,
7
7
  FilePreviewList,
8
- FolderFilterBar,
9
8
  PluginPage,
10
9
  PluginPageError,
11
10
  PluginPageStatus,
12
- useFolderFilter,
13
11
  } from "./page.ts"
14
12
  export { folderPathId } from "./client.ts"
15
13
  export {
@@ -27,6 +25,7 @@ export { isPluginPayloadError } from "./types.ts"
27
25
  export type {
28
26
  FolderPluginDataset,
29
27
  FolderPluginDetection,
28
+ FolderPluginIcon,
30
29
  FolderPluginId,
31
30
  FolderPluginManifest,
32
31
  FolderPluginPayload,
@@ -45,8 +44,3 @@ export type {
45
44
  FolderPluginInitResult,
46
45
  FolderPluginServer,
47
46
  } from "./server.ts"
48
- export type {
49
- FolderFilterChoice,
50
- FolderFilterMode,
51
- FolderSelection,
52
- } from "./page.ts"
@@ -8,10 +8,3 @@ export {
8
8
  FilePreview,
9
9
  FilePreviewList,
10
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"
@@ -13,9 +13,17 @@ export type ContentIssue = {
13
13
  severity?: "warning"
14
14
  }
15
15
 
16
+ /** Plugin-owned SVG path data. No React or page imports are needed for tabs. */
17
+ export type FolderPluginIcon = {
18
+ /** Paths use a 24 × 24 view box and the navigation text color. */
19
+ paths: string[]
20
+ }
21
+
16
22
  export type FolderPluginManifest = {
17
23
  /** Stable identifier used in routes, URLs, and registries. Never a URL segment turned into an import path. */
18
24
  id: FolderPluginId
25
+ /** Optional plugin-owned navigation icon. Omit for a text-only tab. */
26
+ icon?: FolderPluginIcon
19
27
  /** Navigation label shown in the folder shell. */
20
28
  label: string
21
29
  }
@@ -16,6 +16,6 @@
16
16
  "kennel-client": ["../api-client/src/index.ts"]
17
17
  }
18
18
  },
19
- "include": ["src"],
19
+ "include": ["src", "tsup.config.ts"],
20
20
  "exclude": ["node_modules", "dist", "eslint.config.mjs"]
21
21
  }
@@ -6,6 +6,7 @@ export default defineConfig({
6
6
  "src/client.ts",
7
7
  "src/endpoint.ts",
8
8
  "src/page.ts",
9
+ "src/hooks.ts",
9
10
  "src/server.ts",
10
11
  "src/subpath.ts",
11
12
  "src/types.ts",
@@ -1,21 +1,16 @@
1
1
  import type { CSSProperties, ReactNode } from "react"
2
2
  import {
3
- Bot,
4
- CalendarRange,
5
3
  ChevronRight,
6
4
  FileText,
7
5
  Folder,
8
- FolderGit2,
9
6
  FolderOpen,
10
- ListTodo,
11
- Mail,
12
7
  // Sessions functionality is commented out (can be re-enabled).
13
8
  // MessageCircle,
14
- Sparkles,
15
9
  } from "lucide-react"
16
10
 
17
11
  import type {
18
12
  FolderMetadata,
13
+ FolderPluginItem,
19
14
  // Sessions functionality is commented out (can be re-enabled).
20
15
  // FolderSession,
21
16
  FolderTab,
@@ -31,8 +26,8 @@ import { FolderImage } from "@raggle-ai/ui/components/folder-image"
31
26
  import { FolderProjectList } from "@raggle-ai/ui/components/folder-project-list"
32
27
  // Sessions functionality is commented out (can be re-enabled).
33
28
  // import { FolderSessionList } from "@raggle-ai/ui/components/folder-session-list"
34
- import { FolderSkillList } from "@raggle-ai/ui/components/folder-skill-list"
35
29
  import { FolderTabs } from "@raggle-ai/ui/components/folder-tabs"
30
+ import { FolderPluginIcon } from "@raggle-ai/ui/components/plugin-icon"
36
31
  import {
37
32
  SidebarInset,
38
33
  SidebarMenuButton,
@@ -45,9 +40,7 @@ import { Skeleton } from "@raggle-ai/ui/components/skeleton"
45
40
  type ActivityView =
46
41
  // Sessions functionality is commented out (can be re-enabled).
47
42
  // | { type: "sessions" }
48
- | { id: string; type: "plugin" }
49
- | { skills?: true; type: "skills" }
50
- | { type: "folder"; id?: string }
43
+ { id: string; type: "plugin" } | { type: "folder"; id?: string }
51
44
  type ActivityItem =
52
45
  | {
53
46
  count: number
@@ -68,16 +61,6 @@ type ActivityItem =
68
61
  view?: undefined
69
62
  }
70
63
 
71
- /** Presentation-only mapping for explicitly registered plugin ids. */
72
- function pluginIcon(id: string): ReactNode {
73
- if (id === "calendar") return <CalendarRange aria-hidden="true" />
74
- if (id === "tasks") return <ListTodo aria-hidden="true" />
75
- if (id === "mail") return <Mail aria-hidden="true" />
76
- if (id === "agents") return <Bot aria-hidden="true" />
77
- if (id === "projects") return <FolderGit2 aria-hidden="true" />
78
- return <Folder aria-hidden="true" />
79
- }
80
-
81
64
  function Section({
82
65
  children,
83
66
  count,
@@ -178,6 +161,7 @@ function FolderActivityLoading({
178
161
  onOpenFolder,
179
162
  pluginHref,
180
163
  view,
164
+ viewNavigation,
181
165
  tab,
182
166
  tabHref,
183
167
  }: {
@@ -188,6 +172,7 @@ function FolderActivityLoading({
188
172
  onOpenFolder?: () => void
189
173
  pluginHref: (id: string) => string
190
174
  view?: { pluginId: string; subpath?: string }
175
+ viewNavigation?: Pick<FolderPluginItem, "icon" | "label">
191
176
  tab?: FolderTab
192
177
  tabHref: (tab: FolderTab) => string
193
178
  }) {
@@ -195,7 +180,7 @@ function FolderActivityLoading({
195
180
  // const active = tab ?? "sessions"
196
181
  const active = tab ?? "files"
197
182
  const pluginSelected = Boolean(view)
198
- const title = active === "skills" ? "Skills" : "Notes"
183
+ const title = "Notes"
199
184
 
200
185
  const tabs: Array<{
201
186
  href: string
@@ -209,25 +194,13 @@ function FolderActivityLoading({
209
194
  key: "files",
210
195
  label: "Notes",
211
196
  },
212
- {
213
- href: pluginHref("tasks"),
214
- icon: pluginIcon("tasks"),
215
- key: "plugin:tasks",
216
- label: "Tasks",
217
- },
218
- {
219
- href: tabHref("skills"),
220
- icon: <Sparkles aria-hidden="true" />,
221
- key: "skills",
222
- label: "Skills",
223
- },
224
197
  ]
225
- if (view && view.pluginId !== "tasks") {
198
+ if (view) {
226
199
  tabs.push({
227
200
  href: pluginHref(view.pluginId),
228
- icon: pluginIcon(view.pluginId),
201
+ icon: <FolderPluginIcon icon={viewNavigation?.icon} />,
229
202
  key: `plugin:${view.pluginId}`,
230
- label: view.pluginId,
203
+ label: viewNavigation?.label ?? view.pluginId,
231
204
  })
232
205
  }
233
206
 
@@ -253,11 +226,7 @@ function FolderActivityLoading({
253
226
  <section className="landing-section landing-loading-section">
254
227
  <header>
255
228
  <h2>
256
- {active === "skills" ? (
257
- <Sparkles aria-hidden="true" />
258
- ) : (
259
- <FileText aria-hidden="true" />
260
- )}
229
+ <FileText aria-hidden="true" />
261
230
  {title}
262
231
  </h2>
263
232
  <Skeleton className="landing-skeleton-count" />
@@ -348,6 +317,7 @@ function FolderActivity({
348
317
  // sessionTranscriptHref,
349
318
  pluginHref,
350
319
  view,
320
+ viewNavigation,
351
321
  tab,
352
322
  tabHref,
353
323
  }: {
@@ -367,12 +337,11 @@ function FolderActivity({
367
337
  // sessionTranscriptHref?: (session: FolderSession) => string
368
338
  pluginHref: (id: string) => string
369
339
  view?: { pluginId: string; subpath?: string }
340
+ viewNavigation?: Pick<FolderPluginItem, "icon" | "label">
370
341
  tab?: FolderTab
371
342
  tabHref: (tab: FolderTab) => string
372
343
  }) {
373
- const skills = data?.skills?.items ?? []
374
344
  const plugins = data?.plugins?.items ?? []
375
- const pluginMap = new Map(plugins.map((plugin) => [plugin.id, plugin]))
376
345
  const configWarnings =
377
346
  data?.plugins?.configWarnings ?? data?.configWarnings ?? []
378
347
  const coreFolders = data?.workspace?.schemas ?? []
@@ -389,6 +358,7 @@ function FolderActivity({
389
358
  tabHref={tabHref}
390
359
  pluginHref={pluginHref}
391
360
  view={view}
361
+ viewNavigation={viewNavigation}
392
362
  />
393
363
  )
394
364
  }
@@ -400,7 +370,6 @@ function FolderActivity({
400
370
  // tab === "sessions" ? data.sessions.items : data.sessions.items.slice(0, 8)
401
371
  // Sessions functionality is commented out (can be re-enabled).
402
372
  // const sessionsSelected = tab === "sessions"
403
- const skillsSelected = tab === "skills"
404
373
  // A plugin view can render without a navigation item. Disabled plugins are
405
374
  // rejected by the API request, so direct URLs show the shell error state.
406
375
  const pluginSelected = Boolean(view)
@@ -415,52 +384,13 @@ function FolderActivity({
415
384
  key: "files",
416
385
  label: "Notes",
417
386
  },
418
- ...(pluginMap.has("projects")
419
- ? [
420
- {
421
- count: pluginMap.get("projects")?.count ?? 0,
422
- error: pluginMap.get("projects")?.error,
423
- href: pluginHref("projects"),
424
- icon: pluginIcon("projects"),
425
- key: "plugin:projects",
426
- label: "Projects",
427
- view: { id: "projects", type: "plugin" } as ActivityView,
428
- },
429
- ]
430
- : []),
431
- ...(pluginMap.has("tasks")
432
- ? [
433
- {
434
- count: pluginMap.get("tasks")?.count ?? 0,
435
- error: pluginMap.get("tasks")?.error,
436
- href: pluginHref("tasks"),
437
- icon: pluginIcon("tasks"),
438
- key: "plugin:tasks",
439
- label: "Tasks",
440
- view: { id: "tasks", type: "plugin" } as ActivityView,
441
- },
442
- ]
443
- : []),
444
- {
445
- count: skills.length,
446
- href: tabHref("skills"),
447
- icon: <Sparkles aria-hidden="true" />,
448
- key: "skills",
449
- label: "Skills",
450
- view: { skills: true, type: "skills" },
451
- },
452
387
  ...plugins
453
- .filter(
454
- (plugin) =>
455
- plugin.id !== "notes" &&
456
- plugin.id !== "tasks" &&
457
- plugin.id !== "projects",
458
- )
388
+ .filter((plugin) => plugin.id !== "notes")
459
389
  .map((plugin) => ({
460
390
  count: plugin.count ?? 0,
461
391
  error: plugin.error,
462
392
  href: pluginHref(plugin.id),
463
- icon: pluginIcon(plugin.id),
393
+ icon: <FolderPluginIcon icon={plugin.icon} />,
464
394
  key: `plugin:${plugin.id}`,
465
395
  label: plugin.label,
466
396
  view: { id: plugin.id, type: "plugin" } as ActivityView,
@@ -505,16 +435,6 @@ function FolderActivity({
505
435
  </div>
506
436
  ) : null}
507
437
 
508
- {skillsSelected ? (
509
- <Section
510
- count={skills.length}
511
- icon={<Sparkles aria-hidden="true" />}
512
- title="Skills"
513
- >
514
- <FolderSkillList onOpenPath={onOpenPath} skills={skills} />
515
- </Section>
516
- ) : null}
517
-
518
438
  {tab === "projects" && projects.length ? (
519
439
  <Section
520
440
  count={projects.length}
@@ -567,9 +487,7 @@ function FolderActivity({
567
487
  return Boolean(collectionId && item.view.id === collectionId)
568
488
  return item.view?.type === "plugin"
569
489
  ? pluginSelected && item.view.id === view?.pluginId
570
- : item.view?.type === "skills"
571
- ? skillsSelected
572
- : tab === "files" || showDefaultFiles
490
+ : tab === "files" || showDefaultFiles
573
491
  }
574
492
 
575
493
  return (
@@ -2,6 +2,7 @@ import type { ReactNode } from "react"
2
2
 
3
3
  import type {
4
4
  FolderMetadata,
5
+ FolderPluginItem,
5
6
  // Sessions functionality is commented out (can be re-enabled).
6
7
  // FolderSession,
7
8
  FolderTab,
@@ -26,6 +27,7 @@ export function FolderLanding({
26
27
  tab,
27
28
  tabHref,
28
29
  view,
30
+ viewNavigation,
29
31
  }: {
30
32
  children?: ReactNode
31
33
  collectionHref?: (id: string) => string
@@ -45,6 +47,7 @@ export function FolderLanding({
45
47
  tab?: FolderTab
46
48
  tabHref: (tab: FolderTab) => string
47
49
  view?: { pluginId: string; subpath?: string }
50
+ viewNavigation?: Pick<FolderPluginItem, "icon" | "label">
48
51
  }) {
49
52
  return (
50
53
  <FolderActivity
@@ -65,6 +68,7 @@ export function FolderLanding({
65
68
  tab={tab}
66
69
  tabHref={tabHref}
67
70
  view={view}
71
+ viewNavigation={viewNavigation}
68
72
  />
69
73
  )
70
74
  }
@@ -5,8 +5,7 @@ import {
5
5
  MoreHorizontalIcon,
6
6
  } from "lucide-react"
7
7
 
8
- import type { Button } from "@raggle-ai/ui/components/button"
9
- import { buttonVariants } from "@raggle-ai/ui/components/button"
8
+ import { Button, buttonVariants } from "@raggle-ai/ui/components/button"
10
9
  import { cn } from "@raggle-ai/ui/lib/utils"
11
10
 
12
11
  function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
@@ -117,7 +116,94 @@ function PaginationEllipsis({
117
116
  )
118
117
  }
119
118
 
119
+ /** Controlled, zero-based offset pagination for an in-memory collection. */
120
+ function OffsetPagination({
121
+ total,
122
+ offset,
123
+ pageSize,
124
+ onOffsetChange,
125
+ label = "Items",
126
+ }: {
127
+ total: number
128
+ offset: number
129
+ pageSize: number
130
+ onOffsetChange: (offset: number) => void
131
+ label?: string
132
+ }) {
133
+ const pageCount = Math.max(1, Math.ceil(total / pageSize))
134
+ const page = Math.min(Math.floor(offset / pageSize), pageCount - 1)
135
+ const pages = Array.from(
136
+ new Set([0, page - 1, page, page + 1, pageCount - 1]),
137
+ )
138
+ .filter((value) => value >= 0 && value < pageCount)
139
+ .sort((left, right) => left - right)
140
+
141
+ return (
142
+ <div className="grid gap-2">
143
+ <p
144
+ className="text-muted-foreground m-0 text-center text-sm"
145
+ role="status"
146
+ >
147
+ {label} {total ? page * pageSize + 1 : 0}–
148
+ {Math.min((page + 1) * pageSize, total)} of {total}
149
+ </p>
150
+ {pageCount > 1 ? (
151
+ <Pagination aria-label={`${label} pagination`}>
152
+ <PaginationContent className="gap-0 sm:gap-1">
153
+ <PaginationItem>
154
+ <Button
155
+ variant="ghost"
156
+ size="icon"
157
+ className="size-8 sm:size-9"
158
+ aria-label="Previous page"
159
+ disabled={page === 0}
160
+ onClick={() => onOffsetChange((page - 1) * pageSize)}
161
+ >
162
+ <ChevronLeftIcon aria-hidden="true" />
163
+ </Button>
164
+ </PaginationItem>
165
+ {pages.map((value, index) => (
166
+ <React.Fragment key={value}>
167
+ {index > 0 && value - pages[index - 1]! > 1 ? (
168
+ <PaginationItem>
169
+ <PaginationEllipsis className="size-6 sm:size-9" />
170
+ </PaginationItem>
171
+ ) : null}
172
+ <PaginationItem>
173
+ <Button
174
+ variant={value === page ? "outline" : "ghost"}
175
+ size="icon"
176
+ className="size-8 sm:size-9"
177
+ aria-label={`Page ${value + 1}`}
178
+ aria-current={value === page ? "page" : undefined}
179
+ onClick={() => onOffsetChange(value * pageSize)}
180
+ >
181
+ {value + 1}
182
+ </Button>
183
+ </PaginationItem>
184
+ </React.Fragment>
185
+ ))}
186
+ <PaginationItem>
187
+ <Button
188
+ variant="ghost"
189
+ size="icon"
190
+ className="size-8 sm:size-9"
191
+ aria-label="Next page"
192
+ disabled={page === pageCount - 1}
193
+ onClick={() => onOffsetChange((page + 1) * pageSize)}
194
+ >
195
+ <ChevronRightIcon aria-hidden="true" />
196
+ </Button>
197
+ </PaginationItem>
198
+ </PaginationContent>
199
+ </Pagination>
200
+ ) : null}
201
+ </div>
202
+ )
203
+ }
204
+
120
205
  export {
206
+ OffsetPagination,
121
207
  Pagination,
122
208
  PaginationContent,
123
209
  PaginationLink,
@@ -0,0 +1,19 @@
1
+ /** Render plugin-owned path data without importing its page or an icon catalog. */
2
+ export function FolderPluginIcon({ icon }: { icon?: { paths: string[] } }) {
3
+ if (!icon) return null
4
+ return (
5
+ <svg
6
+ aria-hidden="true"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ strokeWidth={2}
11
+ strokeLinecap="round"
12
+ strokeLinejoin="round"
13
+ >
14
+ {icon.paths.map((path, index) => (
15
+ <path key={index} d={path} />
16
+ ))}
17
+ </svg>
18
+ )
19
+ }
@@ -22,14 +22,6 @@ export type FolderProject = {
22
22
  worktree: string
23
23
  }
24
24
 
25
- export type FolderSkill = {
26
- description?: string
27
- name: string
28
- openHref?: string
29
- relativePath: string
30
- source: "global" | "local"
31
- }
32
-
33
25
  export type FolderSession = {
34
26
  client: "claude" | "codex" | "cursor" | "opencode" | "pi"
35
27
  href?: string
@@ -58,6 +50,8 @@ export type FolderPluginItem = {
58
50
  count?: number
59
51
  /** Detection failure detail; the item is still shown but flagged. */
60
52
  error?: string
53
+ /** Plugin-owned SVG path data. Omit for a text-only tab. */
54
+ icon?: { paths: string[] }
61
55
  /** Trusted plugin id, registered in the host's explicit browser registry. */
62
56
  id: string
63
57
  label: string
@@ -72,7 +66,6 @@ export type FolderMetadata = {
72
66
  plugins?: { configWarnings?: string[]; items: FolderPluginItem[] }
73
67
  projects: { items: FolderProject[] }
74
68
  sessions: { items: FolderSession[] }
75
- skills?: { items: FolderSkill[] }
76
69
  workspace?: {
77
70
  breadcrumbs: Array<{
78
71
  icon?: FolderIcon
@@ -104,17 +97,11 @@ export type FolderMetadata = {
104
97
  }
105
98
  }
106
99
 
107
- export type FolderTab = "files" | "projects" | "sessions" | "skills"
100
+ export type FolderTab = "files" | "projects" | "sessions"
108
101
 
109
102
  export function folderTab(search: string): FolderTab | undefined {
110
103
  const tab = new URLSearchParams(search).get("tab")
111
- if (
112
- tab === "files" ||
113
- tab === "projects" ||
114
- tab === "sessions" ||
115
- tab === "skills"
116
- )
117
- return tab
104
+ if (tab === "files" || tab === "projects" || tab === "sessions") return tab
118
105
  return undefined
119
106
  }
120
107
 
@@ -965,8 +965,7 @@
965
965
  color: #747c75;
966
966
  }
967
967
 
968
- .landing-sessions li,
969
- .landing-skills li {
968
+ .landing-sessions li {
970
969
  display: grid;
971
970
  grid-template-columns: minmax(0, 1fr) 36px;
972
971
  }
@@ -975,35 +974,6 @@
975
974
  grid-template-columns: 24px auto minmax(0, 1fr) auto;
976
975
  }
977
976
 
978
- .landing-skills .landing-list-row {
979
- grid-template-columns: minmax(100px, 190px) minmax(0, 1fr) auto;
980
- min-height: 32px;
981
- padding-block: 6px;
982
- }
983
-
984
- .landing-skills strong {
985
- min-width: 0;
986
- color: #363d37;
987
- font-size: 13px;
988
- font-weight: 450;
989
- }
990
-
991
- .landing-skills span {
992
- overflow: hidden;
993
- min-width: 0;
994
- color: #9aa09a;
995
- font-size: 12px;
996
- text-overflow: ellipsis;
997
- white-space: nowrap;
998
- }
999
-
1000
- .landing-skills small {
1001
- margin-top: 0;
1002
- color: #8d948e;
1003
- font-size: 12px;
1004
- text-align: right;
1005
- }
1006
-
1007
977
  .landing-sessions li.is-resumable {
1008
978
  grid-template-columns: minmax(0, 1fr) 44px;
1009
979
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kennel",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
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",
@@ -116,7 +116,7 @@
116
116
  },
117
117
  "devDependencies": {
118
118
  "@raggle-ai/kennel-preview": "0.1.0",
119
- "kennel-plugins": "0.1.3",
119
+ "kennel-plugins": "0.1.4",
120
120
  "@types/node": "^22.13.4",
121
121
  "typescript": "^5.9.3"
122
122
  },
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-N4T1-ew1.js";import{p as f}from"./chunk-JWPE2WC7-B6qGd0ng.js";import{_ as n,l as o}from"./mermaid.core-DVEpXYiF.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-AL45ZHbw.js";import"./index-CPcMcCUY.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-9lJ7f_hI.js";import{p as f}from"./chunk-JWPE2WC7-Ddn7ZSp8.js";import{_ as n,l as o}from"./mermaid.core-DLgjY1g2.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-Blk0Lu0l.js";import"./index-DLn_2LPp.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};