summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJay V <[email protected]>2025-07-04 13:33:23 -0400
committerJay V <[email protected]>2025-07-04 13:53:25 -0400
commit143fd8e07635274403874479a53f0b124ac5f433 (patch)
tree1078e5c7b5aec24aefedbc5cede0ae110718f348
parent06dba28bd69134535ad4a1482b7bbda9f26f96d6 (diff)
downloadopencode-143fd8e07635274403874479a53f0b124ac5f433.tar.gz
opencode-143fd8e07635274403874479a53f0b124ac5f433.zip
docs: share improve markdown rendering of ai responses
-rw-r--r--bun.lock8
-rw-r--r--packages/web/package.json1
-rw-r--r--packages/web/src/components/MarkdownView.tsx32
-rw-r--r--packages/web/src/components/Share.tsx9
-rw-r--r--packages/web/src/components/markdownview.module.css48
-rw-r--r--packages/web/src/components/share.module.css7
6 files changed, 78 insertions, 27 deletions
diff --git a/bun.lock b/bun.lock
index a723e36be..0fec03e8a 100644
--- a/bun.lock
+++ b/bun.lock
@@ -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;