@xiwebdesign/xicms-core 4.17.1 → 4.18.0

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.
Files changed (81) hide show
  1. package/dist/XiCMSProvider.js +3 -1
  2. package/dist/XiCMSProvider.js.map +1 -1
  3. package/dist/admin/blockEditors/index.js +47 -14
  4. package/dist/admin/blockEditors/index.js.map +1 -1
  5. package/dist/admin/lib/compressImage.d.ts +4 -2
  6. package/dist/admin/lib/compressImage.js +7 -5
  7. package/dist/admin/lib/compressImage.js.map +1 -1
  8. package/dist/admin/lib/mediaUsage.d.ts +4 -3
  9. package/dist/admin/lib/mediaUsage.js +21 -10
  10. package/dist/admin/lib/mediaUsage.js.map +1 -1
  11. package/dist/admin/lib/upload.d.ts +4 -0
  12. package/dist/admin/lib/upload.js +26 -6
  13. package/dist/admin/lib/upload.js.map +1 -1
  14. package/dist/admin/media/MediaManager.js +23 -7
  15. package/dist/admin/media/MediaManager.js.map +1 -1
  16. package/dist/admin/media/MediaPicker.js +12 -2
  17. package/dist/admin/media/MediaPicker.js.map +1 -1
  18. package/dist/admin/settings/SettingsPanel.js +4 -1
  19. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  20. package/dist/admin/visual/VisualEditor.js +4 -1
  21. package/dist/admin/visual/VisualEditor.js.map +1 -1
  22. package/dist/blocks/builtins/Agenda.d.ts +6 -0
  23. package/dist/blocks/builtins/Agenda.js +14 -6
  24. package/dist/blocks/builtins/Agenda.js.map +1 -1
  25. package/dist/blocks/builtins/Breadcrumbs.js +5 -1
  26. package/dist/blocks/builtins/Breadcrumbs.js.map +1 -1
  27. package/dist/blocks/builtins/Cards.js +3 -2
  28. package/dist/blocks/builtins/Cards.js.map +1 -1
  29. package/dist/blocks/builtins/Contact.js +2 -1
  30. package/dist/blocks/builtins/Contact.js.map +1 -1
  31. package/dist/blocks/builtins/Faq.js +5 -4
  32. package/dist/blocks/builtins/Faq.js.map +1 -1
  33. package/dist/blocks/builtins/Gallery.js +2 -2
  34. package/dist/blocks/builtins/Gallery.js.map +1 -1
  35. package/dist/blocks/builtins/Jobs.d.ts +8 -1
  36. package/dist/blocks/builtins/Jobs.js +37 -21
  37. package/dist/blocks/builtins/Jobs.js.map +1 -1
  38. package/dist/blocks/builtins/MapBlock.js +31 -4
  39. package/dist/blocks/builtins/MapBlock.js.map +1 -1
  40. package/dist/blocks/builtins/PdfBlock.js +2 -3
  41. package/dist/blocks/builtins/PdfBlock.js.map +1 -1
  42. package/dist/blocks/builtins/Portfolio.js +2 -2
  43. package/dist/blocks/builtins/Portfolio.js.map +1 -1
  44. package/dist/blocks/builtins/Pricing.d.ts +2 -0
  45. package/dist/blocks/builtins/Pricing.js +18 -10
  46. package/dist/blocks/builtins/Pricing.js.map +1 -1
  47. package/dist/blocks/builtins/Sections.js +3 -2
  48. package/dist/blocks/builtins/Sections.js.map +1 -1
  49. package/dist/blocks/builtins/Slider.js +21 -3
  50. package/dist/blocks/builtins/Slider.js.map +1 -1
  51. package/dist/blocks/builtins/Testimonials.js +21 -18
  52. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  53. package/dist/blocks/builtins/WhatsappChat.js +1 -1
  54. package/dist/blocks/builtins/WhatsappChat.js.map +1 -1
  55. package/dist/blocks/builtins/Widgets.js +2 -3
  56. package/dist/blocks/builtins/Widgets.js.map +1 -1
  57. package/dist/blocks/builtins/lib/ics.js +1 -1
  58. package/dist/blocks/builtins/lib/ics.js.map +1 -1
  59. package/dist/blocks/builtins/lib/openingHours.d.ts +4 -1
  60. package/dist/blocks/builtins/lib/openingHours.js +11 -5
  61. package/dist/blocks/builtins/lib/openingHours.js.map +1 -1
  62. package/dist/demo/demoStore.d.ts +11 -1
  63. package/dist/demo/demoStore.js +50 -0
  64. package/dist/demo/demoStore.js.map +1 -1
  65. package/dist/firebase/client.js +2 -1
  66. package/dist/firebase/client.js.map +1 -1
  67. package/dist/index.d.ts +1 -1
  68. package/dist/index.js +2 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/modules/builtins.js +1 -1
  71. package/dist/modules/builtins.js.map +1 -1
  72. package/dist/modules/consent/useConsent.js +15 -3
  73. package/dist/modules/consent/useConsent.js.map +1 -1
  74. package/dist/schema/blocks.d.ts +24 -3
  75. package/dist/schema/blocks.js +4 -1
  76. package/dist/schema/blocks.js.map +1 -1
  77. package/dist/server/seo.d.ts +2 -0
  78. package/dist/server/seo.js +1 -1
  79. package/dist/server/seo.js.map +1 -1
  80. package/dist/styles.css +35 -6
  81. package/package.json +1 -1
@@ -36,8 +36,12 @@ function parseTimeRange(hours) {
36
36
  const m = hours.replace(/\./g, ":").match(/(\d{1,2}):(\d{2})\s*[-–—]\s*(\d{1,2}):(\d{2})/);
37
37
  if (!m) return null;
38
38
  const open = Number(m[1]) * 60 + Number(m[2]);
39
- const close = Number(m[3]) * 60 + Number(m[4]);
40
- if (open >= close || close > 24 * 60) return null;
39
+ let close = Number(m[3]) * 60 + Number(m[4]);
40
+ if (close > 24 * 60 || open > 24 * 60) return null;
41
+ if (open >= close) {
42
+ if (close > 6 * 60) return null;
43
+ close += 24 * 60;
44
+ }
41
45
  return { open, close };
42
46
  }
43
47
  function isOpenNow(rows, now = /* @__PURE__ */ new Date()) {
@@ -49,8 +53,10 @@ function isOpenNow(rows, now = /* @__PURE__ */ new Date()) {
49
53
  if (days.length === 0) continue;
50
54
  const range = parseTimeRange(r.hours);
51
55
  sawParseable = sawParseable || range !== null || /gesloten/i.test(r.hours);
52
- if (!days.includes(day)) continue;
53
- if (range && minutes >= range.open && minutes < range.close) return true;
56
+ if (range) {
57
+ if (days.includes(day) && minutes >= range.open && minutes < range.close) return true;
58
+ if (range.close > 24 * 60 && days.includes((day + 6) % 7) && minutes < range.close - 24 * 60) return true;
59
+ }
54
60
  }
55
61
  return sawParseable ? false : null;
56
62
  }
@@ -65,7 +71,7 @@ function openingHoursSpecJsonLd(rows) {
65
71
  "@type": "OpeningHoursSpecification",
66
72
  dayOfWeek: days.map((d) => DAY_SCHEMA_NAMES[d]),
67
73
  opens: fmt(range.open),
68
- closes: fmt(range.close)
74
+ closes: fmt(range.close % (24 * 60))
69
75
  });
70
76
  }
71
77
  return out;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/blocks/builtins/lib/openingHours.ts"],"sourcesContent":["/**\n * Pure helpers rond openingstijden-rijen ({ day, hours } vrije tekst, bv.\n * \"Maandag t/m vrijdag\" + \"09:00 – 17:00\"). Best-effort parsen van de gangbare NL-notaties;\n * onparseerbare rijen doen gewoon niet mee (geen badge/JSON-LD is beter dan een foute).\n */\n\nexport interface HoursRow {\n day: string;\n hours: string;\n}\n\n/** Dagnamen → JS-dagindex (0 = zondag). Volledige namen én afkortingen. */\nconst DAYS: Record<string, number> = {\n zondag: 0, zo: 0,\n maandag: 1, ma: 1,\n dinsdag: 2, di: 2,\n woensdag: 3, wo: 3,\n donderdag: 4, do: 4,\n vrijdag: 5, vr: 5,\n zaterdag: 6, za: 6,\n};\n\nconst DAY_SCHEMA_NAMES = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\n\n/** \"Maandag t/m vrijdag\" / \"Ma–Vr\" / \"Zaterdag\" → JS-dagindexen; leeg als onbekend. */\nexport function parseDays(label: string): number[] {\n const norm = label.toLowerCase().replace(/\\./g, \"\");\n // Bereik: \"x t/m y\", \"x tm y\", \"x - y\", \"x – y\"\n const range = norm.match(/([a-z]+)\\s*(?:t\\/m|tm|tot en met|[-–])\\s*([a-z]+)/);\n if (range && DAYS[range[1]] !== undefined && DAYS[range[2]] !== undefined) {\n const from = DAYS[range[1]];\n const to = DAYS[range[2]];\n const out: number[] = [];\n for (let d = from; ; d = (d + 1) % 7) {\n out.push(d);\n if (d === to || out.length > 7) break;\n }\n return out;\n }\n // Opsomming of losse dag(en): \"ma, wo, vr\" / \"zaterdag\"\n const found = norm.match(/[a-z]+/g) ?? [];\n const days = found.map((w) => DAYS[w]).filter((d): d is number => d !== undefined);\n return [...new Set(days)];\n}\n\n/** \"09:00 – 17:00\" / \"9.00-17.00\" → minuten sinds middernacht; null bij \"Gesloten\"/onparseerbaar. */\nexport function parseTimeRange(hours: string): { open: number; close: number } | null {\n const m = hours.replace(/\\./g, \":\").match(/(\\d{1,2}):(\\d{2})\\s*[-–—]\\s*(\\d{1,2}):(\\d{2})/);\n if (!m) return null;\n const open = Number(m[1]) * 60 + Number(m[2]);\n const close = Number(m[3]) * 60 + Number(m[4]);\n if (open >= close || close > 24 * 60) return null;\n return { open, close };\n}\n\n/** Is er volgens de rijen NU open? null = niet te bepalen (niets parseerbaars). */\nexport function isOpenNow(rows: HoursRow[], now: Date = new Date()): boolean | null {\n const day = now.getDay();\n const minutes = now.getHours() * 60 + now.getMinutes();\n let sawParseable = false;\n for (const r of rows) {\n const days = parseDays(r.day);\n if (days.length === 0) continue;\n const range = parseTimeRange(r.hours);\n sawParseable = sawParseable || range !== null || /gesloten/i.test(r.hours);\n if (!days.includes(day)) continue;\n if (range && minutes >= range.open && minutes < range.close) return true;\n }\n return sawParseable ? false : null;\n}\n\n/** schema.org OpeningHoursSpecification-array uit de rijen (alleen parseerbare rijen). */\nexport function openingHoursSpecJsonLd(rows: HoursRow[]): Array<Record<string, unknown>> {\n const fmt = (mins: number) => `${String(Math.floor(mins / 60)).padStart(2, \"0\")}:${String(mins % 60).padStart(2, \"0\")}`;\n const out: Array<Record<string, unknown>> = [];\n for (const r of rows) {\n const days = parseDays(r.day);\n const range = parseTimeRange(r.hours);\n if (days.length === 0 || !range) continue;\n out.push({\n \"@type\": \"OpeningHoursSpecification\",\n dayOfWeek: days.map((d) => DAY_SCHEMA_NAMES[d]),\n opens: fmt(range.open),\n closes: fmt(range.close),\n });\n }\n return out;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/blocks/builtins/lib/openingHours.ts"],"sourcesContent":["/**\n * Pure helpers rond openingstijden-rijen ({ day, hours } vrije tekst, bv.\n * \"Maandag t/m vrijdag\" + \"09:00 – 17:00\"). Best-effort parsen van de gangbare NL-notaties;\n * onparseerbare rijen doen gewoon niet mee (geen badge/JSON-LD is beter dan een foute).\n */\n\nexport interface HoursRow {\n day: string;\n hours: string;\n}\n\n/** Dagnamen → JS-dagindex (0 = zondag). Volledige namen én afkortingen. */\nconst DAYS: Record<string, number> = {\n zondag: 0, zo: 0,\n maandag: 1, ma: 1,\n dinsdag: 2, di: 2,\n woensdag: 3, wo: 3,\n donderdag: 4, do: 4,\n vrijdag: 5, vr: 5,\n zaterdag: 6, za: 6,\n};\n\nconst DAY_SCHEMA_NAMES = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\n\n/** \"Maandag t/m vrijdag\" / \"Ma–Vr\" / \"Zaterdag\" → JS-dagindexen; leeg als onbekend. */\nexport function parseDays(label: string): number[] {\n const norm = label.toLowerCase().replace(/\\./g, \"\");\n // Bereik: \"x t/m y\", \"x tm y\", \"x - y\", \"x – y\"\n const range = norm.match(/([a-z]+)\\s*(?:t\\/m|tm|tot en met|[-–])\\s*([a-z]+)/);\n if (range && DAYS[range[1]] !== undefined && DAYS[range[2]] !== undefined) {\n const from = DAYS[range[1]];\n const to = DAYS[range[2]];\n const out: number[] = [];\n for (let d = from; ; d = (d + 1) % 7) {\n out.push(d);\n if (d === to || out.length > 7) break;\n }\n return out;\n }\n // Opsomming of losse dag(en): \"ma, wo, vr\" / \"zaterdag\"\n const found = norm.match(/[a-z]+/g) ?? [];\n const days = found.map((w) => DAYS[w]).filter((d): d is number => d !== undefined);\n return [...new Set(days)];\n}\n\n/**\n * \"09:00 – 17:00\" / \"9.00-17.00\" → minuten sinds middernacht; null bij \"Gesloten\"/onparseerbaar.\n * Sluiten ná middernacht (horeca: \"18:00 – 02:00\") geeft close > 24u — isOpenNow rekent daarmee.\n */\nexport function parseTimeRange(hours: string): { open: number; close: number } | null {\n const m = hours.replace(/\\./g, \":\").match(/(\\d{1,2}):(\\d{2})\\s*[-–—]\\s*(\\d{1,2}):(\\d{2})/);\n if (!m) return null;\n const open = Number(m[1]) * 60 + Number(m[2]);\n let close = Number(m[3]) * 60 + Number(m[4]);\n if (close > 24 * 60 || open > 24 * 60) return null;\n if (open >= close) {\n // Alleen een sluittijd tot 06:00 telt als nachtzaak (\"18:00 – 02:00\");\n // iets als \"17:00 – 09:00\" is vrijwel zeker een omgedraaide typfout.\n if (close > 6 * 60) return null;\n close += 24 * 60; // over middernacht heen\n }\n return { open, close };\n}\n\n/** Is er volgens de rijen NU open? null = niet te bepalen (niets parseerbaars). */\nexport function isOpenNow(rows: HoursRow[], now: Date = new Date()): boolean | null {\n const day = now.getDay();\n const minutes = now.getHours() * 60 + now.getMinutes();\n let sawParseable = false;\n for (const r of rows) {\n const days = parseDays(r.day);\n if (days.length === 0) continue;\n const range = parseTimeRange(r.hours);\n sawParseable = sawParseable || range !== null || /gesloten/i.test(r.hours);\n if (range) {\n // Vandaag geopend (ook als de sluittijd na middernacht valt)…\n if (days.includes(day) && minutes >= range.open && minutes < range.close) return true;\n // …of gisteren geopend tot ná middernacht (bv. za 18:00–02:00 → zo 01:00 nog open).\n if (range.close > 24 * 60 && days.includes((day + 6) % 7) && minutes < range.close - 24 * 60) return true;\n }\n }\n return sawParseable ? false : null;\n}\n\n/** schema.org OpeningHoursSpecification-array uit de rijen (alleen parseerbare rijen). */\nexport function openingHoursSpecJsonLd(rows: HoursRow[]): Array<Record<string, unknown>> {\n const fmt = (mins: number) => `${String(Math.floor(mins / 60)).padStart(2, \"0\")}:${String(mins % 60).padStart(2, \"0\")}`;\n const out: Array<Record<string, unknown>> = [];\n for (const r of rows) {\n const days = parseDays(r.day);\n const range = parseTimeRange(r.hours);\n if (days.length === 0 || !range) continue;\n out.push({\n \"@type\": \"OpeningHoursSpecification\",\n dayOfWeek: days.map((d) => DAY_SCHEMA_NAMES[d]),\n opens: fmt(range.open),\n closes: fmt(range.close % (24 * 60)),\n });\n }\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  import type { Page } from "../schema/pages";
2
- import type { NewsArticle } from "../content/types";
2
+ import type { MediaItem, NewsArticle } from "../content/types";
3
3
  import type { FormDefinition } from "../schema/forms";
4
4
  export declare const DEMO_EVENT = "xicms:demo";
5
5
  /** Zet de demo-bewerkmodus aan/uit (door de provider, op basis van config.demoEditing). */
@@ -15,6 +15,16 @@ export declare function registerDemoNews(siteId: string, articles: NewsArticle[]
15
15
  export declare function getDemoNews(siteId: string): NewsArticle[];
16
16
  export declare function registerDemoForm(siteId: string, formId: string, def: FormDefinition): void;
17
17
  export declare function getDemoForm(siteId: string, formId: string): FormDefinition | null;
18
+ /** Seed de mediabibliotheek van de demo (curated set; alt = zoektekst in de kiezer). */
19
+ export declare function registerDemoMedia(siteId: string, items: Array<Pick<MediaItem, "url" | "name" | "alt">>): void;
20
+ /** Bibliotheek in demo-modus: lokale uploads (nieuwste eerst) + de geseede set. */
21
+ export declare function listDemoMedia(siteId: string): MediaItem[];
22
+ export declare function addDemoMedia(siteId: string, item: {
23
+ url: string;
24
+ name: string;
25
+ }): MediaItem;
26
+ /** Inclusief geheugen-only items (opslag-vol-vangnet) — gebruikt door de kiezer. */
27
+ export declare function listDemoMediaAll(siteId: string): MediaItem[];
18
28
  export declare function loadDemoDraft(siteId: string, id: string): Partial<Page> | null;
19
29
  export declare function saveDemoDraft(siteId: string, id: string, patch: Partial<Page>): void;
20
30
  /** Verwijdert alle lokale demo-bewerkingen + login voor deze site (de "Reset"-knop). */
@@ -1,11 +1,13 @@
1
1
  "use client";
2
2
  const DRAFT_PREFIX = "xicms-demo-draft";
3
+ const MEDIA_PREFIX = "xicms-demo-media";
3
4
  const SESSION_KEY = "xicms-demo-session";
4
5
  const DEMO_EVENT = "xicms:demo";
5
6
  let active = false;
6
7
  const seeds = /* @__PURE__ */ new Map();
7
8
  const newsSeeds = /* @__PURE__ */ new Map();
8
9
  const formSeeds = /* @__PURE__ */ new Map();
10
+ const mediaSeeds = /* @__PURE__ */ new Map();
9
11
  function setDemoActive(on) {
10
12
  active = on;
11
13
  }
@@ -53,6 +55,48 @@ function registerDemoForm(siteId, formId, def) {
53
55
  function getDemoForm(siteId, formId) {
54
56
  return formSeeds.get(`${siteId}::${formId}`) ?? null;
55
57
  }
58
+ function registerDemoMedia(siteId, items) {
59
+ mediaSeeds.set(
60
+ siteId,
61
+ items.map((m, i) => ({ id: `demo-seed-${i}`, url: m.url, path: "demo", name: m.name, alt: m.alt ?? "" }))
62
+ );
63
+ }
64
+ function mediaKey(siteId) {
65
+ return `${MEDIA_PREFIX}:${siteId}`;
66
+ }
67
+ function loadLocalDemoMedia(siteId) {
68
+ if (typeof window === "undefined") return [];
69
+ try {
70
+ const raw = window.localStorage.getItem(mediaKey(siteId));
71
+ return raw ? JSON.parse(raw) : [];
72
+ } catch {
73
+ return [];
74
+ }
75
+ }
76
+ function listDemoMedia(siteId) {
77
+ return [...loadLocalDemoMedia(siteId), ...mediaSeeds.get(siteId) ?? []];
78
+ }
79
+ const memoryOnlyMedia = /* @__PURE__ */ new Map();
80
+ function addDemoMedia(siteId, item) {
81
+ const media = {
82
+ id: `demo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
83
+ url: item.url,
84
+ path: "demo",
85
+ name: item.name,
86
+ alt: ""
87
+ };
88
+ const next = [media, ...loadLocalDemoMedia(siteId)].slice(0, 10);
89
+ try {
90
+ window.localStorage.setItem(mediaKey(siteId), JSON.stringify(next));
91
+ } catch {
92
+ memoryOnlyMedia.set(siteId, [media, ...memoryOnlyMedia.get(siteId) ?? []]);
93
+ }
94
+ emit();
95
+ return media;
96
+ }
97
+ function listDemoMediaAll(siteId) {
98
+ return [...memoryOnlyMedia.get(siteId) ?? [], ...listDemoMedia(siteId)];
99
+ }
56
100
  function draftKey(siteId, id) {
57
101
  return `${DRAFT_PREFIX}:${siteId}:${id}`;
58
102
  }
@@ -83,6 +127,8 @@ function resetDemo(siteId) {
83
127
  if (k && k.startsWith(`${DRAFT_PREFIX}:${siteId}:`)) toRemove.push(k);
84
128
  }
85
129
  toRemove.forEach((k) => window.localStorage.removeItem(k));
130
+ window.localStorage.removeItem(mediaKey(siteId));
131
+ memoryOnlyMedia.delete(siteId);
86
132
  window.sessionStorage.removeItem(SESSION_KEY);
87
133
  } catch {
88
134
  }
@@ -96,14 +142,18 @@ function resolveDemoPage(siteId, path) {
96
142
  }
97
143
  export {
98
144
  DEMO_EVENT,
145
+ addDemoMedia,
99
146
  demoIdForPath,
100
147
  getDemoForm,
101
148
  getDemoNews,
102
149
  getSeed,
103
150
  isDemoActive,
104
151
  isDemoLoggedIn,
152
+ listDemoMedia,
153
+ listDemoMediaAll,
105
154
  loadDemoDraft,
106
155
  registerDemoForm,
156
+ registerDemoMedia,
107
157
  registerDemoNews,
108
158
  registerSeed,
109
159
  resetDemo,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/demo/demoStore.ts"],"sourcesContent":["\"use client\";\n\nimport type { Page } from \"../schema/pages\";\nimport type { NewsArticle } from \"../content/types\";\nimport type { FormDefinition } from \"../schema/forms\";\n\n/**\n * Lokale opslag voor de DEMO-bewerkmodus (`config.demoEditing`). Alle \"bewerkingen\" van een\n * bezoeker (teksten, blokken, publiceren) blijven UITSLUITEND in de browser van die bezoeker\n * (localStorage) — er gaat niets naar Firebase. Zo kunnen eindgebruikers het beheer veilig\n * uitproberen zonder dat er echt iets verandert. Een \"Reset\" wist alles weer.\n *\n * Bewust GEEN onderdeel van de echte (Firebase-)opslag: pagesClient en AuthProvider takken hier\n * alleen naartoe als de demo-modus actief is, en die staat alleen aan op een demo-site.\n */\n\nconst DRAFT_PREFIX = \"xicms-demo-draft\";\nconst SESSION_KEY = \"xicms-demo-session\";\nexport const DEMO_EVENT = \"xicms:demo\";\n\nlet active = false;\nconst seeds = new Map<string, Page>();\nconst newsSeeds = new Map<string, NewsArticle[]>();\nconst formSeeds = new Map<string, FormDefinition>();\n\n/** Zet de demo-bewerkmodus aan/uit (door de provider, op basis van config.demoEditing). */\nexport function setDemoActive(on: boolean) {\n active = on;\n}\nexport function isDemoActive(): boolean {\n return active;\n}\n\n/** Stabiel, deterministisch draft-id per pad, zodat pagina + editor naar dezelfde opslag wijzen. */\nexport function demoIdForPath(path: string): string {\n return `demo:${path || \"/\"}`;\n}\n\nfunction emit() {\n if (typeof window !== \"undefined\") window.dispatchEvent(new Event(DEMO_EVENT));\n}\n\n// ── Pseudo-login (sessie) ──────────────────────────────────────────────────\n// In sessionStorage: verdwijnt als het tabblad sluit — benadrukt het tijdelijke karakter.\nexport function isDemoLoggedIn(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.sessionStorage.getItem(SESSION_KEY) === \"1\";\n } catch {\n return false;\n }\n}\nexport function setDemoLoggedIn(on: boolean) {\n if (typeof window === \"undefined\") return;\n try {\n if (on) window.sessionStorage.setItem(SESSION_KEY, \"1\");\n else window.sessionStorage.removeItem(SESSION_KEY);\n } catch {\n /* opslag geweigerd — login geldt dan alleen voor deze render */\n }\n emit();\n}\n\n// ── Seed-pagina's (de oorspronkelijke inhoud waar een bezoeker op verder bewerkt) ──\nexport function registerSeed(siteId: string, path: string, page: Page) {\n seeds.set(`${siteId}::${path}`, page);\n}\nexport function getSeed(siteId: string, path: string): Page | null {\n return seeds.get(`${siteId}::${path}`) ?? null;\n}\n\n// ── Demo-nieuws (zodat nieuws-blokken/widgets ook in de demo gevuld zijn, zonder Firebase) ──\nexport function registerDemoNews(siteId: string, articles: NewsArticle[]) {\n newsSeeds.set(siteId, articles);\n}\nexport function getDemoNews(siteId: string): NewsArticle[] {\n return newsSeeds.get(siteId) ?? [];\n}\n\n// ── Demo-formulieren (zodat het form-blok werkt zonder Firebase; inzenden doet lokaal niets) ──\nexport function registerDemoForm(siteId: string, formId: string, def: FormDefinition) {\n formSeeds.set(`${siteId}::${formId}`, def);\n}\nexport function getDemoForm(siteId: string, formId: string): FormDefinition | null {\n return formSeeds.get(`${siteId}::${formId}`) ?? null;\n}\n\n// ── Lokale drafts (de bewerkingen) ─────────────────────────────────────────\nfunction draftKey(siteId: string, id: string) {\n return `${DRAFT_PREFIX}:${siteId}:${id}`;\n}\n\nexport function loadDemoDraft(siteId: string, id: string): Partial<Page> | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(draftKey(siteId, id));\n return raw ? (JSON.parse(raw) as Partial<Page>) : null;\n } catch {\n return null;\n }\n}\n\nexport function saveDemoDraft(siteId: string, id: string, patch: Partial<Page>) {\n if (typeof window === \"undefined\") return;\n try {\n const current = loadDemoDraft(siteId, id) ?? {};\n window.localStorage.setItem(draftKey(siteId, id), JSON.stringify({ ...current, ...patch }));\n } catch {\n /* opslag vol/geweigerd — bewerking geldt dan alleen voor deze sessie in het geheugen */\n }\n emit();\n}\n\n/** Verwijdert alle lokale demo-bewerkingen + login voor deze site (de \"Reset\"-knop). */\nexport function resetDemo(siteId: string) {\n if (typeof window === \"undefined\") return;\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const k = window.localStorage.key(i);\n if (k && k.startsWith(`${DRAFT_PREFIX}:${siteId}:`)) toRemove.push(k);\n }\n toRemove.forEach((k) => window.localStorage.removeItem(k));\n window.sessionStorage.removeItem(SESSION_KEY);\n } catch {\n /* negeren */\n }\n emit();\n}\n\n/**\n * De effectieve demo-pagina = seed met lokale bewerkingen eroverheen. Gebruikt door de demo-pagina\n * om de wijzigingen van de bezoeker direct te tonen (ook na sluiten van de editor).\n */\nexport function resolveDemoPage(siteId: string, path: string): Page | null {\n const seed = getSeed(siteId, path);\n if (!seed) return null;\n const draft = loadDemoDraft(siteId, demoIdForPath(path));\n return draft ? ({ ...seed, ...draft } as Page) : seed;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/demo/demoStore.ts"],"sourcesContent":["\"use client\";\n\nimport type { Page } from \"../schema/pages\";\nimport type { MediaItem, NewsArticle } from \"../content/types\";\nimport type { FormDefinition } from \"../schema/forms\";\n\n/**\n * Lokale opslag voor de DEMO-bewerkmodus (`config.demoEditing`). Alle \"bewerkingen\" van een\n * bezoeker (teksten, blokken, publiceren) blijven UITSLUITEND in de browser van die bezoeker\n * (localStorage) — er gaat niets naar Firebase. Zo kunnen eindgebruikers het beheer veilig\n * uitproberen zonder dat er echt iets verandert. Een \"Reset\" wist alles weer.\n *\n * Bewust GEEN onderdeel van de echte (Firebase-)opslag: pagesClient en AuthProvider takken hier\n * alleen naartoe als de demo-modus actief is, en die staat alleen aan op een demo-site.\n */\n\nconst DRAFT_PREFIX = \"xicms-demo-draft\";\nconst MEDIA_PREFIX = \"xicms-demo-media\";\nconst SESSION_KEY = \"xicms-demo-session\";\nexport const DEMO_EVENT = \"xicms:demo\";\n\nlet active = false;\nconst seeds = new Map<string, Page>();\nconst newsSeeds = new Map<string, NewsArticle[]>();\nconst formSeeds = new Map<string, FormDefinition>();\nconst mediaSeeds = new Map<string, MediaItem[]>();\n\n/** Zet de demo-bewerkmodus aan/uit (door de provider, op basis van config.demoEditing). */\nexport function setDemoActive(on: boolean) {\n active = on;\n}\nexport function isDemoActive(): boolean {\n return active;\n}\n\n/** Stabiel, deterministisch draft-id per pad, zodat pagina + editor naar dezelfde opslag wijzen. */\nexport function demoIdForPath(path: string): string {\n return `demo:${path || \"/\"}`;\n}\n\nfunction emit() {\n if (typeof window !== \"undefined\") window.dispatchEvent(new Event(DEMO_EVENT));\n}\n\n// ── Pseudo-login (sessie) ──────────────────────────────────────────────────\n// In sessionStorage: verdwijnt als het tabblad sluit — benadrukt het tijdelijke karakter.\nexport function isDemoLoggedIn(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.sessionStorage.getItem(SESSION_KEY) === \"1\";\n } catch {\n return false;\n }\n}\nexport function setDemoLoggedIn(on: boolean) {\n if (typeof window === \"undefined\") return;\n try {\n if (on) window.sessionStorage.setItem(SESSION_KEY, \"1\");\n else window.sessionStorage.removeItem(SESSION_KEY);\n } catch {\n /* opslag geweigerd — login geldt dan alleen voor deze render */\n }\n emit();\n}\n\n// ── Seed-pagina's (de oorspronkelijke inhoud waar een bezoeker op verder bewerkt) ──\nexport function registerSeed(siteId: string, path: string, page: Page) {\n seeds.set(`${siteId}::${path}`, page);\n}\nexport function getSeed(siteId: string, path: string): Page | null {\n return seeds.get(`${siteId}::${path}`) ?? null;\n}\n\n// ── Demo-nieuws (zodat nieuws-blokken/widgets ook in de demo gevuld zijn, zonder Firebase) ──\nexport function registerDemoNews(siteId: string, articles: NewsArticle[]) {\n newsSeeds.set(siteId, articles);\n}\nexport function getDemoNews(siteId: string): NewsArticle[] {\n return newsSeeds.get(siteId) ?? [];\n}\n\n// ── Demo-formulieren (zodat het form-blok werkt zonder Firebase; inzenden doet lokaal niets) ──\nexport function registerDemoForm(siteId: string, formId: string, def: FormDefinition) {\n formSeeds.set(`${siteId}::${formId}`, def);\n}\nexport function getDemoForm(siteId: string, formId: string): FormDefinition | null {\n return formSeeds.get(`${siteId}::${formId}`) ?? null;\n}\n\n// ── Demo-media (bibliotheek + upload zonder Firebase) ─────────────────────\n// Geseede items (bv. de foto's van de demo-site zelf) + lokale \"uploads\" als data-URL in\n// localStorage. Zo werken kiezen, uploaden en vervangen in de demo écht — alles blijft lokaal.\n\n/** Seed de mediabibliotheek van de demo (curated set; alt = zoektekst in de kiezer). */\nexport function registerDemoMedia(siteId: string, items: Array<Pick<MediaItem, \"url\" | \"name\" | \"alt\">>) {\n mediaSeeds.set(\n siteId,\n items.map((m, i) => ({ id: `demo-seed-${i}`, url: m.url, path: \"demo\", name: m.name, alt: m.alt ?? \"\" })),\n );\n}\n\nfunction mediaKey(siteId: string) {\n return `${MEDIA_PREFIX}:${siteId}`;\n}\n\nfunction loadLocalDemoMedia(siteId: string): MediaItem[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(mediaKey(siteId));\n return raw ? (JSON.parse(raw) as MediaItem[]) : [];\n } catch {\n return [];\n }\n}\n\n/** Bibliotheek in demo-modus: lokale uploads (nieuwste eerst) + de geseede set. */\nexport function listDemoMedia(siteId: string): MediaItem[] {\n return [...loadLocalDemoMedia(siteId), ...(mediaSeeds.get(siteId) ?? [])];\n}\n\n/**\n * \"Upload\" in demo-modus: bewaar de (gecomprimeerde) afbeelding als data-URL in localStorage.\n * localStorage is beperkt (~5 MB) — we houden maximaal 10 lokale uploads aan en laten bij een\n * volle opslag het item alleen voor deze sessie in het geheugen bestaan (zelfde vangnet als drafts).\n */\nconst memoryOnlyMedia = new Map<string, MediaItem[]>();\nexport function addDemoMedia(siteId: string, item: { url: string; name: string }): MediaItem {\n const media: MediaItem = {\n id: `demo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n url: item.url,\n path: \"demo\",\n name: item.name,\n alt: \"\",\n };\n const next = [media, ...loadLocalDemoMedia(siteId)].slice(0, 10);\n try {\n window.localStorage.setItem(mediaKey(siteId), JSON.stringify(next));\n } catch {\n memoryOnlyMedia.set(siteId, [media, ...(memoryOnlyMedia.get(siteId) ?? [])]);\n }\n emit();\n return media;\n}\n\n/** Inclusief geheugen-only items (opslag-vol-vangnet) — gebruikt door de kiezer. */\nexport function listDemoMediaAll(siteId: string): MediaItem[] {\n return [...(memoryOnlyMedia.get(siteId) ?? []), ...listDemoMedia(siteId)];\n}\n\n// ── Lokale drafts (de bewerkingen) ─────────────────────────────────────────\nfunction draftKey(siteId: string, id: string) {\n return `${DRAFT_PREFIX}:${siteId}:${id}`;\n}\n\nexport function loadDemoDraft(siteId: string, id: string): Partial<Page> | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(draftKey(siteId, id));\n return raw ? (JSON.parse(raw) as Partial<Page>) : null;\n } catch {\n return null;\n }\n}\n\nexport function saveDemoDraft(siteId: string, id: string, patch: Partial<Page>) {\n if (typeof window === \"undefined\") return;\n try {\n const current = loadDemoDraft(siteId, id) ?? {};\n window.localStorage.setItem(draftKey(siteId, id), JSON.stringify({ ...current, ...patch }));\n } catch {\n /* opslag vol/geweigerd — bewerking geldt dan alleen voor deze sessie in het geheugen */\n }\n emit();\n}\n\n/** Verwijdert alle lokale demo-bewerkingen + login voor deze site (de \"Reset\"-knop). */\nexport function resetDemo(siteId: string) {\n if (typeof window === \"undefined\") return;\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const k = window.localStorage.key(i);\n if (k && k.startsWith(`${DRAFT_PREFIX}:${siteId}:`)) toRemove.push(k);\n }\n toRemove.forEach((k) => window.localStorage.removeItem(k));\n window.localStorage.removeItem(mediaKey(siteId));\n memoryOnlyMedia.delete(siteId);\n window.sessionStorage.removeItem(SESSION_KEY);\n } catch {\n /* negeren */\n }\n emit();\n}\n\n/**\n * De effectieve demo-pagina = seed met lokale bewerkingen eroverheen. Gebruikt door de demo-pagina\n * om de wijzigingen van de bezoeker direct te tonen (ook na sluiten van de editor).\n */\nexport function resolveDemoPage(siteId: string, path: string): Page | null {\n const seed = getSeed(siteId, path);\n if (!seed) return null;\n const draft = loadDemoDraft(siteId, demoIdForPath(path));\n return draft ? ({ ...seed, ...draft } as Page) : seed;\n}\n"],"mappings":"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","names":[]}
@@ -32,7 +32,8 @@ function getDb() {
32
32
  }
33
33
  function getBucket() {
34
34
  const app = getFirebaseApp();
35
- return app ? getStorage(app) : null;
35
+ if (!app || !readConfig().storageBucket) return null;
36
+ return getStorage(app);
36
37
  }
37
38
  export {
38
39
  getBucket,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/firebase/client.ts"],"sourcesContent":["\"use client\";\n\nimport { initializeApp, getApps, getApp, type FirebaseApp } from \"firebase/app\";\nimport { getAuth, type Auth } from \"firebase/auth\";\nimport { getFirestore, type Firestore } from \"firebase/firestore\";\nimport { getStorage, type FirebaseStorage } from \"firebase/storage\";\n\n/**\n * Client-side Firebase, volledig env-gedreven (NEXT_PUBLIC_*). Als de env niet is ingevuld\n * geven alle helpers `null` terug, zodat een site ook zónder Firebase blijft werken (dan\n * toont hij alleen de standaardteksten en is bewerken uitgeschakeld).\n */\nfunction readConfig() {\n return {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,\n };\n}\n\nexport function isFirebaseConfigured(): boolean {\n const cfg = readConfig();\n return Boolean(cfg.apiKey && cfg.projectId);\n}\n\nexport function getFirebaseApp(): FirebaseApp | null {\n if (!isFirebaseConfigured()) return null;\n if (getApps().length) return getApp();\n return initializeApp(readConfig() as Record<string, string>);\n}\n\nexport function getFirebaseAuth(): Auth | null {\n const app = getFirebaseApp();\n return app ? getAuth(app) : null;\n}\n\nexport function getDb(): Firestore | null {\n const app = getFirebaseApp();\n return app ? getFirestore(app) : null;\n}\n\nexport function getBucket(): FirebaseStorage | null {\n const app = getFirebaseApp();\n return app ? getStorage(app) : null;\n}\n"],"mappings":";AAEA,SAAS,eAAe,SAAS,cAAgC;AACjE,SAAS,eAA0B;AACnC,SAAS,oBAAoC;AAC7C,SAAS,kBAAwC;AAOjD,SAAS,aAAa;AACpB,SAAO;AAAA,IACL,QAAQ,QAAQ,IAAI;AAAA,IACpB,YAAY,QAAQ,IAAI;AAAA,IACxB,WAAW,QAAQ,IAAI;AAAA,IACvB,eAAe,QAAQ,IAAI;AAAA,IAC3B,mBAAmB,QAAQ,IAAI;AAAA,IAC/B,OAAO,QAAQ,IAAI;AAAA,EACrB;AACF;AAEO,SAAS,uBAAgC;AAC9C,QAAM,MAAM,WAAW;AACvB,SAAO,QAAQ,IAAI,UAAU,IAAI,SAAS;AAC5C;AAEO,SAAS,iBAAqC;AACnD,MAAI,CAAC,qBAAqB,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAE,OAAQ,QAAO,OAAO;AACpC,SAAO,cAAc,WAAW,CAA2B;AAC7D;AAEO,SAAS,kBAA+B;AAC7C,QAAM,MAAM,eAAe;AAC3B,SAAO,MAAM,QAAQ,GAAG,IAAI;AAC9B;AAEO,SAAS,QAA0B;AACxC,QAAM,MAAM,eAAe;AAC3B,SAAO,MAAM,aAAa,GAAG,IAAI;AACnC;AAEO,SAAS,YAAoC;AAClD,QAAM,MAAM,eAAe;AAC3B,SAAO,MAAM,WAAW,GAAG,IAAI;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/firebase/client.ts"],"sourcesContent":["\"use client\";\n\nimport { initializeApp, getApps, getApp, type FirebaseApp } from \"firebase/app\";\nimport { getAuth, type Auth } from \"firebase/auth\";\nimport { getFirestore, type Firestore } from \"firebase/firestore\";\nimport { getStorage, type FirebaseStorage } from \"firebase/storage\";\n\n/**\n * Client-side Firebase, volledig env-gedreven (NEXT_PUBLIC_*). Als de env niet is ingevuld\n * geven alle helpers `null` terug, zodat een site ook zónder Firebase blijft werken (dan\n * toont hij alleen de standaardteksten en is bewerken uitgeschakeld).\n */\nfunction readConfig() {\n return {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,\n };\n}\n\nexport function isFirebaseConfigured(): boolean {\n const cfg = readConfig();\n return Boolean(cfg.apiKey && cfg.projectId);\n}\n\nexport function getFirebaseApp(): FirebaseApp | null {\n if (!isFirebaseConfigured()) return null;\n if (getApps().length) return getApp();\n return initializeApp(readConfig() as Record<string, string>);\n}\n\nexport function getFirebaseAuth(): Auth | null {\n const app = getFirebaseApp();\n return app ? getAuth(app) : null;\n}\n\nexport function getDb(): Firestore | null {\n const app = getFirebaseApp();\n return app ? getFirestore(app) : null;\n}\n\nexport function getBucket(): FirebaseStorage | null {\n const app = getFirebaseApp();\n // Zonder storageBucket geeft getStorage() een client zónder default bucket terug, waarna\n // storageRef() een cryptisch `storage/no-default-bucket` gooit (of de upload stil hangt). Geef\n // hier `null` zodat de aanroeper de nette \"Storage niet geconfigureerd\"-melding kan tonen.\n if (!app || !readConfig().storageBucket) return null;\n return getStorage(app);\n}\n"],"mappings":";AAEA,SAAS,eAAe,SAAS,cAAgC;AACjE,SAAS,eAA0B;AACnC,SAAS,oBAAoC;AAC7C,SAAS,kBAAwC;AAOjD,SAAS,aAAa;AACpB,SAAO;AAAA,IACL,QAAQ,QAAQ,IAAI;AAAA,IACpB,YAAY,QAAQ,IAAI;AAAA,IACxB,WAAW,QAAQ,IAAI;AAAA,IACvB,eAAe,QAAQ,IAAI;AAAA,IAC3B,mBAAmB,QAAQ,IAAI;AAAA,IAC/B,OAAO,QAAQ,IAAI;AAAA,EACrB;AACF;AAEO,SAAS,uBAAgC;AAC9C,QAAM,MAAM,WAAW;AACvB,SAAO,QAAQ,IAAI,UAAU,IAAI,SAAS;AAC5C;AAEO,SAAS,iBAAqC;AACnD,MAAI,CAAC,qBAAqB,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAE,OAAQ,QAAO,OAAO;AACpC,SAAO,cAAc,WAAW,CAA2B;AAC7D;AAEO,SAAS,kBAA+B;AAC7C,QAAM,MAAM,eAAe;AAC3B,SAAO,MAAM,QAAQ,GAAG,IAAI;AAC9B;AAEO,SAAS,QAA0B;AACxC,QAAM,MAAM,eAAe;AAC3B,SAAO,MAAM,aAAa,GAAG,IAAI;AACnC;AAEO,SAAS,YAAoC;AAClD,QAAM,MAAM,eAAe;AAI3B,MAAI,CAAC,OAAO,CAAC,WAAW,EAAE,cAAe,QAAO;AAChD,SAAO,WAAW,GAAG;AACvB;","names":[]}
package/dist/index.d.ts CHANGED
@@ -48,7 +48,7 @@ export { PageRenderer } from "./render/PageRenderer";
48
48
  export { PublishedPage } from "./render/PublishedPage";
49
49
  export { defineModule, resolveModules, isModuleActive, ModulesProvider, useModules, useModuleActive, useModuleSettings, applyBlockOverrides, moduleIdForBlockType, builtinModules, validateModuleContract, assertModuleContract, useConsent, ConsentBanner, ConsentGate, ConsentReopenLink, type ConsentState, type ConsentSettings, type ConsentCategory, type ModuleDefinition, type ModuleSiteSetting, type ModulesSiteConfig, type Capability, type ResolvedModules, type BlockOverride, type BlockOverrides, } from "./modules";
50
50
  export { DemoLock } from "./lock/DemoLock";
51
- export { registerSeed, resolveDemoPage, loadDemoDraft, demoIdForPath, registerDemoNews, registerDemoForm, DEMO_EVENT, } from "./demo/demoStore";
51
+ export { registerSeed, resolveDemoPage, loadDemoDraft, demoIdForPath, registerDemoNews, registerDemoForm, registerDemoMedia, DEMO_EVENT, } from "./demo/demoStore";
52
52
  export { DemoStudio } from "./demo/DemoStudio";
53
53
  export { getFirebaseApp, getFirebaseAuth, getDb, getBucket, isFirebaseConfigured, } from "./firebase/client";
54
54
  export { initAppCheck } from "./firebase/appcheck";
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ import {
73
73
  demoIdForPath,
74
74
  registerDemoNews,
75
75
  registerDemoForm,
76
+ registerDemoMedia,
76
77
  DEMO_EVENT
77
78
  } from "./demo/demoStore";
78
79
  import { DemoStudio } from "./demo/DemoStudio";
@@ -146,6 +147,7 @@ export {
146
147
  mergeRegistry,
147
148
  moduleIdForBlockType,
148
149
  registerDemoForm,
150
+ registerDemoMedia,
149
151
  registerDemoNews,
150
152
  registerSeed,
151
153
  resolveDemoPage,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\nexport type { ThemeTokens, NavItem, SiteSettings, FormsSender } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// Content (LEGACY inline editing, v1 plat model — vervangen door het block-model + de visuele\n// editor). Blijft werken en geëxporteerd voor bestaande sites; verwijdering volgt op z'n vroegst\n// in de eerstvolgende major. Nieuw werk: gebruik blocks + data-xicms-edit voor inline bewerken.\n/** @deprecated Gebruik het block-model (pagina's/drafts) + de visuele editor i.p.v. `cms/content`. */\nexport { ContentProvider, useContent } from \"./content/ContentProvider\";\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\n// Bewerkbare componenten (legacy inline)\n/** @deprecated Gebruik een block met `data-xicms-edit` — inline bewerken loopt via de visuele editor. */\nexport { EditableText } from \"./components/EditableText\";\n/** @deprecated Gebruik een richText-block — inline bewerken loopt via de visuele editor. */\nexport { EditableRichText } from \"./components/EditableRichText\";\n/** @deprecated Gebruik een image-block (inline vervangen via de mediakiezer in de visuele editor). */\nexport { EditableImage } from \"./components/EditableImage\";\nexport { EditBar } from \"./components/EditBar\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only): seed registreren + de live (lokaal-bewerkte) pagina ophalen\nexport {\n registerSeed,\n resolveDemoPage,\n loadDemoDraft,\n demoIdForPath,\n registerDemoNews,\n registerDemoForm,\n DEMO_EVENT,\n} from \"./demo/demoStore\";\nexport { DemoStudio } from \"./demo/DemoStudio\";\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAA6C;AACtD,SAAS,wBAAwB;AAQjC,SAAS,cAAc,eAAe;AACtC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAMP,SAAS,iBAAiB,kBAAkB;AAK5C,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;AAEjC,SAAS,qBAAqB;AAC9B,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,SAAS,UAAU,iBAAiB,eAAe;AAGnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,mBAAmB;AAC9C,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,gBAAgB;AAGzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAG3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\nexport type { ThemeTokens, NavItem, SiteSettings, FormsSender } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// Content (LEGACY inline editing, v1 plat model — vervangen door het block-model + de visuele\n// editor). Blijft werken en geëxporteerd voor bestaande sites; verwijdering volgt op z'n vroegst\n// in de eerstvolgende major. Nieuw werk: gebruik blocks + data-xicms-edit voor inline bewerken.\n/** @deprecated Gebruik het block-model (pagina's/drafts) + de visuele editor i.p.v. `cms/content`. */\nexport { ContentProvider, useContent } from \"./content/ContentProvider\";\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\n// Bewerkbare componenten (legacy inline)\n/** @deprecated Gebruik een block met `data-xicms-edit` — inline bewerken loopt via de visuele editor. */\nexport { EditableText } from \"./components/EditableText\";\n/** @deprecated Gebruik een richText-block — inline bewerken loopt via de visuele editor. */\nexport { EditableRichText } from \"./components/EditableRichText\";\n/** @deprecated Gebruik een image-block (inline vervangen via de mediakiezer in de visuele editor). */\nexport { EditableImage } from \"./components/EditableImage\";\nexport { EditBar } from \"./components/EditBar\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only): seed registreren + de live (lokaal-bewerkte) pagina ophalen\nexport {\n registerSeed,\n resolveDemoPage,\n loadDemoDraft,\n demoIdForPath,\n registerDemoNews,\n registerDemoForm,\n registerDemoMedia,\n DEMO_EVENT,\n} from \"./demo/demoStore\";\nexport { DemoStudio } from \"./demo/DemoStudio\";\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAA6C;AACtD,SAAS,wBAAwB;AAQjC,SAAS,cAAc,eAAe;AACtC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAMP,SAAS,iBAAiB,kBAAkB;AAK5C,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;AAEjC,SAAS,qBAAqB;AAC9B,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,SAAS,UAAU,iBAAiB,eAAe;AAGnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,mBAAmB;AAC9C,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,gBAAgB;AAGzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAG3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;","names":[]}
@@ -51,7 +51,7 @@ import {
51
51
  downloadsWidget,
52
52
  countdownWidget
53
53
  } from "../blocks/builtins/Widgets";
54
- const CORE_VERSION = "4.17.1";
54
+ const CORE_VERSION = "4.18.0";
55
55
  const contentModule = defineModule({
56
56
  id: "content",
57
57
  version: CORE_VERSION,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/modules/builtins.ts"],"sourcesContent":["/**\n * Ingebouwde modules (XiCMS 3.0). Elke \"feature\" van 2.x is hier een first-class module geworden,\n * zodat hij per site aan/uit kan en los te verbeteren is. De bijbehorende admin-panelen staan in\n * `@xiwebdesign/xicms-core/admin` (paneel-registry), gekoppeld via dezelfde `id`/`featureKey`.\n *\n * Backward compatible: zonder `SiteConfig.modules`/`features` zijn alle modules actief — identiek\n * aan 2.x. De `content`-module is `core: true` en kan niet uitgezet worden (de basis-bouwstenen).\n */\nimport { z } from \"zod\";\nimport { defineModule } from \"./defineModule\";\nimport type { ModuleDefinition } from \"./types\";\n\nimport { heroBlock } from \"../blocks/builtins/Hero\";\nimport { richTextBlock } from \"../blocks/builtins/RichTextBlock\";\nimport { imageBlock } from \"../blocks/builtins/ImageBlock\";\nimport { ctaBlock } from \"../blocks/builtins/Cta\";\nimport { columnsBlock } from \"../blocks/builtins/Columns\";\nimport { spacerBlock } from \"../blocks/builtins/Spacer\";\nimport { galleryBlock } from \"../blocks/builtins/Gallery\";\nimport { faqBlock } from \"../blocks/builtins/Faq\";\nimport { openingHoursBlock } from \"../blocks/builtins/OpeningHours\";\nimport { pricingBlock } from \"../blocks/builtins/Pricing\";\nimport { newsListBlock } from \"../blocks/builtins/NewsListBlock\";\nimport { formBlock } from \"../blocks/builtins/FormBlock\";\nimport { testimonialsBlock } from \"../blocks/builtins/Testimonials\";\nimport { teamBlock } from \"../blocks/builtins/Team\";\nimport { agendaBlock } from \"../blocks/builtins/Agenda\";\nimport { searchBlock } from \"../blocks/builtins/Search\";\nimport { featureBlock, countersBlock, logosBlock, timelineBlock } from \"../blocks/builtins/Sections\";\nimport { videoBlock } from \"../blocks/builtins/Video\";\nimport { bannerBlock } from \"../blocks/builtins/Banner\";\nimport { contactBlock, socialBlock } from \"../blocks/builtins/Contact\";\nimport { mapBlock } from \"../blocks/builtins/MapBlock\";\nimport { newsletterBlock } from \"../blocks/builtins/Newsletter\";\nimport { bookingBlock } from \"../blocks/builtins/Booking\";\nimport { buttonsBlock, quoteBlock, dividerBlock } from \"../blocks/builtins/Elements\";\nimport { accordionBlock } from \"../blocks/builtins/Accordion\";\nimport { embedBlock } from \"../blocks/builtins/Embed\";\nimport { withSidebarBlock, tabsBlock } from \"../blocks/builtins/Layout\";\nimport { cardsBlock } from \"../blocks/builtins/Cards\";\nimport { downloadsBlock } from \"../blocks/builtins/Downloads\";\nimport { pdfBlock } from \"../blocks/builtins/PdfBlock\";\nimport { countdownBlock } from \"../blocks/builtins/Countdown\";\nimport { sliderBlock } from \"../blocks/builtins/Slider\";\nimport { whatsappChatBlock } from \"../blocks/builtins/WhatsappChat\";\nimport { beforeAfterBlock } from \"../blocks/builtins/BeforeAfter\";\nimport { portfolioBlock } from \"../blocks/builtins/Portfolio\";\nimport { jobsBlock } from \"../blocks/builtins/Jobs\";\nimport { breadcrumbsBlock } from \"../blocks/builtins/Breadcrumbs\";\nimport {\n newsWidget,\n agendaWidget,\n openingHoursWidget,\n contactWidget,\n socialWidget as socialWidgetBlock,\n newsletterWidget,\n ctaWidget,\n galleryWidget,\n testimonialsWidget,\n cardsWidget,\n downloadsWidget,\n countdownWidget,\n} from \"../blocks/builtins/Widgets\";\n\nconst CORE_VERSION = \"4.17.1\";\n\n/**\n * Basis-bouwstenen — altijd aanwezig (niet uit te zetten). Bevat ook de indelingsblokken\n * (kolommen, \"Met zijbalk\") waarmee je widgets naast je hoofdinhoud plaatst, plus de\n * compacte CTA-widget.\n */\nexport const contentModule = defineModule({\n id: \"content\",\n version: CORE_VERSION,\n label: \"Basisinhoud\",\n description: \"Kern-bouwstenen: hero, tekst, afbeelding, CTA, kolommen, zijbalk, witruimte.\",\n core: true,\n blocks: [heroBlock, richTextBlock, imageBlock, ctaBlock, columnsBlock, withSidebarBlock, spacerBlock, buttonsBlock, quoteBlock, dividerBlock, ctaWidget],\n});\n\nexport const galleryModule = defineModule({\n id: \"gallery\",\n version: CORE_VERSION,\n label: \"Fotogalerij\",\n description: \"Galerij-block met grid en (optioneel) lightbox + compacte fotostrip-widget.\",\n blocks: [galleryBlock, galleryWidget],\n // Per-site instelbaar door de agent/klant (modules.gallery.config) zonder core te forken.\n // `.describe()` = het label dat de klant in /beheer ziet.\n settingsSchema: z.object({\n columns: z.number().int().min(1).max(6).default(3).describe(\"Kolommen\"),\n lightbox: z.boolean().default(true).describe(\"Lightbox bij klikken\"),\n }),\n defaultSettings: { columns: 3, lightbox: true },\n});\n\nexport const faqModule = defineModule({\n id: \"faq\",\n version: CORE_VERSION,\n label: \"FAQ\",\n description: \"Veelgestelde vragen (accordeon) met optionele zoek + FAQ-JSON-LD.\",\n blocks: [faqBlock],\n});\n\nexport const openingHoursModule = defineModule({\n id: \"openingHours\",\n version: CORE_VERSION,\n label: \"Openingstijden\",\n description: \"Openingstijden-tabel + compacte widget met 'vandaag' gemarkeerd.\",\n blocks: [openingHoursBlock, openingHoursWidget],\n});\n\nexport const pricingModule = defineModule({\n id: \"pricing\",\n version: CORE_VERSION,\n label: \"Prijzen\",\n blocks: [pricingBlock],\n});\n\nexport const pagesModule = defineModule({\n id: \"pages\",\n version: CORE_VERSION,\n label: \"Pagina's\",\n featureKey: \"pages\",\n description: \"Paginabouwer (draft → publish + versiegeschiedenis).\",\n});\n\nexport const newsModule = defineModule({\n id: \"news\",\n version: CORE_VERSION,\n label: \"Nieuws\",\n featureKey: \"news\",\n description: \"Nieuwsartikelen + nieuwslijst-block + 'laatste nieuws'-widget voor de zijbalk.\",\n blocks: [newsListBlock, newsWidget],\n});\n\nexport const mediaModule = defineModule({\n id: \"media\",\n version: CORE_VERSION,\n label: \"Media\",\n featureKey: \"media\",\n description: \"Mediabeheer met compressie + webp/srcset-varianten.\",\n requires: [{ kind: \"function\", name: \"onMediaUpload\" }, { kind: \"storage\", prefix: \"sites/\" }],\n});\n\nexport const formsModule = defineModule({\n id: \"forms\",\n version: CORE_VERSION,\n label: \"Formulieren\",\n featureKey: \"forms\",\n description: \"Formulierbouwer + inzendingen + e-mailnotificatie.\",\n blocks: [formBlock],\n requires: [\n { kind: \"function\", name: \"submitForm\" },\n { kind: \"firestore\", collection: \"forms\" },\n ],\n});\n\nexport const statsModule = defineModule({\n id: \"stats\",\n version: CORE_VERSION,\n label: \"Statistieken\",\n featureKey: \"stats\",\n description: \"Privacy-vriendelijke bezoekersstatistieken (geen cookies).\",\n requires: [{ kind: \"function\", name: \"trackView\" }],\n});\n\nexport const settingsModule = defineModule({\n id: \"settings\",\n version: CORE_VERSION,\n label: \"Instellingen\",\n core: true,\n description: \"Site-instellingen (nav, thema, SEO, formulier-afzender) — alleen siteadmin.\",\n});\n\n// Opt-in modules (defaultEnabled: false): worden bij installatie of later aangezet via\n// `modules: { <id>: true }`. Geen extra backend nodig — data leeft in de page-blocks.\nexport const testimonialsModule = defineModule({\n id: \"testimonials\",\n version: CORE_VERSION,\n label: \"Reviews\",\n description: \"Klantquotes/reviews met sterren, optionele Review-JSON-LD + compacte review-widget.\",\n defaultEnabled: false,\n blocks: [testimonialsBlock, testimonialsWidget],\n});\n\nexport const teamModule = defineModule({\n id: \"team\",\n version: CORE_VERSION,\n label: \"Team\",\n description: \"Team-/medewerkersoverzicht met foto, functie en bio.\",\n defaultEnabled: false,\n blocks: [teamBlock],\n});\n\nexport const agendaModule = defineModule({\n id: \"agenda\",\n version: CORE_VERSION,\n label: \"Agenda\",\n description: \"Evenementen/agenda (chronologisch) met Event-JSON-LD + 'komende agenda'-widget.\",\n defaultEnabled: false,\n blocks: [agendaBlock, agendaWidget],\n});\n\nexport const searchModule = defineModule({\n id: \"search\",\n version: CORE_VERSION,\n label: \"Zoeken\",\n description: \"Site-zoekblok dat gepubliceerde pagina's (en nieuws) client-side doorzoekt.\",\n defaultEnabled: false,\n blocks: [searchBlock],\n});\n\nexport const featureModule = defineModule({\n id: \"feature\",\n version: CORE_VERSION,\n label: \"Beeld + tekst\",\n description: \"Feature-sectie met afbeelding links/rechts en tekst.\",\n defaultEnabled: false,\n blocks: [featureBlock],\n});\n\nexport const countersModule = defineModule({\n id: \"counters\",\n version: CORE_VERSION,\n label: \"Tellers\",\n description: \"Statistiek-tellers (bv. “100+ klanten”).\",\n defaultEnabled: false,\n blocks: [countersBlock],\n});\n\nexport const logosModule = defineModule({\n id: \"logos\",\n version: CORE_VERSION,\n label: \"Logo's\",\n description: \"Partner-/klantlogo-strip.\",\n defaultEnabled: false,\n blocks: [logosBlock],\n});\n\nexport const timelineModule = defineModule({\n id: \"timeline\",\n version: CORE_VERSION,\n label: \"Tijdlijn\",\n description: \"Stappen/proces als tijdlijn.\",\n defaultEnabled: false,\n blocks: [timelineBlock],\n});\n\nexport const videoModule = defineModule({\n id: \"video\",\n version: CORE_VERSION,\n label: \"Video\",\n description: \"YouTube/Vimeo/MP4-embed (consent-gated).\",\n defaultEnabled: false,\n blocks: [videoBlock],\n});\n\nexport const bannerModule = defineModule({\n id: \"banner\",\n version: CORE_VERSION,\n label: \"Aankondiging\",\n description: \"Aankondigingsbalk, optioneel sluitbaar.\",\n defaultEnabled: false,\n blocks: [bannerBlock],\n});\n\nexport const contactModule = defineModule({\n id: \"contact\",\n version: CORE_VERSION,\n label: \"Contact + kaart + social\",\n description: \"Contactgegevens (JSON-LD), kaart (consent-gated), social-links + compacte widgets.\",\n defaultEnabled: false,\n blocks: [contactBlock, mapBlock, socialBlock, contactWidget, socialWidgetBlock],\n});\n\nexport const newsletterModule = defineModule({\n id: \"newsletter\",\n version: CORE_VERSION,\n label: \"Nieuwsbrief\",\n description: \"Aanmeldformulier via een externe provider-URL + compacte nieuwsbrief-widget.\",\n defaultEnabled: false,\n blocks: [newsletterBlock, newsletterWidget],\n});\n\nexport const bookingModule = defineModule({\n id: \"booking\",\n version: CORE_VERSION,\n label: \"Afspraak/booking\",\n description: \"Boekings-embed (bv. Calendly), consent-gated.\",\n defaultEnabled: false,\n blocks: [bookingBlock],\n});\n\nexport const accordionModule = defineModule({\n id: \"accordion\",\n version: CORE_VERSION,\n label: \"Accordeon\",\n description: \"Generieke in-/uitklapbare panelen.\",\n defaultEnabled: false,\n blocks: [accordionBlock],\n});\n\nexport const embedModule = defineModule({\n id: \"embed\",\n version: CORE_VERSION,\n label: \"Embed\",\n description: \"Generieke responsive iframe-embed (consent-gated).\",\n defaultEnabled: false,\n blocks: [embedBlock],\n});\n\n/* ── Nieuwe modules (3.1) — veelgebruikt op websites, volwaardig + widget waar zinvol ── */\n\nexport const cardsModule = defineModule({\n id: \"cards\",\n version: CORE_VERSION,\n label: \"Diensten / kaarten\",\n description: \"Diensten-/featurekaarten in een grid (icoon of beeld) + compacte 'uitgelicht'-widget.\",\n defaultEnabled: false,\n blocks: [cardsBlock, cardsWidget],\n});\n\nexport const tabsModule = defineModule({\n id: \"tabs\",\n version: CORE_VERSION,\n label: \"Tabbladen\",\n description: \"Tabblad-indeling: per tab een eigen set blokken (bv. beschrijving / specificaties).\",\n defaultEnabled: false,\n blocks: [tabsBlock],\n});\n\nexport const downloadsModule = defineModule({\n id: \"downloads\",\n version: CORE_VERSION,\n label: \"Downloads\",\n description: \"Lijst met downloadbare documenten (brochures, prijslijsten) + compacte widget.\",\n defaultEnabled: false,\n blocks: [downloadsBlock, downloadsWidget],\n});\n\nexport const pdfModule = defineModule({\n id: \"pdf\",\n version: CORE_VERSION,\n label: \"PDF-lezer\",\n description: \"Inline PDF-lezer voor menukaarten/documenten; meerdere PDF's met tabs, zelf te vervangen.\",\n defaultEnabled: false,\n blocks: [pdfBlock],\n});\n\nexport const countdownModule = defineModule({\n id: \"countdown\",\n version: CORE_VERSION,\n label: \"Aftelteller\",\n description: \"Tikkende countdown naar een datum (lancering, evenement, aanbieding) + widget.\",\n defaultEnabled: false,\n blocks: [countdownBlock, countdownWidget],\n});\n\nexport const sliderModule = defineModule({\n id: \"slider\",\n version: CORE_VERSION,\n label: \"Carrousel / slider\",\n description: \"Beeld-/contentcarrousel met dots, pijlen en optioneel autoplay (reduced-motion-veilig).\",\n defaultEnabled: false,\n blocks: [sliderBlock],\n});\n\n/* ── 4.16: nieuwe modules (allemaal opt-in) ──────────────────────────────── */\n\nexport const whatsappModule = defineModule({\n id: \"whatsapp\",\n version: CORE_VERSION,\n label: \"WhatsApp-knop\",\n description: \"Zwevende WhatsApp-/chatknop — laagdrempelig contact (wa.me-link, geen consent nodig).\",\n defaultEnabled: false,\n blocks: [whatsappChatBlock],\n});\n\nexport const beforeAfterModule = defineModule({\n id: \"beforeAfter\",\n version: CORE_VERSION,\n label: \"Voor / na\",\n description: \"Voor/na-vergelijker met sleepbare scheiding — sterk voor hoveniers, schilders, salons.\",\n defaultEnabled: false,\n blocks: [beforeAfterBlock],\n});\n\nexport const portfolioModule = defineModule({\n id: \"portfolio\",\n version: CORE_VERSION,\n label: \"Portfolio / projecten\",\n description: \"Projecten/cases-grid met categorie-filter en lightbox.\",\n defaultEnabled: false,\n blocks: [portfolioBlock],\n});\n\nexport const jobsModule = defineModule({\n id: \"jobs\",\n version: CORE_VERSION,\n label: \"Vacatures\",\n description: \"Vacature-overzicht met JobPosting-JSON-LD (vindbaar via Google Jobs).\",\n defaultEnabled: false,\n blocks: [jobsBlock],\n});\n\nexport const breadcrumbsModule = defineModule({\n id: \"breadcrumbs\",\n version: CORE_VERSION,\n label: \"Kruimelpad\",\n description: \"Kruimelpad afgeleid van de URL, met BreadcrumbList-JSON-LD (SEO).\",\n defaultEnabled: false,\n blocks: [breadcrumbsBlock],\n});\n\nexport const consentModule = defineModule({\n id: \"consent\",\n version: CORE_VERSION,\n label: \"Cookie-consent (AVG)\",\n description: \"Toestemmingsbanner + consent-opslag; stelt niet-essentiële scripts uit.\",\n defaultEnabled: false,\n settingsSchema: z.object({\n message: z\n .string()\n .default(\"We gebruiken cookies om de site te verbeteren. Kies hieronder je voorkeuren.\")\n .describe(\"Bannertekst\"),\n privacyUrl: z.string().optional().describe(\"Link naar privacyverklaring\"),\n version: z.string().optional().describe(\"Beleidsversie (wijzig → banner opnieuw tonen)\"),\n showReopen: z.boolean().default(true).describe(\"Zwevend cookie-knopje om keuze te wijzigen\"),\n categories: z\n .array(\n z.object({\n id: z.string(),\n label: z.string(),\n description: z.string().optional(),\n required: z.boolean().default(false),\n }),\n )\n .default([\n { id: \"necessary\", label: \"Noodzakelijk\", description: \"Nodig om de site te laten werken.\", required: true },\n { id: \"analytics\", label: \"Statistieken\", description: \"Anonieme statistieken om de site te verbeteren.\", required: false },\n { id: \"marketing\", label: \"Marketing\", description: \"Externe embeds zoals video's en kaarten.\", required: false },\n ]),\n }),\n defaultSettings: {},\n});\n\n/** Alle ingebouwde modules (volgorde bepaalt block- en tab-volgorde). */\nexport const builtinModules: ModuleDefinition[] = [\n contentModule,\n pagesModule,\n newsModule,\n mediaModule,\n galleryModule,\n faqModule,\n openingHoursModule,\n pricingModule,\n formsModule,\n statsModule,\n settingsModule,\n testimonialsModule,\n teamModule,\n agendaModule,\n searchModule,\n featureModule,\n countersModule,\n logosModule,\n timelineModule,\n videoModule,\n bannerModule,\n contactModule,\n newsletterModule,\n bookingModule,\n accordionModule,\n embedModule,\n cardsModule,\n tabsModule,\n downloadsModule,\n pdfModule,\n countdownModule,\n sliderModule,\n whatsappModule,\n beforeAfterModule,\n portfolioModule,\n jobsModule,\n breadcrumbsModule,\n consentModule,\n];\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/modules/builtins.ts"],"sourcesContent":["/**\n * Ingebouwde modules (XiCMS 3.0). Elke \"feature\" van 2.x is hier een first-class module geworden,\n * zodat hij per site aan/uit kan en los te verbeteren is. De bijbehorende admin-panelen staan in\n * `@xiwebdesign/xicms-core/admin` (paneel-registry), gekoppeld via dezelfde `id`/`featureKey`.\n *\n * Backward compatible: zonder `SiteConfig.modules`/`features` zijn alle modules actief — identiek\n * aan 2.x. De `content`-module is `core: true` en kan niet uitgezet worden (de basis-bouwstenen).\n */\nimport { z } from \"zod\";\nimport { defineModule } from \"./defineModule\";\nimport type { ModuleDefinition } from \"./types\";\n\nimport { heroBlock } from \"../blocks/builtins/Hero\";\nimport { richTextBlock } from \"../blocks/builtins/RichTextBlock\";\nimport { imageBlock } from \"../blocks/builtins/ImageBlock\";\nimport { ctaBlock } from \"../blocks/builtins/Cta\";\nimport { columnsBlock } from \"../blocks/builtins/Columns\";\nimport { spacerBlock } from \"../blocks/builtins/Spacer\";\nimport { galleryBlock } from \"../blocks/builtins/Gallery\";\nimport { faqBlock } from \"../blocks/builtins/Faq\";\nimport { openingHoursBlock } from \"../blocks/builtins/OpeningHours\";\nimport { pricingBlock } from \"../blocks/builtins/Pricing\";\nimport { newsListBlock } from \"../blocks/builtins/NewsListBlock\";\nimport { formBlock } from \"../blocks/builtins/FormBlock\";\nimport { testimonialsBlock } from \"../blocks/builtins/Testimonials\";\nimport { teamBlock } from \"../blocks/builtins/Team\";\nimport { agendaBlock } from \"../blocks/builtins/Agenda\";\nimport { searchBlock } from \"../blocks/builtins/Search\";\nimport { featureBlock, countersBlock, logosBlock, timelineBlock } from \"../blocks/builtins/Sections\";\nimport { videoBlock } from \"../blocks/builtins/Video\";\nimport { bannerBlock } from \"../blocks/builtins/Banner\";\nimport { contactBlock, socialBlock } from \"../blocks/builtins/Contact\";\nimport { mapBlock } from \"../blocks/builtins/MapBlock\";\nimport { newsletterBlock } from \"../blocks/builtins/Newsletter\";\nimport { bookingBlock } from \"../blocks/builtins/Booking\";\nimport { buttonsBlock, quoteBlock, dividerBlock } from \"../blocks/builtins/Elements\";\nimport { accordionBlock } from \"../blocks/builtins/Accordion\";\nimport { embedBlock } from \"../blocks/builtins/Embed\";\nimport { withSidebarBlock, tabsBlock } from \"../blocks/builtins/Layout\";\nimport { cardsBlock } from \"../blocks/builtins/Cards\";\nimport { downloadsBlock } from \"../blocks/builtins/Downloads\";\nimport { pdfBlock } from \"../blocks/builtins/PdfBlock\";\nimport { countdownBlock } from \"../blocks/builtins/Countdown\";\nimport { sliderBlock } from \"../blocks/builtins/Slider\";\nimport { whatsappChatBlock } from \"../blocks/builtins/WhatsappChat\";\nimport { beforeAfterBlock } from \"../blocks/builtins/BeforeAfter\";\nimport { portfolioBlock } from \"../blocks/builtins/Portfolio\";\nimport { jobsBlock } from \"../blocks/builtins/Jobs\";\nimport { breadcrumbsBlock } from \"../blocks/builtins/Breadcrumbs\";\nimport {\n newsWidget,\n agendaWidget,\n openingHoursWidget,\n contactWidget,\n socialWidget as socialWidgetBlock,\n newsletterWidget,\n ctaWidget,\n galleryWidget,\n testimonialsWidget,\n cardsWidget,\n downloadsWidget,\n countdownWidget,\n} from \"../blocks/builtins/Widgets\";\n\nconst CORE_VERSION = \"4.18.0\";\n\n/**\n * Basis-bouwstenen — altijd aanwezig (niet uit te zetten). Bevat ook de indelingsblokken\n * (kolommen, \"Met zijbalk\") waarmee je widgets naast je hoofdinhoud plaatst, plus de\n * compacte CTA-widget.\n */\nexport const contentModule = defineModule({\n id: \"content\",\n version: CORE_VERSION,\n label: \"Basisinhoud\",\n description: \"Kern-bouwstenen: hero, tekst, afbeelding, CTA, kolommen, zijbalk, witruimte.\",\n core: true,\n blocks: [heroBlock, richTextBlock, imageBlock, ctaBlock, columnsBlock, withSidebarBlock, spacerBlock, buttonsBlock, quoteBlock, dividerBlock, ctaWidget],\n});\n\nexport const galleryModule = defineModule({\n id: \"gallery\",\n version: CORE_VERSION,\n label: \"Fotogalerij\",\n description: \"Galerij-block met grid en (optioneel) lightbox + compacte fotostrip-widget.\",\n blocks: [galleryBlock, galleryWidget],\n // Per-site instelbaar door de agent/klant (modules.gallery.config) zonder core te forken.\n // `.describe()` = het label dat de klant in /beheer ziet.\n settingsSchema: z.object({\n columns: z.number().int().min(1).max(6).default(3).describe(\"Kolommen\"),\n lightbox: z.boolean().default(true).describe(\"Lightbox bij klikken\"),\n }),\n defaultSettings: { columns: 3, lightbox: true },\n});\n\nexport const faqModule = defineModule({\n id: \"faq\",\n version: CORE_VERSION,\n label: \"FAQ\",\n description: \"Veelgestelde vragen (accordeon) met optionele zoek + FAQ-JSON-LD.\",\n blocks: [faqBlock],\n});\n\nexport const openingHoursModule = defineModule({\n id: \"openingHours\",\n version: CORE_VERSION,\n label: \"Openingstijden\",\n description: \"Openingstijden-tabel + compacte widget met 'vandaag' gemarkeerd.\",\n blocks: [openingHoursBlock, openingHoursWidget],\n});\n\nexport const pricingModule = defineModule({\n id: \"pricing\",\n version: CORE_VERSION,\n label: \"Prijzen\",\n blocks: [pricingBlock],\n});\n\nexport const pagesModule = defineModule({\n id: \"pages\",\n version: CORE_VERSION,\n label: \"Pagina's\",\n featureKey: \"pages\",\n description: \"Paginabouwer (draft → publish + versiegeschiedenis).\",\n});\n\nexport const newsModule = defineModule({\n id: \"news\",\n version: CORE_VERSION,\n label: \"Nieuws\",\n featureKey: \"news\",\n description: \"Nieuwsartikelen + nieuwslijst-block + 'laatste nieuws'-widget voor de zijbalk.\",\n blocks: [newsListBlock, newsWidget],\n});\n\nexport const mediaModule = defineModule({\n id: \"media\",\n version: CORE_VERSION,\n label: \"Media\",\n featureKey: \"media\",\n description: \"Mediabeheer met compressie + webp/srcset-varianten.\",\n requires: [{ kind: \"function\", name: \"onMediaUpload\" }, { kind: \"storage\", prefix: \"sites/\" }],\n});\n\nexport const formsModule = defineModule({\n id: \"forms\",\n version: CORE_VERSION,\n label: \"Formulieren\",\n featureKey: \"forms\",\n description: \"Formulierbouwer + inzendingen + e-mailnotificatie.\",\n blocks: [formBlock],\n requires: [\n { kind: \"function\", name: \"submitForm\" },\n { kind: \"firestore\", collection: \"forms\" },\n ],\n});\n\nexport const statsModule = defineModule({\n id: \"stats\",\n version: CORE_VERSION,\n label: \"Statistieken\",\n featureKey: \"stats\",\n description: \"Privacy-vriendelijke bezoekersstatistieken (geen cookies).\",\n requires: [{ kind: \"function\", name: \"trackView\" }],\n});\n\nexport const settingsModule = defineModule({\n id: \"settings\",\n version: CORE_VERSION,\n label: \"Instellingen\",\n core: true,\n description: \"Site-instellingen (nav, thema, SEO, formulier-afzender) — alleen siteadmin.\",\n});\n\n// Opt-in modules (defaultEnabled: false): worden bij installatie of later aangezet via\n// `modules: { <id>: true }`. Geen extra backend nodig — data leeft in de page-blocks.\nexport const testimonialsModule = defineModule({\n id: \"testimonials\",\n version: CORE_VERSION,\n label: \"Reviews\",\n description: \"Klantquotes/reviews met sterren, optionele Review-JSON-LD + compacte review-widget.\",\n defaultEnabled: false,\n blocks: [testimonialsBlock, testimonialsWidget],\n});\n\nexport const teamModule = defineModule({\n id: \"team\",\n version: CORE_VERSION,\n label: \"Team\",\n description: \"Team-/medewerkersoverzicht met foto, functie en bio.\",\n defaultEnabled: false,\n blocks: [teamBlock],\n});\n\nexport const agendaModule = defineModule({\n id: \"agenda\",\n version: CORE_VERSION,\n label: \"Agenda\",\n description: \"Evenementen/agenda (chronologisch) met Event-JSON-LD + 'komende agenda'-widget.\",\n defaultEnabled: false,\n blocks: [agendaBlock, agendaWidget],\n});\n\nexport const searchModule = defineModule({\n id: \"search\",\n version: CORE_VERSION,\n label: \"Zoeken\",\n description: \"Site-zoekblok dat gepubliceerde pagina's (en nieuws) client-side doorzoekt.\",\n defaultEnabled: false,\n blocks: [searchBlock],\n});\n\nexport const featureModule = defineModule({\n id: \"feature\",\n version: CORE_VERSION,\n label: \"Beeld + tekst\",\n description: \"Feature-sectie met afbeelding links/rechts en tekst.\",\n defaultEnabled: false,\n blocks: [featureBlock],\n});\n\nexport const countersModule = defineModule({\n id: \"counters\",\n version: CORE_VERSION,\n label: \"Tellers\",\n description: \"Statistiek-tellers (bv. “100+ klanten”).\",\n defaultEnabled: false,\n blocks: [countersBlock],\n});\n\nexport const logosModule = defineModule({\n id: \"logos\",\n version: CORE_VERSION,\n label: \"Logo's\",\n description: \"Partner-/klantlogo-strip.\",\n defaultEnabled: false,\n blocks: [logosBlock],\n});\n\nexport const timelineModule = defineModule({\n id: \"timeline\",\n version: CORE_VERSION,\n label: \"Tijdlijn\",\n description: \"Stappen/proces als tijdlijn.\",\n defaultEnabled: false,\n blocks: [timelineBlock],\n});\n\nexport const videoModule = defineModule({\n id: \"video\",\n version: CORE_VERSION,\n label: \"Video\",\n description: \"YouTube/Vimeo/MP4-embed (consent-gated).\",\n defaultEnabled: false,\n blocks: [videoBlock],\n});\n\nexport const bannerModule = defineModule({\n id: \"banner\",\n version: CORE_VERSION,\n label: \"Aankondiging\",\n description: \"Aankondigingsbalk, optioneel sluitbaar.\",\n defaultEnabled: false,\n blocks: [bannerBlock],\n});\n\nexport const contactModule = defineModule({\n id: \"contact\",\n version: CORE_VERSION,\n label: \"Contact + kaart + social\",\n description: \"Contactgegevens (JSON-LD), kaart (consent-gated), social-links + compacte widgets.\",\n defaultEnabled: false,\n blocks: [contactBlock, mapBlock, socialBlock, contactWidget, socialWidgetBlock],\n});\n\nexport const newsletterModule = defineModule({\n id: \"newsletter\",\n version: CORE_VERSION,\n label: \"Nieuwsbrief\",\n description: \"Aanmeldformulier via een externe provider-URL + compacte nieuwsbrief-widget.\",\n defaultEnabled: false,\n blocks: [newsletterBlock, newsletterWidget],\n});\n\nexport const bookingModule = defineModule({\n id: \"booking\",\n version: CORE_VERSION,\n label: \"Afspraak/booking\",\n description: \"Boekings-embed (bv. Calendly), consent-gated.\",\n defaultEnabled: false,\n blocks: [bookingBlock],\n});\n\nexport const accordionModule = defineModule({\n id: \"accordion\",\n version: CORE_VERSION,\n label: \"Accordeon\",\n description: \"Generieke in-/uitklapbare panelen.\",\n defaultEnabled: false,\n blocks: [accordionBlock],\n});\n\nexport const embedModule = defineModule({\n id: \"embed\",\n version: CORE_VERSION,\n label: \"Embed\",\n description: \"Generieke responsive iframe-embed (consent-gated).\",\n defaultEnabled: false,\n blocks: [embedBlock],\n});\n\n/* ── Nieuwe modules (3.1) — veelgebruikt op websites, volwaardig + widget waar zinvol ── */\n\nexport const cardsModule = defineModule({\n id: \"cards\",\n version: CORE_VERSION,\n label: \"Diensten / kaarten\",\n description: \"Diensten-/featurekaarten in een grid (icoon of beeld) + compacte 'uitgelicht'-widget.\",\n defaultEnabled: false,\n blocks: [cardsBlock, cardsWidget],\n});\n\nexport const tabsModule = defineModule({\n id: \"tabs\",\n version: CORE_VERSION,\n label: \"Tabbladen\",\n description: \"Tabblad-indeling: per tab een eigen set blokken (bv. beschrijving / specificaties).\",\n defaultEnabled: false,\n blocks: [tabsBlock],\n});\n\nexport const downloadsModule = defineModule({\n id: \"downloads\",\n version: CORE_VERSION,\n label: \"Downloads\",\n description: \"Lijst met downloadbare documenten (brochures, prijslijsten) + compacte widget.\",\n defaultEnabled: false,\n blocks: [downloadsBlock, downloadsWidget],\n});\n\nexport const pdfModule = defineModule({\n id: \"pdf\",\n version: CORE_VERSION,\n label: \"PDF-lezer\",\n description: \"Inline PDF-lezer voor menukaarten/documenten; meerdere PDF's met tabs, zelf te vervangen.\",\n defaultEnabled: false,\n blocks: [pdfBlock],\n});\n\nexport const countdownModule = defineModule({\n id: \"countdown\",\n version: CORE_VERSION,\n label: \"Aftelteller\",\n description: \"Tikkende countdown naar een datum (lancering, evenement, aanbieding) + widget.\",\n defaultEnabled: false,\n blocks: [countdownBlock, countdownWidget],\n});\n\nexport const sliderModule = defineModule({\n id: \"slider\",\n version: CORE_VERSION,\n label: \"Carrousel / slider\",\n description: \"Beeld-/contentcarrousel met dots, pijlen en optioneel autoplay (reduced-motion-veilig).\",\n defaultEnabled: false,\n blocks: [sliderBlock],\n});\n\n/* ── 4.16: nieuwe modules (allemaal opt-in) ──────────────────────────────── */\n\nexport const whatsappModule = defineModule({\n id: \"whatsapp\",\n version: CORE_VERSION,\n label: \"WhatsApp-knop\",\n description: \"Zwevende WhatsApp-/chatknop — laagdrempelig contact (wa.me-link, geen consent nodig).\",\n defaultEnabled: false,\n blocks: [whatsappChatBlock],\n});\n\nexport const beforeAfterModule = defineModule({\n id: \"beforeAfter\",\n version: CORE_VERSION,\n label: \"Voor / na\",\n description: \"Voor/na-vergelijker met sleepbare scheiding — sterk voor hoveniers, schilders, salons.\",\n defaultEnabled: false,\n blocks: [beforeAfterBlock],\n});\n\nexport const portfolioModule = defineModule({\n id: \"portfolio\",\n version: CORE_VERSION,\n label: \"Portfolio / projecten\",\n description: \"Projecten/cases-grid met categorie-filter en lightbox.\",\n defaultEnabled: false,\n blocks: [portfolioBlock],\n});\n\nexport const jobsModule = defineModule({\n id: \"jobs\",\n version: CORE_VERSION,\n label: \"Vacatures\",\n description: \"Vacature-overzicht met JobPosting-JSON-LD (vindbaar via Google Jobs).\",\n defaultEnabled: false,\n blocks: [jobsBlock],\n});\n\nexport const breadcrumbsModule = defineModule({\n id: \"breadcrumbs\",\n version: CORE_VERSION,\n label: \"Kruimelpad\",\n description: \"Kruimelpad afgeleid van de URL, met BreadcrumbList-JSON-LD (SEO).\",\n defaultEnabled: false,\n blocks: [breadcrumbsBlock],\n});\n\nexport const consentModule = defineModule({\n id: \"consent\",\n version: CORE_VERSION,\n label: \"Cookie-consent (AVG)\",\n description: \"Toestemmingsbanner + consent-opslag; stelt niet-essentiële scripts uit.\",\n defaultEnabled: false,\n settingsSchema: z.object({\n message: z\n .string()\n .default(\"We gebruiken cookies om de site te verbeteren. Kies hieronder je voorkeuren.\")\n .describe(\"Bannertekst\"),\n privacyUrl: z.string().optional().describe(\"Link naar privacyverklaring\"),\n version: z.string().optional().describe(\"Beleidsversie (wijzig → banner opnieuw tonen)\"),\n showReopen: z.boolean().default(true).describe(\"Zwevend cookie-knopje om keuze te wijzigen\"),\n categories: z\n .array(\n z.object({\n id: z.string(),\n label: z.string(),\n description: z.string().optional(),\n required: z.boolean().default(false),\n }),\n )\n .default([\n { id: \"necessary\", label: \"Noodzakelijk\", description: \"Nodig om de site te laten werken.\", required: true },\n { id: \"analytics\", label: \"Statistieken\", description: \"Anonieme statistieken om de site te verbeteren.\", required: false },\n { id: \"marketing\", label: \"Marketing\", description: \"Externe embeds zoals video's en kaarten.\", required: false },\n ]),\n }),\n defaultSettings: {},\n});\n\n/** Alle ingebouwde modules (volgorde bepaalt block- en tab-volgorde). */\nexport const builtinModules: ModuleDefinition[] = [\n contentModule,\n pagesModule,\n newsModule,\n mediaModule,\n galleryModule,\n faqModule,\n openingHoursModule,\n pricingModule,\n formsModule,\n statsModule,\n settingsModule,\n testimonialsModule,\n teamModule,\n agendaModule,\n searchModule,\n featureModule,\n countersModule,\n logosModule,\n timelineModule,\n videoModule,\n bannerModule,\n contactModule,\n newsletterModule,\n bookingModule,\n accordionModule,\n embedModule,\n cardsModule,\n tabsModule,\n downloadsModule,\n pdfModule,\n countdownModule,\n sliderModule,\n whatsappModule,\n beforeAfterModule,\n portfolioModule,\n jobsModule,\n breadcrumbsModule,\n consentModule,\n];\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useCallback, useEffect, useState } from "react";
3
+ import { useModuleSettings } from "../context";
3
4
  const STORAGE_KEY = "xicms-consent";
4
5
  const EMPTY = { categories: {}, decidedAt: null };
5
6
  function normalizeConsentState(value) {
@@ -15,6 +16,7 @@ function consentDecided(state, requiredVersion) {
15
16
  if (!requiredVersion) return true;
16
17
  return state.version === requiredVersion;
17
18
  }
19
+ let memoryFallback = null;
18
20
  function read() {
19
21
  if (typeof window === "undefined") return EMPTY;
20
22
  try {
@@ -22,10 +24,11 @@ function read() {
22
24
  if (raw) return normalizeConsentState(JSON.parse(raw));
23
25
  } catch {
24
26
  }
25
- return EMPTY;
27
+ return memoryFallback ?? EMPTY;
26
28
  }
27
29
  function useConsent() {
28
30
  const [state, setState] = useState({ categories: {}, decidedAt: null });
31
+ const settings = useModuleSettings("consent");
29
32
  useEffect(() => {
30
33
  setState(read());
31
34
  const onChange = () => setState(read());
@@ -37,12 +40,21 @@ function useConsent() {
37
40
  try {
38
41
  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
39
42
  } catch {
43
+ memoryFallback = next;
40
44
  }
41
45
  setState(next);
42
46
  window.dispatchEvent(new Event("xicms:consent"));
43
47
  }, []);
44
- const has = useCallback((category) => state.categories[category] === true, [state]);
45
- return { state, decided: state.decidedAt !== null, has, save };
48
+ const decided = consentDecided(state, settings.version);
49
+ const has = useCallback(
50
+ (category) => {
51
+ if (!decided) return false;
52
+ if (state.categories[category] === true) return true;
53
+ return settings.categories !== void 0 && !settings.categories.some((c) => c.id === category);
54
+ },
55
+ [decided, state, settings.categories]
56
+ );
57
+ return { state, decided, has, save };
46
58
  }
47
59
  export {
48
60
  consentDecided,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/modules/consent/useConsent.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\n\nconst STORAGE_KEY = \"xicms-consent\";\n\nexport interface ConsentState {\n /** Per categorie-id of de bezoeker toestemming gaf. */\n categories: Record<string, boolean>;\n /** Tijdstip (ms) waarop de keuze is gemaakt; null = nog niet gekozen. */\n decidedAt: number | null;\n /** Versie van het cookiebeleid waarvoor de keuze gold — wijzigt de site zijn beleid\n * (nieuwe `version` in de instellingen), dan telt een oude keuze niet meer. */\n version?: string;\n}\n\nconst EMPTY: ConsentState = { categories: {}, decidedAt: null };\n\n/** Normaliseer willekeurige opgeslagen JSON naar een geldige ConsentState (defensief tegen\n * corrupte/oude vormen — `has()` mag nooit crashen op een afwijkende localStorage-waarde). */\nexport function normalizeConsentState(value: unknown): ConsentState {\n if (!value || typeof value !== \"object\") return EMPTY;\n const v = value as Record<string, unknown>;\n const categories =\n v.categories && typeof v.categories === \"object\" ? (v.categories as Record<string, boolean>) : {};\n const decidedAt = typeof v.decidedAt === \"number\" ? v.decidedAt : null;\n const version = typeof v.version === \"string\" ? v.version : undefined;\n return version === undefined ? { categories, decidedAt } : { categories, decidedAt, version };\n}\n\n/**\n * Geldt de opgeslagen keuze nog? Alleen als er beslist is én de keuze bij de huidige\n * beleidsversie hoort (geen `requiredVersion` = elke keuze telt — het pre-versie-gedrag).\n */\nexport function consentDecided(state: ConsentState, requiredVersion?: string): boolean {\n if (state.decidedAt === null) return false;\n if (!requiredVersion) return true;\n return state.version === requiredVersion;\n}\n\nfunction read(): ConsentState {\n if (typeof window === \"undefined\") return EMPTY;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw) return normalizeConsentState(JSON.parse(raw));\n } catch {\n /* corrupt/onbeschikbaar: behandel als nog-niet-gekozen */\n }\n return EMPTY;\n}\n\n/**\n * Leest/zet cookie-consent (client-side, geen backend). Andere code gebruikt `has(\"analytics\")`\n * om niet-essentiële scripts pas te laden na toestemming. De `xicms:consent`-event laat losse\n * luisteraars reageren zonder re-render.\n */\nexport function useConsent() {\n const [state, setState] = useState<ConsentState>({ categories: {}, decidedAt: null });\n\n useEffect(() => {\n setState(read());\n const onChange = () => setState(read());\n window.addEventListener(\"xicms:consent\", onChange);\n return () => window.removeEventListener(\"xicms:consent\", onChange);\n }, []);\n\n const save = useCallback((categories: Record<string, boolean>, version?: string) => {\n const next: ConsentState = { categories, decidedAt: Date.now(), ...(version ? { version } : {}) };\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));\n } catch {\n /* opslag geweigerd: keuze geldt dan alleen voor deze sessie */\n }\n setState(next);\n window.dispatchEvent(new Event(\"xicms:consent\"));\n }, []);\n\n const has = useCallback((category: string) => state.categories[category] === true, [state]);\n\n return { state, decided: state.decidedAt !== null, has, save };\n}\n"],"mappings":";AAEA,SAAS,aAAa,WAAW,gBAAgB;AAEjD,MAAM,cAAc;AAYpB,MAAM,QAAsB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK;AAIvD,SAAS,sBAAsB,OAA8B;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,aACJ,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAClG,QAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAClE,QAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,SAAO,YAAY,SAAY,EAAE,YAAY,UAAU,IAAI,EAAE,YAAY,WAAW,QAAQ;AAC9F;AAMO,SAAS,eAAe,OAAqB,iBAAmC;AACrF,MAAI,MAAM,cAAc,KAAM,QAAO;AACrC,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO,MAAM,YAAY;AAC3B;AAEA,SAAS,OAAqB;AAC5B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,WAAW;AACnD,QAAI,IAAK,QAAO,sBAAsB,KAAK,MAAM,GAAG,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAOO,SAAS,aAAa;AAC3B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK,CAAC;AAEpF,YAAU,MAAM;AACd,aAAS,KAAK,CAAC;AACf,UAAM,WAAW,MAAM,SAAS,KAAK,CAAC;AACtC,WAAO,iBAAiB,iBAAiB,QAAQ;AACjD,WAAO,MAAM,OAAO,oBAAoB,iBAAiB,QAAQ;AAAA,EACnE,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,YAAY,CAAC,YAAqC,YAAqB;AAClF,UAAM,OAAqB,EAAE,YAAY,WAAW,KAAK,IAAI,GAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAChG,QAAI;AACF,aAAO,aAAa,QAAQ,aAAa,KAAK,UAAU,IAAI,CAAC;AAAA,IAC/D,QAAQ;AAAA,IAER;AACA,aAAS,IAAI;AACb,WAAO,cAAc,IAAI,MAAM,eAAe,CAAC;AAAA,EACjD,GAAG,CAAC,CAAC;AAEL,QAAM,MAAM,YAAY,CAAC,aAAqB,MAAM,WAAW,QAAQ,MAAM,MAAM,CAAC,KAAK,CAAC;AAE1F,SAAO,EAAE,OAAO,SAAS,MAAM,cAAc,MAAM,KAAK,KAAK;AAC/D;","names":[]}
1
+ {"version":3,"sources":["../../../src/modules/consent/useConsent.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useModuleSettings } from \"../context\";\n\nconst STORAGE_KEY = \"xicms-consent\";\n\nexport interface ConsentState {\n /** Per categorie-id of de bezoeker toestemming gaf. */\n categories: Record<string, boolean>;\n /** Tijdstip (ms) waarop de keuze is gemaakt; null = nog niet gekozen. */\n decidedAt: number | null;\n /** Versie van het cookiebeleid waarvoor de keuze gold — wijzigt de site zijn beleid\n * (nieuwe `version` in de instellingen), dan telt een oude keuze niet meer. */\n version?: string;\n}\n\nconst EMPTY: ConsentState = { categories: {}, decidedAt: null };\n\n/** Normaliseer willekeurige opgeslagen JSON naar een geldige ConsentState (defensief tegen\n * corrupte/oude vormen — `has()` mag nooit crashen op een afwijkende localStorage-waarde). */\nexport function normalizeConsentState(value: unknown): ConsentState {\n if (!value || typeof value !== \"object\") return EMPTY;\n const v = value as Record<string, unknown>;\n const categories =\n v.categories && typeof v.categories === \"object\" ? (v.categories as Record<string, boolean>) : {};\n const decidedAt = typeof v.decidedAt === \"number\" ? v.decidedAt : null;\n const version = typeof v.version === \"string\" ? v.version : undefined;\n return version === undefined ? { categories, decidedAt } : { categories, decidedAt, version };\n}\n\n/**\n * Geldt de opgeslagen keuze nog? Alleen als er beslist is én de keuze bij de huidige\n * beleidsversie hoort (geen `requiredVersion` = elke keuze telt — het pre-versie-gedrag).\n */\nexport function consentDecided(state: ConsentState, requiredVersion?: string): boolean {\n if (state.decidedAt === null) return false;\n if (!requiredVersion) return true;\n return state.version === requiredVersion;\n}\n\n// Vangnet als localStorage weigert (privémodus/vol): de keuze geldt dan alleen voor deze\n// paginasessie, maar wél consistent voor álle componenten (read() geeft hetzelfde terug).\nlet memoryFallback: ConsentState | null = null;\n\nfunction read(): ConsentState {\n if (typeof window === \"undefined\") return EMPTY;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw) return normalizeConsentState(JSON.parse(raw));\n } catch {\n /* corrupt/onbeschikbaar: behandel als nog-niet-gekozen */\n }\n return memoryFallback ?? EMPTY;\n}\n\n/**\n * Leest/zet cookie-consent (client-side, geen backend). Andere code gebruikt `has(\"analytics\")`\n * om niet-essentiële scripts pas te laden na toestemming. De `xicms:consent`-event laat losse\n * luisteraars reageren zonder re-render.\n */\nexport function useConsent() {\n const [state, setState] = useState<ConsentState>({ categories: {}, decidedAt: null });\n // Beleidsversie + categorie-configuratie van de site: een keuze van een oude versie telt niet\n // meer (AVG), en dat moet ook voor `has()` gelden — niet alleen voor het hertonen van de banner.\n const settings = useModuleSettings<{ version?: string; categories?: Array<{ id: string }> }>(\"consent\");\n\n useEffect(() => {\n setState(read());\n const onChange = () => setState(read());\n window.addEventListener(\"xicms:consent\", onChange);\n return () => window.removeEventListener(\"xicms:consent\", onChange);\n }, []);\n\n const save = useCallback((categories: Record<string, boolean>, version?: string) => {\n const next: ConsentState = { categories, decidedAt: Date.now(), ...(version ? { version } : {}) };\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));\n } catch {\n memoryFallback = next; // opslag geweigerd: keuze geldt alleen voor deze paginasessie\n }\n setState(next);\n window.dispatchEvent(new Event(\"xicms:consent\"));\n }, []);\n\n const decided = consentDecided(state, settings.version);\n const has = useCallback(\n (category: string) => {\n if (!decided) return false;\n if (state.categories[category] === true) return true;\n // Een categorie die niet in de banner voorkomt kan een bezoeker nooit aanvinken; na een\n // geldige keuze vallen we dan open i.p.v. de inhoud eeuwig achter een onbereikbare gate\n // te zetten (maatwerk-categorieën vs. de standaard-gates van video/kaart/embed).\n return settings.categories !== undefined && !settings.categories.some((c) => c.id === category);\n },\n [decided, state, settings.categories],\n );\n\n return { state, decided, has, save };\n}\n"],"mappings":";AAEA,SAAS,aAAa,WAAW,gBAAgB;AACjD,SAAS,yBAAyB;AAElC,MAAM,cAAc;AAYpB,MAAM,QAAsB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK;AAIvD,SAAS,sBAAsB,OAA8B;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,aACJ,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAClG,QAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAClE,QAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,SAAO,YAAY,SAAY,EAAE,YAAY,UAAU,IAAI,EAAE,YAAY,WAAW,QAAQ;AAC9F;AAMO,SAAS,eAAe,OAAqB,iBAAmC;AACrF,MAAI,MAAM,cAAc,KAAM,QAAO;AACrC,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO,MAAM,YAAY;AAC3B;AAIA,IAAI,iBAAsC;AAE1C,SAAS,OAAqB;AAC5B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,WAAW;AACnD,QAAI,IAAK,QAAO,sBAAsB,KAAK,MAAM,GAAG,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,SAAO,kBAAkB;AAC3B;AAOO,SAAS,aAAa;AAC3B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK,CAAC;AAGpF,QAAM,WAAW,kBAA4E,SAAS;AAEtG,YAAU,MAAM;AACd,aAAS,KAAK,CAAC;AACf,UAAM,WAAW,MAAM,SAAS,KAAK,CAAC;AACtC,WAAO,iBAAiB,iBAAiB,QAAQ;AACjD,WAAO,MAAM,OAAO,oBAAoB,iBAAiB,QAAQ;AAAA,EACnE,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,YAAY,CAAC,YAAqC,YAAqB;AAClF,UAAM,OAAqB,EAAE,YAAY,WAAW,KAAK,IAAI,GAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAChG,QAAI;AACF,aAAO,aAAa,QAAQ,aAAa,KAAK,UAAU,IAAI,CAAC;AAAA,IAC/D,QAAQ;AACN,uBAAiB;AAAA,IACnB;AACA,aAAS,IAAI;AACb,WAAO,cAAc,IAAI,MAAM,eAAe,CAAC;AAAA,EACjD,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,eAAe,OAAO,SAAS,OAAO;AACtD,QAAM,MAAM;AAAA,IACV,CAAC,aAAqB;AACpB,UAAI,CAAC,QAAS,QAAO;AACrB,UAAI,MAAM,WAAW,QAAQ,MAAM,KAAM,QAAO;AAIhD,aAAO,SAAS,eAAe,UAAa,CAAC,SAAS,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAAA,IAChG;AAAA,IACA,CAAC,SAAS,OAAO,SAAS,UAAU;AAAA,EACtC;AAEA,SAAO,EAAE,OAAO,SAAS,KAAK,KAAK;AACrC;","names":[]}