@@ -66,6 +66,60 @@ function matter(code: string, options: SlidevParserOptions) {
6666 }
6767}
6868
69+ const IMAGE_EXTENSIONS = / \. (?: p n g | j p e ? g | g i f | s v g | w e b p | a v i f | i c o | b m p | t i f f ? ) $ / i
70+
71+ /**
72+ * Extract image URLs from slide content and frontmatter.
73+ * Strips code blocks first to avoid false positives.
74+ */
75+ export function extractImages ( content : string , frontmatter : Record < string , any > ) : string [ ] {
76+ const images = new Set < string > ( )
77+
78+ // Collect from frontmatter keys
79+ for ( const key of [ 'image' , 'backgroundImage' , 'background' ] ) {
80+ const val = frontmatter [ key ]
81+ if ( typeof val === 'string' && val && ! val . startsWith ( 'data:' ) ) {
82+ // For `background`, only include if it looks like an image URL
83+ if ( key === 'background' ) {
84+ if ( IMAGE_EXTENSIONS . test ( val ) || val . startsWith ( '/' ) || val . startsWith ( 'http' ) )
85+ images . add ( val )
86+ }
87+ else {
88+ images . add ( val )
89+ }
90+ }
91+ }
92+
93+ // Strip code blocks to avoid false positives
94+ const stripped = content . replace ( / ^ ` ` ` [ \s \S ] + ?^ ` ` ` / gm, '' )
95+
96+ // Markdown images: 
97+ for ( const [ , url ] of stripped . matchAll ( / ! \[ [ ^ \] ] * \] \( ( [ ^ ) ] + ) \) / g) ) {
98+ if ( url && ! url . startsWith ( 'data:' ) )
99+ images . add ( url . trim ( ) )
100+ }
101+
102+ // Vue component props: src="url", image="url"
103+ for ( const [ , url ] of stripped . matchAll ( / \b (?: s r c | i m a g e ) = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / g) ) {
104+ if ( url && ! url . startsWith ( 'data:' ) && ! url . includes ( '{{' ) && IMAGE_EXTENSIONS . test ( url ) )
105+ images . add ( url . trim ( ) )
106+ }
107+
108+ // Vue bound props: :src="'/path/to/img.png'"
109+ for ( const [ , url ] of stripped . matchAll ( / : (?: s r c | i m a g e ) = [ " ' ] ' ( [ ^ ' ] + ) ' [ " ' ] / g) ) {
110+ if ( url && ! url . startsWith ( 'data:' ) && IMAGE_EXTENSIONS . test ( url ) )
111+ images . add ( url . trim ( ) )
112+ }
113+
114+ // CSS url() with image extension filter
115+ for ( const [ , url ] of stripped . matchAll ( / u r l \( [ " ' ] ? ( [ ^ " ' ) ] + ) [ " ' ] ? \) / g) ) {
116+ if ( url && ! url . startsWith ( 'data:' ) && IMAGE_EXTENSIONS . test ( url ) )
117+ images . add ( url . trim ( ) )
118+ }
119+
120+ return Array . from ( images )
121+ }
122+
69123export function detectFeatures ( code : string ) : SlidevDetectedFeatures {
70124 return {
71125 katex : ! ! code . match ( / \$ .* ?\$ / ) || ! ! code . match ( / \$ \$ / ) ,
@@ -104,6 +158,8 @@ export function parseSlide(raw: string, options: SlidevParserOptions = {}): Omit
104158 if ( frontmatter . level )
105159 level = frontmatter . level || 1
106160
161+ const images = extractImages ( content , frontmatter )
162+
107163 return {
108164 raw,
109165 title,
@@ -116,6 +172,7 @@ export function parseSlide(raw: string, options: SlidevParserOptions = {}): Omit
116172 frontmatterDoc : matterResult . doc ,
117173 frontmatterRaw : matterResult . raw ,
118174 note,
175+ images,
119176 }
120177}
121178
0 commit comments