Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113import { bridge } from "../src/collab/bridge.ts";import "../src/docs/element.ts";import { draftSandbox } from "../src/sandbox-name.ts";import { connectTo } from "../src/sandbox.ts";import { editor, join } from "./peers.ts";
const PATH = "/work/notes.md";
const SAMPLE = `# Notes
Processes in the kernel read and write this file with \`node:fs\`. Their cursors show what they changed.
## Ideas
- Type here while a process runs`;
// A process that types a sentence into the file one word at a time, reading before each write.const TYPIST = `const fs = require("node:fs");const path = ${JSON.stringify(PATH)};const words = "The typist reads the file before every word, so whatever you type meanwhile is kept.".split(" ");const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));(async () => { for (const [i, word] of words.entries()) { const text = fs.readFileSync(path, "utf8"); // The sentence so far, wherever edits around it have moved it; the first word starts a new item. const typed = "- " + words.slice(0, i).join(" "); const marker = "- Type here while a process runs"; let at, insert; if (i > 0 && text.includes(typed)) [at, insert] = [text.indexOf(typed) + typed.length, " " + word]; else if (text.includes(marker)) [at, insert] = [text.indexOf(marker) + marker.length, "\\n- " + word]; else [at, insert] = [text.length, "\\n- " + word]; fs.writeFileSync(path, text.slice(0, at) + insert + text.slice(at)); await sleep(140); } console.log("typist: done");})();`;
// A process that reads once, thinks, then saves atomically (temp file + rename), as editors and agents do.const REWRITE = `const fs = require("node:fs");const path = ${JSON.stringify(PATH)};const text = fs.readFileSync(path, "utf8");console.log("rewrite: read " + text.length + " characters, thinking for 3s");setTimeout(() => { const next = text.replace(/^(#+ )(.*)$/gm, (_, hashes, title) => hashes + title.toUpperCase()) + "\\n## Summary\\n\\nHeadings uppercased by rewrite.js from the version it read.\\n"; fs.writeFileSync(path + ".tmp", next); fs.renameSync(path + ".tmp", path); console.log("rewrite: saved");}, 3000);`;
// A process that watches the file, as a dev server or test runner would.const WATCH = `const fs = require("node:fs");const path = ${JSON.stringify(PATH)};fs.watch(path, () => { const text = fs.readFileSync(path, "utf8"); console.log("watch: " + text.split("\\n").length + " lines, " + text.length + " characters");});console.log("watch: watching " + path);`;
const room = join("nib-kernel:playground", SAMPLE);for (const [name, color] of [["Ada", 0], ["Grace", 1]] as const) { const { doc, presence } = room.replica(); const el = editor("nib-doc", name.toLowerCase()); el.user = { name, colorClassName: `nib-user-${color}` }; el.presence = presence; el.doc = doc;}
const output = document.querySelector<HTMLPreElement>("#console")!;const status = document.querySelector<HTMLOutputElement>("#kernel-status")!;const decoder = new TextDecoder();const log = (line: string) => { output.textContent += line; output.scrollTop = output.scrollHeight;};
async function pipe(stream: ReadableStream<Uint8Array>) { for await (const chunk of stream as unknown as AsyncIterable<Uint8Array>) log(decoder.decode(chunk));}
// The kernel runs on a sandbox origin, as the editor's does (D27), never on this page's.const kernel = await connectTo(draftSandbox());await kernel.applyFiles({ "/app/typist.js": TYPIST, "/app/rewrite.js": REWRITE, "/app/watch.js": WATCH });
// The kernel gets its own replica of the doc, like any other peer.const { doc, presence } = room.replica();bridge(kernel, PATH, doc, { presence });Object.assign(globalThis, { kernel, kernelDoc: doc });
function run(script: string) { const proc = kernel.exec(`/app/${script}`, { argv: ["node", `/app/${script}`] }); void pipe(proc.stdout); void pipe(proc.stderr); return proc;}
// Give the bridge's first write a moment to land before anything watches the file.await new Promise((resolve) => setTimeout(resolve, 50));run("watch.js");for (const [id, script] of [["#run-typist", "typist.js"], ["#run-rewrite", "rewrite.js"]] as const) { const button = document.querySelector<HTMLButtonElement>(id)!; button.disabled = false; button.addEventListener("click", () => run(script));}status.textContent = "Kernel ready. Run a process, then type in either editor while it works.";