opencode/packages/session-ui/component-tests/markdown.fixture.tsx

103 lines
3.7 KiB
TypeScript

import { createSignal, Show } from "solid-js"
import { render } from "solid-js/web"
import { DialogProvider } from "@opencode/ui/context/dialog"
import { I18nProvider, useI18n } from "@opencode/ui/context/i18n"
import { Markdown } from "../src/components/markdown"
import { preloadMarkdown } from "../src/components/markdown-cache"
import { MarkdownProvider, type MarkdownLocalFileExists } from "../src/context/markdown"
import { OpenCode } from "@opencode/client/promise"
import { readLocalImage } from "../../app/src/runtime/server/image"
export {
getCachedMarkdown,
renderCachedMarkdown,
sanitizeMarkdown,
touchCachedMarkdown,
} from "../src/components/markdown-cache"
export { renderMermaidSvg } from "../src/components/markdown-mermaid"
export { MarkdownWorkerDisposedError } from "../src/components/markdown-worker"
export { preloadMarkdown }
// Inline code naming one of these is a file link; any other path stays plain code.
const existingFiles = new Set(["src/file.ts", "new/file.ts"])
export async function mountMarkdown(options: {
text: string
streaming?: boolean
cached?: boolean
images?: boolean
synchronousFiles?: boolean
localFileExists?: MarkdownLocalFileExists
}) {
if (options.cached) await preloadMarkdown(options.text, "markdown-test")
const host = document.createElement("div")
host.dataset.testid = "markdown-fixture"
document.body.appendChild(host)
render(() => {
const api = OpenCode.make({
baseUrl: location.origin,
headers: { Authorization: `Basic ${btoa("opencode:fixture")}` },
})
const [text, setText] = createSignal(options.text)
const [streaming, setStreaming] = createSignal(options.streaming ?? false)
const [visible, setVisible] = createSignal(true)
const [german, setGerman] = createSignal(false)
const english = useI18n()
return (
<DialogProvider>
<textarea aria-label="Markdown text" value={text()} onInput={(event) => setText(event.currentTarget.value)} />
<input
aria-label="Streaming"
type="checkbox"
checked={streaming()}
onChange={(event) => setStreaming(event.currentTarget.checked)}
/>
<input
aria-label="German"
type="checkbox"
checked={german()}
onChange={(event) => setGerman(event.currentTarget.checked)}
/>
<button onClick={() => setVisible((value) => !value)}>Toggle Markdown</button>
<I18nProvider
value={{
locale: () => (german() ? "de" : "en"),
t: (key, params) => (german() && key === "ui.message.copy" ? "Kopieren" : english.t(key, params)),
plural: english.plural,
pluralForm: english.pluralForm,
}}
>
<MarkdownProvider
readImage={(path, signal) =>
options.images ? readLocalImage(api, "C:/project", path, signal) : Promise.resolve(undefined)
}
localFileExists={(path) =>
options.localFileExists?.(path) ??
(options.synchronousFiles ? existingFiles.has(path) : Promise.resolve(existingFiles.has(path)))
}
>
<Show when={visible()}>
<Markdown
text={text()}
streaming={streaming()}
cacheKey={options.cached ? "markdown-test" : undefined}
deferUntilReady
/>
</Show>
</MarkdownProvider>
</I18nProvider>
</DialogProvider>
)
}, host)
// Capture before Promise callbacks run: eventual assertions cannot catch a flash on insertion.
return Array.from(host.querySelectorAll<HTMLElement>(":not(pre) > code"), (node) => ({
text: node.textContent,
kind: node.dataset.inlineCodeKind ?? null,
}))
}