@drawnagency/primitives 0.1.38 → 0.1.40

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 (56) hide show
  1. package/dist/{chunk-OUFUUBZ4.js → chunk-62OWSJ7V.js} +1 -1
  2. package/dist/{chunk-V43WVSVS.js → chunk-A4RARGF2.js} +6 -2
  3. package/dist/{chunk-C2MVDXD7.js → chunk-BU52OBPW.js} +27 -8
  4. package/dist/{chunk-VCZBZMXU.js → chunk-VY67DS3O.js} +36 -10
  5. package/dist/components/primitives/MediaSettingsForms.d.ts +6 -2
  6. package/dist/components/primitives/MediaSettingsForms.d.ts.map +1 -1
  7. package/dist/components/primitives/ResolvedMedia.d.ts +2 -1
  8. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
  9. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  10. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
  11. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
  12. package/dist/hooks/useBuildStatus.d.ts.map +1 -1
  13. package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
  14. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  15. package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
  16. package/dist/index.js +8 -4
  17. package/dist/lib/dexie.d.ts.map +1 -1
  18. package/dist/lib/dexie.js +1 -2
  19. package/dist/lib/index.d.ts +1 -1
  20. package/dist/lib/index.d.ts.map +1 -1
  21. package/dist/lib/index.js +4 -2
  22. package/dist/lib/loader.d.ts.map +1 -1
  23. package/dist/lib/nav.d.ts.map +1 -1
  24. package/dist/lib/sanitize.d.ts +10 -0
  25. package/dist/lib/sanitize.d.ts.map +1 -1
  26. package/dist/lib/text.d.ts.map +1 -1
  27. package/dist/media/index.js +3 -1
  28. package/dist/media/queue.d.ts.map +1 -1
  29. package/dist/media/utils.d.ts +1 -0
  30. package/dist/media/utils.d.ts.map +1 -1
  31. package/dist/media/videoPoster.d.ts.map +1 -1
  32. package/dist/schemas/index.js +2 -2
  33. package/dist/schemas/site-config.d.ts +1 -0
  34. package/dist/schemas/site-config.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/components/primitives/MediaSettingsForms.tsx +25 -6
  37. package/src/components/primitives/ResolvedMedia.tsx +8 -1
  38. package/src/components/sections/MediaGrid/MediaGrid.tsx +3 -0
  39. package/src/components/shell/EditorShell.tsx +3 -0
  40. package/src/components/shell/MediaLibraryModal.tsx +2 -5
  41. package/src/components/shell/SiteSettingsDisplay.tsx +7 -0
  42. package/src/components/shell/SiteSettingsModal.tsx +1 -1
  43. package/src/hooks/useBuildStatus.ts +13 -3
  44. package/src/hooks/useEditorPersistence.ts +19 -6
  45. package/src/hooks/useEditorPublish.ts +23 -6
  46. package/src/hooks/useMediaPipeline.ts +21 -6
  47. package/src/lib/dexie.ts +1 -2
  48. package/src/lib/index.ts +1 -1
  49. package/src/lib/loader.ts +3 -2
  50. package/src/lib/nav.ts +15 -3
  51. package/src/lib/sanitize.ts +22 -3
  52. package/src/lib/text.ts +5 -1
  53. package/src/media/queue.ts +3 -1
  54. package/src/media/utils.ts +8 -0
  55. package/src/media/videoPoster.ts +31 -10
  56. package/src/schemas/site-config.ts +7 -2
@@ -5,21 +5,46 @@ export function generateVideoPoster(
5
5
  return new Promise((resolve, reject) => {
6
6
  const video = document.createElement("video");
7
7
  const url = URL.createObjectURL(blob);
8
- video.muted = true;
9
- video.preload = "auto";
10
- video.src = url;
8
+ let settled = false;
11
9
 
12
10
  const cleanup = () => {
11
+ clearTimeout(timeoutId);
13
12
  URL.revokeObjectURL(url);
14
13
  video.removeAttribute("src");
15
14
  video.load();
16
15
  };
17
16
 
17
+ const safeReject = (err: Error) => {
18
+ if (settled) return;
19
+ settled = true;
20
+ cleanup();
21
+ reject(err);
22
+ };
23
+
24
+ const timeoutId = setTimeout(() => {
25
+ safeReject(new Error("Video poster generation timed out"));
26
+ }, 10_000);
27
+
28
+ video.addEventListener("error", () => {
29
+ safeReject(new Error("Failed to load video for poster generation"));
30
+ }, { once: true });
31
+
32
+ video.muted = true;
33
+ video.preload = "auto";
34
+ video.src = url;
35
+
18
36
  video.addEventListener("loadeddata", () => {
19
37
  video.addEventListener("seeked", () => {
38
+ if (settled) return;
20
39
  try {
21
40
  const w = video.videoWidth;
22
41
  const h = video.videoHeight;
42
+
43
+ if (w === 0 || h === 0) {
44
+ safeReject(new Error("Video dimensions not available"));
45
+ return;
46
+ }
47
+
23
48
  const canvas = document.createElement("canvas");
24
49
  canvas.width = w;
25
50
  canvas.height = h;
@@ -27,6 +52,8 @@ export function generateVideoPoster(
27
52
  ctx.drawImage(video, 0, 0);
28
53
  canvas.toBlob(
29
54
  (posterBlob) => {
55
+ if (settled) return;
56
+ settled = true;
30
57
  cleanup();
31
58
  if (!posterBlob) {
32
59
  reject(new Error("Failed to create poster blob"));
@@ -38,16 +65,10 @@ export function generateVideoPoster(
38
65
  quality / 100,
39
66
  );
40
67
  } catch (err) {
41
- cleanup();
42
- reject(err);
68
+ safeReject(err instanceof Error ? err : new Error(String(err)));
43
69
  }
44
70
  }, { once: true });
45
71
  video.currentTime = isFinite(video.duration) ? Math.min(0.1, video.duration / 2) : 0;
46
72
  }, { once: true });
47
-
48
- video.addEventListener("error", () => {
49
- cleanup();
50
- reject(new Error("Failed to load video for poster generation"));
51
- }, { once: true });
52
73
  });
53
74
  }
@@ -20,8 +20,12 @@ export const IndexSchema = z.object({
20
20
  sections: z.record(z.string(), SectionMetaSchema),
21
21
  lastModified: z.string().nullable().optional(),
22
22
  }).refine(
23
- (data) => data.order.every((id) => id in data.sections),
24
- { message: "All order entries must have a corresponding section in sections" }
23
+ (data) => {
24
+ const orderSet = new Set(data.order);
25
+ return data.order.every((id) => id in data.sections) &&
26
+ Object.keys(data.sections).every((key) => orderSet.has(key));
27
+ },
28
+ { message: "Every id in order must have a sections entry and vice versa" }
25
29
  );
26
30
 
27
31
  export type SiteIndex = z.infer<typeof IndexSchema>;
@@ -33,6 +37,7 @@ export const SiteConfigSchema = z.object({
33
37
  darkMode: z.enum(["light", "dark", "optional"]).default("light"),
34
38
  headingFont: z.string().default("system-ui"),
35
39
  bodyFont: z.string().default("system-ui"),
40
+ uppercaseHeadings: z.boolean().default(true),
36
41
  googleFontsUrl: z.string()
37
42
  .refine(url => url.startsWith("https://fonts.googleapis.com/"), "Must be a Google Fonts URL")
38
43
  .nullable()