zyzz 0.0.23 → 0.0.24
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.
- package/README.md +6 -6
- package/dist/internal/Configuration.d.ts.map +1 -1
- package/dist/internal/Configuration.js +2 -1
- package/dist/internal/Configuration.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +40 -12
- package/dist/node/Host.js.map +1 -1
- package/dist/react-native/internal/Device.js +90 -37
- package/dist/react-native/internal/Device.js.map +3 -3
- package/dist/react-native/internal/NativeZyzz.js.map +2 -2
- package/dist/react-native/internal/Store.d.ts +4 -0
- package/dist/react-native/internal/Store.d.ts.map +1 -1
- package/dist/react-native/internal/Store.js.map +1 -1
- package/dist/react-native/internal/Viewport.d.ts +6 -0
- package/dist/react-native/internal/Viewport.d.ts.map +1 -1
- package/dist/react-native/internal/Viewport.js +2 -0
- package/dist/react-native/internal/Viewport.js.map +1 -1
- package/dist/react-native/react.d.ts +5 -2
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +60 -8
- package/dist/react-native/react.js.map +1 -1
- package/dist/runtime/NativeContext.d.ts +6 -0
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +62 -9
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +4 -1
- package/dist/unplugin/index.js.map +1 -1
- package/dist/variants.d.ts +3 -2
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/native/NativeZyzz.cpp +52 -65
- package/native/NativeZyzz.h +0 -4
- package/native/ZyzzSpecJSI.h +0 -10
- package/native/android/ZyzzSpec-generated.cpp +0 -6
- package/package.json +1 -1
- package/src/internal/Configuration.ts +2 -1
- package/src/node/Host.ts +64 -17
- package/src/react-native/internal/Device.ts +129 -48
- package/src/react-native/internal/NativeZyzz.ts +4 -6
- package/src/react-native/internal/Store.ts +4 -0
- package/src/react-native/internal/Viewport.ts +7 -0
- package/src/react-native/react.ts +81 -11
- package/src/runtime/NativeContext.ts +83 -9
- package/src/unplugin/index.ts +4 -1
- package/src/variants.ts +3 -2
|
@@ -7,6 +7,14 @@ const dependency = Symbol('zyzz.native.dependency')
|
|
|
7
7
|
|
|
8
8
|
/** Render-local set and resolved appearance. */
|
|
9
9
|
export type Context = {
|
|
10
|
+
/**
|
|
11
|
+
* Builds a color that follows the platform appearance. When present, styles whose
|
|
12
|
+
* light and dark selections differ only in color use it and need no scheme update.
|
|
13
|
+
* Must be stable, since adapted styles are cached per selection pair.
|
|
14
|
+
*/
|
|
15
|
+
readonly adaptive?:
|
|
16
|
+
| ((light: string | number, dark: string | number) => unknown)
|
|
17
|
+
| undefined
|
|
10
18
|
/** Resolved system appearance or an explicit override. */
|
|
11
19
|
readonly colorScheme: 'dark' | 'light'
|
|
12
20
|
/** Named compiled set; omission uses each configuration default. */
|
|
@@ -37,7 +45,15 @@ export function create<const tables extends Tables>(
|
|
|
37
45
|
const set = context.set ?? defaultVars
|
|
38
46
|
const table = tables[set] ?? fallback
|
|
39
47
|
if (!table) throw new Error(`Unknown native set: ${set}.`)
|
|
40
|
-
|
|
48
|
+
const style = table[context.colorScheme](input!).style
|
|
49
|
+
if (!context.adaptive) return style
|
|
50
|
+
return (
|
|
51
|
+
adapt(
|
|
52
|
+
table.light(input!).style,
|
|
53
|
+
table.dark(input!).style,
|
|
54
|
+
context.adaptive,
|
|
55
|
+
) ?? style
|
|
56
|
+
)
|
|
41
57
|
}
|
|
42
58
|
return bind(select, (context) => {
|
|
43
59
|
const table = tables[context.set ?? defaultVars] ?? fallback
|
|
@@ -94,10 +110,10 @@ export function responsive<
|
|
|
94
110
|
export function application(value: unknown, input?: unknown): unknown {
|
|
95
111
|
return {
|
|
96
112
|
[binding]: (context: Context) => resolve(value, context, input),
|
|
97
|
-
[dependency]: (context: Context) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
[dependency]: (context: Context) =>
|
|
114
|
+
input === undefined
|
|
115
|
+
? key(value, context)
|
|
116
|
+
: [...key(value, context), ...key(input, context)],
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
@@ -117,6 +133,64 @@ export function key(value: unknown, context: Context): readonly unknown[] {
|
|
|
117
133
|
return []
|
|
118
134
|
}
|
|
119
135
|
|
|
136
|
+
const adapted = new WeakMap<object, WeakMap<object, object | null>>()
|
|
137
|
+
|
|
138
|
+
/** Merges selections that differ only in color, or returns undefined. Results keep their identity across schemes. */
|
|
139
|
+
function adapt(
|
|
140
|
+
light: unknown,
|
|
141
|
+
dark: unknown,
|
|
142
|
+
color: NonNullable<Context['adaptive']>,
|
|
143
|
+
): unknown {
|
|
144
|
+
if (light === dark) return light
|
|
145
|
+
if (!record(light) || !record(dark)) return undefined
|
|
146
|
+
let pairs = adapted.get(light)
|
|
147
|
+
if (!pairs) adapted.set(light, (pairs = new WeakMap()))
|
|
148
|
+
if (pairs.has(dark)) return pairs.get(dark) ?? undefined
|
|
149
|
+
const result: Record<string, unknown> | null = (() => {
|
|
150
|
+
const keys = Object.keys(light)
|
|
151
|
+
if (keys.length !== Object.keys(dark).length) return null
|
|
152
|
+
const merged: Record<string, unknown> = {}
|
|
153
|
+
for (const key of keys) {
|
|
154
|
+
if (!Object.hasOwn(dark, key)) return null
|
|
155
|
+
const [first, second] = [light[key], dark[key]]
|
|
156
|
+
if (equal(first, second)) merged[key] = first
|
|
157
|
+
else if (
|
|
158
|
+
(key === 'color' || key.endsWith('Color')) &&
|
|
159
|
+
(typeof first === 'string' || typeof first === 'number') &&
|
|
160
|
+
(typeof second === 'string' || typeof second === 'number')
|
|
161
|
+
)
|
|
162
|
+
merged[key] = color(first, second)
|
|
163
|
+
else return null
|
|
164
|
+
}
|
|
165
|
+
return merged
|
|
166
|
+
})()
|
|
167
|
+
const value = result && Object.freeze(result)
|
|
168
|
+
pairs.set(dark, value)
|
|
169
|
+
return value ?? undefined
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function equal(left: unknown, right: unknown): boolean {
|
|
173
|
+
if (Object.is(left, right)) return true
|
|
174
|
+
if (Array.isArray(left))
|
|
175
|
+
return (
|
|
176
|
+
Array.isArray(right) &&
|
|
177
|
+
left.length === right.length &&
|
|
178
|
+
left.every((value, index) => equal(value, right[index]))
|
|
179
|
+
)
|
|
180
|
+
if (!record(left) || !record(right)) return false
|
|
181
|
+
const keys = Object.keys(left)
|
|
182
|
+
return (
|
|
183
|
+
keys.length === Object.keys(right).length &&
|
|
184
|
+
keys.every(
|
|
185
|
+
(key) => Object.hasOwn(right, key) && equal(left[key], right[key]),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function record(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
191
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
192
|
+
}
|
|
193
|
+
|
|
120
194
|
function bind(
|
|
121
195
|
select: (context: Context, input?: never) => unknown,
|
|
122
196
|
read: (context: Context) => readonly unknown[],
|
|
@@ -125,10 +199,10 @@ function bind(
|
|
|
125
199
|
return {
|
|
126
200
|
style: {
|
|
127
201
|
[binding]: (context: Context) => select(context, input),
|
|
128
|
-
[dependency]: (context: Context) =>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
202
|
+
[dependency]: (context: Context) =>
|
|
203
|
+
input === undefined
|
|
204
|
+
? read(context)
|
|
205
|
+
: [...read(context), ...key(input, context)],
|
|
132
206
|
},
|
|
133
207
|
}
|
|
134
208
|
}
|
package/src/unplugin/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import * as AtRules from '../compiler/internal/AtRules.js'
|
|
|
13
13
|
import * as Catalogs from '../compiler/internal/Catalogs.js'
|
|
14
14
|
import * as Contract from '../compiler/internal/Contract.js'
|
|
15
15
|
import * as Vite from '../vite/index.js'
|
|
16
|
+
import * as ThemeRules from '../web/internal/Themes.js'
|
|
16
17
|
|
|
17
18
|
/** Portable build configuration. Vite uses the options from `zyzz/vite` instead. */
|
|
18
19
|
export type Options = {
|
|
@@ -284,7 +285,9 @@ const portable = createUnplugin<Options | undefined, false>(
|
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// Only the combined stylesheet is emitted, so generated theme rules are shared once.
|
|
287
289
|
const result = compiler.compile({
|
|
290
|
+
[ThemeRules.shared]: 'all',
|
|
288
291
|
[Syntax.cache]: snapshot.programs(modules),
|
|
289
292
|
compiler: options.compiler,
|
|
290
293
|
reset: options.reset ? Reset.read() : undefined,
|
|
@@ -460,7 +463,7 @@ const portable = createUnplugin<Options | undefined, false>(
|
|
|
460
463
|
let line = 0
|
|
461
464
|
css = ''
|
|
462
465
|
for (const chunk of chunks) {
|
|
463
|
-
if (!chunk.code) continue
|
|
466
|
+
if (!chunk.code.trim()) continue
|
|
464
467
|
const traced = Mapping.fromMap(chunk.map)
|
|
465
468
|
for (const mapping of Mapping.allMappings(traced)) {
|
|
466
469
|
const generated = {
|
package/src/variants.ts
CHANGED
|
@@ -26,8 +26,9 @@ type ChoiceName<choices> = keyof choices extends infer key
|
|
|
26
26
|
? key
|
|
27
27
|
: never
|
|
28
28
|
: never
|
|
29
|
+
/** `void` targets skip dynamic choice return types, which resolve through `Checked` and cycle when selections are read first. */
|
|
29
30
|
type DynamicKeys<choices> = {
|
|
30
|
-
[key in keyof choices]: choices[key] extends (...args: never[]) =>
|
|
31
|
+
[key in keyof choices]: choices[key] extends (...args: never[]) => void
|
|
31
32
|
? key
|
|
32
33
|
: never
|
|
33
34
|
}[keyof choices]
|
|
@@ -36,7 +37,7 @@ type Choice<choices> =
|
|
|
36
37
|
| {
|
|
37
38
|
[key in DynamicKeys<choices>]: choices[key] extends (
|
|
38
39
|
values: infer values,
|
|
39
|
-
) =>
|
|
40
|
+
) => void
|
|
40
41
|
? { readonly [name in key]: values } & {
|
|
41
42
|
readonly [name in Exclude<keyof choices, key>]?: never
|
|
42
43
|
}
|