@fsegurai/marked-extended-embeds 17.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,641 @@
1
+ <!-- SECTION:CUSTOM_INTRO -->
2
+ ---
3
+
4
+ This extension enables seamless integration of external content with automatic platform detection, responsive layouts,
5
+ privacy controls, and extensive customization options. Perfect for documentation, blogs, portfolios, and educational
6
+ content.
7
+
8
+ <!-- SECTION:CUSTOM_TOC_USAGE -->
9
+
10
+ - [Supported Platforms](#supported-platforms)
11
+
12
+ - [Configuration Options](#configuration-options)
13
+ - [Embed Properties](#embed-properties)
14
+ - [Advanced Examples](#advanced-examples)
15
+
16
+ <!-- SECTION:CUSTOM_BASIC_USAGE -->
17
+ **The extension automatically detects the platform from URLs or accepts explicit `provider` specification.**
18
+
19
+ <!-- SECTION:CUSTOM_USAGE_EXAMPLE -->
20
+ const markdown = `
21
+ ::::embed{title="My Video" aspectRatio="16:9"}
22
+ https://www.youtube.com/watch?v=dQw4w9WgXcQ
23
+ ::::embedend
24
+
25
+ ::::embed{provider="codepen" theme="dark"}
26
+ https://codepen.io/username/pen/abc123
27
+ ::::embedend
28
+
29
+ ::::embed{title="Spotify Playlist"}
30
+ https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
31
+ ::::embedend
32
+ `;
33
+
34
+ marked.parse(markdown);
35
+
36
+ <!-- SECTION:CUSTOM_USAGE_NOTES -->
37
+ The extension automatically detects providers, applies responsive layouts with proper aspect ratios, and includes
38
+ accessibility features. All embeds support lazy loading, sandbox security, and customizable styling.
39
+
40
+ ### Styling Your Embeds
41
+
42
+ This extension follows a **separation of concerns** approach, injecting only minimal structural CSS. Visual styling is completely separated for maximum flexibility.
43
+
44
+ #### Generated HTML Structure
45
+
46
+ ```html
47
+
48
+ <div class="marked-extended-embed-container"
49
+ data-provider="youtube"
50
+ data-embed-id="dQw4w9WgXcQ">
51
+ <!-- Loading state -->
52
+ <div class="marked-extended-embed-loading">
53
+ <div class="marked-extended-embed-spinner"></div>
54
+ <p>Loading...</p>
55
+ </div>
56
+
57
+ <!-- Embedded content (iframe) -->
58
+ <iframe
59
+ src="https://www.youtube.com/embed/dQw4w9WgXcQ"
60
+ loading="lazy"
61
+ sandbox="allow-scripts allow-same-origin"
62
+ allowfullscreen>
63
+ </iframe>
64
+
65
+ <!-- Optional caption -->
66
+ <div class="marked-extended-embed-caption">
67
+ Video description
68
+ </div>
69
+ </div>
70
+ ```
71
+
72
+ #### CSS Classes Reference
73
+
74
+ | Class | Purpose | Element |
75
+ |-------|---------|---------|
76
+ | `.marked-extended-embed-container` | Main wrapper | Container |
77
+ | `.marked-extended-embed-container[data-provider="youtube"]` | Provider-specific styling | Container |
78
+ | `.marked-extended-embed-loading` | Loading state overlay | Loading div |
79
+ | `.marked-extended-embed-spinner` | Loading spinner animation | Spinner div |
80
+ | `.marked-extended-embed-loading-text` | "Loading..." text | Text element |
81
+ | `.marked-extended-embed-caption` | Caption/description | Caption div |
82
+ | `iframe` | Embedded content | Iframe |
83
+
84
+ #### Complete Styling Example
85
+
86
+ ```css
87
+ /* Base Container */
88
+ .marked-extended-embed-container {
89
+ position: relative;
90
+ width: 100%;
91
+ margin: 1.5rem 0;
92
+ border-radius: 8px;
93
+ overflow: hidden;
94
+ background: #f5f5f5;
95
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
96
+ }
97
+
98
+ /* Responsive aspect ratio wrapper */
99
+ .marked-extended-embed-container::before {
100
+ content: '';
101
+ display: block;
102
+ padding-top: 56.25%; /* 16:9 aspect ratio */
103
+ }
104
+
105
+ .marked-extended-embed-container iframe {
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ width: 100%;
110
+ height: 100%;
111
+ border: none;
112
+ }
113
+
114
+ /* Loading State */
115
+ .marked-extended-embed-loading {
116
+ position: absolute;
117
+ top: 0;
118
+ left: 0;
119
+ width: 100%;
120
+ height: 100%;
121
+ display: flex;
122
+ flex-direction: column;
123
+ align-items: center;
124
+ justify-content: center;
125
+ background: rgba(0, 0, 0, 0.05);
126
+ z-index: 1;
127
+ }
128
+
129
+ .marked-extended-embed-spinner {
130
+ width: 40px;
131
+ height: 40px;
132
+ border: 4px solid #e0e0e0;
133
+ border-top-color: #2196F3;
134
+ border-radius: 50%;
135
+ animation: spin 1s linear infinite;
136
+ }
137
+
138
+ @keyframes spin {
139
+ 0% { transform: rotate(0deg); }
140
+ 100% { transform: rotate(360deg); }
141
+ }
142
+
143
+ .marked-extended-embed-loading-text {
144
+ margin-top: 1rem;
145
+ font-size: 0.875rem;
146
+ font-weight: 500;
147
+ color: #666;
148
+ }
149
+
150
+ /* Caption Styling */
151
+ .marked-extended-embed-caption {
152
+ padding: 0.75rem 1rem;
153
+ font-size: 0.875rem;
154
+ color: #666;
155
+ background: #fafafa;
156
+ border-top: 1px solid #e0e0e0;
157
+ text-align: center;
158
+ }
159
+
160
+ /* Provider-Specific Backgrounds */
161
+ .marked-extended-embed-container[data-provider="youtube"] {
162
+ background: #000;
163
+ }
164
+
165
+ .marked-extended-embed-container[data-provider="vimeo"] {
166
+ background: #000;
167
+ }
168
+
169
+ .marked-extended-embed-container[data-provider="codepen"] {
170
+ background: #1e1e1e;
171
+ }
172
+
173
+ .marked-extended-embed-container[data-provider="codesandbox"] {
174
+ background: #151515;
175
+ }
176
+
177
+ .marked-extended-embed-container[data-provider="spotify"] {
178
+ background: #191414;
179
+ }
180
+
181
+ .marked-extended-embed-container[data-provider="soundcloud"] {
182
+ background: #ff5500;
183
+ }
184
+
185
+ .marked-extended-embed-container[data-provider="twitter"] {
186
+ background: #fff;
187
+ }
188
+
189
+ .marked-extended-embed-container[data-provider="github-gist"] {
190
+ background: #f6f8fa;
191
+ }
192
+
193
+ .marked-extended-embed-container[data-provider="figma"] {
194
+ background: #fff;
195
+ }
196
+
197
+ .marked-extended-embed-container[data-provider="mermaid"] {
198
+ background: #fff;
199
+ padding: 1rem;
200
+ }
201
+
202
+ .marked-extended-embed-container[data-provider="pdf"] {
203
+ background: #525659;
204
+ }
205
+
206
+ .marked-extended-embed-container[data-provider="loom"] {
207
+ background: #000;
208
+ }
209
+
210
+ .marked-extended-embed-container[data-provider="miro"] {
211
+ background: #fff;
212
+ }
213
+
214
+ .marked-extended-embed-container[data-provider="excalidraw"] {
215
+ background: #fff;
216
+ }
217
+
218
+ .marked-extended-embed-container[data-provider="drawio"],
219
+ .marked-extended-embed-container[data-provider="diagrams-net"] {
220
+ background: #fff;
221
+ }
222
+ ```
223
+
224
+ #### Dark Mode Support
225
+
226
+ ```css
227
+ /* Light theme */
228
+ body.light .marked-extended-embed-container {
229
+ background: #f5f5f5;
230
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
231
+ }
232
+
233
+ body.light .marked-extended-embed-caption {
234
+ background: #fafafa;
235
+ color: #666;
236
+ border-top-color: #e0e0e0;
237
+ }
238
+
239
+ body.light .marked-extended-embed-loading {
240
+ color: #666;
241
+ }
242
+
243
+ body.light .marked-extended-embed-spinner {
244
+ border-color: #e0e0e0;
245
+ border-top-color: #2196F3;
246
+ }
247
+
248
+ /* Dark theme */
249
+ body.dark .marked-extended-embed-container {
250
+ background: #1e1e1e;
251
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
252
+ }
253
+
254
+ body.dark .marked-extended-embed-caption {
255
+ background: #2a2a2a;
256
+ color: #e0e0e0;
257
+ border-top-color: #3a3a3a;
258
+ }
259
+
260
+ body.dark .marked-extended-embed-loading {
261
+ color: #ccc;
262
+ }
263
+
264
+ body.dark .marked-extended-embed-spinner {
265
+ border-color: #333;
266
+ border-top-color: #2196F3;
267
+ }
268
+
269
+ /* Provider-specific dark mode overrides */
270
+ body.dark .marked-extended-embed-container[data-provider="mermaid"],
271
+ body.dark .marked-extended-embed-container[data-provider="twitter"],
272
+ body.dark .marked-extended-embed-container[data-provider="figma"],
273
+ body.dark .marked-extended-embed-container[data-provider="miro"],
274
+ body.dark .marked-extended-embed-container[data-provider="excalidraw"],
275
+ body.dark .marked-extended-embed-container[data-provider="drawio"],
276
+ body.dark .marked-extended-embed-container[data-provider="diagrams-net"] {
277
+ background: #1e1e1e;
278
+ }
279
+
280
+ body.dark .marked-extended-embed-container[data-provider="github-gist"] {
281
+ background: #0d1117;
282
+ }
283
+ ```
284
+
285
+ #### Custom Aspect Ratios
286
+
287
+ ```css
288
+ /* 16:9 (default - videos) */
289
+ .marked-extended-embed-container[data-provider="youtube"]::before,
290
+ .marked-extended-embed-container[data-provider="vimeo"]::before,
291
+ .marked-extended-embed-container[data-provider="loom"]::before {
292
+ padding-top: 56.25%;
293
+ }
294
+
295
+ /* 1:1 (square - music players) */
296
+ .marked-extended-embed-container[data-provider="spotify"]::before,
297
+ .marked-extended-embed-container[data-provider="soundcloud"]::before {
298
+ padding-top: 100%;
299
+ }
300
+
301
+ /* 4:3 (presentations) */
302
+ .marked-extended-embed-container[data-provider="google-slides"]::before {
303
+ padding-top: 75%;
304
+ }
305
+
306
+ /* Full height (code editors) */
307
+ .marked-extended-embed-container[data-provider="codepen"]::before,
308
+ .marked-extended-embed-container[data-provider="codesandbox"]::before,
309
+ .marked-extended-embed-container[data-provider="replit"]::before {
310
+ padding-top: 100%;
311
+ min-height: 500px;
312
+ }
313
+ ```
314
+
315
+ #### Responsive Adjustments
316
+
317
+ ```css
318
+ @media (max-width: 768px) {
319
+ .marked-extended-embed-container {
320
+ margin: 1rem 0;
321
+ border-radius: 4px;
322
+ }
323
+
324
+ .marked-extended-embed-caption {
325
+ padding: 0.5rem 0.75rem;
326
+ font-size: 0.8125rem;
327
+ }
328
+
329
+ /* Reduce height for code editors on mobile */
330
+ .marked-extended-embed-container[data-provider="codepen"]::before,
331
+ .marked-extended-embed-container[data-provider="codesandbox"]::before {
332
+ min-height: 400px;
333
+ }
334
+ }
335
+ ```
336
+
337
+ #### Print Styles
338
+
339
+ ```css
340
+ @media print {
341
+ .marked-extended-embed-container {
342
+ break-inside: avoid;
343
+ box-shadow: none;
344
+ border: 1px solid #ccc;
345
+ }
346
+
347
+ .marked-extended-embed-loading {
348
+ display: none;
349
+ }
350
+
351
+ /* Show caption */
352
+ .marked-extended-embed-caption {
353
+ display: block !important;
354
+ }
355
+
356
+ /* Hide interactive embeds, show placeholder */
357
+ .marked-extended-embed-container iframe {
358
+ display: none;
359
+ }
360
+
361
+ .marked-extended-embed-container::after {
362
+ content: "Embedded content: " attr(data-provider);
363
+ display: block;
364
+ padding: 2rem;
365
+ text-align: center;
366
+ color: #666;
367
+ font-style: italic;
368
+ }
369
+ }
370
+ ```
371
+
372
+ #### Copy Demo Theme
373
+
374
+ For complete styling with all 18+ providers: [embed-theme.css](https://github.com/fsegurai/marked-extensions/blob/main/demo/styles/extensions/embed-theme.css)
375
+
376
+ Check the [demo](https://fsegurai.github.io/marked-extensions) to see all embed types in action.
377
+
378
+ <!-- SECTION:CUSTOM_SECTIONS -->
379
+
380
+ ### Supported Platforms
381
+
382
+ The extension supports **18 different platforms** with automatic URL detection:
383
+
384
+ | Platform | Type | Example URL |
385
+ |------------------|---------------|-------------------------------|
386
+ | **YouTube** | Video | `youtube.com/watch?v=...` |
387
+ | **Vimeo** | Video | `vimeo.com/123456789` |
388
+ | **CodePen** | Code | `codepen.io/user/pen/abc` |
389
+ | **CodeSandbox** | Code | `codesandbox.io/s/abc` |
390
+ | **Twitter** | Social | `twitter.com/user/status/123` |
391
+ | **GitHub Gist** | Code | `gist.github.com/user/abc` |
392
+ | **Spotify** | Music | `open.spotify.com/track/abc` |
393
+ | **SoundCloud** | Music | `soundcloud.com/user/track` |
394
+ | **SlideShare** | Presentation | `slideshare.net/user/slides` |
395
+ | **Figma** | Design | `figma.com/file/abc` |
396
+ | **Loom** | Video | `loom.com/share/abc` |
397
+ | **Miro** | Collaboration | `miro.com/app/board/abc` |
398
+ | **Mermaid** | Diagram | `mermaid://graph TD; A-->B;` |
399
+ | **Excalidraw** | Drawing | `excalidraw.com/#room=abc` |
400
+ | **Draw.io** | Diagram | `diagrams.net/...` |
401
+ | **Diagrams.net** | Diagram | `diagrams.net/...` |
402
+ | **PDF** | Document | Any PDF URL |
403
+ | **iframe** | Generic | Any URL |
404
+
405
+ Each platform is automatically detected from the URL pattern. You can also explicitly specify the provider using the
406
+ `provider` property.
407
+
408
+ ## Configuration Options
409
+
410
+ The marked-extended-embeds extension accepts the following configuration options:
411
+
412
+ - `className`: The base CSS class name for embeds. Defaults to 'marked-extended-embed.'
413
+ - `prefixId`: The prefix ID for embed elements. Defaults to 'embed'.
414
+ - `defaultAspectRatio`: Default aspect ratio ('16:9', '4:3', '1:1', '21:9'). Defaults to '16:9'.
415
+ - `allowFullscreen`: Allow fullscreen mode for embeds. Defaults to true.
416
+ - `lazyLoad`: Enable lazy loading for better performance. Defaults to true.
417
+ - `privacyMode`: Use privacy-enhanced domains when available. Defaults to false.
418
+ - `template`: Custom HTML template for rendering embeds. Defaults to built-in template.
419
+ - `customizeToken`: Function to customize embed tokens. Defaults to null.
420
+ - `injectStyles`: Whether to inject default styles. Defaults to true.
421
+ - `enableSandbox`: Enable iframe sandbox for security. Defaults to true.
422
+ - `sandboxPermissions`: Array of sandbox permissions. Defaults
423
+ to ['allow-scripts', 'allow-same-origin', 'allow-popups', 'allow-presentation'].
424
+ - `providers`: Custom provider configurations. Defaults to {}.
425
+ - `onEmbedLoad`: Callback when embed loads. Defaults to null.
426
+ - `onEmbedError`: Callback when embed fails. Defaults to null.
427
+
428
+ ### Embed Properties
429
+
430
+ Embed syntax supports the following properties:
431
+
432
+ **Common Properties:**
433
+
434
+ - `title`: Title/caption for the embed
435
+ - `aspectRatio`: Aspect ratio ('16:9', '4:3', '1:1', '21:9', 'custom')
436
+ - `width`: Custom width (e.g., '800px')
437
+ - `height`: Custom height (e.g., '600px')
438
+ - `provider`: Explicit provider name
439
+ - `id`: Custom identifier for the embed
440
+ - `className`: Custom CSS class
441
+
442
+ **Media Properties:**
443
+
444
+ - `autoplay`: Auto-play video/audio (boolean)
445
+ - `muted`: Start muted (boolean)
446
+ - `loop`: Loop playback (boolean)
447
+ - `startTime`: Start time for videos (e.g., '30')
448
+ - `controls`: Show media controls (boolean)
449
+
450
+ **Display Properties:**
451
+
452
+ - `lazyLoad`: Enable lazy loading (boolean)
453
+ - `allowFullscreen`: Allow fullscreen mode (boolean)
454
+ - `privacyMode`: Use privacy-enhanced mode (boolean)
455
+ - `theme`: Theme setting ('light', 'dark')
456
+
457
+ **Custom Parameters:**
458
+
459
+ - `param-*`: Any property starting with 'param-' becomes a URL parameter
460
+
461
+ ### Advanced Examples
462
+
463
+ #### YouTube Video with Options
464
+
465
+ ```markdown
466
+ ::::embed{
467
+ title="Tutorial Video"
468
+ aspectRatio="16:9"
469
+ autoplay="true"
470
+ muted="true"
471
+ startTime="30"
472
+ privacyMode="true"
473
+ }
474
+ https://www.youtube.com/watch?v=dQw4w9WgXcQ
475
+ ::::embedend
476
+ ```
477
+
478
+ #### CodePen with Dark Theme
479
+
480
+ ```markdown
481
+ ::::embed{title="Interactive Demo" theme="dark"}
482
+ https://codepen.io/username/pen/abc123
483
+ ::::embedend
484
+ ```
485
+
486
+ #### Responsive Design File
487
+
488
+ ```markdown
489
+ ::::embed{
490
+ title="Mobile App Design"
491
+ aspectRatio="4:3"
492
+ width="100%"
493
+ }
494
+ https://www.figma.com/file/abc123/Mobile-App
495
+ ::::embedend
496
+ ```
497
+
498
+ #### Spotify Playlist
499
+
500
+ ```markdown
501
+ ::::embed{title="Coding Playlist" aspectRatio="1:1"}
502
+ https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
503
+ ::::embedend
504
+ ```
505
+
506
+ #### CodeSandbox Project
507
+
508
+ ```markdown
509
+ ::::embed{
510
+ title="React Example"
511
+ theme="dark"
512
+ param-view="preview"
513
+ }
514
+ https://codesandbox.io/s/react-new
515
+ ::::embedend
516
+ ```
517
+
518
+ #### Mermaid Diagram
519
+
520
+ ```markdown
521
+ ::::embed{provider="mermaid" title="System Architecture"}
522
+ mermaid://graph TD;
523
+ A[Client] -->|Request| B[API Gateway];
524
+ B --> C[Service 1];
525
+ B --> D[Service 2];
526
+ C --> E[Database];
527
+ D --> E;
528
+ ::::embedend
529
+ ```
530
+
531
+ #### PDF Document
532
+
533
+ ```markdown
534
+ ::::embed{
535
+ provider="pdf"
536
+ title="Technical Specification"
537
+ aspectRatio="4:3"
538
+ }
539
+ https://example.com/document.pdf
540
+ ::::embedend
541
+ ```
542
+
543
+ #### Loom Video with Custom Size
544
+
545
+ ```markdown
546
+ ::::embed{
547
+ title="Product Demo"
548
+ width="800px"
549
+ height="450px"
550
+ }
551
+ https://www.loom.com/share/abc123
552
+ ::::embedend
553
+ ```
554
+
555
+ #### Twitter Thread
556
+
557
+ ```markdown
558
+ ::::embed{title="Latest Announcement"}
559
+ https://twitter.com/username/status/1234567890
560
+ ::::embedend
561
+ ```
562
+
563
+ #### GitHub Gist
564
+
565
+ ```markdown
566
+ ::::embed{title="Code Snippet"}
567
+ https://gist.github.com/username/abc123
568
+ ::::embedend
569
+ ```
570
+
571
+ #### Excalidraw Sketch
572
+
573
+ ```markdown
574
+ ::::embed{title="Whiteboard Sketch" aspectRatio="16:9"}
575
+ https://excalidraw.com/#room=abc123,def456
576
+ ::::embedend
577
+ ```
578
+
579
+ #### Draw.io Diagram
580
+
581
+ ```markdown
582
+ ::::embed{provider="drawio" title="System Architecture" aspectRatio="4:3"}
583
+ https://viewer.diagrams.net/?highlight=0000ff&edit=_blank&title=diagram
584
+ ::::embedend
585
+ ```
586
+
587
+ ## Privacy Mode
588
+
589
+ Enable privacy-enhanced embedding for supported platforms:
590
+
591
+ ```markdown
592
+ ::::embed{privacyMode="true"}
593
+ https://www.youtube.com/watch?v=abc123
594
+ ::::embedend
595
+ ```
596
+
597
+ This uses `youtube-nocookie.com` for YouTube and similar privacy-focused domains when available.
598
+
599
+ ## Custom Provider Configuration
600
+
601
+ Configure provider-specific settings:
602
+
603
+ ```javascript
604
+ marked.use(markedExtendedEmbeds({
605
+ providers: {
606
+ youtube: {
607
+ domain: 'www.youtube-nocookie.com',
608
+ params: {
609
+ rel: '0',
610
+ modestbranding: '1',
611
+ },
612
+ },
613
+ spotify: {
614
+ params: {
615
+ theme: '0',
616
+ },
617
+ },
618
+ },
619
+ }));
620
+ ```
621
+
622
+ ## Security Features
623
+
624
+ The extension includes several security features:
625
+
626
+ - **Sandbox by default**: Iframes use sandbox attribute with restricted permissions
627
+ - **Configurable permissions**: Customize allowed iframe capabilities
628
+ - **Privacy mode**: Use no-cookie domains when available
629
+ - **XSS protection**: All user input is escaped
630
+ - **HTTPS enforcement**: Embed URLs use secure protocols
631
+
632
+ ## Accessibility
633
+
634
+ All embeds include proper accessibility features:
635
+
636
+ - Semantic HTML with proper roles
637
+ - Alt text and titles
638
+ - Keyboard navigation support
639
+ - Screen reader compatibility
640
+ - Loading states with aria-labels
641
+