@@ -2,10 +2,10 @@ import { useMarked } from "../context/marked"
22import { useI18n } from "../context/i18n"
33import DOMPurify from "dompurify"
44import morphdom from "morphdom"
5- import { marked , type Tokens } from "marked"
65import { checksum } from "@opencode-ai/util/encode"
76import { ComponentProps , createEffect , createResource , createSignal , onCleanup , splitProps } from "solid-js"
87import { isServer } from "solid-js/web"
8+ import { stream } from "./markdown-stream"
99
1010type Entry = {
1111 hash : string
@@ -58,47 +58,6 @@ function fallback(markdown: string) {
5858 return escape ( markdown ) . replace ( / \r \n ? / g, "\n" ) . replace ( / \n / g, "<br>" )
5959}
6060
61- type Block = {
62- raw : string
63- mode : "full" | "live"
64- }
65-
66- function references ( markdown : string ) {
67- return / ^ \[ [ ^ \] ] + \] : \s + \S + / m. test ( markdown ) || / ^ \[ \^ [ ^ \] ] + \] : \s + / m. test ( markdown )
68- }
69-
70- function incomplete ( raw : string ) {
71- const open = raw . match ( / ^ [ \t ] { 0 , 3 } ( ` { 3 , } | ~ { 3 , } ) / )
72- if ( ! open ) return false
73- const mark = open [ 1 ]
74- if ( ! mark ) return false
75- const char = mark [ 0 ]
76- const size = mark . length
77- const last = raw . trimEnd ( ) . split ( "\n" ) . at ( - 1 ) ?. trim ( ) ?? ""
78- return ! new RegExp ( `^[\\t ]{0,3}${ char } {${ size } ,}[\\t ]*$` ) . test ( last )
79- }
80-
81- function blocks ( markdown : string , streaming : boolean ) {
82- if ( ! streaming || references ( markdown ) ) return [ { raw : markdown , mode : "full" } ] satisfies Block [ ]
83- const tokens = marked . lexer ( markdown )
84- const last = tokens . findLast ( ( token ) => token . type !== "space" )
85- if ( ! last || last . type !== "code" ) return [ { raw : markdown , mode : "full" } ] satisfies Block [ ]
86- const code = last as Tokens . Code
87- if ( ! incomplete ( code . raw ) ) return [ { raw : markdown , mode : "full" } ] satisfies Block [ ]
88- const head = tokens
89- . slice (
90- 0 ,
91- tokens . findLastIndex ( ( token ) => token . type !== "space" ) ,
92- )
93- . map ( ( token ) => token . raw )
94- . join ( "" )
95- if ( ! head ) return [ { raw : code . raw , mode : "live" } ] satisfies Block [ ]
96- return [
97- { raw : head , mode : "full" } ,
98- { raw : code . raw , mode : "live" } ,
99- ] satisfies Block [ ]
100- }
101-
10261type CopyLabels = {
10362 copy : string
10463 copied : string
@@ -251,8 +210,6 @@ function setupCodeCopy(root: HTMLDivElement, getLabels: () => CopyLabels) {
251210 timeouts . set ( button , timeout )
252211 }
253212
254- decorate ( root , getLabels ( ) )
255-
256213 const buttons = Array . from ( root . querySelectorAll ( '[data-slot="markdown-copy-button"]' ) )
257214 for ( const button of buttons ) {
258215 if ( button instanceof HTMLButtonElement ) updateLabel ( button )
@@ -304,7 +261,7 @@ export function Markdown(
304261
305262 const base = src . key ?? checksum ( src . text )
306263 return Promise . all (
307- blocks ( src . text , src . streaming ) . map ( async ( block , index ) => {
264+ stream ( src . text , src . streaming ) . map ( async ( block , index ) => {
308265 const hash = checksum ( block . raw )
309266 const key = base ? `${ base } :${ index } :${ block . mode } ` : hash
310267
@@ -316,7 +273,7 @@ export function Markdown(
316273 }
317274 }
318275
319- const next = await Promise . resolve ( marked . parse ( block . raw ) )
276+ const next = await Promise . resolve ( marked . parse ( block . src ) )
320277 const safe = sanitize ( next )
321278 if ( key && hash ) touch ( key , { hash, html : safe } )
322279 return safe
0 commit comments