@hraness/slopcamera 3.2.7 → 3.3.1

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 (188) hide show
  1. package/NOTICE.md +1 -7
  2. package/PRIVACY.md +16 -7
  3. package/README.md +65 -20
  4. package/apps/desktop/README.md +14 -130
  5. package/apps/desktop/application/context.ts +6 -9
  6. package/apps/desktop/application/default-registry.ts +26 -10
  7. package/apps/desktop/application/operation.ts +14 -5
  8. package/apps/desktop/application/operations/index.ts +4 -1
  9. package/apps/desktop/application/operations/spatial-behavior.ts +151 -0
  10. package/apps/desktop/application/operations/spatial-direction.ts +222 -0
  11. package/apps/desktop/application/operations/spatial-rendered-audit.ts +174 -0
  12. package/apps/desktop/application/operations/spatial-review.ts +194 -0
  13. package/apps/desktop/application/operations/spatial-scene.ts +25 -1
  14. package/apps/desktop/application/registry.ts +2 -2
  15. package/apps/desktop/application/spatial-asset-admission.ts +201 -0
  16. package/apps/desktop/application/spatial-assets.ts +160 -31
  17. package/apps/desktop/application/spatial-render.ts +217 -20
  18. package/apps/desktop/application/spatial-rendered-audit.ts +273 -0
  19. package/apps/desktop/application/spatial-review.ts +361 -0
  20. package/apps/desktop/application/spatial-spz.ts +48 -3
  21. package/apps/desktop/application/spatial-world-import.ts +26 -5
  22. package/apps/desktop/application/spatial-world-metadata.ts +105 -0
  23. package/apps/desktop/cli/args.ts +528 -97
  24. package/apps/desktop/cli/capability-manifest.ts +374 -0
  25. package/apps/desktop/cli/cinema-renderer.ts +608 -0
  26. package/apps/desktop/cli/command-host-resources.ts +31 -2
  27. package/apps/desktop/cli/commands.ts +1215 -70
  28. package/apps/desktop/cli/gateway-review-provider.ts +334 -0
  29. package/apps/desktop/cli/help.ts +183 -23
  30. package/apps/desktop/cli/html-scene.ts +139 -28
  31. package/apps/desktop/cli/io.ts +2 -1
  32. package/apps/desktop/cli/main.ts +26 -50
  33. package/apps/desktop/cli/menubar.ts +244 -0
  34. package/apps/desktop/cli/music-analysis-service.ts +61 -0
  35. package/apps/desktop/cli/portable-surface.ts +18 -5
  36. package/apps/desktop/cli/spatial-asset-service.ts +124 -0
  37. package/apps/desktop/cli/spatial-design-service.ts +237 -0
  38. package/apps/desktop/cli/spatial-generate-service.ts +370 -0
  39. package/apps/desktop/cli/spatial-scene-service.ts +268 -4
  40. package/apps/desktop/cli/workflow-code.ts +1 -1
  41. package/apps/desktop/code/application-node-planner.ts +0 -47
  42. package/apps/desktop/code/host-source-layout.ts +46 -0
  43. package/apps/desktop/code/public.ts +0 -1
  44. package/apps/desktop/code/runtime-identity.ts +50 -8
  45. package/apps/desktop/code/semantic-builder.ts +24 -43
  46. package/apps/desktop/code/source-bundle.ts +23 -6
  47. package/apps/desktop/code/source-typecheck.ts +131 -43
  48. package/apps/desktop/code/worker-client.ts +14 -2
  49. package/apps/desktop/contracts/cinema.ts +736 -0
  50. package/apps/desktop/contracts/index.ts +1 -0
  51. package/apps/desktop/contracts/spatial-asset.ts +16 -0
  52. package/apps/desktop/contracts/spatial-world.ts +26 -3
  53. package/apps/desktop/core/cinema-plan.ts +1320 -0
  54. package/apps/desktop/core/index.ts +1 -0
  55. package/apps/desktop/core/music-analysis.ts +124 -1
  56. package/apps/desktop/core/storage.ts +75 -0
  57. package/apps/desktop/dist/cli/main.js +737 -291
  58. package/apps/desktop/html-overlay/audio-reactivity.ts +127 -0
  59. package/apps/desktop/html-overlay/index.ts +9 -0
  60. package/apps/desktop/html-overlay/runtime.ts +2 -0
  61. package/apps/desktop/html-overlay/scene.ts +29 -0
  62. package/apps/desktop/html-overlay/spatial.ts +619 -57
  63. package/apps/desktop/workflows/cinematic-world.ts +127 -0
  64. package/apps/desktop/workflows/index.ts +6 -0
  65. package/dist/cli.js +32 -434
  66. package/dist/code/advanced.js +1 -269
  67. package/dist/code/index.js +16 -2239
  68. package/dist/generate.js +1 -23
  69. package/dist/host-resources.js +1 -25
  70. package/dist/index-0bbesn4m.js +35 -0
  71. package/dist/index-296qhpf2.js +3 -0
  72. package/dist/index-3cxf2c58.js +3 -0
  73. package/dist/index-77fjfg9f.js +3 -0
  74. package/dist/index-80a36bc0.js +3 -0
  75. package/dist/index-a3vvs3rf.js +3 -0
  76. package/dist/index-b3geqczn.js +5 -0
  77. package/dist/index-cgpkyjqa.js +3 -0
  78. package/dist/index-f0ptv8dm.js +7 -0
  79. package/dist/index-grk47tgs.js +3 -0
  80. package/dist/index-qry58nj2.js +10 -0
  81. package/dist/index.js +1 -240
  82. package/dist/operations.js +1 -28
  83. package/dist/skill-install-gx7afxsd.js +2 -0
  84. package/dist/vectorize/worker.js +1 -141
  85. package/dist/workflow.js +1 -15
  86. package/docs/README.md +5 -1
  87. package/docs/studio.md +1 -1
  88. package/package.json +11 -24
  89. package/skills/slopcamera/SKILL.md +14 -1
  90. package/skills/slopcamera/references/diagrams.md +5 -14
  91. package/skills/slopcamera/references/directed-scenes.md +22 -2
  92. package/skills/slopcamera/references/gateway-media.md +15 -1
  93. package/skills/slopcamera/references/image-galleries.md +166 -0
  94. package/skills/slopcamera/references/install.md +2 -2
  95. package/skills/slopcamera/references/music-video.md +23 -3
  96. package/skills/slopcamera/references/parametric-design.md +14 -0
  97. package/skills/slopcamera/references/reference-led-3d.md +1 -1
  98. package/skills/slopcamera/references/scene-building.md +32 -0
  99. package/skills/slopcamera/references/social-collage-banners.md +204 -0
  100. package/skills/slopcamera/references/support.md +25 -0
  101. package/skills/slopcamera/references/web-media-excerpts.md +120 -0
  102. package/skills/slopcamera/references/workflows-sdk.md +3 -3
  103. package/skills/slopcamera/scripts/compose-social-collage-banner.ts +728 -0
  104. package/src/capability-manifest.ts +361 -0
  105. package/src/cli.ts +250 -102
  106. package/src/code/canonical-json.ts +9 -0
  107. package/src/generate.ts +2 -2
  108. package/src/icon.ts +1111 -0
  109. package/src/image-gallery.ts +1049 -0
  110. package/src/index.ts +11 -23
  111. package/src/mcp/boundary.ts +11 -11
  112. package/src/mcp/index.ts +3 -0
  113. package/src/mcp/server.ts +2 -1
  114. package/src/mcp/tools.ts +1642 -130
  115. package/src/mcp/types.ts +19 -0
  116. package/src/operations.ts +412 -0
  117. package/src/portable-capability-manifest.ts +67 -0
  118. package/src/scene-gallery.ts +184 -0
  119. package/src/spatial-scene/asset-admission.ts +183 -0
  120. package/src/spatial-scene/audit-rendered.ts +692 -0
  121. package/src/spatial-scene/audit.ts +617 -0
  122. package/src/spatial-scene/behavior-audit.ts +252 -0
  123. package/src/spatial-scene/behavior-bake.ts +233 -0
  124. package/src/spatial-scene/behavior-fns.ts +585 -0
  125. package/src/spatial-scene/behavior-gallery.ts +109 -0
  126. package/src/spatial-scene/behavior-stdlib.ts +323 -0
  127. package/src/spatial-scene/behavior-trace.ts +325 -0
  128. package/src/spatial-scene/behavior.ts +618 -0
  129. package/src/spatial-scene/build.ts +632 -0
  130. package/src/spatial-scene/camera-rig.ts +125 -0
  131. package/src/spatial-scene/camera-track.ts +4 -2
  132. package/src/spatial-scene/character.ts +124 -0
  133. package/src/spatial-scene/contracts.ts +75 -7
  134. package/src/spatial-scene/design-templates.ts +351 -0
  135. package/src/spatial-scene/design.ts +327 -0
  136. package/src/spatial-scene/direction-compile.ts +584 -0
  137. package/src/spatial-scene/direction.ts +122 -0
  138. package/src/spatial-scene/effects.ts +149 -0
  139. package/src/spatial-scene/evaluate.ts +109 -29
  140. package/src/spatial-scene/gallery.ts +277 -0
  141. package/src/spatial-scene/generate.ts +239 -0
  142. package/src/spatial-scene/geometry-native.ts +165 -0
  143. package/src/spatial-scene/geometry.ts +1216 -0
  144. package/src/spatial-scene/gltf.ts +600 -77
  145. package/src/spatial-scene/identity.ts +55 -2
  146. package/src/spatial-scene/index.ts +36 -0
  147. package/src/spatial-scene/inspect.ts +35 -6
  148. package/src/spatial-scene/material-lighting.ts +609 -0
  149. package/src/spatial-scene/math.ts +21 -4
  150. package/src/spatial-scene/motion-evidence.ts +88 -0
  151. package/src/spatial-scene/parametric.ts +765 -0
  152. package/src/spatial-scene/particle-preparation.ts +183 -0
  153. package/src/spatial-scene/particle.ts +195 -0
  154. package/src/spatial-scene/patch.ts +109 -15
  155. package/src/spatial-scene/performance.ts +1613 -0
  156. package/src/spatial-scene/probe.ts +137 -0
  157. package/src/spatial-scene/recipe-pack.ts +86 -0
  158. package/src/spatial-scene/render-effects.ts +240 -0
  159. package/src/spatial-scene/review.ts +639 -0
  160. package/src/spatial-scene/simulation-bake.ts +206 -0
  161. package/src/spatial-scene/simulation.ts +187 -0
  162. package/src/spatial-scene/solve.ts +366 -0
  163. package/src/spatial-scene/temporal-audit.ts +354 -0
  164. package/src/support-completion.ts +16 -0
  165. package/src/support.ts +42 -0
  166. package/src/version.ts +1 -1
  167. package/apps/desktop/application/operations/recording/index.ts +0 -5
  168. package/apps/desktop/application/operations/recording/pause.ts +0 -34
  169. package/apps/desktop/application/operations/recording/resume.ts +0 -34
  170. package/apps/desktop/application/operations/recording/shared.ts +0 -199
  171. package/apps/desktop/application/operations/recording/start.ts +0 -169
  172. package/apps/desktop/application/operations/recording/stop.ts +0 -34
  173. package/apps/desktop/capture/protocol.ts +0 -689
  174. package/apps/desktop/cli/capture-bundle.ts +0 -1117
  175. package/apps/desktop/cli/recording-controller.ts +0 -1567
  176. package/apps/desktop/cli/recording-daemon.ts +0 -931
  177. package/dist/index-42zsesc1.js +0 -2257
  178. package/dist/index-7308egqr.js +0 -48
  179. package/dist/index-8txs6fkn.js +0 -712
  180. package/dist/index-mcy8z0br.js +0 -192
  181. package/dist/index-p63wavx0.js +0 -2029
  182. package/dist/index-r7gdhmsp.js +0 -424
  183. package/dist/index-rvghzt7b.js +0 -2013
  184. package/dist/index-sh6xbav6.js +0 -1219
  185. package/dist/index-z1w83f81.js +0 -4
  186. package/dist/index-z7239b4h.js +0 -2363
  187. package/dist/skill-install-dh0ntqba.js +0 -10
  188. package/src/desktop.ts +0 -253
@@ -0,0 +1,127 @@
1
+ import { z } from "zod";
2
+
3
+ import type { AudioReactivityBands } from "../core/music-analysis";
4
+
5
+ export const HTML_AUDIO_REACTIVITY_RESOURCE_NAME = "audio-reactivity";
6
+ export const HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH = "slopcamera/audio-reactivity.json";
7
+ export const HTML_AUDIO_REACTIVITY_MAX_BYTES = 4 * 1024 * 1024;
8
+
9
+ export type HtmlOverlayAudioReactivity = Readonly<{
10
+ kind: "slopcamera.audio-reactivity";
11
+ schemaVersion: 1;
12
+ profile: "bands-v1";
13
+ source: Readonly<{ bytes: number; sha256: string }>;
14
+ pcm: Readonly<{ sampleCount: number; sha256: string }>;
15
+ durationUs: number;
16
+ rateHz: 60;
17
+ bands: AudioReactivityBands;
18
+ }>;
19
+
20
+ export type HtmlOverlayAudioSample = Readonly<{
21
+ bass: number; mid: number; treble: number; energy: number;
22
+ }>;
23
+ export type PreparedHtmlOverlayAudioReactivity = Readonly<{
24
+ durationUs: number;
25
+ sample: (timeUs: number) => HtmlOverlayAudioSample;
26
+ }>;
27
+
28
+ const LIMITS = Object.freeze({ maxSamples: 14_400_000, maxSourceBytes: 512 * 1024 * 1024,
29
+ sampleRateHz: 24_000, hopSize: 400, rateHz: 60, maxTimeUs: 3_600_000_000 });
30
+
31
+ /** One implementation validates the SDK and browser resource, before frame sampling. */
32
+ function createAudioReactivityRuntime(limits: typeof LIMITS) {
33
+ const object = (value: unknown, keys: readonly string[]): Record<string, unknown> => {
34
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
35
+ throw new TypeError("Audio reactivity requires a strict JSON object.");
36
+ }
37
+ const descriptors = Object.getOwnPropertyDescriptors(value);
38
+ if (Reflect.ownKeys(value).length !== keys.length
39
+ || keys.some(key => descriptors[key] === undefined || !("value" in descriptors[key]!))) {
40
+ throw new TypeError("Audio reactivity contains missing, unknown, or accessor fields.");
41
+ }
42
+ return value as Record<string, unknown>;
43
+ };
44
+ const integer = (value: unknown, minimum: number, maximum: number): number => {
45
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum) {
46
+ throw new RangeError("Audio reactivity integer is outside its bound.");
47
+ }
48
+ return value;
49
+ };
50
+ const digest = (value: unknown): string => {
51
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
52
+ throw new TypeError("Audio reactivity requires a SHA-256 digest.");
53
+ }
54
+ return value;
55
+ };
56
+ const parse = (value: unknown): HtmlOverlayAudioReactivity => {
57
+ const input = object(value, ["kind", "schemaVersion", "profile", "source", "pcm", "durationUs", "rateHz", "bands"]);
58
+ if (input.kind !== "slopcamera.audio-reactivity" || input.schemaVersion !== 1
59
+ || input.profile !== "bands-v1" || input.rateHz !== limits.rateHz) {
60
+ throw new TypeError("Unsupported audio reactivity profile or version.");
61
+ }
62
+ const source = object(input.source, ["bytes", "sha256"]);
63
+ const pcm = object(input.pcm, ["sampleCount", "sha256"]);
64
+ const sampleCount = integer(pcm.sampleCount, 1, limits.maxSamples);
65
+ const durationUs = Math.round(sampleCount * 1_000_000 / limits.sampleRateHz);
66
+ if (input.durationUs !== durationUs) throw new RangeError("Audio reactivity duration differs from its PCM sample count.");
67
+ const length = Math.ceil(sampleCount / limits.hopSize) + 1;
68
+ const bands = object(input.bands, ["bass", "mid", "treble", "energy"]);
69
+ const channel = (value: unknown): readonly number[] => {
70
+ const keys = value !== null && typeof value === "object" ? Reflect.ownKeys(value) : [];
71
+ if (!Array.isArray(value) || value.length !== length || keys.length !== length + 1
72
+ || keys.some(key => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key)))) {
73
+ throw new RangeError("Audio reactivity bands must be dense arrays covering the declared duration.");
74
+ }
75
+ const copy: number[] = [];
76
+ for (let index = 0; index < length; index++) {
77
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
78
+ const sample: unknown = descriptor !== undefined && "value" in descriptor ? descriptor.value : undefined;
79
+ if (typeof sample !== "number" || !Number.isFinite(sample) || sample < 0 || sample > 1) {
80
+ throw new RangeError("Audio reactivity band values must be finite numbers in [0, 1].");
81
+ }
82
+ copy.push(sample === 0 ? 0 : sample);
83
+ }
84
+ if (copy.at(-1) !== 0) throw new RangeError("Audio reactivity requires a zero endpoint at the exact duration.");
85
+ return Object.freeze(copy);
86
+ };
87
+ return Object.freeze({
88
+ kind: "slopcamera.audio-reactivity", schemaVersion: 1, profile: "bands-v1", rateHz: 60, durationUs,
89
+ source: Object.freeze({ bytes: integer(source.bytes, 1, limits.maxSourceBytes), sha256: digest(source.sha256) }),
90
+ pcm: Object.freeze({ sampleCount, sha256: digest(pcm.sha256) }),
91
+ bands: Object.freeze({ bass: channel(bands.bass), mid: channel(bands.mid), treble: channel(bands.treble), energy: channel(bands.energy) }),
92
+ });
93
+ };
94
+ const prepareAudioReactivity = (input: unknown): PreparedHtmlOverlayAudioReactivity => {
95
+ const data = parse(input);
96
+ const zero = Object.freeze({ bass: 0, mid: 0, treble: 0, energy: 0 });
97
+ return Object.freeze({ durationUs: data.durationUs, sample(timeUs: number): HtmlOverlayAudioSample {
98
+ integer(timeUs, -limits.maxTimeUs, limits.maxTimeUs);
99
+ if (timeUs < 0 || timeUs >= data.durationUs) return zero;
100
+ const index = Math.min(data.bands.bass.length - 2, Math.floor(timeUs * limits.rateHz / 1_000_000));
101
+ const startUs = index * 1_000_000 / limits.rateHz;
102
+ const endUs = Math.min(data.durationUs, (index + 1) * 1_000_000 / limits.rateHz);
103
+ const mix = Math.max(0, Math.min(1, (timeUs - startUs) / (endUs - startUs)));
104
+ const interpolate = (band: readonly number[]) => band[index]! * (1 - mix) + band[index + 1]! * mix;
105
+ return Object.freeze({ bass: interpolate(data.bands.bass), mid: interpolate(data.bands.mid),
106
+ treble: interpolate(data.bands.treble), energy: interpolate(data.bands.energy) });
107
+ } });
108
+ };
109
+ return { parse, prepareAudioReactivity };
110
+ }
111
+
112
+ const runtime = createAudioReactivityRuntime(LIMITS);
113
+ export const HtmlOverlayAudioReactivitySchema = z.unknown().transform((input, context): HtmlOverlayAudioReactivity => {
114
+ try { return runtime.parse(input); } catch (error) {
115
+ context.addIssue({ code: "custom", message: error instanceof Error ? error.message : "Invalid audio reactivity." });
116
+ return z.NEVER;
117
+ }
118
+ });
119
+
120
+ /** Validate and copy once. Samples depend only on absolute source time, in integer microseconds. */
121
+ export function prepareHtmlOverlayAudioReactivity(input: unknown): PreparedHtmlOverlayAudioReactivity {
122
+ return runtime.prepareAudioReactivity(input);
123
+ }
124
+
125
+ export function createHtmlOverlayAudioReactivityRuntimeSource(): string {
126
+ return `(() => { const runtime = (${createAudioReactivityRuntime.toString()})(${JSON.stringify(LIMITS)}); return { prepareAudioReactivity: runtime.prepareAudioReactivity }; })()`;
127
+ }
@@ -33,6 +33,15 @@ export {
33
33
  type HtmlOverlayTiming,
34
34
  } from "./contracts";
35
35
  export { HtmlSceneInputSchema, type HtmlSceneInput } from "./scene";
36
+ export {
37
+ HTML_AUDIO_REACTIVITY_RESOURCE_NAME,
38
+ HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH,
39
+ HtmlOverlayAudioReactivitySchema,
40
+ prepareHtmlOverlayAudioReactivity,
41
+ type HtmlOverlayAudioReactivity,
42
+ type HtmlOverlayAudioSample,
43
+ type PreparedHtmlOverlayAudioReactivity,
44
+ } from "./audio-reactivity";
36
45
  export {
37
46
  HTML_OVERLAY_SCAFFOLD_CLOCK_INTEGRATIONS,
38
47
  HTML_OVERLAY_SCAFFOLD_KINDS,
@@ -11,6 +11,7 @@ import {
11
11
  import { htmlOverlayAssetLocalUrl } from "./libraries";
12
12
  import { HTML_OVERLAY_RANDOM_ALGORITHM } from "./random";
13
13
  import { createHtmlOverlayMusicClockRuntimeSource } from "./music-clock";
14
+ import { createHtmlOverlayAudioReactivityRuntimeSource } from "./audio-reactivity";
14
15
 
15
16
  const Uint32Schema = z.number().int().safe().min(0).max(0xffff_ffff);
16
17
 
@@ -713,6 +714,7 @@ export function createHtmlOverlayBrowserRuntimeSource(
713
714
  };
714
715
  },
715
716
  ...${createHtmlOverlayMusicClockRuntimeSource()},
717
+ ...${createHtmlOverlayAudioReactivityRuntimeSource()},
716
718
  parameters: deepFreeze(config.parameters),
717
719
  random,
718
720
  randomFor,
@@ -6,6 +6,8 @@ import {
6
6
  } from "./contracts";
7
7
  import { HtmlOverlayExecutionProfileSchema } from "./execution-profile";
8
8
  import { HtmlOverlayLibrarySelectionSchema } from "./libraries";
9
+ import { AUDIO_REACTIVITY_MAX_DURATION_US } from "../core/music-analysis";
10
+ import { HTML_AUDIO_REACTIVITY_RESOURCE_NAME, HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH } from "./audio-reactivity";
9
11
 
10
12
  const ResourceSchema = HtmlOverlayDeclaredResourceSchema.omit({ bytes: true, sha256: true }).extend({
11
13
  path: RepositoryRelativePathSchema,
@@ -27,8 +29,35 @@ export const HtmlSceneInputSchema = z.strictObject({
27
29
  executionProfile: HtmlOverlayExecutionProfileSchema.optional(),
28
30
  audio: z.strictObject({
29
31
  path: z.string().min(1).max(4096).refine(path => !/[\0\r\n]/u.test(path), "Audio path contains a control character."),
32
+ reactivity: z.strictObject({
33
+ profile: z.literal("bands-v1"),
34
+ resource: z.literal(HTML_AUDIO_REACTIVITY_RESOURCE_NAME).optional(),
35
+ sha256: z.string().regex(/^[a-f0-9]{64}$/u).optional(),
36
+ }).optional(),
30
37
  }).optional(),
31
38
  }).superRefine((input, context) => {
39
+ if (input.audio?.reactivity !== undefined && input.timing.durationUs > AUDIO_REACTIVITY_MAX_DURATION_US) {
40
+ context.addIssue({ code: "custom", message: "Audio reactivity is limited to ten minutes per scene." });
41
+ }
42
+ if (input.audio?.reactivity?.resource === undefined && input.audio?.reactivity !== undefined && (
43
+ input.resources.length >= 64
44
+ || input.resources.some(resource => resource.name === HTML_AUDIO_REACTIVITY_RESOURCE_NAME
45
+ || resource.urlPath === HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH)
46
+ )) {
47
+ context.addIssue({ code: "custom", message: "Audio reactivity requires one free resource slot and an unused audio-reactivity name and slopcamera/audio-reactivity.json URL path." });
48
+ }
49
+ if (input.audio?.reactivity?.resource === undefined && input.audio?.reactivity?.sha256 !== undefined) {
50
+ context.addIssue({ code: "custom", message: "An audio-reactivity sidecar digest requires its retained resource name." });
51
+ }
52
+ if (input.audio?.reactivity?.resource !== undefined && !input.resources.some(resource => (
53
+ resource.name === HTML_AUDIO_REACTIVITY_RESOURCE_NAME && resource.urlPath === HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH
54
+ && resource.mediaType === "application/json" && resource.transport === "fetch"
55
+ ))) {
56
+ context.addIssue({ code: "custom", message: "A retained audio-reactivity source must declare its fixed fetched JSON resource." });
57
+ }
58
+ if (input.audio?.reactivity?.resource !== undefined && input.audio.reactivity.sha256 === undefined) {
59
+ context.addIssue({ code: "custom", message: "A retained audio-reactivity source must record its sidecar SHA-256 digest." });
60
+ }
32
61
  const authoring = HtmlOverlayAuthoringInputSchema.safeParse({
33
62
  kind: "slopcamera.html-overlay", schemaVersion: 1, canvas: input.canvas, timing: input.timing,
34
63
  libraries: input.libraries, parameters: input.parameters, seed: input.seed,