docs: share improve markdown rendering of ai responses

This commit is contained in:
Jay V 2025-07-04 13:33:23 -04:00
parent 06dba28bd6
commit 143fd8e076
6 changed files with 78 additions and 27 deletions

View file

@ -31,7 +31,6 @@
"@openauthjs/openauth": "0.4.3", "@openauthjs/openauth": "0.4.3",
"@standard-schema/spec": "1.0.0", "@standard-schema/spec": "1.0.0",
"ai": "catalog:", "ai": "catalog:",
"air": "0.4.14",
"decimal.js": "10.5.0", "decimal.js": "10.5.0",
"diff": "8.0.2", "diff": "8.0.2",
"env-paths": "3.0.0", "env-paths": "3.0.0",
@ -79,6 +78,7 @@
"lang-map": "0.4.0", "lang-map": "0.4.0",
"luxon": "3.6.1", "luxon": "3.6.1",
"marked": "15.0.12", "marked": "15.0.12",
"marked-shiki": "1.2.0",
"rehype-autolink-headings": "7.1.0", "rehype-autolink-headings": "7.1.0",
"sharp": "0.32.5", "sharp": "0.32.5",
"shiki": "3.4.2", "shiki": "3.4.2",
@ -517,8 +517,6 @@
"ai": ["ai@4.3.16", "", { "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=="], "ai": ["ai@4.3.16", "", { "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": ["air@0.4.14", "", { "dependencies": { "zephyr": "~1.3.5" } }, "sha512-E8bl9LlSGSQqjxxjeGIrpYpf8jVyJplsdK1bTobh61F7ks+3aLeXL4KbGSJIFsiaSSz5ZExLU51DGztmQSlZTQ=="],
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
@ -1055,6 +1053,8 @@
"marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
"marked-shiki": ["marked-shiki@1.2.0", "", { "peerDependencies": { "marked": ">=7.0.0", "shiki": ">=1.0.0" } }, "sha512-N924hp8veE6Mc91g5/kCNVoTU7TkeJfB2G2XEWb+k1fVA0Bck2T0rVt93d39BlOYH6ohP4Q9BFlPk+UkblhXbg=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "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": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="],
"zephyr": ["zephyr@1.3.6", "", {}, "sha512-oYH52DGZzIbXNrkijskaR8YpVKnXAe8jNgH1KirglVBnTFOn6mK9/0SVCxGn+73l0Hjhr4UYNzYkO07LXSWy6w=="],
"zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
"zod-openapi": ["zod-openapi@4.2.4", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="], "zod-openapi": ["zod-openapi@4.2.4", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="],

View file

@ -24,6 +24,7 @@
"lang-map": "0.4.0", "lang-map": "0.4.0",
"luxon": "3.6.1", "luxon": "3.6.1",
"marked": "15.0.12", "marked": "15.0.12",
"marked-shiki": "1.2.0",
"rehype-autolink-headings": "7.1.0", "rehype-autolink-headings": "7.1.0",
"sharp": "0.32.5", "sharp": "0.32.5",
"shiki": "3.4.2", "shiki": "3.4.2",

View file

@ -1,21 +1,39 @@
import { type JSX, splitProps, createResource } from "solid-js" import { type JSX, splitProps, createResource } from "solid-js"
import { marked } from "marked" import { marked } from "marked"
import markedShiki from "marked-shiki"
import { codeToHtml } from "shiki"
import { transformerNotationDiff } from "@shikijs/transformers"
import styles from "./markdownview.module.css" import styles from "./markdownview.module.css"
interface MarkdownViewProps extends JSX.HTMLAttributes<HTMLDivElement> { interface MarkdownViewProps extends JSX.HTMLAttributes<HTMLDivElement> {
markdown: string 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) { function MarkdownView(props: MarkdownViewProps) {
const [local, rest] = splitProps(props, ["markdown"]) const [local, rest] = splitProps(props, ["markdown"])
const [html] = createResource(() => local.markdown, async (markdown) => { const [html] = createResource(
return marked.parse(markdown) () => local.markdown,
}) async (markdown) => {
return markedWithShiki.parse(markdown)
return ( },
<div innerHTML={html()} class={styles["markdown-body"]} {...rest} />
) )
return <div innerHTML={html()} class={styles["markdown-body"]} {...rest} />
} }
export default MarkdownView export default MarkdownView

View file

@ -294,15 +294,11 @@ function ResultsButton(props: ResultsButtonProps) {
interface TextPartProps extends JSX.HTMLAttributes<HTMLDivElement> { interface TextPartProps extends JSX.HTMLAttributes<HTMLDivElement> {
text: string text: string
expand?: boolean expand?: boolean
invert?: boolean
highlight?: boolean
} }
function TextPart(props: TextPartProps) { function TextPart(props: TextPartProps) {
const [local, rest] = splitProps(props, [ const [local, rest] = splitProps(props, [
"text", "text",
"expand", "expand",
"invert",
"highlight",
]) ])
const [expanded, setExpanded] = createSignal(false) const [expanded, setExpanded] = createSignal(false)
const [overflowed, setOverflowed] = createSignal(false) const [overflowed, setOverflowed] = createSignal(false)
@ -332,8 +328,6 @@ function TextPart(props: TextPartProps) {
return ( return (
<div <div
class={styles["message-text"]} class={styles["message-text"]}
data-invert={local.invert}
data-highlight={local.highlight}
data-expanded={expanded() || local.expand === true} data-expanded={expanded() || local.expand === true}
{...rest} {...rest}
> >
@ -991,9 +985,9 @@ export default function Share(props: {
</div> </div>
<div data-section="content"> <div data-section="content">
<TextPart <TextPart
invert
text={part().text} text={part().text}
expand={isLastPart()} expand={isLastPart()}
data-background="blue"
/> />
</div> </div>
</div> </div>
@ -1021,7 +1015,6 @@ export default function Share(props: {
</div> </div>
<div data-section="content"> <div data-section="content">
<MarkdownPart <MarkdownPart
highlight
expand={isLastPart()} expand={isLastPart()}
text={stripEnclosingTag(part().text)} text={stripEnclosingTag(part().text)}
/> />

View file

@ -40,11 +40,17 @@
} }
pre { pre {
white-space: pre-wrap; --shiki-dark-bg: var(--sl-color-bg-surface) !important;
border-radius: 0.25rem; background-color: var(--sl-color-bg-surface) !important;
border: 1px solid rgba(0, 0, 0, 0.2);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
line-height: 1.6;
font-size: 0.75rem; font-size: 0.75rem;
white-space: pre-wrap;
word-break: break-word;
span {
white-space: break-spaces;
}
} }
code { 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;
}
} }

View file

@ -493,9 +493,8 @@
} }
} }
&[data-highlight="true"] { &[data-background="none"] { background-color: transparent; }
background-color: var(--sl-color-blue-low); &[data-background="blue"] { background-color: var(--sl-color-blue-low); }
}
&[data-expanded="true"] { &[data-expanded="true"] {
pre { pre {
@ -669,7 +668,7 @@
} }
.message-markdown { .message-markdown {
background-color: var(--sl-color-bg-surface); border: 1px solid var(--sl-color-blue-high);
padding: 0.5rem calc(0.5rem + 3px); padding: 0.5rem calc(0.5rem + 3px);
border-radius: 0.25rem; border-radius: 0.25rem;
display: flex; display: flex;