@geastack/core 0.1.25 → 0.1.27
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/events.cpp +2 -0
- package/include/canvas.h +5 -0
- package/include/events.h +2 -1
- package/include/image.h +2 -4
- package/index.d.ts +24 -7
- package/package.json +4 -4
- package/runtime.cpp +8 -0
- package/scripts/build-gea-vite-geatsc.mjs +192 -94
- package/scripts/gea-native-style-plugin.mjs +31 -0
package/events.cpp
CHANGED
package/include/canvas.h
CHANGED
|
@@ -95,6 +95,11 @@ public:
|
|
|
95
95
|
int width() const;
|
|
96
96
|
int height() const;
|
|
97
97
|
int strideBytes() const;
|
|
98
|
+
// Read or replace one logical framebuffer pixel in native format. Reads are
|
|
99
|
+
// independent of the current clip; writes obey the clip and mark the logical
|
|
100
|
+
// pixel dirty. Writes replace exactly (they do not apply global alpha).
|
|
101
|
+
pixel::native_t readPixelNative(int x, int y) const;
|
|
102
|
+
void writePixelNativeExact(int x, int y, pixel::native_t value);
|
|
98
103
|
|
|
99
104
|
// Software-scroll register: rotates the logical → physical row mapping
|
|
100
105
|
// inside a scroll region without moving any pixels. A pixel write at
|
package/include/events.h
CHANGED
package/include/image.h
CHANGED
|
@@ -124,11 +124,9 @@ private:
|
|
|
124
124
|
void materializeDeferred(int id) const;
|
|
125
125
|
int decodeGif(ImageSlot &image, const std::uint8_t *data, int length);
|
|
126
126
|
int decodeStatic(ImageSlot &image, const std::uint8_t *data, int length, bool opaqueHint);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// large covers so they fit the fragmented PSRAM). Fills `image`, returns 0/-1.
|
|
127
|
+
// Native JPEG backends, selected automatically when provided by the target.
|
|
128
|
+
int decodeJpegEsp(ImageSlot &image, const std::uint8_t *data, int length);
|
|
130
129
|
int decodeJpegTjpgd(ImageSlot &image, const std::uint8_t *data, int length);
|
|
131
|
-
#endif
|
|
132
130
|
int decodeNextGifFrame(ImageSlot &image);
|
|
133
131
|
// dispose() body without taking slotMutex_ (callers hold it).
|
|
134
132
|
void disposeLocked(int id);
|
package/index.d.ts
CHANGED
|
@@ -185,6 +185,8 @@ export type StyleLength =
|
|
|
185
185
|
| `max(${string})`
|
|
186
186
|
| `clamp(${string})`
|
|
187
187
|
export type StyleBox = StyleLength | string
|
|
188
|
+
export type StylePreferredSize = StyleLength | 'auto' | 'min-content' | 'max-content' | 'fit-content'
|
|
189
|
+
export type StyleOverflow = 'visible' | 'hidden' | 'clip' | 'scroll' | 'auto' | 'overlay'
|
|
188
190
|
export type ClassMap = Record<string, string | number | boolean | null | undefined>
|
|
189
191
|
export type ClassValue = string | ClassMap
|
|
190
192
|
|
|
@@ -213,15 +215,27 @@ export interface TouchHost {
|
|
|
213
215
|
export declare const touch: TouchHost
|
|
214
216
|
|
|
215
217
|
export interface Style {
|
|
218
|
+
font?: string
|
|
216
219
|
display?: 'block' | 'flex' | 'grid' | 'none'
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
visibility?: 'visible' | 'hidden' | 'collapse'
|
|
221
|
+
flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'
|
|
222
|
+
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse'
|
|
223
|
+
flexFlow?: string
|
|
224
|
+
boxSizing?: 'content-box' | 'border-box'
|
|
225
|
+
float?: 'none' | 'left' | 'right'
|
|
226
|
+
clear?: 'none' | 'left' | 'right' | 'both'
|
|
227
|
+
writingMode?: 'horizontal-tb' | 'vertical-lr' | 'vertical-rl' | 'sideways-lr' | 'sideways-rl'
|
|
228
|
+
direction?: 'ltr' | 'rtl'
|
|
229
|
+
rowGap?: StyleLength
|
|
230
|
+
columnGap?: StyleLength
|
|
231
|
+
order?: number
|
|
232
|
+
alignContent?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'space-between' | 'space-around'
|
|
219
233
|
justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around'
|
|
220
234
|
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch'
|
|
221
235
|
alignSelf?: 'auto' | 'flex-start' | 'center' | 'flex-end' | 'stretch'
|
|
222
236
|
gap?: StyleLength
|
|
223
|
-
width?:
|
|
224
|
-
height?:
|
|
237
|
+
width?: StylePreferredSize
|
|
238
|
+
height?: StylePreferredSize
|
|
225
239
|
minWidth?: StyleLength
|
|
226
240
|
minHeight?: StyleLength
|
|
227
241
|
maxWidth?: StyleLength
|
|
@@ -257,9 +271,10 @@ export interface Style {
|
|
|
257
271
|
fontFamily?: string
|
|
258
272
|
fontSize?: StyleLength
|
|
259
273
|
textAlign?: 'left' | 'center' | 'right'
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
whiteSpace?: 'normal' | 'nowrap' | 'pre' | 'pre-wrap' | 'pre-line' | 'break-spaces'
|
|
275
|
+
overflow?: StyleOverflow | `${StyleOverflow} ${StyleOverflow}`
|
|
276
|
+
overflowX?: StyleOverflow
|
|
277
|
+
overflowY?: StyleOverflow
|
|
263
278
|
transform?: string | number
|
|
264
279
|
rotate?: string | number
|
|
265
280
|
scale?: string | number
|
|
@@ -445,6 +460,7 @@ export interface GeaEventMap {
|
|
|
445
460
|
input: InputEvent
|
|
446
461
|
keydown: KeyEvent
|
|
447
462
|
rotary: RotaryEvent
|
|
463
|
+
scroll: Event
|
|
448
464
|
}
|
|
449
465
|
|
|
450
466
|
export type TouchEventHandler = (event: TouchEvent) => void
|
|
@@ -1523,6 +1539,7 @@ export interface NativeButtonProps extends NativeEventAttributes {
|
|
|
1523
1539
|
}
|
|
1524
1540
|
|
|
1525
1541
|
export interface NativeVirtualListProps extends NativeEventAttributes {
|
|
1542
|
+
onScroll?: (event: Event) => void
|
|
1526
1543
|
id?: string
|
|
1527
1544
|
class?: ClassValue
|
|
1528
1545
|
style?: Style
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geastack/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "GeaStack app API, reactivity headers, runtime sources, and type declarations.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"@babel/parser": "^7.23.0",
|
|
61
61
|
"@babel/traverse": "^7.23.0",
|
|
62
62
|
"@babel/types": "^7.23.0",
|
|
63
|
-
"@geajs/core": "1.
|
|
64
|
-
"@geajs/vite-plugin": "1.
|
|
63
|
+
"@geajs/core": "1.5.1",
|
|
64
|
+
"@geajs/vite-plugin": "1.5.0",
|
|
65
65
|
"@geastack/compiler": "^1.0.15",
|
|
66
|
-
"@geastack/geatsc-plugin-gea": "^0.1.
|
|
66
|
+
"@geastack/geatsc-plugin-gea": "^0.1.8",
|
|
67
67
|
"opentype.js": "^2.0.0",
|
|
68
68
|
"vite": "^8.0.0"
|
|
69
69
|
}
|
package/runtime.cpp
CHANGED
|
@@ -35,6 +35,13 @@
|
|
|
35
35
|
#define GEA_EMBEDDED_WIFI_EARLY_CONNECT 0
|
|
36
36
|
#endif
|
|
37
37
|
|
|
38
|
+
// UI/audio applications can keep touch event-driven rather than spending CPU
|
|
39
|
+
// on back-to-back no-op frames between samples. Touch events still request an
|
|
40
|
+
// immediate frame; animations continue on the configured frame timer.
|
|
41
|
+
#ifndef GEA_EMBEDDED_CONTINUOUS_TOUCH_FRAMES
|
|
42
|
+
#define GEA_EMBEDDED_CONTINUOUS_TOUCH_FRAMES 1
|
|
43
|
+
#endif
|
|
44
|
+
|
|
38
45
|
#include "css/declarative.h"
|
|
39
46
|
#include "css/engine.h"
|
|
40
47
|
#include "ui/style.h"
|
|
@@ -190,6 +197,7 @@ void dispatch_event(const events::Event &event, const RuntimeOptions &options)
|
|
|
190
197
|
for (;;) {
|
|
191
198
|
const bool catchUp = services::FrameScheduler::takeCatchUpRequest();
|
|
192
199
|
const bool activeDrag =
|
|
200
|
+
GEA_EMBEDDED_CONTINUOUS_TOUCH_FRAMES &&
|
|
193
201
|
events::TouchRuntime::gestureActiveWithin(services::FrameScheduler::nowMs(), 50);
|
|
194
202
|
// TE single-clock: never run a frame back-to-back here — the panel TE posts the
|
|
195
203
|
// next frame exactly one VBlank later, which is the whole point. Back-to-back
|
|
@@ -493,6 +493,7 @@ function writeCompatViteConfig({ configPath, compatSrcDir, appDir, entry, viteOu
|
|
|
493
493
|
.join('\n')
|
|
494
494
|
const config = `import { resolve } from 'node:path'
|
|
495
495
|
import { geaPlugin } from ${q(geaPluginResolved)}
|
|
496
|
+
import { geaNativeStylePlugin } from ${q(path.join(coreRoot, 'scripts/gea-native-style-plugin.mjs'))}
|
|
496
497
|
import { geaAppleNativeModuleAliases, geaModuleGraphPlugins } from ${q(moduleGraphPluginPath)}
|
|
497
498
|
|
|
498
499
|
const root = ${q(compatSrcDir)}
|
|
@@ -510,7 +511,7 @@ export default {
|
|
|
510
511
|
define: {
|
|
511
512
|
${defineEntries}
|
|
512
513
|
},
|
|
513
|
-
plugins: [geaPlugin({ ir: { enabled: true, outFile: ${q(geaIrPath)} } }), ...geaModuleGraph],
|
|
514
|
+
plugins: [geaNativeStylePlugin({ runtimeEntry: ${q(compilerRuntimeEntry)} }), geaPlugin({ ir: { enabled: true, outFile: ${q(geaIrPath)} } }), ...geaModuleGraph],
|
|
514
515
|
resolve: {
|
|
515
516
|
alias: [
|
|
516
517
|
${compilerRuntimeAlias}
|
|
@@ -1491,6 +1492,7 @@ function parseStaticSimpleSelectorSpec(rawSimple) {
|
|
|
1491
1492
|
wantsRoot: false,
|
|
1492
1493
|
wantsFirstChild: false,
|
|
1493
1494
|
wantsLastChild: false,
|
|
1495
|
+
wantsHover: false,
|
|
1494
1496
|
}
|
|
1495
1497
|
let i = 0
|
|
1496
1498
|
if (simple[i] !== '.' && simple[i] !== '#' && simple[i] !== ':') {
|
|
@@ -1514,13 +1516,14 @@ function parseStaticSimpleSelectorSpec(rawSimple) {
|
|
|
1514
1516
|
if (token === 'root') spec.wantsRoot = true
|
|
1515
1517
|
else if (token === 'first-child') spec.wantsFirstChild = true
|
|
1516
1518
|
else if (token === 'last-child') spec.wantsLastChild = true
|
|
1519
|
+
else if (token === 'hover') spec.wantsHover = true
|
|
1517
1520
|
else return null
|
|
1518
1521
|
} else {
|
|
1519
1522
|
return null
|
|
1520
1523
|
}
|
|
1521
1524
|
}
|
|
1522
1525
|
if (!spec.tag && !spec.id && spec.classes.length === 0 &&
|
|
1523
|
-
!spec.wantsRoot && !spec.wantsFirstChild && !spec.wantsLastChild) {
|
|
1526
|
+
!spec.wantsRoot && !spec.wantsFirstChild && !spec.wantsLastChild && !spec.wantsHover) {
|
|
1524
1527
|
return null
|
|
1525
1528
|
}
|
|
1526
1529
|
return spec
|
|
@@ -1555,7 +1558,7 @@ function staticSimpleSelectorSpecCode(simple) {
|
|
|
1555
1558
|
const tag = simple.tag ? JSON.stringify(simple.tag) : 'nullptr'
|
|
1556
1559
|
const id = simple.id ? JSON.stringify(simple.id) : 'nullptr'
|
|
1557
1560
|
const classes = simple.classes.map((className) => JSON.stringify(className)).join(', ')
|
|
1558
|
-
return `{${tag}, ${id}, {${classes}}, ${simple.wantsRoot ? 'true' : 'false'}, ${simple.wantsFirstChild ? 'true' : 'false'}, ${simple.wantsLastChild ? 'true' : 'false'}}`
|
|
1561
|
+
return `{${tag}, ${id}, {${classes}}, ${simple.wantsRoot ? 'true' : 'false'}, ${simple.wantsFirstChild ? 'true' : 'false'}, ${simple.wantsLastChild ? 'true' : 'false'}, ${simple.wantsHover ? 'true' : 'false'}}`
|
|
1559
1562
|
}
|
|
1560
1563
|
|
|
1561
1564
|
function staticSelectorPartSpecCode(part) {
|
|
@@ -1872,8 +1875,8 @@ function parseStaticCssLengthSpec(rawValue) {
|
|
|
1872
1875
|
|
|
1873
1876
|
function cssAlignKeywordValue(value) {
|
|
1874
1877
|
const align = new Map([
|
|
1875
|
-
['flex-start',
|
|
1876
|
-
['start',
|
|
1878
|
+
['flex-start', 6],
|
|
1879
|
+
['start', 6],
|
|
1877
1880
|
['stretch', 0],
|
|
1878
1881
|
['normal', 0],
|
|
1879
1882
|
['center', 1],
|
|
@@ -1939,8 +1942,9 @@ function cssStaticColorTarget(property) {
|
|
|
1939
1942
|
case 'color':
|
|
1940
1943
|
return 'Color'
|
|
1941
1944
|
case 'background':
|
|
1942
|
-
case 'background-color':
|
|
1943
1945
|
return 'Background'
|
|
1946
|
+
case 'background-color':
|
|
1947
|
+
return 'BackgroundColor'
|
|
1944
1948
|
case 'active-background':
|
|
1945
1949
|
case 'active-background-color':
|
|
1946
1950
|
return 'ActiveBackground'
|
|
@@ -1997,9 +2001,6 @@ function parseStaticCssColorRef(rawValue) {
|
|
|
1997
2001
|
function cssIgnoredProperty(property) {
|
|
1998
2002
|
return new Set([
|
|
1999
2003
|
'color-scheme',
|
|
2000
|
-
'background-position',
|
|
2001
|
-
'box-sizing',
|
|
2002
|
-
'font',
|
|
2003
2004
|
'grid-column',
|
|
2004
2005
|
'isolation',
|
|
2005
2006
|
'letter-spacing',
|
|
@@ -2008,7 +2009,6 @@ function cssIgnoredProperty(property) {
|
|
|
2008
2009
|
'scroll-snap-type',
|
|
2009
2010
|
'scrollbar-width',
|
|
2010
2011
|
'text-shadow',
|
|
2011
|
-
'transform-style',
|
|
2012
2012
|
'transition',
|
|
2013
2013
|
'cursor',
|
|
2014
2014
|
'-webkit-tap-highlight-color',
|
|
@@ -2047,6 +2047,10 @@ function cssStaticPropertyValue(property, rawValue) {
|
|
|
2047
2047
|
const values = new Map([['block', 0], ['none', 1], ['grid', 2], ['inline-grid', 2], ['flex', 3], ['inline-flex', 3]])
|
|
2048
2048
|
return values.has(value) ? direct('Display', values.get(value)) : null
|
|
2049
2049
|
}
|
|
2050
|
+
case 'box-sizing':
|
|
2051
|
+
if (value === 'content-box') return direct('BoxSizing', 0)
|
|
2052
|
+
if (value === 'border-box') return direct('BoxSizing', 1)
|
|
2053
|
+
return null
|
|
2050
2054
|
case 'flex-direction':
|
|
2051
2055
|
if (value === 'column') return direct('FlexDirection', 0)
|
|
2052
2056
|
if (value === 'row') return direct('FlexDirection', 1)
|
|
@@ -2134,10 +2138,6 @@ function cssStaticPropertyValue(property, rawValue) {
|
|
|
2134
2138
|
if (lower === 'none') return direct('ImageFit', 3)
|
|
2135
2139
|
if (lower === 'scale-down') return direct('ImageFit', 4)
|
|
2136
2140
|
return null
|
|
2137
|
-
case 'rotate': {
|
|
2138
|
-
const angle = parseStaticAngleTenths(value)
|
|
2139
|
-
return angle === null ? null : direct('TransformRotate', angle)
|
|
2140
|
-
}
|
|
2141
2141
|
case 'text-align':
|
|
2142
2142
|
if (value === 'left' || value === 'start') return direct('TextAlign', 0)
|
|
2143
2143
|
if (value === 'center') return direct('TextAlign', 1)
|
|
@@ -2208,11 +2208,8 @@ function cssStaticPropertyValues(property, rawValue) {
|
|
|
2208
2208
|
}
|
|
2209
2209
|
const boxLength = directLengthValue(length)
|
|
2210
2210
|
if (boxLength !== null) {
|
|
2211
|
-
|
|
2212
|
-
if (
|
|
2213
|
-
if (property === 'border-right') return [direct('BorderRightWidth', boxLength)]
|
|
2214
|
-
if (property === 'border-bottom') return [direct('BorderBottomWidth', boxLength)]
|
|
2215
|
-
if (property === 'border-left') return [direct('BorderLeftWidth', boxLength)]
|
|
2211
|
+
const border = {border:'Border','border-top':'BorderTop','border-right':'BorderRight','border-bottom':'BorderBottom','border-left':'BorderLeft'}[property]
|
|
2212
|
+
if (border) return [direct(`${border}Width`, boxLength), direct(`${border}Relief`, 0), direct(`${border}ColorCurrent`, 1)]
|
|
2216
2213
|
if (property === 'margin') {
|
|
2217
2214
|
return [
|
|
2218
2215
|
direct('MarginTop', boxLength),
|
|
@@ -2250,14 +2247,37 @@ function cssStaticPropertyValues(property, rawValue) {
|
|
|
2250
2247
|
}
|
|
2251
2248
|
}
|
|
2252
2249
|
|
|
2253
|
-
if (property === '
|
|
2254
|
-
const
|
|
2255
|
-
if (
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2250
|
+
if (property === 'rotate') {
|
|
2251
|
+
const parts = value.toLowerCase().split(/\s+/)
|
|
2252
|
+
if (parts.length > 1 && (/(deg|rad|turn|grad)$/.test(parts[0]) ||
|
|
2253
|
+
(parts.length === 2 && ['x', 'y', 'z'].includes(parts[1])))) parts.push(parts.shift())
|
|
2254
|
+
const none = value.toLowerCase() === 'none'
|
|
2255
|
+
const angle = none ? 0 : parseStaticAngleTenths(parts.at(-1))
|
|
2256
|
+
if (angle === null || ![1, 2, 4].includes(parts.length)) return []
|
|
2257
|
+
if (!none && !/(deg|rad|turn)$/i.test(parts.at(-1)) && Number(parts.at(-1)) !== 0) return []
|
|
2258
|
+
let axes = [0, 0, 1000000]
|
|
2259
|
+
if (parts.length === 2) {
|
|
2260
|
+
const axis = ['x', 'y', 'z'].indexOf(parts[0])
|
|
2261
|
+
if (axis < 0) return []
|
|
2262
|
+
axes = [0, 0, 0]; axes[axis] = 1000000
|
|
2263
|
+
} else if (parts.length === 4) {
|
|
2264
|
+
const vector = parts.slice(0, 3).map(Number)
|
|
2265
|
+
if (!vector.every(Number.isFinite)) return []
|
|
2266
|
+
const magnitude = Math.max(...vector.map(Math.abs))
|
|
2267
|
+
axes = magnitude ? vector.map(v => roundCssNumber(v / magnitude * 1000000)) : [0, 0, 0]
|
|
2260
2268
|
}
|
|
2269
|
+
return [direct('RotatePresent', none ? 0 : 1), direct('RotateAngle', axes.some(Boolean) ? angle : 0),
|
|
2270
|
+
direct('RotateAxisX', axes[0]), direct('RotateAxisY', axes[1]), direct('RotateAxisZ', axes[2])]
|
|
2271
|
+
}
|
|
2272
|
+
if (property === 'scale') {
|
|
2273
|
+
const none = value.toLowerCase() === 'none'
|
|
2274
|
+
const parts = none ? ['1'] : value.split(/\s+/)
|
|
2275
|
+
if (parts.length < 1 || parts.length > 3) return []
|
|
2276
|
+
const factors = parts.map(v => v.endsWith('%') ? Number(v.slice(0, -1)) / 100 : Number(v))
|
|
2277
|
+
if (!factors.every(Number.isFinite)) return []
|
|
2278
|
+
const scales = factors.map(v => roundCssNumber(Math.max(-32768, Math.min(32767, v * 1000))))
|
|
2279
|
+
return [direct('ScalePresent', none ? 0 : 1), direct('ScaleX', scales[0]),
|
|
2280
|
+
direct('ScaleY', scales[1] ?? scales[0]), direct('ScaleZ', scales[2] ?? 1000)]
|
|
2261
2281
|
}
|
|
2262
2282
|
|
|
2263
2283
|
return []
|
|
@@ -2311,27 +2331,15 @@ function cssStaticFlexValue(property, rawValue) {
|
|
|
2311
2331
|
const setup = []
|
|
2312
2332
|
const words = splitCssWords(rawValue.trim())
|
|
2313
2333
|
if (words.length === 0) return null
|
|
2334
|
+
// The compact static API has no shrink field. Preserve declarations it
|
|
2335
|
+
// cannot represent through the full compiler, including keyword semantics.
|
|
2336
|
+
if (words.some(word => /^(none|auto|content|max-content|min-content|fit-content)$/i.test(word))) return null
|
|
2314
2337
|
for (const token of words) {
|
|
2315
|
-
const lower = token.toLowerCase()
|
|
2316
|
-
if (lower === 'none') {
|
|
2317
|
-
grow = 0
|
|
2318
|
-
hasBasis = false
|
|
2319
|
-
continue
|
|
2320
|
-
}
|
|
2321
|
-
if (
|
|
2322
|
-
lower === 'auto' ||
|
|
2323
|
-
lower === 'content' ||
|
|
2324
|
-
lower === 'max-content' ||
|
|
2325
|
-
lower === 'min-content' ||
|
|
2326
|
-
lower === 'fit-content' ||
|
|
2327
|
-
lower.includes('%')
|
|
2328
|
-
) {
|
|
2329
|
-
hasBasis = false
|
|
2330
|
-
continue
|
|
2331
|
-
}
|
|
2332
2338
|
const number = Number(token)
|
|
2333
2339
|
if (Number.isFinite(number) && token.match(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/)) {
|
|
2340
|
+
if (number < 0 || numberIndex > 1) return null
|
|
2334
2341
|
if (numberIndex === 0) grow = roundCssNumber(number)
|
|
2342
|
+
else if (number !== 1) return null
|
|
2335
2343
|
numberIndex++
|
|
2336
2344
|
continue
|
|
2337
2345
|
}
|
|
@@ -2399,6 +2407,7 @@ function cssStaticBorderRadiusValue(property, rawValue) {
|
|
|
2399
2407
|
|
|
2400
2408
|
function cssStaticBorderValue(property, rawValue) {
|
|
2401
2409
|
if (property !== 'border') return null
|
|
2410
|
+
if (/\b(?:groove|ridge|inset|outset)\b/i.test(rawValue)) return null
|
|
2402
2411
|
const parts = splitCssWords(rawValue.trim())
|
|
2403
2412
|
if (parts.length === 0) return null
|
|
2404
2413
|
const width = parseStaticCssLengthSpec(parts[0])
|
|
@@ -2414,6 +2423,8 @@ function cssStaticBorderValue(property, rawValue) {
|
|
|
2414
2423
|
}
|
|
2415
2424
|
|
|
2416
2425
|
function cssStaticBorderShorthandValue(property, rawValue) {
|
|
2426
|
+
// The compact width/color API cannot carry a shaded border style.
|
|
2427
|
+
if (/\b(?:groove|ridge|inset|outset)\b/i.test(rawValue)) return null
|
|
2417
2428
|
const targets = {
|
|
2418
2429
|
border: { width: 'BorderWidth', color: 'Border', colorProperty: 'border-color', side: false },
|
|
2419
2430
|
'border-top': { width: 'BorderTopWidth', color: 'BorderTop', colorProperty: 'border-top-color', side: true },
|
|
@@ -2441,7 +2452,8 @@ function cssStaticBorderShorthandValue(property, rawValue) {
|
|
|
2441
2452
|
}
|
|
2442
2453
|
}
|
|
2443
2454
|
if (!targets.side && color && !colorVar) return null
|
|
2444
|
-
return { setup: width.setup, widthTarget: targets.width,
|
|
2455
|
+
return { setup: width.setup, widthTarget: targets.width, reliefTarget: targets.width.replace('Width', 'Relief'),
|
|
2456
|
+
currentColorTarget: `${targets.color}ColorCurrent`, width: width.spec, color, colorVar }
|
|
2445
2457
|
}
|
|
2446
2458
|
|
|
2447
2459
|
function staticColorCode(color) {
|
|
@@ -2781,6 +2793,9 @@ function cssStaticBackgroundValue(property, rawValue) {
|
|
|
2781
2793
|
if (cssHasDynamicValue(rawValue)) return null
|
|
2782
2794
|
const layers = splitTopLevelCss(rawValue, ',')
|
|
2783
2795
|
if (layers.length === 0) return null
|
|
2796
|
+
// The gradient registration API has no base-color field. Keep combined
|
|
2797
|
+
// color/image shorthands on the complete native declaration parser.
|
|
2798
|
+
if (layers.some(layer => splitCssWords(layer).some(word => parseStaticCssColor(word) || /^(?:none|border-box|padding-box|content-box)$/i.test(word)))) return null
|
|
2784
2799
|
|
|
2785
2800
|
const out = {
|
|
2786
2801
|
setup: [],
|
|
@@ -2816,6 +2831,7 @@ function cssStaticBackgroundValue(property, rawValue) {
|
|
|
2816
2831
|
}
|
|
2817
2832
|
|
|
2818
2833
|
if (!out.gradient) return null
|
|
2834
|
+
if (layers.length !== 1 + Number(out.hasOverlayGradient) + Number(Boolean(out.radialGradient?.enabled)) + Number(Boolean(out.gridX)) + Number(Boolean(out.gridY))) return null
|
|
2819
2835
|
if (out.gridX && out.gridY && !staticColorEquals(out.gridX.color, out.gridY.color) && out.gridOrder.join('') !== 'xy') return null
|
|
2820
2836
|
return out
|
|
2821
2837
|
}
|
|
@@ -2837,6 +2853,9 @@ function cssStaticBackgroundFullValue(property, rawValue) {
|
|
|
2837
2853
|
return null
|
|
2838
2854
|
const layers = splitTopLevelCss(rawValue, ',')
|
|
2839
2855
|
if (layers.length === 0) return null
|
|
2856
|
+
// The gradient registration API has no base-color field. Keep combined
|
|
2857
|
+
// color/image shorthands on the complete native declaration parser.
|
|
2858
|
+
if (layers.some(layer => splitCssWords(layer).some(word => parseStaticCssColor(word) || /^(?:none|border-box|padding-box|content-box)$/i.test(word)))) return null
|
|
2840
2859
|
|
|
2841
2860
|
const out = {
|
|
2842
2861
|
setup: [],
|
|
@@ -2883,6 +2902,7 @@ function cssStaticBackgroundFullValue(property, rawValue) {
|
|
|
2883
2902
|
}
|
|
2884
2903
|
|
|
2885
2904
|
if (!out.gradient) return null
|
|
2905
|
+
if (layers.length !== 1 + Number(out.hasOverlayGradient) + Number(Boolean(out.radialGradient?.enabled)) + Number(Boolean(out.gridX)) + Number(Boolean(out.gridY))) return null
|
|
2886
2906
|
if (out.gridX && out.gridY && !staticColorEquals(out.gridX.color, out.gridY.color) && out.gridOrder.join('') !== 'xy') return null
|
|
2887
2907
|
return out.needsFullApi ? out : null
|
|
2888
2908
|
}
|
|
@@ -2891,12 +2911,15 @@ function cssStaticBackgroundSizeValue(property, rawValue) {
|
|
|
2891
2911
|
if (property !== 'background-size') return null
|
|
2892
2912
|
if (cssHasDynamicValue(rawValue)) return null
|
|
2893
2913
|
const layers = splitTopLevelCss(rawValue, ',')
|
|
2894
|
-
if (layers.length
|
|
2914
|
+
if (layers.length !== 1) return null
|
|
2895
2915
|
const parts = splitCssWords(layers[0])
|
|
2896
|
-
if (parts.length === 0) return null
|
|
2897
|
-
const
|
|
2916
|
+
if (parts.length === 0 || parts.length > 2) return null
|
|
2917
|
+
const parseSize = value => value.toLowerCase() === 'auto'
|
|
2918
|
+
? { setup: [], spec: { unit: 'Auto', value: '0' } }
|
|
2919
|
+
: parseStaticCssLengthSpec(value)
|
|
2920
|
+
const x = parseSize(parts[0])
|
|
2898
2921
|
if (!x) return null
|
|
2899
|
-
const y = parts.length > 1 ?
|
|
2922
|
+
const y = parseSize(parts.length > 1 ? parts[1] : 'auto')
|
|
2900
2923
|
if (!y) return null
|
|
2901
2924
|
return { setup: [...x.setup, ...(y === x ? [] : y.setup)], stepX: x.spec, stepY: y.spec }
|
|
2902
2925
|
}
|
|
@@ -3192,6 +3215,7 @@ const staticTransformFlags = {
|
|
|
3192
3215
|
translateZ: 1 << 5,
|
|
3193
3216
|
scaleX: 1 << 8,
|
|
3194
3217
|
scaleY: 1 << 9,
|
|
3218
|
+
scaleZ: 1 << 13,
|
|
3195
3219
|
}
|
|
3196
3220
|
|
|
3197
3221
|
function parseStaticAngleTenths(rawValue) {
|
|
@@ -3212,7 +3236,14 @@ function parseStaticScalePermille(rawValue) {
|
|
|
3212
3236
|
return roundCssNumber(value * 1000)
|
|
3213
3237
|
}
|
|
3214
3238
|
|
|
3239
|
+
function multiplyStaticScalePermille(left, right) {
|
|
3240
|
+
return clampInt16(roundCssNumber((left * right) / 1000))
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3215
3243
|
function parseStaticTransformValue(rawValue) {
|
|
3244
|
+
// Multiple rotation functions need ordered composition. Use the shared C++
|
|
3245
|
+
// CSS compiler rather than independently decomposing a matrix in JS.
|
|
3246
|
+
if ((rawValue.match(/rotate(?:x|y|z|3d)?\s*\(/gi) || []).length > 1) return null
|
|
3216
3247
|
const value = rawValue.trim()
|
|
3217
3248
|
const out = {
|
|
3218
3249
|
setup: [],
|
|
@@ -3225,6 +3256,7 @@ function parseStaticTransformValue(rawValue) {
|
|
|
3225
3256
|
translateZ: zeroStaticLengthSpec(),
|
|
3226
3257
|
scaleX: 1000,
|
|
3227
3258
|
scaleY: 1000,
|
|
3259
|
+
scaleZ: 1000,
|
|
3228
3260
|
}
|
|
3229
3261
|
let sawTransform = false
|
|
3230
3262
|
let i = 0
|
|
@@ -3247,6 +3279,9 @@ function parseStaticTransformValue(rawValue) {
|
|
|
3247
3279
|
if (depth !== 0) return null
|
|
3248
3280
|
const arg = value.slice(argStart, i - 1)
|
|
3249
3281
|
const args = splitTopLevelCss(arg, ',')
|
|
3282
|
+
const axes = name === 'translatex' ? 1 : name === 'translatey' ? 2 : name === 'translatez' ? 4 :
|
|
3283
|
+
(name === 'translate' || name === 'translate3d') ? (1 << Math.min(3, args.length)) - 1 : 0
|
|
3284
|
+
out.flags = (out.flags & ~(axes << 10)) | ((out.flags & 7) ? 0 : (axes << 10))
|
|
3250
3285
|
sawTransform = true
|
|
3251
3286
|
|
|
3252
3287
|
const setLength = (field, flag, raw) => {
|
|
@@ -3288,19 +3323,34 @@ function parseStaticTransformValue(rawValue) {
|
|
|
3288
3323
|
const sx = parseStaticScalePermille(args[0] ?? arg)
|
|
3289
3324
|
const sy = parseStaticScalePermille(args[1] ?? args[0] ?? arg)
|
|
3290
3325
|
if (sx === null || sy === null) return null
|
|
3291
|
-
out.scaleX = sx
|
|
3292
|
-
out.scaleY = sy
|
|
3326
|
+
out.scaleX = multiplyStaticScalePermille(out.scaleX, sx)
|
|
3327
|
+
out.scaleY = multiplyStaticScalePermille(out.scaleY, sy)
|
|
3293
3328
|
out.flags |= staticTransformFlags.scaleX | staticTransformFlags.scaleY
|
|
3329
|
+
} else if (name === 'scale3d') {
|
|
3330
|
+
if (args.length !== 3) return null
|
|
3331
|
+
const sx = parseStaticScalePermille(args[0])
|
|
3332
|
+
const sy = parseStaticScalePermille(args[1])
|
|
3333
|
+
const sz = parseStaticScalePermille(args[2])
|
|
3334
|
+
if (sx === null || sy === null || sz === null) return null
|
|
3335
|
+
out.scaleX = multiplyStaticScalePermille(out.scaleX, sx)
|
|
3336
|
+
out.scaleY = multiplyStaticScalePermille(out.scaleY, sy)
|
|
3337
|
+
out.scaleZ = multiplyStaticScalePermille(out.scaleZ, sz)
|
|
3338
|
+
out.flags |= staticTransformFlags.scaleX | staticTransformFlags.scaleY | staticTransformFlags.scaleZ
|
|
3294
3339
|
} else if (name === 'scalex') {
|
|
3295
3340
|
const scale = parseStaticScalePermille(arg)
|
|
3296
3341
|
if (scale === null) return null
|
|
3297
|
-
out.scaleX = scale
|
|
3342
|
+
out.scaleX = multiplyStaticScalePermille(out.scaleX, scale)
|
|
3298
3343
|
out.flags |= staticTransformFlags.scaleX
|
|
3299
3344
|
} else if (name === 'scaley') {
|
|
3300
3345
|
const scale = parseStaticScalePermille(arg)
|
|
3301
3346
|
if (scale === null) return null
|
|
3302
|
-
out.scaleY = scale
|
|
3347
|
+
out.scaleY = multiplyStaticScalePermille(out.scaleY, scale)
|
|
3303
3348
|
out.flags |= staticTransformFlags.scaleY
|
|
3349
|
+
} else if (name === 'scalez') {
|
|
3350
|
+
const scale = parseStaticScalePermille(arg)
|
|
3351
|
+
if (scale === null) return null
|
|
3352
|
+
out.scaleZ = multiplyStaticScalePermille(out.scaleZ, scale)
|
|
3353
|
+
out.flags |= staticTransformFlags.scaleZ
|
|
3304
3354
|
} else {
|
|
3305
3355
|
return null
|
|
3306
3356
|
}
|
|
@@ -3313,17 +3363,29 @@ function cssStaticTransformValue(property, rawValue) {
|
|
|
3313
3363
|
return parseStaticTransformValue(rawValue)
|
|
3314
3364
|
}
|
|
3315
3365
|
|
|
3316
|
-
function staticTransformArgs(transform) {
|
|
3317
|
-
|
|
3366
|
+
function staticTransformArgs(transform, mediaArg = null) {
|
|
3367
|
+
const fields = `${transform.flags}, ${transform.rotateX}, ${transform.rotateY}, ${transform.rotateZ}, ${staticLengthSpecCode(transform.translateX)}, ${staticLengthSpecCode(transform.translateY)}, ${staticLengthSpecCode(transform.translateZ)}, ${transform.scaleX}, ${transform.scaleY}`
|
|
3368
|
+
if (mediaArg === null) return `${fields}, ${transform.scaleZ}`
|
|
3369
|
+
return `${fields}${mediaArg || ', nullptr'}, ${transform.scaleZ}`
|
|
3318
3370
|
}
|
|
3319
3371
|
|
|
3320
3372
|
const staticAnimatableLengthTargets = new Set(['Width', 'Height', 'Top', 'Left'])
|
|
3321
|
-
const staticAnimatableColorTargets = new Set(['Color', 'Background'])
|
|
3373
|
+
const staticAnimatableColorTargets = new Set(['Color', 'Background', 'BackgroundColor'])
|
|
3322
3374
|
const staticAnimatableDirectProperties = new Set([
|
|
3323
3375
|
'gea::embedded::ui::Property::Opacity',
|
|
3376
|
+
'gea::embedded::ui::Property::RotatePresent',
|
|
3377
|
+
'gea::embedded::ui::Property::RotateAngle',
|
|
3378
|
+
'gea::embedded::ui::Property::RotateAxisX',
|
|
3379
|
+
'gea::embedded::ui::Property::RotateAxisY',
|
|
3380
|
+
'gea::embedded::ui::Property::RotateAxisZ',
|
|
3381
|
+
'gea::embedded::ui::Property::ScalePresent',
|
|
3382
|
+
'gea::embedded::ui::Property::ScaleX',
|
|
3383
|
+
'gea::embedded::ui::Property::ScaleY',
|
|
3384
|
+
'gea::embedded::ui::Property::ScaleZ',
|
|
3324
3385
|
'gea::embedded::ui::Property::TransformRotate',
|
|
3325
3386
|
'gea::embedded::ui::Property::TransformScaleX',
|
|
3326
3387
|
'gea::embedded::ui::Property::TransformScaleY',
|
|
3388
|
+
'gea::embedded::ui::Property::TransformScaleZ',
|
|
3327
3389
|
])
|
|
3328
3390
|
|
|
3329
3391
|
function cssStaticFilterBlurValue(property, rawValue) {
|
|
@@ -3437,12 +3499,14 @@ function emitCssRuleRegistrations(kind, selector, name, value, mediaCondition) {
|
|
|
3437
3499
|
`__gea_stylesheet.registerStaticPropertyRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${direct.property}, ${direct.value}${mediaArg});`,
|
|
3438
3500
|
]
|
|
3439
3501
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3502
|
+
// Each native compiled property group has four slots.
|
|
3503
|
+
const registrations = []
|
|
3504
|
+
for (let i = 0; i < directProperties.length; i += 4) {
|
|
3505
|
+
const entries = directProperties.slice(i, i + 4)
|
|
3506
|
+
.map(direct => `{${direct.property}, ${direct.value}}`).join(', ')
|
|
3507
|
+
registrations.push(`__gea_stylesheet.registerStaticPropertyGroupRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, {${entries}}${mediaArg});`)
|
|
3508
|
+
}
|
|
3509
|
+
return registrations
|
|
3446
3510
|
}
|
|
3447
3511
|
const flex = cssStaticFlexValue(name, value)
|
|
3448
3512
|
if (flex) {
|
|
@@ -3478,6 +3542,7 @@ function emitCssRuleRegistrations(kind, selector, name, value, mediaCondition) {
|
|
|
3478
3542
|
const calls = [
|
|
3479
3543
|
...borderShorthand.setup,
|
|
3480
3544
|
`__gea_stylesheet.registerStaticLengthSpecRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, gea::embedded::ui::StaticStyleLengthProperty::${borderShorthand.widthTarget}, ${staticLengthSpecCode(borderShorthand.width)}${mediaArg});`,
|
|
3545
|
+
`__gea_stylesheet.registerStaticPropertyRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, gea::embedded::ui::Property::${borderShorthand.reliefTarget}, 0${mediaArg});`,
|
|
3481
3546
|
]
|
|
3482
3547
|
if (borderShorthand.colorVar) {
|
|
3483
3548
|
const colorVar = borderShorthand.colorVar
|
|
@@ -3485,6 +3550,8 @@ function emitCssRuleRegistrations(kind, selector, name, value, mediaCondition) {
|
|
|
3485
3550
|
} else if (borderShorthand.color) {
|
|
3486
3551
|
const color = borderShorthand.color
|
|
3487
3552
|
calls.push(`__gea_stylesheet.registerStaticColorRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, gea::embedded::ui::StaticStyleColorProperty::${color.target}, ${color.color.r}, ${color.color.g}, ${color.color.b}, ${color.color.a}${mediaArg});`)
|
|
3553
|
+
} else {
|
|
3554
|
+
calls.push(`__gea_stylesheet.registerStaticPropertyRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, gea::embedded::ui::Property::${borderShorthand.currentColorTarget}, 1${mediaArg});`)
|
|
3488
3555
|
}
|
|
3489
3556
|
return calls
|
|
3490
3557
|
}
|
|
@@ -3519,14 +3586,14 @@ function emitCssRuleRegistrations(kind, selector, name, value, mediaCondition) {
|
|
|
3519
3586
|
if (fullBackground) {
|
|
3520
3587
|
return [
|
|
3521
3588
|
...fullBackground.setup,
|
|
3522
|
-
`__gea_stylesheet.registerStaticBackgroundFullRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticLinearGradientRefCode(fullBackground.gradient)}, ${staticLinearGradientRefCode(fullBackground.overlayGradient)}, ${fullBackground.hasOverlayGradient ? 'true' : 'false'}, ${staticRadialGradientRefCode(fullBackground.radialGradient)}, ${staticGridLineCode(fullBackground.gridX)}, ${staticGridLineCode(fullBackground.gridY)}${mediaArg});`,
|
|
3589
|
+
`__gea_stylesheet.registerStaticBackgroundFullRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticLinearGradientRefCode(fullBackground.gradient)}, ${staticLinearGradientRefCode(fullBackground.overlayGradient)}, ${fullBackground.hasOverlayGradient ? 'true' : 'false'}, ${staticRadialGradientRefCode(fullBackground.radialGradient)}, ${staticGridLineCode(fullBackground.gridX)}, ${staticGridLineCode(fullBackground.gridY)}${mediaArg || ", nullptr"}, ${name === "background-image" ? "true" : "false"});`,
|
|
3523
3590
|
]
|
|
3524
3591
|
}
|
|
3525
3592
|
const background = cssStaticBackgroundValue(name, value)
|
|
3526
3593
|
if (background) {
|
|
3527
3594
|
return [
|
|
3528
3595
|
...background.setup,
|
|
3529
|
-
`__gea_stylesheet.registerStaticBackgroundRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticLinearGradientCode(background.gradient)}, ${staticLinearGradientCode(background.overlayGradient)}, ${background.hasOverlayGradient ? 'true' : 'false'}, ${staticGridLineCode(background.gridX)}, ${staticGridLineCode(background.gridY)}${mediaArg});`,
|
|
3596
|
+
`__gea_stylesheet.registerStaticBackgroundRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticLinearGradientCode(background.gradient)}, ${staticLinearGradientCode(background.overlayGradient)}, ${background.hasOverlayGradient ? 'true' : 'false'}, ${staticGridLineCode(background.gridX)}, ${staticGridLineCode(background.gridY)}${mediaArg || ", nullptr"}, ${name === "background-image" ? "true" : "false"});`,
|
|
3530
3597
|
]
|
|
3531
3598
|
}
|
|
3532
3599
|
const backgroundSize = cssStaticBackgroundSizeValue(name, value)
|
|
@@ -3591,7 +3658,7 @@ function emitCssRuleRegistrations(kind, selector, name, value, mediaCondition) {
|
|
|
3591
3658
|
if (transform) {
|
|
3592
3659
|
return [
|
|
3593
3660
|
...transform.setup,
|
|
3594
|
-
`__gea_stylesheet.registerStaticTransformRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticTransformArgs(transform)}
|
|
3661
|
+
`__gea_stylesheet.registerStaticTransformRule(${staticSelectorKind(kind)}, ${JSON.stringify(selector)}, ${staticTransformArgs(transform, mediaArg)});`,
|
|
3595
3662
|
]
|
|
3596
3663
|
}
|
|
3597
3664
|
}
|
|
@@ -3810,6 +3877,9 @@ function staticCssTapeLengthSpecParts(code) {
|
|
|
3810
3877
|
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) return null
|
|
3811
3878
|
const parts = splitTopLevelCppArgs(trimmed.slice(1, -1))
|
|
3812
3879
|
if (parts.length !== 2) return null
|
|
3880
|
+
// The tape is constexpr: a length expression's value is the id its
|
|
3881
|
+
// registration returned at run time, so that rule stays a plain call.
|
|
3882
|
+
if (!/^[-+]?(\d+\.?\d*|\.\d+)(e[-+]?\d+)?f?$/i.test(parts[1].trim())) return null
|
|
3813
3883
|
return { unit: parts[0], value: parts[1] }
|
|
3814
3884
|
}
|
|
3815
3885
|
|
|
@@ -3989,31 +4059,38 @@ auto __gea_css_tape_selector_kind = [](std::uint8_t kind) {
|
|
|
3989
4059
|
auto __gea_css_tape_length = [](std::uint8_t unit, float value) {
|
|
3990
4060
|
\treturn gea::embedded::ui::StaticStyleLengthSpec{static_cast<gea::embedded::ui::StaticStyleLengthUnit>(unit), value};
|
|
3991
4061
|
};
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
\tstd::
|
|
3997
|
-
\
|
|
3998
|
-
\
|
|
3999
|
-
\
|
|
4000
|
-
\
|
|
4001
|
-
\
|
|
4002
|
-
\
|
|
4003
|
-
\
|
|
4004
|
-
\
|
|
4005
|
-
\
|
|
4006
|
-
\
|
|
4007
|
-
\
|
|
4008
|
-
\
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
\
|
|
4012
|
-
\
|
|
4013
|
-
\
|
|
4014
|
-
\
|
|
4015
|
-
\
|
|
4016
|
-
\
|
|
4062
|
+
// Each tape's lists are static constexpr arrays the call points into: an
|
|
4063
|
+
// initializer_list argument is a temporary array, which the compiler builds on
|
|
4064
|
+
// the stack by copying it out of read-only data at every call.
|
|
4065
|
+
struct __GeaStaticCssTape {
|
|
4066
|
+
\tstd::size_t orderCount;
|
|
4067
|
+
\tconst __GeaStaticCssTapeKind *order;
|
|
4068
|
+
\tconst __GeaStaticCssPropertyOp *propertyOps;
|
|
4069
|
+
\tconst __GeaStaticCssLengthOp *lengthOps;
|
|
4070
|
+
\tconst __GeaStaticCssLengthOp *lengthSpecOps;
|
|
4071
|
+
\tconst __GeaStaticCssColorOp *colorOps;
|
|
4072
|
+
\tconst __GeaStaticCssColorVarOp *colorVarOps;
|
|
4073
|
+
\tconst __GeaStaticCssCustomLengthOp *customLengthOps;
|
|
4074
|
+
\tconst __GeaStaticCssCustomColorOp *customColorOps;
|
|
4075
|
+
\tconst __GeaStaticCssSelectorOnlyOp *boxShadowNoneOps;
|
|
4076
|
+
\tconst __GeaStaticCssNamedOp *fontFamilyOps;
|
|
4077
|
+
\tconst __GeaStaticCssLengthOnlyOp *filterBlurOps;
|
|
4078
|
+
\tconst __GeaStaticCssLineHeightOp *lineHeightOps;
|
|
4079
|
+
};
|
|
4080
|
+
auto __gea_register_static_css_tape = [&](const __GeaStaticCssTape &tape) {
|
|
4081
|
+
\tconst auto *propertyIt = tape.propertyOps;
|
|
4082
|
+
\tconst auto *lengthIt = tape.lengthOps;
|
|
4083
|
+
\tconst auto *lengthSpecIt = tape.lengthSpecOps;
|
|
4084
|
+
\tconst auto *colorIt = tape.colorOps;
|
|
4085
|
+
\tconst auto *colorVarIt = tape.colorVarOps;
|
|
4086
|
+
\tconst auto *customLengthIt = tape.customLengthOps;
|
|
4087
|
+
\tconst auto *customColorIt = tape.customColorOps;
|
|
4088
|
+
\tconst auto *boxShadowNoneIt = tape.boxShadowNoneOps;
|
|
4089
|
+
\tconst auto *fontFamilyIt = tape.fontFamilyOps;
|
|
4090
|
+
\tconst auto *filterBlurIt = tape.filterBlurOps;
|
|
4091
|
+
\tconst auto *lineHeightIt = tape.lineHeightOps;
|
|
4092
|
+
\tfor (std::size_t step = 0; step < tape.orderCount; ++step) {
|
|
4093
|
+
\t\tconst auto kind = tape.order[step];
|
|
4017
4094
|
\t\tswitch (kind) {
|
|
4018
4095
|
\t\tcase __GeaStaticCssTapeKind::Property: {
|
|
4019
4096
|
\t\t\tconst auto &op = *propertyIt++;
|
|
@@ -4114,12 +4191,27 @@ function compactStaticCssTapeCalls(calls) {
|
|
|
4114
4191
|
}
|
|
4115
4192
|
usedTape = true
|
|
4116
4193
|
const order = chunk.map((entry) => `__GeaStaticCssTapeKind::${entry.kind}`).join(', ')
|
|
4117
|
-
const
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4194
|
+
const opTypes = {
|
|
4195
|
+
Property: '__GeaStaticCssPropertyOp',
|
|
4196
|
+
Length: '__GeaStaticCssLengthOp',
|
|
4197
|
+
LengthSpec: '__GeaStaticCssLengthOp',
|
|
4198
|
+
Color: '__GeaStaticCssColorOp',
|
|
4199
|
+
ColorVar: '__GeaStaticCssColorVarOp',
|
|
4200
|
+
CustomLength: '__GeaStaticCssCustomLengthOp',
|
|
4201
|
+
CustomColor: '__GeaStaticCssCustomColorOp',
|
|
4202
|
+
BoxShadowNone: '__GeaStaticCssSelectorOnlyOp',
|
|
4203
|
+
FontFamily: '__GeaStaticCssNamedOp',
|
|
4204
|
+
FilterBlur: '__GeaStaticCssLengthOnlyOp',
|
|
4205
|
+
LineHeight: '__GeaStaticCssLineHeightOp',
|
|
4121
4206
|
}
|
|
4122
|
-
|
|
4207
|
+
const arrays = [`\tstatic constexpr __GeaStaticCssTapeKind order[] = {${order}};`]
|
|
4208
|
+
const pointers = kinds.map((kind) => {
|
|
4209
|
+
const entries = chunk.filter((entry) => entry.kind === kind).map((entry) => entry.code)
|
|
4210
|
+
if (entries.length === 0) return 'nullptr'
|
|
4211
|
+
arrays.push(`\tstatic constexpr ${opTypes[kind]} ops${kind}[] = {\n${entries.map((entry) => `\t\t${entry}`).join(',\n')}\n\t};`)
|
|
4212
|
+
return `ops${kind}`
|
|
4213
|
+
})
|
|
4214
|
+
out.push(`{\n${arrays.join('\n')}\n\t__gea_register_static_css_tape({${chunk.length}, order, ${pointers.join(', ')}});\n}`)
|
|
4123
4215
|
chunk = []
|
|
4124
4216
|
}
|
|
4125
4217
|
for (const call of calls) {
|
|
@@ -4135,6 +4227,7 @@ function compactStaticCssTapeCalls(calls) {
|
|
|
4135
4227
|
return { calls: out, usedTape, stringLiterals }
|
|
4136
4228
|
}
|
|
4137
4229
|
|
|
4230
|
+
let staticCssRegistrationCalls = []
|
|
4138
4231
|
function cssRegistrationCode(viteOutDir) {
|
|
4139
4232
|
const calls = []
|
|
4140
4233
|
staticCssLengthExpressionCounter = 0
|
|
@@ -4153,6 +4246,10 @@ function cssRegistrationCode(viteOutDir) {
|
|
|
4153
4246
|
emitCssRules(stripAtBlocks(block.body), block.condition, calls)
|
|
4154
4247
|
}
|
|
4155
4248
|
}
|
|
4249
|
+
// The calls themselves, before the tape packs them: a target that resolves
|
|
4250
|
+
// styles at build time (the Pebble compiled UI) reads these rather than
|
|
4251
|
+
// re-deriving the cascade from the CSS text.
|
|
4252
|
+
staticCssRegistrationCalls = calls
|
|
4156
4253
|
if (calls.length === 0) return ''
|
|
4157
4254
|
const compacted = compactStaticCssTapeCalls(calls)
|
|
4158
4255
|
const tapePrelude = compacted.usedTape ? staticCssTapePrelude(compacted.stringLiterals) : ''
|
|
@@ -4359,6 +4456,7 @@ const viteBundle = findFirstJsFile(viteOutDir)
|
|
|
4359
4456
|
if (!viteBundle) fail(`Vite produced no JS bundle in ${viteOutDir}`)
|
|
4360
4457
|
const cssPrelude = geaEmbeddedCompat ? cssRegistrationCode(viteOutDir) : ''
|
|
4361
4458
|
const cssPreludePath = path.join(outDir, 'gea-style-registration.cppfrag')
|
|
4459
|
+
fs.writeFileSync(path.join(outDir, 'gea-style-registration.calls.json'), JSON.stringify(staticCssRegistrationCalls, null, 1) + '\n')
|
|
4362
4460
|
if (cssPrelude || cppPreludeSymbol) {
|
|
4363
4461
|
fs.writeFileSync(cssPreludePath, cssPrelude)
|
|
4364
4462
|
} else {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
/** Native lengths are raw numbers; only an authored CSS unit may introduce scaling. */
|
|
5
|
+
export function geaNativeStylePlugin({ runtimeEntry }) {
|
|
6
|
+
if (!runtimeEntry) throw new Error('Native Gea styles require the typed @geajs/core source entry')
|
|
7
|
+
const sourceRoot = path.dirname(fs.realpathSync(runtimeEntry))
|
|
8
|
+
const serializer = path.join(sourceRoot, 'runtime/style-value.ts')
|
|
9
|
+
return {
|
|
10
|
+
name: 'geastack-native-style-units',
|
|
11
|
+
enforce: 'pre',
|
|
12
|
+
// Keep all compiler-generated runtime imports on the same typed source tree.
|
|
13
|
+
// Aliasing only the root left compiler-runtime on the browser dist path.
|
|
14
|
+
resolveId(id) {
|
|
15
|
+
const name = id === '@geajs/core' ? 'index' : id.startsWith('@geajs/core/') ? id.slice('@geajs/core/'.length) : null
|
|
16
|
+
if (name === null) return null
|
|
17
|
+
return [path.join(sourceRoot, `${name}.ts`), path.join(sourceRoot, name, 'index.ts')].find(entry => fs.existsSync(entry)) || null
|
|
18
|
+
},
|
|
19
|
+
load(id) {
|
|
20
|
+
if (id.split('?')[0] !== serializer) return null
|
|
21
|
+
// This module is the browser serialization boundary, not the JSX IR.
|
|
22
|
+
// Replacing it here preserves numeric values in the native IR and prevents
|
|
23
|
+
// browser px defaults from reaching native runtime-backed components/h().
|
|
24
|
+
return `export function styleProp(key: string): string {
|
|
25
|
+
return key.startsWith('--') ? key : key.replace(/[A-Z]/g, c => '-' + c.toLowerCase())
|
|
26
|
+
}
|
|
27
|
+
export function styleValue(_property: string, value: unknown): string { return String(value) }
|
|
28
|
+
`
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
}
|