summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorJay V <[email protected]>2025-06-05 19:14:35 -0400
committerJay V <[email protected]>2025-06-05 19:14:35 -0400
commit65b2cf73d7595ae8f235bd670433220308efe61f (patch)
treee2b3a1ffd8068c4847861a813bac6909cfdef35c /packages
parent95069af03fa38e64144b11f9068ead7c00831064 (diff)
downloadopencode-65b2cf73d7595ae8f235bd670433220308efe61f.tar.gz
opencode-65b2cf73d7595ae8f235bd670433220308efe61f.zip
share page markdown
Diffstat (limited to 'packages')
-rw-r--r--packages/web/package.json1
-rw-r--r--packages/web/src/components/MarkdownView.tsx21
-rw-r--r--packages/web/src/components/Share.tsx59
-rw-r--r--packages/web/src/components/markdownview.module.css40
-rw-r--r--packages/web/src/components/share.module.css31
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;