kennel 0.2.44 → 0.2.45
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/core/api-client.d.ts +1 -1
- package/dist/core/api-client.js +35 -2
- package/dist/core/runtime.js +548 -99
- package/dist/llm/worker.js +11 -2
- package/dist/notes/command.js +20 -4
- package/dist/package/preview.d.ts +1 -1
- package/dist/package/preview.js +760 -495
- package/dist/site/hot-viewer.js +4 -4
- package/dist/site/server-plugins.js +468 -30
- package/dist/site/viewer.d.ts +0 -3
- package/dist/site/viewer.js +11 -41
- package/dist/tui/launcher.d.ts +11 -0
- package/dist/{site/terminal.js → tui/launcher.js} +60 -15
- package/package.json +2 -2
- package/web/assets/{abnfDiagram-VRR7QNED-ooA_TyhC.js → abnfDiagram-VRR7QNED-D8mHNUwf.js} +1 -1
- package/web/assets/{arc-DqQkY48J.js → arc-DORR-Lbn.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-DaXzRGP0.js → architectureDiagram-ZJ3FMSHR-CnmL2DAX.js} +1 -1
- package/web/assets/ask-tray-KUTWZ4MV-VQJJAsDn.js +1 -0
- package/web/assets/{blockDiagram-677ZJIJ3-Br5Y_dbP.js → blockDiagram-677ZJIJ3-_SWUT5nP.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CWIy0S0N.js → c4Diagram-LMCZKHZV-r7ghc180.js} +1 -1
- package/web/assets/channel-r9-1szjb.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-8dxcuNwr.js → chunk-2Q5K7J3B-D_LeMwFJ.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-DB27hz0Y.js → chunk-32BRIVSS-AeVZw1It.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-CTNxGtbA.js → chunk-5VM5RSS4-Bp_epQ8r.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-oKNLktOC.js → chunk-EX3LRPZG-Dz4n-DeM.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-DeQNmWoS.js → chunk-JWPE2WC7-B0MQp8TQ.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-BrW33Mrb.js → chunk-MOJQB5TN-Bkomqk1x.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-DEkTU6j-.js → chunk-RYQCIY6F-B0F7WMyE.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-BuDLgwdB.js → chunk-V7JOEXUC-BDifU9oY.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BsNgrrKe.js → chunk-VR4S4FIN-Bgd4HCG_.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DjT1Bydl.js → chunk-XXDRQBXY-BK8FhRs4.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-L_jmDmNd.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-L_jmDmNd.js +1 -0
- package/web/assets/client-5Q4VI7ZT-BkTtFalV.js +52 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BtAklxrd.js → cose-bilkent-JH36ORCC-Dm3k2U47.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DabtZWZy.js → cynefin-VYW2F7L2-DCnilF1f.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DVk8uQ8L.js → cynefinDiagram-TSTJHNR4-BrulEEQ9.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-CiAbBCor.js → dagre-VKFMJZFB-Dda61i4h.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-DWp-OzJz.js → diagram-FQU43EPY-sTmDEDMM.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-YDllS_1P.js → diagram-G47NLZAW-d-KQnPrm.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BH3l4yRj.js → diagram-NH7WQ7WH-DcRxa_ps.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-CszBvxdr.js → diagram-OA4YK3LP-CYzWtWvV.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-bEhZeER6.js → diagram-WEI45ONY-FI3V_N07.js} +1 -1
- package/web/assets/document-view-TGFRNHKS-VSJS3BGw.js +24 -0
- package/web/assets/{ebnfDiagram-CCIWWBDH-DlpoUUO5.js → ebnfDiagram-CCIWWBDH-DcvVmQhu.js} +1 -1
- package/web/assets/{entity-search-page-YP4SPMBC-WryWZNPk.js → entity-search-page-RIYQYIJJ-Bed3y-5Q.js} +5 -5
- package/web/assets/{erDiagram-Q63AITRT-DROgehIM.js → erDiagram-Q63AITRT-DI5DBlBp.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-63pYiBzl.js → flowDiagram-23GEKE2U-Bz1AbRxB.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-tc34wKHJ.js → ganttDiagram-NO4QXBWP-ChKlhxeo.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-GOjqt583.js → gitGraphDiagram-IHSO6WYX-BtTqLSyY.js} +1 -1
- package/web/assets/{index-qmu99cjp.js → index-CDAH4UPU.js} +157 -157
- package/web/assets/{infoDiagram-FWYZ7A6U-CD5BwkLQ.js → infoDiagram-FWYZ7A6U-COpIz9uZ.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-yIweY6wR.js → ishikawaDiagram-FXEZZL3T-CB7Bdsfr.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-D3MeUGvn.js → journeyDiagram-5HDEW3XC-BMytnJBE.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-CnZ5N79y.js → kanban-definition-HUTT4EX6-BFS4G0g-.js} +1 -1
- package/web/assets/{linear-s5G6iOMz.js → linear-DMUO2qBp.js} +1 -1
- package/web/assets/{linked-session-header-DOEX4FYM-CtTOXcZn.js → linked-session-header-7BUTWROP-DzEcA4Fq.js} +1 -1
- package/web/assets/{mermaid.core-BpUKp9r_.js → mermaid.core-BAj-Ltr-.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-Bp2QH5sN.js → mindmap-definition-LN4V7U3C-CjymOEvH.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-lx1jDUmt.js → pegDiagram-2B236MQR-D64jiUPu.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P--kf2YNbw.js → pieDiagram-ENE6RG2P-CCbI5U6o.js} +1 -1
- package/web/assets/{plugin-host-modules-BKbxlu7U.js → plugin-host-modules-D7pHtNJn.js} +2 -2
- package/web/assets/{project-search-page-TZG7AZBQ-3W40ayYn.js → project-search-page-O6YRLDGU-1bPrifgb.js} +2 -2
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Vr7zWQYV.js → quadrantDiagram-ABIIQ3AL-B7dN_Kn5.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-CqWD8KUC.js → railroadDiagram-RFXS5EU6-BPjAzqcL.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-BHUBdOVh.js → requirementDiagram-TGXJPOKE-BFhnnUWa.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-Cw_ZddWI.js → sankeyDiagram-HTMAVEWB-W2UguUjW.js} +1 -1
- package/web/assets/{selection-popup-ABYH3A27-Gc-RFSWj.js → selection-popup-ABYH3A27-DsiLbG7S.js} +4 -4
- package/web/assets/{sequenceDiagram-DBY2YBRQ-CyCbkAfM.js → sequenceDiagram-DBY2YBRQ-DcOakOD4.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-BG6RM8rb.js → sizeCapture-X5ZJPWSS-D3N248Z6.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-DKpZl5UU.js → stateDiagram-2N3HPSRC-Cir3yiDe.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-DHUy-0iS.js → stateDiagram-v2-6OUMAXLB-BwL0KYLn.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-D14uyHMH.js → swimlanes-5IMT3BWC-Dhq7FmZr.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-D4q-nPpB.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-f4WPhEXS.js → timeline-definition-FHXFAJF6-CMa1wjsI.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-lr4A8guw.js → vennDiagram-L72KCM5P-DvglazwQ.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-CR-Im585.js → wardleyDiagram-EHGQE667-DcogrL32.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DeNQfP7k.js → xychartDiagram-FW5EYKEG-BlsDcr_U.js} +1 -1
- package/web/index.html +1 -1
- package/dist/site/terminal.d.ts +0 -14
- package/web/assets/ask-tray-KUTWZ4MV-Ckn1y1Zw.js +0 -24
- package/web/assets/channel-BQxXBpeY.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-BT1TSwZs.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-BT1TSwZs.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-C5riej52.js +0 -8
package/dist/site/hot-viewer.js
CHANGED
|
@@ -6,10 +6,10 @@ import { createConnection } from 'node:net';
|
|
|
6
6
|
import { tmpdir } from 'node:os';
|
|
7
7
|
import { dirname, relative, resolve } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import { popUrl
|
|
9
|
+
import { popUrl } from './viewer.js';
|
|
10
10
|
import { createKennelClient } from '../core/api-client.js';
|
|
11
11
|
import { pluginSetKey } from '../plugins/external.js';
|
|
12
|
-
import { openPreview, previewTerminal } from '
|
|
12
|
+
import { openPreview, previewTerminal } from '../tui/launcher.js';
|
|
13
13
|
function sourceRoot() {
|
|
14
14
|
return resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
|
|
15
15
|
}
|
|
@@ -179,7 +179,7 @@ export async function hotViewer(options, restarting = false) {
|
|
|
179
179
|
return hotViewer(options, true);
|
|
180
180
|
throw new Error(stale);
|
|
181
181
|
}
|
|
182
|
-
const terminal = previewTerminal(viewerUrl, options
|
|
182
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
183
183
|
if (options.open)
|
|
184
184
|
openPreview(viewerUrl);
|
|
185
185
|
try {
|
|
@@ -245,7 +245,7 @@ export async function hotViewer(options, restarting = false) {
|
|
|
245
245
|
},
|
|
246
246
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
247
247
|
});
|
|
248
|
-
const terminal = previewTerminal(viewerUrl, options
|
|
248
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
249
249
|
void terminal.closed.then(() => {
|
|
250
250
|
if (child.exitCode === null && child.signalCode === null)
|
|
251
251
|
child.kill('SIGTERM');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// packages/api/src/server/plugins.ts
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import { mkdir, readFile, stat as
|
|
5
|
-
import { dirname, resolve as
|
|
3
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
4
|
+
import { mkdir, readFile as readFile2, stat as stat4, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname as dirname4, resolve as resolve5, sep as sep4 } from "node:path";
|
|
6
6
|
|
|
7
7
|
// packages/plugins/src/server.ts
|
|
8
8
|
class FolderPluginFailure extends Error {
|
|
@@ -12,15 +12,453 @@ class FolderPluginFailure extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// packages/knl/src/server.ts
|
|
16
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
|
|
19
|
+
// packages/knl/src/knl.ts
|
|
20
|
+
import { readdir, stat } from "node:fs/promises";
|
|
21
|
+
import { basename, dirname as dirname3, relative, resolve as resolve3, sep as sep2 } from "node:path";
|
|
22
|
+
import * as esbuild from "esbuild";
|
|
23
|
+
|
|
24
|
+
// packages/knl/src/folder-guard.ts
|
|
25
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
26
|
+
import { realpath } from "node:fs/promises";
|
|
27
|
+
import { dirname, resolve, sep } from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
var knlHostResolveDir = dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
var knlHostPackageFolders = (() => {
|
|
31
|
+
const folders = [];
|
|
32
|
+
let current = knlHostResolveDir;
|
|
33
|
+
while (true) {
|
|
34
|
+
const nodeModules = resolve(current, "node_modules");
|
|
35
|
+
if (existsSync(nodeModules))
|
|
36
|
+
folders.push(nodeModules);
|
|
37
|
+
const parent = dirname(current);
|
|
38
|
+
if (parent === current)
|
|
39
|
+
return folders;
|
|
40
|
+
current = parent;
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
async function knlRealRoot(owner) {
|
|
44
|
+
const expected = resolve(await realpath(owner), "knl");
|
|
45
|
+
const real = await realpath(resolve(owner, "knl"));
|
|
46
|
+
if (real !== expected)
|
|
47
|
+
throw new Error("knl/ must be a folder, not a link to another place.");
|
|
48
|
+
return real;
|
|
49
|
+
}
|
|
50
|
+
async function knlReadRoots(owner) {
|
|
51
|
+
const packages = await Promise.all(knlHostPackageFolders.map((folder) => realpath(folder).catch(() => {
|
|
52
|
+
return;
|
|
53
|
+
})));
|
|
54
|
+
return [
|
|
55
|
+
await knlRealRoot(owner),
|
|
56
|
+
...packages.filter((folder) => Boolean(folder))
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
function isKnlReadable(file, roots) {
|
|
60
|
+
let real;
|
|
61
|
+
try {
|
|
62
|
+
real = realpathSync(file);
|
|
63
|
+
} catch {
|
|
64
|
+
real = resolve(file);
|
|
65
|
+
}
|
|
66
|
+
return roots.some((root) => real === root || real.startsWith(`${root}${sep}`));
|
|
67
|
+
}
|
|
68
|
+
function knlReadError(file) {
|
|
69
|
+
return `KNL pages can only use files in knl/ and installed packages: ${file}`;
|
|
70
|
+
}
|
|
71
|
+
function assertKnlReadable(file, roots) {
|
|
72
|
+
if (!isKnlReadable(file, roots))
|
|
73
|
+
throw new Error(knlReadError(file));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// packages/knl/src/tailwind.ts
|
|
77
|
+
import { readFile } from "node:fs/promises";
|
|
78
|
+
import { createRequire } from "node:module";
|
|
79
|
+
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
80
|
+
import { compile } from "@tailwindcss/node";
|
|
81
|
+
import { Scanner } from "@tailwindcss/oxide";
|
|
82
|
+
var require2 = createRequire(import.meta.url);
|
|
83
|
+
function starterKnlStyles() {
|
|
84
|
+
return `@layer theme, utilities;
|
|
85
|
+
@import "tailwindcss/theme.css" layer(theme) prefix(knl);
|
|
86
|
+
@import "tailwindcss/utilities.css" layer(utilities) source(none);
|
|
87
|
+
|
|
88
|
+
@theme {
|
|
89
|
+
--font-sans: system-ui, sans-serif;
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
async function compileKnlCss(js, stylesheet, roots) {
|
|
94
|
+
const base = dirname2(stylesheet);
|
|
95
|
+
assertKnlReadable(stylesheet, roots);
|
|
96
|
+
const css = await readFile(stylesheet, "utf8").catch((error) => {
|
|
97
|
+
if (error.code === "ENOENT")
|
|
98
|
+
return starterKnlStyles();
|
|
99
|
+
throw error;
|
|
100
|
+
});
|
|
101
|
+
const compiler = await compile(css, {
|
|
102
|
+
base,
|
|
103
|
+
from: stylesheet,
|
|
104
|
+
onDependency: (path) => assertKnlReadable(path, roots),
|
|
105
|
+
customJsResolver: async (id) => {
|
|
106
|
+
throw new Error(`KNL does not load Tailwind plugins or configs (${id}).`);
|
|
107
|
+
},
|
|
108
|
+
customCssResolver: async (id) => id === "tailwindcss" || id.startsWith("tailwindcss/") ? require2.resolve(id) : undefined
|
|
109
|
+
});
|
|
110
|
+
const sources = [...compiler.sources];
|
|
111
|
+
if (compiler.root !== "none") {
|
|
112
|
+
sources.push({
|
|
113
|
+
...compiler.root ?? { base, pattern: "**/*" },
|
|
114
|
+
negated: false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
for (const source of sources) {
|
|
118
|
+
if (source.negated)
|
|
119
|
+
continue;
|
|
120
|
+
const fixed = source.pattern.split("/");
|
|
121
|
+
const glob = fixed.findIndex((part) => /[*?[{]/.test(part));
|
|
122
|
+
assertKnlReadable(resolve2(source.base, fixed.slice(0, glob < 0 ? undefined : glob).join("/")), roots);
|
|
123
|
+
}
|
|
124
|
+
const scanner = new Scanner({ sources });
|
|
125
|
+
const candidates = [
|
|
126
|
+
...scanner.scan(),
|
|
127
|
+
...scanner.scanFiles([{ content: js, extension: "js" }])
|
|
128
|
+
];
|
|
129
|
+
return compiler.build(candidates);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// packages/knl/src/knl.ts
|
|
133
|
+
var KNK_FOLDER = "knl";
|
|
134
|
+
var MAX_SOURCE_BYTES = 512 * 1024;
|
|
135
|
+
var MAX_JS_BYTES = 5 * 1024 * 1024;
|
|
136
|
+
var MODULE_FILES = ["index.tsx", "index.ts"];
|
|
137
|
+
function sourceFor(file, knlRoot, scanRoot, owner) {
|
|
138
|
+
const id = toModuleId(file, knlRoot);
|
|
139
|
+
return {
|
|
140
|
+
id,
|
|
141
|
+
file,
|
|
142
|
+
relative: relative(scanRoot, file).split(sep2).join("/"),
|
|
143
|
+
folder: dirname3(file),
|
|
144
|
+
owner
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function toModuleId(file, knlRoot) {
|
|
148
|
+
const relativeKnl = relative(knlRoot, file).split(sep2).join("/");
|
|
149
|
+
return relativeKnl.replace(/\/?index\.(tsx|ts)$/, "") || "index";
|
|
150
|
+
}
|
|
151
|
+
async function collectKnlSources(scanRoot, owner) {
|
|
152
|
+
const knlRoot = resolve3(scanRoot, KNK_FOLDER);
|
|
153
|
+
const knlEntries = await readdir(knlRoot, { withFileTypes: true }).catch(() => []);
|
|
154
|
+
const sources = [];
|
|
155
|
+
const addModule = async (folder) => {
|
|
156
|
+
for (const candidate of MODULE_FILES) {
|
|
157
|
+
const file = resolve3(folder, candidate);
|
|
158
|
+
if (await stat(file).then((d) => d.isFile()).catch(() => false)) {
|
|
159
|
+
sources.push(sourceFor(file, knlRoot, scanRoot, owner));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
await addModule(knlRoot);
|
|
165
|
+
for (const entry of knlEntries) {
|
|
166
|
+
if (!entry.isDirectory())
|
|
167
|
+
continue;
|
|
168
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
169
|
+
continue;
|
|
170
|
+
await addModule(resolve3(knlRoot, entry.name));
|
|
171
|
+
}
|
|
172
|
+
return sources.sort((left, right) => {
|
|
173
|
+
const rank = (left.id === "index" ? 0 : 1) - (right.id === "index" ? 0 : 1);
|
|
174
|
+
return rank !== 0 ? rank : left.id.localeCompare(right.id);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async function loadKnlSources(viewRoot, linkedFolders = []) {
|
|
178
|
+
const sources = [
|
|
179
|
+
...await collectKnlSources(viewRoot, viewRoot),
|
|
180
|
+
...(await Promise.all(linkedFolders.map(async (folder) => (await collectKnlSources(folder, folder)).map((source) => ({
|
|
181
|
+
...source,
|
|
182
|
+
id: `${basename(folder)}:${source.id}`,
|
|
183
|
+
relative: `${basename(folder)}/${source.relative}`
|
|
184
|
+
}))))).flat()
|
|
185
|
+
];
|
|
186
|
+
return { sources };
|
|
187
|
+
}
|
|
188
|
+
var MAX_CACHED_MODULES = 200;
|
|
189
|
+
var compileCache = new Map;
|
|
190
|
+
function knlNodePaths(knlRoot) {
|
|
191
|
+
return [resolve3(knlRoot, "node_modules"), ...knlHostPackageFolders];
|
|
192
|
+
}
|
|
193
|
+
async function compileKnlSource(source) {
|
|
194
|
+
const sourceBytes = (await stat(source.file)).size;
|
|
195
|
+
const tooLarge = (bytes, what) => ({
|
|
196
|
+
id: source.id,
|
|
197
|
+
relative: source.relative,
|
|
198
|
+
js: "",
|
|
199
|
+
css: "",
|
|
200
|
+
issues: [
|
|
201
|
+
{
|
|
202
|
+
path: source.relative,
|
|
203
|
+
message: `Module ${what} is too large (${bytes} bytes; limit ${MAX_SOURCE_BYTES}).`
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
if (sourceBytes > MAX_SOURCE_BYTES)
|
|
208
|
+
return tooLarge(sourceBytes, "source");
|
|
209
|
+
const cached = compileCache.get(source.file);
|
|
210
|
+
const unchanged = cached && (await Promise.all(cached.dependencies.map(async ({ file, mtimeMs }) => (await stat(file).catch(() => {
|
|
211
|
+
return;
|
|
212
|
+
}))?.mtimeMs === mtimeMs))).every(Boolean);
|
|
213
|
+
const knlRoot = resolve3(source.owner, KNK_FOLDER);
|
|
214
|
+
let issuePath = source.relative;
|
|
215
|
+
try {
|
|
216
|
+
const nodePaths = knlNodePaths(knlRoot);
|
|
217
|
+
const roots = await knlReadRoots(source.owner);
|
|
218
|
+
assertKnlReadable(source.file, roots);
|
|
219
|
+
let js;
|
|
220
|
+
if (cached && unchanged) {
|
|
221
|
+
js = cached.js;
|
|
222
|
+
} else {
|
|
223
|
+
const result = await esbuild.build({
|
|
224
|
+
absWorkingDir: source.folder,
|
|
225
|
+
stdin: {
|
|
226
|
+
contents: `import { createElement } from "react"
|
|
227
|
+
import { createRoot } from "react-dom/client"
|
|
228
|
+
import Component from ${JSON.stringify(source.file)}
|
|
229
|
+
|
|
230
|
+
globalThis.__knlMount = (container, context, onError) => {
|
|
231
|
+
const root = createRoot(container, { onUncaughtError: onError })
|
|
232
|
+
root.render(createElement(Component, context))
|
|
233
|
+
}
|
|
234
|
+
`,
|
|
235
|
+
sourcefile: `${source.file}.mount.ts`,
|
|
236
|
+
loader: "ts",
|
|
237
|
+
resolveDir: source.folder
|
|
238
|
+
},
|
|
239
|
+
bundle: true,
|
|
240
|
+
format: "iife",
|
|
241
|
+
target: "es2022",
|
|
242
|
+
jsx: "automatic",
|
|
243
|
+
platform: "browser",
|
|
244
|
+
write: false,
|
|
245
|
+
minify: false,
|
|
246
|
+
sourcemap: false,
|
|
247
|
+
logLevel: "silent",
|
|
248
|
+
metafile: true,
|
|
249
|
+
nodePaths,
|
|
250
|
+
plugins: [readGuard(roots)]
|
|
251
|
+
});
|
|
252
|
+
js = result.outputFiles?.[0]?.text ?? "";
|
|
253
|
+
const files = new Set([
|
|
254
|
+
resolve3(knlRoot, "package.json"),
|
|
255
|
+
...Object.keys(result.metafile.inputs).map((file) => resolve3(source.folder, file)).filter((file) => file !== `${source.file}.mount.ts`)
|
|
256
|
+
]);
|
|
257
|
+
const dependencies = await Promise.all([...files].map(async (file) => ({
|
|
258
|
+
file,
|
|
259
|
+
mtimeMs: (await stat(file).catch(() => {
|
|
260
|
+
return;
|
|
261
|
+
}))?.mtimeMs
|
|
262
|
+
})));
|
|
263
|
+
compileCache.delete(source.file);
|
|
264
|
+
compileCache.set(source.file, { js, dependencies });
|
|
265
|
+
if (compileCache.size > MAX_CACHED_MODULES)
|
|
266
|
+
compileCache.delete(compileCache.keys().next().value);
|
|
267
|
+
}
|
|
268
|
+
const stylesheet = resolve3(knlRoot, "style.css");
|
|
269
|
+
issuePath = relative(source.owner, stylesheet).split(sep2).join("/");
|
|
270
|
+
const css = await compileKnlCss(js, stylesheet, roots);
|
|
271
|
+
issuePath = source.relative;
|
|
272
|
+
if (js.length + css.length > MAX_JS_BYTES) {
|
|
273
|
+
return {
|
|
274
|
+
id: source.id,
|
|
275
|
+
relative: source.relative,
|
|
276
|
+
js: "",
|
|
277
|
+
css: "",
|
|
278
|
+
issues: [
|
|
279
|
+
{
|
|
280
|
+
path: source.relative,
|
|
281
|
+
message: `Compiled module is too large (${js.length + css.length} bytes; limit ${MAX_JS_BYTES}).`
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return { id: source.id, relative: source.relative, js, css, issues: [] };
|
|
287
|
+
} catch (error) {
|
|
288
|
+
return {
|
|
289
|
+
id: source.id,
|
|
290
|
+
relative: source.relative,
|
|
291
|
+
js: "",
|
|
292
|
+
css: "",
|
|
293
|
+
issues: knlBuildIssues(error, issuePath, source)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
var guardResolve = Symbol("knl-guard");
|
|
298
|
+
function readGuard(roots) {
|
|
299
|
+
return {
|
|
300
|
+
name: "knl-read-guard",
|
|
301
|
+
setup(build) {
|
|
302
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
303
|
+
if (args.pluginData === guardResolve)
|
|
304
|
+
return;
|
|
305
|
+
const resolveFrom = (resolveDir) => build.resolve(args.path, {
|
|
306
|
+
importer: args.importer,
|
|
307
|
+
kind: args.kind,
|
|
308
|
+
pluginData: guardResolve,
|
|
309
|
+
resolveDir
|
|
310
|
+
});
|
|
311
|
+
const readable = (result) => result.namespace !== "file" || !result.path || isKnlReadable(result.path, roots);
|
|
312
|
+
let result = await resolveFrom(args.resolveDir);
|
|
313
|
+
const bare = !/^(?:\.{1,2}(?:\/|$)|\/)/.test(args.path);
|
|
314
|
+
if (bare && result.errors.length === 0 && !readable(result))
|
|
315
|
+
result = await resolveFrom(knlHostResolveDir);
|
|
316
|
+
if (result.errors.length > 0)
|
|
317
|
+
return { errors: result.errors };
|
|
318
|
+
if (!readable(result))
|
|
319
|
+
return { errors: [{ text: knlReadError(args.path) }] };
|
|
320
|
+
return result;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function knlBuildIssues(error, relativePath, source) {
|
|
326
|
+
const failures = error?.errors;
|
|
327
|
+
if (Array.isArray(failures) && failures.length > 0) {
|
|
328
|
+
const first = failures[0];
|
|
329
|
+
const file = first?.location?.file ? relative(source.owner, resolve3(source.folder, first.location.file)).split(sep2).join("/") : relativePath;
|
|
330
|
+
const location = first?.location ? `${file}:${first.location.line ?? "?"}` : relativePath;
|
|
331
|
+
return [
|
|
332
|
+
{ path: location, message: first?.text ?? "Module failed to compile." }
|
|
333
|
+
];
|
|
334
|
+
}
|
|
335
|
+
return [
|
|
336
|
+
{
|
|
337
|
+
path: relativePath,
|
|
338
|
+
message: error instanceof Error && error.message ? error.message : "Module failed to compile."
|
|
339
|
+
}
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
function starterKnlSource() {
|
|
343
|
+
return `// knl/index.tsx — this folder's homepage (KNL folder web framework).
|
|
344
|
+
// Every other page lives in its own folder: knl/<page>/index.tsx.
|
|
345
|
+
import * as React from "react"
|
|
346
|
+
|
|
347
|
+
export default function Home() {
|
|
348
|
+
return (
|
|
349
|
+
<div className="knl:p-4 knl:font-sans">
|
|
350
|
+
<h1 className="knl:m-0 knl:text-2xl">Home</h1>
|
|
351
|
+
<p>Edit this file to build the homepage.</p>
|
|
352
|
+
</div>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// packages/knl/src/manifest.ts
|
|
359
|
+
var knlManifest = {
|
|
360
|
+
id: "knl",
|
|
361
|
+
label: "KNL"
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// packages/knl/src/server.ts
|
|
365
|
+
function createKnlPlugin() {
|
|
366
|
+
return {
|
|
367
|
+
...knlManifest,
|
|
368
|
+
async detect(context) {
|
|
369
|
+
const hasKnlFolder = await stat2(join(context.folder, KNK_FOLDER)).then((entry) => entry.isDirectory()).catch(() => false);
|
|
370
|
+
if (!hasKnlFolder)
|
|
371
|
+
return { status: "unavailable" };
|
|
372
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
373
|
+
if (!sources.some((source) => source.id === "index"))
|
|
374
|
+
return { status: "unavailable" };
|
|
375
|
+
return {
|
|
376
|
+
count: sources.length,
|
|
377
|
+
pages: sources.filter((source) => source.id !== "index" && !source.id.endsWith(":index")).map((source) => ({
|
|
378
|
+
id: source.id,
|
|
379
|
+
label: source.id.replaceAll(/[-_]/g, " ").replace(/^./, (letter) => letter.toUpperCase())
|
|
380
|
+
})),
|
|
381
|
+
status: "available"
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
async handle(context, subpath) {
|
|
385
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
386
|
+
const folder = context.folder;
|
|
387
|
+
if (sources.length === 0) {
|
|
388
|
+
return { kind: "available", data: { folder, status: "unavailable" } };
|
|
389
|
+
}
|
|
390
|
+
const requested = subpath.trim() || "index";
|
|
391
|
+
const selected = sources.filter((source) => source.id === requested);
|
|
392
|
+
if (selected.length === 0) {
|
|
393
|
+
return {
|
|
394
|
+
kind: "available",
|
|
395
|
+
data: {
|
|
396
|
+
folder,
|
|
397
|
+
status: "error",
|
|
398
|
+
message: `KNL page not found: ${requested}`
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
const compiled = await Promise.all(selected.map(compileKnlSource));
|
|
403
|
+
const entries = compiled.map((result) => {
|
|
404
|
+
const source = sources.find((item) => item.id === result.id);
|
|
405
|
+
return {
|
|
406
|
+
id: result.id,
|
|
407
|
+
relative: result.relative,
|
|
408
|
+
...result.js ? { js: result.js } : {},
|
|
409
|
+
css: result.css,
|
|
410
|
+
...result.issues.length > 0 ? { issues: result.issues } : {},
|
|
411
|
+
owner: source?.owner ?? folder
|
|
412
|
+
};
|
|
413
|
+
});
|
|
414
|
+
const failing = entries.filter((entry) => entry.issues?.length);
|
|
415
|
+
if (failing.length > 0) {
|
|
416
|
+
return {
|
|
417
|
+
kind: "available",
|
|
418
|
+
data: {
|
|
419
|
+
folder,
|
|
420
|
+
status: "error",
|
|
421
|
+
entries,
|
|
422
|
+
issues: failing[0]?.issues
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
kind: "available",
|
|
428
|
+
data: {
|
|
429
|
+
folder,
|
|
430
|
+
status: "available",
|
|
431
|
+
entries
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
},
|
|
435
|
+
async init() {
|
|
436
|
+
return {
|
|
437
|
+
files: [
|
|
438
|
+
{ path: "knl/index.tsx", content: starterKnlSource() },
|
|
439
|
+
{ path: "knl/style.css", content: starterKnlStyles() },
|
|
440
|
+
{
|
|
441
|
+
path: "knl/package.json",
|
|
442
|
+
content: `${JSON.stringify({ private: true, type: "module" }, null, 2)}
|
|
443
|
+
`
|
|
444
|
+
},
|
|
445
|
+
{ path: "knl/.gitignore", content: `node_modules/
|
|
446
|
+
` }
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
15
453
|
// packages/notes/src/server.ts
|
|
16
|
-
import { readdir, realpath, stat } from "node:fs/promises";
|
|
454
|
+
import { readdir as readdir2, realpath as realpath2, stat as stat3 } from "node:fs/promises";
|
|
17
455
|
import {
|
|
18
|
-
basename,
|
|
456
|
+
basename as basename2,
|
|
19
457
|
extname,
|
|
20
458
|
isAbsolute,
|
|
21
|
-
relative,
|
|
22
|
-
resolve,
|
|
23
|
-
sep
|
|
459
|
+
relative as relative2,
|
|
460
|
+
resolve as resolve4,
|
|
461
|
+
sep as sep3
|
|
24
462
|
} from "node:path";
|
|
25
463
|
|
|
26
464
|
// packages/notes/src/manifest.ts
|
|
@@ -42,11 +480,11 @@ var llmNoteLimit = 20;
|
|
|
42
480
|
async function listMarkdownFiles(folder) {
|
|
43
481
|
const files = [];
|
|
44
482
|
const visit = async (current) => {
|
|
45
|
-
const entries = await
|
|
483
|
+
const entries = await readdir2(current, { withFileTypes: true }).catch(() => []);
|
|
46
484
|
for (const entry of entries) {
|
|
47
485
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
48
486
|
continue;
|
|
49
|
-
const path =
|
|
487
|
+
const path = resolve4(current, entry.name);
|
|
50
488
|
if (entry.isDirectory()) {
|
|
51
489
|
await visit(path);
|
|
52
490
|
continue;
|
|
@@ -54,22 +492,22 @@ async function listMarkdownFiles(folder) {
|
|
|
54
492
|
if (!entry.isFile() || !markdownExtensions.has(extname(entry.name))) {
|
|
55
493
|
continue;
|
|
56
494
|
}
|
|
57
|
-
const details = await
|
|
495
|
+
const details = await stat3(path).catch(() => {
|
|
58
496
|
return;
|
|
59
497
|
});
|
|
60
498
|
files.push({
|
|
61
499
|
activityAt: details?.mtimeMs ?? 0,
|
|
62
|
-
name:
|
|
63
|
-
relativePath:
|
|
500
|
+
name: basename2(entry.name, extname(entry.name)),
|
|
501
|
+
relativePath: relative2(folder, path).split(sep3).join("/")
|
|
64
502
|
});
|
|
65
503
|
}
|
|
66
504
|
};
|
|
67
505
|
await visit(folder);
|
|
68
506
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
69
507
|
}
|
|
70
|
-
var canonical = (path) =>
|
|
508
|
+
var canonical = (path) => realpath2(path).catch(() => path);
|
|
71
509
|
function inside(path, root) {
|
|
72
|
-
const local =
|
|
510
|
+
const local = relative2(root, path);
|
|
73
511
|
return local === "" || !local.startsWith("..") && !isAbsolute(local);
|
|
74
512
|
}
|
|
75
513
|
async function withoutManaged(groups, managedRoots) {
|
|
@@ -81,7 +519,7 @@ async function withoutManaged(groups, managedRoots) {
|
|
|
81
519
|
return { ...group, files: [] };
|
|
82
520
|
}
|
|
83
521
|
const files = await Promise.all(group.files.map(async (file) => {
|
|
84
|
-
const real = await canonical(
|
|
522
|
+
const real = await canonical(resolve4(group.folder, file.relativePath));
|
|
85
523
|
return roots.some((root) => inside(real, root)) ? undefined : file;
|
|
86
524
|
}));
|
|
87
525
|
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
@@ -131,7 +569,7 @@ async function notesLlmSections({
|
|
|
131
569
|
continue;
|
|
132
570
|
seen.add(identity);
|
|
133
571
|
const files = await listMarkdownFiles(source);
|
|
134
|
-
const paths = files.map((file) =>
|
|
572
|
+
const paths = files.map((file) => resolve4(source, file.relativePath));
|
|
135
573
|
groups.push({ folder: source, paths });
|
|
136
574
|
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
137
575
|
}
|
|
@@ -203,14 +641,14 @@ function parsePluginRegistryConfig(content) {
|
|
|
203
641
|
};
|
|
204
642
|
}
|
|
205
643
|
async function nearestKennelConfig(folder) {
|
|
206
|
-
for (let current = folder;; current =
|
|
207
|
-
const candidate =
|
|
208
|
-
const details = await
|
|
644
|
+
for (let current = folder;; current = dirname4(current)) {
|
|
645
|
+
const candidate = resolve5(current, "kennel.json");
|
|
646
|
+
const details = await stat4(candidate).catch(() => {
|
|
209
647
|
return;
|
|
210
648
|
});
|
|
211
649
|
if (details?.isFile())
|
|
212
650
|
return candidate;
|
|
213
|
-
if (
|
|
651
|
+
if (dirname4(current) === current)
|
|
214
652
|
return;
|
|
215
653
|
}
|
|
216
654
|
}
|
|
@@ -219,13 +657,13 @@ async function folderPluginRegistryConfig(folder) {
|
|
|
219
657
|
if (!path)
|
|
220
658
|
return defaultPluginRegistryConfig;
|
|
221
659
|
try {
|
|
222
|
-
return parsePluginRegistryConfig(await
|
|
660
|
+
return parsePluginRegistryConfig(await readFile2(path, "utf8"));
|
|
223
661
|
} catch {
|
|
224
662
|
return defaultPluginRegistryConfig;
|
|
225
663
|
}
|
|
226
664
|
}
|
|
227
665
|
function createFolderServerPlugins() {
|
|
228
|
-
return [createNotesPlugin()];
|
|
666
|
+
return [createNotesPlugin(), createKnlPlugin()];
|
|
229
667
|
}
|
|
230
668
|
function folderServerRegistry() {
|
|
231
669
|
return new Map(createFolderServerPlugins().map((plugin) => [plugin.id, plugin]));
|
|
@@ -267,7 +705,7 @@ async function folderServerRegistryAsync() {
|
|
|
267
705
|
...server,
|
|
268
706
|
async detect(context) {
|
|
269
707
|
const detection = await server.detect(context);
|
|
270
|
-
if (detection.status === "unavailable" &&
|
|
708
|
+
if (detection.status === "unavailable" && existsSync2(resolve5(context.folder, entry.trigger))) {
|
|
271
709
|
return { status: "available" };
|
|
272
710
|
}
|
|
273
711
|
return detection;
|
|
@@ -305,7 +743,7 @@ async function externalApiHandlers() {
|
|
|
305
743
|
return handlers;
|
|
306
744
|
}
|
|
307
745
|
async function scaffoldFolderPlugins(folder, options = {}) {
|
|
308
|
-
const root =
|
|
746
|
+
const root = resolve5(folder);
|
|
309
747
|
const registry = folderServerRegistry();
|
|
310
748
|
const created = [];
|
|
311
749
|
const skipped = [];
|
|
@@ -316,8 +754,8 @@ async function scaffoldFolderPlugins(folder, options = {}) {
|
|
|
316
754
|
const context = { folder: root };
|
|
317
755
|
const result = await plugin.init(context);
|
|
318
756
|
const files = result.files.filter((file) => {
|
|
319
|
-
const target =
|
|
320
|
-
const contained = target === root || target.startsWith(root +
|
|
757
|
+
const target = resolve5(root, file.path);
|
|
758
|
+
const contained = target === root || target.startsWith(root + sep4);
|
|
321
759
|
return contained && file.path !== ".";
|
|
322
760
|
});
|
|
323
761
|
supported.push({
|
|
@@ -325,15 +763,15 @@ async function scaffoldFolderPlugins(folder, options = {}) {
|
|
|
325
763
|
files: files.map((file) => file.path)
|
|
326
764
|
});
|
|
327
765
|
for (const file of files) {
|
|
328
|
-
const target =
|
|
329
|
-
const exists = await
|
|
766
|
+
const target = resolve5(root, file.path);
|
|
767
|
+
const exists = await stat4(target).then(() => true).catch(() => false);
|
|
330
768
|
if (exists && !options.force) {
|
|
331
769
|
skipped.push({ path: file.path, plugin: plugin.id });
|
|
332
770
|
continue;
|
|
333
771
|
}
|
|
334
772
|
if (options.write === false)
|
|
335
773
|
continue;
|
|
336
|
-
await mkdir(
|
|
774
|
+
await mkdir(dirname4(target), { recursive: true });
|
|
337
775
|
await writeFile(target, file.content);
|
|
338
776
|
created.push({ path: file.path, plugin: plugin.id });
|
|
339
777
|
}
|
package/dist/site/viewer.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { type PluginView } from "./terminal.js";
|
|
2
|
-
/** Installed-plugin view links for the terminal menu; each is hyperlinked. */
|
|
3
|
-
export declare function pluginViewLinks(viewerUrl: string, root: string): PluginView[];
|
|
4
1
|
export type ViewerOptions = {
|
|
5
2
|
dryRun: boolean;
|
|
6
3
|
host: string;
|