mirror of
https://github.com/sst/opencode.git
synced 2025-08-04 13:30:52 +00:00
share page markdown
This commit is contained in:
parent
95069af03f
commit
65b2cf73d7
6 changed files with 153 additions and 2 deletions
3
bun.lock
3
bun.lock
|
@ -65,6 +65,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",
|
||||
|
@ -935,6 +936,8 @@
|
|||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"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=="],
|
||||
|
|
|
@ -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",
|
||||
|
|
21
packages/web/src/components/MarkdownView.tsx
Normal file
21
packages/web/src/components/MarkdownView.tsx
Normal file
|
@ -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
|
||||
|
|
@ -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()}
|
||||
/>
|
||||
|
|
40
packages/web/src/components/markdownview.module.css
Normal file
40
packages/web/src/components/markdownview.module.css
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue