1
- import { Plugin , Notice , Modal , App } from "obsidian" ;
1
+ import { Plugin , Notice , Modal , App , MarkdownRenderer } from "obsidian" ;
2
2
3
3
interface Snippet {
4
4
id : string ;
@@ -240,6 +240,29 @@ class CssSnippetStoreModal extends Modal {
240
240
241
241
card . createDiv ( { cls : 'snippet-store-button-wrapper' } ) ;
242
242
243
+ card . addEventListener ( 'click' , async ( event ) => {
244
+ // Prevent click events on buttons inside the card from triggering README modal
245
+ if ( ( event . target as HTMLElement ) . tagName . toLowerCase ( ) === 'button' ) return ;
246
+
247
+ const readmeUrl = `https://raw.githubusercontent.com/${ snippet . repo } /refs/heads/main/${ snippet . folder } /README.md` ;
248
+ try {
249
+ if ( await isOnline ( ) ) {
250
+ const response = await fetchWithTimeout ( readmeUrl ) ;
251
+ if ( ! response . ok ) {
252
+ new Notice ( `Could not fetch README: ${ response . statusText } ` ) ;
253
+ return ;
254
+ }
255
+ const readme = await response . text ( ) ;
256
+ new SnippetReadmeModal ( this . app , snippet , readme ) . open ( ) ;
257
+ } else {
258
+ new Notice ( "No Internet connection..." ) ;
259
+ }
260
+ } catch ( error ) {
261
+ console . error ( error ) ;
262
+ new Notice ( `Error fetching README: ${ error . message } ` ) ;
263
+ }
264
+ } ) ;
265
+
243
266
// Now update just the button based on snippet state
244
267
this . updateSnippetCard ( snippet ) ;
245
268
} ) ;
@@ -320,4 +343,67 @@ export async function isOnline(timeout = 3000): Promise<boolean> {
320
343
} catch ( e ) {
321
344
return false ;
322
345
}
346
+ }
347
+
348
+ class SnippetReadmeModal extends Modal {
349
+ constructor (
350
+ app : App ,
351
+ private snippet : Snippet ,
352
+ private readmeContent : string
353
+ ) {
354
+ super ( app ) ;
355
+ }
356
+
357
+ async onOpen ( ) {
358
+ const { contentEl } = this ;
359
+ contentEl . empty ( ) ;
360
+ contentEl . addClass ( "snippet-readme-modal" ) ;
361
+ this . modalEl . style . width = "80vw" ;
362
+ this . modalEl . style . height = "80vh" ;
363
+
364
+ // Title
365
+ contentEl . createEl ( "h2" , {
366
+ text : `${ this . snippet . name } – README` ,
367
+ } ) ;
368
+
369
+ // Rewrite relative image paths to absolute GitHub raw URLs
370
+ const adjustedContent = this . rewriteRelativeMediaPaths ( this . readmeContent ) ;
371
+
372
+ // Markdown container
373
+ const markdownContainer = contentEl . createDiv ( ) ;
374
+ markdownContainer . style . overflowY = "auto" ;
375
+ markdownContainer . style . maxHeight = "65vh" ;
376
+ markdownContainer . style . padding = "1rem" ;
377
+ markdownContainer . style . backgroundColor = "var(--background-secondary)" ;
378
+ markdownContainer . style . borderRadius = "8px" ;
379
+
380
+ // Render Markdown using Obsidian's renderer
381
+ await MarkdownRenderer . renderMarkdown (
382
+ adjustedContent ,
383
+ markdownContainer ,
384
+ "" ,
385
+ this
386
+ ) ;
387
+
388
+ markdownContainer . querySelectorAll ( "img" ) . forEach ( ( img ) => {
389
+ img . style . maxWidth = "100%" ;
390
+ img . style . height = "auto" ;
391
+ img . style . display = "block" ;
392
+ img . style . margin = "1rem auto" ; // Optional: center images
393
+ } ) ;
394
+ }
395
+
396
+ onClose ( ) {
397
+ this . contentEl . empty ( ) ;
398
+ }
399
+
400
+ private rewriteRelativeMediaPaths ( content : string ) : string {
401
+ const base = `https://raw.githubusercontent.com/${ this . snippet . repo } /refs/heads/main/${ this . snippet . folder } /` ;
402
+
403
+ // Regex to match image/video markdown with relative path
404
+ return content . replace ( / ! \[ ( [ ^ \] ] * ) \] \( ( \. \/ [ ^ ) ] + ) \) / g, ( match , alt , relPath ) => {
405
+ const url = base + relPath . replace ( "./" , "" ) ;
406
+ return `` ;
407
+ } ) ;
408
+ }
323
409
}
0 commit comments