@@ -14,6 +14,7 @@ import type { HighlighterCore } from 'shiki'
14
14
import { getViewport } from '~/atoms/hooks'
15
15
import { AutoResizeHeight } from '~/components/modules/shared/AutoResizeHeight'
16
16
import { useMaskScrollArea } from '~/hooks/shared/use-mask-scrollarea'
17
+ import { stopPropagation } from '~/lib/dom'
17
18
import { clsxm } from '~/lib/helper'
18
19
19
20
import { MotionButtonBase } from '../../button'
@@ -97,15 +98,38 @@ export const ShikiHighLighter: FC<Props> = (props) => {
97
98
}
98
99
} , [ value , codeBlockRef ] )
99
100
100
- const renderedHtml = useMemo ( ( ) => {
101
+ const highlightedHtml = useMemo ( ( ) => {
101
102
if ( ! highlighter ) return ''
102
103
return codeHighlighter ( highlighter , {
103
104
attrs : attrs || '' ,
105
+ // code: `${value.split('\n')[0].repeat(10)} // [!code highlight]\n${value}`,
104
106
code : value ,
105
107
lang : language ? language . toLowerCase ( ) : '' ,
106
108
} )
107
109
} , [ attrs , language , value , highlighter ] )
108
110
111
+ const [ renderedHtml , setRenderedHtml ] = useState ( highlightedHtml )
112
+ useEffect ( ( ) => {
113
+ setRenderedHtml ( highlightedHtml )
114
+ requestAnimationFrame ( ( ) => {
115
+ if ( ! highlightedHtml ) return
116
+ if ( ! codeBlockRef ) return
117
+
118
+ const $lines = codeBlockRef . querySelectorAll ( '.line' )
119
+ const maxLineWidth = Math . max (
120
+ ...Array . from ( $lines ) . map ( ( el ) => {
121
+ return ( el as HTMLElement ) . scrollWidth
122
+ } ) ,
123
+ )
124
+ $lines . forEach ( ( el ) => {
125
+ ; ( el as HTMLElement ) . style . width = `${ maxLineWidth } px`
126
+ } )
127
+
128
+ const pre = codeBlockRef . querySelector ( 'pre' )
129
+ if ( pre ) setRenderedHtml ( pre . outerHTML )
130
+ } )
131
+ } , [ codeBlockRef , highlightedHtml ] )
132
+
109
133
const filename = useMemo ( ( ) => {
110
134
return parseFilenameFromAttrs ( attrs || '' )
111
135
} , [ attrs ] )
@@ -114,10 +138,15 @@ export const ShikiHighLighter: FC<Props> = (props) => {
114
138
size : 'lg' ,
115
139
} )
116
140
141
+ const hasHeader = ! ! filename
142
+
117
143
return (
118
- < div className = { clsx ( styles [ 'code-card' ] , 'group' ) } >
144
+ < div
145
+ className = { clsx ( styles [ 'code-card' ] , 'group' ) }
146
+ onCopy = { stopPropagation }
147
+ >
119
148
{ ! ! filename && (
120
- < div className = "z-10 flex w-full items-center justify-between rounded-t-xl bg-accent/20 px-4 py-2 text-sm" >
149
+ < div className = "z-10 flex w-full items-center justify-between rounded-t-xl bg-accent/20 px-5 py-2 text-sm" >
121
150
< span className = "shrink-0 flex-grow truncate" > { filename } </ span >
122
151
< span
123
152
className = "pointer-events-none flex-shrink-0 flex-grow-0"
@@ -136,7 +165,7 @@ export const ShikiHighLighter: FC<Props> = (props) => {
136
165
{ language . toUpperCase ( ) }
137
166
</ div >
138
167
) }
139
- < div className = "bg-accent/10 py-4" >
168
+ < div className = "bg-accent/5 py-4" >
140
169
< MotionButtonBase
141
170
onClick = { handleCopy }
142
171
className = { clsx (
@@ -156,6 +185,13 @@ export const ShikiHighLighter: FC<Props> = (props) => {
156
185
! isCollapsed ? '!max-h-[100%]' : isOverflow ? maskClassName : '' ,
157
186
styles [ 'scroll-container' ] ,
158
187
) }
188
+ style = {
189
+ {
190
+ '--sr-margin' : ! hasHeader
191
+ ? `${ ( language ?. length || 0 ) + 1 } em`
192
+ : '1rem' ,
193
+ } as any
194
+ }
159
195
dangerouslySetInnerHTML = {
160
196
renderedHtml
161
197
? {
@@ -165,8 +201,8 @@ export const ShikiHighLighter: FC<Props> = (props) => {
165
201
}
166
202
>
167
203
{ renderedHtml ? undefined : (
168
- < pre className = "bg-transparent px-4 " >
169
- < code className = "!px-4 " > { value } </ code >
204
+ < pre className = "bg-transparent px-5 " >
205
+ < code className = "!px-5 " > { value } </ code >
170
206
</ pre >
171
207
) }
172
208
</ div >
0 commit comments