@drawnagency/primitives 0.1.71 → 0.1.73

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 (90) hide show
  1. package/dist/{chunk-RTYJZAKR.js → chunk-7T2WQSWF.js} +12 -12
  2. package/dist/chunk-IFHRHV7S.js +117 -0
  3. package/dist/{chunk-2BHPWO7M.js → chunk-N6AE6NBB.js} +158 -32
  4. package/dist/{chunk-WXVKX6PY.js → chunk-NKVZGPR6.js} +41 -95
  5. package/dist/{chunk-3DCG4DNU.js → chunk-PZJL2S3Z.js} +8 -4
  6. package/dist/chunk-RBTSUR5V.js +37 -0
  7. package/dist/{chunk-6UDYJQL6.js → chunk-S55OZ7F2.js} +9 -1
  8. package/dist/{chunk-NR7UB6DZ.js → chunk-SFKYUKVN.js} +1 -1
  9. package/dist/{chunk-YMVYTYGQ.js → chunk-SWIHUIFT.js} +28 -16
  10. package/dist/{chunk-KWBM27UR.js → chunk-T7MP7ANP.js} +13 -64
  11. package/dist/{chunk-MU3CDERQ.js → chunk-XTRTK26E.js} +276 -96
  12. package/dist/{chunk-GKV77AA6.js → chunk-ZRWGV5O6.js} +3 -3
  13. package/dist/chunk-ZSPCE6MZ.js +70 -0
  14. package/dist/components/editor/SettingsForm.d.ts.map +1 -1
  15. package/dist/components/editor/index.js +6 -3
  16. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
  17. package/dist/components/primitives/index.js +7 -7
  18. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
  19. package/dist/components/sections/Video/VideoBlock.d.ts +31 -0
  20. package/dist/components/sections/Video/VideoBlock.d.ts.map +1 -0
  21. package/dist/components/sections/Video/index.d.ts +28 -0
  22. package/dist/components/sections/Video/index.d.ts.map +1 -0
  23. package/dist/components/sections/all-sections.d.ts +25 -0
  24. package/dist/components/sections/all-sections.d.ts.map +1 -1
  25. package/dist/components/sections/brand-guide.js +7 -7
  26. package/dist/components/sections/core-sections.d.ts +25 -0
  27. package/dist/components/sections/core-sections.d.ts.map +1 -1
  28. package/dist/components/sections/register-schemas.js +13 -12
  29. package/dist/components/sections/register.js +13 -12
  30. package/dist/components/shared/Tabs.d.ts +1 -1
  31. package/dist/components/shared/VideoSourceField.d.ts +8 -0
  32. package/dist/components/shared/VideoSourceField.d.ts.map +1 -0
  33. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  34. package/dist/components/shell/MediaLibraryModal.d.ts +4 -1
  35. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  36. package/dist/hooks/useEditorPublish.d.ts +4 -1
  37. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  38. package/dist/hooks/useMediaPipeline.d.ts +9 -1
  39. package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
  40. package/dist/index.js +28 -24
  41. package/dist/lib/index.js +10 -10
  42. package/dist/lib/registry.d.ts +6 -0
  43. package/dist/lib/registry.d.ts.map +1 -1
  44. package/dist/lib/save-chunks.d.ts +28 -0
  45. package/dist/lib/save-chunks.d.ts.map +1 -0
  46. package/dist/lib/video-url.d.ts +7 -0
  47. package/dist/lib/video-url.d.ts.map +1 -0
  48. package/dist/lib/video-url.js +9 -0
  49. package/dist/media/index.js +6 -2
  50. package/dist/media/resolve.d.ts +14 -0
  51. package/dist/media/resolve.d.ts.map +1 -1
  52. package/dist/media/videoPoster.d.ts +1 -0
  53. package/dist/media/videoPoster.d.ts.map +1 -1
  54. package/dist/schemas/index.js +4 -4
  55. package/dist/schemas/link.d.ts.map +1 -1
  56. package/dist/schemas/link.js +1 -1
  57. package/dist/schemas/media.d.ts +13 -0
  58. package/dist/schemas/media.d.ts.map +1 -1
  59. package/dist/schemas/shared.d.ts.map +1 -1
  60. package/dist/schemas/site-config.d.ts +1 -0
  61. package/dist/schemas/site-config.d.ts.map +1 -1
  62. package/dist/storage/index.d.ts +1 -1
  63. package/dist/storage/index.d.ts.map +1 -1
  64. package/dist/storage/types.d.ts +26 -0
  65. package/dist/storage/types.d.ts.map +1 -1
  66. package/package.json +5 -1
  67. package/src/components/editor/SettingsForm.tsx +11 -0
  68. package/src/components/primitives/ResolvedMedia.tsx +10 -5
  69. package/src/components/sections/Media/MediaBlock.tsx +2 -1
  70. package/src/components/sections/Video/VideoBlock.tsx +149 -0
  71. package/src/components/sections/Video/index.tsx +111 -0
  72. package/src/components/sections/core-sections.ts +2 -0
  73. package/src/components/shared/Tabs.tsx +2 -2
  74. package/src/components/shared/VideoSourceField.tsx +144 -0
  75. package/src/components/shell/EditorShell.tsx +16 -2
  76. package/src/components/shell/MediaLibraryModal.tsx +47 -1
  77. package/src/hooks/useEditorPublish.ts +137 -50
  78. package/src/hooks/useMediaPipeline.ts +188 -25
  79. package/src/lib/registry.ts +7 -0
  80. package/src/lib/save-chunks.ts +53 -0
  81. package/src/lib/video-url.ts +39 -0
  82. package/src/media/resolve.ts +18 -0
  83. package/src/media/videoPoster.ts +2 -2
  84. package/src/schemas/link.ts +15 -4
  85. package/src/schemas/media.ts +12 -0
  86. package/src/schemas/shared.ts +23 -14
  87. package/src/storage/index.ts +1 -1
  88. package/src/storage/types.ts +23 -0
  89. package/dist/chunk-4E66N6UV.js +0 -10
  90. package/dist/chunk-I5737FJY.js +0 -62
@@ -1,25 +1,25 @@
1
- import {
2
- Checkbox,
3
- FormLabel,
4
- Input
5
- } from "./chunk-5THS4K5D.js";
6
1
  import {
7
2
  EditableGrid,
8
3
  EditablePlainText,
9
4
  MediaBlock,
10
5
  containerGridClass,
11
6
  useEditableCollection
12
- } from "./chunk-KWBM27UR.js";
7
+ } from "./chunk-T7MP7ANP.js";
8
+ import {
9
+ Checkbox,
10
+ FormLabel,
11
+ Input
12
+ } from "./chunk-5THS4K5D.js";
13
13
  import {
14
14
  AddIcon,
15
15
  DeleteIcon,
16
16
  DragHandle,
17
17
  IconButton
18
- } from "./chunk-I5737FJY.js";
18
+ } from "./chunk-IFHRHV7S.js";
19
19
  import {
20
20
  DEFAULT_LINK,
21
21
  LinkValueSchema
22
- } from "./chunk-3DCG4DNU.js";
22
+ } from "./chunk-PZJL2S3Z.js";
23
23
  import {
24
24
  curatedIcons,
25
25
  getIcon
@@ -27,13 +27,13 @@ import {
27
27
  import {
28
28
  cn
29
29
  } from "./chunk-7EOSDFS3.js";
30
- import {
31
- HexColorSchema,
32
- SingleMediaReferenceSchema
33
- } from "./chunk-YMVYTYGQ.js";
34
30
  import {
35
31
  defineSection
36
32
  } from "./chunk-VKAGMEKE.js";
33
+ import {
34
+ HexColorSchema,
35
+ SingleMediaReferenceSchema
36
+ } from "./chunk-SWIHUIFT.js";
37
37
 
38
38
  // src/components/sections/Colors/index.tsx
39
39
  import { z as z2 } from "zod";
@@ -0,0 +1,117 @@
1
+ import {
2
+ getMediaProvider
3
+ } from "./chunk-S55OZ7F2.js";
4
+ import {
5
+ cn
6
+ } from "./chunk-7EOSDFS3.js";
7
+
8
+ // src/components/shell/MediaLibraryContext.tsx
9
+ import { createContext, useContext } from "react";
10
+ var MediaLibraryContext = createContext(null);
11
+ function useMediaLibrary() {
12
+ return useContext(MediaLibraryContext);
13
+ }
14
+
15
+ // src/hooks/useResolvedMedia.ts
16
+ import { useMemo } from "react";
17
+ function useResolvedMedia(imageId) {
18
+ const ctx = useMediaLibrary();
19
+ return useMemo(() => {
20
+ if (!imageId || !ctx) {
21
+ return { src: void 0, srcset: void 0, poster: void 0, alt: "", kind: "image" };
22
+ }
23
+ const { manifest, pendingItems, pendingLocalUrls, siteConfig } = ctx;
24
+ const sizes = siteConfig?.media?.sizes ?? [640, 1080, 1920];
25
+ const manifestItem = manifest.images[imageId];
26
+ const pendingItem = pendingItems.find((i) => i.id === imageId);
27
+ const item = manifestItem ?? pendingItem;
28
+ const alt = item?.alt ?? "";
29
+ const kind = item?.kind ?? "image";
30
+ const width = item?.width;
31
+ const height = item?.height;
32
+ const localUrl = pendingLocalUrls[imageId];
33
+ if (localUrl) {
34
+ return { src: localUrl, srcset: void 0, poster: void 0, alt, kind, width, height };
35
+ }
36
+ if (manifestItem) {
37
+ const provider = getMediaProvider();
38
+ const resolved = provider.resolve(manifestItem, sizes);
39
+ if (resolved && resolved.tag === "img") {
40
+ return {
41
+ src: resolved.src,
42
+ srcset: "srcSet" in resolved ? resolved.srcSet : void 0,
43
+ poster: void 0,
44
+ alt,
45
+ kind,
46
+ width: resolved.width,
47
+ height: resolved.height
48
+ };
49
+ }
50
+ if (resolved && resolved.tag === "video") {
51
+ return { src: resolved.src, srcset: void 0, poster: resolved.poster, alt, kind, width: resolved.width, height: resolved.height };
52
+ }
53
+ }
54
+ return { src: void 0, srcset: void 0, poster: void 0, alt, kind, width, height };
55
+ }, [imageId, ctx]);
56
+ }
57
+
58
+ // src/components/shared/icons.tsx
59
+ import { GripVertical, Plus, Trash2, X, Settings } from "lucide-react";
60
+ import { GripVertical as GripVertical2, Plus as Plus2, Trash2 as Trash22, X as X2, Settings as Settings2 } from "lucide-react";
61
+ import { jsx } from "react/jsx-runtime";
62
+ function DragHandle({ size = 16 }) {
63
+ return /* @__PURE__ */ jsx(GripVertical, { size });
64
+ }
65
+ function AddIcon({ size = 14 }) {
66
+ return /* @__PURE__ */ jsx(Plus, { size });
67
+ }
68
+ function DeleteIcon({ size = 14 }) {
69
+ return /* @__PURE__ */ jsx(Trash2, { size });
70
+ }
71
+ function SettingsIcon({ size = 14 }) {
72
+ return /* @__PURE__ */ jsx(Settings, { size });
73
+ }
74
+
75
+ // src/components/shared/IconButton.tsx
76
+ import { forwardRef } from "react";
77
+ import { jsx as jsx2 } from "react/jsx-runtime";
78
+ var sizeClasses = {
79
+ sm: "h-chrome-sm w-chrome-sm",
80
+ md: "h-chrome-md w-chrome-md",
81
+ lg: "h-chrome-lg w-chrome-lg"
82
+ };
83
+ var intentClasses = {
84
+ default: "text-base-contrast-light hover:text-base-contrast",
85
+ destructive: "text-destructive hover:text-destructive-hover",
86
+ primary: "text-primary/60 hover:text-primary"
87
+ };
88
+ var IconButton = forwardRef(
89
+ function IconButton2({ icon, label, size = "md", intent = "default", className, ...rest }, ref) {
90
+ return /* @__PURE__ */ jsx2(
91
+ "button",
92
+ {
93
+ ref,
94
+ type: "button",
95
+ className: cn(
96
+ "cursor-pointer flex items-center justify-center rounded transition-colors",
97
+ sizeClasses[size],
98
+ intentClasses[intent],
99
+ className
100
+ ),
101
+ "aria-label": label,
102
+ ...rest,
103
+ children: icon
104
+ }
105
+ );
106
+ }
107
+ );
108
+
109
+ export {
110
+ useMediaLibrary,
111
+ useResolvedMedia,
112
+ DragHandle,
113
+ AddIcon,
114
+ DeleteIcon,
115
+ SettingsIcon,
116
+ IconButton
117
+ };
@@ -1,17 +1,20 @@
1
+ import {
2
+ Button,
3
+ usePages
4
+ } from "./chunk-ZSPCE6MZ.js";
1
5
  import {
2
6
  Checkbox,
3
7
  FormLabel,
4
8
  Input
5
9
  } from "./chunk-5THS4K5D.js";
6
- import {
7
- usePages
8
- } from "./chunk-4E66N6UV.js";
9
10
  import {
10
11
  DeleteIcon,
11
12
  DragHandle,
12
13
  IconButton,
13
- SettingsIcon
14
- } from "./chunk-I5737FJY.js";
14
+ SettingsIcon,
15
+ useMediaLibrary,
16
+ useResolvedMedia
17
+ } from "./chunk-IFHRHV7S.js";
15
18
  import {
16
19
  cn
17
20
  } from "./chunk-7EOSDFS3.js";
@@ -86,7 +89,7 @@ function SettingsButton({ onClick }) {
86
89
  }
87
90
 
88
91
  // src/components/editor/SettingsForm.tsx
89
- import { useId as useId2, useState as useState2 } from "react";
92
+ import { useId as useId2, useState as useState3 } from "react";
90
93
 
91
94
  // src/components/shared/Select.tsx
92
95
  import { useId } from "react";
@@ -205,11 +208,125 @@ function LinkField({ label, value, onChange }) {
205
208
  ] });
206
209
  }
207
210
 
208
- // src/components/shared/Tabs.tsx
211
+ // src/components/shared/VideoSourceField.tsx
209
212
  import { useState } from "react";
210
213
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
214
+ function VideoSourceField({ value, onChange }) {
215
+ const mediaLibrary = useMediaLibrary();
216
+ const [mode, setMode] = useState(value?.type === "external" ? "embed" : "library");
217
+ const [url, setUrl] = useState(value?.type === "external" ? value.url : "");
218
+ const [loading, setLoading] = useState(false);
219
+ const [error, setError] = useState(null);
220
+ const [fallback, setFallback] = useState(false);
221
+ const libraryImageId = value?.type === "library" ? value.ref.imageId : void 0;
222
+ const resolved = useResolvedMedia(libraryImageId);
223
+ async function submitEmbed() {
224
+ if (loading) return;
225
+ const trimmed = url.trim();
226
+ if (!trimmed) return;
227
+ setLoading(true);
228
+ setError(null);
229
+ setFallback(false);
230
+ try {
231
+ const res = await fetch(`/api/media/oembed?url=${encodeURIComponent(trimmed)}`);
232
+ const body = await res.json();
233
+ if (!res.ok) {
234
+ setError(body?.message ?? "Couldn't load that video.");
235
+ return;
236
+ }
237
+ onChange({
238
+ type: "external",
239
+ provider: body.provider,
240
+ id: body.id,
241
+ url: body.url,
242
+ width: body.width,
243
+ height: body.height
244
+ });
245
+ if (body.fallback) {
246
+ setFallback(true);
247
+ } else {
248
+ setUrl("");
249
+ }
250
+ } catch {
251
+ setError("Couldn't reach the video service.");
252
+ } finally {
253
+ setLoading(false);
254
+ }
255
+ }
256
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3", children: [
257
+ /* @__PURE__ */ jsx7(FormLabel, { children: "Source" }),
258
+ /* @__PURE__ */ jsx7("div", { className: "flex gap-2", role: "tablist", children: ["library", "embed"].map((m) => /* @__PURE__ */ jsx7(
259
+ "button",
260
+ {
261
+ type: "button",
262
+ role: "tab",
263
+ "aria-selected": mode === m,
264
+ onClick: () => setMode(m),
265
+ className: cn(
266
+ "cursor-pointer rounded border px-3 py-1 text-sm",
267
+ mode === m ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
268
+ ),
269
+ children: m === "library" ? "Choose from library" : "Paste embed URL"
270
+ },
271
+ m
272
+ )) }),
273
+ mode === "library" ? /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
274
+ libraryImageId ? resolved.poster ? /* @__PURE__ */ jsx7("img", { src: resolved.poster, alt: "", className: "h-12 w-20 rounded object-cover" }) : /* @__PURE__ */ jsx7("span", { className: "text-sm text-base-contrast-light", children: "Video selected" }) : /* @__PURE__ */ jsx7("span", { className: "text-sm text-base-contrast-light", children: "No video selected" }),
275
+ /* @__PURE__ */ jsx7(
276
+ Button,
277
+ {
278
+ type: "button",
279
+ variant: "secondary",
280
+ size: "sm",
281
+ onClick: () => mediaLibrary?.openSelectModal((id) => onChange({ type: "library", ref: { imageId: id } })),
282
+ children: "Choose video"
283
+ }
284
+ )
285
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
286
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
287
+ /* @__PURE__ */ jsx7(
288
+ "input",
289
+ {
290
+ type: "text",
291
+ value: url,
292
+ onChange: (e) => {
293
+ setUrl(e.target.value);
294
+ setError(null);
295
+ setFallback(false);
296
+ },
297
+ onKeyDown: (e) => {
298
+ if (e.key === "Enter") {
299
+ e.preventDefault();
300
+ void submitEmbed();
301
+ }
302
+ },
303
+ placeholder: "Paste a YouTube or Vimeo URL\u2026",
304
+ className: "flex-1 rounded border border-base-200 bg-base px-2 py-1 text-sm text-base-contrast placeholder:text-base-contrast-light focus:outline-none focus:ring-1 focus:ring-primary"
305
+ }
306
+ ),
307
+ /* @__PURE__ */ jsx7(
308
+ Button,
309
+ {
310
+ type: "button",
311
+ variant: "primary",
312
+ size: "sm",
313
+ onClick: () => void submitEmbed(),
314
+ disabled: loading || !url.trim(),
315
+ children: loading ? "Loading\u2026" : "Use link"
316
+ }
317
+ )
318
+ ] }),
319
+ error ? /* @__PURE__ */ jsx7("p", { className: "text-xs text-red-600", children: error }) : null,
320
+ !error && fallback ? /* @__PURE__ */ jsx7("p", { className: "text-xs text-base-contrast-light/70", children: "Added \u2014 couldn't fetch exact dimensions, using 16:9." }) : null
321
+ ] })
322
+ ] });
323
+ }
324
+
325
+ // src/components/shared/Tabs.tsx
326
+ import { useState as useState2 } from "react";
327
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
211
328
  function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar }) {
212
- const [uncontrolledId, setUncontrolledId] = useState(defaultTabId ?? tabs[0]?.id);
329
+ const [uncontrolledId, setUncontrolledId] = useState2(defaultTabId ?? tabs[0]?.id);
213
330
  const isControlled = activeTabId !== void 0;
214
331
  const activeId = isControlled ? activeTabId : uncontrolledId;
215
332
  const activeTab = tabs.find((t) => t.id === activeId) ?? tabs[0];
@@ -217,8 +334,8 @@ function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar })
217
334
  if (!isControlled) setUncontrolledId(id);
218
335
  onTabChange?.(id);
219
336
  }
220
- return /* @__PURE__ */ jsxs5("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
221
- /* @__PURE__ */ jsx7("div", { className: cn("border-b border-base-200", fullBleedTabBar && "-mx-6 px-6"), children: /* @__PURE__ */ jsx7("div", { className: "flex", role: "tablist", children: tabs.map((tab) => /* @__PURE__ */ jsx7(
337
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
338
+ /* @__PURE__ */ jsx8("div", { className: cn("border-b border-base-200", fullBleedTabBar && "-mx-6 px-6"), children: /* @__PURE__ */ jsx8("div", { className: "flex", role: "tablist", children: tabs.map((tab) => /* @__PURE__ */ jsx8(
222
339
  "button",
223
340
  {
224
341
  type: "button",
@@ -233,12 +350,12 @@ function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar })
233
350
  },
234
351
  tab.id
235
352
  )) }) }),
236
- /* @__PURE__ */ jsx7("div", { role: "tabpanel", children: activeTab?.content })
353
+ /* @__PURE__ */ jsx8("div", { role: "tabpanel", className: cn(fullBleedTabBar && "pt-4"), children: activeTab?.content })
237
354
  ] });
238
355
  }
239
356
 
240
357
  // src/components/editor/SettingsForm.tsx
241
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
358
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
242
359
  function resolveValues(schema, values) {
243
360
  const resolved = {};
244
361
  for (const [key, field] of Object.entries(schema)) {
@@ -278,9 +395,9 @@ function RangeField({
278
395
  onChange
279
396
  }) {
280
397
  const id = useId2();
281
- return /* @__PURE__ */ jsxs6("div", { children: [
282
- /* @__PURE__ */ jsx8(FormLabel, { htmlFor: id, children: field.label }),
283
- /* @__PURE__ */ jsx8(
398
+ return /* @__PURE__ */ jsxs7("div", { children: [
399
+ /* @__PURE__ */ jsx9(FormLabel, { htmlFor: id, children: field.label }),
400
+ /* @__PURE__ */ jsx9(
284
401
  "input",
285
402
  {
286
403
  id,
@@ -300,7 +417,7 @@ function RangeField({
300
417
  function renderField(key, field, value, handleFieldChange) {
301
418
  switch (field.type) {
302
419
  case "text":
303
- return /* @__PURE__ */ jsx8(
420
+ return /* @__PURE__ */ jsx9(
304
421
  Input,
305
422
  {
306
423
  label: field.label,
@@ -311,7 +428,7 @@ function renderField(key, field, value, handleFieldChange) {
311
428
  key
312
429
  );
313
430
  case "number":
314
- return /* @__PURE__ */ jsx8(
431
+ return /* @__PURE__ */ jsx9(
315
432
  Input,
316
433
  {
317
434
  label: field.label,
@@ -322,7 +439,7 @@ function renderField(key, field, value, handleFieldChange) {
322
439
  key
323
440
  );
324
441
  case "checkbox":
325
- return /* @__PURE__ */ jsx8(
442
+ return /* @__PURE__ */ jsx9(
326
443
  Checkbox,
327
444
  {
328
445
  label: field.label,
@@ -332,7 +449,7 @@ function renderField(key, field, value, handleFieldChange) {
332
449
  key
333
450
  );
334
451
  case "select":
335
- return /* @__PURE__ */ jsx8(
452
+ return /* @__PURE__ */ jsx9(
336
453
  Select,
337
454
  {
338
455
  label: field.label,
@@ -343,7 +460,7 @@ function renderField(key, field, value, handleFieldChange) {
343
460
  key
344
461
  );
345
462
  case "range":
346
- return /* @__PURE__ */ jsx8(
463
+ return /* @__PURE__ */ jsx9(
347
464
  RangeField,
348
465
  {
349
466
  field,
@@ -353,7 +470,7 @@ function renderField(key, field, value, handleFieldChange) {
353
470
  key
354
471
  );
355
472
  case "link":
356
- return /* @__PURE__ */ jsx8(
473
+ return /* @__PURE__ */ jsx9(
357
474
  LinkField,
358
475
  {
359
476
  label: field.label,
@@ -362,12 +479,21 @@ function renderField(key, field, value, handleFieldChange) {
362
479
  },
363
480
  key
364
481
  );
482
+ case "video-source":
483
+ return /* @__PURE__ */ jsx9(
484
+ VideoSourceField,
485
+ {
486
+ value,
487
+ onChange: (v) => handleFieldChange(key, v)
488
+ },
489
+ key
490
+ );
365
491
  default:
366
492
  return null;
367
493
  }
368
494
  }
369
495
  function SettingsForm({ schema, values, onChange, tabs }) {
370
- const [local, setLocal] = useState2(() => resolveValues(schema, values));
496
+ const [local, setLocal] = useState3(() => resolveValues(schema, values));
371
497
  function handleFieldChange(key, newValue) {
372
498
  const next = { ...local, [key]: newValue };
373
499
  setLocal(next);
@@ -378,16 +504,16 @@ function SettingsForm({ schema, values, onChange, tabs }) {
378
504
  const listed = new Set(tabs.flatMap((t) => t.fields));
379
505
  const otherKeys = Object.keys(schema).filter((key) => !listed.has(key));
380
506
  const tabItems = [
381
- ...tabs.map((t) => ({ id: t.label, label: t.label, content: /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: fields(t.fields) }) })),
382
- ...otherKeys.length > 0 ? [{ id: "__other__", label: "Other", content: /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: fields(otherKeys) }) }] : []
507
+ ...tabs.map((t) => ({ id: t.label, label: t.label, content: /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-4", children: fields(t.fields) }) })),
508
+ ...otherKeys.length > 0 ? [{ id: "__other__", label: "Other", content: /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-4", children: fields(otherKeys) }) }] : []
383
509
  ];
384
- return /* @__PURE__ */ jsx8(Tabs, { tabs: tabItems, fullBleedTabBar: true });
510
+ return /* @__PURE__ */ jsx9(Tabs, { tabs: tabItems, fullBleedTabBar: true });
385
511
  }
386
- return /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: Object.entries(schema).map(([key, field]) => renderField(key, field, local[key], handleFieldChange)) });
512
+ return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-4", children: Object.entries(schema).map(([key, field]) => renderField(key, field, local[key], handleFieldChange)) });
387
513
  }
388
514
 
389
515
  // src/components/shared/Popover.tsx
390
- import { useEffect as useEffect2, useLayoutEffect, useRef, useState as useState3 } from "react";
516
+ import { useEffect as useEffect2, useLayoutEffect, useRef, useState as useState4 } from "react";
391
517
 
392
518
  // src/hooks/useFocusTrap.ts
393
519
  import { useEffect } from "react";
@@ -438,7 +564,7 @@ function useFocusTrap(ref, active) {
438
564
  }
439
565
 
440
566
  // src/components/shared/Popover.tsx
441
- import { jsx as jsx9 } from "react/jsx-runtime";
567
+ import { jsx as jsx10 } from "react/jsx-runtime";
442
568
  function Popover({
443
569
  isOpen,
444
570
  onClose,
@@ -448,7 +574,7 @@ function Popover({
448
574
  className
449
575
  }) {
450
576
  const panelRef = useRef(null);
451
- const [resolvedAlign, setResolvedAlign] = useState3("start");
577
+ const [resolvedAlign, setResolvedAlign] = useState4("start");
452
578
  useFocusTrap(panelRef, isOpen);
453
579
  useLayoutEffect(() => {
454
580
  if (!isOpen || align !== "auto") {
@@ -488,7 +614,7 @@ function Popover({
488
614
  }, [isOpen, onClose, anchorRef]);
489
615
  if (!isOpen) return null;
490
616
  const effectiveAlign = align === "auto" ? resolvedAlign : align;
491
- return /* @__PURE__ */ jsx9(
617
+ return /* @__PURE__ */ jsx10(
492
618
  "div",
493
619
  {
494
620
  ref: panelRef,
@@ -506,10 +632,10 @@ function Popover({
506
632
 
507
633
  // src/components/shared/PopoverItem.tsx
508
634
  import { forwardRef as forwardRef2 } from "react";
509
- import { jsx as jsx10 } from "react/jsx-runtime";
635
+ import { jsx as jsx11 } from "react/jsx-runtime";
510
636
  var PopoverItem = forwardRef2(
511
637
  function PopoverItem2({ className, children, ...rest }, ref) {
512
- return /* @__PURE__ */ jsx10(
638
+ return /* @__PURE__ */ jsx11(
513
639
  "button",
514
640
  {
515
641
  ref,