sitedrift 0.3.5 → 0.3.7
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/assets/viewer.css +8 -0
- package/assets/viewer.html +2 -2
- package/assets/viewer.js +72 -69
- package/package.json +1 -1
- package/src/frame-content.mjs +10 -1
- package/src/viewer.mjs +1 -1
package/assets/viewer.css
CHANGED
|
@@ -133,7 +133,11 @@ button:focus-visible, summary:focus-visible {
|
|
|
133
133
|
border-bottom: 1px solid var(--line);
|
|
134
134
|
}
|
|
135
135
|
.compact-side { min-inline-size: 0; display: flex; align-items: center; gap: 6px; }
|
|
136
|
+
.app.solo .compact-side { cursor: pointer; }
|
|
137
|
+
.compact-favicon { display: block; flex: 0 0 auto; inline-size: 16px; block-size: 16px; border-radius: 4px; }
|
|
138
|
+
.compact-copy { min-inline-size: 0; display: flex; align-items: baseline; gap: 7px; overflow: hidden; }
|
|
136
139
|
.compact-title { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 650; }
|
|
140
|
+
.compact-origin { min-inline-size: 0; overflow: hidden; color: var(--muted); text-overflow: ellipsis; white-space: nowrap; font-size: 10px; }
|
|
137
141
|
.compact-controls { display: flex; align-items: center; gap: 8px; justify-self: center; }
|
|
138
142
|
|
|
139
143
|
.caret {
|
|
@@ -323,6 +327,10 @@ button[data-action="notes"] {
|
|
|
323
327
|
.status-err { color: #fff; background: var(--err); }
|
|
324
328
|
.compact-side .status-badge { block-size: 16px; }
|
|
325
329
|
|
|
330
|
+
@media (width <= 600px) {
|
|
331
|
+
.compact-origin { display: none; }
|
|
332
|
+
}
|
|
333
|
+
|
|
326
334
|
.meta-diff {
|
|
327
335
|
display: none;
|
|
328
336
|
align-items: center;
|
package/assets/viewer.html
CHANGED
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
<button class="caret" data-action="compact" title="Expand review chrome" aria-label="Expand review chrome">
|
|
106
106
|
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m3 10 5-5 5 5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
107
107
|
</button>
|
|
108
|
-
<div class="compact-side" data-compact-side="dev"><span class="pill dev">DEV</span><span class="compact-title" data-compact-title="dev">Loading…</span><span class="status-badge"></span></div>
|
|
108
|
+
<div class="compact-side" data-compact-side="dev"><span class="pill dev">DEV</span><img class="compact-favicon" data-compact-favicon="dev" alt=""><span class="compact-copy"><span class="compact-title" data-compact-title="dev">Loading…</span><span class="compact-origin" data-compact-origin="dev"></span></span><span class="status-badge"></span></div>
|
|
109
109
|
<div class="compact-controls">
|
|
110
110
|
<div class="modes" role="group" aria-label="View mode">
|
|
111
111
|
<button data-mode="split">Split</button>
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M15.5 6.5V3m0 0H12m3.5 0-2.2 2.2A6 6 0 1 0 15.8 11" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
121
121
|
</button>
|
|
122
122
|
</div>
|
|
123
|
-
<div class="compact-side" data-compact-side="live"><span class="pill live">LIVE</span><span class="compact-title" data-compact-title="live">Loading…</span><span class="status-badge"></span></div>
|
|
123
|
+
<div class="compact-side" data-compact-side="live"><span class="pill live">LIVE</span><img class="compact-favicon" data-compact-favicon="live" alt=""><span class="compact-copy"><span class="compact-title" data-compact-title="live">Loading…</span><span class="compact-origin" data-compact-origin="live"></span></span><span class="status-badge"></span></div>
|
|
124
124
|
<button class="icon" data-action="notes" title="Review notes" aria-label="Review notes">
|
|
125
125
|
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M5 3.5h10a1.5 1.5 0 0 1 1.5 1.5v7A1.5 1.5 0 0 1 15 13.5H9L5 17v-3.5A1.5 1.5 0 0 1 3.5 12V5A1.5 1.5 0 0 1 5 3.5Z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg>
|
|
126
126
|
</button>
|
package/assets/viewer.js
CHANGED
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
const settleTimers = { dev: [], live: [] };
|
|
31
31
|
const frameState = { dev: { y: 0, max: 0 }, live: { y: 0, max: 0 } };
|
|
32
32
|
let order = params.get('swap') === '1' ? ['live', 'dev'] : ['dev', 'live'];
|
|
33
|
-
let syncScroll = queryOrStoredBool('scroll', 'site-compare-scroll',
|
|
33
|
+
let syncScroll = queryOrStoredBool('scroll', 'site-compare-scroll', !!config.hosted);
|
|
34
34
|
let scrollMode = params.get('scrollMode') || localStorage.getItem('site-compare-scroll-mode') || 'exact';
|
|
35
35
|
if (!['exact', 'ratio'].includes(scrollMode)) scrollMode = 'exact';
|
|
36
|
-
let mirrorLinks = queryOrStoredBool('mirror', 'site-compare-mirror',
|
|
36
|
+
let mirrorLinks = queryOrStoredBool('mirror', 'site-compare-mirror', !!config.hosted);
|
|
37
37
|
let mobileMode = (params.get('mode') || localStorage.getItem('site-compare-mode')) === 'mobile';
|
|
38
38
|
let compactMode = queryOrStoredBool('compact', 'site-compare-compact', !!config.hosted);
|
|
39
39
|
const storedView = localStorage.getItem('site-compare-view');
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
let dockMode = queryOrStoredBool('dock', 'site-compare-dock', true);
|
|
55
55
|
let scrollOwner = null;
|
|
56
56
|
const meta = { dev: null, live: null };
|
|
57
|
+
const statusDetails = { dev: {}, live: {} };
|
|
57
58
|
const apiHeaders = {
|
|
58
59
|
authorization: 'Bearer ' + config.token,
|
|
59
60
|
'content-type': 'application/json',
|
|
@@ -87,72 +88,12 @@
|
|
|
87
88
|
: config[side] + normalizeRoute(route);
|
|
88
89
|
}
|
|
89
90
|
function framePost(side, type, data = {}) {
|
|
90
|
-
if (config.hosted) {
|
|
91
|
-
const win = frame(side).contentWindow;
|
|
92
|
-
const scrolling = frame(side).contentDocument?.scrollingElement;
|
|
93
|
-
if (type === 'scroll' && scrolling) scrolling.scrollTop = Number(data.y) || 0;
|
|
94
|
-
if (type === 'reload') win?.location.reload();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
91
|
frame(side).contentWindow?.postMessage(
|
|
98
92
|
{ source: 'sitedrift-parent', side, type, ...data },
|
|
99
93
|
config.hosted ? '*' : config.frameOrigins[side],
|
|
100
94
|
);
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
function hostedSnapshot(side) {
|
|
104
|
-
if (!config.hosted) return;
|
|
105
|
-
const iframe = frame(side);
|
|
106
|
-
const doc = iframe.contentDocument;
|
|
107
|
-
const win = iframe.contentWindow;
|
|
108
|
-
if (!doc || !win) return;
|
|
109
|
-
const q = (selector) => doc.querySelector(selector);
|
|
110
|
-
const images = [...doc.querySelectorAll('img')];
|
|
111
|
-
const title = (doc.title || '').trim();
|
|
112
|
-
const description = q('meta[name="description"]')?.content?.trim() || '';
|
|
113
|
-
const canonical = q('link[rel="canonical"]')?.href || '';
|
|
114
|
-
const checks = [
|
|
115
|
-
['Title present', !!title],
|
|
116
|
-
['Title 30–60 chars', title.length >= 30 && title.length <= 60, String(title.length)],
|
|
117
|
-
['Meta description', !!description],
|
|
118
|
-
['Description 70–160', description.length >= 70 && description.length <= 160, String(description.length)],
|
|
119
|
-
['Exactly one H1', doc.querySelectorAll('h1').length === 1, `${doc.querySelectorAll('h1').length} found`],
|
|
120
|
-
['Canonical link', !!q('link[rel="canonical"]')],
|
|
121
|
-
['Viewport meta', !!q('meta[name="viewport"]')],
|
|
122
|
-
['html lang', !!doc.documentElement.lang],
|
|
123
|
-
['Open Graph title', !!q('meta[property="og:title"]')],
|
|
124
|
-
['Open Graph image', !!q('meta[property="og:image"]')],
|
|
125
|
-
['Not noindex', !(q('meta[name="robots"]')?.content || '').toLowerCase().includes('noindex')],
|
|
126
|
-
['Favicon', !!q('link[rel~="icon"]')],
|
|
127
|
-
['Images have alt', images.every((image) => image.hasAttribute('alt')), `${images.filter((image) => !image.hasAttribute('alt')).length} missing`],
|
|
128
|
-
].map(([label, ok, note]) => ({ label, ok, note }));
|
|
129
|
-
const url = new URL(iframe.src);
|
|
130
|
-
const route = (url.pathname.slice(proxyPath(side).length) || '/') + url.search + url.hash;
|
|
131
|
-
renderMetadata(side, {
|
|
132
|
-
route,
|
|
133
|
-
meta: {
|
|
134
|
-
title,
|
|
135
|
-
description,
|
|
136
|
-
canonical,
|
|
137
|
-
heading: q('h1')?.textContent?.trim() || '',
|
|
138
|
-
siteName: q('meta[property="og:site_name"]')?.content?.trim() || '',
|
|
139
|
-
icon: q('link[rel~="icon"]')?.href || '',
|
|
140
|
-
checks,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
fetchStatus(side, route);
|
|
144
|
-
const reportScroll = () => {
|
|
145
|
-
const root = doc.scrollingElement || doc.documentElement;
|
|
146
|
-
frameState[side] = {
|
|
147
|
-
y: Number(win.scrollY) || 0,
|
|
148
|
-
max: Math.max(0, root.scrollHeight - win.innerHeight),
|
|
149
|
-
};
|
|
150
|
-
syncFrom(side);
|
|
151
|
-
};
|
|
152
|
-
win.addEventListener('scroll', reportScroll, { passive: true });
|
|
153
|
-
reportScroll();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
97
|
function statusBadges(side) {
|
|
157
98
|
return [
|
|
158
99
|
document.querySelector('.label[data-label="' + side + '"] .status-badge'),
|
|
@@ -160,6 +101,31 @@
|
|
|
160
101
|
].filter(Boolean);
|
|
161
102
|
}
|
|
162
103
|
|
|
104
|
+
function formatMs(value) {
|
|
105
|
+
return Number.isFinite(value) && value >= 0 ? `${Math.round(value)} ms` : '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function formatBytes(value) {
|
|
109
|
+
if (!Number.isFinite(value) || value <= 0) return '';
|
|
110
|
+
if (value < 1024) return `${Math.round(value)} B`;
|
|
111
|
+
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
112
|
+
return `${(value / 1024 / 1024).toFixed(1)} MB`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function statusTitle(side, status) {
|
|
116
|
+
const detail = statusDetails[side];
|
|
117
|
+
const lines = [`${side.toUpperCase()} returned ${status || 'an error'}`];
|
|
118
|
+
if (detail.requestMs) lines.push(`Status check: ${formatMs(detail.requestMs)}`);
|
|
119
|
+
if (detail.response) lines.push(`Document response: ${formatMs(detail.response)}`);
|
|
120
|
+
if (detail.dom) lines.push(`DOM ready: ${formatMs(detail.dom)}`);
|
|
121
|
+
if (detail.load) lines.push(`Window load: ${formatMs(detail.load)}`);
|
|
122
|
+
const size = formatBytes(detail.transfer || detail.decoded);
|
|
123
|
+
if (size) lines.push(`${detail.transfer ? 'Transferred' : 'Decoded size'}: ${size}`);
|
|
124
|
+
if (detail.type) lines.push(`Content-Type: ${detail.type}`);
|
|
125
|
+
if (detail.cache) lines.push(`Cache-Control: ${detail.cache}`);
|
|
126
|
+
return lines.join('\n');
|
|
127
|
+
}
|
|
128
|
+
|
|
163
129
|
function setStatusBadge(side, status) {
|
|
164
130
|
const cls = status >= 200 && status < 300 ? 'status-ok'
|
|
165
131
|
: status >= 300 && status < 400 ? 'status-warn'
|
|
@@ -168,6 +134,8 @@
|
|
|
168
134
|
for (const badge of statusBadges(side)) {
|
|
169
135
|
badge.className = 'status-badge show ' + cls;
|
|
170
136
|
badge.textContent = text;
|
|
137
|
+
badge.title = statusTitle(side, status);
|
|
138
|
+
badge.setAttribute('aria-label', badge.title.replaceAll('\n', '. '));
|
|
171
139
|
}
|
|
172
140
|
}
|
|
173
141
|
|
|
@@ -175,15 +143,26 @@
|
|
|
175
143
|
for (const badge of statusBadges(side)) {
|
|
176
144
|
badge.className = 'status-badge';
|
|
177
145
|
badge.textContent = '';
|
|
146
|
+
badge.removeAttribute('title');
|
|
147
|
+
badge.removeAttribute('aria-label');
|
|
178
148
|
}
|
|
179
149
|
}
|
|
180
150
|
|
|
181
151
|
function fetchStatus(side, route) {
|
|
182
152
|
const url = statusUrl(side, route);
|
|
153
|
+
const started = performance.now();
|
|
183
154
|
const read = (method) => fetch(url, { method, cache: 'no-store', redirect: 'manual' });
|
|
184
155
|
read('HEAD')
|
|
185
156
|
.then((res) => (res.status === 405 || res.status === 501 ? read('GET') : res))
|
|
186
|
-
.then((res) =>
|
|
157
|
+
.then((res) => {
|
|
158
|
+
statusDetails[side] = {
|
|
159
|
+
...statusDetails[side],
|
|
160
|
+
requestMs: performance.now() - started,
|
|
161
|
+
type: res.headers.get('content-type') || '',
|
|
162
|
+
cache: res.headers.get('cache-control') || '',
|
|
163
|
+
};
|
|
164
|
+
setStatusBadge(side, res.status || (res.type === 'opaqueredirect' ? 302 : 0));
|
|
165
|
+
})
|
|
187
166
|
.catch(() => setStatusBadge(side, 0));
|
|
188
167
|
}
|
|
189
168
|
|
|
@@ -299,14 +278,26 @@
|
|
|
299
278
|
let canonicalPath = canonical;
|
|
300
279
|
try { canonicalPath = new URL(canonical).pathname; } catch {}
|
|
301
280
|
meta[side] = { title, description, canonicalPath, heading };
|
|
281
|
+
statusDetails[side] = { ...statusDetails[side], ...(source.timing || {}) };
|
|
302
282
|
label.querySelector('.page-heading').textContent = heading;
|
|
303
283
|
label.querySelector('.page-heading').title = title || heading;
|
|
304
284
|
updateDocTitle();
|
|
305
|
-
document.querySelector('[data-compact-title="' + side + '"]')
|
|
285
|
+
const compactTitle = document.querySelector('[data-compact-title="' + side + '"]');
|
|
286
|
+
compactTitle.textContent = heading;
|
|
287
|
+
compactTitle.title = title || heading;
|
|
288
|
+
const compactOrigin = document.querySelector('[data-compact-origin="' + side + '"]');
|
|
289
|
+
compactOrigin.textContent = new URL(config[side]).host + route;
|
|
290
|
+
compactOrigin.title = config[side] + route;
|
|
306
291
|
label.querySelector('.origin').textContent = config[side] + route;
|
|
307
292
|
const fav = label.querySelector('.favicon');
|
|
308
293
|
fav.onerror = () => { fav.onerror = null; fav.src = '/icon.svg'; };
|
|
309
294
|
fav.src = faviconSrc;
|
|
295
|
+
const compactFav = document.querySelector('[data-compact-favicon="' + side + '"]');
|
|
296
|
+
compactFav.onerror = () => {
|
|
297
|
+
compactFav.onerror = null;
|
|
298
|
+
compactFav.src = config.hosted ? '/__sitedrift/assets/icon.svg' : '/icon.svg';
|
|
299
|
+
};
|
|
300
|
+
compactFav.src = faviconSrc;
|
|
310
301
|
label.querySelector('.open-side').href = direct(side, route);
|
|
311
302
|
const card = label.querySelector('.seo-card');
|
|
312
303
|
const sourceRow = element('div', 'seo-source');
|
|
@@ -407,10 +398,14 @@
|
|
|
407
398
|
label.querySelector('.pill').textContent = side.toUpperCase();
|
|
408
399
|
label.querySelector('.page-heading').textContent = 'Loading…';
|
|
409
400
|
document.querySelector('[data-compact-title="' + side + '"]').textContent = 'Loading…';
|
|
401
|
+
document.querySelector('[data-compact-origin="' + side + '"]').textContent =
|
|
402
|
+
new URL(config[side]).host + route;
|
|
403
|
+
document.querySelector('[data-compact-favicon="' + side + '"]').removeAttribute('src');
|
|
410
404
|
label.querySelector('.origin').textContent = config[side] + route;
|
|
411
405
|
label.querySelector('.favicon').src = '/__' + side + '/favicon.ico';
|
|
412
406
|
label.querySelector('.open-side').href = direct(side, route);
|
|
413
407
|
meta[side] = null;
|
|
408
|
+
statusDetails[side] = {};
|
|
414
409
|
clearStatusBadge(side);
|
|
415
410
|
}
|
|
416
411
|
renderMetaDiff();
|
|
@@ -561,16 +556,16 @@
|
|
|
561
556
|
setLinkedScroll(side, frameState[side].y + delta);
|
|
562
557
|
} else if (message.type === 'navigate') {
|
|
563
558
|
go(message.route);
|
|
559
|
+
} else if (message.type === 'dismiss') {
|
|
560
|
+
closePopovers();
|
|
564
561
|
} else if (message.type === 'key') {
|
|
565
562
|
runFrameKey(message.key, side, message);
|
|
566
563
|
}
|
|
567
564
|
});
|
|
568
|
-
if (config.hosted) {
|
|
569
|
-
for (const side of ['dev', 'live']) {
|
|
570
|
-
frame(side).addEventListener('load', () => hostedSnapshot(side));
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
565
|
|
|
566
|
+
function closePopovers() {
|
|
567
|
+
for (const details of document.querySelectorAll('details[open]')) details.removeAttribute('open');
|
|
568
|
+
}
|
|
574
569
|
scrollButton.addEventListener('click', () => {
|
|
575
570
|
syncScroll = !syncScroll;
|
|
576
571
|
scrollButton.classList.toggle('active', syncScroll);
|
|
@@ -661,6 +656,14 @@
|
|
|
661
656
|
renderModes();
|
|
662
657
|
}
|
|
663
658
|
for (const button of modeButtons) button.addEventListener('click', () => setMode(button.dataset.mode));
|
|
659
|
+
for (const identity of document.querySelectorAll('.compact-side')) {
|
|
660
|
+
identity.addEventListener('click', () => {
|
|
661
|
+
if (viewMode !== 'solo') return;
|
|
662
|
+
focusSide = identity.dataset.compactSide === 'dev' ? 'live' : 'dev';
|
|
663
|
+
app.dataset.focus = focusSide;
|
|
664
|
+
renderModes();
|
|
665
|
+
});
|
|
666
|
+
}
|
|
664
667
|
for (const slider of overlaySliders) slider.addEventListener('input', () => {
|
|
665
668
|
if (viewMode !== 'overlay') setMode('overlay');
|
|
666
669
|
if (overlayBlend === 'difference') setOverlayBlend('opacity');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sitedrift",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Catch the drift between dev and live — frame your local site and production side-by-side on the same route, locked scroll, with a difference-blend overlay. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/frame-content.mjs
CHANGED
|
@@ -11,6 +11,14 @@ export function frameBridge(side, prefix = `/__${side}`) {
|
|
|
11
11
|
const title=(document.title||'').trim();
|
|
12
12
|
const description=q('meta[name="description"]')?.content?.trim()||'';
|
|
13
13
|
const canonical=q('link[rel="canonical"]')?.href||'';
|
|
14
|
+
const navigation=performance.getEntriesByType('navigation')[0];
|
|
15
|
+
const timing=navigation?{
|
|
16
|
+
response:Math.round(navigation.responseEnd),
|
|
17
|
+
dom:Math.round(navigation.domContentLoadedEventEnd),
|
|
18
|
+
load:Math.round(navigation.loadEventEnd||navigation.duration),
|
|
19
|
+
transfer:Number(navigation.transferSize)||0,
|
|
20
|
+
decoded:Number(navigation.decodedBodySize)||0
|
|
21
|
+
}:null;
|
|
14
22
|
const checks=[
|
|
15
23
|
['Title present',!!title],['Title 30–60 chars',title.length>=30&&title.length<=60,title.length+''],
|
|
16
24
|
['Meta description',!!description],['Description 70–160',description.length>=70&&description.length<=160,description.length+''],
|
|
@@ -23,7 +31,7 @@ export function frameBridge(side, prefix = `/__${side}`) {
|
|
|
23
31
|
['Images have alt',imgs.every((img)=>img.hasAttribute('alt')),imgs.filter((img)=>!img.hasAttribute('alt')).length+' missing']
|
|
24
32
|
].map(([label,ok,note])=>({label,ok,note}));
|
|
25
33
|
send('ready',{route:route(),meta:{title,description,canonical,heading:q('h1')?.textContent?.trim()||'',
|
|
26
|
-
siteName:q('meta[property="og:site_name"]')?.content?.trim()||'',icon:q('link[rel~="icon"]')?.href||'',checks}});
|
|
34
|
+
siteName:q('meta[property="og:site_name"]')?.content?.trim()||'',icon:q('link[rel~="icon"]')?.href||'',checks,timing}});
|
|
27
35
|
send('scroll',{y:scrollY,max:Math.max(0,root().scrollHeight-innerHeight)});
|
|
28
36
|
};
|
|
29
37
|
addEventListener('message',(event)=>{
|
|
@@ -43,6 +51,7 @@ export function frameBridge(side, prefix = `/__${side}`) {
|
|
|
43
51
|
if(linked&&!typing&&!event.metaKey&&!event.ctrlKey&&!event.altKey)send('key',{key:event.key,shift:event.shiftKey,y:scrollY,height:innerHeight,max:Math.max(0,root().scrollHeight-innerHeight)});
|
|
44
52
|
},true);
|
|
45
53
|
addEventListener('click',(event)=>{
|
|
54
|
+
send('dismiss');
|
|
46
55
|
if(!mirror||event.defaultPrevented||event.button!==0||event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)return;
|
|
47
56
|
const link=event.target.closest('a[href]');if(!link||link.target==='_blank'||link.hasAttribute('download'))return;
|
|
48
57
|
const url=new URL(link.href,location.href);if(url.origin!==location.origin||!url.pathname.startsWith(prefix))return;
|
package/src/viewer.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
|
|
3
3
|
// Bumped when the viewer assets change; busts the ?v= cache and reported in
|
|
4
4
|
// /health so the `site compare` wrapper knows when to restart the server.
|
|
5
|
-
export const VIEWER_VERSION =
|
|
5
|
+
export const VIEWER_VERSION = 30;
|
|
6
6
|
|
|
7
7
|
function readAsset(path) {
|
|
8
8
|
try {
|