Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Docs: a workspace knowledge base people and agents write together | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import * as Y from "yjs"; | |
| 5 | ||
| 6 | import { seed } from "./blocks.ts"; | |
| 7 | import { anchorThread, applyEdit, commentMarkKey, findTarget, rangeIds, rangeMarkdown, restoreFrom, unanchorThread } from "./edits.ts"; | |
| 8 | import { documentMarkdown, outline, topContainers } from "./markdown.ts"; | |
| 9 | ||
| 10 | const PAGE = `# Plan | |
| 11 | ||
| 12 | Intro. | |
| 13 | ||
| 14 | ## Rollout | |
| 15 | ||
| 16 | Old rollout. | |
| 17 | ||
| 18 | - step one | |
| 19 | ||
| 20 | ### Detail | |
| 21 | ||
| 22 | Fine print. | |
| 23 | ||
| 24 | ## Risks | |
| 25 | ||
| 26 | None. | |
| 27 | `; | |
| 28 | ||
| 29 | function docFrom(markdown: string) { | |
| 30 | const doc = new Y.Doc(); | |
| 31 | const fragment = doc.getXmlFragment("document-store"); | |
| 32 | seed(doc, fragment, markdown); | |
| 33 | return { doc, fragment }; | |
| 34 | } | |
| 35 | ||
| 36 | test("a section runs to the next heading of its level or higher, subsections included", () => { | |
| 37 | const { fragment } = docFrom(PAGE); | |
| 38 | const range = findTarget(fragment, { kind: "section", heading: "rollout" })!; | |
| 39 | assert.equal(rangeMarkdown(fragment, range), "## Rollout\n\nOld rollout.\n\n- step one\n\n### Detail\n\nFine print."); | |
| 40 | assert.equal(findTarget(fragment, { kind: "section", heading: "Nope" }), null); | |
| 41 | }); | |
| 42 | ||
| 43 | test("replacing a section changes only that section", () => { | |
| 44 | const { doc, fragment } = docFrom(PAGE); | |
| 45 | const ok = applyEdit(doc, fragment, { kind: "section", heading: "Rollout" }, "## Rollout\n\nShip to 10% first, then everyone."); | |
| 46 | assert.equal(ok, true); | |
| 47 | assert.equal(documentMarkdown(fragment), "# Plan\n\nIntro.\n\n## Rollout\n\nShip to 10% first, then everyone.\n\n## Risks\n\nNone.\n"); | |
| 48 | }); | |
| 49 | ||
| 50 | test("blocks targets use the ids agents read from the outline", () => { | |
| 51 | const { doc, fragment } = docFrom(PAGE); | |
| 52 | const blocks = outline(fragment); | |
| 53 | const intro = blocks.find((b) => b.markdown === "Intro.")!; | |
| 54 | applyEdit(doc, fragment, { kind: "blocks", from_block: intro.id, to_block: intro.id }, "A better intro."); | |
| 55 | assert.match(documentMarkdown(fragment), /^# Plan\n\nA better intro\.\n\n## Rollout/); | |
| 56 | // Untouched blocks keep their ids. | |
| 57 | assert.equal(outline(fragment)[0]!.id, blocks[0]!.id); | |
| 58 | }); | |
| 59 | ||
| 60 | test("append adds at the end; document replaces everything", () => { | |
| 61 | const { doc, fragment } = docFrom("# A"); | |
| 62 | applyEdit(doc, fragment, { kind: "append" }, "Added."); | |
| 63 | assert.equal(documentMarkdown(fragment), "# A\n\nAdded.\n"); | |
| 64 | applyEdit(doc, fragment, { kind: "document" }, ""); | |
| 65 | // The editor keeps one empty block. | |
| 66 | assert.equal(topContainers(fragment).length, 1); | |
| 67 | assert.equal(documentMarkdown(fragment), ""); | |
| 68 | }); | |
| 69 | ||
| 70 | test("an edit made on the server merges with a person typing at the same time", () => { | |
| 71 | const { doc, fragment } = docFrom(PAGE); | |
| 72 | const person = new Y.Doc(); | |
| 73 | Y.applyUpdate(person, Y.encodeStateAsUpdate(doc)); | |
| 74 | // The person types in the intro while the agent rewrites Risks. | |
| 75 | const theirs = person.getXmlFragment("document-store"); | |
| 76 | const intro = topContainers(theirs)[1]!.get(0) as Y.XmlElement; | |
| 77 | (intro.get(0) as Y.XmlText).insert(0, "Hello. "); | |
| 78 | applyEdit(doc, fragment, { kind: "section", heading: "Risks" }, "## Risks\n\nRollback is one click."); | |
| 79 | Y.applyUpdate(doc, Y.encodeStateAsUpdate(person)); | |
| 80 | Y.applyUpdate(person, Y.encodeStateAsUpdate(doc)); | |
| 81 | const merged = documentMarkdown(fragment); | |
| 82 | assert.equal(merged, documentMarkdown(theirs)); | |
| 83 | assert.match(merged, /Hello\. Intro\./); | |
| 84 | assert.match(merged, /Rollback is one click\./); | |
| 85 | }); | |
| 86 | ||
| 87 | test("restoring a version brings back its blocks as a new change", () => { | |
| 88 | const { doc, fragment } = docFrom("# v1\n\nfirst"); | |
| 89 | const v1 = Y.encodeStateAsUpdate(doc); | |
| 90 | applyEdit(doc, fragment, { kind: "document" }, "# v2\n\nsecond"); | |
| 91 | const old = new Y.Doc(); | |
| 92 | Y.applyUpdate(old, v1); | |
| 93 | restoreFrom(doc, fragment, old.getXmlFragment("document-store")); | |
| 94 | assert.equal(documentMarkdown(fragment), "# v1\n\nfirst\n"); | |
| 95 | }); | |
| 96 | ||
| 97 | test("a comment anchors to the text between two positions, across blocks", () => { | |
| 98 | const { doc, fragment } = docFrom("alpha beta\n\ngamma delta"); | |
| 99 | const tops = topContainers(fragment); | |
| 100 | const first = (tops[0]!.get(0) as Y.XmlElement).get(0) as Y.XmlText; | |
| 101 | const second = (tops[1]!.get(0) as Y.XmlElement).get(0) as Y.XmlText; | |
| 102 | const anchor = Y.relativePositionToJSON(Y.createRelativePositionFromTypeIndex(first, 6)); | |
| 103 | const head = Y.relativePositionToJSON(Y.createRelativePositionFromTypeIndex(second, 5)); | |
| 104 | const quote = anchorThread(doc, fragment, anchor, head, "t1"); | |
| 105 | assert.equal(quote, "beta gamma"); | |
| 106 | const key = commentMarkKey("t1"); | |
| 107 | assert.match(key, /^comment--[A-Za-z0-9+/=]{8}$/); | |
| 108 | assert.deepEqual(first.toDelta(), [{ insert: "alpha " }, { insert: "beta", attributes: { [key]: { orphan: false, threadId: "t1" } } }]); | |
| 109 | // Marks don't show in Markdown. | |
| 110 | assert.equal(documentMarkdown(fragment), "alpha beta\n\ngamma delta\n"); | |
| 111 | unanchorThread(doc, fragment, "t1"); | |
| 112 | assert.deepEqual(first.toDelta(), [{ insert: "alpha beta" }]); | |
| 113 | }); | |
| 114 | ||
| 115 | test("range ids list the blocks a suggestion covers", () => { | |
| 116 | const { fragment } = docFrom(PAGE); | |
| 117 | const range = findTarget(fragment, { kind: "section", heading: "Risks" })!; | |
| 118 | assert.equal(rangeIds(fragment, range).length, 2); | |
| 119 | }); |