From 143fd8e07635274403874479a53f0b124ac5f433 Mon Sep 17 00:00:00 2001 From: Jay V Date: Fri, 4 Jul 2025 13:33:23 -0400 Subject: [PATCH] docs: share improve markdown rendering of ai responses --- bun.lock | 8 ++-- packages/web/package.json | 1 + packages/web/src/components/MarkdownView.tsx | 32 ++++++++++--- packages/web/src/components/Share.tsx | 9 +--- .../src/components/markdownview.module.css | 48 +++++++++++++++++-- packages/web/src/components/share.module.css | 7 ++- 6 files changed, 78 insertions(+), 27 deletions(-) diff --git a/bun.lock b/bun.lock index a723e36b..0fec03e8 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": ["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-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-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=="], "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=="], - "zephyr": ["zephyr@1.3.6", "", {}, "sha512-oYH52DGZzIbXNrkijskaR8YpVKnXAe8jNgH1KirglVBnTFOn6mK9/0SVCxGn+73l0Hjhr4UYNzYkO07LXSWy6w=="], - "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=="], diff --git a/packages/web/package.json b/packages/web/package.json index 383b979f..c1722b2b 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 5e21c0d7..7a63bc0c 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 { 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 ( -
+ const [html] = createResource( + () => local.markdown, + async (markdown) => { + return markedWithShiki.parse(markdown) + }, ) + + return
} export default MarkdownView - diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index ff838dab..7f2c45b1 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 { 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 (
@@ -991,9 +985,9 @@ export default function Share(props: {
@@ -1021,7 +1015,6 @@ export default function Share(props: {
diff --git a/packages/web/src/components/markdownview.module.css b/packages/web/src/components/markdownview.module.css index a4360bde..9524c5cd 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 dafbdd8a..d8eac0e5 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;