kennel 0.2.11 → 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.
- package/dist/cli/src/plugins/external.js +19 -7
- package/dist/cli/src/site/server-plugins.d.ts +1 -4
- package/dist/core/api-client.js +3 -11
- package/dist/core/runtime.js +84 -695
- package/dist/package/preview.js +99 -729
- package/dist/plugins/external.js +19 -7
- package/dist/site/server-plugins.d.ts +1 -4
- package/dist/site/server-plugins.js +28 -537
- package/kernel/apps/web/package.json +0 -1
- package/kernel/apps/web/src/app.css +0 -1
- package/kernel/apps/web/src/app.tsx +7 -24
- package/kernel/apps/web/src/folder-plugin-view.tsx +6 -3
- package/kernel/apps/web/src/plugins.ts +2 -14
- package/kernel/apps/web/vite.config.ts +22 -20
- package/kernel/packages/api/openapi.json +85 -224
- package/kernel/packages/api/package.json +0 -1
- package/kernel/packages/api/src/contracts/schemas.ts +1 -9
- package/kernel/packages/api/src/server/folder-metadata-plugin.ts +9 -151
- package/kernel/packages/api/src/server/plugins.ts +11 -26
- package/kernel/packages/api/src/shared/viewer-route.ts +3 -5
- package/kernel/packages/api-client/src/generated.d.ts +16 -53
- package/kernel/packages/markdown/README.md +2 -2
- package/kernel/packages/notes/package.json +1 -0
- package/kernel/packages/notes/src/client.tsx +0 -1
- package/kernel/packages/notes/src/manifest.ts +7 -0
- package/kernel/packages/notes/src/pages/notes-page.tsx +1 -1
- package/kernel/packages/notes/src/ui/folder-notes-list.tsx +52 -7
- package/kernel/packages/plugins/SKILL.md +3 -2
- package/kernel/packages/plugins/package.json +5 -1
- package/kernel/packages/plugins/src/hooks.ts +24 -0
- package/kernel/packages/plugins/src/index.ts +1 -7
- package/kernel/packages/plugins/src/page.ts +0 -7
- package/kernel/packages/plugins/src/types.ts +8 -0
- package/kernel/packages/plugins/tsconfig.json +1 -1
- package/kernel/packages/plugins/tsup.config.ts +1 -0
- package/kernel/packages/ui/src/components/folder-activity.tsx +16 -98
- package/kernel/packages/ui/src/components/folder-landing.tsx +4 -0
- package/kernel/packages/ui/src/components/markdown-copy-block.tsx +78 -0
- package/kernel/packages/ui/src/components/pagination.tsx +88 -2
- package/kernel/packages/ui/src/components/plugin-icon.tsx +19 -0
- package/kernel/packages/ui/src/lib/folder-activity.ts +4 -17
- package/kernel/packages/ui/src/lib/source-components.tsx +12 -6
- package/kernel/packages/ui/src/styles/folder-activity.css +1 -31
- package/package.json +2 -2
- package/web/assets/{abnfDiagram-VRR7QNED-CSaxgLw6.js → abnfDiagram-VRR7QNED-Dt7QEF5H.js} +1 -1
- package/web/assets/app-C1j063QX.js +2 -0
- package/web/assets/app-options-98-a6ivv.js +25 -0
- package/web/assets/{app-view-header-Cpx74SQs.js → app-view-header-BJ9CWTE1.js} +1 -1
- package/web/assets/{arc-DOrTJz--.js → arc-PFB0H7oD.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-EjOjwlGK.js → architectureDiagram-ZJ3FMSHR-BHLqEXlZ.js} +1 -1
- package/web/assets/{ask-tray-DLEoRbpV.js → ask-tray-C3gBesHJ.js} +1 -1
- package/web/assets/{blockDiagram-677ZJIJ3-Dg2LWLOW.js → blockDiagram-677ZJIJ3-UbnYoYDg.js} +1 -1
- package/web/assets/{button-C6arH7zt.js → button-CuuAtS1I.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CWHqGPk8.js → c4Diagram-LMCZKHZV-4OSx1V4W.js} +1 -1
- package/web/assets/channel-BuvY5cQN.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-Dzz0QWV_.js → chunk-2Q5K7J3B-CgxJ1aJb.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-BocStooX.js → chunk-32BRIVSS-Bfwxg2LM.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-D8BF5Y5h.js → chunk-5VM5RSS4-BZN9a2hI.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-CWMl9KkL.js → chunk-EX3LRPZG-CrH6hhqq.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-BZEr7iFk.js → chunk-JWPE2WC7-Ddn7ZSp8.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-DbKnLJ33.js → chunk-MOJQB5TN-9lJ7f_hI.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-BF9KWoi2.js → chunk-RYQCIY6F-QrFo_00s.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-e88zamUH.js → chunk-V7JOEXUC-Dn0lQjVf.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-NDHM58HJ.js → chunk-VR4S4FIN-DXVPySkB.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-BtzSzNoD.js → chunk-XXDRQBXY-CBiFDOdZ.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-DCYNCJhe.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-DCYNCJhe.js +1 -0
- package/web/assets/client-DBJY3XDL.js +43 -0
- package/web/assets/client-DKJs0w4y.js +6 -0
- package/web/assets/{command-BWtewOrt.js → command-onjTnuXT.js} +1 -1
- package/web/assets/{copy-context-YESgZ_hu.js → copy-context-CciRaLNN.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-i7x8jZ9M.js → cose-bilkent-JH36ORCC-CGRic2um.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-D8HPrHMt.js → cynefin-VYW2F7L2-Blk0Lu0l.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-BtkeCNXb.js → cynefinDiagram-TSTJHNR4-DpnnHRDK.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-oIThwshO.js → dagre-VKFMJZFB-llzMokKp.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-DTuhEo03.js → diagram-FQU43EPY-DC6xHovR.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-BcQ_4_fE.js → diagram-G47NLZAW-B8GVS3EA.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BA4VC8Du.js → diagram-NH7WQ7WH-DWUaARfh.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-BOpyrmty.js → diagram-OA4YK3LP-9Uaes_Yi.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-BzrSPDJ7.js → diagram-WEI45ONY-DPPTBWud.js} +1 -1
- package/web/assets/document-D23WzFsW.js +23 -0
- package/web/assets/{ebnfDiagram-CCIWWBDH-BNV_4xDp.js → ebnfDiagram-CCIWWBDH-BPrKneNw.js} +1 -1
- package/web/assets/{entities-D0qpC3B9.js → entities-DPg0B8vd.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-NT1La9pi.js → erDiagram-Q63AITRT-CFqYBcnb.js} +1 -1
- package/web/assets/{file-viewer-C1N6vaip.js → file-viewer-Ck73SsLA.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-CIYWUYkz.js → flowDiagram-23GEKE2U-C7RpYqaT.js} +1 -1
- package/web/assets/folder-filter-bar-DzejUj87.js +4 -0
- package/web/assets/{folder-image-Bz0xznQT.js → folder-image-BjIicmQ6.js} +1 -1
- package/web/assets/folder-landing-CCIXZfkp.js +55 -0
- package/web/assets/folder-list-TuRwuTss.js +1 -0
- package/web/assets/folder-mobile-bar-Bqnfwi-z.js +1 -0
- package/web/assets/folder-plugin-view-CrinCX1o.js +2 -0
- package/web/assets/{ganttDiagram-NO4QXBWP-DeUfoXQA.js → ganttDiagram-NO4QXBWP-Cz2sxU8F.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-DMeTgUPh.js → gitGraphDiagram-IHSO6WYX-BSuz0yF-.js} +1 -1
- package/web/assets/{index-CCXE8zc9.js → index-BVyeNcuc.js} +1 -1
- package/web/assets/{index-CegvVQ1C.js → index-Cg-gtBkw.js} +1 -1
- package/web/assets/index-DLn_2LPp.js +2 -0
- package/web/assets/{index-DY0VGBht.js → index-DbVDdnzL.js} +1 -1
- package/web/assets/index-uY9jLiL5.css +1 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-DI_P8Ghi.js → infoDiagram-FWYZ7A6U-CpeKgqoS.js} +1 -1
- package/web/assets/input-Bz_puamS.js +9 -0
- package/web/assets/{ishikawaDiagram-FXEZZL3T-B8bM-8CD.js → ishikawaDiagram-FXEZZL3T-5c2Tmknb.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-pviYejXU.js → journeyDiagram-5HDEW3XC-Cu-wRkR4.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-DfvdDvpU.js → kanban-definition-HUTT4EX6-BunSqkPe.js} +1 -1
- package/web/assets/{linear-DNOuKTk_.js → linear-C8e8bGVv.js} +1 -1
- package/web/assets/{mermaid.core-CisVJ_Cs.js → mermaid.core-DLgjY1g2.js} +4 -4
- package/web/assets/{metadata-C7E_6HIN.js → metadata-DEfJKlnd.js} +1 -1
- package/web/assets/{mindmap-definition-LN4V7U3C-DzdcaduN.js → mindmap-definition-LN4V7U3C-W9ENYFOD.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-D_7xhlXd.js → pegDiagram-2B236MQR-0vPlHCFp.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-CisikHMW.js → pieDiagram-ENE6RG2P-CakaveRV.js} +1 -1
- package/web/assets/{popover-D7MTK-sz.js → popover-3AMcPJpt.js} +1 -1
- package/web/assets/{project-search-dialog-BsHc39D0.js → project-search-dialog-BSKA4peX.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-D6adg-7z.js → quadrantDiagram-ABIIQ3AL-jxHw_egj.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-CyPAHeJ-.js → railroadDiagram-RFXS5EU6-lIjDXPZt.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-CgpSiPTv.js → requirementDiagram-TGXJPOKE-C-JiT4bc.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-BxvReW6S.js → sankeyDiagram-HTMAVEWB-SuLPAw9X.js} +1 -1
- package/web/assets/{search-DEgwpq-P.js → search-CKujW14f.js} +1 -1
- package/web/assets/{selection-popup-d94JnDPy.js → selection-popup-CtxT9SWn.js} +3 -3
- package/web/assets/{sequenceDiagram-DBY2YBRQ-uuplzETI.js → sequenceDiagram-DBY2YBRQ-o9UwTkZR.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-Bi3F7nOa.js → sizeCapture-X5ZJPWSS-ClbWGybF.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-DqypF7lE.js → stateDiagram-2N3HPSRC-Ce4avoWx.js} +1 -1
- package/web/assets/stateDiagram-v2-6OUMAXLB-CsrPTZoJ.js +1 -0
- package/web/assets/{swimlanes-5IMT3BWC-CGD9YT1l.js → swimlanes-5IMT3BWC-DMZI4XZT.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-pShSVcTd.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-DCjNS9Ol.js → timeline-definition-FHXFAJF6-CPIpIgPN.js} +1 -1
- package/web/assets/{utils-DCaHk0Gm.js → utils-Cg80pk1W.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-BJ58yQgD.js → vennDiagram-L72KCM5P-_rha8uDZ.js} +1 -1
- package/web/assets/view-context-BDtIUHQy.js +229 -0
- package/web/assets/{viewer-capabilities-DCuJdf64.js → viewer-capabilities-YYZqMs56.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-BiLuq9mr.js → wardleyDiagram-EHGQE667-D4EAOS35.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DzAZpIRG.js → xychartDiagram-FW5EYKEG-DR8PwXpe.js} +1 -1
- package/web/index.html +2 -2
- package/kernel/packages/agents/eslint.config.js +0 -10
- package/kernel/packages/agents/package.json +0 -75
- package/kernel/packages/agents/src/agent-card.tsx +0 -334
- package/kernel/packages/agents/src/client.tsx +0 -13
- package/kernel/packages/agents/src/index.ts +0 -18
- package/kernel/packages/agents/src/manifest.ts +0 -7
- package/kernel/packages/agents/src/node.ts +0 -447
- package/kernel/packages/agents/src/profiles.ts +0 -137
- package/kernel/packages/agents/src/resolve.ts +0 -69
- package/kernel/packages/agents/src/server.ts +0 -60
- package/kernel/packages/agents/src/types.ts +0 -82
- package/kernel/packages/agents/src/viewer.tsx +0 -370
- package/kernel/packages/agents/test/profiles.test.mjs +0 -88
- package/kernel/packages/agents/test/resolve.test.mjs +0 -729
- package/kernel/packages/agents/tsconfig.json +0 -17
- package/kernel/packages/api/test/agents-loading.test.ts +0 -94
- package/kernel/packages/ui/src/components/folder-skill-list.tsx +0 -62
- package/web/assets/app-DoXXGPMx.js +0 -2
- package/web/assets/app-options-jTBHy0A7.js +0 -25
- package/web/assets/channel-54w4vQjC.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-Bh9Ib2pw.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-Bh9Ib2pw.js +0 -1
- package/web/assets/client-BgUDusrU.js +0 -5
- package/web/assets/client-COaHUqSC.js +0 -17
- package/web/assets/client-UC06V2VL.js +0 -22
- package/web/assets/document-BDtU3CbX.js +0 -19
- package/web/assets/folder-filter-bar-C_2Mobfq.js +0 -4
- package/web/assets/folder-landing-3gqbGaYS.js +0 -54
- package/web/assets/folder-list-DDI0TF2e.js +0 -1
- package/web/assets/folder-metadata-DjV3ZUra.js +0 -2
- package/web/assets/folder-mobile-bar-CF4CpJXL.js +0 -1
- package/web/assets/folder-plugin-view-C48J0HKe.js +0 -3
- package/web/assets/index-Bp9DidgD.js +0 -1
- package/web/assets/index-CXhBn-ar.js +0 -1
- package/web/assets/index-CtuOSBHa.js +0 -2
- package/web/assets/index-Dwhf8F_U.css +0 -1
- package/web/assets/select-DGaccqVB.js +0 -30
- package/web/assets/stateDiagram-v2-6OUMAXLB-EsEb0a5Q.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-DKIYuN5l.js +0 -8
- package/web/assets/use-folder-filter-hA6fa7nm.js +0 -1
- package/web/assets/view-context-BrzQ7WBV.js +0 -229
|
@@ -18,11 +18,9 @@ import { markdownMetadata } from "@raggle-ai/markdown";
|
|
|
18
18
|
import { readFolder } from "@raggle-ai/markdown/node";
|
|
19
19
|
import { execFile } from "node:child_process";
|
|
20
20
|
import { sameOrigin } from "kennel-plugins/endpoint";
|
|
21
|
-
import { parse as parseYaml } from "yaml";
|
|
22
21
|
|
|
23
22
|
import type { FolderPluginServer } from "kennel-plugins/server";
|
|
24
23
|
import type { LocalMarkdownFileSummary, LocalProject } from "@raggle-ai/local";
|
|
25
|
-
import type { FolderSkill } from "@raggle-ai/ui/lib/folder-activity";
|
|
26
24
|
import { runApiRoute } from "kennel-plugins/endpoint";
|
|
27
25
|
import { FolderPluginFailure } from "kennel-plugins/server";
|
|
28
26
|
import { folderWorkspace } from "@raggle-ai/schemas/workspace";
|
|
@@ -74,26 +72,6 @@ const {
|
|
|
74
72
|
sortProjectList,
|
|
75
73
|
} = local;
|
|
76
74
|
|
|
77
|
-
type SkillSchema = {
|
|
78
|
-
description?: unknown;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
function skillDescription(content: string) {
|
|
82
|
-
if (!content.startsWith("---\n")) return undefined;
|
|
83
|
-
const end = content.indexOf("\n---", 4);
|
|
84
|
-
if (end < 0) return undefined;
|
|
85
|
-
const schema = parseYaml(content.slice(4, end)) as SkillSchema;
|
|
86
|
-
return typeof schema.description === "string"
|
|
87
|
-
? schema.description
|
|
88
|
-
: undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function skillSource(folder: string, root: string): FolderSkill["source"] {
|
|
92
|
-
if (root === folder) return "local";
|
|
93
|
-
if (root.startsWith(resolve(folder, ".gtl") + sep)) return "local";
|
|
94
|
-
return "global";
|
|
95
|
-
}
|
|
96
|
-
|
|
97
75
|
type IconAsset = {
|
|
98
76
|
mediaType: "image/jpeg" | "image/png";
|
|
99
77
|
source: "local" | "favicon";
|
|
@@ -135,8 +113,6 @@ export type FolderMetadataPluginOptions = {
|
|
|
135
113
|
restrictToRoot?: boolean;
|
|
136
114
|
root: string;
|
|
137
115
|
sessionFinding?: SessionFinding;
|
|
138
|
-
/** Override the home directory used for Agent global files. Intended for tests. */
|
|
139
|
-
agentsHomeDir?: string;
|
|
140
116
|
/** Override isolated linked-project discovery, for tests. */
|
|
141
117
|
linkedFolderLoader?: LinkedFolderLoader;
|
|
142
118
|
/**
|
|
@@ -355,105 +331,6 @@ function unique(values: string[]) {
|
|
|
355
331
|
return [...new Set(values)];
|
|
356
332
|
}
|
|
357
333
|
|
|
358
|
-
async function existingDirectory(path: string) {
|
|
359
|
-
const details = await stat(path).catch(() => undefined);
|
|
360
|
-
return details?.isDirectory() ? path : undefined;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function metadataPaths(metadata: {
|
|
364
|
-
files?: { items: Array<{ path?: string; relativePath?: string }> };
|
|
365
|
-
}) {
|
|
366
|
-
return (metadata.files?.items ?? [])
|
|
367
|
-
.flatMap((file) => [file.path, file.relativePath])
|
|
368
|
-
.filter((path): path is string => Boolean(path));
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function ancestorFolders(folder: string) {
|
|
372
|
-
const folders = [];
|
|
373
|
-
for (
|
|
374
|
-
let current = folder;
|
|
375
|
-
dirname(current) !== current;
|
|
376
|
-
current = dirname(current)
|
|
377
|
-
) {
|
|
378
|
-
folders.push(current);
|
|
379
|
-
}
|
|
380
|
-
return folders;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
async function linkedSkillRoots(
|
|
384
|
-
folder: string,
|
|
385
|
-
metadata: {
|
|
386
|
-
files?: { items: Array<{ path?: string; relativePath?: string }> };
|
|
387
|
-
},
|
|
388
|
-
) {
|
|
389
|
-
const gtlNames = metadataPaths(metadata)
|
|
390
|
-
.map((path) => path.split(/[\\/]\.gtl[\\/]/)[1])
|
|
391
|
-
.filter((path): path is string => Boolean(path))
|
|
392
|
-
.map((path) => path.split(/[\\/]/)[0])
|
|
393
|
-
.filter((name): name is string => Boolean(name));
|
|
394
|
-
const gtlRoots = gtlNames.flatMap((name) => [
|
|
395
|
-
resolve(folder, ".gtl", name),
|
|
396
|
-
...ancestorFolders(folder).map((ancestor) => resolve(ancestor, name)),
|
|
397
|
-
]);
|
|
398
|
-
|
|
399
|
-
const name = folder
|
|
400
|
-
.replace(/[\\/]+$/, "")
|
|
401
|
-
.split(/[\\/]/)
|
|
402
|
-
.at(-1);
|
|
403
|
-
const siblingIndex = name
|
|
404
|
-
? [
|
|
405
|
-
resolve(dirname(folder), `${name}-index`),
|
|
406
|
-
resolve(dirname(dirname(folder)), `${name}-index`),
|
|
407
|
-
]
|
|
408
|
-
: [];
|
|
409
|
-
const roots = await Promise.all(
|
|
410
|
-
unique([...gtlRoots, ...siblingIndex]).map(existingDirectory),
|
|
411
|
-
);
|
|
412
|
-
return roots.filter((path): path is string => Boolean(path));
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
async function listFolderSkills(
|
|
416
|
-
folder: string,
|
|
417
|
-
metadata: {
|
|
418
|
-
files?: { items: Array<{ path?: string; relativePath?: string }> };
|
|
419
|
-
},
|
|
420
|
-
) {
|
|
421
|
-
const roots = unique([folder, ...(await linkedSkillRoots(folder, metadata))]);
|
|
422
|
-
const skills = await Promise.all(
|
|
423
|
-
roots.map(async (root) => {
|
|
424
|
-
const skillsFolder = resolve(root, ".agents", "skills");
|
|
425
|
-
const entries = await readdir(skillsFolder, {
|
|
426
|
-
withFileTypes: true,
|
|
427
|
-
}).catch(() => []);
|
|
428
|
-
return (
|
|
429
|
-
await Promise.all(
|
|
430
|
-
entries
|
|
431
|
-
.filter(
|
|
432
|
-
(entry) => entry.isDirectory() && !entry.name.startsWith("."),
|
|
433
|
-
)
|
|
434
|
-
.map(async (entry) => {
|
|
435
|
-
const path = resolve(skillsFolder, entry.name);
|
|
436
|
-
const skillPath = resolve(path, "SKILL.md");
|
|
437
|
-
const details = await stat(skillPath).catch(() => undefined);
|
|
438
|
-
if (!details?.isFile()) return undefined;
|
|
439
|
-
const content = await readFile(skillPath, "utf8").catch(() => "");
|
|
440
|
-
return {
|
|
441
|
-
description: skillDescription(content),
|
|
442
|
-
name: label(entry.name),
|
|
443
|
-
openHref: openPathHref(path),
|
|
444
|
-
relativePath: relative(root, path),
|
|
445
|
-
source: skillSource(folder, root),
|
|
446
|
-
};
|
|
447
|
-
}),
|
|
448
|
-
)
|
|
449
|
-
).filter((skill) => skill);
|
|
450
|
-
}),
|
|
451
|
-
);
|
|
452
|
-
return unique(skills.flat().map((skill) => JSON.stringify(skill)))
|
|
453
|
-
.map((skill) => JSON.parse(skill) as FolderSkill)
|
|
454
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
455
|
-
}
|
|
456
|
-
|
|
457
334
|
function isMachineRoot(folder: string) {
|
|
458
335
|
return folder === homedir() || dirname(folder) === folder;
|
|
459
336
|
}
|
|
@@ -506,7 +383,6 @@ async function shallowMetadata(folder: string) {
|
|
|
506
383
|
projects: { items: projects },
|
|
507
384
|
schemaVersion: 3 as const,
|
|
508
385
|
sessions: { items: [] },
|
|
509
|
-
skills: { items: [] },
|
|
510
386
|
};
|
|
511
387
|
}
|
|
512
388
|
|
|
@@ -773,6 +649,7 @@ async function pluginAvailability(
|
|
|
773
649
|
const items: Array<{
|
|
774
650
|
count?: number;
|
|
775
651
|
error?: string;
|
|
652
|
+
icon?: { paths: string[] };
|
|
776
653
|
id: string;
|
|
777
654
|
label: string;
|
|
778
655
|
}> = [];
|
|
@@ -785,6 +662,7 @@ async function pluginAvailability(
|
|
|
785
662
|
if (detection.status === "available") {
|
|
786
663
|
items.push({
|
|
787
664
|
count: detection.count,
|
|
665
|
+
icon: plugin.icon,
|
|
788
666
|
id: plugin.id,
|
|
789
667
|
label: plugin.label,
|
|
790
668
|
});
|
|
@@ -794,6 +672,7 @@ async function pluginAvailability(
|
|
|
794
672
|
// shell can surface it instead of treating it as absent content.
|
|
795
673
|
items.push({
|
|
796
674
|
error: detection.error,
|
|
675
|
+
icon: plugin.icon,
|
|
797
676
|
id: plugin.id,
|
|
798
677
|
label: plugin.label,
|
|
799
678
|
});
|
|
@@ -811,7 +690,6 @@ export function createFolderApi({
|
|
|
811
690
|
restrictToRoot = false,
|
|
812
691
|
root,
|
|
813
692
|
sessionFinding,
|
|
814
|
-
agentsHomeDir,
|
|
815
693
|
linkedFolderLoader,
|
|
816
694
|
remoteAccess = false,
|
|
817
695
|
lockedFile,
|
|
@@ -822,9 +700,7 @@ export function createFolderApi({
|
|
|
822
700
|
listFolderSessions,
|
|
823
701
|
readSessionTranscript,
|
|
824
702
|
};
|
|
825
|
-
const basePlugins = folderServerRegistryAsync(
|
|
826
|
-
agentsHomeDir,
|
|
827
|
-
});
|
|
703
|
+
const basePlugins = folderServerRegistryAsync();
|
|
828
704
|
const baseExternalApiHandlers = externalApiHandlers();
|
|
829
705
|
const assetFiles = new Map<
|
|
830
706
|
string,
|
|
@@ -1374,14 +1250,11 @@ export function createFolderApi({
|
|
|
1374
1250
|
request.headers["x-kennel-retry-linked"] === "1"
|
|
1375
1251
|
)
|
|
1376
1252
|
linkedDiscovery.retry(folder);
|
|
1377
|
-
const discovery =
|
|
1378
|
-
pluginId === "agents" || pluginId === "tasks"
|
|
1379
|
-
? linkedState(folder)
|
|
1380
|
-
: undefined;
|
|
1253
|
+
const discovery = linkedState(folder);
|
|
1381
1254
|
const result = await plugin.handle(
|
|
1382
1255
|
{
|
|
1383
1256
|
folder,
|
|
1384
|
-
linkedFolders: discovery
|
|
1257
|
+
linkedFolders: discovery.folders,
|
|
1385
1258
|
},
|
|
1386
1259
|
subpath,
|
|
1387
1260
|
);
|
|
@@ -1395,7 +1268,7 @@ export function createFolderApi({
|
|
|
1395
1268
|
folder,
|
|
1396
1269
|
plugin: pluginId,
|
|
1397
1270
|
status: "unavailable",
|
|
1398
|
-
|
|
1271
|
+
linkedStatus: discovery.status,
|
|
1399
1272
|
}),
|
|
1400
1273
|
);
|
|
1401
1274
|
return;
|
|
@@ -1410,7 +1283,7 @@ export function createFolderApi({
|
|
|
1410
1283
|
folder,
|
|
1411
1284
|
plugin: pluginId,
|
|
1412
1285
|
...data,
|
|
1413
|
-
|
|
1286
|
+
linkedStatus: discovery.status,
|
|
1414
1287
|
}),
|
|
1415
1288
|
);
|
|
1416
1289
|
} catch (error) {
|
|
@@ -1498,7 +1371,6 @@ export function createFolderApi({
|
|
|
1498
1371
|
folder,
|
|
1499
1372
|
projects: { items: [] },
|
|
1500
1373
|
sessions: { items: [] },
|
|
1501
|
-
skills: { items: [] },
|
|
1502
1374
|
workspace: await readWorkspace(folder),
|
|
1503
1375
|
},
|
|
1504
1376
|
{ "Cache-Control": "private, max-age=0, must-revalidate" },
|
|
@@ -1697,26 +1569,12 @@ export function createFolderApi({
|
|
|
1697
1569
|
? await shallowMetadata(folder)
|
|
1698
1570
|
: await loadFolderData(folder);
|
|
1699
1571
|
|
|
1700
|
-
|
|
1701
|
-
respond({
|
|
1702
|
-
folder: metadata.folder,
|
|
1703
|
-
skills: {
|
|
1704
|
-
items: await listFolderSkills(metadata.folder, metadata),
|
|
1705
|
-
},
|
|
1706
|
-
});
|
|
1707
|
-
return;
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
const [assets, skills] = await Promise.all([
|
|
1711
|
-
hydrateAssets(metadata, assetFiles),
|
|
1712
|
-
listFolderSkills(metadata.folder, metadata),
|
|
1713
|
-
]);
|
|
1572
|
+
const assets = await hydrateAssets(metadata, assetFiles);
|
|
1714
1573
|
const body = {
|
|
1715
1574
|
...metadata,
|
|
1716
1575
|
...assets,
|
|
1717
1576
|
sessions: { items: [] },
|
|
1718
1577
|
files: await notesFiles(metadata.folder, metadata.files.items),
|
|
1719
|
-
skills: { items: skills },
|
|
1720
1578
|
};
|
|
1721
1579
|
respond(body);
|
|
1722
1580
|
} catch (error) {
|
|
@@ -4,17 +4,11 @@ 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
|
-
import { createAgentsPlugin } from "@raggle-ai/agents/server";
|
|
8
7
|
import { FolderPluginFailure } from "kennel-plugins/server";
|
|
9
8
|
import { createNotesPlugin } from "@raggle-ai/notes/server";
|
|
10
9
|
|
|
11
10
|
export { FolderPluginFailure };
|
|
12
11
|
|
|
13
|
-
export type FolderServerRegistryOptions = {
|
|
14
|
-
/** Override the home directory used for Agent global files. Intended for tests. */
|
|
15
|
-
agentsHomeDir?: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
12
|
export type FolderPluginRegistryConfig = {
|
|
19
13
|
plugins: {
|
|
20
14
|
disable: string[];
|
|
@@ -44,22 +38,13 @@ export function configuredFolderServerRegistry(
|
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
/** Core server plugins. Installed plugins are loaded separately. */
|
|
47
|
-
export function createFolderServerPlugins(
|
|
48
|
-
|
|
49
|
-
): FolderPluginServer[] {
|
|
50
|
-
return [
|
|
51
|
-
createAgentsPlugin({
|
|
52
|
-
homeDir: options.agentsHomeDir,
|
|
53
|
-
}),
|
|
54
|
-
createNotesPlugin(),
|
|
55
|
-
];
|
|
41
|
+
export function createFolderServerPlugins(): FolderPluginServer[] {
|
|
42
|
+
return [createNotesPlugin()];
|
|
56
43
|
}
|
|
57
44
|
|
|
58
|
-
export function folderServerRegistry(
|
|
59
|
-
options: FolderServerRegistryOptions = {},
|
|
60
|
-
): Map<string, FolderPluginServer> {
|
|
45
|
+
export function folderServerRegistry(): Map<string, FolderPluginServer> {
|
|
61
46
|
return new Map(
|
|
62
|
-
createFolderServerPlugins(
|
|
47
|
+
createFolderServerPlugins().map((plugin) => [plugin.id, plugin]),
|
|
63
48
|
);
|
|
64
49
|
}
|
|
65
50
|
|
|
@@ -107,17 +92,17 @@ function externalServerEntries(): ExternalServerEntry[] {
|
|
|
107
92
|
});
|
|
108
93
|
}
|
|
109
94
|
|
|
110
|
-
export async function folderServerRegistryAsync(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const registry = folderServerRegistry(
|
|
95
|
+
export async function folderServerRegistryAsync(): Promise<
|
|
96
|
+
Map<string, FolderPluginServer>
|
|
97
|
+
> {
|
|
98
|
+
const registry = folderServerRegistry();
|
|
114
99
|
for (const entry of externalServerEntries()) {
|
|
115
100
|
if (registry.has(entry.id))
|
|
116
101
|
throw new Error(`External plugin id collides with core plugin: ${entry.id}`);
|
|
117
102
|
const imported = await import(pathToFileURL(entry.module).href);
|
|
118
103
|
const value = imported[entry.symbol];
|
|
119
104
|
const plugin =
|
|
120
|
-
typeof value === "function" ? value(
|
|
105
|
+
typeof value === "function" ? value() : value;
|
|
121
106
|
if (!plugin || typeof plugin !== "object")
|
|
122
107
|
throw new Error(`External plugin ${entry.id} did not export ${entry.symbol}`);
|
|
123
108
|
const server = plugin as FolderPluginServer;
|
|
@@ -198,10 +183,10 @@ export type FolderPluginScaffoldResult = {
|
|
|
198
183
|
*/
|
|
199
184
|
export async function scaffoldFolderPlugins(
|
|
200
185
|
folder: string,
|
|
201
|
-
options:
|
|
186
|
+
options: { force?: boolean; write?: boolean } = {},
|
|
202
187
|
): Promise<FolderPluginScaffoldResult> {
|
|
203
188
|
const root = resolve(folder);
|
|
204
|
-
const registry = folderServerRegistry(
|
|
189
|
+
const registry = folderServerRegistry();
|
|
205
190
|
const created: FolderPluginScaffoldFile[] = [];
|
|
206
191
|
const skipped: FolderPluginScaffoldFile[] = [];
|
|
207
192
|
const supported: FolderPluginScaffoldResult["supported"] = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FolderTab } from "@raggle-ai/ui/lib/folder-activity";
|
|
2
2
|
|
|
3
|
-
export type ViewerSection = "notes" | "projects" | "sessions"
|
|
3
|
+
export type ViewerSection = "notes" | "projects" | "sessions";
|
|
4
4
|
|
|
5
5
|
export type ViewerPluginView = {
|
|
6
6
|
pluginId: string;
|
|
@@ -30,7 +30,6 @@ const sectionTabs: Record<ViewerSection, FolderTab> = {
|
|
|
30
30
|
notes: "files",
|
|
31
31
|
projects: "projects",
|
|
32
32
|
sessions: "sessions",
|
|
33
|
-
skills: "skills",
|
|
34
33
|
};
|
|
35
34
|
|
|
36
35
|
import { folderPathId as encodeBase64Url } from "kennel-plugins/client";
|
|
@@ -51,7 +50,7 @@ function url(value: string) {
|
|
|
51
50
|
|
|
52
51
|
function tabSection(tab: string | null): ViewerSection | undefined {
|
|
53
52
|
if (tab === "files") return "notes";
|
|
54
|
-
if (tab === "projects" || tab === "sessions"
|
|
53
|
+
if (tab === "projects" || tab === "sessions") return tab;
|
|
55
54
|
return undefined;
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -156,8 +155,7 @@ export function parseViewerRoute(value: string): ViewerRoute {
|
|
|
156
155
|
if (
|
|
157
156
|
section === "notes" ||
|
|
158
157
|
section === "projects" ||
|
|
159
|
-
section === "sessions"
|
|
160
|
-
section === "skills"
|
|
158
|
+
section === "sessions"
|
|
161
159
|
) {
|
|
162
160
|
return { ...route, section };
|
|
163
161
|
}
|
|
@@ -303,7 +303,7 @@ export interface operations {
|
|
|
303
303
|
parameters: {
|
|
304
304
|
query?: {
|
|
305
305
|
path?: string;
|
|
306
|
-
part?: "notes" | "identity" | "discovery" | "workspace" | "collections" | "plugins" | "
|
|
306
|
+
part?: "notes" | "identity" | "discovery" | "workspace" | "collections" | "plugins" | "sessions" | "calendar" | "schema";
|
|
307
307
|
notesScope?: "local" | "linked" | "all";
|
|
308
308
|
id?: string;
|
|
309
309
|
sort?: "alpha" | "recent";
|
|
@@ -348,6 +348,9 @@ export interface operations {
|
|
|
348
348
|
items: {
|
|
349
349
|
count?: number;
|
|
350
350
|
error?: string;
|
|
351
|
+
icon?: {
|
|
352
|
+
paths: string[];
|
|
353
|
+
};
|
|
351
354
|
id: string;
|
|
352
355
|
label: string;
|
|
353
356
|
}[];
|
|
@@ -373,16 +376,6 @@ export interface operations {
|
|
|
373
376
|
title: string;
|
|
374
377
|
}[];
|
|
375
378
|
};
|
|
376
|
-
skills?: {
|
|
377
|
-
items: {
|
|
378
|
-
description?: string;
|
|
379
|
-
name: string;
|
|
380
|
-
openHref?: string;
|
|
381
|
-
relativePath: string;
|
|
382
|
-
/** @enum {string} */
|
|
383
|
-
source: "global" | "local";
|
|
384
|
-
}[];
|
|
385
|
-
};
|
|
386
379
|
workspace?: {
|
|
387
380
|
breadcrumbs: {
|
|
388
381
|
icon?: {
|
|
@@ -608,22 +601,13 @@ export interface operations {
|
|
|
608
601
|
items: {
|
|
609
602
|
count?: number;
|
|
610
603
|
error?: string;
|
|
604
|
+
icon?: {
|
|
605
|
+
paths: string[];
|
|
606
|
+
};
|
|
611
607
|
id: string;
|
|
612
608
|
label: string;
|
|
613
609
|
}[];
|
|
614
610
|
};
|
|
615
|
-
} | {
|
|
616
|
-
folder: string;
|
|
617
|
-
skills: {
|
|
618
|
-
items: {
|
|
619
|
-
description?: string;
|
|
620
|
-
name: string;
|
|
621
|
-
openHref?: string;
|
|
622
|
-
relativePath: string;
|
|
623
|
-
/** @enum {string} */
|
|
624
|
-
source: "global" | "local";
|
|
625
|
-
}[];
|
|
626
|
-
};
|
|
627
611
|
} | {
|
|
628
612
|
folder: string;
|
|
629
613
|
assets: {
|
|
@@ -651,6 +635,9 @@ export interface operations {
|
|
|
651
635
|
items: {
|
|
652
636
|
count?: number;
|
|
653
637
|
error?: string;
|
|
638
|
+
icon?: {
|
|
639
|
+
paths: string[];
|
|
640
|
+
};
|
|
654
641
|
id: string;
|
|
655
642
|
label: string;
|
|
656
643
|
}[];
|
|
@@ -676,16 +663,6 @@ export interface operations {
|
|
|
676
663
|
title: string;
|
|
677
664
|
}[];
|
|
678
665
|
};
|
|
679
|
-
skills?: {
|
|
680
|
-
items: {
|
|
681
|
-
description?: string;
|
|
682
|
-
name: string;
|
|
683
|
-
openHref?: string;
|
|
684
|
-
relativePath: string;
|
|
685
|
-
/** @enum {string} */
|
|
686
|
-
source: "global" | "local";
|
|
687
|
-
}[];
|
|
688
|
-
};
|
|
689
666
|
workspace?: {
|
|
690
667
|
breadcrumbs: {
|
|
691
668
|
icon?: {
|
|
@@ -750,6 +727,9 @@ export interface operations {
|
|
|
750
727
|
items: {
|
|
751
728
|
count?: number;
|
|
752
729
|
error?: string;
|
|
730
|
+
icon?: {
|
|
731
|
+
paths: string[];
|
|
732
|
+
};
|
|
753
733
|
id: string;
|
|
754
734
|
label: string;
|
|
755
735
|
}[];
|
|
@@ -775,16 +755,6 @@ export interface operations {
|
|
|
775
755
|
title: string;
|
|
776
756
|
}[];
|
|
777
757
|
};
|
|
778
|
-
skills?: {
|
|
779
|
-
items: {
|
|
780
|
-
description?: string;
|
|
781
|
-
name: string;
|
|
782
|
-
openHref?: string;
|
|
783
|
-
relativePath: string;
|
|
784
|
-
/** @enum {string} */
|
|
785
|
-
source: "global" | "local";
|
|
786
|
-
}[];
|
|
787
|
-
};
|
|
788
758
|
workspace?: {
|
|
789
759
|
breadcrumbs: {
|
|
790
760
|
icon?: {
|
|
@@ -849,6 +819,9 @@ export interface operations {
|
|
|
849
819
|
items: {
|
|
850
820
|
count?: number;
|
|
851
821
|
error?: string;
|
|
822
|
+
icon?: {
|
|
823
|
+
paths: string[];
|
|
824
|
+
};
|
|
852
825
|
id: string;
|
|
853
826
|
label: string;
|
|
854
827
|
}[];
|
|
@@ -874,16 +847,6 @@ export interface operations {
|
|
|
874
847
|
title: string;
|
|
875
848
|
}[];
|
|
876
849
|
};
|
|
877
|
-
skills?: {
|
|
878
|
-
items: {
|
|
879
|
-
description?: string;
|
|
880
|
-
name: string;
|
|
881
|
-
openHref?: string;
|
|
882
|
-
relativePath: string;
|
|
883
|
-
/** @enum {string} */
|
|
884
|
-
source: "global" | "local";
|
|
885
|
-
}[];
|
|
886
|
-
};
|
|
887
850
|
workspace?: {
|
|
888
851
|
breadcrumbs: {
|
|
889
852
|
icon?: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @raggle-ai/markdown
|
|
2
2
|
|
|
3
3
|
Internal BarackDown re-export consumed by every Kennel package that renders
|
|
4
|
-
Markdown (`packages/ui`, `packages/
|
|
5
|
-
`
|
|
4
|
+
Markdown (`packages/ui`, `packages/api`, external plugins, `cli`, and
|
|
5
|
+
`apps/web`). The
|
|
6
6
|
purpose of this tiny package is to be the single place where Kennel decides
|
|
7
7
|
which BarkDown implementation it uses.
|
|
8
8
|
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"exports": {
|
|
22
22
|
".": "./src/index.ts",
|
|
23
23
|
"./client": "./src/client.tsx",
|
|
24
|
+
"./data/use-folder-notes": "./src/data/use-folder-notes.ts",
|
|
24
25
|
"./manifest": "./src/manifest.ts",
|
|
25
26
|
"./parse": "./src/data/parse.ts",
|
|
26
27
|
"./pages/single-note-page": "./src/pages/single-note-page.tsx",
|
|
@@ -2,5 +2,12 @@ import type { FolderPluginManifest } from "kennel-plugins"
|
|
|
2
2
|
|
|
3
3
|
export const notesManifest = {
|
|
4
4
|
id: "notes",
|
|
5
|
+
icon: {
|
|
6
|
+
paths: [
|
|
7
|
+
"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z",
|
|
8
|
+
"M14 2v6h6",
|
|
9
|
+
"M8 13h8M8 17h6",
|
|
10
|
+
],
|
|
11
|
+
},
|
|
5
12
|
label: "Notes",
|
|
6
13
|
} satisfies FolderPluginManifest
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { ReactNode } from "react"
|
|
2
|
+
import { useMemo, useState } from "react"
|
|
2
3
|
|
|
3
4
|
import type { FolderFile } from "@raggle-ai/ui/lib/folder-activity"
|
|
5
|
+
import { Input } from "@raggle-ai/ui/components/input"
|
|
4
6
|
import { NoteCard } from "@raggle-ai/ui/components/note-card"
|
|
7
|
+
import { OffsetPagination } from "@raggle-ai/ui/components/pagination"
|
|
8
|
+
|
|
9
|
+
const pageSize = 25
|
|
10
|
+
const emptyFiles: FolderFile[] = []
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* Note rows for the main Notes tab, backed by the shared NoteCard so linked
|
|
@@ -11,7 +17,7 @@ export function FolderNotesList({
|
|
|
11
17
|
action,
|
|
12
18
|
documentHref,
|
|
13
19
|
error,
|
|
14
|
-
files =
|
|
20
|
+
files = emptyFiles,
|
|
15
21
|
filter,
|
|
16
22
|
folder,
|
|
17
23
|
folderHref,
|
|
@@ -26,13 +32,33 @@ export function FolderNotesList({
|
|
|
26
32
|
folderHref: (folder: string) => string
|
|
27
33
|
loading: boolean
|
|
28
34
|
}) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
const [offset, setOffset] = useState(0)
|
|
36
|
+
const [search, setSearch] = useState("")
|
|
37
|
+
const sorted = useMemo(
|
|
38
|
+
() =>
|
|
39
|
+
[...files].sort(
|
|
40
|
+
(left, right) =>
|
|
41
|
+
right.activityAt - left.activityAt ||
|
|
42
|
+
`${left.folder ?? ""}/${left.relativePath}`.localeCompare(
|
|
43
|
+
`${right.folder ?? ""}/${right.relativePath}`,
|
|
44
|
+
),
|
|
34
45
|
),
|
|
46
|
+
[files],
|
|
47
|
+
)
|
|
48
|
+
const matching = useMemo(() => {
|
|
49
|
+
const query = search.trim().toLowerCase()
|
|
50
|
+
if (!query) return sorted
|
|
51
|
+
return sorted.filter((file) =>
|
|
52
|
+
`${file.name}\n${file.folder ?? ""}/${file.relativePath}`
|
|
53
|
+
.toLowerCase()
|
|
54
|
+
.includes(query),
|
|
55
|
+
)
|
|
56
|
+
}, [sorted, search])
|
|
57
|
+
const effectiveOffset = Math.min(
|
|
58
|
+
offset,
|
|
59
|
+
Math.max(0, Math.ceil(matching.length / pageSize) - 1) * pageSize,
|
|
35
60
|
)
|
|
61
|
+
const visible = matching.slice(effectiveOffset, effectiveOffset + pageSize)
|
|
36
62
|
|
|
37
63
|
return (
|
|
38
64
|
<div className="mx-auto grid w-full max-w-3xl gap-4 px-4 py-5">
|
|
@@ -40,12 +66,22 @@ export function FolderNotesList({
|
|
|
40
66
|
<h1 className="m-0 text-2xl font-semibold text-[#233025]">Notes</h1>
|
|
41
67
|
{action}
|
|
42
68
|
</div>
|
|
69
|
+
<Input
|
|
70
|
+
type="search"
|
|
71
|
+
aria-label="Search notes by name or path"
|
|
72
|
+
placeholder="Search notes by name or path…"
|
|
73
|
+
value={search}
|
|
74
|
+
onChange={(event) => {
|
|
75
|
+
setSearch(event.target.value)
|
|
76
|
+
setOffset(0)
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
43
79
|
{filter}
|
|
44
80
|
{loading ? <p className="m-0 text-sm text-[#6c7a6d]">Loading…</p> : null}
|
|
45
81
|
{error ? <p className="m-0 text-sm text-[#9c3a3a]">{error}</p> : null}
|
|
46
82
|
{!loading && !error && !visible.length ? (
|
|
47
83
|
<p className="m-0 text-sm text-[#6c7a6d]">
|
|
48
|
-
{
|
|
84
|
+
{search.trim() ? "No notes match this search." : "No notes found."}
|
|
49
85
|
</p>
|
|
50
86
|
) : null}
|
|
51
87
|
{visible.length ? (
|
|
@@ -71,6 +107,15 @@ export function FolderNotesList({
|
|
|
71
107
|
))}
|
|
72
108
|
</ol>
|
|
73
109
|
) : null}
|
|
110
|
+
{matching.length ? (
|
|
111
|
+
<OffsetPagination
|
|
112
|
+
label="Notes"
|
|
113
|
+
total={matching.length}
|
|
114
|
+
offset={effectiveOffset}
|
|
115
|
+
pageSize={pageSize}
|
|
116
|
+
onOffsetChange={setOffset}
|
|
117
|
+
/>
|
|
118
|
+
) : null}
|
|
74
119
|
</div>
|
|
75
120
|
)
|
|
76
121
|
}
|