refactor(ui): isolate session components (#33670)
This commit is contained in:
parent
1ef0fd5d01
commit
04c1730c90
126 changed files with 294 additions and 142 deletions
194
packages/session-ui/src/components/markdown-stream.test.ts
Normal file
194
packages/session-ui/src/components/markdown-stream.test.ts
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { canReusePendingBlock, project, stream } from "./markdown-stream"
|
||||
|
||||
describe("markdown stream", () => {
|
||||
test("heals incomplete emphasis while streaming", () => {
|
||||
expect(stream("hello **world", true)).toEqual([{ raw: "hello **world", src: "hello **world**", mode: "live" }])
|
||||
expect(stream("say `code", true)).toEqual([{ raw: "say `code", src: "say `code`", mode: "live" }])
|
||||
})
|
||||
|
||||
test("keeps incomplete links non-clickable until they finish", () => {
|
||||
expect(stream("see [docs](https://example.com/gu", true)).toEqual([
|
||||
{ raw: "see [docs](https://example.com/gu", src: "see docs", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("splits an unfinished trailing code fence from stable content", () => {
|
||||
expect(stream("before\n\n```ts\nconst x = 1", true)).toEqual([
|
||||
{ raw: "before\n\n", src: "before\n\n", mode: "full" },
|
||||
{ raw: "```ts\nconst x = 1", src: "const x = 1", mode: "code", language: "ts" },
|
||||
])
|
||||
})
|
||||
|
||||
test("fully parses a code fence once it closes", () => {
|
||||
const text = "before\n\n```ts\nconst x = 1\n```"
|
||||
expect(stream(text, true)).toEqual([
|
||||
{ raw: "before\n\n", src: "before\n\n", mode: "full" },
|
||||
{ raw: "```ts\nconst x = 1\n```", src: "const x = 1", mode: "code", language: "ts", complete: true },
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps a completed code fence in worker-rendered code mode when prose follows", () => {
|
||||
expect(stream("```ts\nconst x = 1\n```\n\nafter", true)).toEqual([
|
||||
{ raw: "```ts\nconst x = 1\n```\n\n", src: "const x = 1", mode: "code", language: "ts", complete: true },
|
||||
{ raw: "after", src: "after", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("freezes completed top-level blocks and only keeps the tail live", () => {
|
||||
expect(stream("# Plan\n\nFinished paragraph.\n\n- live item", true)).toEqual([
|
||||
{ raw: "# Plan\n\n", src: "# Plan\n\n", mode: "full" },
|
||||
{ raw: "Finished paragraph.\n\n", src: "Finished paragraph.\n\n", mode: "full" },
|
||||
{ raw: "- live item", src: "- live item", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps a growing table together until a later block freezes it", () => {
|
||||
expect(stream("| a | b |\n|---|---|\n| 1 | 2 |", true)).toEqual([
|
||||
{ raw: "| a | b |\n|---|---|\n| 1 | 2 |", src: "| a | b |\n|---|---|\n| 1 | 2 |", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("reprojects non-prefix replacements from current content", () => {
|
||||
expect(stream("# Replacement\n\nNew body", true)).toEqual([
|
||||
{ raw: "# Replacement\n\n", src: "# Replacement\n\n", mode: "full" },
|
||||
{ raw: "New body", src: "New body", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("reprojects truncation without retaining removed blocks", () => {
|
||||
expect(stream("Only the restored prefix", true)).toEqual([
|
||||
{ raw: "Only the restored prefix", src: "Only the restored prefix", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("shifts later blocks when an earlier block is inserted", () => {
|
||||
expect(stream("# Inserted\n\nFirst body\n\nSecond body", true)).toEqual([
|
||||
{ raw: "# Inserted\n\n", src: "# Inserted\n\n", mode: "full" },
|
||||
{ raw: "First body\n\n", src: "First body\n\n", mode: "full" },
|
||||
{ raw: "Second body", src: "Second body", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps reference-style markdown as one block", () => {
|
||||
expect(stream("[docs][1]\n\n[1]: https://example.com", true)).toEqual([
|
||||
{
|
||||
raw: "[docs][1]\n\n[1]: https://example.com",
|
||||
src: "[docs][1]\n\n[1]: https://example.com",
|
||||
mode: "live",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps compact and indented reference definitions with their uses", () => {
|
||||
expect(stream("[docs]\n\n [docs]:/guide", true)).toEqual([
|
||||
{
|
||||
raw: "[docs]\n\n [docs]:/guide",
|
||||
src: "[docs]\n\n [docs]:/guide",
|
||||
mode: "live",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps multiline reference definitions with their uses", () => {
|
||||
expect(stream("[docs][id]\n\n[id]:\n /guide", true)).toEqual([
|
||||
{
|
||||
raw: "[docs][id]\n\n[id]:\n /guide",
|
||||
src: "[docs][id]\n\n[id]:\n /guide",
|
||||
mode: "live",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("uses only the language portion of fence metadata", () => {
|
||||
expect(stream("```ts title=example\nconst x = 1", true)).toEqual([
|
||||
{
|
||||
raw: "```ts title=example\nconst x = 1",
|
||||
src: "const x = 1",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("preserves trailing newlines in open code fences", () => {
|
||||
expect(stream("```ts\nconst x = 1\n", true)).toEqual([
|
||||
{
|
||||
raw: "```ts\nconst x = 1\n",
|
||||
src: "const x = 1\n",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("only reuses pending blocks with compatible identity and content", () => {
|
||||
expect(
|
||||
canReusePendingBlock({ mode: "full", raw: "First\n\n" }, { mode: "full", raw: "# Inserted\n\n", src: "" }),
|
||||
).toBe(false)
|
||||
expect(
|
||||
canReusePendingBlock({ mode: "code", raw: "```ts\none" }, { mode: "code", raw: "```ts\none two", src: "" }),
|
||||
).toBe(true)
|
||||
expect(canReusePendingBlock({ mode: "code", raw: "```ts\none" }, { mode: "live", raw: "one", src: "" })).toBe(false)
|
||||
})
|
||||
|
||||
test("appends plain code deltas without reprojecting frozen blocks", () => {
|
||||
const previous = project(undefined, "# Plan\n\n```ts\nconst one = 1\n", true)
|
||||
const next = project(previous, `${previous.text}const two = 2\n`, true)
|
||||
|
||||
expect(next.blocks[0]).toBe(previous.blocks[0])
|
||||
expect(next.blocks.at(-1)).toEqual({
|
||||
raw: "```ts\nconst one = 1\nconst two = 2\n",
|
||||
src: "const one = 1\nconst two = 2\n",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
})
|
||||
})
|
||||
|
||||
test("does not add a blank line before the first streamed code", () => {
|
||||
const previous = project(undefined, "```ts\n", true)
|
||||
const next = project(previous, `${previous.text}const x = 1`, true)
|
||||
|
||||
expect(next.blocks.at(-1)).toEqual({
|
||||
raw: "```ts\nconst x = 1",
|
||||
src: "const x = 1",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
})
|
||||
})
|
||||
|
||||
test("closes code fences split across provider deltas", () => {
|
||||
const open = project(undefined, "```ts\nconst x = 1\n", true)
|
||||
const one = project(open, `${open.text}\``, true)
|
||||
const two = project(one, `${one.text}\``, true)
|
||||
const closed = project(two, `${two.text}\``, true)
|
||||
const prose = project(closed, `${closed.text}\nafter`, true)
|
||||
|
||||
expect(closed.blocks.at(-1)).toEqual({
|
||||
raw: "```ts\nconst x = 1\n```",
|
||||
src: "const x = 1",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
complete: true,
|
||||
})
|
||||
expect(prose.blocks).toEqual([
|
||||
{ raw: "```ts\nconst x = 1\n```\n", src: "const x = 1", mode: "code", language: "ts", complete: true },
|
||||
{ raw: "after", src: "after", mode: "live" },
|
||||
])
|
||||
})
|
||||
|
||||
test("closes tilde fences split across provider deltas", () => {
|
||||
const open = project(undefined, "~~~ts\nconst x = 1\n", true)
|
||||
const one = project(open, `${open.text}~`, true)
|
||||
const two = project(one, `${one.text}~`, true)
|
||||
const closed = project(two, `${two.text}~`, true)
|
||||
|
||||
expect(closed.blocks.at(-1)).toEqual({
|
||||
raw: "~~~ts\nconst x = 1\n~~~",
|
||||
src: "const x = 1",
|
||||
mode: "code",
|
||||
language: "ts",
|
||||
complete: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue