kennel 0.2.43 → 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.
Files changed (85) hide show
  1. package/dist/core/api-client.d.ts +1 -1
  2. package/dist/core/api-client.js +35 -2
  3. package/dist/core/runtime.js +548 -99
  4. package/dist/llm/worker.js +11 -2
  5. package/dist/notes/command.js +20 -4
  6. package/dist/package/preview.d.ts +1 -1
  7. package/dist/package/preview.js +760 -495
  8. package/dist/site/hot-viewer.js +4 -4
  9. package/dist/site/server-plugins.js +468 -30
  10. package/dist/site/viewer.d.ts +0 -3
  11. package/dist/site/viewer.js +11 -41
  12. package/dist/tui/launcher.d.ts +11 -0
  13. package/dist/{site/terminal.js → tui/launcher.js} +60 -15
  14. package/package.json +2 -2
  15. package/web/assets/{abnfDiagram-VRR7QNED-C95Ks6-o.js → abnfDiagram-VRR7QNED-D8mHNUwf.js} +1 -1
  16. package/web/assets/{arc-BZo1f2tP.js → arc-DORR-Lbn.js} +1 -1
  17. package/web/assets/{architectureDiagram-ZJ3FMSHR-DeheWY9Z.js → architectureDiagram-ZJ3FMSHR-CnmL2DAX.js} +1 -1
  18. package/web/assets/ask-tray-KUTWZ4MV-VQJJAsDn.js +1 -0
  19. package/web/assets/{blockDiagram-677ZJIJ3-C2gPgefk.js → blockDiagram-677ZJIJ3-_SWUT5nP.js} +1 -1
  20. package/web/assets/{c4Diagram-LMCZKHZV-D7eqpETg.js → c4Diagram-LMCZKHZV-r7ghc180.js} +1 -1
  21. package/web/assets/channel-r9-1szjb.js +1 -0
  22. package/web/assets/{chunk-2Q5K7J3B-CqPrOden.js → chunk-2Q5K7J3B-D_LeMwFJ.js} +1 -1
  23. package/web/assets/{chunk-32BRIVSS-CxWYyFcn.js → chunk-32BRIVSS-AeVZw1It.js} +1 -1
  24. package/web/assets/{chunk-5VM5RSS4-WxfwUe_w.js → chunk-5VM5RSS4-Bp_epQ8r.js} +1 -1
  25. package/web/assets/{chunk-EX3LRPZG-BwbKQfAB.js → chunk-EX3LRPZG-Dz4n-DeM.js} +1 -1
  26. package/web/assets/{chunk-JWPE2WC7-CY2fyOB-.js → chunk-JWPE2WC7-B0MQp8TQ.js} +1 -1
  27. package/web/assets/{chunk-MOJQB5TN-BJb4GtCJ.js → chunk-MOJQB5TN-Bkomqk1x.js} +1 -1
  28. package/web/assets/{chunk-RYQCIY6F-FZB0Ijl-.js → chunk-RYQCIY6F-B0F7WMyE.js} +1 -1
  29. package/web/assets/{chunk-V7JOEXUC-Bat_9367.js → chunk-V7JOEXUC-BDifU9oY.js} +1 -1
  30. package/web/assets/{chunk-VR4S4FIN-BV_nIsil.js → chunk-VR4S4FIN-Bgd4HCG_.js} +1 -1
  31. package/web/assets/{chunk-XXDRQBXY-nZF49NM1.js → chunk-XXDRQBXY-BK8FhRs4.js} +1 -1
  32. package/web/assets/classDiagram-OUVF2IWQ-L_jmDmNd.js +1 -0
  33. package/web/assets/classDiagram-v2-EOCWNBFH-L_jmDmNd.js +1 -0
  34. package/web/assets/client-5Q4VI7ZT-BkTtFalV.js +52 -0
  35. package/web/assets/{cose-bilkent-JH36ORCC-5jLaefF7.js → cose-bilkent-JH36ORCC-Dm3k2U47.js} +1 -1
  36. package/web/assets/{cynefin-VYW2F7L2-D_M6zxNK.js → cynefin-VYW2F7L2-DCnilF1f.js} +1 -1
  37. package/web/assets/{cynefinDiagram-TSTJHNR4-hmCcn34X.js → cynefinDiagram-TSTJHNR4-BrulEEQ9.js} +1 -1
  38. package/web/assets/{dagre-VKFMJZFB-Brka7Xhg.js → dagre-VKFMJZFB-Dda61i4h.js} +1 -1
  39. package/web/assets/{diagram-FQU43EPY-BWpeshcb.js → diagram-FQU43EPY-sTmDEDMM.js} +1 -1
  40. package/web/assets/{diagram-G47NLZAW-Cl2XbP7n.js → diagram-G47NLZAW-d-KQnPrm.js} +1 -1
  41. package/web/assets/{diagram-NH7WQ7WH-BBF0AyF6.js → diagram-NH7WQ7WH-DcRxa_ps.js} +1 -1
  42. package/web/assets/{diagram-OA4YK3LP-ggxUOgcy.js → diagram-OA4YK3LP-CYzWtWvV.js} +1 -1
  43. package/web/assets/{diagram-WEI45ONY-5gqEm0IZ.js → diagram-WEI45ONY-FI3V_N07.js} +1 -1
  44. package/web/assets/document-view-TGFRNHKS-VSJS3BGw.js +24 -0
  45. package/web/assets/{ebnfDiagram-CCIWWBDH-DvOOCI9m.js → ebnfDiagram-CCIWWBDH-DcvVmQhu.js} +1 -1
  46. package/web/assets/{entity-search-page-YP4SPMBC-CL6z_JLV.js → entity-search-page-RIYQYIJJ-Bed3y-5Q.js} +5 -5
  47. package/web/assets/{erDiagram-Q63AITRT-LMvg3eDB.js → erDiagram-Q63AITRT-DI5DBlBp.js} +1 -1
  48. package/web/assets/{flowDiagram-23GEKE2U-BkWKqObX.js → flowDiagram-23GEKE2U-Bz1AbRxB.js} +1 -1
  49. package/web/assets/{ganttDiagram-NO4QXBWP-DUGEnNqM.js → ganttDiagram-NO4QXBWP-ChKlhxeo.js} +1 -1
  50. package/web/assets/{gitGraphDiagram-IHSO6WYX-CDQG334H.js → gitGraphDiagram-IHSO6WYX-BtTqLSyY.js} +1 -1
  51. package/web/assets/{index-B4WnXxGr.js → index-CDAH4UPU.js} +157 -157
  52. package/web/assets/{infoDiagram-FWYZ7A6U-BVS6-42t.js → infoDiagram-FWYZ7A6U-COpIz9uZ.js} +1 -1
  53. package/web/assets/{ishikawaDiagram-FXEZZL3T-B63B2D4K.js → ishikawaDiagram-FXEZZL3T-CB7Bdsfr.js} +1 -1
  54. package/web/assets/{journeyDiagram-5HDEW3XC-DFZcImlX.js → journeyDiagram-5HDEW3XC-BMytnJBE.js} +1 -1
  55. package/web/assets/{kanban-definition-HUTT4EX6-Cmd4XrqE.js → kanban-definition-HUTT4EX6-BFS4G0g-.js} +1 -1
  56. package/web/assets/{linear-DAkYxlnA.js → linear-DMUO2qBp.js} +1 -1
  57. package/web/assets/{linked-session-header-DOEX4FYM-D0mAsins.js → linked-session-header-7BUTWROP-DzEcA4Fq.js} +1 -1
  58. package/web/assets/{mermaid.core-BE2AlF1A.js → mermaid.core-BAj-Ltr-.js} +4 -4
  59. package/web/assets/{mindmap-definition-LN4V7U3C-SuG3IorS.js → mindmap-definition-LN4V7U3C-CjymOEvH.js} +1 -1
  60. package/web/assets/{pegDiagram-2B236MQR-Bscdsbzn.js → pegDiagram-2B236MQR-D64jiUPu.js} +1 -1
  61. package/web/assets/{pieDiagram-ENE6RG2P-Dg_t_TWu.js → pieDiagram-ENE6RG2P-CCbI5U6o.js} +1 -1
  62. package/web/assets/{plugin-host-modules-CcpBt3st.js → plugin-host-modules-D7pHtNJn.js} +2 -2
  63. package/web/assets/{project-search-page-TZG7AZBQ-DrMBMFbS.js → project-search-page-O6YRLDGU-1bPrifgb.js} +2 -2
  64. package/web/assets/{quadrantDiagram-ABIIQ3AL-CHImGA_b.js → quadrantDiagram-ABIIQ3AL-B7dN_Kn5.js} +1 -1
  65. package/web/assets/{railroadDiagram-RFXS5EU6-BZRS_CFF.js → railroadDiagram-RFXS5EU6-BPjAzqcL.js} +1 -1
  66. package/web/assets/{requirementDiagram-TGXJPOKE-Ckw1abgN.js → requirementDiagram-TGXJPOKE-BFhnnUWa.js} +1 -1
  67. package/web/assets/{sankeyDiagram-HTMAVEWB-BZVBAe8k.js → sankeyDiagram-HTMAVEWB-W2UguUjW.js} +1 -1
  68. package/web/assets/{selection-popup-ABYH3A27-D0_B1ozz.js → selection-popup-ABYH3A27-DsiLbG7S.js} +4 -4
  69. package/web/assets/{sequenceDiagram-DBY2YBRQ-DA8YzEAG.js → sequenceDiagram-DBY2YBRQ-DcOakOD4.js} +1 -1
  70. package/web/assets/{sizeCapture-X5ZJPWSS-B_aUT6yb.js → sizeCapture-X5ZJPWSS-D3N248Z6.js} +1 -1
  71. package/web/assets/{stateDiagram-2N3HPSRC-DE0B_1vK.js → stateDiagram-2N3HPSRC-Cir3yiDe.js} +1 -1
  72. package/web/assets/{stateDiagram-v2-6OUMAXLB-LQnwbWz3.js → stateDiagram-v2-6OUMAXLB-BwL0KYLn.js} +1 -1
  73. package/web/assets/{swimlanes-5IMT3BWC-DGjZrxBh.js → swimlanes-5IMT3BWC-Dhq7FmZr.js} +2 -2
  74. package/web/assets/swimlanesDiagram-G3AALYLV-D4q-nPpB.js +8 -0
  75. package/web/assets/{timeline-definition-FHXFAJF6-BnDMvIhe.js → timeline-definition-FHXFAJF6-CMa1wjsI.js} +1 -1
  76. package/web/assets/{vennDiagram-L72KCM5P-Bolx4asy.js → vennDiagram-L72KCM5P-DvglazwQ.js} +1 -1
  77. package/web/assets/{wardleyDiagram-EHGQE667-DKGCLLoK.js → wardleyDiagram-EHGQE667-DcogrL32.js} +1 -1
  78. package/web/assets/{xychartDiagram-FW5EYKEG-BEPuzAws.js → xychartDiagram-FW5EYKEG-BlsDcr_U.js} +1 -1
  79. package/web/index.html +1 -1
  80. package/dist/site/terminal.d.ts +0 -14
  81. package/web/assets/ask-tray-KUTWZ4MV-CxvfIw9u.js +0 -24
  82. package/web/assets/channel-CrJW7F7C.js +0 -1
  83. package/web/assets/classDiagram-OUVF2IWQ-D9l66AaJ.js +0 -1
  84. package/web/assets/classDiagram-v2-EOCWNBFH-D9l66AaJ.js +0 -1
  85. package/web/assets/swimlanesDiagram-G3AALYLV-C-GBzQUn.js +0 -8
@@ -210,10 +210,119 @@ var init_document_kinds = __esm(() => {
210
210
  };
211
211
  });
212
212
 
213
- // packages/preview/src/terminal.ts
214
- import { execFile, execFileSync } from "node:child_process";
215
- import { basename } from "node:path";
216
- import { clearScreenDown, cursorTo, emitKeypressEvents } from "node:readline";
213
+ // packages/plugins/src/client.ts
214
+ function folderPathId(path) {
215
+ const bytes = new TextEncoder().encode(path);
216
+ let binary = "";
217
+ for (const byte of bytes)
218
+ binary += String.fromCharCode(byte);
219
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
220
+ }
221
+ var init_client = () => {};
222
+
223
+ // packages/api/src/shared/viewer-route.ts
224
+ function decodeBase64Url(value) {
225
+ const padded = `${value}${"=".repeat((4 - value.length % 4) % 4)}`;
226
+ const binary = atob(padded.replaceAll("-", "+").replaceAll("_", "/"));
227
+ return new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
228
+ }
229
+ function url(value) {
230
+ if (/^https?:\/\//.test(value))
231
+ return new URL(value);
232
+ if (value.startsWith("?"))
233
+ return new URL(`/${value}`, "http://127.0.0.1");
234
+ return new URL(value || "/", "http://127.0.0.1");
235
+ }
236
+ function tabSection(tab) {
237
+ if (tab === "files")
238
+ return "notes";
239
+ if (tab === "projects" || tab === "sessions")
240
+ return tab;
241
+ return;
242
+ }
243
+ function folderPathId2(path) {
244
+ return folderPathId(path);
245
+ }
246
+ function decodeFolderPathId(id) {
247
+ return decodeBase64Url(id);
248
+ }
249
+ function folderHomeHref(folder) {
250
+ return `/folder/${folderPathId2(folder)}`;
251
+ }
252
+ function folderPluginHref(folder, pluginId, subpath = "") {
253
+ const base = `/folder/${folderPathId2(folder)}/views/${encodeURIComponent(pluginId)}`;
254
+ return subpath ? `${base}/${encodeURIComponent(subpath)}` : base;
255
+ }
256
+ function folderUrl(base, folder, options = {}) {
257
+ const current = new URL(base);
258
+ current.pathname = options.view ? folderPluginHref(folder, options.view.pluginId, options.view.subpath) : options.document ? folderDocumentHref(folder, options.document) : folderHomeHref(folder);
259
+ current.searchParams.delete("folder");
260
+ if (options.document && options.watch && !options.view) {
261
+ current.searchParams.set("watch", "1");
262
+ }
263
+ return current;
264
+ }
265
+ function folderDocumentHref(folder, document2) {
266
+ return `/folder/${folderPathId2(folder)}/document/${folderPathId2(document2)}`;
267
+ }
268
+ function parseViewerRoute(value) {
269
+ const current = url(value);
270
+ const session = current.searchParams.get("session") ?? undefined;
271
+ const folder = current.searchParams.get("folder");
272
+ if (folder) {
273
+ return {
274
+ document: current.searchParams.get("document") ?? current.searchParams.get("file") ?? undefined,
275
+ embedded: current.searchParams.get("embedded") === "1",
276
+ folder,
277
+ panel: current.searchParams.get("panel") === "1",
278
+ section: tabSection(current.searchParams.get("tab")) ?? "notes",
279
+ ...session ? { session } : {}
280
+ };
281
+ }
282
+ const parts = current.pathname.split("/").filter(Boolean);
283
+ if (parts[0] !== "folder" || !parts[1]) {
284
+ return {
285
+ document: current.searchParams.get("file") ?? current.searchParams.get("document") ?? undefined,
286
+ embedded: current.searchParams.get("embedded") === "1",
287
+ folder: undefined,
288
+ panel: current.searchParams.get("panel") === "1",
289
+ section: tabSection(current.searchParams.get("tab")),
290
+ ...session ? { session } : {}
291
+ };
292
+ }
293
+ const section = parts[2];
294
+ const route = {
295
+ embedded: current.searchParams.get("embedded") === "1",
296
+ folder: decodeFolderPathId(parts[1]),
297
+ panel: current.searchParams.get("panel") === "1",
298
+ ...session ? { session } : {}
299
+ };
300
+ if (section === "collections" && parts[3]) {
301
+ return { ...route, collectionId: decodeFolderPathId(parts[3]) };
302
+ }
303
+ if (section === "views" && parts[3]) {
304
+ return {
305
+ ...route,
306
+ view: {
307
+ pluginId: decodeURIComponent(parts[3]),
308
+ ...parts[4] ? { subpath: parts[4] } : {}
309
+ }
310
+ };
311
+ }
312
+ if (section === "notes" || section === "projects" || section === "sessions") {
313
+ return { ...route, section };
314
+ }
315
+ if (section === "document" && parts[3]) {
316
+ return { ...route, document: decodeFolderPathId(parts[3]) };
317
+ }
318
+ return route;
319
+ }
320
+ var init_viewer_route = __esm(() => {
321
+ init_client();
322
+ });
323
+
324
+ // packages/preview/src/runtime.ts
325
+ import { execFile } from "node:child_process";
217
326
  function openPreview(url) {
218
327
  const command = process.platform === "darwin" ? "/usr/bin/open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
219
328
  execFile(command, [url], (error) => {
@@ -221,212 +330,21 @@ function openPreview(url) {
221
330
  console.error(`Could not open browser: ${error.message}`);
222
331
  });
223
332
  }
224
- function copyPreviewLink(url) {
225
- const command = process.platform === "darwin" ? "/usr/bin/pbcopy" : process.platform === "win32" ? "clip" : process.env.WAYLAND_DISPLAY ? "wl-copy" : "xclip";
226
- const args = command === "xclip" ? ["-selection", "clipboard"] : [];
227
- return new Promise((done, fail) => {
228
- const child = execFile(command, args, (error) => error ? fail(error) : done());
229
- child.stdin?.on("error", fail);
230
- child.stdin?.end(url);
231
- });
232
- }
233
- function repositoryName(folder) {
234
- try {
235
- const root = execFileSync("git", ["-C", folder, "rev-parse", "--show-toplevel"], {
236
- encoding: "utf8",
237
- stdio: ["ignore", "pipe", "ignore"],
238
- timeout: 1000
239
- });
240
- return terminalText(basename(root.trim()));
241
- } catch {
242
- return;
243
- }
244
- }
245
- function previewTerminal(url, target, startShare, views) {
246
- url = new URL(url).href;
247
- const input = process.stdin;
248
- const output = process.stdout;
249
- const terminal = Boolean(output.isTTY && process.env.TERM !== "dumb");
250
- const interactive = terminal && Boolean(input.isTTY);
251
- const name = terminalText(basename(target.target ?? target.root) || target.root);
252
- const title = terminalText(basename(target.root) || target.root);
253
- const repository = interactive ? repositoryName(target.root) : undefined;
254
- const color = terminal && process.env.NO_COLOR === undefined && output.hasColors();
255
- const accent = (text) => color ? `\x1B[32m${text}\x1B[39m` : text;
256
- const linkTo = (label, href, plain = `${label}
257
- ${href}`) => terminal ? `\x1B]8;;${href}\x07${accent(label)}\x1B]8;;\x07` : plain;
258
- const link = (label) => linkTo(label, url);
259
- const wasRaw = Boolean(input.isRaw);
260
- const wasFlowing = input.readableFlowing === true;
333
+ function viewerLifetime() {
261
334
  let resolveClosed;
262
335
  const closed = new Promise((resolve) => {
263
336
  resolveClosed = resolve;
264
337
  });
265
- let finished = false;
266
- let busy = false;
267
- let selected = 0;
268
- let status = "";
269
- let share;
270
- const render = () => {
271
- if (!interactive || finished)
272
- return;
273
- cursorTo(output, 0, 0);
274
- clearScreenDown(output);
275
- const width = Math.max(1, (output.columns || 80) - 1);
276
- const fit = (text) => {
277
- const characters = Array.from(terminalText(text));
278
- return characters.length > width ? characters.slice(0, width - 1).join("") + "…" : characters.join("");
279
- };
280
- const choices = [];
281
- actions.forEach((item, index) => {
282
- const label = fit(item.shortcut ? `[${item.shortcut}] ${item.label}` : item.label);
283
- let text = index === selected ? accent(`› ${label}`) : ` ${label}`;
284
- if (item.view !== undefined)
285
- text = linkTo(text, item.view, accent(text));
286
- choices.push(text);
287
- });
288
- output.write([
289
- `\x1B[1m${link(fit(title))}\x1B[22m`,
290
- ...repository ? [`${color ? "\x1B[2m" : ""}${fit(repository)}${color ? "\x1B[22m" : ""}`] : [],
291
- ...target.target ? [`File: ${fit(name)}`] : [],
292
- "",
293
- ...choices,
294
- "",
295
- "↑ ↓ to move · Enter to choose · Esc or Ctrl+C to exit",
296
- ...status ? ["", status] : [],
297
- ""
298
- ].join(`
299
- `));
300
- };
301
- const shareFolder = async (password = false) => {
302
- status = "Preparing a private Tailscale link…";
303
- render();
304
- try {
305
- share ??= startShare(password);
306
- let active;
307
- try {
308
- active = await share;
309
- } catch (error) {
310
- share = undefined;
311
- throw error;
312
- }
313
- if (finished)
314
- return;
315
- status = active.password ? `Tailscale link: ${active.url}
316
- Username: ${active.username} · Password: ${active.password}
317
- Password required. Send the password separately.
318
- Connect Tailscale on your phone to the same tailnet.
319
- Trusted HTTPS; no certificate installation required.
320
- Keep this command running. Ctrl+C stops sharing.` : `Tailscale link: ${active.url}
321
- Connect Tailscale on your phone to the same tailnet.
322
- Trusted HTTPS; no password required on the tailnet.
323
- Keep this command running. Ctrl+C stops sharing.`;
324
- render();
325
- try {
326
- await copyPreviewLink(active.url);
327
- status += `
328
- Tailscale link copied to clipboard (without the password).`;
329
- } catch (error) {
330
- status += `
331
- Could not copy link: ${terminalText(error instanceof Error ? error.message : String(error))}`;
332
- }
333
- } catch (error) {
334
- status = `Could not share folder: ${terminalText(error instanceof Error ? error.message : String(error))}`;
335
- }
336
- };
337
338
  const close = () => {
338
- if (finished)
339
- return;
340
- finished = true;
341
339
  process.off("SIGINT", close);
342
340
  process.off("SIGTERM", close);
343
- process.off("exit", close);
344
- if (interactive) {
345
- input.off("keypress", keypress);
346
- input.off("end", close);
347
- output.off("resize", render);
348
- output.write("\x1B[?25h\x1B[?1049l");
349
- input.setRawMode(wasRaw);
350
- if (!wasFlowing)
351
- input.pause();
352
- }
353
- if (share) {
354
- share.then((active) => active.close()).catch((error) => {
355
- console.error(` Could not stop the Tailscale share: ${error instanceof Error ? error.message : error}`);
356
- }).finally(() => resolveClosed());
357
- } else
358
- resolveClosed();
359
- };
360
- const actions = [
361
- ...(views ?? []).map((view) => ({ label: view.id, run: () => {
362
- openPreview(view.url);
363
- status = "Browser open requested.";
364
- }, view: view.url })),
365
- { label: "Open in browser", shortcut: "o", run: () => {
366
- openPreview(url);
367
- status = "Browser open requested.";
368
- } },
369
- { label: "Share to another device", shortcut: "s", run: () => shareFolder(false) },
370
- { label: "Share link with password", shortcut: "p", run: () => shareFolder(true) },
371
- { label: "Exit", shortcut: "q", run: close }
372
- ];
373
- const activate = async () => {
374
- busy = true;
375
- try {
376
- await actions[selected].run();
377
- } catch (error) {
378
- status = `Could not complete action: ${terminalText(error instanceof Error ? error.message : String(error))}`;
379
- } finally {
380
- busy = false;
381
- render();
382
- }
383
- };
384
- const keypress = (_text, key) => {
385
- if (key.name === "escape" || key.ctrl && (key.name === "c" || key.name === "d")) {
386
- close();
387
- return;
388
- }
389
- if (busy || finished)
390
- return;
391
- const shortcut = !key.ctrl && !key.meta ? actions.findIndex((item) => item.shortcut === key.name) : -1;
392
- if (shortcut !== -1) {
393
- selected = shortcut;
394
- activate();
395
- return;
396
- }
397
- if (key.name === "up")
398
- selected = (selected - 1 + actions.length) % actions.length;
399
- else if (key.name === "down")
400
- selected = (selected + 1) % actions.length;
401
- else if (key.name === "return") {
402
- activate();
403
- return;
404
- } else
405
- return;
406
- render();
341
+ resolveClosed();
407
342
  };
408
343
  process.once("SIGINT", close);
409
344
  process.once("SIGTERM", close);
410
- process.once("exit", close);
411
- if (interactive) {
412
- output.write("\x1B[?1049h\x1B[?25l");
413
- output.on("resize", render);
414
- emitKeypressEvents(input);
415
- input.setRawMode(true);
416
- input.on("keypress", keypress);
417
- input.once("end", close);
418
- input.resume();
419
- }
420
- if (interactive)
421
- render();
422
- else
423
- console.log(`
424
- ${link(name)}
425
- `);
426
- return { close, closed, interactive };
345
+ return { close, closed };
427
346
  }
428
- var terminalText = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
429
- var init_terminal = () => {};
347
+ var init_runtime = () => {};
430
348
 
431
349
  // node_modules/.bun/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
432
350
  function randomID() {
@@ -5792,6 +5710,7 @@ var init_schemas3 = __esm(() => {
5792
5710
  icon: object({ paths: array(string2().min(1)).min(1) }).optional(),
5793
5711
  id: string2(),
5794
5712
  label: string2(),
5713
+ pages: array(object({ id: string2(), label: string2() })).optional(),
5795
5714
  subpath: string2().optional()
5796
5715
  });
5797
5716
  plugins = object({
@@ -5999,83 +5918,6 @@ var init_schemas3 = __esm(() => {
5999
5918
  };
6000
5919
  });
6001
5920
 
6002
- // packages/api/src/shared/viewer-route.ts
6003
- function decodeBase64Url(value) {
6004
- const padded = `${value}${"=".repeat((4 - value.length % 4) % 4)}`;
6005
- const binary = atob(padded.replaceAll("-", "+").replaceAll("_", "/"));
6006
- return new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
6007
- }
6008
- function url(value) {
6009
- if (/^https?:\/\//.test(value))
6010
- return new URL(value);
6011
- if (value.startsWith("?"))
6012
- return new URL(`/${value}`, "http://127.0.0.1");
6013
- return new URL(value || "/", "http://127.0.0.1");
6014
- }
6015
- function tabSection(tab) {
6016
- if (tab === "files")
6017
- return "notes";
6018
- if (tab === "projects" || tab === "sessions")
6019
- return tab;
6020
- return;
6021
- }
6022
- function decodeFolderPathId(id) {
6023
- return decodeBase64Url(id);
6024
- }
6025
- function parseViewerRoute(value) {
6026
- const current = url(value);
6027
- const session = current.searchParams.get("session") ?? undefined;
6028
- const folder = current.searchParams.get("folder");
6029
- if (folder) {
6030
- return {
6031
- document: current.searchParams.get("document") ?? current.searchParams.get("file") ?? undefined,
6032
- embedded: current.searchParams.get("embedded") === "1",
6033
- folder,
6034
- panel: current.searchParams.get("panel") === "1",
6035
- section: tabSection(current.searchParams.get("tab")) ?? "notes",
6036
- ...session ? { session } : {}
6037
- };
6038
- }
6039
- const parts = current.pathname.split("/").filter(Boolean);
6040
- if (parts[0] !== "folder" || !parts[1]) {
6041
- return {
6042
- document: current.searchParams.get("file") ?? current.searchParams.get("document") ?? undefined,
6043
- embedded: current.searchParams.get("embedded") === "1",
6044
- folder: undefined,
6045
- panel: current.searchParams.get("panel") === "1",
6046
- section: tabSection(current.searchParams.get("tab")),
6047
- ...session ? { session } : {}
6048
- };
6049
- }
6050
- const section = parts[2];
6051
- const route = {
6052
- embedded: current.searchParams.get("embedded") === "1",
6053
- folder: decodeFolderPathId(parts[1]),
6054
- panel: current.searchParams.get("panel") === "1",
6055
- ...session ? { session } : {}
6056
- };
6057
- if (section === "collections" && parts[3]) {
6058
- return { ...route, collectionId: decodeFolderPathId(parts[3]) };
6059
- }
6060
- if (section === "views" && parts[3]) {
6061
- return {
6062
- ...route,
6063
- view: {
6064
- pluginId: parts[3],
6065
- ...parts[4] ? { subpath: parts[4] } : {}
6066
- }
6067
- };
6068
- }
6069
- if (section === "notes" || section === "projects" || section === "sessions") {
6070
- return { ...route, section };
6071
- }
6072
- if (section === "document" && parts[3]) {
6073
- return { ...route, document: decodeFolderPathId(parts[3]) };
6074
- }
6075
- return { ...route, section: "notes" };
6076
- }
6077
- var init_viewer_route = () => {};
6078
-
6079
5921
  // packages/api-client/src/errors.ts
6080
5922
  async function expectedError(response) {
6081
5923
  const body = await response.text().catch(() => "");
@@ -6228,13 +6070,14 @@ var init_src = __esm(() => {
6228
6070
  init_schemas3();
6229
6071
  init_viewer_route();
6230
6072
  init_errors3();
6073
+ init_viewer_route();
6231
6074
  init_errors3();
6232
6075
  init_responses();
6233
6076
  api2 = createKennelClient();
6234
6077
  });
6235
6078
 
6236
6079
  // packages/preview/src/hot-viewer.ts
6237
- import { execFileSync as execFileSync2, spawn } from "node:child_process";
6080
+ import { execFileSync, spawn } from "node:child_process";
6238
6081
  import { randomUUID } from "node:crypto";
6239
6082
  import { existsSync } from "node:fs";
6240
6083
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
@@ -6283,13 +6126,13 @@ function startupLock(host, port) {
6283
6126
  return resolve(tmpdir(), `kennel-hot-viewer-${name}.lock`);
6284
6127
  }
6285
6128
  function lanHost() {
6286
- const route = execFileSync2("/sbin/route", ["-n", "get", "default"], {
6129
+ const route = execFileSync("/sbin/route", ["-n", "get", "default"], {
6287
6130
  encoding: "utf8"
6288
6131
  });
6289
6132
  const device = route.match(/interface:\s+(\S+)/)?.[1];
6290
6133
  if (!device)
6291
6134
  throw new Error("kennel could not find the local network interface");
6292
- const host = execFileSync2("/usr/sbin/ipconfig", ["getifaddr", device], {
6135
+ const host = execFileSync("/usr/sbin/ipconfig", ["getifaddr", device], {
6293
6136
  encoding: "utf8"
6294
6137
  }).trim();
6295
6138
  if (!host)
@@ -6350,18 +6193,9 @@ async function hotViewer(options) {
6350
6193
  return;
6351
6194
  }
6352
6195
  const reuse = async () => {
6353
- const terminal = previewTerminal(viewerUrl, options, async () => {
6354
- await Promise.resolve().then(() => init_share());
6355
- return startPreviewShare(options);
6356
- });
6196
+ console.log(viewerUrl);
6357
6197
  if (options.open)
6358
6198
  openPreview(viewerUrl);
6359
- try {
6360
- if (terminal.interactive)
6361
- await terminal.closed;
6362
- } finally {
6363
- terminal.close();
6364
- }
6365
6199
  };
6366
6200
  const status = await viewerStatus(url);
6367
6201
  if (status === "running") {
@@ -6409,11 +6243,9 @@ async function hotViewer(options) {
6409
6243
  },
6410
6244
  stdio: ["ignore", "inherit", "inherit"]
6411
6245
  });
6412
- const terminal = previewTerminal(viewerUrl, options, async () => {
6413
- await Promise.resolve().then(() => init_share());
6414
- return startPreviewShare(options);
6415
- });
6416
- terminal.closed.then(() => {
6246
+ const lifetime = viewerLifetime();
6247
+ console.log(viewerUrl);
6248
+ lifetime.closed.then(() => {
6417
6249
  if (child.exitCode === null && child.signalCode === null)
6418
6250
  child.kill("SIGTERM");
6419
6251
  });
@@ -6424,7 +6256,7 @@ async function hotViewer(options) {
6424
6256
  done();
6425
6257
  return;
6426
6258
  }
6427
- terminal.close();
6259
+ lifetime.close();
6428
6260
  if (await viewerRunning(url, 20, true)) {
6429
6261
  await reuse();
6430
6262
  done();
@@ -6433,12 +6265,12 @@ async function hotViewer(options) {
6433
6265
  fail(new Error(`Kennel hot viewer stopped with exit code ${code ?? "unknown"}`));
6434
6266
  });
6435
6267
  }).finally(async () => {
6436
- terminal.close();
6268
+ lifetime.close();
6437
6269
  await releaseLock(lock, token);
6438
6270
  });
6439
6271
  }
6440
6272
  var init_hot_viewer = __esm(() => {
6441
- init_terminal();
6273
+ init_runtime();
6442
6274
  init_src();
6443
6275
  });
6444
6276
 
@@ -28324,7 +28156,7 @@ var init_server = __esm(() => {
28324
28156
  // packages/schemas/src/data/workspace.ts
28325
28157
  import { existsSync as existsSync3, readFileSync, realpathSync, statSync as statSync2 } from "node:fs";
28326
28158
  import {
28327
- basename as basename2,
28159
+ basename,
28328
28160
  dirname as dirname3,
28329
28161
  isAbsolute,
28330
28162
  relative as relative3,
@@ -28385,7 +28217,7 @@ function folderWorkspace(input, { allowMissingSchemas = false } = {}) {
28385
28217
  for (let current = folder;inside(root, current); current = dirname3(current)) {
28386
28218
  breadcrumbs.unshift({
28387
28219
  path: current,
28388
- title: basename2(current),
28220
+ title: basename(current),
28389
28221
  schema: schemaRoots.some((owner) => inside(owner, current))
28390
28222
  });
28391
28223
  if (current === root)
@@ -37934,7 +37766,7 @@ var init_load = __esm(() => {
37934
37766
  // packages/schemas/src/node.ts
37935
37767
  import { readdir as readdir2, readFile as readFile4, realpath as realpath2, stat as stat2 } from "node:fs/promises";
37936
37768
  import {
37937
- basename as basename3,
37769
+ basename as basename2,
37938
37770
  dirname as dirname5,
37939
37771
  isAbsolute as isAbsolute3,
37940
37772
  relative as relative5,
@@ -37967,7 +37799,7 @@ function createCoreFolderIndex() {
37967
37799
  affected.add(scope);
37968
37800
  }
37969
37801
  }
37970
- if (inside3(path, scope) || basename3(path) === "raggle.json" && inside3(dirname5(path), scope) || ["schema.json", "_schema.json"].includes(basename3(path)) && dirname5(path) === scope)
37802
+ if (inside3(path, scope) || basename2(path) === "raggle.json" && inside3(dirname5(path), scope) || ["schema.json", "_schema.json"].includes(basename2(path)) && dirname5(path) === scope)
37971
37803
  affected.add(scope);
37972
37804
  }
37973
37805
  for (const file of records.keys())
@@ -38076,7 +37908,7 @@ function createCoreFolderIndex() {
38076
37908
  path,
38077
37909
  relativePath: localPath,
38078
37910
  modifiedAt: info.mtimeMs,
38079
- title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ?? basename3(path, ".md"),
37911
+ title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ?? basename2(path, ".md"),
38080
37912
  fields: parsed.fields,
38081
37913
  validationErrors,
38082
37914
  icon: await recordIcon(parsed.fields, path, entry.folder, parsed.body)
@@ -38102,7 +37934,7 @@ function createCoreFolderIndex() {
38102
37934
  const data = {
38103
37935
  id,
38104
37936
  ...entry,
38105
- title: text(schema?.title) ?? config.title ?? basename3(entry.folder),
37937
+ title: text(schema?.title) ?? config.title ?? basename2(entry.folder),
38106
37938
  navLabel: config.ui?.navLabel,
38107
37939
  icon: config.icon,
38108
37940
  count: indexedRecords.length,
@@ -38173,7 +38005,7 @@ var init_node3 = __esm(() => {
38173
38005
 
38174
38006
  // packages/schemas/src/server.ts
38175
38007
  import { createHash as createHash3 } from "node:crypto";
38176
- import { basename as basename4, relative as relative6 } from "node:path";
38008
+ import { basename as basename3, relative as relative6 } from "node:path";
38177
38009
  function createSchemaCollectionServer(host, options = {}) {
38178
38010
  const collectionIndex = createCoreFolderIndex();
38179
38011
  const collectionScopes = new Set;
@@ -38223,7 +38055,7 @@ function createSchemaCollectionServer(host, options = {}) {
38223
38055
  };
38224
38056
  const invalidate = (path) => {
38225
38057
  const affected = collectionIndex.invalidate(path);
38226
- if (affected.length || basename4(path) === "raggle.json" && collectionScopes.size) {
38058
+ if (affected.length || basename3(path) === "raggle.json" && collectionScopes.size) {
38227
38059
  clearTimeout(collectionRefresh);
38228
38060
  collectionRefresh = setTimeout(() => {
38229
38061
  host.emit({
@@ -38329,7 +38161,7 @@ import {
38329
38161
  writeFile as writeFile3
38330
38162
  } from "node:fs/promises";
38331
38163
  import { homedir as homedir2 } from "node:os";
38332
- import { basename as basename5, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
38164
+ import { basename as basename4, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
38333
38165
  import { promisify } from "node:util";
38334
38166
  function kennelConfigHome() {
38335
38167
  const xdg = process.env.XDG_CONFIG_HOME?.trim();
@@ -38403,7 +38235,12 @@ async function resolveCheckout(folder) {
38403
38235
  }
38404
38236
  let output;
38405
38237
  try {
38406
- output = await git(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
38238
+ output = await git([
38239
+ "rev-parse",
38240
+ "--path-format=absolute",
38241
+ "--show-toplevel",
38242
+ "--git-common-dir"
38243
+ ], start);
38407
38244
  } catch (error) {
38408
38245
  if (gitExitCode(error) === 128) {
38409
38246
  throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
@@ -38505,7 +38342,11 @@ var init_store = __esm(() => {
38505
38342
  run = promisify(execFile2);
38506
38343
  UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
38507
38344
  markdownExtensions = new Set([".md", ".markdown"]);
38508
- caseInsensitiveHosts = new Set(["github.com", "gitlab.com", "bitbucket.org"]);
38345
+ caseInsensitiveHosts = new Set([
38346
+ "github.com",
38347
+ "gitlab.com",
38348
+ "bitbucket.org"
38349
+ ]);
38509
38350
  RepositoryNotesError = class RepositoryNotesError extends Error {
38510
38351
  code;
38511
38352
  constructor(code, message) {
@@ -38520,7 +38361,7 @@ var init_store = __esm(() => {
38520
38361
  import { readdir as readdir4, readFile as readFile6, realpath as realpath4, stat as stat4 } from "node:fs/promises";
38521
38362
  import { homedir as homedir3 } from "node:os";
38522
38363
  import {
38523
- basename as basename6,
38364
+ basename as basename5,
38524
38365
  dirname as dirname7,
38525
38366
  extname as extname2,
38526
38367
  isAbsolute as isAbsolute4,
@@ -38667,10 +38508,10 @@ async function readEntity(folderPath, rootPath) {
38667
38508
  return {
38668
38509
  content: document2.body,
38669
38510
  path: relative8(folderPath, filePath),
38670
- title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename6(filePath, extname2(filePath))
38511
+ title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
38671
38512
  };
38672
38513
  }));
38673
- const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename6(folderPath);
38514
+ const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
38674
38515
  const relativePath = relative8(rootPath, folderPath) || ".";
38675
38516
  return {
38676
38517
  aliases: stringValues(index.frontmatter.aliases),
@@ -39043,9 +38884,461 @@ var init_folder_icons = __esm(() => {
39043
38884
  ]);
39044
38885
  });
39045
38886
 
38887
+ // packages/knl/src/folder-guard.ts
38888
+ import { existsSync as existsSync4, realpathSync as realpathSync2 } from "node:fs";
38889
+ import { realpath as realpath6 } from "node:fs/promises";
38890
+ import { dirname as dirname9, resolve as resolve11, sep as sep6 } from "node:path";
38891
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
38892
+ async function knlRealRoot(owner) {
38893
+ const expected = resolve11(await realpath6(owner), "knl");
38894
+ const real = await realpath6(resolve11(owner, "knl"));
38895
+ if (real !== expected)
38896
+ throw new Error("knl/ must be a folder, not a link to another place.");
38897
+ return real;
38898
+ }
38899
+ async function knlReadRoots(owner) {
38900
+ const packages = await Promise.all(knlHostPackageFolders.map((folder) => realpath6(folder).catch(() => {
38901
+ return;
38902
+ })));
38903
+ return [
38904
+ await knlRealRoot(owner),
38905
+ ...packages.filter((folder) => Boolean(folder))
38906
+ ];
38907
+ }
38908
+ function isKnlReadable(file, roots) {
38909
+ let real;
38910
+ try {
38911
+ real = realpathSync2(file);
38912
+ } catch {
38913
+ real = resolve11(file);
38914
+ }
38915
+ return roots.some((root) => real === root || real.startsWith(`${root}${sep6}`));
38916
+ }
38917
+ function knlReadError(file) {
38918
+ return `KNL pages can only use files in knl/ and installed packages: ${file}`;
38919
+ }
38920
+ function assertKnlReadable(file, roots) {
38921
+ if (!isKnlReadable(file, roots))
38922
+ throw new Error(knlReadError(file));
38923
+ }
38924
+ var knlHostResolveDir, knlHostPackageFolders;
38925
+ var init_folder_guard = __esm(() => {
38926
+ knlHostResolveDir = dirname9(fileURLToPath2(import.meta.url));
38927
+ knlHostPackageFolders = (() => {
38928
+ const folders = [];
38929
+ let current = knlHostResolveDir;
38930
+ while (true) {
38931
+ const nodeModules = resolve11(current, "node_modules");
38932
+ if (existsSync4(nodeModules))
38933
+ folders.push(nodeModules);
38934
+ const parent = dirname9(current);
38935
+ if (parent === current)
38936
+ return folders;
38937
+ current = parent;
38938
+ }
38939
+ })();
38940
+ });
38941
+
38942
+ // packages/knl/src/tailwind.ts
38943
+ import { readFile as readFile10 } from "node:fs/promises";
38944
+ import { createRequire as createRequire2 } from "node:module";
38945
+ import { dirname as dirname10, resolve as resolve12 } from "node:path";
38946
+ import { compile } from "@tailwindcss/node";
38947
+ import { Scanner } from "@tailwindcss/oxide";
38948
+ function starterKnlStyles() {
38949
+ return `@layer theme, utilities;
38950
+ @import "tailwindcss/theme.css" layer(theme) prefix(knl);
38951
+ @import "tailwindcss/utilities.css" layer(utilities) source(none);
38952
+
38953
+ @theme {
38954
+ --font-sans: system-ui, sans-serif;
38955
+ }
38956
+ `;
38957
+ }
38958
+ async function compileKnlCss(js, stylesheet, roots) {
38959
+ const base = dirname10(stylesheet);
38960
+ assertKnlReadable(stylesheet, roots);
38961
+ const css = await readFile10(stylesheet, "utf8").catch((error) => {
38962
+ if (error.code === "ENOENT")
38963
+ return starterKnlStyles();
38964
+ throw error;
38965
+ });
38966
+ const compiler = await compile(css, {
38967
+ base,
38968
+ from: stylesheet,
38969
+ onDependency: (path) => assertKnlReadable(path, roots),
38970
+ customJsResolver: async (id) => {
38971
+ throw new Error(`KNL does not load Tailwind plugins or configs (${id}).`);
38972
+ },
38973
+ customCssResolver: async (id) => id === "tailwindcss" || id.startsWith("tailwindcss/") ? require2.resolve(id) : undefined
38974
+ });
38975
+ const sources = [...compiler.sources];
38976
+ if (compiler.root !== "none") {
38977
+ sources.push({
38978
+ ...compiler.root ?? { base, pattern: "**/*" },
38979
+ negated: false
38980
+ });
38981
+ }
38982
+ for (const source of sources) {
38983
+ if (source.negated)
38984
+ continue;
38985
+ const fixed = source.pattern.split("/");
38986
+ const glob = fixed.findIndex((part) => /[*?[{]/.test(part));
38987
+ assertKnlReadable(resolve12(source.base, fixed.slice(0, glob < 0 ? undefined : glob).join("/")), roots);
38988
+ }
38989
+ const scanner = new Scanner({ sources });
38990
+ const candidates = [
38991
+ ...scanner.scan(),
38992
+ ...scanner.scanFiles([{ content: js, extension: "js" }])
38993
+ ];
38994
+ return compiler.build(candidates);
38995
+ }
38996
+ var require2;
38997
+ var init_tailwind = __esm(() => {
38998
+ init_folder_guard();
38999
+ require2 = createRequire2(import.meta.url);
39000
+ });
39001
+
39002
+ // packages/knl/src/knl.ts
39003
+ import { readdir as readdir5, stat as stat7 } from "node:fs/promises";
39004
+ import { basename as basename6, dirname as dirname11, relative as relative10, resolve as resolve13, sep as sep7 } from "node:path";
39005
+ import * as esbuild from "esbuild";
39006
+ function sourceFor(file, knlRoot, scanRoot, owner) {
39007
+ const id = toModuleId(file, knlRoot);
39008
+ return {
39009
+ id,
39010
+ file,
39011
+ relative: relative10(scanRoot, file).split(sep7).join("/"),
39012
+ folder: dirname11(file),
39013
+ owner
39014
+ };
39015
+ }
39016
+ function toModuleId(file, knlRoot) {
39017
+ const relativeKnl = relative10(knlRoot, file).split(sep7).join("/");
39018
+ return relativeKnl.replace(/\/?index\.(tsx|ts)$/, "") || "index";
39019
+ }
39020
+ async function collectKnlSources(scanRoot, owner) {
39021
+ const knlRoot = resolve13(scanRoot, KNK_FOLDER);
39022
+ const knlEntries = await readdir5(knlRoot, { withFileTypes: true }).catch(() => []);
39023
+ const sources = [];
39024
+ const addModule = async (folder) => {
39025
+ for (const candidate of MODULE_FILES) {
39026
+ const file = resolve13(folder, candidate);
39027
+ if (await stat7(file).then((d) => d.isFile()).catch(() => false)) {
39028
+ sources.push(sourceFor(file, knlRoot, scanRoot, owner));
39029
+ return;
39030
+ }
39031
+ }
39032
+ };
39033
+ await addModule(knlRoot);
39034
+ for (const entry of knlEntries) {
39035
+ if (!entry.isDirectory())
39036
+ continue;
39037
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
39038
+ continue;
39039
+ await addModule(resolve13(knlRoot, entry.name));
39040
+ }
39041
+ return sources.sort((left, right) => {
39042
+ const rank = (left.id === "index" ? 0 : 1) - (right.id === "index" ? 0 : 1);
39043
+ return rank !== 0 ? rank : left.id.localeCompare(right.id);
39044
+ });
39045
+ }
39046
+ async function loadKnlSources(viewRoot, linkedFolders = []) {
39047
+ const sources = [
39048
+ ...await collectKnlSources(viewRoot, viewRoot),
39049
+ ...(await Promise.all(linkedFolders.map(async (folder) => (await collectKnlSources(folder, folder)).map((source) => ({
39050
+ ...source,
39051
+ id: `${basename6(folder)}:${source.id}`,
39052
+ relative: `${basename6(folder)}/${source.relative}`
39053
+ }))))).flat()
39054
+ ];
39055
+ return { sources };
39056
+ }
39057
+ function knlNodePaths(knlRoot) {
39058
+ return [resolve13(knlRoot, "node_modules"), ...knlHostPackageFolders];
39059
+ }
39060
+ async function compileKnlSource(source) {
39061
+ const sourceBytes = (await stat7(source.file)).size;
39062
+ const tooLarge = (bytes, what) => ({
39063
+ id: source.id,
39064
+ relative: source.relative,
39065
+ js: "",
39066
+ css: "",
39067
+ issues: [
39068
+ {
39069
+ path: source.relative,
39070
+ message: `Module ${what} is too large (${bytes} bytes; limit ${MAX_SOURCE_BYTES}).`
39071
+ }
39072
+ ]
39073
+ });
39074
+ if (sourceBytes > MAX_SOURCE_BYTES)
39075
+ return tooLarge(sourceBytes, "source");
39076
+ const cached = compileCache.get(source.file);
39077
+ const unchanged = cached && (await Promise.all(cached.dependencies.map(async ({ file, mtimeMs }) => (await stat7(file).catch(() => {
39078
+ return;
39079
+ }))?.mtimeMs === mtimeMs))).every(Boolean);
39080
+ const knlRoot = resolve13(source.owner, KNK_FOLDER);
39081
+ let issuePath = source.relative;
39082
+ try {
39083
+ const nodePaths = knlNodePaths(knlRoot);
39084
+ const roots = await knlReadRoots(source.owner);
39085
+ assertKnlReadable(source.file, roots);
39086
+ let js;
39087
+ if (cached && unchanged) {
39088
+ js = cached.js;
39089
+ } else {
39090
+ const result = await esbuild.build({
39091
+ absWorkingDir: source.folder,
39092
+ stdin: {
39093
+ contents: `import { createElement } from "react"
39094
+ import { createRoot } from "react-dom/client"
39095
+ import Component from ${JSON.stringify(source.file)}
39096
+
39097
+ globalThis.__knlMount = (container, context, onError) => {
39098
+ const root = createRoot(container, { onUncaughtError: onError })
39099
+ root.render(createElement(Component, context))
39100
+ }
39101
+ `,
39102
+ sourcefile: `${source.file}.mount.ts`,
39103
+ loader: "ts",
39104
+ resolveDir: source.folder
39105
+ },
39106
+ bundle: true,
39107
+ format: "iife",
39108
+ target: "es2022",
39109
+ jsx: "automatic",
39110
+ platform: "browser",
39111
+ write: false,
39112
+ minify: false,
39113
+ sourcemap: false,
39114
+ logLevel: "silent",
39115
+ metafile: true,
39116
+ nodePaths,
39117
+ plugins: [readGuard(roots)]
39118
+ });
39119
+ js = result.outputFiles?.[0]?.text ?? "";
39120
+ const files = new Set([
39121
+ resolve13(knlRoot, "package.json"),
39122
+ ...Object.keys(result.metafile.inputs).map((file) => resolve13(source.folder, file)).filter((file) => file !== `${source.file}.mount.ts`)
39123
+ ]);
39124
+ const dependencies = await Promise.all([...files].map(async (file) => ({
39125
+ file,
39126
+ mtimeMs: (await stat7(file).catch(() => {
39127
+ return;
39128
+ }))?.mtimeMs
39129
+ })));
39130
+ compileCache.delete(source.file);
39131
+ compileCache.set(source.file, { js, dependencies });
39132
+ if (compileCache.size > MAX_CACHED_MODULES)
39133
+ compileCache.delete(compileCache.keys().next().value);
39134
+ }
39135
+ const stylesheet = resolve13(knlRoot, "style.css");
39136
+ issuePath = relative10(source.owner, stylesheet).split(sep7).join("/");
39137
+ const css = await compileKnlCss(js, stylesheet, roots);
39138
+ issuePath = source.relative;
39139
+ if (js.length + css.length > MAX_JS_BYTES) {
39140
+ return {
39141
+ id: source.id,
39142
+ relative: source.relative,
39143
+ js: "",
39144
+ css: "",
39145
+ issues: [
39146
+ {
39147
+ path: source.relative,
39148
+ message: `Compiled module is too large (${js.length + css.length} bytes; limit ${MAX_JS_BYTES}).`
39149
+ }
39150
+ ]
39151
+ };
39152
+ }
39153
+ return { id: source.id, relative: source.relative, js, css, issues: [] };
39154
+ } catch (error) {
39155
+ return {
39156
+ id: source.id,
39157
+ relative: source.relative,
39158
+ js: "",
39159
+ css: "",
39160
+ issues: knlBuildIssues(error, issuePath, source)
39161
+ };
39162
+ }
39163
+ }
39164
+ function readGuard(roots) {
39165
+ return {
39166
+ name: "knl-read-guard",
39167
+ setup(build) {
39168
+ build.onResolve({ filter: /.*/ }, async (args) => {
39169
+ if (args.pluginData === guardResolve)
39170
+ return;
39171
+ const resolveFrom = (resolveDir) => build.resolve(args.path, {
39172
+ importer: args.importer,
39173
+ kind: args.kind,
39174
+ pluginData: guardResolve,
39175
+ resolveDir
39176
+ });
39177
+ const readable = (result) => result.namespace !== "file" || !result.path || isKnlReadable(result.path, roots);
39178
+ let result = await resolveFrom(args.resolveDir);
39179
+ const bare = !/^(?:\.{1,2}(?:\/|$)|\/)/.test(args.path);
39180
+ if (bare && result.errors.length === 0 && !readable(result))
39181
+ result = await resolveFrom(knlHostResolveDir);
39182
+ if (result.errors.length > 0)
39183
+ return { errors: result.errors };
39184
+ if (!readable(result))
39185
+ return { errors: [{ text: knlReadError(args.path) }] };
39186
+ return result;
39187
+ });
39188
+ }
39189
+ };
39190
+ }
39191
+ function knlBuildIssues(error, relativePath, source) {
39192
+ const failures = error?.errors;
39193
+ if (Array.isArray(failures) && failures.length > 0) {
39194
+ const first = failures[0];
39195
+ const file = first?.location?.file ? relative10(source.owner, resolve13(source.folder, first.location.file)).split(sep7).join("/") : relativePath;
39196
+ const location = first?.location ? `${file}:${first.location.line ?? "?"}` : relativePath;
39197
+ return [
39198
+ { path: location, message: first?.text ?? "Module failed to compile." }
39199
+ ];
39200
+ }
39201
+ return [
39202
+ {
39203
+ path: relativePath,
39204
+ message: error instanceof Error && error.message ? error.message : "Module failed to compile."
39205
+ }
39206
+ ];
39207
+ }
39208
+ function starterKnlSource() {
39209
+ return `// knl/index.tsx — this folder's homepage (KNL folder web framework).
39210
+ // Every other page lives in its own folder: knl/<page>/index.tsx.
39211
+ import * as React from "react"
39212
+
39213
+ export default function Home() {
39214
+ return (
39215
+ <div className="knl:p-4 knl:font-sans">
39216
+ <h1 className="knl:m-0 knl:text-2xl">Home</h1>
39217
+ <p>Edit this file to build the homepage.</p>
39218
+ </div>
39219
+ )
39220
+ }
39221
+ `;
39222
+ }
39223
+ var KNK_FOLDER = "knl", MAX_SOURCE_BYTES, MAX_JS_BYTES, MODULE_FILES, MAX_CACHED_MODULES = 200, compileCache, guardResolve;
39224
+ var init_knl = __esm(() => {
39225
+ init_folder_guard();
39226
+ init_tailwind();
39227
+ MAX_SOURCE_BYTES = 512 * 1024;
39228
+ MAX_JS_BYTES = 5 * 1024 * 1024;
39229
+ MODULE_FILES = ["index.tsx", "index.ts"];
39230
+ compileCache = new Map;
39231
+ guardResolve = Symbol("knl-guard");
39232
+ });
39233
+
39234
+ // packages/knl/src/manifest.ts
39235
+ var knlManifest;
39236
+ var init_manifest = __esm(() => {
39237
+ knlManifest = {
39238
+ id: "knl",
39239
+ label: "KNL"
39240
+ };
39241
+ });
39242
+
39243
+ // packages/knl/src/server.ts
39244
+ import { stat as stat8 } from "node:fs/promises";
39245
+ import { join as join5 } from "node:path";
39246
+ function createKnlPlugin() {
39247
+ return {
39248
+ ...knlManifest,
39249
+ async detect(context) {
39250
+ const hasKnlFolder = await stat8(join5(context.folder, KNK_FOLDER)).then((entry) => entry.isDirectory()).catch(() => false);
39251
+ if (!hasKnlFolder)
39252
+ return { status: "unavailable" };
39253
+ const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
39254
+ if (!sources.some((source) => source.id === "index"))
39255
+ return { status: "unavailable" };
39256
+ return {
39257
+ count: sources.length,
39258
+ pages: sources.filter((source) => source.id !== "index" && !source.id.endsWith(":index")).map((source) => ({
39259
+ id: source.id,
39260
+ label: source.id.replaceAll(/[-_]/g, " ").replace(/^./, (letter) => letter.toUpperCase())
39261
+ })),
39262
+ status: "available"
39263
+ };
39264
+ },
39265
+ async handle(context, subpath) {
39266
+ const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
39267
+ const folder = context.folder;
39268
+ if (sources.length === 0) {
39269
+ return { kind: "available", data: { folder, status: "unavailable" } };
39270
+ }
39271
+ const requested = subpath.trim() || "index";
39272
+ const selected = sources.filter((source) => source.id === requested);
39273
+ if (selected.length === 0) {
39274
+ return {
39275
+ kind: "available",
39276
+ data: {
39277
+ folder,
39278
+ status: "error",
39279
+ message: `KNL page not found: ${requested}`
39280
+ }
39281
+ };
39282
+ }
39283
+ const compiled = await Promise.all(selected.map(compileKnlSource));
39284
+ const entries = compiled.map((result) => {
39285
+ const source = sources.find((item) => item.id === result.id);
39286
+ return {
39287
+ id: result.id,
39288
+ relative: result.relative,
39289
+ ...result.js ? { js: result.js } : {},
39290
+ css: result.css,
39291
+ ...result.issues.length > 0 ? { issues: result.issues } : {},
39292
+ owner: source?.owner ?? folder
39293
+ };
39294
+ });
39295
+ const failing = entries.filter((entry) => entry.issues?.length);
39296
+ if (failing.length > 0) {
39297
+ return {
39298
+ kind: "available",
39299
+ data: {
39300
+ folder,
39301
+ status: "error",
39302
+ entries,
39303
+ issues: failing[0]?.issues
39304
+ }
39305
+ };
39306
+ }
39307
+ return {
39308
+ kind: "available",
39309
+ data: {
39310
+ folder,
39311
+ status: "available",
39312
+ entries
39313
+ }
39314
+ };
39315
+ },
39316
+ async init() {
39317
+ return {
39318
+ files: [
39319
+ { path: "knl/index.tsx", content: starterKnlSource() },
39320
+ { path: "knl/style.css", content: starterKnlStyles() },
39321
+ {
39322
+ path: "knl/package.json",
39323
+ content: `${JSON.stringify({ private: true, type: "module" }, null, 2)}
39324
+ `
39325
+ },
39326
+ { path: "knl/.gitignore", content: `node_modules/
39327
+ ` }
39328
+ ]
39329
+ };
39330
+ }
39331
+ };
39332
+ }
39333
+ var init_server3 = __esm(() => {
39334
+ init_knl();
39335
+ init_manifest();
39336
+ init_tailwind();
39337
+ });
39338
+
39046
39339
  // packages/notes/src/manifest.ts
39047
39340
  var notesManifest;
39048
- var init_manifest = __esm(() => {
39341
+ var init_manifest2 = __esm(() => {
39049
39342
  notesManifest = {
39050
39343
  id: "notes",
39051
39344
  icon: {
@@ -39060,23 +39353,23 @@ var init_manifest = __esm(() => {
39060
39353
  });
39061
39354
 
39062
39355
  // packages/notes/src/server.ts
39063
- import { readdir as readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
39356
+ import { readdir as readdir6, realpath as realpath7, stat as stat9 } from "node:fs/promises";
39064
39357
  import {
39065
39358
  basename as basename7,
39066
39359
  extname as extname5,
39067
39360
  isAbsolute as isAbsolute6,
39068
- relative as relative10,
39069
- resolve as resolve11,
39070
- sep as sep6
39361
+ relative as relative11,
39362
+ resolve as resolve14,
39363
+ sep as sep8
39071
39364
  } from "node:path";
39072
39365
  async function listMarkdownFiles(folder) {
39073
39366
  const files = [];
39074
39367
  const visit = async (current) => {
39075
- const entries = await readdir5(current, { withFileTypes: true }).catch(() => []);
39368
+ const entries = await readdir6(current, { withFileTypes: true }).catch(() => []);
39076
39369
  for (const entry of entries) {
39077
39370
  if (entry.name === "node_modules" || entry.name.startsWith("."))
39078
39371
  continue;
39079
- const path = resolve11(current, entry.name);
39372
+ const path = resolve14(current, entry.name);
39080
39373
  if (entry.isDirectory()) {
39081
39374
  await visit(path);
39082
39375
  continue;
@@ -39084,13 +39377,13 @@ async function listMarkdownFiles(folder) {
39084
39377
  if (!entry.isFile() || !markdownExtensions2.has(extname5(entry.name))) {
39085
39378
  continue;
39086
39379
  }
39087
- const details = await stat7(path).catch(() => {
39380
+ const details = await stat9(path).catch(() => {
39088
39381
  return;
39089
39382
  });
39090
39383
  files.push({
39091
39384
  activityAt: details?.mtimeMs ?? 0,
39092
39385
  name: basename7(entry.name, extname5(entry.name)),
39093
- relativePath: relative10(folder, path).split(sep6).join("/")
39386
+ relativePath: relative11(folder, path).split(sep8).join("/")
39094
39387
  });
39095
39388
  }
39096
39389
  };
@@ -39098,7 +39391,7 @@ async function listMarkdownFiles(folder) {
39098
39391
  return files.sort((left, right) => right.activityAt - left.activityAt);
39099
39392
  }
39100
39393
  function inside5(path, root) {
39101
- const local = relative10(root, path);
39394
+ const local = relative11(root, path);
39102
39395
  return local === "" || !local.startsWith("..") && !isAbsolute6(local);
39103
39396
  }
39104
39397
  async function withoutManaged(groups, managedRoots) {
@@ -39110,7 +39403,7 @@ async function withoutManaged(groups, managedRoots) {
39110
39403
  return { ...group, files: [] };
39111
39404
  }
39112
39405
  const files = await Promise.all(group.files.map(async (file) => {
39113
- const real = await canonical(resolve11(group.folder, file.relativePath));
39406
+ const real = await canonical(resolve14(group.folder, file.relativePath));
39114
39407
  return roots.some((root) => inside5(real, root)) ? undefined : file;
39115
39408
  }));
39116
39409
  return { ...group, files: files.filter((file) => file !== undefined) };
@@ -39160,7 +39453,7 @@ async function notesLlmSections({
39160
39453
  continue;
39161
39454
  seen.add(identity);
39162
39455
  const files = await listMarkdownFiles(source);
39163
- const paths = files.map((file) => resolve11(source, file.relativePath));
39456
+ const paths = files.map((file) => resolve14(source, file.relativePath));
39164
39457
  groups.push({ folder: source, paths });
39165
39458
  files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
39166
39459
  }
@@ -39204,17 +39497,17 @@ function createNotesPlugin() {
39204
39497
  llm: notesLlmSections
39205
39498
  };
39206
39499
  }
39207
- var markdownExtensions2, llmNoteLimit = 20, canonical = (path) => realpath6(path).catch(() => path);
39208
- var init_server3 = __esm(() => {
39209
- init_manifest();
39500
+ var markdownExtensions2, llmNoteLimit = 20, canonical = (path) => realpath7(path).catch(() => path);
39501
+ var init_server4 = __esm(() => {
39502
+ init_manifest2();
39210
39503
  markdownExtensions2 = new Set([".md", ".markdown"]);
39211
39504
  });
39212
39505
 
39213
39506
  // packages/api/src/server/plugins.ts
39214
39507
  import { pathToFileURL } from "node:url";
39215
- import { existsSync as existsSync4 } from "node:fs";
39216
- import { mkdir as mkdir4, readFile as readFile10, stat as stat8, writeFile as writeFile6 } from "node:fs/promises";
39217
- import { dirname as dirname9, resolve as resolve12, sep as sep7 } from "node:path";
39508
+ import { existsSync as existsSync5 } from "node:fs";
39509
+ import { mkdir as mkdir4, readFile as readFile11, stat as stat10, writeFile as writeFile6 } from "node:fs/promises";
39510
+ import { dirname as dirname12, resolve as resolve15, sep as sep9 } from "node:path";
39218
39511
  function configuredFolderServerRegistry(registry, resolvedConfig) {
39219
39512
  const disabled = new Set(resolvedConfig.plugins.disable);
39220
39513
  const orderedIds = resolvedConfig.plugins.order.filter((id) => registry.has(id) && !disabled.has(id));
@@ -39238,14 +39531,14 @@ function parsePluginRegistryConfig(content) {
39238
39531
  };
39239
39532
  }
39240
39533
  async function nearestKennelConfig(folder) {
39241
- for (let current = folder;; current = dirname9(current)) {
39242
- const candidate = resolve12(current, "kennel.json");
39243
- const details = await stat8(candidate).catch(() => {
39534
+ for (let current = folder;; current = dirname12(current)) {
39535
+ const candidate = resolve15(current, "kennel.json");
39536
+ const details = await stat10(candidate).catch(() => {
39244
39537
  return;
39245
39538
  });
39246
39539
  if (details?.isFile())
39247
39540
  return candidate;
39248
- if (dirname9(current) === current)
39541
+ if (dirname12(current) === current)
39249
39542
  return;
39250
39543
  }
39251
39544
  }
@@ -39254,13 +39547,13 @@ async function folderPluginRegistryConfig(folder) {
39254
39547
  if (!path)
39255
39548
  return defaultPluginRegistryConfig;
39256
39549
  try {
39257
- return parsePluginRegistryConfig(await readFile10(path, "utf8"));
39550
+ return parsePluginRegistryConfig(await readFile11(path, "utf8"));
39258
39551
  } catch {
39259
39552
  return defaultPluginRegistryConfig;
39260
39553
  }
39261
39554
  }
39262
39555
  function createFolderServerPlugins() {
39263
- return [createNotesPlugin()];
39556
+ return [createNotesPlugin(), createKnlPlugin()];
39264
39557
  }
39265
39558
  function folderServerRegistry() {
39266
39559
  return new Map(createFolderServerPlugins().map((plugin) => [plugin.id, plugin]));
@@ -39302,7 +39595,7 @@ async function folderServerRegistryAsync() {
39302
39595
  ...server,
39303
39596
  async detect(context) {
39304
39597
  const detection = await server.detect(context);
39305
- if (detection.status === "unavailable" && existsSync4(resolve12(context.folder, entry.trigger))) {
39598
+ if (detection.status === "unavailable" && existsSync5(resolve15(context.folder, entry.trigger))) {
39306
39599
  return { status: "available" };
39307
39600
  }
39308
39601
  return detection;
@@ -39342,6 +39635,7 @@ async function externalApiHandlers() {
39342
39635
  var defaultPluginRegistryConfig;
39343
39636
  var init_plugins = __esm(() => {
39344
39637
  init_server3();
39638
+ init_server4();
39345
39639
  defaultPluginRegistryConfig = {
39346
39640
  plugins: { disable: [], order: [] }
39347
39641
  };
@@ -39349,29 +39643,29 @@ var init_plugins = __esm(() => {
39349
39643
 
39350
39644
  // packages/api/src/server/linked-folder-discovery.ts
39351
39645
  import { execFile as execFile4 } from "node:child_process";
39352
- import { existsSync as existsSync5 } from "node:fs";
39353
- import { createRequire as createRequire2 } from "node:module";
39354
- import { dirname as dirname10, join as join5 } from "node:path";
39355
- import { fileURLToPath as fileURLToPath2 } from "node:url";
39646
+ import { existsSync as existsSync6 } from "node:fs";
39647
+ import { createRequire as createRequire3 } from "node:module";
39648
+ import { dirname as dirname13, join as join6 } from "node:path";
39649
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
39356
39650
  function resolveLocalModule(from = import.meta.url) {
39357
- const require2 = createRequire2(from);
39651
+ const require2 = createRequire3(from);
39358
39652
  const candidates = [];
39359
39653
  try {
39360
39654
  candidates.push(require2.resolve("@raggle-ai/local"));
39361
39655
  } catch {}
39362
- let directory = dirname10(fileURLToPath2(from));
39363
- while (dirname10(directory) !== directory) {
39364
- if (existsSync5(join5(directory, "raggle.json"))) {
39656
+ let directory = dirname13(fileURLToPath3(from));
39657
+ while (dirname13(directory) !== directory) {
39658
+ if (existsSync6(join6(directory, "raggle.json"))) {
39365
39659
  for (const candidate of [
39366
- join5(dirname10(directory), "raggle-ai-scout", "packages", "local"),
39367
- join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
39660
+ join6(dirname13(directory), "raggle-ai-scout", "packages", "local"),
39661
+ join6(directory, "..", "..", "raggle-ai-scout", "packages", "local")
39368
39662
  ]) {
39369
39663
  if (!candidates.includes(candidate))
39370
39664
  candidates.push(candidate);
39371
39665
  }
39372
39666
  break;
39373
39667
  }
39374
- directory = dirname10(directory);
39668
+ directory = dirname13(directory);
39375
39669
  }
39376
39670
  for (const candidate of candidates) {
39377
39671
  try {
@@ -39380,7 +39674,7 @@ function resolveLocalModule(from = import.meta.url) {
39380
39674
  return candidate;
39381
39675
  } catch {}
39382
39676
  }
39383
- return candidates[0] ?? join5(dirname10(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
39677
+ return candidates[0] ?? join6(dirname13(fileURLToPath3(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
39384
39678
  }
39385
39679
  function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
39386
39680
  return new Promise((resolve, reject) => {
@@ -39569,25 +39863,25 @@ var init_project_discovery = __esm(() => {
39569
39863
 
39570
39864
  // packages/api/src/server/folder-metadata-plugin.ts
39571
39865
  import { createHash as createHash5 } from "node:crypto";
39572
- import { realpathSync as realpathSync2 } from "node:fs";
39573
- import { readdir as readdir6, readFile as readFile11, realpath as realpath7, stat as stat9 } from "node:fs/promises";
39866
+ import { realpathSync as realpathSync3 } from "node:fs";
39867
+ import { readdir as readdir7, readFile as readFile12, realpath as realpath8, stat as stat11 } from "node:fs/promises";
39574
39868
  import { homedir as homedir4 } from "node:os";
39575
39869
  import {
39576
39870
  basename as basename8,
39577
- dirname as dirname11,
39871
+ dirname as dirname14,
39578
39872
  extname as extname6,
39579
39873
  isAbsolute as isAbsolute7,
39580
- relative as relative12,
39581
- resolve as resolve13,
39582
- sep as sep8
39874
+ relative as relative13,
39875
+ resolve as resolve16,
39876
+ sep as sep10
39583
39877
  } from "node:path";
39584
39878
  import { execFile as execFile6 } from "node:child_process";
39585
39879
  import local from "@raggle-ai/local";
39586
39880
  function metadataFile(path, root) {
39587
- const candidate = relative12(root, path);
39881
+ const candidate = relative13(root, path);
39588
39882
  if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
39589
39883
  return false;
39590
- const parts = candidate.split(sep8);
39884
+ const parts = candidate.split(sep10);
39591
39885
  if (parts.some((part) => part === "node_modules"))
39592
39886
  return false;
39593
39887
  return metadataExtensions.has(extname6(candidate).toLowerCase());
@@ -39598,10 +39892,10 @@ async function configuredAiInputModule(folder) {
39598
39892
  return typeof module === "string" && module.trim() ? module.trim() : null;
39599
39893
  }
39600
39894
  function folderPath(value, root, lockedRoot) {
39601
- const folder = realpathSync2(resolve13(value ?? root));
39895
+ const folder = realpathSync3(resolve16(value ?? root));
39602
39896
  if (!lockedRoot)
39603
39897
  return folder;
39604
- const path = relative12(lockedRoot, folder);
39898
+ const path = relative13(lockedRoot, folder);
39605
39899
  if (!path || !path.startsWith("..") && !isAbsolute7(path))
39606
39900
  return folder;
39607
39901
  throw new Error(`Folder is outside the viewer root: ${folder}`);
@@ -39666,15 +39960,15 @@ function unique(values) {
39666
39960
  return [...new Set(values)];
39667
39961
  }
39668
39962
  function isMachineRoot(folder) {
39669
- return folder === homedir4() || dirname11(folder) === folder;
39963
+ return folder === homedir4() || dirname14(folder) === folder;
39670
39964
  }
39671
39965
  async function shallowMetadata(folder) {
39672
- const entries = await readdir6(folder, { withFileTypes: true });
39966
+ const entries = await readdir7(folder, { withFileTypes: true });
39673
39967
  const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
39674
39968
  const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname6(entry.name).toLowerCase())).map(async (entry) => {
39675
- const path = resolve13(folder, entry.name);
39676
- const details = await stat9(path);
39677
- const content = await readFile11(path, "utf8").catch(() => "");
39969
+ const path = resolve16(folder, entry.name);
39970
+ const details = await stat11(path);
39971
+ const content = await readFile12(path, "utf8").catch(() => "");
39678
39972
  const title = markdownMetadata(content).title;
39679
39973
  return {
39680
39974
  activityAt: details.mtimeMs,
@@ -39687,10 +39981,10 @@ async function shallowMetadata(folder) {
39687
39981
  };
39688
39982
  }));
39689
39983
  const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
39690
- id: createHash5("sha1").update(resolve13(folder, entry.name)).digest("hex"),
39984
+ id: createHash5("sha1").update(resolve16(folder, entry.name)).digest("hex"),
39691
39985
  name: entry.name,
39692
39986
  relativePath: entry.name,
39693
- worktree: resolve13(folder, entry.name)
39987
+ worktree: resolve16(folder, entry.name)
39694
39988
  })).sort((left, right) => left.name.localeCompare(right.name));
39695
39989
  return {
39696
39990
  changes: { items: [] },
@@ -39717,7 +40011,7 @@ async function loadFolderData(folder) {
39717
40011
  id: project.id,
39718
40012
  worktree: project.worktree,
39719
40013
  name: project.name ?? basename8(project.worktree),
39720
- relativePath: relative12(folder, project.worktree)
40014
+ relativePath: relative13(folder, project.worktree)
39721
40015
  }))
39722
40016
  }
39723
40017
  };
@@ -39725,10 +40019,10 @@ async function loadFolderData(folder) {
39725
40019
  async function notesFiles(folder, files, origin) {
39726
40020
  return {
39727
40021
  items: await Promise.all(files.map(async (file) => {
39728
- const path = file.path ?? (file.relativePath ? resolve13(folder, file.relativePath) : undefined);
40022
+ const path = file.path ?? (file.relativePath ? resolve16(folder, file.relativePath) : undefined);
39729
40023
  if (!path)
39730
40024
  return origin ? { ...file, folder: origin } : file;
39731
- const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile11(path, "utf8").catch(() => "") : "";
40025
+ const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile12(path, "utf8").catch(() => "") : "";
39732
40026
  const title = content ? markdownMetadata(content).title : undefined;
39733
40027
  return {
39734
40028
  ...file,
@@ -39742,7 +40036,7 @@ async function notesFiles(folder, files, origin) {
39742
40036
  async function managedNotesFiles(directory) {
39743
40037
  const notes = await listManagedNotes(directory);
39744
40038
  return Promise.all(notes.map(async (note) => {
39745
- const content = await readFile11(note.path, "utf8").catch(() => "");
40039
+ const content = await readFile12(note.path, "utf8").catch(() => "");
39746
40040
  const title = content ? markdownMetadata(content).title : undefined;
39747
40041
  return {
39748
40042
  ...note,
@@ -39753,7 +40047,7 @@ async function managedNotesFiles(directory) {
39753
40047
  }));
39754
40048
  }
39755
40049
  function insideFolder(path, root) {
39756
- const local = relative12(root, path);
40050
+ const local = relative13(root, path);
39757
40051
  return local === "" || !local.startsWith("..") && !isAbsolute7(local);
39758
40052
  }
39759
40053
  async function withoutManagedFiles(items, managedRoot) {
@@ -39785,15 +40079,15 @@ async function localProjectSearchCache(cache) {
39785
40079
  function mediaTarget(folder, file, lockedTarget) {
39786
40080
  if (!file)
39787
40081
  return;
39788
- const rootReal = realpathSync2(resolve13(folder));
39789
- const path = resolve13(folder, file);
40082
+ const rootReal = realpathSync3(resolve16(folder));
40083
+ const path = resolve16(folder, file);
39790
40084
  let pathReal;
39791
40085
  try {
39792
- pathReal = realpathSync2(path);
40086
+ pathReal = realpathSync3(path);
39793
40087
  } catch {
39794
40088
  return;
39795
40089
  }
39796
- const rel = relative12(rootReal, pathReal);
40090
+ const rel = relative13(rootReal, pathReal);
39797
40091
  if (rel.startsWith("..") || isAbsolute7(rel))
39798
40092
  return;
39799
40093
  if (lockedTarget && pathReal !== lockedTarget)
@@ -39833,15 +40127,15 @@ function documentError(response) {
39833
40127
  function documentTarget(folder, file, lockedTarget) {
39834
40128
  if (!file)
39835
40129
  return;
39836
- const rootReal = realpathSync2(resolve13(folder));
39837
- const path = resolve13(folder, file);
40130
+ const rootReal = realpathSync3(resolve16(folder));
40131
+ const path = resolve16(folder, file);
39838
40132
  let pathReal;
39839
40133
  try {
39840
- pathReal = realpathSync2(path);
40134
+ pathReal = realpathSync3(path);
39841
40135
  } catch {
39842
40136
  return;
39843
40137
  }
39844
- const rel = relative12(rootReal, pathReal);
40138
+ const rel = relative13(rootReal, pathReal);
39845
40139
  if (rel.startsWith("..") || isAbsolute7(rel))
39846
40140
  return;
39847
40141
  if (lockedTarget && pathReal !== lockedTarget)
@@ -39899,7 +40193,8 @@ async function pluginAvailability(registry, folder, linkedFolders, configWarning
39899
40193
  error,
39900
40194
  icon: plugin.icon,
39901
40195
  id: plugin.id,
39902
- label: plugin.label
40196
+ label: plugin.label,
40197
+ ...detection.status === "available" && detection.pages ? { pages: detection.pages } : {}
39903
40198
  });
39904
40199
  }
39905
40200
  } catch {}
@@ -39914,8 +40209,8 @@ function createFolderApi({
39914
40209
  remoteAccess = false,
39915
40210
  lockedFile
39916
40211
  }) {
39917
- let lockedRoot = restrictToRoot ? realpathSync2(root) : undefined;
39918
- const lockedTarget = lockedFile ? realpathSync2(lockedFile) : undefined;
40212
+ let lockedRoot = restrictToRoot ? realpathSync3(root) : undefined;
40213
+ const lockedTarget = lockedFile ? realpathSync3(lockedFile) : undefined;
39919
40214
  let basePlugins;
39920
40215
  let baseExternalApiHandlers;
39921
40216
  const assetFiles = new Map;
@@ -39953,7 +40248,7 @@ function createFolderApi({
39953
40248
  const serverHost = server.host;
39954
40249
  const remote = remoteAccess || serverHost === true || typeof serverHost === "string" && !isLoopbackServerHost(serverHost);
39955
40250
  if (remote)
39956
- lockedRoot = lockedRoot ?? realpathSync2(root);
40251
+ lockedRoot = lockedRoot ?? realpathSync3(root);
39957
40252
  const collectionServer = createSchemaCollectionServer(server, {
39958
40253
  boundary: lockedRoot,
39959
40254
  lockedTarget
@@ -39979,7 +40274,7 @@ function createFolderApi({
39979
40274
  if (!folder)
39980
40275
  return;
39981
40276
  server.emit({
39982
- data: { path: relative12(folder, path) },
40277
+ data: { path: relative13(folder, path) },
39983
40278
  event: "kennel:metadata-changed",
39984
40279
  type: "custom"
39985
40280
  });
@@ -40062,7 +40357,7 @@ function createFolderApi({
40062
40357
  "Content-Type": asset.mediaType,
40063
40358
  ETag: `"${hash}"`
40064
40359
  });
40065
- response.end(await readFile11(asset.path));
40360
+ response.end(await readFile12(asset.path));
40066
40361
  } catch (error) {
40067
40362
  response.writeHead(404, {
40068
40363
  "Content-Type": "text/plain; charset=utf-8"
@@ -40134,14 +40429,14 @@ function createFolderApi({
40134
40429
  }
40135
40430
  if (url.pathname === "/api/documents") {
40136
40431
  const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
40137
- const target = lockedTarget ? documentTarget(folder, relative12(folder, lockedTarget), lockedTarget) : undefined;
40432
+ const target = lockedTarget ? documentTarget(folder, relative13(folder, lockedTarget), lockedTarget) : undefined;
40138
40433
  const dataset = lockedTarget ? {
40139
40434
  root: folder,
40140
40435
  documents: target ? [
40141
40436
  {
40142
- path: relative12(folder, target.path),
40437
+ path: relative13(folder, target.path),
40143
40438
  kind: target.kind,
40144
- content: previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : ""
40439
+ content: previewTextKind(target.kind) ? await readFile12(target.path, "utf8") : ""
40145
40440
  }
40146
40441
  ] : []
40147
40442
  } : await readFolder(folder);
@@ -40157,7 +40452,7 @@ function createFolderApi({
40157
40452
  documentError(response);
40158
40453
  return;
40159
40454
  }
40160
- const details = await stat9(target.path);
40455
+ const details = await stat11(target.path);
40161
40456
  if (!details.isFile()) {
40162
40457
  documentError(response);
40163
40458
  return;
@@ -40169,16 +40464,16 @@ function createFolderApi({
40169
40464
  return;
40170
40465
  }
40171
40466
  watch(folder);
40172
- const content = previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : "";
40467
+ const content = previewTextKind(target.kind) ? await readFile12(target.path, "utf8") : "";
40173
40468
  sendContract(response, url, {
40174
40469
  content,
40175
40470
  kind: target.kind,
40176
- path: relative12(target.root, target.path),
40471
+ path: relative13(target.root, target.path),
40177
40472
  root: folder,
40178
40473
  ...previewTextKind(target.kind) ? {} : {
40179
40474
  rawUrl: `/api/raw?${new URLSearchParams({
40180
40475
  path: folder,
40181
- file: relative12(target.root, target.path)
40476
+ file: relative13(target.root, target.path)
40182
40477
  })}`
40183
40478
  }
40184
40479
  }, {
@@ -40198,7 +40493,7 @@ function createFolderApi({
40198
40493
  documentError(response);
40199
40494
  return;
40200
40495
  }
40201
- const details = await stat9(target.path);
40496
+ const details = await stat11(target.path);
40202
40497
  if (!details.isFile()) {
40203
40498
  documentError(response);
40204
40499
  return;
@@ -40222,7 +40517,7 @@ function createFolderApi({
40222
40517
  "Content-Disposition": `attachment; filename="${basename8(target.path)}"`
40223
40518
  } : {}
40224
40519
  });
40225
- response.end(await readFile11(target.path));
40520
+ response.end(await readFile12(target.path));
40226
40521
  } catch (error) {
40227
40522
  documentError(response);
40228
40523
  }
@@ -40236,7 +40531,7 @@ function createFolderApi({
40236
40531
  documentError(response);
40237
40532
  return;
40238
40533
  }
40239
- const details = await stat9(target.path);
40534
+ const details = await stat11(target.path);
40240
40535
  if (!details.isFile()) {
40241
40536
  documentError(response);
40242
40537
  return;
@@ -40248,7 +40543,7 @@ function createFolderApi({
40248
40543
  return;
40249
40544
  }
40250
40545
  watch(folder);
40251
- const data = await readFile11(target.path);
40546
+ const data = await readFile12(target.path);
40252
40547
  const baseHeaders = {
40253
40548
  "Accept-Ranges": "bytes",
40254
40549
  "Cache-Control": "private, max-age=0, must-revalidate",
@@ -40411,7 +40706,7 @@ function createFolderApi({
40411
40706
  workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
40412
40707
  const files = [
40413
40708
  workspace.configPath,
40414
- resolve13(workspace.root, "raggle.json")
40709
+ resolve16(workspace.root, "raggle.json")
40415
40710
  ].filter((path) => Boolean(path));
40416
40711
  for (const path of files)
40417
40712
  contexts.add(path);
@@ -40552,7 +40847,7 @@ function createFolderApi({
40552
40847
  }
40553
40848
  };
40554
40849
  }
40555
- var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts, canonicalPath = (path) => realpath7(path).catch(() => path);
40850
+ var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts, canonicalPath = (path) => realpath8(path).catch(() => path);
40556
40851
  var init_folder_metadata_plugin = __esm(() => {
40557
40852
  init_contracts();
40558
40853
  init_markdown();
@@ -40616,29 +40911,29 @@ var init_folder_metadata_plugin = __esm(() => {
40616
40911
  import {
40617
40912
  createServer
40618
40913
  } from "node:http";
40619
- import { readFile as readFile12, realpath as realpath8, stat as stat10 } from "node:fs/promises";
40620
- import { extname as extname7, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
40914
+ import { readFile as readFile13, realpath as realpath9, stat as stat12 } from "node:fs/promises";
40915
+ import { extname as extname7, isAbsolute as isAbsolute8, relative as relative14, resolve as resolve17 } from "node:path";
40621
40916
  async function sendFile(request, response, directory, pathname, fallback) {
40622
- const root = await realpath8(directory);
40917
+ const root = await realpath9(directory);
40623
40918
  const inside = (path) => {
40624
- const local = relative13(root, path);
40919
+ const local = relative14(root, path);
40625
40920
  return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
40626
40921
  };
40627
- let target = resolve14(root, `.${decodeURIComponent(pathname)}`);
40922
+ let target = resolve17(root, `.${decodeURIComponent(pathname)}`);
40628
40923
  if (!inside(target)) {
40629
40924
  response.writeHead(403);
40630
40925
  response.end();
40631
40926
  return;
40632
40927
  }
40633
- if (!(await stat10(target).catch(() => null))?.isFile()) {
40928
+ if (!(await stat12(target).catch(() => null))?.isFile()) {
40634
40929
  if (!fallback) {
40635
40930
  response.writeHead(404);
40636
40931
  response.end();
40637
40932
  return;
40638
40933
  }
40639
- target = resolve14(root, fallback);
40934
+ target = resolve17(root, fallback);
40640
40935
  }
40641
- target = await realpath8(target);
40936
+ target = await realpath9(target);
40642
40937
  if (!inside(target)) {
40643
40938
  response.writeHead(403);
40644
40939
  response.end();
@@ -40648,7 +40943,7 @@ async function sendFile(request, response, directory, pathname, fallback) {
40648
40943
  "Content-Type": contentTypes[extname7(target)] ?? "application/octet-stream",
40649
40944
  "X-Content-Type-Options": "nosniff"
40650
40945
  });
40651
- response.end(request.method === "HEAD" ? undefined : await readFile12(target));
40946
+ response.end(request.method === "HEAD" ? undefined : await readFile13(target));
40652
40947
  }
40653
40948
  function createKennelApi(options) {
40654
40949
  const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
@@ -40800,7 +41095,7 @@ data: ${JSON.stringify(event.data)}
40800
41095
  };
40801
41096
  }
40802
41097
  var contentTypes;
40803
- var init_server4 = __esm(() => {
41098
+ var init_server5 = __esm(() => {
40804
41099
  init_watcher();
40805
41100
  init_folder_metadata_plugin();
40806
41101
  contentTypes = {
@@ -40820,18 +41115,31 @@ var init_server4 = __esm(() => {
40820
41115
  };
40821
41116
  });
40822
41117
 
41118
+ // packages/preview/src/share.ts
41119
+ import { dirname as dirname16, resolve as resolve19, join as join7 } from "node:path";
41120
+ import { homedir as homedir5 } from "node:os";
41121
+ import { readFileSync as readFileSync2 } from "node:fs";
41122
+ import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
41123
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
41124
+
40823
41125
  // packages/preview/src/viewer.ts
40824
- import { execFileSync as execFileSync3 } from "node:child_process";
40825
- import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
41126
+ init_document_kinds();
41127
+ init_viewer_route();
41128
+ init_runtime();
41129
+ import { execFileSync as execFileSync2 } from "node:child_process";
41130
+ import { existsSync as existsSync7, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
40826
41131
  import { createServer as createServer2 } from "node:net";
40827
- import { dirname as dirname12, isAbsolute as isAbsolute9, relative as relative14, resolve as resolve15 } from "node:path";
40828
- import { fileURLToPath as fileURLToPath3 } from "node:url";
41132
+ import { dirname as dirname15, isAbsolute as isAbsolute9, relative as relative15, resolve as resolve18 } from "node:path";
41133
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
41134
+ var loopbackHosts = new Set([
41135
+ "127.0.0.1",
41136
+ "::1",
41137
+ "::ffff:127.0.0.1",
41138
+ "localhost"
41139
+ ]);
40829
41140
  function isLoopbackHost(host) {
40830
41141
  return loopbackHosts.has(host) || host.startsWith("127.");
40831
41142
  }
40832
- function encodeBase64Url2(value) {
40833
- return Buffer.from(value, "utf8").toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
40834
- }
40835
41143
  function splitViewerArgs(input) {
40836
41144
  let hasHost = false;
40837
41145
  let hasRoot = false;
@@ -40907,8 +41215,8 @@ function splitViewerArgs(input) {
40907
41215
  }
40908
41216
  if (arg.startsWith("-"))
40909
41217
  throw new Error(`Unknown viewer option: ${arg}`);
40910
- const candidate = resolve15(arg);
40911
- const isDirectory = existsSync6(candidate) && statSync3(candidate).isDirectory();
41218
+ const candidate = resolve18(arg);
41219
+ const isDirectory = existsSync7(candidate) && statSync3(candidate).isDirectory();
40912
41220
  if (isDirectory) {
40913
41221
  if (hasRoot)
40914
41222
  throw new Error("kennel accepts one folder");
@@ -40918,7 +41226,7 @@ function splitViewerArgs(input) {
40918
41226
  }
40919
41227
  if (hasRoot)
40920
41228
  throw new Error("kennel accepts one folder");
40921
- options.root = dirname12(candidate);
41229
+ options.root = dirname15(candidate);
40922
41230
  options.target = candidate;
40923
41231
  hasRoot = true;
40924
41232
  }
@@ -40937,7 +41245,7 @@ function splitViewerArgs(input) {
40937
41245
  }
40938
41246
  if (options.lan)
40939
41247
  options.host = "0.0.0.0";
40940
- if (!existsSync6(options.root) || !statSync3(options.root).isDirectory()) {
41248
+ if (!existsSync7(options.root) || !statSync3(options.root).isDirectory()) {
40941
41249
  throw new Error(`Folder not found: ${options.root}`);
40942
41250
  }
40943
41251
  if (options.target) {
@@ -40954,23 +41262,18 @@ function splitViewerArgs(input) {
40954
41262
  function popUrl(host, port, options) {
40955
41263
  const scheme = options.tailscale ? "https" : "http";
40956
41264
  const authority = options.tailscale ? host : `${host}:${port}`;
40957
- const url = new URL(`${scheme}://${authority}/`);
40958
- if (!options.target) {
40959
- url.pathname = `/folder/${encodeBase64Url2(options.root)}/notes`;
40960
- return url.href;
40961
- }
40962
- url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(relative14(options.root, options.target))}`;
40963
- if (options.watch)
40964
- url.searchParams.set("watch", "1");
40965
- return url.href;
41265
+ return folderUrl(`${scheme}://${authority}/`, options.root, {
41266
+ document: options.target ? relative15(options.root, options.target) : undefined,
41267
+ watch: options.watch
41268
+ }).href;
40966
41269
  }
40967
41270
  function viewerPortWasExplicit(input) {
40968
41271
  return input.some((arg) => arg === "--port" || arg.startsWith("--port="));
40969
41272
  }
40970
41273
  function isSameOrDescendant(parent, child) {
40971
- const parentReal = realpathSync3(parent);
40972
- const childReal = realpathSync3(child);
40973
- const childRelative = relative14(parentReal, childReal);
41274
+ const parentReal = realpathSync4(parent);
41275
+ const childReal = realpathSync4(child);
41276
+ const childRelative = relative15(parentReal, childReal);
40974
41277
  return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute9(childRelative);
40975
41278
  }
40976
41279
  async function runningViewer(url) {
@@ -41004,13 +41307,13 @@ async function availableViewerPort(start, host) {
41004
41307
  throw new Error(`Kennel could not find a free port at or above ${start}`);
41005
41308
  }
41006
41309
  function lanHost2() {
41007
- const route = execFileSync3("/sbin/route", ["-n", "get", "default"], {
41310
+ const route = execFileSync2("/sbin/route", ["-n", "get", "default"], {
41008
41311
  encoding: "utf8"
41009
41312
  });
41010
41313
  const device = route.match(/interface:\s+(\S+)/)?.[1];
41011
41314
  if (!device)
41012
41315
  throw new Error("kennel could not find the local network interface");
41013
- const host = execFileSync3("/usr/sbin/ipconfig", ["getifaddr", device], {
41316
+ const host = execFileSync2("/usr/sbin/ipconfig", ["getifaddr", device], {
41014
41317
  encoding: "utf8"
41015
41318
  }).trim();
41016
41319
  if (!host)
@@ -41018,7 +41321,7 @@ function lanHost2() {
41018
41321
  return host;
41019
41322
  }
41020
41323
  function tailscaleHost() {
41021
- const status = JSON.parse(execFileSync3("tailscale", ["status", "--json"], { encoding: "utf8" }));
41324
+ const status = JSON.parse(execFileSync2("tailscale", ["status", "--json"], { encoding: "utf8" }));
41022
41325
  const host = typeof status.Self?.DNSName === "string" ? status.Self.DNSName.replace(/\.$/, "") : "";
41023
41326
  if (host)
41024
41327
  return host;
@@ -41032,13 +41335,13 @@ async function viewer(input) {
41032
41335
  await hotViewer(options);
41033
41336
  return;
41034
41337
  }
41035
- const web = resolve15(dirname12(fileURLToPath3(import.meta.url)), "../../web");
41036
- if (!existsSync6(resolve15(web, "index.html"))) {
41338
+ const web = resolve18(dirname15(fileURLToPath4(import.meta.url)), "../../web");
41339
+ if (!existsSync7(resolve18(web, "index.html"))) {
41037
41340
  throw new Error(`Kennel web app not found. Run the CLI build first.`);
41038
41341
  }
41039
41342
  const remote = options.tailscale || !isLoopbackHost(options.host);
41040
- const lockedTarget = options.target ? realpathSync3(options.target) : undefined;
41041
- await Promise.resolve().then(() => init_server4());
41343
+ const lockedTarget = options.target ? realpathSync4(options.target) : undefined;
41344
+ await Promise.resolve().then(() => init_server5());
41042
41345
  const displayHost = options.tailscale ? tailscaleHost() : options.lan ? lanHost2() : options.host;
41043
41346
  if (remote)
41044
41347
  process.env.KENNEL_VIEWER_HOSTS = [
@@ -41049,18 +41352,9 @@ async function viewer(input) {
41049
41352
  const existing = await runningViewer(initialViewerUrl);
41050
41353
  if (existing && isSameOrDescendant(existing.root, options.root)) {
41051
41354
  const viewerUrl = popUrl(displayHost, options.port, options);
41052
- const terminal = previewTerminal(viewerUrl, options, async () => {
41053
- await Promise.resolve().then(() => init_share());
41054
- return startPreviewShare(options);
41055
- });
41355
+ console.log(viewerUrl);
41056
41356
  if (options.open)
41057
41357
  openPreview(viewerUrl);
41058
- try {
41059
- if (terminal.interactive)
41060
- await terminal.closed;
41061
- } finally {
41062
- terminal.close();
41063
- }
41064
41358
  return;
41065
41359
  }
41066
41360
  if (!portExplicit) {
@@ -41077,38 +41371,25 @@ async function viewer(input) {
41077
41371
  try {
41078
41372
  await api.listen(Number(options.port));
41079
41373
  if (options.tailscale) {
41080
- execFileSync3("tailscale", ["serve", "--bg", "--yes", options.port]);
41374
+ execFileSync2("tailscale", ["serve", "--bg", "--yes", options.port]);
41081
41375
  }
41082
41376
  const viewerUrl = popUrl(displayHost, options.port, options);
41083
- const terminal = previewTerminal(viewerUrl, options, async () => {
41084
- await Promise.resolve().then(() => init_share());
41085
- return startPreviewShare(options);
41086
- });
41087
- if (options.open)
41088
- openPreview(viewerUrl);
41377
+ const lifetime = viewerLifetime();
41089
41378
  try {
41090
- await terminal.closed;
41379
+ console.log(viewerUrl);
41380
+ if (options.open)
41381
+ openPreview(viewerUrl);
41382
+ await lifetime.closed;
41091
41383
  } finally {
41092
- terminal.close();
41384
+ lifetime.close();
41093
41385
  }
41094
41386
  } finally {
41095
41387
  await api.close();
41096
41388
  }
41097
41389
  if (options.tailscale) {
41098
- execFileSync3("tailscale", ["serve", "--https=443", "off"]);
41390
+ execFileSync2("tailscale", ["serve", "--https=443", "off"]);
41099
41391
  }
41100
41392
  }
41101
- var loopbackHosts;
41102
- var init_viewer = __esm(() => {
41103
- init_document_kinds();
41104
- init_terminal();
41105
- loopbackHosts = new Set([
41106
- "127.0.0.1",
41107
- "::1",
41108
- "::ffff:127.0.0.1",
41109
- "localhost"
41110
- ]);
41111
- });
41112
41393
 
41113
41394
  // packages/preview/src/protected-share.ts
41114
41395
  import { createHash as createHash6, randomBytes, timingSafeEqual } from "node:crypto";
@@ -41169,11 +41450,11 @@ async function startProtectedShare(handle) {
41169
41450
  throw error;
41170
41451
  }
41171
41452
  }
41172
- var init_protected_share = () => {};
41173
41453
 
41174
41454
  // packages/preview/src/tailscale-share.ts
41175
41455
  import { execFile as execFile7 } from "node:child_process";
41176
41456
  import { promisify as promisify2 } from "node:util";
41457
+ var run2 = promisify2(execFile7);
41177
41458
  async function serveConfig() {
41178
41459
  const { stdout } = await run2("tailscale", ["serve", "status", "--json"]);
41179
41460
  return JSON.parse(stdout);
@@ -41213,17 +41494,9 @@ async function startTailscaleProxy(host, port) {
41213
41494
  throw error;
41214
41495
  }
41215
41496
  }
41216
- var run2;
41217
- var init_tailscale_share = __esm(() => {
41218
- run2 = promisify2(execFile7);
41219
- });
41220
41497
 
41221
41498
  // packages/preview/src/share.ts
41222
- import { dirname as dirname13, resolve as resolve16, join as join6 } from "node:path";
41223
- import { homedir as homedir5 } from "node:os";
41224
- import { readFileSync as readFileSync2 } from "node:fs";
41225
- import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
41226
- import { fileURLToPath as fileURLToPath4 } from "node:url";
41499
+ var plainViewerPort = "5180";
41227
41500
  function splitShareArgs(input) {
41228
41501
  const options = {
41229
41502
  folder: undefined,
@@ -41275,10 +41548,10 @@ function splitShareArgs(input) {
41275
41548
  if (options.port && !/^\d+$/.test(options.port)) {
41276
41549
  throw new Error("kennel share requires a numeric port");
41277
41550
  }
41278
- if (folder && !existsSync7(folder)) {
41551
+ if (folder && !existsSync8(folder)) {
41279
41552
  throw new Error(`Folder not found: ${folder}`);
41280
41553
  }
41281
- options.folder = folder ? resolve16(folder) : undefined;
41554
+ options.folder = folder ? resolve19(folder) : undefined;
41282
41555
  return options;
41283
41556
  }
41284
41557
  async function runningViewerRoot(port = plainViewerPort) {
@@ -41298,27 +41571,27 @@ async function runningViewerRoot(port = plainViewerPort) {
41298
41571
  return;
41299
41572
  }
41300
41573
  async function startPreviewShare(target) {
41301
- const root = realpathSync4(target.root);
41302
- const configPath = join6(process.env.XDG_CONFIG_HOME?.trim() || join6(homedir5(), ".config"), "kennel", "config.json");
41574
+ const root = realpathSync5(target.root);
41575
+ const configPath = join7(process.env.XDG_CONFIG_HOME?.trim() || join7(homedir5(), ".config"), "kennel", "config.json");
41303
41576
  let configuredPassword = false;
41304
41577
  try {
41305
41578
  const config = JSON.parse(readFileSync2(configPath, "utf8"));
41306
41579
  configuredPassword = config.linkSharing?.password === true;
41307
41580
  } catch {}
41308
41581
  const password = target.password ?? configuredPassword;
41309
- const web = resolve16(dirname13(fileURLToPath4(import.meta.url)), "../../web");
41310
- if (!existsSync7(resolve16(web, "index.html"))) {
41582
+ const web = resolve19(dirname16(fileURLToPath5(import.meta.url)), "../../web");
41583
+ if (!existsSync8(resolve19(web, "index.html"))) {
41311
41584
  throw new Error("Kennel web app not found. Run the CLI build first.");
41312
41585
  }
41313
41586
  const host = await tailscaleHostname();
41314
- await Promise.resolve().then(() => init_server4());
41587
+ await Promise.resolve().then(() => init_server5());
41315
41588
  process.env.KENNEL_VIEWER_HOSTS = [process.env.KENNEL_VIEWER_HOSTS, host].filter(Boolean).join(",");
41316
41589
  const api = createKennelApi({
41317
41590
  root,
41318
41591
  host: "127.0.0.1",
41319
41592
  remoteAccess: true,
41320
41593
  restrictToRoot: true,
41321
- lockedFile: target.target ? realpathSync4(target.target) : undefined,
41594
+ lockedFile: target.target ? realpathSync5(target.target) : undefined,
41322
41595
  webRoot: web
41323
41596
  });
41324
41597
  let shared;
@@ -41398,7 +41671,7 @@ async function share(input) {
41398
41671
  console.log(`Sharing ${folder}`);
41399
41672
  if (options.dryRun) {
41400
41673
  const port = options.port ?? "5181";
41401
- const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}/notes` : popUrl(lanHost2(), port, { dryRun: true, host: "0.0.0.0", hot: false, lan: true, open: false, root: folder, port, tailscale: false, watch: false, password: false });
41674
+ const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}` : popUrl(lanHost2(), port, { dryRun: true, host: "0.0.0.0", hot: false, lan: true, open: false, root: folder, port, tailscale: false, watch: false, password: false });
41402
41675
  console.log(url);
41403
41676
  console.log(`kennel serve --static ${options.tailscale ? "--tailscale" : "--lan"}${options.password ? " --password" : ""}${options.port ? ` --port ${options.port}` : ""} ${folder}`);
41404
41677
  return;
@@ -41411,14 +41684,6 @@ async function share(input) {
41411
41684
  folder
41412
41685
  ]);
41413
41686
  }
41414
- var plainViewerPort = "5180";
41415
- var init_share = __esm(() => {
41416
- init_viewer();
41417
- init_protected_share();
41418
- init_tailscale_share();
41419
- });
41420
- init_share();
41421
-
41422
41687
  export {
41423
41688
  plainViewerPort,
41424
41689
  runningViewerRoot,