@drawnagency/primitives 0.1.0 → 0.2.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 (139) hide show
  1. package/dist/auth/index.js +26 -3
  2. package/dist/chunk-2VTPWODA.js +60 -0
  3. package/dist/chunk-CS7F6IOY.js +39 -0
  4. package/dist/chunk-HOJAF4VD.js +264 -0
  5. package/dist/chunk-IP6ODLXX.js +341 -0
  6. package/dist/chunk-T4BJ6RSB.js +58 -0
  7. package/dist/chunk-UKEVUCIZ.js +200 -0
  8. package/dist/chunk-UMSFICAC.js +36 -0
  9. package/dist/index.js +156 -4
  10. package/dist/lib/index.js +62 -12
  11. package/dist/lib/sanitize.d.ts.map +1 -1
  12. package/dist/media/index.js +36 -9
  13. package/dist/schemas/index.js +52 -7
  14. package/package.json +5 -4
  15. package/src/lib/sanitize.ts +6 -2
  16. package/dist/auth/cookies.js +0 -44
  17. package/dist/auth/errors.js +0 -10
  18. package/dist/auth/security.js +0 -48
  19. package/dist/auth/types.js +0 -1
  20. package/dist/components/brandguide/ColorSwatchSettings.js +0 -10
  21. package/dist/components/brandguide/Colors.js +0 -79
  22. package/dist/components/brandguide/DoDontList.js +0 -22
  23. package/dist/components/brandguide/DoDontMediaGrid.js +0 -5
  24. package/dist/components/editor/AudiencePicker.js +0 -24
  25. package/dist/components/editor/DeleteButton.js +0 -6
  26. package/dist/components/editor/DragHandle.js +0 -8
  27. package/dist/components/editor/InsertButton.js +0 -7
  28. package/dist/components/editor/SectionWrapper.js +0 -135
  29. package/dist/components/editor/SettingsButton.js +0 -6
  30. package/dist/components/editor/SettingsForm.js +0 -64
  31. package/dist/components/editor/StatusBadge.js +0 -10
  32. package/dist/components/editor/StatusPicker.js +0 -30
  33. package/dist/components/editor/index.js +0 -7
  34. package/dist/components/primitives/CustomParagraph.js +0 -24
  35. package/dist/components/primitives/EditableGrid.js +0 -90
  36. package/dist/components/primitives/EditableList.js +0 -54
  37. package/dist/components/primitives/EditablePlainText.js +0 -52
  38. package/dist/components/primitives/EditableRichText.js +0 -86
  39. package/dist/components/primitives/HeadingSection.js +0 -7
  40. package/dist/components/primitives/IconPicker.js +0 -21
  41. package/dist/components/primitives/LinkPopover.js +0 -48
  42. package/dist/components/primitives/MediaSettingsForms.js +0 -42
  43. package/dist/components/primitives/ResolvedMedia.js +0 -9
  44. package/dist/components/primitives/RichTextToolbar.js +0 -26
  45. package/dist/components/primitives/tiptap-presets.js +0 -44
  46. package/dist/components/primitives/useEditableCollection.js +0 -61
  47. package/dist/components/primitives/useEditablePlainText.js +0 -27
  48. package/dist/components/primitives/useEditableRichText.js +0 -52
  49. package/dist/components/sections/Button/CTAButton.js +0 -18
  50. package/dist/components/sections/Button/index.js +0 -28
  51. package/dist/components/sections/Colors/index.js +0 -34
  52. package/dist/components/sections/DoDontList/index.js +0 -33
  53. package/dist/components/sections/DoDontMediaGrid/index.js +0 -41
  54. package/dist/components/sections/IconList/IconList.js +0 -131
  55. package/dist/components/sections/IconList/IconListSettings.js +0 -22
  56. package/dist/components/sections/IconList/index.js +0 -27
  57. package/dist/components/sections/LinkHeading/index.js +0 -15
  58. package/dist/components/sections/MediaGrid/MediaGrid.js +0 -62
  59. package/dist/components/sections/MediaGrid/index.js +0 -35
  60. package/dist/components/sections/Prose/Prose.js +0 -11
  61. package/dist/components/sections/Prose/index.js +0 -15
  62. package/dist/components/sections/SectionLayout.js +0 -15
  63. package/dist/components/sections/SplitContent/SplitContent.js +0 -31
  64. package/dist/components/sections/SplitContent/SplitContentSettings.js +0 -17
  65. package/dist/components/sections/SplitContent/index.js +0 -27
  66. package/dist/components/sections/SubHeading/index.js +0 -18
  67. package/dist/components/sections/SubSubHeading/index.js +0 -18
  68. package/dist/components/sections/ViewRenderer.js +0 -13
  69. package/dist/components/sections/register-schemas.js +0 -15
  70. package/dist/components/sections/register.js +0 -15
  71. package/dist/components/shared/Button.js +0 -27
  72. package/dist/components/shared/Checkbox.js +0 -10
  73. package/dist/components/shared/ColorPicker.js +0 -5
  74. package/dist/components/shared/ErrorBoundary.js +0 -30
  75. package/dist/components/shared/FontPicker.js +0 -190
  76. package/dist/components/shared/FormLabel.js +0 -5
  77. package/dist/components/shared/IconButton.js +0 -16
  78. package/dist/components/shared/Input.js +0 -8
  79. package/dist/components/shared/Navigation.js +0 -71
  80. package/dist/components/shared/PasswordInput.js +0 -11
  81. package/dist/components/shared/Popover.js +0 -33
  82. package/dist/components/shared/PopoverItem.js +0 -6
  83. package/dist/components/shared/Select.js +0 -9
  84. package/dist/components/shared/Textarea.js +0 -8
  85. package/dist/components/shared/Toggle.js +0 -5
  86. package/dist/components/shared/Tooltip.js +0 -8
  87. package/dist/components/shared/icons.js +0 -23
  88. package/dist/components/shell/AudienceAddForm.js +0 -43
  89. package/dist/components/shell/AudienceRow.js +0 -74
  90. package/dist/components/shell/EditorContext.js +0 -24
  91. package/dist/components/shell/EditorLoginForm.js +0 -46
  92. package/dist/components/shell/EditorModal.js +0 -43
  93. package/dist/components/shell/EditorModalContext.js +0 -20
  94. package/dist/components/shell/EditorShell.js +0 -483
  95. package/dist/components/shell/MediaLibraryContext.js +0 -5
  96. package/dist/components/shell/MediaLibraryModal.js +0 -145
  97. package/dist/components/shell/ProcessingIndicator.js +0 -15
  98. package/dist/components/shell/SectionSkeleton.js +0 -22
  99. package/dist/components/shell/SectionTypePicker.js +0 -15
  100. package/dist/components/shell/SiteSettingsDisplay.js +0 -28
  101. package/dist/components/shell/SiteSettingsModal.js +0 -40
  102. package/dist/components/shell/SiteSettingsUsers.js +0 -87
  103. package/dist/components/shell/SiteSettingsViewerAccess.js +0 -94
  104. package/dist/components/shell/ViewerLoginForm.js +0 -40
  105. package/dist/data/google-fonts.json +0 -7718
  106. package/dist/hooks/index.js +0 -6
  107. package/dist/hooks/useActiveHeadings.js +0 -99
  108. package/dist/hooks/useEditorPersistence.js +0 -73
  109. package/dist/hooks/useEditorPublish.js +0 -145
  110. package/dist/hooks/useFocusTrap.js +0 -51
  111. package/dist/hooks/useMediaPipeline.js +0 -253
  112. package/dist/hooks/useResolvedMedia.js +0 -39
  113. package/dist/lib/cn.js +0 -5
  114. package/dist/lib/contrast.js +0 -11
  115. package/dist/lib/dexie.js +0 -236
  116. package/dist/lib/events.js +0 -15
  117. package/dist/lib/google-fonts.js +0 -11
  118. package/dist/lib/grid.js +0 -7
  119. package/dist/lib/icons.js +0 -27
  120. package/dist/lib/loader.js +0 -57
  121. package/dist/lib/nav.js +0 -58
  122. package/dist/lib/registry.js +0 -64
  123. package/dist/lib/safeRedirect.js +0 -11
  124. package/dist/lib/sanitize.js +0 -6
  125. package/dist/lib/timestamp.js +0 -28
  126. package/dist/media/github.js +0 -60
  127. package/dist/media/queue.js +0 -116
  128. package/dist/media/resolve.js +0 -50
  129. package/dist/media/types.js +0 -1
  130. package/dist/media/utils.js +0 -41
  131. package/dist/media/videoPoster.js +0 -44
  132. package/dist/media/worker.js +0 -73
  133. package/dist/schemas/audience.js +0 -19
  134. package/dist/schemas/auth.js +0 -22
  135. package/dist/schemas/media-grid-options.js +0 -7
  136. package/dist/schemas/media.js +0 -28
  137. package/dist/schemas/sections.js +0 -12
  138. package/dist/schemas/shared.js +0 -71
  139. package/dist/schemas/site-config.js +0 -26
@@ -0,0 +1,341 @@
1
+ // src/media/utils.ts
2
+ async function hashFileBuffer(buffer) {
3
+ if (typeof crypto !== "undefined" && crypto.subtle) {
4
+ const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
5
+ const hashArray = new Uint8Array(hashBuffer);
6
+ return Array.from(hashArray.slice(0, 8)).map((b) => b.toString(16).padStart(2, "0")).join("");
7
+ }
8
+ const bytes = new Uint8Array(buffer);
9
+ const seeds = [2166136261, 84696351];
10
+ let result = "";
11
+ for (const seed of seeds) {
12
+ let h = seed;
13
+ for (let i = 0; i < bytes.length; i++) {
14
+ h ^= bytes[i];
15
+ h = Math.imul(h, 16777619);
16
+ }
17
+ result += (h >>> 0).toString(16).padStart(8, "0");
18
+ }
19
+ return result;
20
+ }
21
+ function sanitizeMediaName(name) {
22
+ return name.replace(/\.[^.]+$/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
23
+ }
24
+ var MIME_TO_EXT = {
25
+ "image/gif": "gif",
26
+ "image/apng": "apng",
27
+ "image/png": "png",
28
+ "image/jpeg": "jpg",
29
+ "image/webp": "webp",
30
+ "image/svg+xml": "svg",
31
+ "video/mp4": "mp4",
32
+ "video/webm": "webm"
33
+ };
34
+ var EXT_TO_MIME = {
35
+ ...Object.fromEntries(
36
+ Object.entries(MIME_TO_EXT).map(([mime, ext]) => [`.${ext}`, mime])
37
+ ),
38
+ ".jpeg": "image/jpeg"
39
+ };
40
+ function mimeToExt(mime) {
41
+ return MIME_TO_EXT[mime] ?? "bin";
42
+ }
43
+ function displayFilenameExt(mime) {
44
+ const ext = MIME_TO_EXT[mime];
45
+ return ext ? `.${ext}` : "";
46
+ }
47
+
48
+ // src/media/github.ts
49
+ function createGitHubMediaAdapter(manifest) {
50
+ return {
51
+ async hash(file) {
52
+ return hashFileBuffer(file.buffer);
53
+ },
54
+ async exists(hash) {
55
+ return hash in manifest.images;
56
+ },
57
+ async process(file, hash, _sizes) {
58
+ const name = sanitizeMediaName(file.originalName);
59
+ const folder = `${name}-${hash}`;
60
+ return {
61
+ id: hash,
62
+ hash,
63
+ kind: "image",
64
+ originalName: name,
65
+ width: 0,
66
+ height: 0,
67
+ mimeType: "image/webp",
68
+ size: 0,
69
+ folder,
70
+ variants: [],
71
+ alt: ""
72
+ };
73
+ },
74
+ async delete(id) {
75
+ delete manifest.images[id];
76
+ },
77
+ resolve(id, sizes) {
78
+ const item = manifest.images[id];
79
+ if (!item) return null;
80
+ if (item.kind === "video") {
81
+ return {
82
+ tag: "video",
83
+ src: `/api/media/${id}/original.${mimeToExt(item.mimeType)}`,
84
+ poster: `/api/media/${id}/poster.webp`,
85
+ width: item.width,
86
+ height: item.height,
87
+ autoplay: true,
88
+ loop: true,
89
+ muted: true
90
+ };
91
+ }
92
+ if (item.kind === "animated") {
93
+ return {
94
+ tag: "img",
95
+ src: `/api/media/${id}/original.${mimeToExt(item.mimeType)}`,
96
+ width: item.width,
97
+ height: item.height
98
+ };
99
+ }
100
+ const availableVariants = item.variants.filter(
101
+ (v) => sizes.length === 0 || sizes.includes(v.width)
102
+ );
103
+ const sorted = [...availableVariants].sort((a, b) => a.width - b.width);
104
+ const midIndex = Math.floor(sorted.length / 2);
105
+ const defaultVariant = sorted[midIndex] ?? sorted[0];
106
+ if (!defaultVariant) return null;
107
+ const srcSet = sorted.map((v) => `/api/media/${id}/${v.width}.webp ${v.width}w`).join(", ");
108
+ return {
109
+ tag: "img",
110
+ src: `/api/media/${id}/${defaultVariant.width}.webp`,
111
+ srcSet,
112
+ width: item.width,
113
+ height: item.height
114
+ };
115
+ },
116
+ async list() {
117
+ return Object.values(manifest.images);
118
+ }
119
+ };
120
+ }
121
+
122
+ // src/media/queue.ts
123
+ var ProcessingQueue = class {
124
+ items = /* @__PURE__ */ new Map();
125
+ pending = [];
126
+ activeWorkers = /* @__PURE__ */ new Map();
127
+ options;
128
+ nextId = 0;
129
+ constructor(options) {
130
+ this.options = options;
131
+ }
132
+ add(input) {
133
+ const id = `media-${this.nextId++}`;
134
+ const item = {
135
+ id,
136
+ originalName: input.originalName,
137
+ mimeType: input.mimeType,
138
+ hash: input.hash,
139
+ kind: input.kind,
140
+ percent: 0,
141
+ state: "queued"
142
+ };
143
+ this.items.set(id, item);
144
+ this.pending.push({ input, id });
145
+ this.options.onEvent({ type: "queued", item: { ...item } });
146
+ this.processNext();
147
+ return id;
148
+ }
149
+ getStatus() {
150
+ let active = 0;
151
+ let queued = 0;
152
+ for (const item of this.items.values()) {
153
+ if (item.state === "active") active++;
154
+ if (item.state === "queued") queued++;
155
+ }
156
+ return {
157
+ active,
158
+ queued,
159
+ total: this.items.size,
160
+ items: Array.from(this.items.values())
161
+ };
162
+ }
163
+ processNext() {
164
+ while (this.activeWorkers.size < this.options.maxConcurrent && this.pending.length > 0) {
165
+ const next = this.pending.shift();
166
+ this.startProcessing(next.id, next.input);
167
+ }
168
+ }
169
+ startProcessing(id, input) {
170
+ const item = this.items.get(id);
171
+ item.state = "active";
172
+ this.options.onEvent({ type: "started", item: { ...item } });
173
+ const worker = this.options.createWorker();
174
+ this.activeWorkers.set(id, worker);
175
+ worker.onmessage = (e) => {
176
+ const msg = e.data;
177
+ if (msg.id !== id) return;
178
+ if (msg.type === "progress") {
179
+ item.percent = msg.percent;
180
+ this.options.onEvent({ type: "progress", item: { ...item } });
181
+ } else if (msg.type === "complete") {
182
+ item.state = "complete";
183
+ item.percent = 100;
184
+ item.result = {
185
+ variants: msg.variants,
186
+ primaryBlob: msg.primaryBlob,
187
+ posterBlob: msg.posterBlob,
188
+ width: msg.width,
189
+ height: msg.height
190
+ };
191
+ this.options.onEvent({ type: "complete", item: { ...item } });
192
+ this.cleanupWorker(id, worker);
193
+ this.processNext();
194
+ } else if (msg.type === "error") {
195
+ item.state = "error";
196
+ item.error = msg.message;
197
+ this.options.onEvent({ type: "error", item: { ...item } });
198
+ this.cleanupWorker(id, worker);
199
+ this.processNext();
200
+ }
201
+ };
202
+ worker.onerror = (e) => {
203
+ item.state = "error";
204
+ item.error = e.message || "Worker error";
205
+ this.options.onEvent({ type: "error", item: { ...item } });
206
+ this.cleanupWorker(id, worker);
207
+ this.processNext();
208
+ };
209
+ worker.postMessage({
210
+ type: "process",
211
+ id,
212
+ buffer: input.buffer,
213
+ originalName: input.originalName,
214
+ mimeType: input.mimeType,
215
+ hash: input.hash,
216
+ kind: input.kind,
217
+ sizes: this.options.sizes,
218
+ quality: this.options.quality
219
+ });
220
+ }
221
+ cleanupWorker(id, worker) {
222
+ worker.terminate();
223
+ this.activeWorkers.delete(id);
224
+ }
225
+ destroy() {
226
+ for (const [id, worker] of this.activeWorkers) {
227
+ worker.terminate();
228
+ this.activeWorkers.delete(id);
229
+ }
230
+ this.pending = [];
231
+ this.items.clear();
232
+ }
233
+ };
234
+
235
+ // src/media/resolve.ts
236
+ function resolveMedia(item, manifest, sizes) {
237
+ if (!item.imageId) return null;
238
+ const adapter = createMediaAdapter(manifest);
239
+ return adapter.resolve(item.imageId, sizes);
240
+ }
241
+ function resolveManifestReferences(content, manifest, sizes) {
242
+ if (Array.isArray(content)) {
243
+ return content.map((item) => resolveManifestReferences(item, manifest, sizes));
244
+ }
245
+ if (content !== null && typeof content === "object") {
246
+ const obj = content;
247
+ if (typeof obj.imageId === "string") {
248
+ const manifestItem = manifest.images[obj.imageId];
249
+ const resolved = resolveMedia({ imageId: obj.imageId }, manifest, sizes);
250
+ const patch = {
251
+ alt: manifestItem?.alt ?? ""
252
+ };
253
+ if (resolved && resolved.tag === "img") {
254
+ patch.src = resolved.src;
255
+ if ("srcSet" in resolved && resolved.srcSet) {
256
+ patch.srcset = resolved.srcSet;
257
+ }
258
+ } else if (resolved && resolved.tag === "video") {
259
+ patch.src = resolved.src;
260
+ if (resolved.poster) {
261
+ patch.poster = resolved.poster;
262
+ }
263
+ }
264
+ return { ...obj, ...patch };
265
+ }
266
+ const result = {};
267
+ for (const [key, value] of Object.entries(obj)) {
268
+ result[key] = resolveManifestReferences(value, manifest, sizes);
269
+ }
270
+ return result;
271
+ }
272
+ return content;
273
+ }
274
+
275
+ // src/media/videoPoster.ts
276
+ function generateVideoPoster(blob, quality) {
277
+ return new Promise((resolve, reject) => {
278
+ const video = document.createElement("video");
279
+ const url = URL.createObjectURL(blob);
280
+ video.muted = true;
281
+ video.preload = "auto";
282
+ video.src = url;
283
+ const cleanup = () => {
284
+ URL.revokeObjectURL(url);
285
+ video.removeAttribute("src");
286
+ video.load();
287
+ };
288
+ video.addEventListener("loadeddata", () => {
289
+ video.addEventListener("seeked", () => {
290
+ try {
291
+ const w = video.videoWidth;
292
+ const h = video.videoHeight;
293
+ const canvas = document.createElement("canvas");
294
+ canvas.width = w;
295
+ canvas.height = h;
296
+ const ctx = canvas.getContext("2d");
297
+ ctx.drawImage(video, 0, 0);
298
+ canvas.toBlob(
299
+ (posterBlob) => {
300
+ cleanup();
301
+ if (!posterBlob) {
302
+ reject(new Error("Failed to create poster blob"));
303
+ return;
304
+ }
305
+ resolve({ posterBlob, width: w, height: h });
306
+ },
307
+ "image/webp",
308
+ quality / 100
309
+ );
310
+ } catch (err) {
311
+ cleanup();
312
+ reject(err);
313
+ }
314
+ }, { once: true });
315
+ video.currentTime = isFinite(video.duration) ? Math.min(0.1, video.duration / 2) : 0;
316
+ }, { once: true });
317
+ video.addEventListener("error", () => {
318
+ cleanup();
319
+ reject(new Error("Failed to load video for poster generation"));
320
+ }, { once: true });
321
+ });
322
+ }
323
+
324
+ // src/media/index.ts
325
+ function createMediaAdapter(manifest) {
326
+ return createGitHubMediaAdapter(manifest);
327
+ }
328
+
329
+ export {
330
+ hashFileBuffer,
331
+ sanitizeMediaName,
332
+ MIME_TO_EXT,
333
+ EXT_TO_MIME,
334
+ mimeToExt,
335
+ displayFilenameExt,
336
+ ProcessingQueue,
337
+ resolveMedia,
338
+ resolveManifestReferences,
339
+ generateVideoPoster,
340
+ createMediaAdapter
341
+ };
@@ -0,0 +1,58 @@
1
+ import {
2
+ HexColorSchema
3
+ } from "./chunk-UKEVUCIZ.js";
4
+
5
+ // src/schemas/audience.ts
6
+ import { z } from "zod";
7
+ var AudienceNameSchema = z.string().min(1).max(32).regex(
8
+ /^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$/,
9
+ "lowercase alphanumeric, dashes and underscores allowed"
10
+ );
11
+ var AudienceColorSchema = HexColorSchema.nullable();
12
+ function slugifyAudienceName(input) {
13
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
14
+ }
15
+
16
+ // src/schemas/media-grid-options.ts
17
+ import { z as z2 } from "zod";
18
+ var MediaGridOptionsSchema = z2.object({
19
+ square: z2.boolean().optional(),
20
+ border: z2.boolean().optional(),
21
+ crop: z2.boolean().optional(),
22
+ showCaptions: z2.boolean().optional()
23
+ }).default({});
24
+
25
+ // src/schemas/auth.ts
26
+ import { z as z3 } from "zod";
27
+ var RoleSchema = z3.enum(["owner", "editor"]);
28
+ var SessionSchema = z3.object({
29
+ userId: z3.string().nullable(),
30
+ email: z3.string().nullable(),
31
+ role: RoleSchema,
32
+ siteId: z3.string()
33
+ });
34
+ var SiteUserSchema = z3.object({
35
+ id: z3.string(),
36
+ email: z3.string(),
37
+ role: RoleSchema,
38
+ createdAt: z3.string()
39
+ });
40
+ var AudienceSchema = z3.object({
41
+ name: z3.string(),
42
+ displayName: z3.string(),
43
+ color: z3.string().nullable(),
44
+ readonly: z3.boolean(),
45
+ hasPassword: z3.boolean(),
46
+ isDefault: z3.boolean()
47
+ });
48
+
49
+ export {
50
+ AudienceNameSchema,
51
+ AudienceColorSchema,
52
+ slugifyAudienceName,
53
+ MediaGridOptionsSchema,
54
+ RoleSchema,
55
+ SessionSchema,
56
+ SiteUserSchema,
57
+ AudienceSchema
58
+ };
@@ -0,0 +1,200 @@
1
+ import {
2
+ MediaConfigSchema
3
+ } from "./chunk-UMSFICAC.js";
4
+
5
+ // src/schemas/shared.ts
6
+ import { z } from "zod";
7
+ var TextHeadingLine = z.object({
8
+ type: z.literal("heading"),
9
+ text: z.string()
10
+ });
11
+ var TextParagraphLine = z.object({
12
+ type: z.enum(["paragraph", "paragraph_large"]),
13
+ text: z.string()
14
+ });
15
+ var TextLabelValueLine = z.object({
16
+ type: z.literal("label_value"),
17
+ label: z.string(),
18
+ text: z.string()
19
+ });
20
+ var TextListItemLine = z.object({
21
+ type: z.enum(["list_item_unordered", "list_item_ordered"]),
22
+ text: z.string()
23
+ });
24
+ var TextLineSchema = z.discriminatedUnion("type", [
25
+ TextHeadingLine,
26
+ TextParagraphLine,
27
+ TextLabelValueLine,
28
+ TextListItemLine
29
+ ]);
30
+ var BaseMediaRef = z.object({
31
+ imageId: z.string().default(""),
32
+ caption: z.union([z.string(), z.array(z.string())]).optional(),
33
+ background: z.string().optional(),
34
+ invertFrom: z.string().optional(),
35
+ border: z.boolean().optional(),
36
+ objectFit: z.enum(["cover", "contain"]).optional()
37
+ });
38
+ var ImageRef = BaseMediaRef.extend({
39
+ type: z.literal("image")
40
+ });
41
+ var VideoRef = BaseMediaRef.extend({
42
+ type: z.literal("video"),
43
+ poster: z.string().optional(),
44
+ autoplay: z.boolean().optional(),
45
+ loop: z.boolean().optional(),
46
+ muted: z.boolean().optional()
47
+ });
48
+ var DoDontImageRef = BaseMediaRef.extend({
49
+ type: z.literal("doDontImage"),
50
+ doDont: z.enum(["do", "dont"])
51
+ });
52
+ var LinkedImageRef = BaseMediaRef.extend({
53
+ type: z.literal("linkedImage"),
54
+ href: z.string(),
55
+ target: z.string().optional(),
56
+ linkText: z.string().optional()
57
+ });
58
+ var MediaReferenceSchema = z.discriminatedUnion("type", [
59
+ ImageRef,
60
+ VideoRef,
61
+ DoDontImageRef,
62
+ LinkedImageRef
63
+ ]);
64
+ var HexColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/, "must be a 6-digit hex color");
65
+ var ColorSpaceSchema = z.object({
66
+ hex: HexColorSchema.optional(),
67
+ rgb: z.string().optional(),
68
+ cmyk: z.string().optional(),
69
+ pantone: z.string().optional()
70
+ }).refine(
71
+ (data) => data.hex !== void 0 || data.rgb !== void 0 || data.cmyk !== void 0 || data.pantone !== void 0,
72
+ { message: "At least one color space must be defined" }
73
+ );
74
+ var ColorItemSchema = z.object({
75
+ name: z.string().optional(),
76
+ spaces: z.array(ColorSpaceSchema).min(1)
77
+ });
78
+
79
+ // src/lib/registry.ts
80
+ function defineSection(def) {
81
+ return def;
82
+ }
83
+ function createRegistry() {
84
+ const sections = /* @__PURE__ */ new Map();
85
+ const schemas = /* @__PURE__ */ new Map();
86
+ return {
87
+ registerSection(def) {
88
+ if (sections.has(def.type)) {
89
+ throw new Error(`Section type "${def.type}" is already registered`);
90
+ }
91
+ sections.set(def.type, def);
92
+ },
93
+ registerSchema(type, schema) {
94
+ schemas.set(type, schema);
95
+ },
96
+ getSection(type) {
97
+ return sections.get(type);
98
+ },
99
+ getSchema(type) {
100
+ return schemas.get(type) ?? sections.get(type)?.schema;
101
+ },
102
+ getAllSections() {
103
+ return Array.from(sections.values());
104
+ },
105
+ getAllSchemas() {
106
+ const merged = new Map(schemas);
107
+ for (const [type, def] of sections) {
108
+ if (!merged.has(type)) merged.set(type, def.schema);
109
+ }
110
+ return Array.from(merged.values());
111
+ },
112
+ clearRegistry() {
113
+ sections.clear();
114
+ schemas.clear();
115
+ }
116
+ };
117
+ }
118
+ var defaultRegistry = createRegistry();
119
+ function registerSection(def) {
120
+ defaultRegistry.registerSection(def);
121
+ }
122
+ function registerSchema(type, schema) {
123
+ defaultRegistry.registerSchema(type, schema);
124
+ }
125
+ function getSection(type) {
126
+ return defaultRegistry.getSection(type);
127
+ }
128
+ function getSchema(type) {
129
+ return defaultRegistry.getSchema(type);
130
+ }
131
+ function getAllSections() {
132
+ return defaultRegistry.getAllSections();
133
+ }
134
+ function getAllSchemas() {
135
+ return defaultRegistry.getAllSchemas();
136
+ }
137
+ function clearRegistry() {
138
+ defaultRegistry.clearRegistry();
139
+ }
140
+
141
+ // src/schemas/sections.ts
142
+ import { z as z2 } from "zod";
143
+ function getSectionContentSchema() {
144
+ const schemas = getAllSchemas();
145
+ if (schemas.length < 2) {
146
+ throw new Error("At least 2 section schemas must be registered before validation");
147
+ }
148
+ return z2.union(schemas);
149
+ }
150
+ function getSectionSchema() {
151
+ return z2.object({ id: z2.string() }).and(getSectionContentSchema());
152
+ }
153
+
154
+ // src/schemas/site-config.ts
155
+ import { z as z3 } from "zod";
156
+ var SectionMetaSchema = z3.object({
157
+ type: z3.string(),
158
+ status: z3.enum(["draft", "published", "archived"]),
159
+ access: z3.array(z3.string())
160
+ });
161
+ var IndexSchema = z3.object({
162
+ siteId: z3.string(),
163
+ order: z3.array(z3.string()),
164
+ sections: z3.record(z3.string(), SectionMetaSchema)
165
+ }).refine(
166
+ (data) => data.order.every((id) => id in data.sections),
167
+ { message: "All order entries must have a corresponding section in sections" }
168
+ );
169
+ var SiteConfigSchema = z3.object({
170
+ siteName: z3.string().default("Brand Portal"),
171
+ primaryColor: HexColorSchema.default("#009ca6"),
172
+ primaryContrast: HexColorSchema.default("#f0f0f0"),
173
+ darkMode: z3.enum(["light", "dark", "optional"]).default("light"),
174
+ headingFont: z3.string().default("system-ui"),
175
+ bodyFont: z3.string().default("system-ui"),
176
+ googleFontsUrl: z3.string().refine((url) => url.startsWith("https://fonts.googleapis.com/"), "Must be a Google Fonts URL").nullable().default(null),
177
+ media: MediaConfigSchema.default(MediaConfigSchema.parse({}))
178
+ });
179
+
180
+ export {
181
+ TextLineSchema,
182
+ MediaReferenceSchema,
183
+ HexColorSchema,
184
+ ColorSpaceSchema,
185
+ ColorItemSchema,
186
+ defineSection,
187
+ createRegistry,
188
+ registerSection,
189
+ registerSchema,
190
+ getSection,
191
+ getSchema,
192
+ getAllSections,
193
+ getAllSchemas,
194
+ clearRegistry,
195
+ getSectionContentSchema,
196
+ getSectionSchema,
197
+ SectionMetaSchema,
198
+ IndexSchema,
199
+ SiteConfigSchema
200
+ };
@@ -0,0 +1,36 @@
1
+ // src/schemas/media.ts
2
+ import { z } from "zod";
3
+ var VariantSchema = z.object({
4
+ width: z.number().int().positive(),
5
+ height: z.number().int().positive(),
6
+ size: z.number().int().nonnegative()
7
+ });
8
+ var MediaItemSchema = z.object({
9
+ id: z.string(),
10
+ hash: z.string(),
11
+ kind: z.enum(["image", "animated", "video"]),
12
+ originalName: z.string(),
13
+ width: z.number().int().positive(),
14
+ height: z.number().int().positive(),
15
+ mimeType: z.string(),
16
+ size: z.number().int().nonnegative(),
17
+ folder: z.string(),
18
+ variants: z.array(VariantSchema),
19
+ alt: z.string().default("")
20
+ });
21
+ var ImageManifestSchema = z.object({
22
+ images: z.record(z.string(), MediaItemSchema)
23
+ });
24
+ var MediaConfigSchema = z.object({
25
+ adapter: z.enum(["github"]).default("github"),
26
+ sizes: z.array(z.number()).default([640, 1080, 1920]),
27
+ maxFileSize: z.number().default(5242880),
28
+ quality: z.number().min(1).max(100).default(85)
29
+ });
30
+
31
+ export {
32
+ VariantSchema,
33
+ MediaItemSchema,
34
+ ImageManifestSchema,
35
+ MediaConfigSchema
36
+ };