@terrymooreii/sia 2.1.1 → 2.1.3

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.
@@ -818,6 +818,46 @@ img {
818
818
  box-shadow: var(--shadow-md);
819
819
  }
820
820
 
821
+ /* YouTube Embed Styles */
822
+ .prose .youtube-embed {
823
+ position: relative;
824
+ padding-bottom: 56.25%; /* 16:9 aspect ratio */
825
+ height: 0;
826
+ overflow: hidden;
827
+ margin: 1.5em 0;
828
+ border-radius: var(--radius-md);
829
+ box-shadow: var(--shadow-md);
830
+ }
831
+
832
+ .prose .youtube-embed iframe {
833
+ position: absolute;
834
+ top: 0;
835
+ left: 0;
836
+ width: 100%;
837
+ height: 100%;
838
+ border: 0;
839
+ }
840
+
841
+ /* Giphy Embed Styles */
842
+ .prose .giphy-embed {
843
+ position: relative;
844
+ padding-bottom: 100%; /* Giphy embeds are typically square */
845
+ height: 0;
846
+ overflow: hidden;
847
+ margin: 1.5em 0;
848
+ border-radius: var(--radius-md);
849
+ box-shadow: var(--shadow-md);
850
+ }
851
+
852
+ .prose .giphy-embed iframe {
853
+ position: absolute;
854
+ top: 0;
855
+ left: 0;
856
+ width: 100%;
857
+ height: 100%;
858
+ border: 0;
859
+ }
860
+
821
861
  .prose a {
822
862
  text-decoration: underline;
823
863
  text-decoration-color: var(--color-border);
@@ -796,6 +796,44 @@ img {
796
796
  border-radius: var(--radius-md);
797
797
  }
798
798
 
799
+ /* YouTube Embed Styles */
800
+ .prose .youtube-embed {
801
+ position: relative;
802
+ padding-bottom: 56.25%; /* 16:9 aspect ratio */
803
+ height: 0;
804
+ overflow: hidden;
805
+ margin: 1.25em 0;
806
+ border-radius: var(--radius-md);
807
+ }
808
+
809
+ .prose .youtube-embed iframe {
810
+ position: absolute;
811
+ top: 0;
812
+ left: 0;
813
+ width: 100%;
814
+ height: 100%;
815
+ border: 0;
816
+ }
817
+
818
+ /* Giphy Embed Styles */
819
+ .prose .giphy-embed {
820
+ position: relative;
821
+ padding-bottom: 100%; /* Giphy embeds are typically square */
822
+ height: 0;
823
+ overflow: hidden;
824
+ margin: 1.25em 0;
825
+ border-radius: var(--radius-md);
826
+ }
827
+
828
+ .prose .giphy-embed iframe {
829
+ position: absolute;
830
+ top: 0;
831
+ left: 0;
832
+ width: 100%;
833
+ height: 100%;
834
+ border: 0;
835
+ }
836
+
799
837
  .prose a {
800
838
  text-decoration: underline;
801
839
  text-decoration-color: var(--color-border);
package/lib/content.js CHANGED
@@ -120,6 +120,133 @@ marked.setOptions({
120
120
  breaks: false
121
121
  });
122
122
 
123
+ /**
124
+ * Extract YouTube video ID from various URL formats
125
+ * Supports:
126
+ * - https://www.youtube.com/watch?v=VIDEO_ID
127
+ * - https://youtu.be/VIDEO_ID
128
+ * - https://www.youtube.com/embed/VIDEO_ID
129
+ * - https://youtube.com/watch?v=VIDEO_ID
130
+ * - VIDEO_ID (if it's just an 11-character alphanumeric string)
131
+ */
132
+ function extractYouTubeId(url) {
133
+ if (!url) return null;
134
+
135
+ // If it's just a video ID (11 characters, alphanumeric)
136
+ if (/^[a-zA-Z0-9_-]{11}$/.test(url)) {
137
+ return url;
138
+ }
139
+
140
+ // Match various YouTube URL patterns
141
+ const patterns = [
142
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
143
+ /youtube\.com\/.*[?&]v=([a-zA-Z0-9_-]{11})/
144
+ ];
145
+
146
+ for (const pattern of patterns) {
147
+ const match = url.match(pattern);
148
+ if (match && match[1]) {
149
+ return match[1];
150
+ }
151
+ }
152
+
153
+ return null;
154
+ }
155
+
156
+ /**
157
+ * Convert YouTube URLs in HTML to responsive embeds
158
+ */
159
+ function embedYouTubeVideos(html) {
160
+ if (!html) return html;
161
+
162
+ // Pattern to match YouTube links in HTML
163
+ // Matches: <a href="...youtube...">...</a>
164
+ const linkPattern = /<a\s+[^>]*href=["']([^"']*youtube[^"']*)["'][^>]*>([^<]*)<\/a>/gi;
165
+
166
+ return html.replace(linkPattern, (match, url, linkText) => {
167
+ const videoId = extractYouTubeId(url);
168
+ if (videoId) {
169
+ // Return responsive YouTube embed
170
+ return `<div class="youtube-embed"><iframe src="https://www.youtube.com/embed/${videoId}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
171
+ }
172
+ return match; // Return original if not a valid YouTube URL
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Extract Giphy GIF ID from various URL formats
178
+ * Supports:
179
+ * - https://giphy.com/gifs/ID
180
+ * - https://gph.is/g/ID
181
+ * - https://giphy.com/embed/ID
182
+ * - https://media.giphy.com/media/ID/giphy.gif (extracts ID)
183
+ */
184
+ function extractGiphyId(url) {
185
+ if (!url) return null;
186
+
187
+ // Match Giphy URL patterns
188
+ const patterns = [
189
+ /giphy\.com\/gifs\/([a-zA-Z0-9]+)/,
190
+ /giphy\.com\/embed\/([a-zA-Z0-9]+)/,
191
+ /gph\.is\/g\/([a-zA-Z0-9]+)/,
192
+ /media\.giphy\.com\/media\/([a-zA-Z0-9]+)\//
193
+ ];
194
+
195
+ for (const pattern of patterns) {
196
+ const match = url.match(pattern);
197
+ if (match && match[1]) {
198
+ return match[1];
199
+ }
200
+ }
201
+
202
+ return null;
203
+ }
204
+
205
+ /**
206
+ * Convert Giphy URLs in HTML to responsive embeds
207
+ */
208
+ function embedGiphyGifs(html) {
209
+ if (!html) return html;
210
+
211
+ // Pattern to match Giphy links in HTML
212
+ const linkPattern = /<a\s+[^>]*href=["']([^"']*giphy[^"']*)["'][^>]*>([^<]*)<\/a>/gi;
213
+
214
+ return html.replace(linkPattern, (match, url, linkText) => {
215
+ const gifId = extractGiphyId(url);
216
+ if (gifId) {
217
+ // Return responsive Giphy embed
218
+ return `<div class="giphy-embed"><iframe src="https://giphy.com/embed/${gifId}" frameBorder="0" class="giphy-embed" allowFullScreen></iframe></div>`;
219
+ }
220
+ return match;
221
+ });
222
+ }
223
+
224
+ // Override the link renderer to handle YouTube and Giphy URLs
225
+ marked.use({
226
+ renderer: {
227
+ link(token) {
228
+ const videoId = extractYouTubeId(token.href);
229
+
230
+ if (videoId) {
231
+ // Return responsive YouTube embed instead of a link
232
+ return `<div class="youtube-embed"><iframe src="https://www.youtube.com/embed/${videoId}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
233
+ }
234
+
235
+ const gifId = extractGiphyId(token.href);
236
+
237
+ if (gifId) {
238
+ // Return responsive Giphy embed instead of a link
239
+ return `<div class="giphy-embed"><iframe src="https://giphy.com/embed/${gifId}" frameBorder="0" class="giphy-embed" allowFullScreen></iframe></div>`;
240
+ }
241
+
242
+ // Use default link rendering for non-YouTube/Giphy links
243
+ const text = token.text || token.href;
244
+ const title = token.title ? ` title="${token.title}"` : '';
245
+ return `<a href="${token.href}"${title}>${text}</a>`;
246
+ }
247
+ }
248
+ });
249
+
123
250
  /**
124
251
  * Generate a URL-friendly slug from a string
125
252
  */
@@ -172,7 +299,11 @@ export function parseContent(filePath) {
172
299
  const { data: frontMatter, content: markdown } = matter(content);
173
300
 
174
301
  // Parse markdown to HTML
175
- const html = marked.parse(markdown);
302
+ let html = marked.parse(markdown);
303
+
304
+ // Convert any remaining YouTube/Giphy links to embeds (handles autolinked URLs)
305
+ html = embedYouTubeVideos(html);
306
+ html = embedGiphyGifs(html);
176
307
 
177
308
  // Get slug from front matter or filename
178
309
  const slug = frontMatter.slug || getSlugFromFilename(filePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terrymooreii/sia",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "A simple, powerful static site generator with markdown, front matter, and Nunjucks templates",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
package/readme.md CHANGED
@@ -258,6 +258,55 @@ Plain URLs are automatically converted to clickable links:
258
258
  Visit https://example.com for more info.
259
259
  ```
260
260
 
261
+ ### Media Embeds
262
+
263
+ Sia automatically converts YouTube and Giphy URLs into responsive embeds.
264
+
265
+ #### YouTube Videos
266
+
267
+ YouTube videos can be embedded using any of these methods:
268
+
269
+ **As a markdown link:**
270
+ ```markdown
271
+ [Watch this video](https://www.youtube.com/watch?v=dQw4w9WgXcQ)
272
+ ```
273
+
274
+ **As a plain URL (auto-linked):**
275
+ ```markdown
276
+ https://www.youtube.com/watch?v=dQw4w9WgXcQ
277
+ ```
278
+
279
+ **Using short URL format:**
280
+ ```markdown
281
+ https://youtu.be/dQw4w9WgXcQ
282
+ ```
283
+
284
+ All of these formats are automatically converted to responsive YouTube embeds.
285
+
286
+ #### Giphy GIFs
287
+
288
+ Giphy GIFs can be embedded in two ways:
289
+
290
+ **As a direct image link (standard markdown):**
291
+ ```markdown
292
+ ![Alt text](https://media.giphy.com/media/ID/giphy.gif)
293
+ ```
294
+
295
+ **As a Giphy share URL (auto-embedded):**
296
+ ```markdown
297
+ [Check this out](https://giphy.com/gifs/ID)
298
+ ```
299
+
300
+ Or just paste the URL:
301
+ ```markdown
302
+ https://giphy.com/gifs/ID
303
+ ```
304
+
305
+ Giphy share URLs are automatically converted to responsive embeds. Supported formats include:
306
+ - `https://giphy.com/gifs/ID`
307
+ - `https://gph.is/g/ID`
308
+ - `https://giphy.com/embed/ID`
309
+
261
310
  ### GitHub Flavored Markdown
262
311
 
263
312
  Full GFM support including: