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 | |
| parent | 06dba28bd69134535ad4a1482b7bbda9f26f96d6 (diff) | |
| download | opencode-143fd8e07635274403874479a53f0b124ac5f433.tar.gz opencode-143fd8e07635274403874479a53f0b124ac5f433.zip | |
docs: share improve markdown rendering of ai responses
| -rw-r--r-- | bun.lock | 8 | ||||
| -rw-r--r-- | packages/web/package.json | 1 | ||||
| -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 |
6 files changed, 78 insertions, 27 deletions
@@ -31,7 +31,6 @@ "@openauthjs/openauth": "0.4.3", "@standard-schema/spec": "1.0.0", "ai": "catalog:", - "air": "0.4.14", "decimal.js": "10.5.0", "diff": "8.0.2", "env-paths": "3.0.0", @@ -79,6 +78,7 @@ "lang-map": "0.4.0", "luxon": "3.6.1", "marked": "15.0.12", + "marked-shiki": "1.2.0", "rehype-autolink-headings": "7.1.0", "sharp": "0.32.5", "shiki": "3.4.2", @@ -517,8 +517,6 @@ "ai": ["[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g=="], - "air": ["[email protected]", "", { "dependencies": { "zephyr": "~1.3.5" } }, "sha512-E8bl9LlSGSQqjxxjeGIrpYpf8jVyJplsdK1bTobh61F7ks+3aLeXL4KbGSJIFsiaSSz5ZExLU51DGztmQSlZTQ=="], - "ansi-align": ["[email protected]", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], "ansi-regex": ["[email protected]", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -1055,6 +1053,8 @@ "marked": ["[email protected]", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], + "marked-shiki": ["[email protected]", "", { "peerDependencies": { "marked": ">=7.0.0", "shiki": ">=1.0.0" } }, "sha512-N924hp8veE6Mc91g5/kCNVoTU7TkeJfB2G2XEWb+k1fVA0Bck2T0rVt93d39BlOYH6ohP4Q9BFlPk+UkblhXbg=="], + "math-intrinsics": ["[email protected]", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "mdast-util-definitions": ["[email protected]", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], @@ -1703,8 +1703,6 @@ "youch": ["[email protected]", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], - "zephyr": ["[email protected]", "", {}, "sha512-oYH52DGZzIbXNrkijskaR8YpVKnXAe8jNgH1KirglVBnTFOn6mK9/0SVCxGn+73l0Hjhr4UYNzYkO07LXSWy6w=="], - "zod": ["[email protected]", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], "zod-openapi": ["[email protected]", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="], diff --git a/packages/web/package.json b/packages/web/package.json index 383b979fa..c1722b2bf 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -24,6 +24,7 @@ "lang-map": "0.4.0", "luxon": "3.6.1", "marked": "15.0.12", + "marked-shiki": "1.2.0", "rehype-autolink-headings": "7.1.0", "sharp": "0.32.5", "shiki": "3.4.2", 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; |
