@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.
- package/defaults/styles/main.css +40 -0
- package/defaults/styles/minimal.css +38 -0
- package/lib/content.js +132 -1
- package/package.json +1 -1
- package/readme.md +49 -0
package/defaults/styles/main.css
CHANGED
|
@@ -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
|
-
|
|
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
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
|
+

|
|
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:
|