zyzz 0.0.21 → 0.0.22

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 (220) hide show
  1. package/Zyzz.podspec +16 -0
  2. package/dist/Style.d.ts +3 -1
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js.map +1 -1
  5. package/dist/babel/NativeJsx.d.ts.map +1 -1
  6. package/dist/babel/NativeJsx.js +204 -12
  7. package/dist/babel/NativeJsx.js.map +1 -1
  8. package/dist/babel/index.d.ts +6 -2
  9. package/dist/babel/index.d.ts.map +1 -1
  10. package/dist/babel/index.js +10 -1
  11. package/dist/babel/index.js.map +1 -1
  12. package/dist/compiler/Graph.d.ts.map +1 -1
  13. package/dist/compiler/Graph.js +64 -0
  14. package/dist/compiler/Graph.js.map +1 -1
  15. package/dist/compiler/Native.d.ts +6 -3
  16. package/dist/compiler/Native.d.ts.map +1 -1
  17. package/dist/compiler/Native.js +142 -24
  18. package/dist/compiler/Native.js.map +1 -1
  19. package/dist/compiler/Source.d.ts +2 -0
  20. package/dist/compiler/Source.d.ts.map +1 -1
  21. package/dist/compiler/Source.js +17 -5
  22. package/dist/compiler/Source.js.map +1 -1
  23. package/dist/compiler/internal/Configurations.d.ts +2 -0
  24. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  25. package/dist/compiler/internal/Configurations.js +21 -3
  26. package/dist/compiler/internal/Configurations.js.map +1 -1
  27. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  28. package/dist/compiler/internal/Contract.js +75 -38
  29. package/dist/compiler/internal/Contract.js.map +1 -1
  30. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  31. package/dist/compiler/internal/Contributions.js +7 -1
  32. package/dist/compiler/internal/Contributions.js.map +1 -1
  33. package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
  34. package/dist/compiler/internal/Descriptors.js +11 -2
  35. package/dist/compiler/internal/Descriptors.js.map +1 -1
  36. package/dist/compiler/internal/NativeBindings.d.ts +24 -26
  37. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  38. package/dist/compiler/internal/NativeBindings.js +61 -8
  39. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  40. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  41. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  42. package/dist/compiler/internal/NativeMedia.js +287 -0
  43. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  44. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  45. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  46. package/dist/compiler/internal/NativeVars.js +157 -0
  47. package/dist/compiler/internal/NativeVars.js.map +1 -0
  48. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  49. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  50. package/dist/compiler/internal/PackedStyles.js +2 -0
  51. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  52. package/dist/compiler/internal/Themes.d.ts +22 -1
  53. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  54. package/dist/compiler/internal/Themes.js +118 -3
  55. package/dist/compiler/internal/Themes.js.map +1 -1
  56. package/dist/compiler/internal/Vars.d.ts +1 -1
  57. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  58. package/dist/compiler/internal/Vars.js +7 -2
  59. package/dist/compiler/internal/Vars.js.map +1 -1
  60. package/dist/default.js.zyzz.json +1 -1
  61. package/dist/internal/Completion.d.ts +1 -1
  62. package/dist/internal/Completion.d.ts.map +1 -1
  63. package/dist/internal/Targets.d.ts.map +1 -1
  64. package/dist/internal/Targets.js +2 -0
  65. package/dist/internal/Targets.js.map +1 -1
  66. package/dist/internal/Token.d.ts +0 -2
  67. package/dist/internal/Token.d.ts.map +1 -1
  68. package/dist/internal/Token.js.map +1 -1
  69. package/dist/internal/Value.d.ts +7 -0
  70. package/dist/internal/Value.d.ts.map +1 -1
  71. package/dist/internal/Value.js.map +1 -1
  72. package/dist/metro/Graph.d.ts +22 -7
  73. package/dist/metro/Graph.d.ts.map +1 -1
  74. package/dist/metro/Graph.js +179 -42
  75. package/dist/metro/Graph.js.map +1 -1
  76. package/dist/metro/Watch.d.ts +28 -4
  77. package/dist/metro/Watch.d.ts.map +1 -1
  78. package/dist/metro/Watch.js +39 -7
  79. package/dist/metro/Watch.js.map +1 -1
  80. package/dist/metro/index.d.ts +15 -4
  81. package/dist/metro/index.d.ts.map +1 -1
  82. package/dist/metro/index.js +27 -0
  83. package/dist/metro/index.js.map +1 -1
  84. package/dist/metro/transformer.d.ts +5 -1
  85. package/dist/metro/transformer.d.ts.map +1 -1
  86. package/dist/metro/transformer.js +5 -3
  87. package/dist/metro/transformer.js.map +1 -1
  88. package/dist/react-native/StyleSheet.d.ts +3 -2
  89. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  90. package/dist/react-native/StyleSheet.js +17 -34
  91. package/dist/react-native/StyleSheet.js.map +1 -1
  92. package/dist/react-native/index.d.ts +1 -1
  93. package/dist/react-native/index.d.ts.map +1 -1
  94. package/dist/react-native/index.js +1 -1
  95. package/dist/react-native/index.js.map +1 -1
  96. package/dist/react-native/index.native.d.ts +5 -0
  97. package/dist/react-native/index.native.js +20 -0
  98. package/dist/react-native/index.native.js.map +7 -0
  99. package/dist/react-native/internal/Calculation.d.ts +7 -0
  100. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  101. package/dist/react-native/internal/Calculation.js +134 -0
  102. package/dist/react-native/internal/Calculation.js.map +1 -0
  103. package/dist/react-native/internal/Device.js +191 -0
  104. package/dist/react-native/internal/Device.js.map +7 -0
  105. package/dist/react-native/internal/NativeZyzz.js +7 -0
  106. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  107. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  108. package/dist/react-native/internal/Scalar.js +9 -2
  109. package/dist/react-native/internal/Scalar.js.map +1 -1
  110. package/dist/react-native/internal/Store.d.ts +32 -0
  111. package/dist/react-native/internal/Store.d.ts.map +1 -0
  112. package/dist/react-native/internal/Store.js +57 -0
  113. package/dist/react-native/internal/Store.js.map +1 -0
  114. package/dist/react-native/internal/Tokens.d.ts +21 -0
  115. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  116. package/dist/react-native/internal/Tokens.js +58 -0
  117. package/dist/react-native/internal/Tokens.js.map +1 -0
  118. package/dist/react-native/internal/Viewport.d.ts +6 -0
  119. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  120. package/dist/react-native/internal/Viewport.js +4 -0
  121. package/dist/react-native/internal/Viewport.js.map +1 -0
  122. package/dist/react-native/react.d.ts +74 -11
  123. package/dist/react-native/react.d.ts.map +1 -1
  124. package/dist/react-native/react.js +111 -12
  125. package/dist/react-native/react.js.map +1 -1
  126. package/dist/react-native/reanimated.d.ts +39 -0
  127. package/dist/react-native/reanimated.d.ts.map +1 -0
  128. package/dist/react-native/reanimated.js +76 -0
  129. package/dist/react-native/reanimated.js.map +1 -0
  130. package/dist/runtime/NativeContext.d.ts +10 -1
  131. package/dist/runtime/NativeContext.d.ts.map +1 -1
  132. package/dist/runtime/NativeContext.js +67 -14
  133. package/dist/runtime/NativeContext.js.map +1 -1
  134. package/dist/runtime/NativeDynamic.d.ts +3 -2
  135. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  136. package/dist/runtime/NativeDynamic.js +32 -7
  137. package/dist/runtime/NativeDynamic.js.map +1 -1
  138. package/dist/runtime/NativeStatic.d.ts +23 -0
  139. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  140. package/dist/runtime/NativeStatic.js +106 -0
  141. package/dist/runtime/NativeStatic.js.map +1 -0
  142. package/dist/runtime/NativeVars.d.ts +53 -0
  143. package/dist/runtime/NativeVars.d.ts.map +1 -0
  144. package/dist/runtime/NativeVars.js +77 -0
  145. package/dist/runtime/NativeVars.js.map +1 -0
  146. package/dist/runtime/index.d.ts +4 -0
  147. package/dist/runtime/index.d.ts.map +1 -1
  148. package/dist/runtime/index.js +4 -0
  149. package/dist/runtime/index.js.map +1 -1
  150. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  151. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  152. package/dist/runtime/internal/NativeMedia.js +36 -0
  153. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  154. package/dist/vite/index.d.ts.map +1 -1
  155. package/dist/vite/index.js +7 -35
  156. package/dist/vite/index.js.map +1 -1
  157. package/dist/web/Css.d.ts.map +1 -1
  158. package/dist/web/Css.js +31 -19
  159. package/dist/web/Css.js.map +1 -1
  160. package/dist/web/internal/Themes.d.ts +0 -2
  161. package/dist/web/internal/Themes.d.ts.map +1 -1
  162. package/dist/web/internal/Themes.js +30 -28
  163. package/dist/web/internal/Themes.js.map +1 -1
  164. package/dist/web/page.d.ts +2 -0
  165. package/dist/web/page.d.ts.map +1 -1
  166. package/native/NativeZyzz.cpp +215 -0
  167. package/native/NativeZyzz.h +52 -0
  168. package/native/ZyzzSpecJSI.h +65 -0
  169. package/native/android/CMakeLists.txt +8 -0
  170. package/native/android/ZyzzSpec-generated.cpp +50 -0
  171. package/native/android/ZyzzSpec.h +31 -0
  172. package/native/ios/ZyzzModuleProvider.mm +13 -0
  173. package/package.json +39 -1
  174. package/react-native.config.ts +14 -0
  175. package/src/Style.ts +11 -8
  176. package/src/babel/NativeJsx.ts +305 -28
  177. package/src/babel/index.ts +21 -3
  178. package/src/compiler/Graph.ts +75 -0
  179. package/src/compiler/Native.ts +158 -26
  180. package/src/compiler/Source.ts +32 -5
  181. package/src/compiler/internal/Configurations.ts +25 -3
  182. package/src/compiler/internal/Contract.ts +98 -50
  183. package/src/compiler/internal/Contributions.ts +7 -1
  184. package/src/compiler/internal/Descriptors.ts +12 -8
  185. package/src/compiler/internal/NativeBindings.ts +98 -15
  186. package/src/compiler/internal/NativeMedia.ts +342 -0
  187. package/src/compiler/internal/NativeVars.ts +228 -0
  188. package/src/compiler/internal/PackedStyles.ts +4 -0
  189. package/src/compiler/internal/Themes.ts +187 -2
  190. package/src/compiler/internal/Vars.ts +12 -1
  191. package/src/internal/Completion.ts +6 -3
  192. package/src/internal/Targets.ts +2 -0
  193. package/src/internal/Token.ts +0 -2
  194. package/src/internal/Value.ts +20 -0
  195. package/src/metro/Graph.ts +235 -50
  196. package/src/metro/Watch.ts +95 -10
  197. package/src/metro/index.ts +62 -4
  198. package/src/metro/transformer.ts +21 -11
  199. package/src/react-native/StyleSheet.ts +28 -44
  200. package/src/react-native/index.native.ts +10 -0
  201. package/src/react-native/index.ts +1 -1
  202. package/src/react-native/internal/Calculation.ts +159 -0
  203. package/src/react-native/internal/Device.ts +231 -0
  204. package/src/react-native/internal/NativeZyzz.ts +20 -0
  205. package/src/react-native/internal/Scalar.ts +13 -2
  206. package/src/react-native/internal/Store.ts +78 -0
  207. package/src/react-native/internal/Tokens.ts +85 -0
  208. package/src/react-native/internal/Viewport.ts +8 -0
  209. package/src/react-native/react.ts +211 -19
  210. package/src/react-native/reanimated.ts +162 -0
  211. package/src/runtime/NativeContext.ts +88 -17
  212. package/src/runtime/NativeDynamic.ts +35 -10
  213. package/src/runtime/NativeStatic.ts +145 -0
  214. package/src/runtime/NativeVars.ts +189 -0
  215. package/src/runtime/index.ts +6 -0
  216. package/src/runtime/internal/NativeMedia.ts +63 -0
  217. package/src/vite/index.ts +9 -42
  218. package/src/web/Css.ts +42 -27
  219. package/src/web/internal/Themes.ts +34 -57
  220. package/src/web/page.ts +2 -0
@@ -0,0 +1,85 @@
1
+ /** Resolves portable references for native tables and callback expressions. @module */
2
+ import * as Calculation from './Calculation.js'
3
+ import type * as Scalar from './Scalar.js'
4
+ import type * as Style from '../../Style.js'
5
+ import * as Token from '../../internal/Token.js'
6
+
7
+ /** Identifies values whose behavior requires a web target. */
8
+ export class UnsupportedError extends Error {
9
+ override name = 'Tokens.UnsupportedError'
10
+ }
11
+
12
+ /** Selects live token aliases and schemes without evaluating authored code. */
13
+ export function resolve(
14
+ input: Style.Declaration['value'],
15
+ options: resolve.Options,
16
+ ): string | number {
17
+ const active = new Set<Token.Reference>()
18
+ function scalar(
19
+ value: Token.Value | Style.Declaration['value'],
20
+ ): string | number {
21
+ if (typeof value === 'number' || typeof value === 'string') return value
22
+ if (
23
+ Token.is(value) &&
24
+ !Object.getOwnPropertyDescriptor(value, Token.web)?.value
25
+ ) {
26
+ if (active.has(value))
27
+ throw new Error('Native token aliases must not form a cycle.')
28
+ const metadata = options.metadata
29
+ const shared =
30
+ metadata &&
31
+ (metadata.contract === value.contract ||
32
+ (metadata.contract[Token.identity] !== undefined &&
33
+ metadata.contract[Token.identity] ===
34
+ value.contract[Token.identity]))
35
+ const next = shared
36
+ ? Object.hasOwn(metadata.values, value.path)
37
+ ? metadata.values[value.path]
38
+ : undefined
39
+ : value.value
40
+ if (next === undefined) throw new Error('Theme is missing a live token.')
41
+ active.add(value)
42
+ const result = scalar(next)
43
+ active.delete(value)
44
+ return result
45
+ }
46
+ if (Token.isExpression(value)) {
47
+ const text = value.parts.map(scalar).join('')
48
+ if (
49
+ !('group' in value) ||
50
+ value.group !== 'spacing' ||
51
+ !/^\s*calc\(/i.test(text)
52
+ )
53
+ return text
54
+ return `${Calculation.length(text, options) / (options.units?.px ?? 1)}px`
55
+ }
56
+ if (
57
+ value &&
58
+ typeof value === 'object' &&
59
+ 'light' in value &&
60
+ 'dark' in value
61
+ ) {
62
+ if (value.light !== value.dark)
63
+ options.resolution.schemeIndependent = false
64
+ return scalar(value[options.colorScheme])
65
+ }
66
+ if (value && typeof value === 'object' && 'default' in value)
67
+ throw new UnsupportedError(
68
+ 'Media-conditioned variables require a web target.',
69
+ )
70
+ throw new UnsupportedError(
71
+ 'Web variables, expressions, and dynamic bindings are not native scalar tokens.',
72
+ )
73
+ }
74
+ return scalar(input)
75
+ }
76
+
77
+ /** Native token selection and explicit unit conversion. */
78
+ export declare namespace resolve {
79
+ /** Profile metadata and scheme independence shared by table preparation. */
80
+ type Options = Scalar.Options & {
81
+ readonly colorScheme: 'dark' | 'light'
82
+ readonly metadata: Token.Metadata | undefined
83
+ readonly resolution: { schemeIndependent: boolean }
84
+ }
85
+ }
@@ -0,0 +1,8 @@
1
+ /** Connects the native device adapter to framework subscriptions. @module */
2
+ import type * as Media from '../../runtime/internal/NativeMedia.js'
3
+ import * as React from 'react'
4
+
5
+ /** Window measurements owned by the native Provider lifecycle. */
6
+ export const context = React.createContext<Media.Viewport | undefined>(
7
+ undefined,
8
+ )
@@ -1,47 +1,239 @@
1
- /** React lifecycle for automatically compiled native styling. @module */
2
- import * as React from 'react'
1
+ /** React subscriptions for compiled native styles and variable values. @module */
2
+ import * as Config from '../Config.js'
3
3
  import * as NativeContext from '../runtime/NativeContext.js'
4
+ import * as NativeVars from '../runtime/NativeVars.js'
5
+ import * as React from 'react'
6
+ import type * as Vars from '../Vars.js'
7
+ import * as Store from './internal/Store.js'
8
+ import * as Viewport from './internal/Viewport.js'
9
+
10
+ const unbound = {
11
+ getSnapshot: () => undefined,
12
+ subscribe: () => () => {},
13
+ }
14
+
15
+ /** Creates native authoring helpers and a Provider typed to the configured variables. */
16
+ export function defineConfig<const options extends Config.create.Options = {}>(
17
+ options: options & Parameters<typeof Config.create<options>>[0] = {} as never,
18
+ ): defineConfig.ReturnType<options> {
19
+ const config = Config.create<options>(options)
20
+ const defaultVars = (options as Config.VariableOptions).defaultVars
21
+ const names =
22
+ defaultVars === undefined
23
+ ? undefined
24
+ : new Set(Object.keys((options as Config.VariableOptions).vars))
4
25
 
5
- const context = React.createContext(styles(undefined))
26
+ function BoundProvider(props: defineConfig.ProviderProps<options>) {
27
+ if (props.vars !== undefined && !names?.has(props.vars))
28
+ throw new Error(`Unknown native vars: ${props.vars}.`)
6
29
 
7
- /** Supplies set and resolved device appearance to compiled native components. */
30
+ return React.createElement(Provider, {
31
+ ...props,
32
+ vars: props.vars ?? defaultVars,
33
+ })
34
+ }
35
+
36
+ return Object.freeze({
37
+ ...config,
38
+ Provider: BoundProvider,
39
+ }) as defineConfig.ReturnType<options>
40
+ }
41
+
42
+ /** Native configuration helpers and inferred Provider props. */
43
+ export declare namespace defineConfig {
44
+ /** Resolved scheme and variable names from the owning configuration. */
45
+ type ProviderProps<options extends Config.create.Options> = Omit<
46
+ Provider.Props,
47
+ 'vars'
48
+ > & {
49
+ /** Selected variables. Omission uses the owning configuration's default. */
50
+ readonly vars?:
51
+ | (options extends { defaultVars: string; vars: infer catalog }
52
+ ? Extract<keyof catalog, string>
53
+ : never)
54
+ | undefined
55
+ }
56
+ /** Existing authoring helpers plus a configuration-scoped Provider. */
57
+ type ReturnType<options extends Config.create.Options = {}> =
58
+ Config.create.ReturnType<options> & {
59
+ /** Provides this configuration's typed selection to native consumers. */
60
+ readonly Provider: React.FunctionComponent<ProviderProps<options>>
61
+ }
62
+ }
63
+
64
+ /** Supplies selected variables, appearance, and adapter measurements to this subtree. */
8
65
  export function Provider(props: Provider.Props) {
66
+ if (Object.hasOwn(props, 'set'))
67
+ throw new Error('Provider uses vars instead of set.')
9
68
  if (
10
69
  (props.colorScheme !== 'light' && props.colorScheme !== 'dark') ||
11
- (props.set !== undefined &&
12
- (typeof props.set !== 'string' || !props.set.trim()))
70
+ (props.vars !== undefined &&
71
+ (typeof props.vars !== 'string' || !props.vars.trim()))
13
72
  )
14
73
  throw new Error(
15
- 'Native appearance requires a resolved light/dark scheme and a nonempty set name.',
74
+ 'Native appearance requires a resolved light/dark scheme and a nonempty vars name.',
16
75
  )
17
- const value = React.useMemo(
18
- () => styles({ colorScheme: props.colorScheme, set: props.set }),
19
- [props.colorScheme, props.set],
76
+
77
+ const viewport = React.useContext(Viewport.context)
78
+ const [store] = React.useState(() =>
79
+ Store.create({ colorScheme: props.colorScheme, set: props.vars, viewport }),
20
80
  )
21
- return React.createElement(context.Provider, { value }, props.children)
81
+ const [, publish] = React.useReducer((version: number) => version + 1, 0)
82
+ const committed = React.useRef(props.children)
83
+ const snapshot = store.getSnapshot()
84
+ const pending =
85
+ snapshot.colorScheme !== props.colorScheme ||
86
+ snapshot.set !== props.vars ||
87
+ snapshot.viewport?.width !== viewport?.width ||
88
+ snapshot.viewport?.height !== viewport?.height
89
+ const children = pending ? committed.current : props.children
90
+ React.useLayoutEffect(() => {
91
+ committed.current = children
92
+ if (!pending) return
93
+ // Keep the committed subtree until its selection is published. This avoids
94
+ // rendering new application props with the previous store snapshot, without
95
+ // mutating an external store during a potentially abandoned React render.
96
+ store.update({ colorScheme: props.colorScheme, set: props.vars, viewport })
97
+ publish()
98
+ }, [children, pending, props.colorScheme, props.vars, store, viewport])
99
+
100
+ return React.createElement(Store.context.Provider, { value: store }, children)
22
101
  }
23
102
 
24
- /** Application boundary inputs. Pass React Native's useColorScheme result with a light fallback. */
103
+ /** Application boundary for native variable selection. */
25
104
  export declare namespace Provider {
26
- /** Theme selection and resolved appearance for this React subtree. */
27
- type Props = NativeContext.Context & {
28
- /** Components that consume compiled native styles. */
105
+ /** Resolved appearance and a configuration-relative variable name. */
106
+ type Props = {
107
+ /** Components that consume compiled native styling or values. */
29
108
  readonly children?: React.ReactNode | undefined
109
+ /** Resolved device scheme or an application override. */
110
+ readonly colorScheme: 'dark' | 'light'
111
+ /** Selected variables. Omission uses each configuration's default. */
112
+ readonly vars?: string | undefined
30
113
  }
31
114
  }
32
115
 
33
- /** Compiler-inserted unconditional subscription, including memoized components. */
116
+ /** Compiler-inserted subscription for resolving native style applications. */
34
117
  export function useStyles() {
35
- return React.useContext(context)
118
+ const store = React.useContext(Store.context) ?? unbound
119
+ const value = React.useSyncExternalStore(
120
+ store.subscribe,
121
+ store.getSnapshot,
122
+ store.getSnapshot,
123
+ )
124
+ return React.useMemo(() => styles(value), [value])
125
+ }
126
+
127
+ /** Compiler fallback for native view bindings when no native adapter is installed. */
128
+ export function useNativeStyles() {
129
+ const selected = useStyles()
130
+ return { ...selected, view: selected.props }
131
+ }
132
+
133
+ /**
134
+ * Reads readonly native variables using the nearest Provider's selection.
135
+ * @throws For missing providers, uncompiled definitions, or unsupported field reads.
136
+ */
137
+ export function useVars<const variables extends Vars.Definition>(
138
+ variables: variables,
139
+ select?: undefined,
140
+ ): NativeVars.Values<Vars.Extract<variables>>
141
+ /**
142
+ * Subscribes to a selection, comparing results with Object.is.
143
+ * @throws For invalid variable selection or unsupported field reads.
144
+ */
145
+ export function useVars<const variables extends Vars.Definition, selected>(
146
+ variables: variables,
147
+ select: (values: NativeVars.Values<Vars.Extract<variables>>) => selected,
148
+ ): selected
149
+ export function useVars(
150
+ variables: Vars.Definition,
151
+ select: (values: never) => unknown = (values) => values,
152
+ ) {
153
+ const store = React.useContext(Store.context)
154
+ if (!store) throw new Error('useVars requires a Zyzz Provider.')
155
+
156
+ const read = React.useMemo(() => {
157
+ let previous: NativeContext.Context | undefined
158
+ let selected: unknown
159
+ return () => {
160
+ const snapshot = store.getSnapshot()
161
+ if (snapshot !== previous) {
162
+ const next = select(NativeVars.read(variables, snapshot) as never)
163
+ if (!previous || !Object.is(selected, next)) selected = next
164
+ previous = snapshot
165
+ }
166
+ return selected
167
+ }
168
+ }, [select, store, variables])
169
+
170
+ return React.useSyncExternalStore(store.subscribe, read, read)
171
+ }
172
+
173
+ /**
174
+ * Resolves compiled style props for components using React updates.
175
+ * @param Component - Function, class, or ref-forwarding component receiving native styles.
176
+ * @param options - Additional style-bearing prop names.
177
+ * @returns A component preserving the original props and ref.
178
+ * @throws For invalid style prop names or unresolved native selections.
179
+ */
180
+ export function withStyles<const component extends React.ElementType>(
181
+ Component: component,
182
+ options: withStyles.Options<component> = {},
183
+ ) {
184
+ const additional = options.styleProps ?? []
185
+ if (
186
+ !Array.isArray(additional) ||
187
+ additional.some(
188
+ (name) =>
189
+ typeof name !== 'string' || !name || name === 'key' || name === 'ref',
190
+ )
191
+ )
192
+ throw new Error(
193
+ 'withStyles requires style prop names excluding key and ref.',
194
+ )
195
+
196
+ const names = [...new Set(['style', 'contentContainerStyle', ...additional])]
197
+ const Wrapped = React.forwardRef<
198
+ React.ComponentRef<component>,
199
+ React.ComponentPropsWithoutRef<component>
200
+ >((props, ref) => {
201
+ const selected = useStyles()
202
+ const resolved: Record<string, unknown> = { ...props, ref }
203
+ for (const name of names)
204
+ if (Object.hasOwn(props, name))
205
+ resolved[name] = selected.style(
206
+ (props as Record<string, unknown>)[name],
207
+ )
208
+
209
+ return React.createElement(Component, resolved)
210
+ })
211
+ Wrapped.displayName = `withStyles(${typeof Component === 'string' ? Component : Component.displayName || Component.name || 'Component'})`
212
+
213
+ return Wrapped
214
+ }
215
+
216
+ /** Style-bearing component properties resolved by the wrapper. */
217
+ export declare namespace withStyles {
218
+ /** Additional names are resolved alongside style and contentContainerStyle. */
219
+ type Options<component extends React.ElementType> = {
220
+ /** Additional style props declared by the wrapped component. */
221
+ readonly styleProps?:
222
+ | readonly Exclude<
223
+ Extract<keyof React.ComponentProps<component>, string>,
224
+ 'key' | 'ref'
225
+ >[]
226
+ | undefined
227
+ }
36
228
  }
37
229
 
38
230
  function styles(value: NativeContext.Context | undefined) {
39
231
  return {
40
- style: (style: unknown, input?: unknown) =>
41
- NativeContext.resolve(style, value, input),
42
232
  props: (props: Record<string, unknown> | null | undefined) =>
43
233
  props && Object.hasOwn(props, 'style')
44
234
  ? { ...props, style: NativeContext.resolve(props.style, value) }
45
235
  : props,
236
+ style: (style: unknown, input?: unknown) =>
237
+ NativeContext.resolve(style, value, input),
46
238
  }
47
239
  }
@@ -0,0 +1,162 @@
1
+ /** Publishes scoped native selections to Reanimated shared values. @module */
2
+ import * as NativeContext from '../runtime/NativeContext.js'
3
+ import * as NativeVars from '../runtime/NativeVars.js'
4
+ import * as React from 'react'
5
+ import * as ReactNative from 'react-native'
6
+ import * as Reanimated from 'react-native-reanimated'
7
+ import * as Store from './internal/Store.js'
8
+ import type * as StyleSheet from './StyleSheet.js'
9
+ import type * as Vars from '../Vars.js'
10
+
11
+ /**
12
+ * Reads a numeric or color property from a selected, composed native style.
13
+ * @param appliedStyle - Applied native style, including arrays and caller overrides.
14
+ * @param property - Numeric or color property to publish.
15
+ * @returns A shared value updated on style input and Provider selection changes.
16
+ * @throws For unresolved bindings, missing properties, or unsupported property values.
17
+ */
18
+ export function useAnimatedStyleValue<
19
+ const property extends useAnimatedStyleValue.Property,
20
+ >(
21
+ appliedStyle: StyleSheet.StyleProp<object>,
22
+ property: property,
23
+ ): Reanimated.SharedValue<useAnimatedStyleValue.Value<property>> {
24
+ const store = React.useContext(Store.context)
25
+ const read = React.useCallback(() => {
26
+ const resolved = NativeContext.resolve(appliedStyle, store?.getSnapshot())
27
+ const style = ReactNative.StyleSheet.flatten(
28
+ resolved as ReactNative.StyleProp<ReactNative.ViewStyle>,
29
+ )
30
+ const value = style?.[property as keyof ReactNative.ViewStyle]
31
+ const color = property === 'color' || property.endsWith('Color')
32
+ if (
33
+ color
34
+ ? typeof value !== 'string' || ReactNative.processColor(value) == null
35
+ : typeof value !== 'number' || !Number.isFinite(value)
36
+ )
37
+ throw new Error(
38
+ `useAnimatedStyleValue requires a numeric or color value for ${property}.`,
39
+ )
40
+
41
+ return value as useAnimatedStyleValue.Value<property>
42
+ }, [appliedStyle, property, store])
43
+
44
+ return useSelection(read, store)
45
+ }
46
+
47
+ /** Properties supported by scalar style animation bindings. */
48
+ export declare namespace useAnimatedStyleValue {
49
+ /** Numeric native properties and native color properties. */
50
+ type Property = {
51
+ [key in keyof StyleSheet.NativeStyle]-?: key extends
52
+ | 'color'
53
+ | `${string}Color`
54
+ ? key
55
+ : Extract<StyleSheet.NativeStyle[key], number> extends never
56
+ ? never
57
+ : key
58
+ }[keyof StyleSheet.NativeStyle] &
59
+ string
60
+ /** Color strings or finite numeric selections, excluding layout strings. */
61
+ type Value<property extends Property> = property extends
62
+ | 'color'
63
+ | `${string}Color`
64
+ ? string
65
+ : number
66
+ }
67
+
68
+ /**
69
+ * Publishes compiled native variables without subscribing the component to React updates.
70
+ * @param variables - Statically linked variables from the owning configuration.
71
+ * @param select - Optional JS selector, with results compared using Object.is.
72
+ * @returns A shared value containing the selected serializable native values.
73
+ * @throws For a missing Provider, unsupported variable reads, or nonserializable selections.
74
+ */
75
+ export function useAnimatedVars<const variables extends Vars.Definition>(
76
+ variables: variables,
77
+ select?: undefined,
78
+ ): Reanimated.SharedValue<NativeVars.Values<Vars.Extract<variables>>>
79
+ /** Publishes a selected variable value to the UI thread. */
80
+ export function useAnimatedVars<
81
+ const variables extends Vars.Definition,
82
+ selected extends useAnimatedVars.Value,
83
+ >(
84
+ variables: variables,
85
+ select: (values: NativeVars.Values<Vars.Extract<variables>>) => selected,
86
+ ): Reanimated.SharedValue<selected>
87
+ export function useAnimatedVars(
88
+ variables: Vars.Definition,
89
+ select: (values: never) => unknown = (values) => values,
90
+ ) {
91
+ const store = React.useContext(Store.context)
92
+ if (!store) throw new Error('useAnimatedVars requires a Zyzz Provider.')
93
+
94
+ const read = React.useCallback(
95
+ () => select(NativeVars.read(variables, store.getSnapshot()) as never),
96
+ [select, store, variables],
97
+ )
98
+
99
+ return useSelection(read, store)
100
+ }
101
+
102
+ /** Serializable selections shared with animation worklets. */
103
+ export declare namespace useAnimatedVars {
104
+ /** Plain native values, arrays, and objects without functions or class instances. */
105
+ type Value =
106
+ | boolean
107
+ | null
108
+ | number
109
+ | readonly Value[]
110
+ | string
111
+ | undefined
112
+ | { readonly [key: string]: Value }
113
+ }
114
+
115
+ function useSelection<value>(
116
+ read: () => value,
117
+ store: ReturnType<typeof Store.create> | undefined,
118
+ ): Reanimated.SharedValue<value> {
119
+ const initial = read()
120
+ serializable(initial)
121
+ const previous = React.useRef(initial)
122
+ const shared = Reanimated.useSharedValue(initial)
123
+
124
+ React.useLayoutEffect(() => {
125
+ const update = () => {
126
+ const next = read()
127
+ if (Object.is(previous.current, next)) return
128
+ serializable(next)
129
+ shared.set(next)
130
+ previous.current = next
131
+ }
132
+ update()
133
+ return store?.subscribe(update)
134
+ }, [read, shared, store])
135
+
136
+ return shared
137
+ }
138
+
139
+ function serializable(value: unknown, ancestors = new Set<object>()): void {
140
+ if (value == null || typeof value === 'string' || typeof value === 'boolean')
141
+ return
142
+ if (typeof value === 'number' && Number.isFinite(value)) return
143
+ if (
144
+ typeof value !== 'object' ||
145
+ (Array.isArray(value)
146
+ ? Object.getPrototypeOf(value) !== Array.prototype
147
+ : Object.getPrototypeOf(value) !== Object.prototype &&
148
+ Object.getPrototypeOf(value) !== null)
149
+ )
150
+ throw new Error(
151
+ 'Animated selections require finite, serializable native values.',
152
+ )
153
+
154
+ if (ancestors.has(value))
155
+ throw new Error('Animated selections cannot contain cycles.')
156
+ if (Object.getOwnPropertySymbols(value).length)
157
+ throw new Error('Animated selections cannot contain symbol properties.')
158
+
159
+ ancestors.add(value)
160
+ for (const entry of Object.values(value)) serializable(entry, ancestors)
161
+ ancestors.delete(value)
162
+ }
@@ -1,7 +1,9 @@
1
- /** Defers precompiled native selection until a React render supplies its context. @module */
1
+ /** Defers precompiled native selection until a consumer supplies its scoped selection. @module */
2
2
  import type * as Native from './Native.js'
3
+ import * as Media from './internal/NativeMedia.js'
3
4
 
4
5
  const binding = Symbol('zyzz.native.context')
6
+ const dependency = Symbol('zyzz.native.dependency')
5
7
 
6
8
  /** Render-local set and resolved appearance. */
7
9
  export type Context = {
@@ -9,6 +11,8 @@ export type Context = {
9
11
  readonly colorScheme: 'dark' | 'light'
10
12
  /** Named compiled set; omission uses each configuration default. */
11
13
  readonly set?: string | undefined
14
+ /** Logical window dimensions supplied by the native adapter. */
15
+ readonly viewport?: Media.Viewport | undefined
12
16
  }
13
17
 
14
18
  type Callable = (input: never) => Native.Props<object>
@@ -25,6 +29,9 @@ export function create<const tables extends Tables>(
25
29
  Object.keys(tables).length === 1 && defaultVars === 'default'
26
30
  ? tables.default
27
31
  : undefined
32
+ const selections = new Set(
33
+ Object.values(tables).flatMap((table) => [table.dark, table.light]),
34
+ )
28
35
 
29
36
  function select(context: Context, input?: never) {
30
37
  const set = context.set ?? defaultVars
@@ -32,22 +39,12 @@ export function create<const tables extends Tables>(
32
39
  if (!table) throw new Error(`Unknown native set: ${set}.`)
33
40
  return table[context.colorScheme](input!).style
34
41
  }
35
- function props(input?: never) {
36
- return {
37
- style: {
38
- [binding]: (context: Context) => select(context, input),
39
- },
40
- }
41
- }
42
- const defaults = props()
43
- Object.freeze(defaults.style)
44
- Object.freeze(defaults)
45
- const callable = (input?: never) =>
46
- input === undefined ? defaults : props(input)
47
- Object.defineProperty(callable, binding, {
48
- value: select,
49
- })
50
- return callable as tables[keyof tables]['light']
42
+ return bind(select, (context) => {
43
+ const table = tables[context.set ?? defaultVars] ?? fallback
44
+ if (!table)
45
+ throw new Error(`Unknown native set: ${context.set ?? defaultVars}.`)
46
+ return selections.size === 1 ? [] : [table[context.colorScheme]]
47
+ }) as tables[keyof tables]['light']
51
48
  }
52
49
 
53
50
  /** Resolves generated bindings and arrays while preserving caller-owned native objects. */
@@ -72,3 +69,77 @@ export function resolve(
72
69
  return (...args: unknown[]) => resolve(value(...args), context)
73
70
  return value
74
71
  }
72
+
73
+ /** Selects a precompiled media alternative before applying its scoped styling. */
74
+ export function responsive<
75
+ const profiles extends Readonly<Record<string, Callable>>,
76
+ >(
77
+ queries: readonly Media.Query[],
78
+ profiles: profiles,
79
+ ): profiles[keyof profiles] {
80
+ return bind(
81
+ (context, input) => {
82
+ const profile = profiles[Media.select(queries, context.viewport)]
83
+ if (!profile) throw new Error('Native media alternative is missing.')
84
+ return resolve(profile, context, input)
85
+ },
86
+ (context) => {
87
+ const profile = profiles[Media.select(queries, context.viewport)]
88
+ return [profile, ...key(profile, context)]
89
+ },
90
+ ) as unknown as profiles[keyof profiles]
91
+ }
92
+
93
+ /** Retains a compiled application and its input until a view selects its context. */
94
+ export function application(value: unknown, input?: unknown): unknown {
95
+ return {
96
+ [binding]: (context: Context) => resolve(value, context, input),
97
+ [dependency]: (context: Context) => [
98
+ ...key(value, context),
99
+ ...key(input, context),
100
+ ],
101
+ }
102
+ }
103
+
104
+ /** Returns compiler-owned alternatives consumed by a native style application. */
105
+ export function key(value: unknown, context: Context): readonly unknown[] {
106
+ if (Array.isArray(value)) return value.flatMap((entry) => key(entry, context))
107
+ if (
108
+ value &&
109
+ (typeof value === 'object' || typeof value === 'function') &&
110
+ dependency in value
111
+ ) {
112
+ const read = value[dependency]
113
+ if (typeof read === 'function') return read(context)
114
+ }
115
+ if (value && typeof value === 'object' && 'style' in value)
116
+ return key(value.style, context)
117
+ return []
118
+ }
119
+
120
+ function bind(
121
+ select: (context: Context, input?: never) => unknown,
122
+ read: (context: Context) => readonly unknown[],
123
+ ) {
124
+ function props(input?: never) {
125
+ return {
126
+ style: {
127
+ [binding]: (context: Context) => select(context, input),
128
+ [dependency]: (context: Context) => [
129
+ ...read(context),
130
+ ...key(input, context),
131
+ ],
132
+ },
133
+ }
134
+ }
135
+ const defaults = props()
136
+ Object.freeze(defaults.style)
137
+ Object.freeze(defaults)
138
+ const callable = (input?: never) =>
139
+ input === undefined ? defaults : props(input)
140
+ Object.defineProperties(callable, {
141
+ [binding]: { value: select },
142
+ [dependency]: { value: read },
143
+ })
144
+ return callable
145
+ }