@xosen/site-sdk 0.0.14 → 0.0.15
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/dist/composables/useLivePreview.d.ts +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +119 -105
- package/dist/types/blocks.d.ts +2 -1
- package/dist/types/layout.d.ts +1 -0
- package/package.json +14 -14
- package/src/composables/useLivePreview.ts +12 -0
- package/src/composables/usePageData.ts +2 -2
- package/src/composables/useSiteComponents.ts +6 -2
- package/src/index.ts +1 -1
- package/src/types/blocks.ts +2 -1
- package/src/types/layout.ts +14 -2
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Router } from 'vue-router';
|
|
2
|
+
export interface PreviewSiteData {
|
|
3
|
+
config?: Record<string, any>;
|
|
4
|
+
skin?: Record<string, any>;
|
|
5
|
+
components?: Record<string, any>;
|
|
6
|
+
}
|
|
2
7
|
export interface PreviewMessage {
|
|
3
8
|
type: 'xosen-preview-update';
|
|
9
|
+
site?: PreviewSiteData;
|
|
4
10
|
page: {
|
|
5
11
|
slug: string;
|
|
6
12
|
locale: string;
|
|
@@ -50,5 +56,14 @@ export declare function useLivePreview(): {
|
|
|
50
56
|
meta?: Record<string, any> | undefined;
|
|
51
57
|
layout?: string | undefined;
|
|
52
58
|
} | null>;
|
|
59
|
+
previewSite: import("vue").Ref<{
|
|
60
|
+
config?: Record<string, any> | undefined;
|
|
61
|
+
skin?: Record<string, any> | undefined;
|
|
62
|
+
components?: Record<string, any> | undefined;
|
|
63
|
+
} | null, PreviewSiteData | {
|
|
64
|
+
config?: Record<string, any> | undefined;
|
|
65
|
+
skin?: Record<string, any> | undefined;
|
|
66
|
+
components?: Record<string, any> | undefined;
|
|
67
|
+
} | null>;
|
|
53
68
|
isPreviewMode: import("vue").Ref<boolean, boolean>;
|
|
54
69
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
|
10
10
|
export { useDynamicLayout, buildLayoutCSS } from './composables/useDynamicLayout.js';
|
|
11
11
|
export type { LayoutConfig, LayoutZone, GridBreakpoint, LayoutSchemaDefinition, LayoutField } from './types/layout.js';
|
|
12
12
|
export { layoutSchema, layoutPresets } from './types/layout.js';
|
|
13
|
-
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
13
|
+
export type { PreviewMessage, PreviewSiteData } from './composables/useLivePreview.js';
|
|
14
14
|
export { createSiteWorker } from './worker/index.js';
|
|
15
15
|
export type { WorkerEnv, WorkerConfig, PageData } from './worker/index.js';
|
|
16
16
|
export type { Block, BlockSettings, HeroBlockData, HtmlBlockData, CardsBlockData, ImageTextBlockData, PricingBlockData, PricingPlan, ContactsBlockData, Office, GalleryBlockData, MapBlockData, } from './types/blocks.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ref as
|
|
2
|
-
import { useI18n as
|
|
1
|
+
import { ref as v, onMounted as S, watch as _, computed as u, onUnmounted as N, reactive as L, provide as P } from "vue";
|
|
2
|
+
import { useI18n as T } from "vue-i18n";
|
|
3
3
|
import { useRoute as I } from "vue-router";
|
|
4
4
|
function Z(o, a) {
|
|
5
|
-
const t =
|
|
5
|
+
const t = v(null), { locale: e } = T(), s = window.__SITE_DATA__, n = s?.locale;
|
|
6
6
|
function r() {
|
|
7
|
-
return
|
|
7
|
+
return s && s[o] && e.value === n ? (t.value = s[o], !0) : !1;
|
|
8
8
|
}
|
|
9
9
|
async function i() {
|
|
10
10
|
try {
|
|
@@ -13,14 +13,14 @@ function Z(o, a) {
|
|
|
13
13
|
} catch {
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
return
|
|
16
|
+
return S(() => {
|
|
17
17
|
r() || i();
|
|
18
18
|
}), _(e, () => {
|
|
19
19
|
i();
|
|
20
20
|
}), t;
|
|
21
21
|
}
|
|
22
22
|
function q() {
|
|
23
|
-
const o = window.__SITE_DATA__, a = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => a.value?.navigation || []),
|
|
23
|
+
const o = window.__SITE_DATA__, a = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => a.value?.navigation || []), s = u(() => a.value?.locales || []), n = u(() => a.value?.defaultLocale || "en"), r = u(() => a.value?.branding || {}), i = u(() => a.value?.footer || {}), c = u(() => a.value?.features || {});
|
|
24
24
|
function p(m) {
|
|
25
25
|
const g = c.value[m];
|
|
26
26
|
return typeof g == "boolean" ? g : typeof g == "object";
|
|
@@ -29,8 +29,8 @@ function q() {
|
|
|
29
29
|
config: a,
|
|
30
30
|
components: t,
|
|
31
31
|
navigation: e,
|
|
32
|
-
locales:
|
|
33
|
-
defaultLocale:
|
|
32
|
+
locales: s,
|
|
33
|
+
defaultLocale: n,
|
|
34
34
|
branding: r,
|
|
35
35
|
footer: i,
|
|
36
36
|
features: c,
|
|
@@ -41,11 +41,11 @@ function H(o) {
|
|
|
41
41
|
function a(t) {
|
|
42
42
|
const e = document.documentElement;
|
|
43
43
|
if (t.colors)
|
|
44
|
-
for (const [
|
|
45
|
-
|
|
44
|
+
for (const [s, n] of Object.entries(t.colors))
|
|
45
|
+
n && e.style.setProperty(`--x-color-${D(s)}`, n);
|
|
46
46
|
t.typography?.fontFamily && e.style.setProperty("--x-font-family", t.typography.fontFamily), t.typography?.headingFont && e.style.setProperty("--x-font-heading", t.typography.headingFont), t.typography?.baseFontSize && e.style.setProperty("--x-font-size", t.typography.baseFontSize), t.shape?.borderRadius && e.style.setProperty("--x-border-radius", t.shape.borderRadius), t.shape?.maxWidth && e.style.setProperty("--x-max-width", t.shape.maxWidth);
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
S(() => {
|
|
49
49
|
if (o) {
|
|
50
50
|
a(o);
|
|
51
51
|
return;
|
|
@@ -63,8 +63,8 @@ function D(o) {
|
|
|
63
63
|
}
|
|
64
64
|
function Q() {
|
|
65
65
|
const a = window.__SITE_DATA__?.apiBase || "";
|
|
66
|
-
async function t(
|
|
67
|
-
const r = await fetch(`${a}${
|
|
66
|
+
async function t(n) {
|
|
67
|
+
const r = await fetch(`${a}${n}`);
|
|
68
68
|
if (!r.ok)
|
|
69
69
|
throw new Error(`API error: ${r.status}`);
|
|
70
70
|
return r.json();
|
|
@@ -72,16 +72,16 @@ function Q() {
|
|
|
72
72
|
async function e() {
|
|
73
73
|
return t("/v1/billing/tariffs");
|
|
74
74
|
}
|
|
75
|
-
async function
|
|
75
|
+
async function s() {
|
|
76
76
|
return t("/v1/billing/services");
|
|
77
77
|
}
|
|
78
78
|
return {
|
|
79
79
|
get: t,
|
|
80
80
|
getTariffs: e,
|
|
81
|
-
getProducts:
|
|
81
|
+
getProducts: s
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function $() {
|
|
85
85
|
try {
|
|
86
86
|
return window.self !== window.top;
|
|
87
87
|
} catch {
|
|
@@ -89,82 +89,83 @@ function A() {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
function V(o) {
|
|
92
|
-
|
|
92
|
+
$() && o.beforeEach((a, t) => !t.name);
|
|
93
93
|
}
|
|
94
94
|
function C() {
|
|
95
|
-
const o =
|
|
96
|
-
function
|
|
97
|
-
const n =
|
|
98
|
-
n?.type === "xosen-preview-update" && n.page && (o.value = n.page, a.value = !0);
|
|
95
|
+
const o = v(null), a = v(null), t = v($());
|
|
96
|
+
function e(s) {
|
|
97
|
+
const n = s.data;
|
|
98
|
+
n?.type === "xosen-preview-update" && n.page && (o.value = n.page, n.site && (a.value = n.site), t.value = !0);
|
|
99
99
|
}
|
|
100
|
-
return
|
|
101
|
-
window.addEventListener("message",
|
|
100
|
+
return S(() => {
|
|
101
|
+
window.addEventListener("message", e);
|
|
102
102
|
}), N(() => {
|
|
103
|
-
window.removeEventListener("message",
|
|
103
|
+
window.removeEventListener("message", e);
|
|
104
104
|
}), {
|
|
105
105
|
previewPage: o,
|
|
106
|
-
|
|
106
|
+
previewSite: a,
|
|
107
|
+
isPreviewMode: t
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
|
-
const R = /* @__PURE__ */ Symbol("pageContext"),
|
|
110
|
+
const R = /* @__PURE__ */ Symbol("pageContext"), k = L({});
|
|
110
111
|
function j() {
|
|
111
112
|
const o = window.__SITE_DATA__;
|
|
112
113
|
if (o)
|
|
113
114
|
for (const a of Object.keys(o))
|
|
114
|
-
a.startsWith("page:") && (
|
|
115
|
+
a.startsWith("page:") && (k[a] = o[a]);
|
|
115
116
|
}
|
|
116
117
|
j();
|
|
117
118
|
function ee(o) {
|
|
118
|
-
const { locale: a } =
|
|
119
|
-
const l = e.value ? t.value :
|
|
119
|
+
const { locale: a } = T(), { previewPage: t, isPreviewMode: e } = C(), s = window.__SITE_DATA__, n = v(null), r = v(!0), i = v(!1), c = u(() => typeof o == "string" ? o : o.value), p = u(() => `page:${c.value}`), m = u(() => {
|
|
120
|
+
const l = e.value ? t.value : n.value;
|
|
120
121
|
if (!l) return {};
|
|
121
122
|
const { blocks: f, meta: d, slug: W, locale: J, ...E } = l;
|
|
122
123
|
return E;
|
|
123
124
|
});
|
|
124
125
|
P(R, m);
|
|
125
|
-
const g = u(() => e.value && t.value?.slug === c.value ? t.value.blocks || [] :
|
|
126
|
+
const g = u(() => e.value && t.value?.slug === c.value ? t.value.blocks || [] : n.value?.blocks || []);
|
|
126
127
|
function b(l) {
|
|
127
|
-
|
|
128
|
+
n.value = l, l && (k[p.value] = l);
|
|
128
129
|
}
|
|
129
130
|
function y() {
|
|
130
|
-
const l =
|
|
131
|
-
return l && a.value ===
|
|
131
|
+
const l = s?.[p.value];
|
|
132
|
+
return l && a.value === s?.locale ? (b(l), i.value = !1, w(), !0) : !1;
|
|
132
133
|
}
|
|
133
134
|
async function h() {
|
|
134
135
|
r.value = !0, i.value = !1;
|
|
135
136
|
try {
|
|
136
137
|
const l = await fetch(`/api/content/${p.value}:${a.value}`);
|
|
137
|
-
l.ok ? (b(await l.json()),
|
|
138
|
+
l.ok ? (b(await l.json()), w()) : (b(null), i.value = !0);
|
|
138
139
|
} catch {
|
|
139
140
|
b(null), i.value = !0;
|
|
140
141
|
} finally {
|
|
141
142
|
r.value = !1;
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
|
-
function
|
|
145
|
-
if (!
|
|
146
|
-
if (
|
|
147
|
-
const f =
|
|
148
|
-
document.title = `${
|
|
145
|
+
function w() {
|
|
146
|
+
if (!n.value) return;
|
|
147
|
+
if (n.value.title) {
|
|
148
|
+
const f = s?.config?.branding, d = f?.siteName ? ` — ${f.siteName}` : "";
|
|
149
|
+
document.title = `${n.value.title}${d}`;
|
|
149
150
|
}
|
|
150
|
-
const l =
|
|
151
|
+
const l = n.value.meta?.description;
|
|
151
152
|
if (l) {
|
|
152
153
|
const f = document.querySelector('meta[name="description"]');
|
|
153
154
|
f && f.setAttribute("content", l);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
return _(t, (l) => {
|
|
157
|
-
l?.slug === c.value && (r.value = !1, i.value = !1, (l.title || l.blocks) && (
|
|
158
|
-
...
|
|
158
|
+
l?.slug === c.value && (r.value = !1, i.value = !1, (l.title || l.blocks) && (n.value = {
|
|
159
|
+
...n.value || {},
|
|
159
160
|
...l.title ? { title: l.title } : {},
|
|
160
161
|
...l.blocks ? { blocks: l.blocks } : {}
|
|
161
|
-
},
|
|
162
|
-
}),
|
|
162
|
+
}, w()));
|
|
163
|
+
}), S(() => {
|
|
163
164
|
y() ? r.value = !1 : h();
|
|
164
165
|
}), _(a, () => h()), _(c, () => {
|
|
165
166
|
y() ? r.value = !1 : h();
|
|
166
167
|
}), {
|
|
167
|
-
page:
|
|
168
|
+
page: n,
|
|
168
169
|
blocks: g,
|
|
169
170
|
pageContext: m,
|
|
170
171
|
loading: r,
|
|
@@ -174,36 +175,38 @@ function ee(o) {
|
|
|
174
175
|
};
|
|
175
176
|
}
|
|
176
177
|
function te() {
|
|
177
|
-
const { locale: o } =
|
|
178
|
-
async function
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
178
|
+
const { locale: o } = T(), a = window.__SITE_DATA__, t = a?.locale, e = v({ ...a?.components || {} });
|
|
179
|
+
async function s(r) {
|
|
180
|
+
if (!$()) {
|
|
181
|
+
if (r === t) {
|
|
182
|
+
e.value = { ...a?.components || {} };
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const i = await fetch(`/api/content/components:${r}`);
|
|
187
|
+
i.ok && (e.value = await i.json());
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
187
190
|
}
|
|
188
191
|
}
|
|
189
|
-
_(o, (r) =>
|
|
190
|
-
o.value !== t &&
|
|
192
|
+
_(o, (r) => s(r)), S(() => {
|
|
193
|
+
o.value !== t && s(o.value);
|
|
191
194
|
});
|
|
192
|
-
function
|
|
195
|
+
function n(r) {
|
|
193
196
|
return u(() => e.value[r] || null);
|
|
194
197
|
}
|
|
195
198
|
return {
|
|
196
199
|
/** All components (reactive, locale-aware) */
|
|
197
200
|
components: e,
|
|
198
201
|
/** Get a single component by key */
|
|
199
|
-
getComponent:
|
|
202
|
+
getComponent: n,
|
|
200
203
|
/** Force refetch for current locale */
|
|
201
|
-
reload: () =>
|
|
204
|
+
reload: () => s(o.value)
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
function ae() {
|
|
205
|
-
const { locale: o } =
|
|
206
|
-
function
|
|
208
|
+
const { locale: o } = T(), a = window.__SITE_DATA__, t = u(() => a?.config?.locales || []), e = u(() => a?.config?.defaultLocale || "en"), s = u(() => t.value.length > 1);
|
|
209
|
+
function n(i) {
|
|
207
210
|
o.value = i, localStorage.setItem("x-site-locale", i);
|
|
208
211
|
}
|
|
209
212
|
function r(i) {
|
|
@@ -213,8 +216,8 @@ function ae() {
|
|
|
213
216
|
locale: o,
|
|
214
217
|
locales: t,
|
|
215
218
|
defaultLocale: e,
|
|
216
|
-
hasMultipleLocales:
|
|
217
|
-
switchLocale:
|
|
219
|
+
hasMultipleLocales: s,
|
|
220
|
+
switchLocale: n,
|
|
218
221
|
resolveText: r
|
|
219
222
|
};
|
|
220
223
|
}
|
|
@@ -245,10 +248,10 @@ const O = {
|
|
|
245
248
|
main: { type: "slot" }
|
|
246
249
|
}
|
|
247
250
|
};
|
|
248
|
-
function
|
|
251
|
+
function x(o, a) {
|
|
249
252
|
const t = [];
|
|
250
253
|
if (o.areas?.length) {
|
|
251
|
-
const e = o.areas.map((
|
|
254
|
+
const e = o.areas.map((s) => `"${s}"`).join(" ");
|
|
252
255
|
t.push(`grid-template-areas: ${e}`);
|
|
253
256
|
}
|
|
254
257
|
return o.columns && t.push(`grid-template-columns: ${o.columns}`), o.rows && t.push(`grid-template-rows: ${o.rows}`), o.gap && t.push(`gap: ${o.gap}`), t.length ? `${a} { ${t.join("; ")}; }` : "";
|
|
@@ -258,26 +261,26 @@ function G(o, a) {
|
|
|
258
261
|
if (!e) return t.join(`
|
|
259
262
|
`);
|
|
260
263
|
if (e.default) {
|
|
261
|
-
const
|
|
262
|
-
|
|
264
|
+
const s = x(e.default, a);
|
|
265
|
+
s && t.push(s);
|
|
263
266
|
}
|
|
264
|
-
for (const
|
|
265
|
-
const
|
|
266
|
-
if (!
|
|
267
|
-
const r =
|
|
268
|
-
r && t.push(`@media (min-width: ${O[
|
|
267
|
+
for (const s of ["sm", "md", "lg", "xl"]) {
|
|
268
|
+
const n = e[s];
|
|
269
|
+
if (!n) continue;
|
|
270
|
+
const r = x(n, a);
|
|
271
|
+
r && t.push(`@media (min-width: ${O[s]}px) { ${r} }`);
|
|
269
272
|
}
|
|
270
273
|
return t.join(`
|
|
271
274
|
`);
|
|
272
275
|
}
|
|
273
276
|
function oe() {
|
|
274
|
-
const o = I(), { previewPage: a, isPreviewMode: t } = C(), e = window.__SITE_DATA__,
|
|
277
|
+
const o = I(), { previewPage: a, isPreviewMode: t } = C(), e = window.__SITE_DATA__, s = `dl-${Math.random().toString(36).slice(2, 8)}`, n = u(() => ["dynamic-layout", s]), r = u(() => {
|
|
275
278
|
if (o.path === "/_preview_")
|
|
276
279
|
return a.value?.layout || "landing";
|
|
277
280
|
if (t.value && a.value?.layout)
|
|
278
281
|
return a.value.layout;
|
|
279
282
|
const p = o.path === "/" ? "page:home" : `page:${o.params.slug || ""}`;
|
|
280
|
-
return (
|
|
283
|
+
return (k[p] || e?.[p])?.layout || "default";
|
|
281
284
|
}), i = u(() => {
|
|
282
285
|
const p = e?.config?.layouts;
|
|
283
286
|
if (p) {
|
|
@@ -285,12 +288,12 @@ function oe() {
|
|
|
285
288
|
if (m?.zones) return m;
|
|
286
289
|
}
|
|
287
290
|
return r.value === "landing" ? F : z;
|
|
288
|
-
}), c = u(() => G(i.value, `.${
|
|
291
|
+
}), c = u(() => G(i.value, `.${s}`));
|
|
289
292
|
return {
|
|
290
293
|
layoutName: r,
|
|
291
294
|
layoutConfig: i,
|
|
292
295
|
layoutCSS: c,
|
|
293
|
-
layoutClass:
|
|
296
|
+
layoutClass: n
|
|
294
297
|
};
|
|
295
298
|
}
|
|
296
299
|
const ne = {
|
|
@@ -299,7 +302,13 @@ const ne = {
|
|
|
299
302
|
description: "CSS Grid layout with responsive breakpoints and component zones",
|
|
300
303
|
fields: [
|
|
301
304
|
// Mobile (default)
|
|
302
|
-
{
|
|
305
|
+
{
|
|
306
|
+
key: "grid.default.areas",
|
|
307
|
+
type: "grid-areas",
|
|
308
|
+
label: "Grid Areas (mobile)",
|
|
309
|
+
breakpoint: "default",
|
|
310
|
+
required: !0
|
|
311
|
+
},
|
|
303
312
|
{ key: "grid.default.columns", type: "text", label: "Columns (mobile)", breakpoint: "default" },
|
|
304
313
|
{ key: "grid.default.rows", type: "text", label: "Rows (mobile)", breakpoint: "default" },
|
|
305
314
|
{ key: "grid.default.gap", type: "text", label: "Gap (mobile)", breakpoint: "default" },
|
|
@@ -385,7 +394,12 @@ const ne = {
|
|
|
385
394
|
config: {
|
|
386
395
|
grid: {
|
|
387
396
|
default: { areas: ["header", "main", "aside", "footer"], columns: "1fr", rows: "auto 1fr auto auto" },
|
|
388
|
-
md: {
|
|
397
|
+
md: {
|
|
398
|
+
areas: ["header header header", "main main aside", "footer footer footer"],
|
|
399
|
+
columns: "1fr 1fr 300px",
|
|
400
|
+
rows: "auto 1fr auto",
|
|
401
|
+
gap: "24px"
|
|
402
|
+
}
|
|
389
403
|
},
|
|
390
404
|
zones: {
|
|
391
405
|
header: { component: "nav-menu" },
|
|
@@ -400,9 +414,9 @@ function U(o, a) {
|
|
|
400
414
|
const e = new URL(o.url).searchParams.get("lang");
|
|
401
415
|
if (e && a.supportedLocales?.includes(e)) return e;
|
|
402
416
|
if (a.supportedLocales?.length) {
|
|
403
|
-
const
|
|
404
|
-
for (const
|
|
405
|
-
if (
|
|
417
|
+
const s = o.headers.get("Accept-Language") || "";
|
|
418
|
+
for (const n of a.supportedLocales)
|
|
419
|
+
if (s.includes(n)) return n;
|
|
406
420
|
}
|
|
407
421
|
return a.defaultLocale;
|
|
408
422
|
}
|
|
@@ -410,12 +424,12 @@ function K(o, a, t) {
|
|
|
410
424
|
if (!o.pathname.startsWith("/api/content/")) return null;
|
|
411
425
|
const e = decodeURIComponent(o.pathname.replace("/api/content/", ""));
|
|
412
426
|
return e ? (async () => {
|
|
413
|
-
let
|
|
414
|
-
if (!
|
|
415
|
-
const
|
|
416
|
-
|
|
427
|
+
let s = await a.SITE_CONTENT.get(e);
|
|
428
|
+
if (!s) {
|
|
429
|
+
const n = e.lastIndexOf(":");
|
|
430
|
+
n > 0 && e.slice(n + 1) !== t && (s = await a.SITE_CONTENT.get(e.slice(0, n + 1) + t));
|
|
417
431
|
}
|
|
418
|
-
return
|
|
432
|
+
return s ? new Response(s, {
|
|
419
433
|
headers: {
|
|
420
434
|
"Content-Type": "application/json",
|
|
421
435
|
"Cache-Control": "public, max-age=60",
|
|
@@ -424,29 +438,29 @@ function K(o, a, t) {
|
|
|
424
438
|
}) : Response.json({ error: "Not found" }, { status: 404 });
|
|
425
439
|
})() : Promise.resolve(Response.json({ error: "Key is required" }, { status: 400 }));
|
|
426
440
|
}
|
|
427
|
-
function
|
|
441
|
+
function A(o, a, t) {
|
|
428
442
|
let e = o;
|
|
429
443
|
if (a.title) {
|
|
430
|
-
const
|
|
431
|
-
e = e.replace(/<title>[^<]*<\/title>/, `<title>${a.title}${
|
|
444
|
+
const s = t ? ` — ${t}` : "";
|
|
445
|
+
e = e.replace(/<title>[^<]*<\/title>/, `<title>${a.title}${s}</title>`);
|
|
432
446
|
}
|
|
433
447
|
return a.meta?.description && (e = e.replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${a.meta.description}$2`)), a.meta?.ogTitle && (e = e.replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${a.meta.ogTitle}$2`)), a.html && (e = e.replace('<div id="app"></div>', `<div id="app">${a.html}</div>`)), e;
|
|
434
448
|
}
|
|
435
449
|
function re(o) {
|
|
436
450
|
return {
|
|
437
451
|
async fetch(a, t) {
|
|
438
|
-
const e = new URL(a.url),
|
|
439
|
-
if (
|
|
452
|
+
const e = new URL(a.url), s = K(e, t, o.defaultLocale);
|
|
453
|
+
if (s) return s;
|
|
440
454
|
if (M.test(e.pathname))
|
|
441
455
|
return t.ASSETS.fetch(a);
|
|
442
|
-
const
|
|
456
|
+
const n = U(a, o), r = new URL("/index.html", a.url);
|
|
443
457
|
let c = await (await t.ASSETS.fetch(new Request(r))).text();
|
|
444
458
|
const p = o.defaultLocale, [m, g] = await Promise.all([
|
|
445
459
|
t.SITE_CONTENT.get("config"),
|
|
446
|
-
t.SITE_CONTENT.get(`content:${
|
|
460
|
+
t.SITE_CONTENT.get(`content:${n}`)
|
|
447
461
|
]), b = m ? JSON.parse(m) : {};
|
|
448
462
|
let y = g ? JSON.parse(g) : {};
|
|
449
|
-
if (!g &&
|
|
463
|
+
if (!g && n !== p) {
|
|
450
464
|
const f = await t.SITE_CONTENT.get(`content:${p}`);
|
|
451
465
|
f && (y = JSON.parse(f));
|
|
452
466
|
}
|
|
@@ -454,22 +468,22 @@ function re(o) {
|
|
|
454
468
|
if (h) {
|
|
455
469
|
const f = h[1];
|
|
456
470
|
if (!y[f]) {
|
|
457
|
-
let d = await t.SITE_CONTENT.get(`page:${f}:${
|
|
458
|
-
!d &&
|
|
471
|
+
let d = await t.SITE_CONTENT.get(`page:${f}:${n}`);
|
|
472
|
+
!d && n !== p && (d = await t.SITE_CONTENT.get(`page:${f}:${p}`)), d && (y[f] = JSON.parse(d));
|
|
459
473
|
}
|
|
460
474
|
}
|
|
461
|
-
const
|
|
462
|
-
locale:
|
|
475
|
+
const w = {
|
|
476
|
+
locale: n,
|
|
463
477
|
config: b
|
|
464
478
|
};
|
|
465
479
|
for (const [f, d] of Object.entries(y))
|
|
466
|
-
|
|
480
|
+
w[`page:${f}`] = d;
|
|
467
481
|
if (h) {
|
|
468
|
-
const f = h[1], d =
|
|
469
|
-
d && (c =
|
|
482
|
+
const f = h[1], d = w[`page:${f}`];
|
|
483
|
+
d && (c = A(c, d, o.siteName));
|
|
470
484
|
}
|
|
471
|
-
e.pathname === "/" && y.home && (c =
|
|
472
|
-
const l = `<script>window.__SITE_DATA__ = ${JSON.stringify(
|
|
485
|
+
e.pathname === "/" && y.home && (c = A(c, y.home, o.siteName));
|
|
486
|
+
const l = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
|
|
473
487
|
return c = c.replace("</head>", `${l}
|
|
474
488
|
</head>`), new Response(c, {
|
|
475
489
|
headers: {
|
|
@@ -484,7 +498,7 @@ export {
|
|
|
484
498
|
R as PAGE_CONTEXT_KEY,
|
|
485
499
|
G as buildLayoutCSS,
|
|
486
500
|
re as createSiteWorker,
|
|
487
|
-
|
|
501
|
+
$ as isInPreview,
|
|
488
502
|
se as layoutPresets,
|
|
489
503
|
ne as layoutSchema,
|
|
490
504
|
V as setupPreviewRouter,
|
package/dist/types/blocks.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ export interface HeroBlockData {
|
|
|
18
18
|
badge?: string;
|
|
19
19
|
image?: string;
|
|
20
20
|
backgroundImage?: string;
|
|
21
|
-
|
|
21
|
+
/** Overlay: false to disable, number (0-1) for opacity, or CSS string for custom gradient */
|
|
22
|
+
overlay?: boolean | number | string;
|
|
22
23
|
buttons?: Array<{
|
|
23
24
|
text: string;
|
|
24
25
|
url: string;
|
package/dist/types/layout.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xosen/site-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Shared Vue components and composables for Xosen site templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,23 +24,23 @@
|
|
|
24
24
|
"dist",
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
29
|
+
"dev": "vite build --watch",
|
|
30
|
+
"clean": "rm -rf dist"
|
|
31
|
+
},
|
|
27
32
|
"peerDependencies": {
|
|
28
33
|
"vue": "^3.5.0",
|
|
29
34
|
"vue-i18n": "^11.0.0",
|
|
30
35
|
"vue-router": "^5.0.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
|
-
"vue": "
|
|
34
|
-
"vue-i18n": "
|
|
35
|
-
"vue-router": "
|
|
36
|
-
"vue-tsc": "
|
|
37
|
-
"typescript": "
|
|
38
|
-
"vite": "
|
|
39
|
-
"@vitejs/plugin-vue": "
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
43
|
-
"dev": "vite build --watch",
|
|
44
|
-
"clean": "rm -rf dist"
|
|
38
|
+
"vue": "catalog:",
|
|
39
|
+
"vue-i18n": "catalog:",
|
|
40
|
+
"vue-router": "catalog:",
|
|
41
|
+
"vue-tsc": "catalog:",
|
|
42
|
+
"typescript": "catalog:",
|
|
43
|
+
"vite": "catalog:",
|
|
44
|
+
"@vitejs/plugin-vue": "catalog:"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { ref, onMounted, onUnmounted } from 'vue';
|
|
2
2
|
import type { Router } from 'vue-router';
|
|
3
3
|
|
|
4
|
+
export interface PreviewSiteData {
|
|
5
|
+
config?: Record<string, any>;
|
|
6
|
+
skin?: Record<string, any>;
|
|
7
|
+
components?: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
export interface PreviewMessage {
|
|
5
11
|
type: 'xosen-preview-update';
|
|
12
|
+
site?: PreviewSiteData;
|
|
6
13
|
page: {
|
|
7
14
|
slug: string;
|
|
8
15
|
locale: string;
|
|
@@ -45,12 +52,16 @@ export function setupPreviewRouter(router: Router) {
|
|
|
45
52
|
*/
|
|
46
53
|
export function useLivePreview() {
|
|
47
54
|
const previewPage = ref<PreviewMessage['page'] | null>(null);
|
|
55
|
+
const previewSite = ref<PreviewSiteData | null>(null);
|
|
48
56
|
const isPreviewMode = ref(isInPreview());
|
|
49
57
|
|
|
50
58
|
function handleMessage(event: MessageEvent) {
|
|
51
59
|
const data = event.data;
|
|
52
60
|
if (data?.type === 'xosen-preview-update' && data.page) {
|
|
53
61
|
previewPage.value = data.page;
|
|
62
|
+
if (data.site) {
|
|
63
|
+
previewSite.value = data.site;
|
|
64
|
+
}
|
|
54
65
|
isPreviewMode.value = true;
|
|
55
66
|
}
|
|
56
67
|
}
|
|
@@ -65,6 +76,7 @@ export function useLivePreview() {
|
|
|
65
76
|
|
|
66
77
|
return {
|
|
67
78
|
previewPage,
|
|
79
|
+
previewSite,
|
|
68
80
|
isPreviewMode,
|
|
69
81
|
};
|
|
70
82
|
}
|
|
@@ -40,7 +40,7 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
40
40
|
const loading = ref(true);
|
|
41
41
|
const notFound = ref(false);
|
|
42
42
|
|
|
43
|
-
const resolvedSlug = computed(() => typeof slug === 'string' ? slug : slug.value);
|
|
43
|
+
const resolvedSlug = computed(() => (typeof slug === 'string' ? slug : slug.value));
|
|
44
44
|
const pageKey = computed(() => `page:${resolvedSlug.value}`);
|
|
45
45
|
|
|
46
46
|
// Page context: page metadata without blocks (for injection)
|
|
@@ -121,7 +121,7 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
121
121
|
notFound.value = false;
|
|
122
122
|
if (p.title || p.blocks) {
|
|
123
123
|
page.value = {
|
|
124
|
-
...(page.value || {} as PageConfig),
|
|
124
|
+
...(page.value || ({} as PageConfig)),
|
|
125
125
|
...(p.title ? { title: p.title } : {}),
|
|
126
126
|
...(p.blocks ? { blocks: p.blocks as Block[] } : {}),
|
|
127
127
|
};
|
|
@@ -2,6 +2,7 @@ import { ref, computed, watch, onMounted, type Ref } from 'vue';
|
|
|
2
2
|
import { useI18n } from 'vue-i18n';
|
|
3
3
|
|
|
4
4
|
import type { SiteData } from '../types/config.js';
|
|
5
|
+
import { isInPreview } from './useLivePreview.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Locale-aware access to site components (navigation, footer, sidebar, etc.).
|
|
@@ -14,12 +15,15 @@ export function useSiteComponents() {
|
|
|
14
15
|
const siteData = (window as any).__SITE_DATA__ as SiteData | undefined;
|
|
15
16
|
const initialLocale = siteData?.locale;
|
|
16
17
|
|
|
17
|
-
const components = ref<Record<string, any>>({ ...(siteData as any)?.components || {} });
|
|
18
|
+
const components = ref<Record<string, any>>({ ...((siteData as any)?.components || {}) });
|
|
18
19
|
|
|
19
20
|
async function fetchComponents(loc: string) {
|
|
21
|
+
// Skip fetching in preview mode — components come via postMessage
|
|
22
|
+
if (isInPreview()) return;
|
|
23
|
+
|
|
20
24
|
// If same as initial locale, use preloaded data
|
|
21
25
|
if (loc === initialLocale) {
|
|
22
|
-
components.value = { ...(siteData as any)?.components || {} };
|
|
26
|
+
components.value = { ...((siteData as any)?.components || {}) };
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
29
|
try {
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
|
11
11
|
export { useDynamicLayout, buildLayoutCSS } from './composables/useDynamicLayout.js';
|
|
12
12
|
export type { LayoutConfig, LayoutZone, GridBreakpoint, LayoutSchemaDefinition, LayoutField } from './types/layout.js';
|
|
13
13
|
export { layoutSchema, layoutPresets } from './types/layout.js';
|
|
14
|
-
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
14
|
+
export type { PreviewMessage, PreviewSiteData } from './composables/useLivePreview.js';
|
|
15
15
|
|
|
16
16
|
// Worker
|
|
17
17
|
export { createSiteWorker } from './worker/index.js';
|
package/src/types/blocks.ts
CHANGED
|
@@ -20,7 +20,8 @@ export interface HeroBlockData {
|
|
|
20
20
|
badge?: string;
|
|
21
21
|
image?: string;
|
|
22
22
|
backgroundImage?: string;
|
|
23
|
-
|
|
23
|
+
/** Overlay: false to disable, number (0-1) for opacity, or CSS string for custom gradient */
|
|
24
|
+
overlay?: boolean | number | string;
|
|
24
25
|
buttons?: Array<{ text: string; url: string; variant?: 'primary' | 'outline' | 'white' }>;
|
|
25
26
|
stats?: Array<{ value: string; label: string }>;
|
|
26
27
|
}
|
package/src/types/layout.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface LayoutZone {
|
|
|
14
14
|
type?: 'slot';
|
|
15
15
|
component?: string;
|
|
16
16
|
class?: string;
|
|
17
|
+
style?: Record<string, string>;
|
|
17
18
|
container?: boolean;
|
|
18
19
|
containerStyle?: Record<string, string>;
|
|
19
20
|
}
|
|
@@ -50,7 +51,13 @@ export const layoutSchema: LayoutSchemaDefinition = {
|
|
|
50
51
|
description: 'CSS Grid layout with responsive breakpoints and component zones',
|
|
51
52
|
fields: [
|
|
52
53
|
// Mobile (default)
|
|
53
|
-
{
|
|
54
|
+
{
|
|
55
|
+
key: 'grid.default.areas',
|
|
56
|
+
type: 'grid-areas',
|
|
57
|
+
label: 'Grid Areas (mobile)',
|
|
58
|
+
breakpoint: 'default',
|
|
59
|
+
required: true,
|
|
60
|
+
},
|
|
54
61
|
{ key: 'grid.default.columns', type: 'text', label: 'Columns (mobile)', breakpoint: 'default' },
|
|
55
62
|
{ key: 'grid.default.rows', type: 'text', label: 'Rows (mobile)', breakpoint: 'default' },
|
|
56
63
|
{ key: 'grid.default.gap', type: 'text', label: 'Gap (mobile)', breakpoint: 'default' },
|
|
@@ -145,7 +152,12 @@ export const layoutPresets: Record<string, { displayName: string; config: Layout
|
|
|
145
152
|
config: {
|
|
146
153
|
grid: {
|
|
147
154
|
default: { areas: ['header', 'main', 'aside', 'footer'], columns: '1fr', rows: 'auto 1fr auto auto' },
|
|
148
|
-
md: {
|
|
155
|
+
md: {
|
|
156
|
+
areas: ['header header header', 'main main aside', 'footer footer footer'],
|
|
157
|
+
columns: '1fr 1fr 300px',
|
|
158
|
+
rows: 'auto 1fr auto',
|
|
159
|
+
gap: '24px',
|
|
160
|
+
},
|
|
149
161
|
},
|
|
150
162
|
zones: {
|
|
151
163
|
header: { component: 'nav-menu' },
|