@markdy/astro 0.7.1 → 0.7.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/package.json +2 -2
- package/src/Markdy.astro +89 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/astro",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Astro island component for MarkdyScript animations.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@markdy/renderer-dom": "0.7.
|
|
40
|
+
"@markdy/renderer-dom": "0.7.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"astro": ">=4.0.0"
|
package/src/Markdy.astro
CHANGED
|
@@ -88,20 +88,20 @@ const {
|
|
|
88
88
|
<!--
|
|
89
89
|
SSR placeholder: keeps layout stable before the island hydrates.
|
|
90
90
|
Background matches the scene bg prop to avoid a visible flash.
|
|
91
|
-
|
|
92
|
-
accessible name — this inner text is decorative.
|
|
91
|
+
A <button> so it is keyboard-accessible and semantically interactive.
|
|
93
92
|
-->
|
|
94
|
-
<
|
|
93
|
+
<button
|
|
95
94
|
class="markdy-placeholder"
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
type="button"
|
|
96
|
+
aria-label={`Play ${title}`}
|
|
97
|
+
style={`width:100%;height:100%;background:${bg};display:flex;align-items:center;justify-content:center;cursor:pointer;border:none;padding:0`}
|
|
98
98
|
>
|
|
99
99
|
<span
|
|
100
|
-
style="font-family:sans-serif;font-size:12px;color:#bbb;letter-spacing:0.05em"
|
|
100
|
+
style="font-family:sans-serif;font-size:12px;color:#bbb;letter-spacing:0.05em;pointer-events:none"
|
|
101
101
|
>
|
|
102
102
|
▶ markdy
|
|
103
103
|
</span>
|
|
104
|
-
</
|
|
104
|
+
</button>
|
|
105
105
|
|
|
106
106
|
<!--
|
|
107
107
|
noscript fallback: rendered for crawlers / users that do not run JS.
|
|
@@ -121,13 +121,90 @@ const {
|
|
|
121
121
|
</noscript>
|
|
122
122
|
</div>
|
|
123
123
|
|
|
124
|
+
<script is:inline>
|
|
125
|
+
// ── Rocket Loader rescue ──────────────────────────────────────────────────
|
|
126
|
+
// Cloudflare Rocket Loader rewrites <script type="module" src="..."> in
|
|
127
|
+
// one of two ways, both of which prevent the hydration logic from running:
|
|
128
|
+
//
|
|
129
|
+
// A) Changes type="module" → type="{uuid}-module" (keeps tag in DOM)
|
|
130
|
+
// B) Wraps the entire tag in an HTML comment:
|
|
131
|
+
// <!--<script type="{uuid}-module" src="..."></script>-->
|
|
132
|
+
// making it invisible to querySelectorAll.
|
|
133
|
+
//
|
|
134
|
+
// This inline script is NOT touched by Rocket Loader (no src, no type attr).
|
|
135
|
+
// It rescues module scripts via two strategies:
|
|
136
|
+
// 1. Query the DOM for type$="-module" + src (covers case A)
|
|
137
|
+
// 2. Regex-parse the raw HTML for commented-out script tags (covers case B)
|
|
138
|
+
// Dynamically-created scripts bypass Rocket Loader entirely.
|
|
139
|
+
(function () {
|
|
140
|
+
if (window.__markdyRescue) return;
|
|
141
|
+
window.__markdyRescue = true;
|
|
142
|
+
|
|
143
|
+
var rescued = false;
|
|
144
|
+
function rescueModuleScripts() {
|
|
145
|
+
if (rescued) return;
|
|
146
|
+
rescued = true;
|
|
147
|
+
|
|
148
|
+
var srcs = [];
|
|
149
|
+
|
|
150
|
+
// Strategy 1: Rocket Loader kept the tag in DOM but changed the type.
|
|
151
|
+
// type="module" → type="{uuid}-module" (still has src attribute)
|
|
152
|
+
document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) {
|
|
153
|
+
srcs.push(s.src);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely:
|
|
157
|
+
// <!--<script type="{uuid}-module" src="..."></script>-->
|
|
158
|
+
// These are invisible to querySelectorAll, so we parse the raw HTML.
|
|
159
|
+
// We handle both attribute orderings (type-first or src-first).
|
|
160
|
+
var html = document.documentElement.innerHTML;
|
|
161
|
+
var reSrcFirst = /<!--<script\s[^>]*src="([^"]+)"[^>]*type="[^"]*-module"[^>]*>\s*<\/script>-->/g;
|
|
162
|
+
var reTypeFirst = /<!--<script\s[^>]*type="[^"]*-module"[^>]*src="([^"]+)"[^>]*>\s*<\/script>-->/g;
|
|
163
|
+
var m;
|
|
164
|
+
while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); }
|
|
165
|
+
while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); }
|
|
166
|
+
|
|
167
|
+
// Re-inject each found src as a real module script.
|
|
168
|
+
// Deduplicate first, then inject. Dynamically-created scripts bypass
|
|
169
|
+
// Rocket Loader entirely. Modules with the same URL are only executed
|
|
170
|
+
// once by the browser (cached), so re-injecting already-running scripts
|
|
171
|
+
// is safe.
|
|
172
|
+
var seen = {};
|
|
173
|
+
srcs.forEach(function (src) {
|
|
174
|
+
if (seen[src]) return;
|
|
175
|
+
seen[src] = true;
|
|
176
|
+
var fix = document.createElement('script');
|
|
177
|
+
fix.type = 'module';
|
|
178
|
+
fix.src = src;
|
|
179
|
+
document.head.appendChild(fix);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Rescue when user clicks the placeholder (fallback if autoplay failed).
|
|
184
|
+
document.addEventListener('click', function (e) {
|
|
185
|
+
var t = e.target;
|
|
186
|
+
if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) {
|
|
187
|
+
rescueModuleScripts();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Rescue automatically after a short delay for autoplay.
|
|
192
|
+
// Only fires if initAll() never ran (no data-markdy-init on any root).
|
|
193
|
+
setTimeout(function () {
|
|
194
|
+
if (document.querySelector('.markdy-root:not([data-markdy-init])')) {
|
|
195
|
+
rescueModuleScripts();
|
|
196
|
+
}
|
|
197
|
+
}, 1500);
|
|
198
|
+
}());
|
|
199
|
+
</script>
|
|
200
|
+
|
|
124
201
|
<script>
|
|
125
202
|
// ── Hydration ────────────────────────────────────────────────────────────
|
|
126
203
|
// The renderer is dynamically imported so it is code-split from the
|
|
127
204
|
// host-page bundle. The browser only downloads it when an element is
|
|
128
205
|
// about to enter the viewport, keeping Time-to-Interactive low.
|
|
129
206
|
|
|
130
|
-
async function hydrate(el: HTMLElement): Promise<void> {
|
|
207
|
+
async function hydrate(el: HTMLElement, forcePlay = false): Promise<void> {
|
|
131
208
|
const code = el.dataset.markdyCode;
|
|
132
209
|
if (!code) return;
|
|
133
210
|
|
|
@@ -141,7 +218,7 @@ const {
|
|
|
141
218
|
// Leave assets empty on parse failure — renderer falls back to DSL paths.
|
|
142
219
|
}
|
|
143
220
|
|
|
144
|
-
const autoplay = el.dataset.markdyAutoplay !== "false";
|
|
221
|
+
const autoplay = forcePlay || el.dataset.markdyAutoplay !== "false";
|
|
145
222
|
const loop = el.dataset.markdyLoop !== "false";
|
|
146
223
|
const copyright = el.dataset.markdyCopyright !== "false";
|
|
147
224
|
const progressBar = el.dataset.markdyProgressBar !== "false";
|
|
@@ -209,11 +286,11 @@ const {
|
|
|
209
286
|
{ threshold: 0.25 },
|
|
210
287
|
);
|
|
211
288
|
|
|
212
|
-
function doHydrate(el: HTMLElement): void {
|
|
289
|
+
function doHydrate(el: HTMLElement, forcePlay = false): void {
|
|
213
290
|
observer.unobserve(el);
|
|
214
291
|
el.dataset.markdyInit = "hydrating";
|
|
215
292
|
scheduleHydration(() => {
|
|
216
|
-
hydrate(el).catch(() => {
|
|
293
|
+
hydrate(el, forcePlay).catch(() => {
|
|
217
294
|
el.dataset.markdyInit = "error";
|
|
218
295
|
el.removeAttribute("aria-busy");
|
|
219
296
|
});
|
|
@@ -261,6 +338,6 @@ const {
|
|
|
261
338
|
if (!root) return;
|
|
262
339
|
const state = root.dataset.markdyInit;
|
|
263
340
|
if (state === "done" || state === "hydrating") return;
|
|
264
|
-
doHydrate(root);
|
|
341
|
+
doHydrate(root, true);
|
|
265
342
|
});
|
|
266
343
|
</script>
|