1- import { For , createResource , Show } from "solid-js" ;
1+ import { For , createResource , createSignal , Show } from "solid-js" ;
22import { setState , state , tempState } from "../../App" ;
33import { setPreview } from "../../App" ;
44import { openPopout } from "../rightSidebar/memberList/popout.jsx" ;
55import { twemojiUrl } from "./twemoji.js" ;
6- const markdownCache = new Map ( ) ;
7-
86import { switchToChannel } from "../../App" ;
97import { BeamEmbed } from "./embeds/BeamEmbed.jsx" ;
108import { getEmbedProvider } from "./embeds/registry.jsx" ;
9+ import hljs from 'highlight.js' ;
10+ import 'highlight.js/styles/atom-one-dark.css' ;
11+
12+ const markdownCache = new Map ( ) ;
13+ import { parseMarkdown as tokenizeMarkdown } from "./markdown_tokenizer.js" ;
1114
1215function getParsedMarkdown ( id , content ) {
1316 if ( markdownCache . has ( id ) ) {
@@ -71,35 +74,6 @@ function ServerInviteEmbed(props) {
7174 ) ;
7275}
7376
74- function readMaskedLink ( text , start ) {
75- if ( text [ start ] !== "[" ) return null ;
76-
77- let i = start + 1 ;
78- while ( i < text . length && text [ i ] !== "]" ) i ++ ;
79- if ( text [ i ] !== "]" || text [ i + 1 ] !== "(" ) return null ;
80-
81- const label = text . slice ( start + 1 , i ) ;
82-
83- i += 2 ;
84-
85- let depth = 1 ;
86- const urlStart = i ;
87-
88- while ( i < text . length && depth > 0 ) {
89- if ( text [ i ] === "(" ) depth ++ ;
90- else if ( text [ i ] === ")" ) depth -- ;
91- i ++ ;
92- }
93-
94- if ( depth !== 0 ) return null ;
95-
96- return {
97- label,
98- url : text . slice ( urlStart , i - 1 ) ,
99- end : i ,
100- } ;
101- }
102-
10377export function Embed ( props ) {
10478 const embed = props . embed ;
10579 if ( embed . type == "image" ) return ;
@@ -140,7 +114,6 @@ export function Embed(props) {
140114
141115 { embed . title && < div class = "embed_title" > { embed . title } </ div > }
142116
143- { /* FIXED: Use renderTokens instead of parseMarkdown to avoid circular loop */ }
144117 { embed . description && (
145118 < div class = "embed_description" >
146119 { renderTokens ( tokenizeMarkdown ( embed . description ) ) }
@@ -194,7 +167,6 @@ function EmbeddedLink(props) {
194167 const type = data ( ) . type ;
195168
196169 if ( type . startsWith ( "image/" ) ) {
197- console . log ( "d" ) ;
198170 return (
199171 < img
200172 src = { props . url }
@@ -235,14 +207,60 @@ function EmbeddedLink(props) {
235207 </ Show >
236208 ) ;
237209}
238- import { parseMarkdown as tokenizeMarkdown } from "./markdown_tokenizer.js" ;
239210
240211let keyCounter = 0 ;
241212
242213function getKey ( ) {
243214 return `key_${ keyCounter ++ } ` ;
244215}
245216
217+ function syntaxHighlight ( code , language ) {
218+ if ( ! language ) {
219+ return code ;
220+ }
221+
222+ try {
223+ return hljs . highlight ( code , { language } ) . value ;
224+ } catch ( e ) {
225+ return hljs . highlightAuto ( code ) . value ;
226+ }
227+ }
228+
229+ function CodeBlockDisplay ( props ) {
230+ const [ copied , setCopied ] = createSignal ( false ) ;
231+
232+ const handleCopy = ( ) => {
233+ navigator . clipboard . writeText ( props . code ) ;
234+ setCopied ( true ) ;
235+ setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
236+ } ;
237+
238+ const highlightedCode = syntaxHighlight ( props . code , props . language ) ;
239+
240+ return (
241+ < div class = "code_block_container" >
242+ < div class = "code_block_header" >
243+ < span class = "code_block_language" >
244+ { props . language || 'text' }
245+ </ span >
246+ < button
247+ class = "code_block_copy"
248+ onClick = { handleCopy }
249+ title = "Copy code"
250+ >
251+ { copied ( ) ? 'Copied' : 'Copy' }
252+ </ button >
253+ </ div >
254+ < pre class = "code_block_content" >
255+ < code
256+ class = { `hljs lang-${ props . language || 'text' } ` }
257+ innerHTML = { highlightedCode }
258+ />
259+ </ pre >
260+ </ div >
261+ ) ;
262+ }
263+
246264function renderToken ( token , depth = 0 ) {
247265 if ( ! token ) return null ;
248266
@@ -257,6 +275,13 @@ function renderToken(token, depth = 0) {
257275 case "text" :
258276 return token . value ;
259277
278+ case "italic" :
279+ return (
280+ < i key = { key } >
281+ { token . children ?. map ( ( t ) => renderToken ( t , depth + 1 ) ) }
282+ </ i >
283+ ) ;
284+
260285 case "bold" :
261286 return (
262287 < strong key = { key } >
@@ -281,9 +306,11 @@ function renderToken(token, depth = 0) {
281306
282307 case "codeBlock" :
283308 return (
284- < pre key = { key } class = "code_block" >
285- < code class = { `lang-${ token . language } ` } > { token . code } </ code >
286- </ pre >
309+ < CodeBlockDisplay
310+ key = { key }
311+ code = { token . code }
312+ language = { token . language }
313+ />
287314 ) ;
288315
289316 case "link" :
@@ -304,6 +331,7 @@ function renderToken(token, depth = 0) {
304331 }
305332 return < EmbeddedLink key = { key } url = { token . url } /> ;
306333 }
334+
307335 case "emoji" :
308336 return (
309337 < img
@@ -329,8 +357,10 @@ function renderToken(token, depth = 0) {
329357 />
330358 ) ;
331359 }
360+
332361 case "newline" :
333362 return < br key = { key } /> ;
363+
334364 case "sticker" :
335365 return (
336366 < img
@@ -340,6 +370,7 @@ function renderToken(token, depth = 0) {
340370 alt = ""
341371 />
342372 ) ;
373+
343374 case "timestamp" : {
344375 const date = new Date ( token . unix * 1000 ) ;
345376
0 commit comments