ferst-core 0.4.9 → 0.4.10
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/layouts/Base.astro +17 -6
- package/lib/theme-preview.ts +52 -3
- package/package.json +1 -1
package/layouts/Base.astro
CHANGED
|
@@ -172,12 +172,23 @@ const metaDescription = description ?? identity.description;
|
|
|
172
172
|
document.addEventListener('astro:page-load', ferstMarkExternalLinks);
|
|
173
173
|
</script>
|
|
174
174
|
<script>
|
|
175
|
-
// Live theme preview: only when this page is FRAMED
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
// Live theme preview: only when this page is FRAMED and either loaded with
|
|
176
|
+
// ?preview=1 OR already in a preview session (so the draft survives clicking
|
|
177
|
+
// links inside the frame, which drop the query). Listens for draft theme
|
|
178
|
+
// settings over postMessage and re-themes live. Dynamically imported so the
|
|
179
|
+
// code never ships to normal visitors; never runs unframed.
|
|
180
|
+
try {
|
|
181
|
+
var inPreview =
|
|
182
|
+
new URLSearchParams(location.search).has('preview') ||
|
|
183
|
+
sessionStorage.getItem('ferst-preview-on') === '1';
|
|
184
|
+
if (window.parent !== window && inPreview) {
|
|
185
|
+
import('../lib/theme-preview').then((m) => m.initThemePreviewListener());
|
|
186
|
+
}
|
|
187
|
+
} catch (_) {
|
|
188
|
+
/* sessionStorage blocked — fall back to the ?preview query only */
|
|
189
|
+
if (window.parent !== window && new URLSearchParams(location.search).has('preview')) {
|
|
190
|
+
import('../lib/theme-preview').then((m) => m.initThemePreviewListener());
|
|
191
|
+
}
|
|
181
192
|
}
|
|
182
193
|
</script>
|
|
183
194
|
</head>
|
package/lib/theme-preview.ts
CHANGED
|
@@ -28,6 +28,27 @@ export const PREVIEW_MESSAGES = {
|
|
|
28
28
|
} as const;
|
|
29
29
|
|
|
30
30
|
const STYLE_ID = 'ferst-preview';
|
|
31
|
+
/** Marks a live preview session so the draft survives navigation WITHIN the frame
|
|
32
|
+
* (internal links drop ?preview=1). Base.astro's gate also checks this flag. */
|
|
33
|
+
export const PREVIEW_SESSION_FLAG = 'ferst-preview-on';
|
|
34
|
+
const PREVIEW_DRAFT_KEY = 'ferst-preview-draft';
|
|
35
|
+
|
|
36
|
+
function rememberDraft(theme: ThemeSettings): void {
|
|
37
|
+
try {
|
|
38
|
+
sessionStorage.setItem(PREVIEW_DRAFT_KEY, JSON.stringify(theme));
|
|
39
|
+
} catch {
|
|
40
|
+
/* storage blocked — the page still themes from the live postMessage */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function recallDraft(): ThemeSettings | null {
|
|
45
|
+
try {
|
|
46
|
+
const raw = sessionStorage.getItem(PREVIEW_DRAFT_KEY);
|
|
47
|
+
return raw ? (JSON.parse(raw) as ThemeSettings) : null;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
31
52
|
|
|
32
53
|
/** Origins allowed to drive the preview (besides same-origin, i.e. the CMS pane).
|
|
33
54
|
* The Ferst portal — both its custom domains and the bare Cloudflare Pages URLs
|
|
@@ -66,8 +87,30 @@ export function applyPreviewTheme(theme: ThemeSettings): void {
|
|
|
66
87
|
else delete document.documentElement.dataset.field;
|
|
67
88
|
}
|
|
68
89
|
|
|
69
|
-
/** Wire the page to receive drafts from an allowed surface, and announce readiness.
|
|
90
|
+
/** Wire the page to receive drafts from an allowed surface, and announce readiness.
|
|
91
|
+
* Persists the draft for the session so it survives navigation inside the frame. */
|
|
70
92
|
export function initThemePreviewListener(): void {
|
|
93
|
+
try {
|
|
94
|
+
sessionStorage.setItem(PREVIEW_SESSION_FLAG, '1'); // keep previewing across in-frame nav
|
|
95
|
+
} catch {
|
|
96
|
+
/* storage blocked — the portal re-push on ready still re-themes each new page */
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const reapply = () => {
|
|
100
|
+
const draft = recallDraft();
|
|
101
|
+
if (draft) {
|
|
102
|
+
try {
|
|
103
|
+
applyPreviewTheme(draft);
|
|
104
|
+
} catch {
|
|
105
|
+
/* ignore a bad stored draft */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// A freshly-navigated page in the session: re-theme immediately from the stored
|
|
111
|
+
// draft (before the portal re-pushes), so tokens don't drop on link clicks.
|
|
112
|
+
reapply();
|
|
113
|
+
|
|
71
114
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
72
115
|
if (!isAllowedPreviewOrigin(event.origin)) return;
|
|
73
116
|
const data = event.data as { type?: string; theme?: ThemeSettings; mode?: string } | null;
|
|
@@ -75,6 +118,7 @@ export function initThemePreviewListener(): void {
|
|
|
75
118
|
if (data.type === PREVIEW_MESSAGES.theme && data.theme) {
|
|
76
119
|
try {
|
|
77
120
|
applyPreviewTheme(data.theme);
|
|
121
|
+
rememberDraft(data.theme);
|
|
78
122
|
} catch {
|
|
79
123
|
/* a malformed draft must never break the preview page */
|
|
80
124
|
}
|
|
@@ -82,10 +126,15 @@ export function initThemePreviewListener(): void {
|
|
|
82
126
|
document.documentElement.dataset.theme = data.mode === 'dark' ? 'dark' : 'light';
|
|
83
127
|
}
|
|
84
128
|
});
|
|
85
|
-
|
|
129
|
+
|
|
130
|
+
// View-transition (ClientRouter) pages swap the DOM without a full load, which
|
|
131
|
+
// drops the injected <style> — re-apply after each swap.
|
|
132
|
+
document.addEventListener('astro:page-load', reapply);
|
|
133
|
+
|
|
134
|
+
// Tell whoever framed us that we're listening, so it can push the latest draft.
|
|
86
135
|
try {
|
|
87
136
|
window.parent?.postMessage({ type: PREVIEW_MESSAGES.ready }, '*');
|
|
88
137
|
} catch {
|
|
89
|
-
/* no parent / blocked — the
|
|
138
|
+
/* no parent / blocked — the stored draft already re-themed this page */
|
|
90
139
|
}
|
|
91
140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ferst-core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ferst Core — the shared, brand-agnostic client-site system: Astro components, layouts, content schemas, the layered config resolver, and a neutral styling architecture that every client overrides.",
|
|
6
6
|
"license": "BUSL-1.1",
|