@@ -13,6 +13,8 @@ export class GlMarkdown extends LitElement {
1313 ruleStyles ,
1414 css `
1515 : host {
16+ dis play: contents;
17+
1618 - - markdown- compact- block- spacing: 8px;
1719 - - markdown- lis t- spacing: 20px;
1820 }
@@ -105,34 +107,55 @@ export class GlMarkdown extends LitElement {
105107 li > ul {
106108 margin-top : 0 ;
107109 }
108- ` ,
110+ = `,
109111 ] ;
110112
111113 @property ( { type : String } )
112- private markdown = '' ;
114+ markdown = '' ;
113115
114116 @property ( { type : String , reflect : true } )
115117 density : 'compact' | 'document' = 'compact' ;
116118
119+ @property ( { type : Boolean , reflect : true } )
120+ inline = false ;
121+
117122 override render ( ) : unknown {
118123 return html `${ this . markdown ? until ( this . renderMarkdown ( this . markdown ) , 'Loading...' ) : '' } ` ;
119124 }
120125
121126 private async renderMarkdown ( markdown : string ) {
122- marked . setOptions ( {
123- gfm : true ,
124- // smartypants: true,
125- // langPrefix: 'language-',
126- } ) ;
127+ marked . setOptions ( { gfm : true } ) ;
127128
128- marked . use ( { renderer : getMarkdownRenderer ( ) } ) ;
129+ const renderer = this . inline ? getInlineMarkdownRenderer ( ) : getMarkdownRenderer ( ) ;
130+ marked . use ( { renderer : renderer } ) ;
129131
130132 let rendered = await marked . parse ( markdownEscapeEscapedIcons ( markdown ) ) ;
131133 rendered = renderThemeIconsWithinText ( rendered ) ;
132134 return unsafeHTML ( rendered ) ;
133135 }
134136}
135137
138+ const escapeReplacements : { [ index : string ] : string } = {
139+ '&' : '&' ,
140+ '<' : '<' ,
141+ '>' : '>' ,
142+ '"' : '"' ,
143+ "'" : ''' ,
144+ } ;
145+ const getEscapeReplacement = ( ch : string ) => escapeReplacements [ ch ] ;
146+
147+ export function escape ( html : string , encode ?: boolean ) {
148+ if ( encode ) {
149+ if ( / [ & < > " ' ] / . test ( html ) ) {
150+ return html . replace ( / [ & < > " ' ] / g, getEscapeReplacement ) ;
151+ }
152+ } else if ( / [ < > " ' ] | & (? ! ( # \d { 1 , 7 } | # [ X x ] [ a - f A - F 0 - 9 ] { 1 , 6 } | \w + ) ; ) / . test ( html ) ) {
153+ return html . replace ( / [ < > " ' ] | & (? ! ( # \d { 1 , 7 } | # [ X x ] [ a - f A - F 0 - 9 ] { 1 , 6 } | \w + ) ; ) / g, getEscapeReplacement ) ;
154+ }
155+
156+ return html ;
157+ }
158+
136159function getMarkdownRenderer ( ) : RendererObject {
137160 return {
138161 image : function ( this : RendererThis , { href, title, text } : Tokens . Image ) : string {
@@ -157,37 +180,69 @@ function getMarkdownRenderer(): RendererObject {
157180 const text = this . parser . parseInline ( tokens ) ;
158181 return `<p>${ text } </p>` ;
159182 } ,
160- link : function ( this : RendererThis , { href, title, tokens } : Tokens . Link ) : string | false {
161- if ( typeof href !== 'string' ) return '' ;
162-
163- // Remove markdown escapes. Workaround for https://github.com/chjj/marked/issues/829
164- let text = this . parser . parseInline ( tokens ) ;
165- if ( href === text ) {
166- // raw link case
167- text = removeMarkdownEscapes ( text ) ;
168- }
169-
170- title = typeof title === 'string' ? escapeDoubleQuotes ( removeMarkdownEscapes ( title ) ) : '' ;
183+ html : function ( this : RendererThis , { text } : Tokens . HTML | Tokens . Tag ) : string {
184+ const match = text . match ( / ^ ( < s p a n [ ^ > ] + > ) | ( < \/ \s * s p a n > ) $ / ) ;
185+ return match ? text : '' ;
186+ } ,
187+ } ;
188+ }
171189
172- // HTML Encode href
173- href = removeMarkdownEscapes ( href )
174- . replace ( / & / g, '&' )
175- . replace ( / < / g, '<' )
176- . replace ( / > / g, '>' )
177- . replace ( / " / g, '"' )
178- . replace ( / ' / g, ''' ) ;
190+ function getInlineMarkdownRenderer ( ) : RendererObject {
191+ let listIndex = 0 ;
192+ let isOrderedList = false ;
193+
194+ const renderListItem = function ( this : RendererThis , item : Tokens . ListItem ) : string {
195+ // In inline mode, render list item with symbol prefix
196+ const text = this . parser . parse ( item . tokens , Boolean ( item . loose ) ) ;
197+ // Get the symbol: task checkbox, number for ordered, bullet for unordered
198+ let symbol : string ;
199+ if ( item . task ) {
200+ symbol = item . checked ? '☑' : '☐' ;
201+ } else if ( isOrderedList ) {
202+ symbol = `${ listIndex } .` ;
203+ listIndex ++ ;
204+ } else {
205+ symbol = '•' ;
206+ }
207+ return `${ symbol } ${ text . trim ( ) } ` ;
208+ } ;
179209
180- return `<a href="${ href } " title="${ title || href } " draggable="false">${ text } </a>` ;
210+ return {
211+ image : function ( this : RendererThis , { text } : Tokens . Image ) : string {
212+ // In inline mode, use alt text if available, otherwise skip
213+ return text || '' ;
214+ } ,
215+ paragraph : function ( this : RendererThis , { tokens } : Tokens . Paragraph ) : string {
216+ const text = this . parser . parseInline ( tokens ) ;
217+ return text ;
181218 } ,
182- code : function ( this : RendererThis , { text, lang } : Tokens . Code ) : string {
183- // Remote code may include characters that need to be escaped to be visible in HTML
184- text = text . replace ( / < / g, '<' ) ;
185- return `<pre class="language-${ lang } "><code>${ text } </code></pre>` ;
219+ list : function ( this : RendererThis , token : Tokens . List ) : string {
220+ // In inline mode, render list items separated by spaces with their symbols
221+ isOrderedList = token . ordered ;
222+ listIndex = typeof token . start === 'number' ? token . start : 1 ;
223+ let body = '' ;
224+ for ( const item of token . items ) {
225+ body += renderListItem . call ( this , item ) ;
226+ }
227+ return body ;
228+ } ,
229+ listitem : renderListItem ,
230+ link : function ( this : RendererThis , { tokens } : Tokens . Link ) : string | false {
231+ const text = this . parser . parseInline ( tokens ) ;
232+ return text ;
186233 } ,
187- codespan : function ( this : RendererThis , { text } : Tokens . Codespan ) : string {
188- // Remote code may include characters that need to be escaped to be visible in HTML
189- text = text . replace ( / < / g, '<' ) ;
190- return `<code>${ text } </code>` ;
234+ code : function ( this : RendererThis , { text } : Tokens . Code ) : string {
235+ // In inline mode, wrap in code tag but without pre block formatting
236+ return `<code>${ escape ( text , true ) } </code>` ;
237+ } ,
238+
239+ br : function ( ) : string {
240+ // In inline mode, render as a space instead of line break
241+ return ' ' ;
242+ } ,
243+ html : function ( ) : string {
244+ // In inline mode, skip HTML tags
245+ return '' ;
191246 } ,
192247 } ;
193248}
@@ -264,10 +319,3 @@ function renderThemeIcon(icon: ThemeIcon): string {
264319function escapeDoubleQuotes ( input : string ) {
265320 return input . replace ( / " / g, '"' ) ;
266321}
267-
268- function removeMarkdownEscapes ( text : string ) : string {
269- if ( ! text ) {
270- return text ;
271- }
272- return text . replace ( / \\ ( [ \\ ` * _ { } [ \] ( ) # + \- . ! ~ ] ) / g, '$1' ) ;
273- }
0 commit comments