Skip to content
84 linesCodeBlameRaw

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.

Merge main into Artifacts Phase 21import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { type DocNode, docToMarkdown, escapeText, markdownToDoc } from "./chat-compose.ts";
5
6const text = (value: string, ...marks: (string | { type: string; attrs?: Record<string, unknown> })[]): DocNode => ({
7 type: "text",
8 text: value,
9 ...(marks.length ? { marks: marks.map((mark) => (typeof mark === "string" ? { type: mark } : mark)) } : {}),
10});
11const p = (...content: DocNode[]): DocNode => (content.length ? { type: "paragraph", content } : { type: "paragraph" });
12const doc = (...content: DocNode[]): DocNode => ({ type: "doc", content });
13
14test("formatting is written as Markdown", () => {
15 assert.equal(
16 docToMarkdown(doc(p(text("bold", "bold"), text(" "), text("it", "italic"), text(" "), text("gone", "strike"), text(" "), text("a*b", "code")))),
17 "**bold** _it_ ~~gone~~ `a*b`",
18 );
19 // Marks that overlap open and close once.
20 assert.equal(docToMarkdown(doc(p(text("a", "bold"), text("b", "bold", "italic"), text("c", "bold")))), "**a_b_c**");
21 // Spaces at a mark's edges sit outside it.
22 assert.equal(docToMarkdown(doc(p(text("say "), text(" hi ", "bold"), text("there")))), "say **hi** there");
23 assert.equal(docToMarkdown(doc(p(text("tick ` in", "code")))), "`` tick ` in ``");
24});
25
26test("links: with their text, or bare when the text is the address", () => {
27 const link = (href: string) => ({ type: "link", attrs: { href } });
28 assert.equal(docToMarkdown(doc(p(text("the docs", link("https://g1t.sh/docs"))))), "[the docs](https://g1t.sh/docs)");
29 assert.equal(docToMarkdown(doc(p(text("https://g1t.sh", link("https://g1t.sh"))))), "https://g1t.sh");
30 assert.equal(docToMarkdown(doc(p(text("x", link("https://a.example/a b"))))), "[x](<https://a.example/a%20b>)");
31});
32
33test("each paragraph is a line; an empty one is a blank line", () => {
34 assert.equal(docToMarkdown(doc(p(text("one")), p(text("two")), p(), p(text("three")))), "one\ntwo\n\nthree");
35 assert.equal(docToMarkdown(doc(p(), p(text("x")), p())), "x");
36});
37
38test("text that only looks like Markdown is escaped, and mentions stay mentions", () => {
39 assert.equal(escapeText("2*3 and snake_case and _edge_ and [x] and `t` and ~5"), "2\\*3 and snake_case and \\_edge\\_ and \\[x\\] and \\`t\\` and \\~5");
40 assert.equal(docToMarkdown(doc(p(text("- not a list")), p(text("1. not either")), p(text("# nor this")), p(text("> or this")))), "\\- not a list\n1\\. not either\n\\# nor this\n\\> or this");
41 assert.equal(docToMarkdown(doc(p(text("ask @ana in #web about #12")))), "ask @ana in #web about #12");
42});
43
44test("lists, quotes and code blocks", () => {
45 const item = (...content: DocNode[]): DocNode => ({ type: "listItem", content });
46 assert.equal(
47 docToMarkdown(
48 doc(
49 p(text("Plan:")),
50 { type: "bulletList", content: [item(p(text("one"))), item(p(text("two")), { type: "orderedList", attrs: { start: 1 }, content: [item(p(text("deep")))] })] },
51 p(text("after")),
52 ),
53 ),
54 "Plan:\n\n- one\n- two\n 1. deep\n\nafter",
55 );
56 assert.equal(docToMarkdown(doc({ type: "orderedList", attrs: { start: 3 }, content: [item(p(text("c"))), item(p(text("d")))] })), "3. c\n4. d");
57 assert.equal(docToMarkdown(doc({ type: "blockquote", content: [p(text("quoted")), p(), p(text("more"))] })), "> quoted\n>\n> more");
58 assert.equal(docToMarkdown(doc({ type: "codeBlock", attrs: { language: "ts" }, content: [{ type: "text", text: "const a = 1;\n\n\nconst b = `x`;" }] })), "```ts\nconst a = 1;\n\n\nconst b = `x`;\n```");
59 assert.equal(docToMarkdown(doc({ type: "codeBlock", attrs: { language: null }, content: [{ type: "text", text: "```\nfence\n```" }] })), "````\n```\nfence\n```\n````");
60});
61
62test("Markdown comes back into the composer as it was written", () => {
63 const samples = [
64 "**bold** _it_ ~~gone~~ `code`",
65 "one\ntwo\n\nthree",
66 "Plan:\n\n- one\n- two\n 1. deep\n\nafter",
67 "3. c\n4. d",
68 "> quoted\n>\n> more",
69 "```ts\nconst a = 1;\n\nconst b = 2;\n```",
70 "[the docs](https://g1t.sh/docs) and https://g1t.sh",
71 "ask @ana in #web about g1t#12",
72 "\\- not a list and 2\\*3 and snake_case",
73 "**a _b_ c**",
The top of every page is one surface: the breadcrumb, search, spend, Ask g1t, bell and + share one height, corners and spacing, Ask g1t is a plain tinted button with the g1t mark drawn on whole pixels, a page's own header sits directly under the bar at the same left edge, and every section, conversations and open artifacts included, draws the same rounded panel. Tables written in chat render as tables, and the site's file, checkbox, radio, number and date controls are the design system's. Button, Separator, Breadcrumb, Table and FileDrop join the shared components; the chat guide says how tables work.74 "| Session | Length |\n| --- | --: |\n| **Welcome** | `45` min |",
Merge main into Artifacts Phase 275 ];
76 for (const sample of samples) assert.equal(docToMarkdown(markdownToDoc(sample)), sample, sample);
77});
78
79test("Markdown the composer cannot write still comes in", () => {
80 // A heading is a bold line; a rule is left out; an image is a link.
81 assert.equal(docToMarkdown(markdownToDoc("## Notes\n---\ntext")), "**Notes**\ntext");
82 assert.equal(docToMarkdown(markdownToDoc("*star em* and __strong__ and ~one~")), "_star em_ and **strong** and ~~one~~");
83 assert.deepEqual(markdownToDoc(""), { type: "doc", content: [{ type: "paragraph" }] });
84});

This file's history is long; its oldest lines are credited to the oldest commit read.