diff options
| author | Jay V <[email protected]> | 2025-06-05 19:14:35 -0400 |
|---|---|---|
| committer | Jay V <[email protected]> | 2025-06-05 19:14:35 -0400 |
| commit | 65b2cf73d7595ae8f235bd670433220308efe61f (patch) | |
| tree | e2b3a1ffd8068c4847861a813bac6909cfdef35c /packages | |
| parent | 95069af03fa38e64144b11f9068ead7c00831064 (diff) | |
| download | opencode-65b2cf73d7595ae8f235bd670433220308efe61f.tar.gz opencode-65b2cf73d7595ae8f235bd670433220308efe61f.zip | |
share page markdown
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/web/package.json | 1 | ||||
| -rw-r--r-- | packages/web/src/components/MarkdownView.tsx | 21 | ||||
| -rw-r--r-- | packages/web/src/components/Share.tsx | 59 | ||||
| -rw-r--r-- | packages/web/src/components/markdownview.module.css | 40 | ||||
| -rw-r--r-- | packages/web/src/components/share.module.css | 31 |
5 files changed, 150 insertions, 2 deletions
diff --git a/packages/web/package.json b/packages/web/package.json index e1a114643..cba5a4598 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -21,6 +21,7 @@ "astro-sst": "3.1.4", "diff": "8.0.2", "luxon": "3.6.1", + "marked": "15.0.12", "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 new file mode 100644 index 000000000..319ea72a6 --- /dev/null +++ b/packages/web/src/components/MarkdownView.tsx @@ -0,0 +1,21 @@ +import { type JSX, splitProps, createResource } from "solid-js" +import { marked } from "marked" +import styles from "./markdownview.module.css" + +interface MarkdownViewProps extends JSX.HTMLAttributes<HTMLDivElement> { + markdown: string +} + +function MarkdownView(props: MarkdownViewProps) { + const [local, rest] = splitProps(props, ["markdown"]) + const [html] = createResource(async () => { + return marked.parse(local.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 d79620025..aac487739 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -12,6 +12,7 @@ import { createSignal, } from "solid-js" import { DateTime } from "luxon" +import { createStore, reconcile } from "solid-js/store" import { IconOpenAI, IconGemini, IconAnthropic } from "./icons/custom" import { IconCpuChip, @@ -32,9 +33,9 @@ import { } from "./icons" import DiffView from "./DiffView" import CodeBlock from "./CodeBlock" +import MarkdownView from "./MarkdownView" import styles from "./share.module.css" import { type UIMessage } from "ai" -import { createStore, reconcile } from "solid-js/store" const MIN_DURATION = 2 @@ -268,6 +269,60 @@ function TextPart(props: TextPartProps) { ) } +interface MarkdownPartProps extends JSX.HTMLAttributes<HTMLDivElement> { + text: string + expand?: boolean +} +function MarkdownPart(props: MarkdownPartProps) { + const [local, rest] = splitProps(props, ["text", "expand"]) + const [expanded, setExpanded] = createSignal(false) + const [overflowed, setOverflowed] = createSignal(false) + let divEl: HTMLDivElement | undefined + + function checkOverflow() { + if (divEl && !local.expand) { + setOverflowed(divEl.scrollHeight > divEl.clientHeight + 1) + } + } + + onMount(() => { + checkOverflow() + window.addEventListener("resize", checkOverflow) + }) + + createEffect(() => { + local.text + setTimeout(checkOverflow, 0) + }) + + onCleanup(() => { + window.removeEventListener("resize", checkOverflow) + }) + + return ( + <div + class={styles["message-markdown"]} + data-expanded={expanded() || local.expand === true} + {...rest} + > + <MarkdownView + data-elment-markdown + markdown={local.text} + ref={(el) => (divEl = el)} + /> + {((!local.expand && overflowed()) || expanded()) && ( + <button + type="button" + data-element-button-text + onClick={() => setExpanded((e) => !e)} + > + {expanded() ? "Show less" : "Show more"} + </button> + )} + </div> + ) +} + interface TerminalPartProps extends JSX.HTMLAttributes<HTMLDivElement> { text: string desc?: string @@ -682,7 +737,7 @@ export default function Share(props: { api: string }) { <div></div> </div> <div data-section="content"> - <TextPart + <MarkdownPart text={part().text} expand={isLastPart()} /> diff --git a/packages/web/src/components/markdownview.module.css b/packages/web/src/components/markdownview.module.css new file mode 100644 index 000000000..802e7f15e --- /dev/null +++ b/packages/web/src/components/markdownview.module.css @@ -0,0 +1,40 @@ +.markdown-body { + font-size: 0.875rem; + line-height: 1.5; + + p, + blockquote, + ul, + ol, + dl, + table, + pre { + margin-bottom: 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + margin-bottom: 0.5rem; + } + + & > *:last-child { + margin-bottom: 0; + } + + code { + font-weight: 500; + + &::before { + content: "`"; + font-weight: 600; + } + &::after { + content: "`"; + font-weight: 600; + } + } +} diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css index 8f193fc0b..e64354c99 100644 --- a/packages/web/src/components/share.module.css +++ b/packages/web/src/components/share.module.css @@ -351,6 +351,7 @@ } } + [data-part-type="tool-write"], [data-part-type="tool-read"], [data-part-type="tool-fetch"] { [data-part-tool-result] { @@ -534,6 +535,36 @@ } } +.message-markdown { + background-color: var(--sl-color-bg-surface); + padding: 0.5rem calc(0.5rem + 3px); + border-radius: 0.25rem; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 1rem; + + button { + flex: 0 0 auto; + padding: 2px 0; + font-size: 0.75rem; + } + + &[data-expanded="true"] { + [data-elment-markdown] { + display: block; + } + } + &[data-expanded="false"] { + [data-elment-markdown] { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + } + } +} + .diff-code-block { pre { line-height: 1.25; |
