react-native-webrtc-kaleidoscope 2.1.1 → 2.2.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 (166) hide show
  1. package/README.md +8 -0
  2. package/dist/catalog/composites/clouds/clouds.controls.d.ts.map +1 -1
  3. package/dist/catalog/composites/clouds/clouds.d.ts.map +1 -1
  4. package/dist/catalog/composites/corporate-blobs/corporate-blobs.controls.d.ts.map +1 -1
  5. package/dist/catalog/composites/corporate-blobs/corporate-blobs.d.ts.map +1 -1
  6. package/dist/catalog/composites/corporate-blobs/corporate-blobs.web.d.ts.map +1 -1
  7. package/dist/catalog/composites/fairy-cave/fairy-cave.controls.d.ts.map +1 -1
  8. package/dist/catalog/composites/fairy-cave/fairy-cave.d.ts.map +1 -1
  9. package/dist/catalog/composites/fairy-cave/fairy-cave.web.d.ts.map +1 -1
  10. package/dist/catalog/composites/fairy-grotto/fairy-grotto.controls.d.ts.map +1 -1
  11. package/dist/catalog/composites/fairy-grotto/fairy-grotto.d.ts.map +1 -1
  12. package/dist/catalog/composites/fairy-grotto/fairy-grotto.web.d.ts.map +1 -1
  13. package/dist/catalog/composites/fairy-hollow/fairy-hollow.controls.d.ts.map +1 -1
  14. package/dist/catalog/composites/fairy-hollow/fairy-hollow.d.ts.map +1 -1
  15. package/dist/catalog/composites/fairy-hollow/fairy-hollow.web.d.ts.map +1 -1
  16. package/dist/catalog/composites/nebula/nebula.controls.d.ts.map +1 -1
  17. package/dist/catalog/composites/nebula/nebula.d.ts.map +1 -1
  18. package/dist/catalog/composites/nebula/nebula.web.d.ts.map +1 -1
  19. package/dist/catalog/composites/observation-deck/observation-deck.controls.d.ts.map +1 -1
  20. package/dist/catalog/composites/observation-deck/observation-deck.d.ts.map +1 -1
  21. package/dist/catalog/composites/observation-deck/observation-deck.web.d.ts.map +1 -1
  22. package/dist/catalog/composites/simianlights/simianlights.controls.d.ts.map +1 -1
  23. package/dist/catalog/composites/simianlights/simianlights.d.ts.map +1 -1
  24. package/dist/catalog/composites/simianlights/simianlights.web.d.ts.map +1 -1
  25. package/dist/catalog/composites/underwater/underwater.controls.d.ts.map +1 -1
  26. package/dist/catalog/composites/underwater/underwater.d.ts.map +1 -1
  27. package/dist/catalog/composites/underwater/underwater.web.d.ts.map +1 -1
  28. package/dist/catalog/composites/wizard-tower/wizard-tower.controls.d.ts.map +1 -1
  29. package/dist/catalog/composites/wizard-tower/wizard-tower.d.ts.map +1 -1
  30. package/dist/catalog/composites/wizard-tower/wizard-tower.web.d.ts.map +1 -1
  31. package/dist/catalog/composites/wizard-tower-night/wizard-tower-night.controls.d.ts.map +1 -1
  32. package/dist/catalog/composites/wizard-tower-night/wizard-tower-night.d.ts.map +1 -1
  33. package/dist/catalog/composites/wizard-tower-night/wizard-tower-night.web.d.ts.map +1 -1
  34. package/dist/catalog/images/corporate/corporate-logo.d.ts.map +1 -1
  35. package/dist/catalog/images/corporate/corporate-logo.web.d.ts.map +1 -1
  36. package/dist/catalog/images/debug/debug-resolutions.d.ts.map +1 -1
  37. package/dist/catalog/images/debug/debug-resolutions.web.d.ts.map +1 -1
  38. package/dist/catalog/images/fairy-caves/grotto.d.ts.map +1 -1
  39. package/dist/catalog/images/fairy-caves/grotto.web.d.ts.map +1 -1
  40. package/dist/catalog/images/fairy-caves/hollow.d.ts.map +1 -1
  41. package/dist/catalog/images/fairy-caves/hollow.web.d.ts.map +1 -1
  42. package/dist/catalog/images/fairy-caves/treehouse-2.d.ts.map +1 -1
  43. package/dist/catalog/images/fairy-caves/treehouse-2.web.d.ts.map +1 -1
  44. package/dist/catalog/images/fairy-caves/treehouse-3.d.ts.map +1 -1
  45. package/dist/catalog/images/fairy-caves/treehouse-3.web.d.ts.map +1 -1
  46. package/dist/catalog/images/fairy-caves/treehouse.d.ts.map +1 -1
  47. package/dist/catalog/images/fairy-caves/treehouse.web.d.ts.map +1 -1
  48. package/dist/catalog/images/home/home-dark.d.ts.map +1 -1
  49. package/dist/catalog/images/home/home-dark.web.d.ts.map +1 -1
  50. package/dist/catalog/images/home/home-light.d.ts.map +1 -1
  51. package/dist/catalog/images/home/home-light.web.d.ts.map +1 -1
  52. package/dist/catalog/images/image-ids.d.ts.map +1 -1
  53. package/dist/catalog/images/image.types.d.ts.map +1 -1
  54. package/dist/catalog/images/index.d.ts.map +1 -1
  55. package/dist/catalog/images/nature/landscape-dark.d.ts.map +1 -1
  56. package/dist/catalog/images/nature/landscape-dark.web.d.ts.map +1 -1
  57. package/dist/catalog/images/nature/landscape-light.d.ts.map +1 -1
  58. package/dist/catalog/images/nature/landscape-light.web.d.ts.map +1 -1
  59. package/dist/catalog/images/office/office-dark.d.ts.map +1 -1
  60. package/dist/catalog/images/office/office-dark.web.d.ts.map +1 -1
  61. package/dist/catalog/images/office/office-light.d.ts.map +1 -1
  62. package/dist/catalog/images/office/office-light.web.d.ts.map +1 -1
  63. package/dist/catalog/images/sci-fi/sci-fi-light.d.ts.map +1 -1
  64. package/dist/catalog/images/sci-fi/sci-fi-light.web.d.ts.map +1 -1
  65. package/dist/catalog/images/simiancraft/simiancraft-dark-transparency.d.ts.map +1 -1
  66. package/dist/catalog/images/simiancraft/simiancraft-dark-transparency.web.d.ts.map +1 -1
  67. package/dist/catalog/images/simiancraft/simiancraft-dark.d.ts.map +1 -1
  68. package/dist/catalog/images/simiancraft/simiancraft-dark.web.d.ts.map +1 -1
  69. package/dist/catalog/images/simiancraft/simiancraft-light-transparency.d.ts.map +1 -1
  70. package/dist/catalog/images/simiancraft/simiancraft-light-transparency.web.d.ts.map +1 -1
  71. package/dist/catalog/images/simiancraft/simiancraft-light.d.ts.map +1 -1
  72. package/dist/catalog/images/simiancraft/simiancraft-light.web.d.ts.map +1 -1
  73. package/dist/catalog/images/spaceship/observation-deck.d.ts.map +1 -1
  74. package/dist/catalog/images/spaceship/observation-deck.web.d.ts.map +1 -1
  75. package/dist/catalog/images/underwater/oceanscape-dark.d.ts.map +1 -1
  76. package/dist/catalog/images/underwater/oceanscape-dark.web.d.ts.map +1 -1
  77. package/dist/catalog/images/wizard-tower/wizard-tower-1.d.ts.map +1 -1
  78. package/dist/catalog/images/wizard-tower/wizard-tower-1.web.d.ts.map +1 -1
  79. package/dist/catalog/images/wizard-tower/wizard-tower-2.d.ts.map +1 -1
  80. package/dist/catalog/images/wizard-tower/wizard-tower-2.web.d.ts.map +1 -1
  81. package/dist/catalog/images/wizard-tower/wizard-tower-night.d.ts.map +1 -1
  82. package/dist/catalog/images/wizard-tower/wizard-tower-night.web.d.ts.map +1 -1
  83. package/dist/catalog/shaders/_shared/types.d.ts.map +1 -1
  84. package/dist/catalog/shaders/anamorphic-lensflare/anamorphic-lensflare.d.ts.map +1 -1
  85. package/dist/catalog/shaders/anamorphic-lensflare/anamorphic-lensflare.form.d.ts.map +1 -1
  86. package/dist/catalog/shaders/blur/blur.d.ts.map +1 -1
  87. package/dist/catalog/shaders/blur/blur.form.d.ts.map +1 -1
  88. package/dist/catalog/shaders/clouds/clouds.d.ts.map +1 -1
  89. package/dist/catalog/shaders/clouds/clouds.form.d.ts.map +1 -1
  90. package/dist/catalog/shaders/corporate-blobs/corporate-blobs.d.ts.map +1 -1
  91. package/dist/catalog/shaders/corporate-blobs/corporate-blobs.form.d.ts.map +1 -1
  92. package/dist/catalog/shaders/fireflies/fireflies.d.ts.map +1 -1
  93. package/dist/catalog/shaders/fireflies/fireflies.form.d.ts.map +1 -1
  94. package/dist/catalog/shaders/godrays/godrays.d.ts.map +1 -1
  95. package/dist/catalog/shaders/godrays/godrays.form.d.ts.map +1 -1
  96. package/dist/catalog/shaders/index.d.ts.map +1 -1
  97. package/dist/catalog/shaders/light-beams-and-motes/light-beams-and-motes.d.ts.map +1 -1
  98. package/dist/catalog/shaders/light-beams-and-motes/light-beams-and-motes.form.d.ts.map +1 -1
  99. package/dist/catalog/shaders/nebula/nebula.d.ts.map +1 -1
  100. package/dist/catalog/shaders/nebula/nebula.form.d.ts.map +1 -1
  101. package/dist/catalog/shaders/plasma/plasma.d.ts.map +1 -1
  102. package/dist/catalog/shaders/plasma/plasma.form.d.ts.map +1 -1
  103. package/dist/catalog/shaders/simianlights/simianlights.d.ts.map +1 -1
  104. package/dist/catalog/shaders/simianlights/simianlights.form.d.ts.map +1 -1
  105. package/dist/src/components/form/control-form.d.ts.map +1 -1
  106. package/dist/src/components/form/make-controls.d.ts.map +1 -1
  107. package/dist/src/components/form/scope.d.ts.map +1 -1
  108. package/dist/src/components/form/use-field.d.ts.map +1 -1
  109. package/dist/src/components/preset-book-menu/index.d.ts.map +1 -1
  110. package/dist/src/components/preset-book-menu/layout.d.ts.map +1 -1
  111. package/dist/src/components/preset-book-menu/preset-book-menu.types.d.ts.map +1 -1
  112. package/dist/src/components/preset-book-menu/preset-grid.d.ts.map +1 -1
  113. package/dist/src/components/preset-book-menu/resolve-image-uri.d.ts.map +1 -1
  114. package/dist/src/components/preset-book-menu/resolve-image-uri.types.d.ts.map +1 -1
  115. package/dist/src/components/preset-book-menu/resolve-image-uri.web.d.ts.map +1 -1
  116. package/dist/src/components/preset-control-panel/composite-layer-control-panel.d.ts.map +1 -1
  117. package/dist/src/components/preset-control-panel/control-section.d.ts.map +1 -1
  118. package/dist/src/components/preset-control-panel/control.d.ts.map +1 -1
  119. package/dist/src/components/preset-control-panel/index.d.ts.map +1 -1
  120. package/dist/src/components/preset-control-panel/mask-control-panel.d.ts.map +1 -1
  121. package/dist/src/components/preset-control-panel/preset-control-panel.d.ts.map +1 -1
  122. package/dist/src/components/preset-control-panel/transform-control-panel.d.ts.map +1 -1
  123. package/dist/src/components/preset-tile/index.d.ts.map +1 -1
  124. package/dist/src/components/theme/provider.d.ts.map +1 -1
  125. package/dist/src/components/theme/slots.d.ts.map +1 -1
  126. package/dist/src/components/ui/button.d.ts.map +1 -1
  127. package/dist/src/components/ui/color-picker.d.ts.map +1 -1
  128. package/dist/src/components/ui/index.d.ts.map +1 -1
  129. package/dist/src/components/ui/label.d.ts.map +1 -1
  130. package/dist/src/components/ui/point.d.ts.map +1 -1
  131. package/dist/src/components/ui/polygon-field.d.ts.map +1 -1
  132. package/dist/src/components/ui/readout.d.ts.map +1 -1
  133. package/dist/src/components/ui/slider-value.d.ts.map +1 -1
  134. package/dist/src/components/ui/slider.d.ts.map +1 -1
  135. package/dist/src/components/ui/switch.d.ts.map +1 -1
  136. package/dist/src/index.d.ts.map +1 -1
  137. package/dist/src/index.web.d.ts.map +1 -1
  138. package/dist/src/kaleidoscope/controls.d.ts.map +1 -1
  139. package/dist/src/kaleidoscope/effect.d.ts.map +1 -1
  140. package/dist/src/kaleidoscope/effect.types.d.ts.map +1 -1
  141. package/dist/src/kaleidoscope/shader-to-spec.d.ts.map +1 -1
  142. package/dist/src/kaleidoscope/types.d.ts.map +1 -1
  143. package/dist/src/kaleidoscope.preset-book.types.d.ts.map +1 -1
  144. package/dist/src/lib/primitives.types.d.ts.map +1 -1
  145. package/dist/src/lib/test-id.d.ts.map +1 -1
  146. package/dist/src/livekit.d.ts +2 -0
  147. package/dist/src/livekit.d.ts.map +1 -1
  148. package/dist/src/livekit.js +7 -1
  149. package/dist/src/livekit.js.map +1 -1
  150. package/dist/src/nativewind.d.ts.map +1 -1
  151. package/dist/web-driver/effects/composite.d.ts.map +1 -1
  152. package/dist/web-driver/effects/layer-shaders.d.ts.map +1 -1
  153. package/dist/web-driver/effects/transform.d.ts.map +1 -1
  154. package/dist/web-driver/index.d.ts.map +1 -1
  155. package/dist/web-driver/insertable-streams.d.ts.map +1 -1
  156. package/dist/web-driver/segmenter.d.ts.map +1 -1
  157. package/dist/web-driver/shaders.d.ts.map +1 -1
  158. package/dist/web-driver/shaders.generated.d.ts.map +1 -1
  159. package/dist/web-driver/tuning.d.ts +14 -0
  160. package/dist/web-driver/tuning.d.ts.map +1 -1
  161. package/dist/web-driver/tuning.js +24 -6
  162. package/dist/web-driver/tuning.js.map +1 -1
  163. package/llms.txt +576 -0
  164. package/package.json +8 -7
  165. package/plugin/build/lib/preset-book.js +48 -15
  166. package/src/livekit.ts +7 -0
@@ -1 +1 @@
1
- {"version":3,"file":"composite-layer-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/composite-layer-control-panel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;CAChE,CAAC;AAkBF,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,SAAS,GACV,EAAE,+BAA+B,2CAQjC","sourcesContent":["// CompositeLayerControlPanel: render a shader's `*_CONTROLS` descriptor list as\n// fields, in order, data-driven. This is the AUTO-FORM (the default when a shader\n// ships no custom layout form): `<CompositeLayerControlPanel controls={CLOUDS_CONTROLS} />`.\n// It maps every descriptor through the shared `dispatchControl`, so it handles\n// every kind (color, float, switch, polygon) the same way `<Control>` does.\n//\n// KaleidoscopePreset-level customization is props: pass a filtered `controls`\n// array to hide knobs, or `overrides` to narrow a control's range/label for this\n// composite. For real layout (grouping, split, per-beam lists), a shader exports\n// a custom form of `<Control uniform>`s instead of using this panel.\n\nimport { Fragment } from 'react';\nimport type { UniformControl } from '../../../catalog/shaders';\nimport { dispatchControl } from './control';\n\n/** Per-uniform range/label override, keyed by uniform name. */\nexport type ControlOverride = {\n readonly min?: number;\n readonly max?: number;\n readonly step?: number;\n readonly label?: string;\n};\n\nexport type CompositeLayerControlPanelProps = {\n readonly controls: readonly UniformControl[];\n readonly overrides?: Readonly<Record<string, ControlOverride>>;\n};\n\n// Merge a preset override onto a descriptor. min/max/step only apply to float;\n// label applies to any kind.\nfunction withOverride(c: UniformControl, o?: ControlOverride): UniformControl {\n if (!o) return c;\n if (c.kind === 'float') {\n return {\n ...c,\n min: o.min ?? c.min,\n max: o.max ?? c.max,\n step: o.step ?? c.step,\n label: o.label ?? c.label,\n };\n }\n return o.label ? { ...c, label: o.label } : c;\n}\n\nexport function CompositeLayerControlPanel({\n controls,\n overrides,\n}: CompositeLayerControlPanelProps) {\n return (\n <>\n {controls.map((c) => (\n <Fragment key={c.name}>{dispatchControl(withOverride(c, overrides?.[c.name]))}</Fragment>\n ))}\n </>\n );\n}\n"]}
1
+ {"version":3,"file":"composite-layer-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/composite-layer-control-panel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;CAChE,CAAC;AAkBF,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,SAAS,GACV,EAAE,+BAA+B,2CAQjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"control-section.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/control-section.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AA+BzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CAwB7E","sourcesContent":["// ControlSection: the shared chrome every control group wears (a title + a\n// controls slot + a web-only copy button). Rendered INSIDE a ControlForm so the\n// copy button can read that form's live view model and serialize it.\n//\n// Copy is a desktop tweak-then-paste-into-a-preset workflow: the button renders\n// only on web (Platform.OS), writes via navigator.clipboard, and depends on no\n// clipboard module. Native intentionally has no copy button.\n\nimport type { ReactNode } from 'react';\nimport { useContext } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Platform, StyleSheet, Text, View } from 'react-native';\nimport { ControlFormContext, type FieldValue } from '../form/control-form';\nimport { useThemeSlot } from '../theme/provider';\nimport { Button } from '../ui/button';\n\ntype Values = Readonly<Record<string, FieldValue>>;\n\n// Round off float noise (drag jitter, the slider epsilon) and snap near-zero to\n// a true 0 so the pasted JSON reads as intended.\nconst round = (n: number): number => {\n const r = Math.round(n * 1e4) / 1e4;\n return Math.abs(r) < 1e-4 ? 0 : r;\n};\n\nconst roundForCopy = (values: Values): Record<string, FieldValue> => {\n const out: Record<string, FieldValue> = {};\n for (const [k, v] of Object.entries(values)) {\n out[k] = Array.isArray(v) ? v.map(round) : round(v as number);\n }\n return out;\n};\n\n// Web-only clipboard write, typed off globalThis so it needs no DOM lib and is a\n// no-op anywhere `navigator.clipboard` is absent.\nconst writeClipboard = (text: string): void => {\n const nav = (globalThis as { navigator?: { clipboard?: { writeText?: (t: string) => unknown } } })\n .navigator;\n nav?.clipboard?.writeText?.(text);\n};\n\nexport type ControlSectionProps = {\n readonly title: string;\n readonly children: ReactNode;\n readonly style?: StyleProp<ViewStyle>;\n};\n\nexport function ControlSection({ title, children, style }: ControlSectionProps) {\n const form = useContext(ControlFormContext);\n const { style: themeStyle } = useThemeSlot('section');\n const canCopy = Platform.OS === 'web' && form !== null;\n return (\n <View style={[styles.section, themeStyle as StyleProp<ViewStyle>, style]}>\n <View style={styles.header}>\n <Text accessibilityRole=\"header\" style={styles.title}>\n {title}\n </Text>\n {canCopy && form ? (\n <Button\n testID={`${form.path}.copy`}\n onPress={() =>\n writeClipboard(`${title}: ${JSON.stringify(roundForCopy(form.values), null, 2)}`)\n }\n >\n copy\n </Button>\n ) : null}\n </View>\n <View>{children}</View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n section: { gap: 6 },\n header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n title: {\n color: '#aaa',\n fontSize: 12,\n fontWeight: '600',\n textTransform: 'uppercase',\n letterSpacing: 1,\n },\n});\n"]}
1
+ {"version":3,"file":"control-section.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/control-section.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AA+BzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CAwB7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/control.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO/D,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,GAAG,SAAS,CAY5D;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,YAAY,aAUhD","sourcesContent":["// Control: place one control by uniform id inside a per-shader form. It takes\n// ONLY the uniform name, resolves that uniform's descriptor from the enclosing\n// ControlForm, and renders the kind's self-wiring primitive with the descriptor's\n// props. Taking only the name is the invariant: a layout form can ARRANGE controls\n// (wrap them in Views, group them) but cannot redefine a control's behavior.\n//\n// `dispatchControl` is the shared kind -> primitive map, reused by the auto-form\n// (CompositeLayerControlPanel renders every descriptor) and by <Control> (renders\n// one looked-up descriptor).\n\nimport type { ReactNode } from 'react';\nimport { useContext } from 'react';\nimport type { UniformControl } from '../../../catalog/shaders';\nimport { ControlFormContext } from '../form/control-form';\nimport { ColorPicker } from '../ui/color-picker';\nimport { PolygonField } from '../ui/polygon-field';\nimport { Slider } from '../ui/slider';\nimport { Switch } from '../ui/switch';\n\n/** Render one control descriptor as its kind's self-wiring primitive. */\nexport function dispatchControl(c: UniformControl): ReactNode {\n const label = c.label ?? c.name;\n switch (c.kind) {\n case 'color':\n return <ColorPicker uniform={c.name} label={label} />;\n case 'float':\n return <Slider uniform={c.name} min={c.min} max={c.max} step={c.step} label={label} />;\n case 'switch':\n return <Switch uniform={c.name} label={label} />;\n case 'polygon':\n return <PolygonField uniform={c.name} points={c.points} label={label} />;\n }\n}\n\nexport type ControlProps = {\n /** The uniform id to place; its descriptor is resolved from the ControlForm. */\n readonly uniform: string;\n};\n\nexport function Control({ uniform }: ControlProps) {\n const ctx = useContext(ControlFormContext);\n const descriptor = ctx?.controls?.find((c) => c.name === uniform);\n if (!descriptor) {\n throw new Error(\n `<Control uniform=\"${uniform}\"/>: no descriptor found. Pass controls={X_CONTROLS} ` +\n 'to the enclosing <ControlForm>.',\n );\n }\n return dispatchControl(descriptor);\n}\n"]}
1
+ {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/control.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO/D,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,GAAG,SAAS,CAY5D;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,YAAY,aAUhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/index.ts"],"names":[],"mappings":"AAQA,YAAY,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAClG,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACnF,cAAc,OAAO,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,KAAK,+BAA+B,EACpC,KAAK,eAAe,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC","sourcesContent":["// Public entry for the opt-in controls composition kit (subpath `./controls`).\n//\n// A per-form `ControlForm` micro-provider with `useField`/`makeControls`, the\n// self-theming field primitives, the `ControlSection` chrome, and the\n// `PresetControlPanel`, all themed via `KaleidoscopeThemeProvider`. This barrel\n// grows commit by commit; the theme surface lands first so later primitives can\n// register against a real exported entry.\n\nexport type { KaleidoscopeControls } from '../../kaleidoscope.preset-book.types';\nexport { ControlForm, type ControlFormProps, type FieldValue } from '../form/control-form';\nexport { makeControls } from '../form/make-controls';\nexport { type Field, useField } from '../form/use-field';\nexport { KaleidoscopeThemeProvider, useKaleidoscopeTheme, useThemeSlot } from '../theme/provider';\nexport type { KaleidoscopeThemeSlots, SlotStyle, ThemeSlot } from '../theme/slots';\nexport * from '../ui';\nexport {\n CompositeLayerControlPanel,\n type CompositeLayerControlPanelProps,\n type ControlOverride,\n} from './composite-layer-control-panel';\nexport { Control, type ControlProps, dispatchControl } from './control';\nexport { ControlSection, type ControlSectionProps } from './control-section';\nexport {\n MaskControlPanel,\n type MaskControlPanelProps,\n} from './mask-control-panel';\nexport { PresetControlPanel, type PresetControlPanelProps } from './preset-control-panel';\nexport {\n TransformControlPanel,\n type TransformControlPanelProps,\n} from './transform-control-panel';\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/index.ts"],"names":[],"mappings":"AAQA,YAAY,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAClG,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACnF,cAAc,OAAO,CAAC;AACtB,OAAO,EACL,0BAA0B,EAC1B,KAAK,+BAA+B,EACpC,KAAK,eAAe,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"mask-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/mask-control-panel.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAO1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAwCF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAgB,EAChB,YAAiC,GAClC,EAAE,qBAAqB,2CAmBvB","sourcesContent":["// MaskControlPanel: the segmentation-edge panel, themed and controlled.\n// Either slider emits the full MaskInput (the mask verb is absolute). Not a\n// layer form, so it has no ControlForm (and thus no copy button) and uses the\n// raw slider directly with the themed Label/Readout primitives.\n\nimport RNSlider from '@react-native-community/slider';\nimport { StyleSheet, View } from 'react-native';\nimport type { MaskInput } from '../../kaleidoscope/types';\nimport { MASK_TESTID_PREFIX } from '../../lib/test-id';\nimport { Label } from '../ui/label';\nimport { Readout } from '../ui/readout';\nimport { SLIDER_TINTS } from '../ui/slider-value';\nimport { ControlSection } from './control-section';\n\nexport type MaskControlPanelProps = {\n readonly hardness: number;\n readonly threshold: number;\n readonly onChange: (mask: MaskInput) => void;\n readonly disabled?: boolean;\n /** Root for this instance's test ids; override when a screen mounts two. */\n readonly testIDPrefix?: string;\n};\n\nfunction MaskRow({\n label,\n value,\n disabled,\n onChange,\n testID,\n}: {\n readonly label: string;\n readonly value: number;\n readonly disabled: boolean;\n readonly onChange: (v: number) => void;\n readonly testID: string;\n}) {\n return (\n <View style={styles.row}>\n <View style={styles.line}>\n <Label>{label}</Label>\n <Readout>{value.toFixed(2)}</Readout>\n </View>\n <RNSlider\n style={styles.slider}\n testID={testID}\n accessibilityLabel={label}\n // Floor at 0.01: at exactly 0 the mask smoothstep range collapses\n // (lo === hi) and the edge breaks. 0.01 keeps it well-defined. This is a\n // shader-math floor, distinct from the field slider's web-crash epsilon.\n minimumValue={0.01}\n maximumValue={1}\n step={0.01}\n value={value}\n disabled={disabled}\n onValueChange={onChange}\n {...SLIDER_TINTS}\n />\n </View>\n );\n}\n\nexport function MaskControlPanel({\n hardness,\n threshold,\n onChange,\n disabled = false,\n testIDPrefix = MASK_TESTID_PREFIX,\n}: MaskControlPanelProps) {\n return (\n <ControlSection title=\"mask\">\n <MaskRow\n label=\"hardness\"\n value={hardness}\n disabled={disabled}\n onChange={(v) => onChange({ hardness: v, threshold })}\n testID={`${testIDPrefix}.hardness`}\n />\n <MaskRow\n label=\"threshold\"\n value={threshold}\n disabled={disabled}\n onChange={(v) => onChange({ hardness, threshold: v })}\n testID={`${testIDPrefix}.threshold`}\n />\n </ControlSection>\n );\n}\n\nconst styles = StyleSheet.create({\n row: { gap: 2 },\n line: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n slider: { width: '100%', height: 32 },\n});\n"]}
1
+ {"version":3,"file":"mask-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/mask-control-panel.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAO1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAwCF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAgB,EAChB,YAAiC,GAClC,EAAE,qBAAqB,2CAmBvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"preset-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/preset-control-panel.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,sCAAsC,CAAC;AAG9C,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,sBAAsB,IAAI;IACtE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,sBAAsB,EAAE,EACnE,OAAO,EACP,KAAK,EACL,OAAO,EACP,QAAgB,GACjB,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI,CAoBlD","sourcesContent":["// PresetControlPanel: the thin, controlled editor. For the active preset it\n// renders that preset's `controls` component (keyed by preset id, so a switch\n// remounts and the ControlForms re-seed), handing it the per-layer baked\n// uniforms and a single shared onPatch. It never calls `kaleidoscope` itself;\n// the host routes onPatch into `kaleidoscope(activeId, [patch])`.\n\nimport type { ReactElement } from 'react';\nimport type {\n KaleidoscopeControls,\n KaleidoscopePresetBook,\n} from '../../kaleidoscope.preset-book.types';\nimport { ControlScopeContext } from '../form/scope';\n\nexport type PresetControlPanelProps<P extends KaleidoscopePresetBook> = {\n readonly presets: P;\n /** The active preset id, or null when nothing is selected. */\n readonly value: (keyof P & string) | null;\n /** Routed to the host, which applies it via `kaleidoscope(value, [patch])`. */\n readonly onPatch: KaleidoscopeControls['onPatch'];\n readonly disabled?: boolean;\n};\n\nexport function PresetControlPanel<P extends KaleidoscopePresetBook>({\n presets,\n value,\n onPatch,\n disabled = false,\n}: PresetControlPanelProps<P>): ReactElement | null {\n if (value === null) return null;\n const preset = presets[value];\n const Controls = preset?.controls;\n if (!Controls) return null;\n\n // Per-layer baked uniforms keyed by id, for the controls component to seed each\n // layer's ControlForm. Only tunable layers carry uniforms.\n const uniforms: Record<string, Record<string, number | readonly number[]>> = {};\n for (const layer of preset.layers) {\n if ('uniforms' in layer) {\n uniforms[layer.id] = { ...layer.uniforms } as Record<string, number | readonly number[]>;\n }\n }\n\n return (\n <ControlScopeContext.Provider value={value}>\n <Controls key={value} uniforms={uniforms} onPatch={onPatch} disabled={disabled} />\n </ControlScopeContext.Provider>\n );\n}\n"]}
1
+ {"version":3,"file":"preset-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/preset-control-panel.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,sCAAsC,CAAC;AAG9C,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,sBAAsB,IAAI;IACtE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,sBAAsB,EAAE,EACnE,OAAO,EACP,KAAK,EACL,OAAO,EACP,QAAgB,GACjB,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI,CAoBlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/transform-control-panel.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO/D,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAyCF,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,MAAU,EACV,QAAQ,EACR,QAAgB,EAChB,YAAsC,GACvC,EAAE,0BAA0B,2CAoD5B","sourcesContent":["// TransformControlPanel: absolute flip + 90-degree rotation, themed and\n// controlled. Every change emits the full TransformInput (the transform verb is\n// absolute). Toggles read the `active` theme slot for their on state.\n\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport type { TransformInput } from '../../kaleidoscope/types';\nimport { flipTestId, rotateTestId, TRANSFORM_TESTID_PREFIX } from '../../lib/test-id';\nimport { useThemeSlot } from '../theme/provider';\nimport { ControlSection } from './control-section';\n\nconst ROTATIONS = [0, 90, 180, 270] as const;\n\nexport type TransformControlPanelProps = {\n readonly flip?: { readonly x?: boolean; readonly y?: boolean };\n readonly rotate?: number;\n readonly onChange: (transform: TransformInput) => void;\n readonly disabled?: boolean;\n /** Root for this instance's test ids; override when a screen mounts two. */\n readonly testIDPrefix?: string;\n};\n\nfunction FlipToggle({\n label,\n icon,\n on,\n disabled,\n onPress,\n testID,\n accessibilityLabel,\n}: {\n readonly label: string;\n readonly icon: string;\n readonly on: boolean;\n readonly disabled: boolean;\n readonly onPress: () => void;\n readonly testID: string;\n readonly accessibilityLabel: string;\n}) {\n const { style: activeStyle } = useThemeSlot('active');\n return (\n <Pressable\n accessibilityRole=\"switch\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ checked: on, disabled }}\n testID={testID}\n disabled={disabled}\n onPress={onPress}\n style={[\n styles.flip,\n on && styles.on,\n on && (activeStyle as StyleProp<ViewStyle>),\n disabled && styles.disabled,\n ]}\n >\n <Text style={styles.flipIcon}>{icon}</Text>\n <Text style={styles.flipLabel}>{label}</Text>\n </Pressable>\n );\n}\n\nexport function TransformControlPanel({\n flip,\n rotate = 0,\n onChange,\n disabled = false,\n testIDPrefix = TRANSFORM_TESTID_PREFIX,\n}: TransformControlPanelProps) {\n const x = flip?.x ?? false;\n const y = flip?.y ?? false;\n const { style: activeStyle } = useThemeSlot('active');\n return (\n <ControlSection title=\"transform\">\n <View style={styles.flipRow}>\n <FlipToggle\n label=\"X\"\n icon=\"↔\"\n on={x}\n disabled={disabled}\n onPress={() => onChange({ flip: { x: !x, y }, rotate })}\n testID={flipTestId(testIDPrefix, 'x')}\n accessibilityLabel=\"Flip horizontal\"\n />\n <FlipToggle\n label=\"Y\"\n icon=\"↕\"\n on={y}\n disabled={disabled}\n onPress={() => onChange({ flip: { x, y: !y }, rotate })}\n testID={flipTestId(testIDPrefix, 'y')}\n accessibilityLabel=\"Flip vertical\"\n />\n </View>\n <View style={styles.rotRow}>\n {ROTATIONS.map((deg) => {\n const on = rotate === deg;\n return (\n <Pressable\n key={deg}\n accessibilityRole=\"radio\"\n accessibilityLabel={deg === 0 ? 'No rotation' : `Rotate ${deg} degrees`}\n accessibilityState={{ selected: on, disabled }}\n testID={rotateTestId(testIDPrefix, deg)}\n disabled={disabled}\n onPress={() => onChange({ flip: { x, y }, rotate: deg })}\n style={[\n styles.rot,\n on && styles.on,\n on && (activeStyle as StyleProp<ViewStyle>),\n disabled && styles.disabled,\n ]}\n >\n <Text style={styles.rotText}>{deg}°</Text>\n </Pressable>\n );\n })}\n </View>\n </ControlSection>\n );\n}\n\nconst styles = StyleSheet.create({\n flipRow: { flexDirection: 'row', gap: 8 },\n rotRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },\n flip: {\n flex: 1,\n paddingVertical: 12,\n backgroundColor: '#2a2a2a',\n borderRadius: 6,\n alignItems: 'center',\n gap: 2,\n },\n rot: {\n flexGrow: 1,\n minWidth: 56,\n paddingVertical: 10,\n backgroundColor: '#2a2a2a',\n borderRadius: 6,\n alignItems: 'center',\n },\n on: { backgroundColor: '#4a8f3f' },\n disabled: { opacity: 0.5 },\n flipIcon: { color: '#fff', fontSize: 22, lineHeight: 26 },\n flipLabel: { color: '#fff', fontWeight: '500', fontSize: 13 },\n rotText: { color: '#fff', fontWeight: '600', fontSize: 13 },\n});\n"]}
1
+ {"version":3,"file":"transform-control-panel.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-control-panel/transform-control-panel.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO/D,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAyCF,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,MAAU,EACV,QAAQ,EACR,QAAgB,EAChB,YAAsC,GACvC,EAAE,0BAA0B,2CAoD5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-tile/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAGL,KAAK,SAAS,EAId,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,UAAU,eAAe;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CA6ChD","sourcesContent":["// Leaf: one preset tile. The single item shape for every family; it prints the\n// label and, if the preset has a resolved thumbnail, paints it as a wallpaper\n// behind the label; with no thumbnail it is a recessed pressable button of the\n// SAME footprint, so a thumbnail-less preset never breaks the grid's flow. The\n// variant is chosen per preset by `uri` presence, not by family. Hover/press\n// \"light up\" via an overlay sheet (web hover is inert on native, where\n// `hovered` is simply undefined).\n//\n// The tile is a fixed 16:9 box (`aspectRatio`), matching the 1280x720 images and\n// 320x180 thumbnails, so cover-fit shows the whole image and every tile is the\n// same shape regardless of how wide a column resolves to. Width comes from the\n// flex row (`flexBasis`/`maxWidth`); `aspectRatio` derives the height from it.\n// (Historical note: an earlier attempt rendered thin strips when Yoga failed to\n// derive height from a percentage `flexBasis` + `aspectRatio`; if that recurs on\n// a target, restore an explicit `height` floor here.)\n//\n// Styling is headless: defaults + `style` (wins) + `className` (consumed by the\n// `./nativewind` interop, inert without it).\n\nimport {\n Image,\n Pressable,\n type StyleProp,\n StyleSheet,\n Text,\n View,\n type ViewStyle,\n} from 'react-native';\n\ninterface PresetTileProps {\n readonly label: string;\n /**\n * Resolved thumbnail source. A `string` is a URL (web) or file:// URI (native\n * preset-name lookup); a `number` is a Metro asset module id consumed directly\n * by `<Image source={number}>`. Undefined renders the recessed button.\n */\n readonly uri: string | number | undefined;\n readonly selected: boolean;\n readonly disabled?: boolean | undefined;\n readonly onPress: () => void;\n /** Optional corner badge, e.g. \"demo-owned\". */\n readonly badge?: string | undefined;\n /** NativeWind class; resolved via the `./nativewind` interop registration. */\n readonly className?: string | undefined;\n /** RN style override; applied after the defaults. */\n readonly style?: StyleProp<ViewStyle> | undefined;\n /** Deterministic `accessibilityIdentifier` (`kld.preset.<id>`). */\n readonly testID?: string | undefined;\n}\n\nexport function PresetTile(props: PresetTileProps) {\n const { label, uri, selected, disabled = false, onPress, badge, style, testID } = props;\n const hasWallpaper = !!uri;\n return (\n <Pressable\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: selected, disabled }}\n testID={testID}\n disabled={disabled}\n onPress={onPress}\n style={[\n styles.tile,\n hasWallpaper ? styles.wallpaper : styles.recessed,\n selected && styles.selected,\n disabled && styles.disabled,\n style,\n ]}\n >\n {({ pressed, hovered = false }: { pressed: boolean; hovered?: boolean }) => (\n <>\n {hasWallpaper ? (\n <Image\n source={typeof uri === 'number' ? uri : { uri }}\n style={styles.thumb}\n resizeMode=\"cover\"\n />\n ) : null}\n {hasWallpaper ? <View style={styles.scrim} /> : null}\n {badge ? (\n <View style={styles.badge}>\n <Text style={styles.badgeText}>{badge}</Text>\n </View>\n ) : null}\n <View style={styles.labelWrap}>\n <Text numberOfLines={2} style={styles.label}>\n {label}\n </Text>\n </View>\n {hovered || pressed ? (\n <View pointerEvents=\"none\" style={[styles.glow, pressed && styles.glowPressed]} />\n ) : null}\n </>\n )}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n tile: {\n aspectRatio: 16 / 9,\n minWidth: 96,\n // GENERAL React Native gotcha worth naming, because it bites LLM-generated\n // layouts in particular: Yoga (RN's flex engine) will silently collapse a\n // view to zero height when the layout describes height purely by derivation\n // (a percentage `flexBasis` plus an `aspectRatio`, or content that has not\n // measured yet) without an explicit floor. It smashes shut like an\n // unbroken IE6 `<div>`. If a row of tiles ever renders as thin strips, the\n // first thing to check is whether each tile has a `height` or `minHeight`\n // anywhere in its chain.\n //\n // THIS SITE: declared `flexBasis: '30%'` + `aspectRatio: 16/9` and no\n // height floor; each tile resolved to 118 x 4 px on iOS (Maestro hierarchy\n // bounds), turning every wallpaper tile into a thin gray strip. The\n // recessed variant survived only because its visible 2px border made the\n // 4px collapse read as a deliberate divider. 54 = 96 * 9 / 16 (the\n // minWidth's aspect-derived height), so a tile whose width grows past the\n // floor still tracks the 16:9 ratio via `aspectRatio`.\n minHeight: 54,\n flexGrow: 1,\n flexBasis: '30%',\n maxWidth: '32%',\n borderRadius: 6,\n overflow: 'hidden',\n borderWidth: 2,\n borderColor: 'transparent',\n },\n // Wallpaper variant: the thumbnail fills the tile, label over a legibility scrim.\n wallpaper: { backgroundColor: '#1a1a1a' },\n // Recessed variant (no thumbnail): a pressable inset area, same footprint.\n recessed: { backgroundColor: '#242424', borderColor: '#333' },\n selected: { borderColor: '#4a8f3f' },\n disabled: { opacity: 0.5 },\n thumb: { ...StyleSheet.absoluteFillObject },\n scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0, 0, 0, 0.35)' },\n badge: {\n position: 'absolute',\n top: 4,\n left: 4,\n backgroundColor: 'rgba(217, 119, 6, 0.92)',\n borderRadius: 4,\n paddingHorizontal: 5,\n paddingVertical: 2,\n },\n badgeText: { color: '#fff', fontSize: 9, fontWeight: '700' },\n labelWrap: {\n ...StyleSheet.absoluteFillObject,\n alignItems: 'center',\n justifyContent: 'center',\n },\n label: {\n color: '#fff',\n fontSize: 12,\n fontWeight: '700',\n textAlign: 'center',\n paddingHorizontal: 6,\n },\n // Hover/press \"lights up\": a translucent sheet over the whole tile.\n glow: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(255, 255, 255, 0.12)' },\n glowPressed: { backgroundColor: 'rgba(255, 255, 255, 0.04)' },\n});\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/preset-tile/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAGL,KAAK,SAAS,EAId,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,UAAU,eAAe;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CA6ChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../../src/components/theme/provider.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAM5E;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,2CAEA;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,IAAI,sBAAsB,CAE7D;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC5B,CAMA","sourcesContent":["// The theme provider: one context holding the flat slot bank that every control\n// primitive reads to self-decorate. Mirrors the React Native Reusables\n// `TextClassContext` idea, generalized to a slot map.\n//\n// Pass a STABLE `value` (React Compiler memoizes it on the consumer side;\n// otherwise memoize it by hand): a fresh object each render re-renders every\n// themed primitive.\n//\n// Leaf module: imports only `react` and the slot types. It must never import from\n// sibling component modules or from `ui/`, so `ui/` can depend on it (the one\n// allowed `ui/` -> `theme/` edge) without a cycle.\n\nimport type { ReactNode } from 'react';\nimport { createContext, useContext } from 'react';\nimport type { KaleidoscopeThemeSlots, SlotStyle, ThemeSlot } from './slots';\n\nconst EMPTY: KaleidoscopeThemeSlots = {};\n\nconst ThemeContext = createContext<KaleidoscopeThemeSlots>(EMPTY);\n\n/**\n * Wrap the controls UI to theme every primitive at once. `value` is the slot\n * bank (`labelClassName`, `sliderStyle`, ...); memoize it at the call site.\n */\nexport function KaleidoscopeThemeProvider({\n value,\n children,\n}: {\n readonly value?: KaleidoscopeThemeSlots;\n readonly children: ReactNode;\n}) {\n return <ThemeContext.Provider value={value ?? EMPTY}>{children}</ThemeContext.Provider>;\n}\n\n/** The full slot bank from context (empty when no provider is mounted). */\nexport function useKaleidoscopeTheme(): KaleidoscopeThemeSlots {\n return useContext(ThemeContext);\n}\n\n/** The `{ className, style }` pair for one slot, for a primitive to merge last. */\nexport function useThemeSlot(slot: ThemeSlot): {\n readonly className?: string;\n readonly style?: SlotStyle;\n} {\n const slots = useContext(ThemeContext);\n return {\n className: slots[`${slot}ClassName`],\n style: slots[`${slot}Style`],\n };\n}\n"]}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../../src/components/theme/provider.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAM5E;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,2CAEA;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,IAAI,sBAAsB,CAE7D;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC5B,CAMA"}
@@ -1 +1 @@
1
- {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../../src/components/theme/slots.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhF,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAEtE,mFAAmF;AACnF,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,CAAC;AAEf;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,CAAC,IAAI,SAAS,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM;CACtD,GAAG;IACF,QAAQ,EAAE,CAAC,IAAI,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS;CACrD,CAAC","sourcesContent":["// The controls theme slot bank: one className + style pair per primitive\n// component and per interaction state. A primitive reads its slot via\n// `useThemeSlot` and merges it AFTER its built-in defaults (local props win).\n//\n// Leaf module: imports only react-native types, nothing from sibling `controls/`\n// modules or from `ui/`, so `ui/` can read the theme without an import cycle.\n\nimport type { ImageStyle, StyleProp, TextStyle, ViewStyle } from 'react-native';\n\n/** A style value for any slot (container, text, or image targets). */\nexport type SlotStyle = StyleProp<ViewStyle | TextStyle | ImageStyle>;\n\n/** Semantic slot names: one per primitive component, one per interaction state. */\nexport type ThemeSlot =\n | 'label'\n | 'readout'\n | 'slider'\n | 'colorPicker'\n | 'button'\n | 'tabs'\n | 'tile'\n | 'section'\n | 'active'\n | 'inactive'\n | 'disabled';\n\n/**\n * The theme bank: a `<slot>ClassName` (resolved by the `./nativewind` interop)\n * and a `<slot>Style` (the always-works RN fallback) per slot. All optional; an\n * unset slot falls back to the primitive's built-in default. `readout` is the\n * value display (named distinctly from a control's mutating `value`).\n */\nexport type KaleidoscopeThemeSlots = {\n readonly [S in ThemeSlot as `${S}ClassName`]?: string;\n} & {\n readonly [S in ThemeSlot as `${S}Style`]?: SlotStyle;\n};\n"]}
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../../src/components/theme/slots.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhF,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAEtE,mFAAmF;AACnF,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,CAAC;AAEf;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,CAAC,IAAI,SAAS,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM;CACtD,GAAG;IACF,QAAQ,EAAE,CAAC,IAAI,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS;CACrD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/button.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,2CAqBzF","sourcesContent":["// Button: a themed Pressable + label, used for the copy button and any control\n// action. Themes via the `button` slot, with the `disabled` state slot overlaid\n// at the container when disabled.\n\nimport type { ReactNode } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { Pressable, StyleSheet, Text } from 'react-native';\nimport { useThemeSlot } from '../theme/provider';\n\nexport type ButtonProps = {\n readonly children: ReactNode;\n readonly onPress: () => void;\n readonly disabled?: boolean;\n /** NativeWind class; resolved via the `./nativewind` interop. */\n readonly className?: string;\n readonly style?: StyleProp<ViewStyle>;\n /** Deterministic `accessibilityIdentifier` for the pressable. */\n readonly testID?: string;\n};\n\nexport function Button({ children, onPress, disabled = false, style, testID }: ButtonProps) {\n const { style: themeStyle } = useThemeSlot('button');\n const { style: disabledStyle } = useThemeSlot('disabled');\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n testID={testID}\n onPress={onPress}\n disabled={disabled}\n style={[\n styles.button,\n themeStyle as StyleProp<ViewStyle>,\n disabled && styles.disabled,\n disabled && (disabledStyle as StyleProp<ViewStyle>),\n style,\n ]}\n >\n <Text style={styles.text}>{children}</Text>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n button: {\n paddingHorizontal: 8,\n paddingVertical: 3,\n backgroundColor: '#2a2a2a',\n borderRadius: 4,\n alignSelf: 'flex-start',\n },\n disabled: { opacity: 0.5 },\n text: { color: '#8888ff', fontSize: 11, fontWeight: '600' },\n});\n"]}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/button.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,2CAqBzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/color-picker.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,gBAAgB,2CAuCtE","sourcesContent":["// ColorPicker: a self-wiring vec3 field. v1 is the swatch + three channel\n// sliders the demo shipped; it reads/writes a single RGB uniform via `useField`\n// and emits the whole triple on any channel change. Themes via `colorPicker`.\n\nimport RNSlider from '@react-native-community/slider';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { StyleSheet, View } from 'react-native';\nimport { useField } from '../form/use-field';\nimport { useThemeSlot } from '../theme/provider';\nimport { Label } from './label';\nimport { Readout } from './readout';\nimport { SLIDER_TINTS, safeSliderValue } from './slider-value';\n\nconst CH = ['R', 'G', 'B'] as const;\nconst CH_KEY = ['r', 'g', 'b'] as const;\n\nexport type ColorPickerProps = {\n /** The uniform key this picker drives (an RGB triple). */\n readonly uniform: string;\n /** Display name; defaults to the uniform key. */\n readonly label?: string;\n /** NativeWind class for the container; resolved via the `./nativewind` interop. */\n readonly className?: string;\n readonly style?: StyleProp<ViewStyle>;\n};\n\nexport function ColorPicker({ uniform, label, style }: ColorPickerProps) {\n const field = useField(uniform);\n const { style: themeStyle } = useThemeSlot('colorPicker');\n const rgb = Array.isArray(field.value) ? field.value : [0, 0, 0];\n const r = rgb[0] ?? 0;\n const g = rgb[1] ?? 0;\n const b = rgb[2] ?? 0;\n const swatch = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;\n const name = label ?? uniform;\n const setChannel = (i: number, v: number) => {\n const next = [r, g, b];\n next[i] = v;\n field.onChange(next);\n };\n return (\n <View testID={field.testID} style={[styles.row, themeStyle as StyleProp<ViewStyle>, style]}>\n <View style={styles.labelWrap}>\n <View style={[styles.swatch, { backgroundColor: swatch }]} />\n <Label>{name}</Label>\n </View>\n {[r, g, b].map((v, i) => (\n <View key={CH[i]} style={styles.chanLine}>\n <Readout>{CH[i]}</Readout>\n <RNSlider\n style={styles.chanSlider}\n testID={`${field.testID}.${CH_KEY[i]}`}\n accessibilityLabel={`${name} ${CH[i]}`}\n minimumValue={0}\n maximumValue={1}\n step={0.01}\n value={safeSliderValue(v)}\n disabled={field.disabled}\n onValueChange={(nv) => setChannel(i, nv)}\n {...SLIDER_TINTS}\n />\n </View>\n ))}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n row: { gap: 2 },\n labelWrap: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n swatch: { width: 12, height: 12, borderRadius: 2, borderWidth: 1, borderColor: '#555' },\n chanLine: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n chanSlider: { flex: 1, height: 28 },\n});\n"]}
1
+ {"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/color-picker.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,gBAAgB,2CAuCtE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC","sourcesContent":["// The field primitives barrel. These are the styleable leaves the parity test\n// pairs against the `./nativewind` cssInterop registrations (providers and\n// shells are NOT here, so the test's bidirectional check stays scoped to\n// components that actually take a `className`).\n\nexport { Button, type ButtonProps } from './button';\nexport { ColorPicker, type ColorPickerProps } from './color-picker';\nexport { Label, type LabelProps } from './label';\nexport { Readout, type ReadoutProps } from './readout';\nexport { Slider, type SliderProps } from './slider';\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,UAAU,2CAGpD","sourcesContent":["// Label: a themed Text for a control's name. Reads the `label` theme slot's\n// style and merges it after the default; `className` is consumed by the\n// `./nativewind` cssInterop at the boundary and arrives folded into `style`.\n\nimport type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle } from 'react-native';\nimport { StyleSheet, Text } from 'react-native';\nimport { useThemeSlot } from '../theme/provider';\n\nexport type LabelProps = {\n readonly children: ReactNode;\n /** NativeWind class; resolved via the `./nativewind` interop. */\n readonly className?: string;\n readonly style?: StyleProp<TextStyle>;\n};\n\nexport function Label({ children, style }: LabelProps) {\n const { style: themeStyle } = useThemeSlot('label');\n return <Text style={[styles.label, themeStyle as StyleProp<TextStyle>, style]}>{children}</Text>;\n}\n\nconst styles = StyleSheet.create({\n label: { color: '#ccc', fontSize: 12 },\n});\n"]}
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,UAAU,2CAGpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/point.tsx"],"names":[],"mappings":"AAmBA,MAAM,MAAM,UAAU,GAAG;IACvB,gCAAgC;IAChC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,KAAK,CAAC,EACpB,CAAC,EACD,CAAC,EACD,GAAG,EACH,GAAG,EACH,IAAW,EACX,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE,UAAU,2CA6CZ","sourcesContent":["// Point: a presentational (x, y) coordinate editor. A square box with a dot drawn\n// at the point's position, plus two HORIZONTAL sliders labeled X and Y. (A rotated\n// \"vertical\" slider renders upright but still drags side-to-side on RN-web, which\n// reads as broken; two plain horizontal sliders are honest.) The box gives the\n// spatial feedback the vertical axis would have: the dot tracks (x, y) live, x\n// left-to-right and y bottom-to-top (y-up, matching the shaders).\n//\n// Presentational on purpose: it owns no field, just `{ x, y, onChange }`. The\n// self-wiring lives in the container that drives it (PolygonField slices a vec2\n// array into one Point per vertex). Reusable for anything that is an xy coordinate\n// (a point light, a focal point).\n\nimport RNSlider from '@react-native-community/slider';\nimport { StyleSheet, View } from 'react-native';\nimport { Readout } from './readout';\nimport { SLIDER_TINTS, safeSliderValue } from './slider-value';\n\nconst BOX = 56;\n\nexport type PointProps = {\n /** Current x, in [min, max]. */\n readonly x: number;\n /** Current y, in [min, max]; drawn bottom-to-top (y-up). */\n readonly y: number;\n /** Slider range for both axes. */\n readonly min: number;\n readonly max: number;\n readonly step?: number;\n /** Emitted on either axis change with the full (x, y). */\n readonly onChange: (x: number, y: number) => void;\n /** Short caption above the box (e.g. the vertex index). */\n readonly label?: string;\n readonly disabled?: boolean;\n /** Base test id; the x slider appends `.x`, the y slider `.y`. */\n readonly testID?: string;\n};\n\nexport function Point({\n x,\n y,\n min,\n max,\n step = 0.01,\n onChange,\n label,\n disabled,\n testID,\n}: PointProps) {\n const span = max - min || 1;\n const xFrac = Math.min(1, Math.max(0, (x - min) / span));\n const yFrac = Math.min(1, Math.max(0, (y - min) / span));\n return (\n <View style={styles.wrap}>\n {label ? <Readout>{label}</Readout> : null}\n <View style={styles.box}>\n <View\n style={[styles.dot, { left: `${xFrac * 100}%`, top: `${(1 - yFrac) * 100}%` }]}\n pointerEvents=\"none\"\n />\n </View>\n <View style={styles.axis}>\n <Readout>X</Readout>\n <RNSlider\n style={styles.slider}\n testID={testID ? `${testID}.x` : undefined}\n accessibilityLabel={label ? `${label} x` : 'x'}\n minimumValue={min}\n maximumValue={max}\n step={step}\n value={safeSliderValue(x)}\n disabled={disabled}\n onValueChange={(v) => onChange(v, y)}\n {...SLIDER_TINTS}\n />\n </View>\n <View style={styles.axis}>\n <Readout>Y</Readout>\n <RNSlider\n style={styles.slider}\n testID={testID ? `${testID}.y` : undefined}\n accessibilityLabel={label ? `${label} y` : 'y'}\n minimumValue={min}\n maximumValue={max}\n step={step}\n value={safeSliderValue(y)}\n disabled={disabled}\n onValueChange={(v) => onChange(x, v)}\n {...SLIDER_TINTS}\n />\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrap: { alignItems: 'center', gap: 3, width: BOX + 44 },\n box: {\n width: BOX,\n height: BOX,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: 'rgba(255,255,255,0.35)',\n backgroundColor: 'rgba(255,255,255,0.05)',\n position: 'relative',\n },\n dot: {\n position: 'absolute',\n width: 7,\n height: 7,\n borderRadius: 4,\n marginLeft: -3.5,\n marginTop: -3.5,\n backgroundColor: '#fff',\n },\n axis: { flexDirection: 'row', alignItems: 'center', gap: 4, alignSelf: 'stretch' },\n slider: { flex: 1, height: 28 },\n});\n"]}
1
+ {"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/point.tsx"],"names":[],"mappings":"AAmBA,MAAM,MAAM,UAAU,GAAG;IACvB,gCAAgC;IAChC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,KAAK,CAAC,EACpB,CAAC,EACD,CAAC,EACD,GAAG,EACH,GAAG,EACH,IAAW,EACX,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE,UAAU,2CA6CZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"polygon-field.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/polygon-field.tsx"],"names":[],"mappings":"AAiBA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CAmDzE","sourcesContent":["// PolygonField: editor for a `kind: 'polygon'` uniform (a vec2 array, stored flat\n// as [x0,y0, x1,y1, ...]). It is COMPOSED OF POINTS: one <Point> editor per vertex,\n// laid out in a SPATIAL GRID rather than a flat row, so the editors sit where their\n// corners are. With the row-major corner convention (p0=TL, p1=TR, p2=BL, p3=BR), a\n// 2-column grid puts each editor in its quad position: top-left edits the top-left\n// corner. The grid is √-shaped (cols = ceil(√points)), so a 4-point polygon is 2x2.\n// Self-wires the flat array via `useField`; each Point writes back its (x, y) slice.\n\nimport { StyleSheet, View } from 'react-native';\nimport { useField } from '../form/use-field';\nimport { Label } from './label';\nimport { Point } from './point';\n\n/** Editing range for both axes; shaft anchors sit slightly off-screen (-0.2..1.2). */\nconst RANGE_MIN = -0.2;\nconst RANGE_MAX = 1.2;\n\nexport type PolygonFieldProps = {\n /** The uniform key this field drives: a vec2 array, flat [x0,y0, x1,y1, ...]. */\n readonly uniform: string;\n /** Number of polygon vertices; laid out in a ceil(√points)-column grid. */\n readonly points: number;\n /** Display name; defaults to the uniform key. */\n readonly label?: string;\n};\n\nexport function PolygonField({ uniform, points, label }: PolygonFieldProps) {\n const field = useField(uniform);\n const flat = Array.isArray(field.value) ? field.value : [];\n const name = label ?? uniform;\n\n const setPoint = (p: number, x: number, y: number) => {\n const next = flat.slice();\n while (next.length < points * 2) next.push(0);\n next[p * 2] = x;\n next[p * 2 + 1] = y;\n field.onChange(next);\n };\n\n // Spatial grid: cols = ceil(√points) (2 for a quad), filled row-major so vertex\n // order maps to grid position. Array.from callbacks (not mutated for-counters)\n // keep the per-Point onChange closures off the React Compiler's bail list.\n const cols = Math.max(1, Math.ceil(Math.sqrt(points)));\n const rowCount = Math.ceil(points / cols);\n\n return (\n <View style={styles.wrap}>\n <Label>{name}</Label>\n <View style={styles.grid}>\n {Array.from({ length: rowCount }, (_, r) => (\n <View\n // biome-ignore lint/suspicious/noArrayIndexKey: grid rows are positional and fixed in count.\n key={r}\n style={styles.gridRow}\n >\n {Array.from({ length: cols }, (_, ci) => {\n const p = r * cols + ci;\n if (p >= points) return null;\n return (\n <Point\n key={p}\n x={flat[p * 2] ?? 0}\n y={flat[p * 2 + 1] ?? 0}\n min={RANGE_MIN}\n max={RANGE_MAX}\n label={`p${p}`}\n disabled={field.disabled}\n testID={`${field.testID}.${p}`}\n onChange={(x, y) => setPoint(p, x, y)}\n />\n );\n })}\n </View>\n ))}\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrap: { gap: 4 },\n grid: { gap: 8, alignSelf: 'flex-start' },\n gridRow: { flexDirection: 'row', gap: 8 },\n});\n"]}
1
+ {"version":3,"file":"polygon-field.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/polygon-field.tsx"],"names":[],"mappings":"AAiBA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CAmDzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"readout.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/readout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,YAAY,2CAKxD","sourcesContent":["// Readout: a themed Text for a control's current value (distinct slot from the\n// label, and named `readout` to avoid colliding with a control's mutating value).\n\nimport type { ReactNode } from 'react';\nimport type { StyleProp, TextStyle } from 'react-native';\nimport { StyleSheet, Text } from 'react-native';\nimport { useThemeSlot } from '../theme/provider';\n\nexport type ReadoutProps = {\n readonly children: ReactNode;\n /** NativeWind class; resolved via the `./nativewind` interop. */\n readonly className?: string;\n readonly style?: StyleProp<TextStyle>;\n};\n\nexport function Readout({ children, style }: ReadoutProps) {\n const { style: themeStyle } = useThemeSlot('readout');\n return (\n <Text style={[styles.readout, themeStyle as StyleProp<TextStyle>, style]}>{children}</Text>\n );\n}\n\nconst styles = StyleSheet.create({\n readout: { color: '#8888ff', fontSize: 12, fontVariant: ['tabular-nums'] },\n});\n"]}
1
+ {"version":3,"file":"readout.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/readout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,YAAY,2CAKxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"slider-value.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/slider-value.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,MAAO,MAAM,KAAG,MACQ,CAAC;AAErD,0EAA0E;AAC1E,eAAO,MAAM,YAAY;aACvB,qBAAqB,EAAE,SAAS;aAChC,qBAAqB,EAAE,MAAM;aAC7B,cAAc,EAAE,SAAS;CACjB,CAAC","sourcesContent":["// The web slider wrapper maps an exact 0 (and NaN/undefined) to `undefined`,\n// then crashes calling `.toFixed()` on it. A hair above zero is visually\n// identical and keeps the control alive; the real (possibly 0) value still\n// flows through onChange and the readout, so this is presentation-only.\nconst SLIDER_EPSILON = 1e-4;\n\nexport const safeSliderValue = (v: number): number =>\n Number.isFinite(v) && v !== 0 ? v : SLIDER_EPSILON;\n\n/** Shared track/thumb tints so every slider in the kit reads the same. */\nexport const SLIDER_TINTS = {\n minimumTrackTintColor: '#8888ff',\n maximumTrackTintColor: '#444',\n thumbTintColor: '#eeeeff',\n} as const;\n"]}
1
+ {"version":3,"file":"slider-value.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/slider-value.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,MAAO,MAAM,KAAG,MACQ,CAAC;AAErD,0EAA0E;AAC1E,eAAO,MAAM,YAAY;aACvB,qBAAqB,EAAE,SAAS;aAChC,qBAAqB,EAAE,MAAM;aAC7B,cAAc,EAAE,SAAS;CACjB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASzD,MAAM,MAAM,WAAW,GAAG;IACxB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,WAAW,2CAkDnF","sourcesContent":["// Slider: a self-wiring scalar field. Reads/writes its uniform via `useField`\n// (so the thumb tracks the drag synchronously; only the ControlForm's emit\n// debounces), and pairs the slider with a typed number input so you can drag OR\n// type/paste an exact value. Renders its own label, themes via the `slider` slot.\n\nimport RNSlider from '@react-native-community/slider';\nimport { useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { StyleSheet, TextInput, View } from 'react-native';\nimport { useField } from '../form/use-field';\nimport { useThemeSlot } from '../theme/provider';\nimport { Label } from './label';\nimport { SLIDER_TINTS, safeSliderValue } from './slider-value';\n\nconst fmt = (v: number) => (Number.isFinite(v) ? String(Math.round(v * 1000) / 1000) : '0');\n\nexport type SliderProps = {\n /** The uniform key this slider drives (a scalar). */\n readonly uniform: string;\n readonly min: number;\n readonly max: number;\n readonly step?: number;\n /** Display name; defaults to the uniform key. */\n readonly label?: string;\n /** NativeWind class for the container; resolved via the `./nativewind` interop. */\n readonly className?: string;\n readonly style?: StyleProp<ViewStyle>;\n};\n\nexport function Slider({ uniform, min, max, step = 0.01, label, style }: SliderProps) {\n const field = useField(uniform);\n const { style: themeStyle } = useThemeSlot('slider');\n const value = typeof field.value === 'number' ? field.value : 0;\n const name = label ?? uniform;\n\n // The number input holds its own draft text so partial entries (\"0.\", \"-\") and\n // pastes survive; the slider writes the draft too, so dragging keeps it in sync.\n // A preset switch remounts the form, so the draft re-seeds from the new value.\n const [draft, setDraft] = useState(() => fmt(value));\n\n const fromSlider = (v: number) => {\n setDraft(fmt(v));\n field.onChange(v);\n };\n const fromText = (t: string) => {\n setDraft(t);\n const v = Number.parseFloat(t);\n if (Number.isFinite(v)) field.onChange(Math.min(max, Math.max(min, v)));\n };\n\n return (\n <View style={[styles.row, themeStyle as StyleProp<ViewStyle>, style]}>\n <View style={styles.line}>\n <Label>{name}</Label>\n <TextInput\n style={styles.num}\n testID={field.testID ? `${field.testID}.num` : undefined}\n accessibilityLabel={`${name} value`}\n value={draft}\n keyboardType=\"numeric\"\n editable={!field.disabled}\n selectTextOnFocus\n onChangeText={fromText}\n />\n </View>\n <RNSlider\n style={styles.slider}\n testID={field.testID}\n accessibilityLabel={name}\n minimumValue={min}\n maximumValue={max}\n step={step}\n value={safeSliderValue(value)}\n disabled={field.disabled}\n onValueChange={fromSlider}\n {...SLIDER_TINTS}\n />\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n row: { gap: 2 },\n line: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n num: {\n minWidth: 56,\n color: '#8888ff',\n fontSize: 12,\n fontVariant: ['tabular-nums'],\n textAlign: 'right',\n paddingVertical: 0,\n paddingHorizontal: 4,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: 'rgba(136,136,255,0.4)',\n borderRadius: 3,\n },\n slider: { width: '100%', height: 32 },\n});\n"]}
1
+ {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASzD,MAAM,MAAM,WAAW,GAAG;IACxB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,WAAW,2CAkDnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/switch.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,2CAgBrD","sourcesContent":["// Switch: a self-wiring boolean field for `kind: 'switch'` uniforms (0/1). Reads\n// and writes its uniform via `useField`, the same contract as Slider/ColorPicker.\n\nimport { Switch as RNSwitch, StyleSheet, View } from 'react-native';\nimport { useField } from '../form/use-field';\nimport { Label } from './label';\n\nexport type SwitchProps = {\n /** The uniform key this switch drives (0 = off, 1 = on). */\n readonly uniform: string;\n /** Display name; defaults to the uniform key. */\n readonly label?: string;\n};\n\nexport function Switch({ uniform, label }: SwitchProps) {\n const field = useField(uniform);\n const on = (typeof field.value === 'number' ? field.value : 0) > 0.5;\n const name = label ?? uniform;\n return (\n <View style={styles.row}>\n <Label>{name}</Label>\n <RNSwitch\n testID={field.testID}\n accessibilityLabel={name}\n value={on}\n disabled={field.disabled}\n onValueChange={(v) => field.onChange(v ? 1 : 0)}\n />\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n row: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n alignItems: 'center',\n paddingVertical: 2,\n },\n});\n"]}
1
+ {"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/switch.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,2CAgBrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AA6BlG,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,2BAA2B,EAC3B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AA6JlD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,sBAAsB,SACxD,gBAAgB,WACd,uBAAuB,CAAC,CAAC,CAAC,KAClC,mBAAmB,CAAC,CAAC,CAmBvB,CAAC","sourcesContent":["// Native entry point. Metro picks this up via package.json \"react-native\"\n// and the \".\" subpath export's \"react-native\" condition.\n//\n// Thin facade over `track._setVideoEffects(names)` from react-native-webrtc.\n// Native frame processors are registered at app boot by the Expo Module's\n// OnCreate hook (see android/.../KaleidoscopeModule.kt and ios/.../KaleidoscopeModule.swift);\n// this facade just dispatches into the existing upstream registry.\n\nimport { requireNativeModule } from 'expo-modules-core';\nimport { Platform } from 'react-native';\nimport {\n createControls,\n type Reconcile,\n type ResetLayerUniforms,\n type SetLayerUniforms,\n type SetMask,\n} from './kaleidoscope/controls';\nimport { toEffectSpec } from './kaleidoscope/effect';\nimport type { ApplyVideoEffects, CompositeSpec } from './kaleidoscope/effect.types';\nimport type { KaleidoscopeBinding, KaleidoscopeBindOptions } from './kaleidoscope/types';\nimport type { KaleidoscopeLayer, KaleidoscopePresetBook } from './kaleidoscope.preset-book.types';\n\n// The native module's tuning functions. Only the three the JS layer drives are\n// declared: blur sigma (from a blur preset's options) and the mask edge (from\n// the mask() verb). The native module also exposes segmentation/debug/reset\n// functions, but nothing in JS calls them anymore, so they're not declared here.\ninterface KaleidoscopeNativeModule {\n setMaskHardness: (value: number) => void;\n setMaskThreshold: (value: number) => void;\n // KaleidoscopePreset layer-stack channel. Optional: a native build predating the\n // compositor won't expose it, so callers guard with `?.`. JS sends the active\n // composite's ordered layer stack as a JSON string; native parses + composites\n // it. Blur sigma and generative uniforms now ride inside each layer's\n // `uniforms`, so there is no separate setBlurSigma/setShaderUniforms channel.\n setCompositeLayers?: (json: string) => void;\n}\n\n// Lazy because the module is not available during pure-JS tests; the\n// getter throws if you call a setter outside a real native runtime, which\n// is the right failure mode.\nconst nativeModule = (): KaleidoscopeNativeModule =>\n requireNativeModule<KaleidoscopeNativeModule>('RnWebrtcKaleidoscope');\n\n// The native tuning functions (setMaskHardness / setMaskThreshold) remain on the\n// native module and are called internally: the mask edge flows from the mask()\n// verb (see bindKaleidoscope). The composite layer stack flows through\n// setCompositeLayers. The old global set* JS exports are gone; effects are driven by\n// kaleidoscope / transform / mask, not loose setters.\n\nexport type { CatalogImageId } from '../catalog/images';\nexport type {\n AnamorphicLensFlareUniforms,\n BlurUniforms,\n CloudsUniforms,\n CorporateBlobsUniforms,\n FirefliesUniforms,\n GodraysUniforms,\n LayerShaderName,\n LayerShaderOptions,\n LightBeamsAndMotesUniforms,\n NebulaUniforms,\n PatchableShaderName,\n PlasmaUniforms,\n ShaderUniformsMap,\n SimianlightsUniforms,\n UniformControl,\n} from '../catalog/shaders';\n// Per-shader control descriptors (platform-agnostic data). Imported individually\n// per the shader a preset's layer uses; there is no all-shaders aggregate.\nexport {\n ANAMORPHIC_LENSFLARE_CONTROLS,\n BLUR_CONTROLS,\n CLOUDS_CONTROLS,\n CORPORATE_BLOBS_CONTROLS,\n defaultUniforms,\n FIREFLIES_CONTROLS,\n GODRAYS_CONTROLS,\n LIGHT_BEAMS_AND_MOTES_CONTROLS,\n NEBULA_CONTROLS,\n PLASMA_CONTROLS,\n SIMIANLIGHTS_CONTROLS,\n} from '../catalog/shaders';\nexport type {\n CompositeSpec,\n EffectInput,\n EffectName,\n EffectSpec,\n TransformName,\n TransformSpec,\n} from './kaleidoscope/effect.types';\nexport type {\n KaleidoscopeBinding,\n KaleidoscopeBindOptions,\n MaskInput,\n PatchesFor,\n PatchFor,\n TransformInput,\n} from './kaleidoscope/types';\nexport type {\n KaleidoscopeBlendMode,\n KaleidoscopeControls,\n KaleidoscopeLayer,\n KaleidoscopeLayerTarget,\n KaleidoscopePreset,\n KaleidoscopePresetBook,\n KaleidoscopePresetEntry,\n KaleidoscopeTaxonomy,\n} from './kaleidoscope.preset-book.types';\nexport type { RGB } from './lib/primitives.types';\n\ninterface WebRTCTrackExtensions {\n remote?: boolean;\n // Upstream's typed signature is `string[]`, but the platforms diverge on\n // how to clear effects:\n // - Android: passing `null` takes the\n // `videoSource.setVideoProcessor(null)` branch (the only correct clear\n // path); passing `[]` crashes EglRenderer.\n // - iOS: the Obj-C method declares `names` as `nonnull NSArray<NSString *>`,\n // so `null` violates the bridge contract; `[]` is the supported clear\n // value (iOS's `VideoEffectProcessor` with no processors is a\n // passthrough).\n // We type the parameter as the union so the facade can platform-split at\n // the call site. See `applyVideoEffects` below.\n _setVideoEffects?: (names: ReadonlyArray<string> | null) => void;\n}\n\n// Effect names registered native-side in Registration.kt / Registration.swift.\n// Anything not in this list gets filtered out before reaching upstream,\n// because rn-webrtc's setVideoEffects calls ProcessorProvider.getProcessor()\n// and filters nulls into an empty list, which then hits the\n// VideoEffectProcessor empty-processors-list bug (refcount goes negative,\n// EglRenderer crashes one frame later). Dropping an unregistered name here is\n// the safe behavior.\n//\n// The art axis is now one registered \"composite\" compositor: blur, images, and\n// generative shaders are all layers inside it, delivered out-of-band via\n// setCompositeLayers. The four geometric transform ops share one native processor\n// per platform (TransformFactory on Android, TransformProcessor on iOS); each is\n// a flat name. iOS registers the same set via ios/.../Registration.swift.\nconst TRANSFORM_EFFECTS: readonly string[] = ['flip-x', 'flip-y', 'rotate-cw', 'rotate-ccw'];\n\n// Android (Registration.kt) and iOS (Registration.swift) install an identical\n// effect set, so one list covers both natives. If the platforms ever diverge,\n// split this back into per-platform lists keyed off Platform.OS.\nconst NATIVE_REGISTERED_EFFECTS_LIST: readonly string[] = [...TRANSFORM_EFFECTS, 'composite'];\n\nconst registeredForPlatform = (): readonly string[] =>\n Platform.OS === 'android' || Platform.OS === 'ios' ? NATIVE_REGISTERED_EFFECTS_LIST : [];\n\nconst NATIVE_REGISTERED_EFFECTS: ReadonlySet<string> = new Set(registeredForPlatform());\n\n// Serialize a composite's layer stack to the JSON shape the native CompositeLayers\n// channel parses: an array of { id, shader, target, blend?, source?, uniforms? }.\n// Every layer carries its `id`. An `image` layer's native `source` IS its `id`\n// (the bundled WebP basename the prebuild plugin copied under that name); all\n// other layers carry their uniforms.\nconst serializeCompositeLayers = (layers: ReadonlyArray<KaleidoscopeLayer>): string => {\n const wire = layers.map((layer) => {\n const base: Record<string, unknown> = {\n id: layer.id,\n shader: layer.shader,\n target: layer.target ?? 'background',\n };\n if (layer.blend != null) base.blend = layer.blend;\n if (layer.shader === 'image') {\n // The layer id is the image id (the bundled WebP basename); the native\n // compositor resolves assets/images/<id>.webp from it.\n base.source = layer.id;\n } else if ('uniforms' in layer) {\n base.uniforms = layer.uniforms;\n }\n return base;\n });\n return JSON.stringify(wire);\n};\n\nconst specToNativeName = (spec: ReturnType<typeof toEffectSpec>): string => {\n // The composite runs through the single registered \"composite\" compositor; its\n // layer stack is delivered out-of-band via setCompositeLayers (see applyVideoEffects).\n if (spec.name === 'composite') {\n return 'composite';\n }\n return spec.name;\n};\n\n// Last effect set applied to each track, as a stable signature. Used to skip\n// redundant native calls: rn-webrtc rebuilds the native frame processors on\n// EVERY _setVideoEffects call (Android constructs a fresh processor per call),\n// so re-issuing an unchanged set (a React re-render, an idempotent effect\n// hook) churns GL + segmentation resources for no reason. The WeakMap lets the\n// entry be collected when the track is.\nconst lastAppliedSignatureByTrack = new WeakMap<object, string>();\n\n// The lower-level native primitive: route a spec array through the upstream\n// `_setVideoEffects`. Internal now (the public surface is the three verbs);\n// `bindKaleidoscope`'s reconcile drives it.\nconst applyVideoEffects: ApplyVideoEffects = (track, effects) => {\n const t = track as MediaStreamTrack & WebRTCTrackExtensions;\n if (t.remote) {\n throw new Error('kaleidoscope: cannot apply effects to remote tracks');\n }\n if (typeof t._setVideoEffects !== 'function') {\n throw new Error(\n 'kaleidoscope: track has no _setVideoEffects method (is react-native-webrtc >=124 installed?)',\n );\n }\n const specs = effects.map(toEffectSpec);\n // Deliver the composite's layer stack out-of-band before the \"composite\" name\n // is dispatched. Blur sigma and generative uniforms ride inside each layer's\n // `uniforms`. Guarded: a native build without the compositor lacks the function\n // (and drops the \"composite\" name below if not registered).\n for (const spec of specs) {\n if (spec.name === 'composite') {\n nativeModule().setCompositeLayers?.(serializeCompositeLayers((spec as CompositeSpec).layers));\n }\n }\n // The composite name is book-driven: the prebuild copied the images and native\n // registration installed the one \"composite\" compositor, so it passes the\n // crash-guard. Transforms must be in the static set (always are).\n const mapped = specs.map((spec) => ({\n name: specToNativeName(spec),\n trusted: spec.name === 'composite',\n }));\n const names = mapped\n .filter((m) => m.trusted || NATIVE_REGISTERED_EFFECTS.has(m.name))\n .map((m) => m.name);\n\n // Dedup against the last set applied to this track. Order is significant\n // (effects chain in array order), so the signature preserves it. Skip the\n // native call when nothing changed; the first call for any given set always\n // proceeds (no prior entry).\n const signature = names.join('\\n');\n if (lastAppliedSignatureByTrack.get(track) === signature) {\n return track;\n }\n lastAppliedSignatureByTrack.set(track, signature);\n\n const dropped = mapped\n .filter((m) => !m.trusted && !NATIVE_REGISTERED_EFFECTS.has(m.name))\n .map((m) => m.name);\n if (dropped.length > 0) {\n console.warn(\n `kaleidoscope: dropping effects not registered on this native platform: ${dropped.join(', ')}. ` +\n 'Web has its own registry; this is a native-only filter.',\n );\n }\n // Platforms diverge on how to clear effects:\n // - Android: rn-webrtc 124 has a bug where passing `[]` installs a\n // VideoEffectProcessor with an empty processors list, whose\n // onFrameCaptured then double-releases the input frame (retain once,\n // release twice) and crashes EglRenderer one frame later. The only\n // correct clear is `null`, which takes the upstream else-branch that\n // resets the processor via `videoSource.setVideoProcessor(null)`.\n // - iOS: the upstream Obj-C `_setVideoEffects` method declares\n // `names` as `(nonnull NSArray<NSString *> *)`, so passing `null`\n // violates the React Native bridge's nonnull contract. The supported\n // clear value is `[]`, which iOS's VideoEffectProcessor treats as a\n // passthrough (no double-release bug on this platform).\n // The explicit type annotation is required; without it TS widens the\n // empty-array literal to `never[] | null`.\n const clearValue: ReadonlyArray<string> | null = Platform.OS === 'ios' ? [] : null;\n t._setVideoEffects(names.length === 0 ? clearValue : names);\n return track;\n};\n\n/**\n * Bind a track and a preset book; get the three verbs back\n * (`{ kaleidoscope, transform, mask }`). On native the track is mutated in\n * place, so `controls.track` is the bound track and `onTrack` fires with it\n * after each `kaleidoscope` preset switch and `transform` command. `mask`\n * updates the segmentation edge the per-frame processors read.\n */\nexport const bindKaleidoscope = <P extends KaleidoscopePresetBook>(\n track: MediaStreamTrack,\n options: KaleidoscopeBindOptions<P>,\n): KaleidoscopeBinding<P> => {\n const reconcile: Reconcile = {\n apply: (specs) => applyVideoEffects(track, specs),\n dispose: () => {},\n };\n const setMask: SetMask = (hardness, threshold) => {\n nativeModule().setMaskHardness(hardness);\n nativeModule().setMaskThreshold(threshold);\n };\n // Native has no live per-layer uniform channel yet (Phase B): a patch of the\n // active preset is a no-op here, so the verb's patch path is inert on native\n // until the compositor reads layer-id-keyed overrides. The verb still drives\n // preset switches through reconcile.\n const setLayerUniforms: SetLayerUniforms = () => {};\n // Native re-sends the full layer stack (with baked uniforms) on every preset\n // switch via setCompositeLayers, so there is no stale override to clear; no-op for\n // parity with the inert setLayerUniforms above.\n const resetLayerUniforms: ResetLayerUniforms = () => {};\n return createControls(track, options, reconcile, setMask, setLayerUniforms, resetLayerUniforms);\n};\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AA6BlG,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,2BAA2B,EAC3B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AA6JlD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,sBAAsB,SACxD,gBAAgB,WACd,uBAAuB,CAAC,CAAC,CAAC,KAClC,mBAAmB,CAAC,CAAC,CAmBvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/index.web.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,kBAAkB,EAOxB,MAAM,eAAe,CAAC;AASvB,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAQ/E,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,2BAA2B,EAC3B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAiBlD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,UAC/B,gBAAgB,WACd,aAAa,CAAC,WAAW,CAAC,KAClC,kBA0BF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,sBAAsB,SACxD,gBAAgB,WACd,uBAAuB,CAAC,CAAC,CAAC,KAClC,mBAAmB,CAAC,CAAC,CA4BvB,CAAC","sourcesContent":["// Web entry point (source). This builds to `dist/index.web.js`, which web\n// bundlers resolve through the package's `browser` export condition. With an\n// `exports` map present, that condition supersedes platform-extension\n// resolution; the `.web.ts` suffix here is just our source convention, not what\n// selects this file at consume time.\n//\n// Every visual effect is one composite (a layer stack) or one transform. A\n// composite wires an Insertable-Streams stage that runs the whole layer stack\n// through the compositor and returns a new MediaStreamTrack carrying the\n// transformed frames. Pass the returned track to a `<video>` element or to\n// `RTCRtpSender.replaceTrack(...)`.\n\nimport {\n applyEffectToTrack,\n type DisposablePipeline,\n type FrameTransform,\n makeComposite,\n makeTransform,\n resetLayerUniforms as resetCompositeLayerUniforms,\n setLayerUniforms as setCompositeLayerUniforms,\n tuning,\n} from '../web-driver';\nimport {\n createControls,\n type Reconcile,\n type ResetLayerUniforms,\n type SetLayerUniforms,\n type SetMask,\n} from './kaleidoscope/controls';\nimport { toEffectSpec } from './kaleidoscope/effect';\nimport type { EffectInput, EffectSpec } from './kaleidoscope/effect.types';\nimport type { KaleidoscopeBinding, KaleidoscopeBindOptions } from './kaleidoscope/types';\nimport type { KaleidoscopePresetBook } from './kaleidoscope.preset-book.types';\n\n// The tuning channel (tuning.*) is internal: the mask edge flows from the mask()\n// verb (see bindKaleidoscope). Per-layer uniform tuning flows from the\n// kaleidoscope verb's patch path into the composite compositor's live channel. The\n// old global set* exports are gone; effects are driven by kaleidoscope /\n// transform / mask, not loose setters.\n\nexport type { CatalogImageId } from '../catalog/images';\nexport type {\n AnamorphicLensFlareUniforms,\n BlurUniforms,\n CloudsUniforms,\n CorporateBlobsUniforms,\n FirefliesUniforms,\n GodraysUniforms,\n LayerShaderName,\n LayerShaderOptions,\n LightBeamsAndMotesUniforms,\n NebulaUniforms,\n PatchableShaderName,\n PlasmaUniforms,\n ShaderUniformsMap,\n SimianlightsUniforms,\n UniformControl,\n} from '../catalog/shaders';\nexport {\n ANAMORPHIC_LENSFLARE_CONTROLS,\n BLUR_CONTROLS,\n CLOUDS_CONTROLS,\n CORPORATE_BLOBS_CONTROLS,\n defaultUniforms,\n FIREFLIES_CONTROLS,\n GODRAYS_CONTROLS,\n LIGHT_BEAMS_AND_MOTES_CONTROLS,\n NEBULA_CONTROLS,\n PLASMA_CONTROLS,\n SIMIANLIGHTS_CONTROLS,\n} from '../catalog/shaders';\nexport type {\n CompositeSpec,\n EffectInput,\n EffectName,\n EffectSpec,\n TransformName,\n TransformSpec,\n} from './kaleidoscope/effect.types';\nexport type {\n KaleidoscopeBinding,\n KaleidoscopeBindOptions,\n MaskInput,\n PatchesFor,\n PatchFor,\n TransformInput,\n} from './kaleidoscope/types';\nexport type {\n KaleidoscopeBlendMode,\n KaleidoscopeControls,\n KaleidoscopeLayer,\n KaleidoscopeLayerTarget,\n KaleidoscopePreset,\n KaleidoscopePresetBook,\n KaleidoscopePresetEntry,\n KaleidoscopeTaxonomy,\n} from './kaleidoscope.preset-book.types';\nexport type { RGB } from './lib/primitives.types';\n\nconst specToTransform = (spec: EffectSpec): FrameTransform => {\n switch (spec.name) {\n case 'flip-x':\n case 'flip-y':\n case 'rotate-cw':\n case 'rotate-ccw':\n return makeTransform(spec.name);\n case 'composite':\n // The layer stack runs as a single compositor stage (painter's order,\n // per-layer blend), not a serial chain of replace-stages. Blur, image, and\n // generative layers all live inside it.\n return makeComposite(spec.layers);\n }\n};\n\n/**\n * Like `applyVideoEffects`, but also returns a `dispose()` that tears down every\n * Insertable-Streams stage (stops each generator and aborts each pipe). The\n * LiveKit adapter (`react-native-webrtc-kaleidoscope/livekit`) uses this so a\n * camera flip (restart) or unpublish (destroy) does not leak generators. The\n * page-shared segmenter and WebGL state are module singletons reused across\n * stages, so they are intentionally NOT torn down here.\n */\nexport const applyVideoEffectsDisposable = (\n track: MediaStreamTrack,\n effects: ReadonlyArray<EffectInput>,\n): DisposablePipeline => {\n if (!track || track.kind !== 'video') {\n throw new Error('kaleidoscope: applyVideoEffects requires a video MediaStreamTrack');\n }\n if (effects.length === 0) {\n return { track, dispose: () => {} };\n }\n\n let current = track;\n const disposers: Array<() => void> = [];\n for (const input of effects) {\n const spec = toEffectSpec(input);\n const transform = specToTransform(spec);\n const stage = applyEffectToTrack(current, transform);\n current = stage.track;\n disposers.push(stage.dispose);\n }\n return {\n track: current,\n dispose: () => {\n // Tear down in reverse so downstream stages stop before their sources.\n for (const dispose of disposers.reverse()) {\n dispose();\n }\n },\n };\n};\n\n/**\n * Bind a track and a preset book; get the three verbs back\n * (`{ kaleidoscope, transform, mask }`). Presets live in the consumer's\n * project; these verbs drive them. On web each `kaleidoscope` preset switch and\n * each `transform` command rebuilds the Insertable-Streams pipeline and yields a\n * new output track, so read the live track from `onTrack` (or `controls.track`);\n * the prior pipeline is disposed each command and on `dispose()`. A `kaleidoscope`\n * patch of the active preset and `mask` both update what the running pipeline\n * reads per frame (no rebuild).\n */\nexport const bindKaleidoscope = <P extends KaleidoscopePresetBook>(\n track: MediaStreamTrack,\n options: KaleidoscopeBindOptions<P>,\n): KaleidoscopeBinding<P> => {\n let prevDispose = (): void => {};\n const reconcile: Reconcile = {\n apply: (specs) => {\n // Rebuild the whole pipeline from the base track each command, disposing\n // the previous one (generators/pipes) so stages don't leak.\n prevDispose();\n const { track: out, dispose } = applyVideoEffectsDisposable(track, specs);\n prevDispose = dispose;\n return out;\n },\n dispose: () => prevDispose(),\n };\n const setMask: SetMask = (hardness, threshold) => {\n tuning.setMaskHardness(hardness);\n tuning.setMaskThreshold(threshold);\n };\n // Live per-layer uniform channel: a patch of the active preset writes here\n // (keyed by layer id) and the running compositor merges it each frame.\n const setLayerUniforms: SetLayerUniforms = (id, uniforms) => {\n setCompositeLayerUniforms(id, uniforms);\n };\n // A preset switch drops every live override so reused layer ids revert to the\n // new preset's baked uniforms (see createControls).\n const resetLayerUniforms: ResetLayerUniforms = () => {\n resetCompositeLayerUniforms();\n };\n return createControls(track, options, reconcile, setMask, setLayerUniforms, resetLayerUniforms);\n};\n"]}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/index.web.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,kBAAkB,EAOxB,MAAM,eAAe,CAAC;AASvB,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAQ/E,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,2BAA2B,EAC3B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,8BAA8B,EAC9B,eAAe,EACf,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAiBlD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,UAC/B,gBAAgB,WACd,aAAa,CAAC,WAAW,CAAC,KAClC,kBA0BF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,sBAAsB,SACxD,gBAAgB,WACd,uBAAuB,CAAC,CAAC,CAAC,KAClC,mBAAmB,CAAC,CAAC,CA4BvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/controls.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAsB,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACpG,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAkB,MAAM,SAAS,CAAC;AAE5F,uEAAuE;AACvE,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,gBAAgB,CAAC;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,KAC3D,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC;AAiB5C,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,sBAAsB,aAClD,gBAAgB,wBACL,uBAAuB,CAAC,CAAC,CAAC,aACrC,SAAS,WACX,OAAO,oBACE,gBAAgB,sBACd,kBAAkB,KACrC,mBAAmB,CAAC,CAAC,CA0DvB,CAAC","sourcesContent":["// The three-verb controls: shared composite-state machine, platform-agnostic.\n//\n// Holds the art effect (one composite) and the transform op list, reconciles\n// them into an ordered EffectSpec array (art FIRST so segmentation sees the\n// upright frame, transform LAST so it reorients the finished composite), and\n// applies them through an injected platform `reconcile`. Web rebuilds the\n// pipeline and yields a new track (disposing the prior one); native mutates in\n// place. `mask` writes the segmentation edge through an injected `setMask`; the\n// running composite reads it per frame, so it needs no rebuild.\n//\n// The art verb is rebuild-aware: switching to a different preset rebuilds, but\n// patching the currently-active preset routes through an injected live\n// layer-uniform channel (`setLayerUniforms`, keyed by layer id) so a slider drag\n// updates the running composite without a rebuild.\n\nimport type { KaleidoscopePreset, KaleidoscopePresetBook } from '../kaleidoscope.preset-book.types';\nimport type { EffectSpec, TransformName } from './effect.types';\nimport { compositeToEffectSpec } from './shader-to-spec';\nimport type { KaleidoscopeBinding, KaleidoscopeBindOptions, TransformInput } from './types';\n\n/** Apply the ordered specs to the base track and return the output. */\nexport type Reconcile = {\n apply: (specs: ReadonlyArray<EffectSpec>) => MediaStreamTrack;\n dispose: () => void;\n};\n\n/** Write the segmentation mask edge (platform tuning channel). */\nexport type SetMask = (hardness: number, threshold: number) => void;\n\n/**\n * Write a live per-layer uniform override (platform tuning channel), keyed by\n * layer id. The running composite merges these over the layer's baked uniforms\n * each frame, with no rebuild. Mirrors `setMask`.\n */\nexport type SetLayerUniforms = (\n id: string,\n uniforms: Readonly<Record<string, number | readonly number[]>>,\n) => void;\n\n/**\n * Drop every live per-layer override (platform tuning channel). A preset switch\n * calls this so a reused layer id reverts to the new preset's baked uniforms\n * rather than inheriting a stale override from the prior preset.\n */\nexport type ResetLayerUniforms = () => void;\n\n// Decompose an absolute transform into the discrete ops the pipeline already\n// runs (reused on web and native). Flips first, then rotation; rotation snaps to\n// the nearest 90°. 180° is two CW steps; 270° is one CCW step.\nconst decomposeTransform = (t?: TransformInput): EffectSpec[] => {\n if (!t) return [];\n const names: TransformName[] = [];\n if (t.flip?.x) names.push('flip-x');\n if (t.flip?.y) names.push('flip-y');\n const deg = (((Math.round((t.rotate ?? 0) / 90) * 90) % 360) + 360) % 360;\n if (deg === 90) names.push('rotate-cw');\n else if (deg === 180) names.push('rotate-cw', 'rotate-cw');\n else if (deg === 270) names.push('rotate-ccw');\n return names.map((name) => ({ name }));\n};\n\nexport const createControls = <P extends KaleidoscopePresetBook>(\n baseTrack: MediaStreamTrack,\n { presets, onTrack }: KaleidoscopeBindOptions<P>,\n reconcile: Reconcile,\n setMask: SetMask,\n setLayerUniforms: SetLayerUniforms,\n resetLayerUniforms: ResetLayerUniforms,\n): KaleidoscopeBinding<P> => {\n let art: EffectSpec | null = null;\n let transformOps: EffectSpec[] = [];\n let current = baseTrack;\n // The id of the active preset (null when cleared). A patch of THIS id routes\n // through the live channel; any other cmd rebuilds.\n let activeId: keyof P | null = null;\n\n const apply = (): void => {\n const specs: EffectSpec[] = [];\n if (art) specs.push(art);\n specs.push(...transformOps);\n current = reconcile.apply(specs);\n onTrack?.(current);\n };\n\n return {\n kaleidoscope: (\n cmd: keyof P | null,\n patches?: ReadonlyArray<{\n readonly id: string;\n readonly uniforms: Readonly<Record<string, number | readonly number[]>>;\n }>,\n ) => {\n // Patch the currently-active preset: route through the live no-rebuild\n // channel, keyed by layer id. The `shader` field on a patch is only for\n // narrowing; the channel resolves by `id`.\n if (cmd != null && cmd === activeId && patches && patches.length > 0) {\n for (const patch of patches) {\n setLayerUniforms(patch.id, patch.uniforms);\n }\n return;\n }\n // Switch the preset (or clear): rebuild. Drop every live override first so\n // a reused layer id (e.g. 'blur', shared by the low/medium/high blur\n // presets) takes the new preset's baked uniforms instead of carrying a\n // stale slider override across. A transform rebuild does NOT pass through\n // here, so slider tweaks survive flips/rotations of the active preset.\n activeId = cmd;\n art = cmd == null ? null : compositeToEffectSpec(presets[cmd] as KaleidoscopePreset);\n resetLayerUniforms();\n apply();\n },\n transform: (t) => {\n transformOps = decomposeTransform(t);\n apply();\n },\n mask: (m) => {\n // Updates the edge the per-frame composite reads; no pipeline rebuild.\n setMask(m.hardness, m.threshold);\n },\n get track() {\n return current;\n },\n dispose: () => {\n reconcile.dispose();\n },\n };\n};\n"]}
1
+ {"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/controls.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAsB,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACpG,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAkB,MAAM,SAAS,CAAC;AAE5F,uEAAuE;AACvE,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,gBAAgB,CAAC;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,KAC3D,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC;AAiB5C,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,sBAAsB,aAClD,gBAAgB,wBACL,uBAAuB,CAAC,CAAC,CAAC,aACrC,SAAS,WACX,OAAO,oBACE,gBAAgB,sBACd,kBAAkB,KACrC,mBAAmB,CAAC,CAAC,CA0DvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,2EAA2E;AAC3E,eAAO,MAAM,YAAY,UAAW,WAAW,KAAG,UACmB,CAAC","sourcesContent":["import type { EffectInput, EffectSpec } from './effect.types';\n\n/** Normalize an `EffectInput` (bare name or spec) into an `EffectSpec`. */\nexport const toEffectSpec = (input: EffectInput): EffectSpec =>\n typeof input === 'string' ? ({ name: input } as EffectSpec) : input;\n"]}
1
+ {"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,2EAA2E;AAC3E,eAAO,MAAM,YAAY,UAAW,WAAW,KAAG,UACmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"effect.types.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/effect.types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,KAChC,gBAAgB,CAAC","sourcesContent":["// The runtime effect contract: the lower-level spec the drivers consume.\n//\n// A consumer never writes these; they sit below the preset-book vocabulary. A\n// `KaleidoscopePreset` projects into a `CompositeSpec` (see shader-to-spec), the\n// transform verb produces `TransformSpec`s, and `applyVideoEffects` takes the\n// union. Each driver (android / ios / web-driver) renders this same spec.\n\nimport type { KaleidoscopeLayer } from '../kaleidoscope.preset-book.types';\n\n/**\n * Geometric reorientation of the frame: an axis flip or a 90-degree rotation.\n * The operation IS the effect name (parameterless), compatible with the\n * bare-string input form and the flat-string native registry. On web these run\n * in display space; native pipelines correct for the camera buffer's rotation.\n */\nexport type TransformName = 'flip-x' | 'flip-y' | 'rotate-cw' | 'rotate-ccw';\n\nexport type TransformSpec = {\n readonly name: TransformName;\n};\n\n/**\n * A composed effect: an ordered painter's stack of layers, run by the one\n * compositor as a single stage. Layer 0 is the base; later layers blend over it.\n * Every former singleton (blur, image, generative shader) is a layer inside it.\n */\nexport type CompositeSpec = {\n readonly name: 'composite';\n readonly layers: ReadonlyArray<KaleidoscopeLayer>;\n};\n\nexport type EffectSpec = CompositeSpec | TransformSpec;\n\n/**\n * The discriminant. Useful for typed switch statements and the bare-string call\n * shape for the parameterless transforms (`applyVideoEffects(track, ['flip-x'])`).\n */\nexport type EffectName = EffectSpec['name'];\n\n/**\n * `applyVideoEffects` accepts either a bare transform name (the parameterless\n * transforms) or a full `EffectSpec` object (a composite or a transform). The\n * bare-string form is `TransformName`, NOT `EffectName`: a bare `'composite'`\n * would normalize to a layerless spec and crash the compositor, so the type\n * rejects it. A composite must always arrive as a full `CompositeSpec`.\n */\nexport type EffectInput = EffectSpec | TransformName;\n\n/**\n * Apply zero or more effects to a local `MediaStreamTrack`.\n *\n * - Native: thin facade over `track._setVideoEffects(names)` from\n * `react-native-webrtc`. Returns the same track reference; mutation is in place.\n * - Web: builds an Insertable-Streams pipeline and returns a NEW track carrying\n * the transformed frames.\n *\n * Throws on remote tracks, unknown effect names, missing platform capabilities,\n * or non-video tracks.\n */\nexport type ApplyVideoEffects = (\n track: MediaStreamTrack,\n effects: ReadonlyArray<EffectInput>,\n) => MediaStreamTrack;\n"]}
1
+ {"version":3,"file":"effect.types.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/effect.types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,KAChC,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"shader-to-spec.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/shader-to-spec.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,eAAO,MAAM,qBAAqB,cAAe,kBAAkB,KAAG,UAGpE,CAAC","sourcesContent":["// Translate a book composite into the lower-level EffectSpec the primitive\n// `applyVideoEffects` consumes. This is the seam between the book vocabulary the\n// consumer sees and the effect the pipeline runs; the controls own the active\n// composite and reconcile through here.\n//\n// Every book entry is a composite (an ordered layer stack), so this is a thin\n// projection: the layers already carry their own ids, sources, uniforms, and\n// blend. Transforms are not book entries; the transform verb handles them.\n\nimport type { KaleidoscopePreset } from '../kaleidoscope.preset-book.types';\nimport type { EffectSpec } from './effect.types';\n\nexport const compositeToEffectSpec = (composite: KaleidoscopePreset): EffectSpec => ({\n name: 'composite',\n layers: composite.layers,\n});\n"]}
1
+ {"version":3,"file":"shader-to-spec.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/shader-to-spec.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,eAAO,MAAM,qBAAqB,cAAe,kBAAkB,KAAG,UAGpE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/types.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,mBAAmB,CAAC;CACtD,GACG;IAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAA;CAAE,GACpE,KAAK,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,sBAAsB,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,aAAa,CACzF,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CACjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG;IACtB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,sBAAsB,IAAI;IACtE,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,KAAK,mBAAmB,CAAC,CAAC,SAAS,sBAAsB,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAC7E,GAAG,EAAE,CAAC,GAAG,IAAI,EACb,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,KACvB,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,sBAAsB;IACnE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B","sourcesContent":["// The three-verb surface: types.\n//\n// Bind a track and a preset book once; get three typed verbs back:\n// - kaleidoscope(cmd, patches?) the art axis: which composite (layer stack)\n// fills the frame. cmd is a preset id from the book (narrowed), or null to\n// clear. patches optionally merge per-layer uniform overrides (addressed by\n// layer id); patching the currently-active preset routes through the live\n// no-rebuild channel, so sliders stay smooth.\n// - transform(t?) the geometry axis: absolute flips + 90° rotation.\n// - mask(m) the segmentation edge shared by every art effect.\n//\n// Shaders live in the library; consumers add presets (composites) over them,\n// never new shaders. Per shader-world convention, numeric uniforms are\n// normalized 0..1 where practical; ranges are documented in JSDoc as hints for\n// IntelliSense and tooling, not enforced at runtime (validation is userland).\n\nimport type { PatchableShaderName, ShaderUniformsMap } from '../../catalog/shaders';\nimport type { KaleidoscopePresetBook } from '../kaleidoscope.preset-book.types';\n\n/**\n * A live per-layer uniform override for ONE layer, derived from the layer's own\n * type: `id` is the layer's id, `uniforms` is `Partial` of the shader's uniform\n * type (re-indexed from `ShaderUniformsMap` by the layer's literal `shader`).\n * Non-tunable layers (`image`, `direct`) distribute to `never`, so they cannot be\n * patched. The runtime resolves by `id`; the shader is never sent on the wire.\n */\nexport type PatchFor<L> = L extends {\n readonly id: infer I extends string;\n readonly shader: infer S extends PatchableShaderName;\n}\n ? { readonly id: I; readonly uniforms: Partial<ShaderUniformsMap[S]> }\n : never;\n\n/**\n * The patches `kaleidoscope` accepts for preset `K` in book `P`: per-layer\n * overrides, each addressed by one of that preset's tunable layer ids and typed\n * by that layer's shader. At a literal `cmd` call site this narrows to the\n * preset's ids/uniforms; with a variable `cmd` it widens to the book-wide union\n * and is runtime-checked by id.\n */\nexport type PatchesFor<P extends KaleidoscopePresetBook, K extends keyof P> = ReadonlyArray<\n PatchFor<P[K]['layers'][number]>\n>;\n\n/**\n * Absolute, stateless geometric transform. Every call is the full desired state\n * from the identity orientation: re-passing is the caller's responsibility, and\n * `transform()` (or `transform({})`) resets to identity. Rotation snaps to the\n * nearest 90°; arbitrary angles and offset are a later step.\n */\nexport type TransformInput = {\n /** Mirror flips about each axis. */\n readonly flip?: { readonly x?: boolean; readonly y?: boolean };\n /** Clockwise rotation in degrees; snapped to the nearest 90 (0/90/180/270). */\n readonly rotate?: number;\n};\n\n/** The segmentation mask edge, shared by every art effect (not transforms). */\nexport type MaskInput = {\n /** Edge hardness, 0..1. 0 = soft halo, 1 = near-step. */\n readonly hardness: number;\n /** Edge threshold, 0..1. Higher rejects low-confidence (chair-edge) pixels. */\n readonly threshold: number;\n};\n\nexport type KaleidoscopeBindOptions<P extends KaleidoscopePresetBook> = {\n /** The consumer's preset book. Declare it `as const satisfies KaleidoscopePresetBook`. */\n readonly presets: P;\n /**\n * Called with the live output track after every art/transform command. On web\n * each command yields a NEW MediaStreamTrack (the pipeline is rebuilt); on\n * native the same track is mutated in place and passed back.\n */\n readonly onTrack?: (track: MediaStreamTrack) => void;\n};\n\n/**\n * The art verb: select a composite by id (rebuilding the pipeline), or clear it\n * with `null`. When `cmd` is the currently-active preset id and `patches` is\n * given, the patches merge through the live no-rebuild uniform channel (keyed by\n * layer id) instead of rebuilding, so a slider drag stays smooth.\n */\ntype KaleidoscopeCommand<P extends KaleidoscopePresetBook> = <K extends keyof P>(\n cmd: K | null,\n patches?: PatchesFor<P, K>,\n) => void;\n\n/**\n * The three verbs for one bound track and book, plus the live track and a\n * teardown. `kaleidoscope` (preset switch) and `transform` rebuild the composite\n * (web yields a new track via onTrack); a `kaleidoscope` patch of the active\n * preset and `mask` both update what the running composite reads each frame, so\n * they need no rebuild.\n */\nexport interface KaleidoscopeBinding<P extends KaleidoscopePresetBook> {\n readonly kaleidoscope: KaleidoscopeCommand<P>;\n readonly transform: (t?: TransformInput) => void;\n readonly mask: (m: MaskInput) => void;\n readonly track: MediaStreamTrack;\n readonly dispose: () => void;\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/kaleidoscope/types.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,mBAAmB,CAAC;CACtD,GACG;IAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAA;CAAE,GACpE,KAAK,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,sBAAsB,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,aAAa,CACzF,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CACjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG;IACtB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,sBAAsB,IAAI;IACtE,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,KAAK,mBAAmB,CAAC,CAAC,SAAS,sBAAsB,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAC7E,GAAG,EAAE,CAAC,GAAG,IAAI,EACb,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,KACvB,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,sBAAsB;IACnE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"kaleidoscope.preset-book.types.d.ts","sourceRoot":"","sources":["../../src/kaleidoscope.preset-book.types.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClG,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;QACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC;KAC/D,KAAK,IAAI,CAAC;IACX,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1D;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG,SAAS,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,IAAI,eAAe,GAAG;QAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACnB,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;QAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC;KACxC,GAAG,kBAAkB,CAAC,CAAC,CAAC;CAC1B,CAAC,eAAe,CAAC,CAAC;AAEnB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAClD;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CACzD,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAElF;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAkB,CAAC","sourcesContent":["// Point of entry #1: the preset-book vocabulary. The base of the pyramid.\n//\n// The complete, declarative lexicon a consumer writes in their\n// `kaleidoscope.preset-book.ts`, and the single source of truth for it: any file\n// that needs one of these types imports it from here. A preset book is a record\n// of named presets; a preset is a name + taxonomy + (optional) thumbnail +\n// (optional) controls + an ordered stack of layers; a layer names a `shader` from\n// the catalog and carries that shader's options.\n//\n// What is NOT here: the runtime effect contract (`CompositeSpec` etc. live in\n// `kaleidoscope/effect.types`; the consumer never writes those), the shader\n// option catalog (`LayerShaderOptions` lives with the shaders it describes), and\n// generic primitives (`RGB` in `lib/primitives.types`).\n\nimport type { ComponentType } from 'react';\nimport type { LayerShaderName, LayerShaderOptions } from '../catalog/shaders';\n\n/**\n * A preset's place in the picker: an ordered path of group names, deepest last.\n * One level (`['Backgrounds']`) is a flat group; two (`['Worlds', 'Wizard Tower']`)\n * is group then subgroup. The list IS the depth, so a group can't be set without\n * its parent. Extend to three levels by adding a member here when needed.\n */\nexport type KaleidoscopeTaxonomy = readonly [string] | readonly [string, string];\n\n/**\n * What the Tuner passes a preset's `controls` component: the per-layer baked\n * uniforms keyed by layer id, and the single shared `onPatch` every layer's\n * `ControlForm` emits through (the `id` discriminates). Intentionally loose at\n * this boundary (heterogeneous layers); per-shader typing is recovered inside\n * each `<Shader>Controls` via `makeControls<U>()`.\n */\nexport type KaleidoscopeControls = {\n readonly uniforms: Readonly<Record<string, Readonly<Record<string, number | readonly number[]>>>>;\n readonly onPatch: (patch: {\n readonly id: string;\n readonly uniforms: Record<string, number | readonly number[]>;\n }) => void;\n readonly disabled?: boolean;\n};\n\n/** How a layer blends over the layers beneath it (painter's order). */\nexport type KaleidoscopeBlendMode = 'normal' | 'additive';\n\n/**\n * Which part of the frame a layer applies to. Omit for `'background'` (the\n * accumulated stack so far); `'subject'` stencils the layer to the segmented\n * person. The same shader can run on either target.\n */\nexport type KaleidoscopeLayerTarget = 'background' | 'subject';\n\n/**\n * One layer in a preset: a `shader` (from the catalog) applied to a `target`,\n * with optional `blend`. The `shader` is the discriminant and carries that\n * shader's required fields (a discriminated union over the catalog's\n * `LayerShaderOptions`). `id` is required and unique within one preset; it is the\n * address a patch resolves against, and for an `image` layer it doubles as the\n * bundled-WebP basename the native facade sends as the image `source`.\n */\nexport type KaleidoscopeLayer = {\n readonly [S in LayerShaderName]: {\n readonly id: string;\n readonly shader: S;\n readonly target?: KaleidoscopeLayerTarget;\n readonly blend?: KaleidoscopeBlendMode;\n } & LayerShaderOptions[S];\n}[LayerShaderName];\n\n/**\n * One preset: an ordered painter's stack of layers under one book name, plus\n * display metadata. `kaleidoscope(id)` runs the whole stack as one composite\n * through the one compositor.\n */\nexport type KaleidoscopePreset = {\n /** Human-readable label for the picker. */\n readonly name: string;\n /** Grouping path for the picker, root first (e.g. `['Worlds', 'Wizard Tower']`). */\n readonly taxonomy: KaleidoscopeTaxonomy;\n /**\n * Optional thumbnail for the picker rail.\n * - `string`: a resolved URL (web) or native preset name routed through the\n * image resolver.\n * - `number`: a Metro asset module id (`require('./foo.webp')`), consumed\n * directly by `<Image source={number}>`.\n */\n readonly thumbnail?: string | number;\n /** The painter's stack, back to front. Each layer's `id` is unique here. */\n readonly layers: ReadonlyArray<KaleidoscopeLayer>;\n /**\n * Optional tuning component the Tuner renders, mounting a `ControlForm` +\n * `ControlSection` per tunable layer. `undefined` renders nothing. The\n * `import type` keeps presets runtime-React-free.\n */\n readonly controls?: ComponentType<KaleidoscopeControls>;\n};\n\n/** The consumer's book: a flat record of presets keyed by id. */\nexport type KaleidoscopePresetBook = Readonly<Record<string, KaleidoscopePreset>>;\n\n/**\n * A materialized book entry: a `KaleidoscopePreset` plus the `id` it was keyed\n * by. What the picker and tuner iterate (the book is keyed; this carries the key\n * inline so a flattened list keeps its identity).\n */\nexport type KaleidoscopePresetEntry = { readonly id: string } & KaleidoscopePreset;\n"]}
1
+ {"version":3,"file":"kaleidoscope.preset-book.types.d.ts","sourceRoot":"","sources":["../../src/kaleidoscope.preset-book.types.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClG,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;QACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC;KAC/D,KAAK,IAAI,CAAC;IACX,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1D;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG,SAAS,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,IAAI,eAAe,GAAG;QAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACnB,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;QAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC;KACxC,GAAG,kBAAkB,CAAC,CAAC,CAAC;CAC1B,CAAC,eAAe,CAAC,CAAC;AAEnB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAClD;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CACzD,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAElF;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.types.d.ts","sourceRoot":"","sources":["../../../src/lib/primitives.types.ts"],"names":[],"mappings":"AAGA,yCAAyC;AACzC,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC","sourcesContent":["// Generic, cross-everything primitives. No domain or platform coupling, so they\n// are safe in any flow (runtime, prebuild, components).\n\n/** RGB color, each channel in [0, 1]. */\nexport type RGB = readonly [number, number, number];\n"]}
1
+ {"version":3,"file":"primitives.types.d.ts","sourceRoot":"","sources":["../../../src/lib/primitives.types.ts"],"names":[],"mappings":"AAGA,yCAAyC;AACzC,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"test-id.d.ts","sourceRoot":"","sources":["../../../src/lib/test-id.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,WAAW,QAAQ,CAAC;AAEjC,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,kBAA6B,CAAC;AAClE,eAAO,MAAM,kBAAkB,aAAwB,CAAC;AAExD;;;GAGG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAatC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAElE;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEnD","sourcesContent":["// The test-id grammar: pure builders for the deterministic, semantic\n// `accessibilityIdentifier`s every interactive kit leaf carries. One source of\n// truth so a Maestro flow can address a control by a stable id instead of its\n// brittle visible text. No React/RN import; this is plain data.\n//\n// Shape (dot-delimited, rooted at `kld`):\n// control field kld.<preset>.<layer>.<uniform> (+ .r/.g/.b per channel)\n// transform kld.transform.rotate-90 | flip-x\n// mask kld.mask.hardness\n// family tab kld.family.<slug>\n// category item kld.category.<slug-family>.<slug-category>\n// preset tile kld.preset.<id>\n//\n// preset/layer/uniform ids and book keys are already stable tokens and are used\n// verbatim; only display strings (family, category) are slugged.\n\nexport const TESTID_ROOT = 'kld';\n\n/** Default static prefixes for the two control families that have no preset scope. */\nexport const TRANSFORM_TESTID_PREFIX = `${TESTID_ROOT}.transform`;\nexport const MASK_TESTID_PREFIX = `${TESTID_ROOT}.mask`;\n\n/**\n * Normalize a display string to a stable id segment: lowercase, spaces and\n * underscores to `-`, drop anything outside `[a-z0-9-]`, collapse repeats, trim.\n */\nexport function slug(s: string): string {\n return (\n s\n .trim()\n .toLowerCase()\n .replace(/[\\s_]+/g, '-')\n .replace(/[^a-z0-9-]+/g, '')\n .replace(/-+/g, '-')\n // Runs are collapsed to a single '-' above, so each edge has at most one\n // hyphen; trim one char (no quantifier) instead of '-+$', whose backtracking\n // is polynomial on a long hyphen run (ReDoS, code-scanning alert #9).\n .replace(/^-|-$/g, '')\n );\n}\n\n/**\n * The scope a ControlForm's fields hang off: `kld.<preset>.<layer>`, or\n * `kld.<layer>` when rendered standalone (no Tuner provides a preset).\n */\nexport function controlScope(presetId: string | null, layerId: string): string {\n return presetId ? `${TESTID_ROOT}.${presetId}.${layerId}` : `${TESTID_ROOT}.${layerId}`;\n}\n\n/** One field's id within its form scope: `<scope>.<uniform>`. */\nexport function fieldTestId(scope: string, uniform: string): string {\n return `${scope}.${uniform}`;\n}\n\n/** A rotation button: `<prefix>.rotate-<deg>`. */\nexport function rotateTestId(prefix: string, deg: number): string {\n return `${prefix}.rotate-${deg}`;\n}\n\n/** A flip toggle: `<prefix>.flip-<axis>`. */\nexport function flipTestId(prefix: string, axis: 'x' | 'y'): string {\n return `${prefix}.flip-${axis}`;\n}\n\n/** A family tab: `kld.family.<slug>`. */\nexport function familyTestId(family: string): string {\n return `${TESTID_ROOT}.family.${slug(family)}`;\n}\n\n/**\n * A category menu item, qualified by its family so the same label under two\n * families does not collide: `kld.category.<slug-family>.<slug-category>`.\n */\nexport function categoryTestId(family: string, category: string): string {\n return `${TESTID_ROOT}.category.${slug(family)}.${slug(category)}`;\n}\n\n/** A preset tile: `kld.preset.<id>` (id is the book key, already a stable token). */\nexport function presetTileTestId(id: string): string {\n return `${TESTID_ROOT}.preset.${id}`;\n}\n"]}
1
+ {"version":3,"file":"test-id.d.ts","sourceRoot":"","sources":["../../../src/lib/test-id.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,WAAW,QAAQ,CAAC;AAEjC,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,kBAA6B,CAAC;AAClE,eAAO,MAAM,kBAAkB,aAAwB,CAAC;AAExD;;;GAGG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAatC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAElE;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -1,5 +1,7 @@
1
1
  import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';
2
2
  import type { EffectInput } from './kaleidoscope/effect.types';
3
+ export { setMaskTuning } from '../web-driver/tuning';
4
+ export type { MaskInput } from './kaleidoscope/types';
3
5
  /**
4
6
  * A LiveKit `TrackProcessor` that applies Kaleidoscope video effects to a local
5
7
  * camera track. Construct it with the same effect inputs `applyVideoEffects`
@@ -1 +1 @@
1
- {"version":3,"file":"livekit.d.ts","sourceRoot":"","sources":["../../src/livekit.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IAC5E,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,eAAe,CAA6B;IAEpD,YAAY,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,EAE9C;IAEK,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlE;IAEK,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAI7B;CACF","sourcesContent":["// Opt-in LiveKit adapter, exported from the `react-native-webrtc-kaleidoscope/livekit`\n// subpath. It wraps the web Insertable-Streams pipeline in LiveKit's\n// TrackProcessor interface so consumers do not have to hand-write the glue.\n//\n// Why this exists: the core web API is `applyVideoEffects(track) -> newTrack`,\n// which assumes the caller owns the RTCRtpSender and calls replaceTrack itself.\n// LiveKit owns the sender, so consumers go through `track.setProcessor(...)`\n// instead. This adapter is that bridge:\n//\n// import { KaleidoscopeProcessor } from 'react-native-webrtc-kaleidoscope/livekit';\n// await localVideoTrack.setProcessor(\n// new KaleidoscopeProcessor([{ name: 'composite', layers: [{ id: 'you', shader: 'direct', target: 'subject' }] }]),\n// );\n//\n// `livekit-client` is an OPTIONAL peer dependency: the import below is type-only\n// (erased at build), so the published JS has no runtime dependency on LiveKit\n// and the agnostic core stays decoupled. Only consumers that import this subpath\n// need livekit-client installed (they already have it).\n//\n// This adapter is web-only: it builds on MediaStreamTrackProcessor /\n// MediaStreamTrackGenerator (Insertable Streams), which exist in Chromium-based\n// browsers. On unsupported environments the underlying pipeline throws a typed\n// error at init().\n\nimport type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';\nimport { applyVideoEffectsDisposable } from './index.web';\nimport type { EffectInput } from './kaleidoscope/effect.types';\n\n/**\n * A LiveKit `TrackProcessor` that applies Kaleidoscope video effects to a local\n * camera track. Construct it with the same effect inputs `applyVideoEffects`\n * accepts (bare transform names like `'flip-x'`, or full `EffectSpec` composite\n * objects), then pass it to `localTrack.setProcessor(processor)`.\n *\n * `restart` (camera flip / source change) and `destroy` (unpublish) tear down\n * the prior Insertable-Streams pipeline, so repeated flips do not leak\n * generators. The page-shared segmenter and WebGL state are module singletons\n * reused across pipelines, so they are intentionally retained.\n */\nexport class KaleidoscopeProcessor implements TrackProcessor<Track.Kind.Video> {\n readonly name = 'kaleidoscope';\n processedTrack?: MediaStreamTrack;\n\n private readonly effects: ReadonlyArray<EffectInput>;\n private disposePipeline: (() => void) | null = null;\n\n constructor(effects: ReadonlyArray<EffectInput>) {\n this.effects = effects;\n }\n\n async init(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n const { track, dispose } = applyVideoEffectsDisposable(opts.track, this.effects);\n this.processedTrack = track;\n this.disposePipeline = dispose;\n }\n\n async restart(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n await this.destroy();\n await this.init(opts);\n }\n\n async destroy(): Promise<void> {\n this.disposePipeline?.();\n this.disposePipeline = null;\n this.processedTrack = undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"livekit.d.ts","sourceRoot":"","sources":["../../src/livekit.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAM/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IAC5E,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,eAAe,CAA6B;IAEpD,YAAY,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,EAE9C;IAEK,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlE;IAEK,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAI7B;CACF"}
@@ -23,8 +23,14 @@
23
23
  // browsers. On unsupported environments the underlying pipeline throws a typed
24
24
  // error at init().
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.KaleidoscopeProcessor = void 0;
26
+ exports.KaleidoscopeProcessor = exports.setMaskTuning = void 0;
27
27
  const index_web_1 = require("./index.web");
28
+ // The first-class mask surface for the processor path (the twin of the
29
+ // binding's `mask` verb; see the implementation's doc in web-driver/tuning.ts).
30
+ // LiveKit owns the RTCRtpSender, so consumers here have no binding to call
31
+ // `mask` on; this export is how they drive the page-shared mask edge.
32
+ var tuning_1 = require("../web-driver/tuning");
33
+ Object.defineProperty(exports, "setMaskTuning", { enumerable: true, get: function () { return tuning_1.setMaskTuning; } });
28
34
  /**
29
35
  * A LiveKit `TrackProcessor` that applies Kaleidoscope video effects to a local
30
36
  * camera track. Construct it with the same effect inputs `applyVideoEffects`
@@ -1 +1 @@
1
- {"version":3,"file":"livekit.js","sourceRoot":"","sources":["../../src/livekit.ts"],"names":[],"mappings":";AAAA,uFAAuF;AACvF,qEAAqE;AACrE,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,wCAAwC;AACxC,EAAE;AACF,sFAAsF;AACtF,wCAAwC;AACxC,wHAAwH;AACxH,OAAO;AACP,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,iFAAiF;AACjF,wDAAwD;AACxD,EAAE;AACF,qEAAqE;AACrE,gFAAgF;AAChF,+EAA+E;AAC/E,mBAAmB;;;AAGnB,2CAA0D;AAG1D;;;;;;;;;;GAUG;AACH;IACW,IAAI,GAAG,cAAc,CAAC;IAC/B,cAAc,CAAoB;IAEjB,OAAO,CAA6B;IAC7C,eAAe,GAAwB,IAAI,CAAC;IAEpD,YAAY,OAAmC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAwC;QACjD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAA,uCAA2B,EAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjF,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAwC;QACpD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;IAClC,CAAC;CACF","sourcesContent":["// Opt-in LiveKit adapter, exported from the `react-native-webrtc-kaleidoscope/livekit`\n// subpath. It wraps the web Insertable-Streams pipeline in LiveKit's\n// TrackProcessor interface so consumers do not have to hand-write the glue.\n//\n// Why this exists: the core web API is `applyVideoEffects(track) -> newTrack`,\n// which assumes the caller owns the RTCRtpSender and calls replaceTrack itself.\n// LiveKit owns the sender, so consumers go through `track.setProcessor(...)`\n// instead. This adapter is that bridge:\n//\n// import { KaleidoscopeProcessor } from 'react-native-webrtc-kaleidoscope/livekit';\n// await localVideoTrack.setProcessor(\n// new KaleidoscopeProcessor([{ name: 'composite', layers: [{ id: 'you', shader: 'direct', target: 'subject' }] }]),\n// );\n//\n// `livekit-client` is an OPTIONAL peer dependency: the import below is type-only\n// (erased at build), so the published JS has no runtime dependency on LiveKit\n// and the agnostic core stays decoupled. Only consumers that import this subpath\n// need livekit-client installed (they already have it).\n//\n// This adapter is web-only: it builds on MediaStreamTrackProcessor /\n// MediaStreamTrackGenerator (Insertable Streams), which exist in Chromium-based\n// browsers. On unsupported environments the underlying pipeline throws a typed\n// error at init().\n\nimport type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';\nimport { applyVideoEffectsDisposable } from './index.web';\nimport type { EffectInput } from './kaleidoscope/effect.types';\n\n/**\n * A LiveKit `TrackProcessor` that applies Kaleidoscope video effects to a local\n * camera track. Construct it with the same effect inputs `applyVideoEffects`\n * accepts (bare transform names like `'flip-x'`, or full `EffectSpec` composite\n * objects), then pass it to `localTrack.setProcessor(processor)`.\n *\n * `restart` (camera flip / source change) and `destroy` (unpublish) tear down\n * the prior Insertable-Streams pipeline, so repeated flips do not leak\n * generators. The page-shared segmenter and WebGL state are module singletons\n * reused across pipelines, so they are intentionally retained.\n */\nexport class KaleidoscopeProcessor implements TrackProcessor<Track.Kind.Video> {\n readonly name = 'kaleidoscope';\n processedTrack?: MediaStreamTrack;\n\n private readonly effects: ReadonlyArray<EffectInput>;\n private disposePipeline: (() => void) | null = null;\n\n constructor(effects: ReadonlyArray<EffectInput>) {\n this.effects = effects;\n }\n\n async init(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n const { track, dispose } = applyVideoEffectsDisposable(opts.track, this.effects);\n this.processedTrack = track;\n this.disposePipeline = dispose;\n }\n\n async restart(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n await this.destroy();\n await this.init(opts);\n }\n\n async destroy(): Promise<void> {\n this.disposePipeline?.();\n this.disposePipeline = null;\n this.processedTrack = undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"livekit.js","sourceRoot":"","sources":["../../src/livekit.ts"],"names":[],"mappings":";AAAA,uFAAuF;AACvF,qEAAqE;AACrE,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,wCAAwC;AACxC,EAAE;AACF,sFAAsF;AACtF,wCAAwC;AACxC,wHAAwH;AACxH,OAAO;AACP,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,iFAAiF;AACjF,wDAAwD;AACxD,EAAE;AACF,qEAAqE;AACrE,gFAAgF;AAChF,+EAA+E;AAC/E,mBAAmB;;;AAGnB,2CAA0D;AAG1D,uEAAuE;AACvE,gFAAgF;AAChF,2EAA2E;AAC3E,sEAAsE;AACtE,+CAAqD;AAA5C,uGAAA,aAAa,OAAA;AAGtB;;;;;;;;;;GAUG;AACH;IACW,IAAI,GAAG,cAAc,CAAC;IAC/B,cAAc,CAAoB;IAEjB,OAAO,CAA6B;IAC7C,eAAe,GAAwB,IAAI,CAAC;IAEpD,YAAY,OAAmC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAwC;QACjD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAA,uCAA2B,EAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjF,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAwC;QACpD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;IAClC,CAAC;CACF","sourcesContent":["// Opt-in LiveKit adapter, exported from the `react-native-webrtc-kaleidoscope/livekit`\n// subpath. It wraps the web Insertable-Streams pipeline in LiveKit's\n// TrackProcessor interface so consumers do not have to hand-write the glue.\n//\n// Why this exists: the core web API is `applyVideoEffects(track) -> newTrack`,\n// which assumes the caller owns the RTCRtpSender and calls replaceTrack itself.\n// LiveKit owns the sender, so consumers go through `track.setProcessor(...)`\n// instead. This adapter is that bridge:\n//\n// import { KaleidoscopeProcessor } from 'react-native-webrtc-kaleidoscope/livekit';\n// await localVideoTrack.setProcessor(\n// new KaleidoscopeProcessor([{ name: 'composite', layers: [{ id: 'you', shader: 'direct', target: 'subject' }] }]),\n// );\n//\n// `livekit-client` is an OPTIONAL peer dependency: the import below is type-only\n// (erased at build), so the published JS has no runtime dependency on LiveKit\n// and the agnostic core stays decoupled. Only consumers that import this subpath\n// need livekit-client installed (they already have it).\n//\n// This adapter is web-only: it builds on MediaStreamTrackProcessor /\n// MediaStreamTrackGenerator (Insertable Streams), which exist in Chromium-based\n// browsers. On unsupported environments the underlying pipeline throws a typed\n// error at init().\n\nimport type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';\nimport { applyVideoEffectsDisposable } from './index.web';\nimport type { EffectInput } from './kaleidoscope/effect.types';\n\n// The first-class mask surface for the processor path (the twin of the\n// binding's `mask` verb; see the implementation's doc in web-driver/tuning.ts).\n// LiveKit owns the RTCRtpSender, so consumers here have no binding to call\n// `mask` on; this export is how they drive the page-shared mask edge.\nexport { setMaskTuning } from '../web-driver/tuning';\nexport type { MaskInput } from './kaleidoscope/types';\n\n/**\n * A LiveKit `TrackProcessor` that applies Kaleidoscope video effects to a local\n * camera track. Construct it with the same effect inputs `applyVideoEffects`\n * accepts (bare transform names like `'flip-x'`, or full `EffectSpec` composite\n * objects), then pass it to `localTrack.setProcessor(processor)`.\n *\n * `restart` (camera flip / source change) and `destroy` (unpublish) tear down\n * the prior Insertable-Streams pipeline, so repeated flips do not leak\n * generators. The page-shared segmenter and WebGL state are module singletons\n * reused across pipelines, so they are intentionally retained.\n */\nexport class KaleidoscopeProcessor implements TrackProcessor<Track.Kind.Video> {\n readonly name = 'kaleidoscope';\n processedTrack?: MediaStreamTrack;\n\n private readonly effects: ReadonlyArray<EffectInput>;\n private disposePipeline: (() => void) | null = null;\n\n constructor(effects: ReadonlyArray<EffectInput>) {\n this.effects = effects;\n }\n\n async init(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n const { track, dispose } = applyVideoEffectsDisposable(opts.track, this.effects);\n this.processedTrack = track;\n this.disposePipeline = dispose;\n }\n\n async restart(opts: ProcessorOptions<Track.Kind.Video>): Promise<void> {\n await this.destroy();\n await this.init(opts);\n }\n\n async destroy(): Promise<void> {\n this.disposePipeline?.();\n this.disposePipeline = null;\n this.processedTrack = undefined;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"nativewind.d.ts","sourceRoot":"","sources":["../../src/nativewind.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAWrD","sourcesContent":["// Opt-in NativeWind integration for the picker components (subpath\n// `./nativewind`). Import and call this once from your NativeWind interop setup\n// (the file where you register third-party components for `className`):\n//\n// import { registerKaleidoscopeNativeWind } from 'react-native-webrtc-kaleidoscope/nativewind';\n// registerKaleidoscopeNativeWind();\n//\n// This is the only module that imports `nativewind` (an optional peer); the\n// core `./ui` components stay style-agnostic. Without this call the components\n// still work via their defaults and the `style` prop; `className` is simply\n// inert. `cssInterop` maps each component's `className` onto its `style` target,\n// so the class string the consumer passes is resolved by their NativeWind setup.\n\nimport { cssInterop } from 'nativewind';\nimport { PresetBookMenu } from './components/preset-book-menu';\nimport { PresetBookMenuLayout } from './components/preset-book-menu/layout';\nimport { PresetGrid } from './components/preset-book-menu/preset-grid';\nimport { PresetTile } from './components/preset-tile';\nimport { Button } from './components/ui/button';\nimport { ColorPicker } from './components/ui/color-picker';\nimport { Label } from './components/ui/label';\nimport { Readout } from './components/ui/readout';\nimport { Slider } from './components/ui/slider';\n\n/**\n * Register the picker and control primitives with NativeWind so they accept\n * `className`. Idempotent; call once at app/interop setup. Requires the optional\n * `nativewind` peer dependency.\n */\nexport function registerKaleidoscopeNativeWind(): void {\n const mapping = { className: 'style' } as const;\n cssInterop(PresetBookMenu, mapping);\n cssInterop(PresetBookMenuLayout, mapping);\n cssInterop(PresetGrid, mapping);\n cssInterop(PresetTile, mapping);\n cssInterop(Label, mapping);\n cssInterop(Readout, mapping);\n cssInterop(Slider, mapping);\n cssInterop(ColorPicker, mapping);\n cssInterop(Button, mapping);\n}\n"]}
1
+ {"version":3,"file":"nativewind.d.ts","sourceRoot":"","sources":["../../src/nativewind.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAWrD"}