diff options
| author | Jay V <[email protected]> | 2025-07-04 13:33:23 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-07-04 13:53:25 -0400 |
| commit | 143fd8e07635274403874479a53f0b124ac5f433 (patch) | |
| tree | 1078e5c7b5aec24aefedbc5cede0ae110718f348 /packages/web/src | |
| parent | 06dba28bd69134535ad4a1482b7bbda9f26f96d6 (diff) | |
| download | opencode-143fd8e07635274403874479a53f0b124ac5f433.tar.gz opencode-143fd8e07635274403874479a53f0b124ac5f433.zip | |
docs: share improve markdown rendering of ai responses
Diffstat (limited to 'packages/web/src')
| -rw-r--r-- | packages/web/src/components/MarkdownView.tsx | 32 | ||||
| -rw-r--r-- | packages/web/src/components/Share.tsx | 9 | ||||
| -rw-r--r-- | packages/web/src/components/markdownview.module.css | 48 | ||||
| -rw-r--r-- | packages/web/src/components/share.module.css | 7 |
4 files changed, 74 insertions, 22 deletions
diff --git a/packages/web/src/components/MarkdownView.tsx b/packages/web/src/components/MarkdownView.tsx index 5e21c0d7e..7a63bc0cb 100644 --- a/packages/web/src/components/MarkdownView.tsx +++ b/packages/web/src/components/MarkdownView.tsx @@ -1,21 +1,39 @@ import { type JSX, splitProps, createResource } from "solid-js" import { marked } from "marked" +import markedShiki from "marked-shiki" +import { codeToHtml } from "shiki" +import { transformerNotationDiff } from "@shikijs/transformers" import styles from "./markdownview.module.css" interface MarkdownViewProps extends JSX.HTMLAttributes<HTMLDivElement> { markdown: string } +const markedWithShiki = marked.use( + markedShiki({ + highlight(code, lang) { + return codeToHtml(code, { + lang: lang || "text", + themes: { + light: "github-light", + dark: "github-dark", + }, + transformers: [transformerNotationDiff()], + }) + }, + }), +) + function MarkdownView(props: MarkdownViewProps) { const [local, rest] = splitProps(props, ["markdown"]) - const [html] = createResource(() => local.markdown, async (markdown) => { - return marked.parse(markdown) - }) - - return ( - <div innerHTML={html()} class={styles["markdown-body"]} {...rest} /> + const [html] = createResource( + () => local.markdown, + async (markdown) => { + return markedWithShiki.parse(markdown) + }, ) + + return <div innerHTML={html()} class={styles["markdown-body"]} {...rest} /> } export default MarkdownView - diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index ff838dabe..7f2c45b18 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -294,15 +294,11 @@ function ResultsButton(props: ResultsButtonProps) { interface TextPartProps extends JSX.HTMLAttributes<HTMLDivElement> { text: string expand?: boolean - invert?: boolean - highlight?: boolean } function TextPart(props: TextPartProps) { const [local, rest] = splitProps(props, [ "text", "expand", - "invert", - "highlight", ]) const [expanded, setExpanded] = createSignal(false) const [overflowed, setOverflowed] = createSignal(false) @@ -332,8 +328,6 @@ function TextPart(props: TextPartProps) { return ( <div class={styles["message-text"]} - data-invert={local.invert} - data-highlight={local.highlight} data-expanded={expanded() || local.expand === true} {...rest} > @@ -991,9 +985,9 @@ export default function Share(props: { </div> <div data-section="content"> <TextPart - invert text={part().text} expand={isLastPart()} + data-background="blue" /> </div> </div> @@ -1021,7 +1015,6 @@ export default function Share(props: { </div> <div data-section="content"> <MarkdownPart - highlight expand={isLastPart()} text={stripEnclosingTag(part().text)} /> diff --git a/packages/web/src/components/markdownview.module.css b/packages/web/src/components/markdownview.module.css index a4360bde9..9524c5cd7 100644 --- a/packages/web/src/components/markdownview.module.css +++ b/packages/web/src/components/markdownview.module.css @@ -40,11 +40,17 @@ } pre { - white-space: pre-wrap; - border-radius: 0.25rem; - border: 1px solid rgba(0, 0, 0, 0.2); + --shiki-dark-bg: var(--sl-color-bg-surface) !important; + background-color: var(--sl-color-bg-surface) !important; padding: 0.5rem 0.75rem; + line-height: 1.6; font-size: 0.75rem; + white-space: pre-wrap; + word-break: break-word; + + span { + white-space: break-spaces; + } } code { @@ -61,4 +67,40 @@ } } } + + table { + border-collapse: collapse; + width: 100%; + } + + th, + td { + border: 1px solid var(--sl-color-border); + padding: 0.5rem 0.75rem; + text-align: left; + } + + th { + border-bottom: 1px solid var(--sl-color-border); + } + + /* Remove outer borders */ + table tr:first-child th, + table tr:first-child td { + border-top: none; + } + + table tr:last-child td { + border-bottom: none; + } + + table th:first-child, + table td:first-child { + border-left: none; + } + + table th:last-child, + table td:last-child { + border-right: none; + } } diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css index dafbdd8a2..d8eac0e58 100644 --- a/packages/web/src/components/share.module.css +++ b/packages/web/src/components/share.module.css @@ -493,9 +493,8 @@ } } - &[data-highlight="true"] { - background-color: var(--sl-color-blue-low); - } + &[data-background="none"] { background-color: transparent; } + &[data-background="blue"] { background-color: var(--sl-color-blue-low); } &[data-expanded="true"] { pre { @@ -669,7 +668,7 @@ } .message-markdown { - background-color: var(--sl-color-bg-surface); + border: 1px solid var(--sl-color-blue-high); padding: 0.5rem calc(0.5rem + 3px); border-radius: 0.25rem; display: flex; |
