@geastack/core 0.1.23

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 (73) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +36 -0
  3. package/app_frame_perf.cpp +77 -0
  4. package/css.d.ts +1 -0
  5. package/events.cpp +67 -0
  6. package/gea_app_entry.cpp +317 -0
  7. package/gea_sources.mjs +126 -0
  8. package/gea_sources.sh +36 -0
  9. package/include/app.h +60 -0
  10. package/include/apps.h +46 -0
  11. package/include/audio.h +90 -0
  12. package/include/ble.h +212 -0
  13. package/include/bluetooth/hid_profile.h +92 -0
  14. package/include/camera.h +107 -0
  15. package/include/canvas.h +290 -0
  16. package/include/display.h +390 -0
  17. package/include/display_present.h +869 -0
  18. package/include/event.h +32 -0
  19. package/include/events.h +116 -0
  20. package/include/gea/embedded.h +13 -0
  21. package/include/gea/reactive_runtime.h +32 -0
  22. package/include/gea.h +14 -0
  23. package/include/gea_perf_config.h +96 -0
  24. package/include/geaos/abi.h +165 -0
  25. package/include/geolocation.h +126 -0
  26. package/include/gpio.h +61 -0
  27. package/include/graphics/font.h +122 -0
  28. package/include/i2c.h +20 -0
  29. package/include/image.h +156 -0
  30. package/include/imu.h +23 -0
  31. package/include/input.h +33 -0
  32. package/include/memory.h +52 -0
  33. package/include/pixel.h +907 -0
  34. package/include/platform/file_cache.h +18 -0
  35. package/include/power.h +11 -0
  36. package/include/runtime.h +83 -0
  37. package/include/services/app_runner.h +10 -0
  38. package/include/services/app_state.h +14 -0
  39. package/include/services/battery_service.h +12 -0
  40. package/include/services/bluetooth_service.h +13 -0
  41. package/include/services/diagnostics.h +76 -0
  42. package/include/services/diagnostics_platform.h +54 -0
  43. package/include/services/frame_scheduler.h +71 -0
  44. package/include/services/mirror.h +49 -0
  45. package/include/services/network_services.h +25 -0
  46. package/include/services/ota.h +32 -0
  47. package/include/services/runtime_log.h +11 -0
  48. package/include/services/storage_service.h +26 -0
  49. package/include/touch.h +38 -0
  50. package/include/ui/signal.h +149 -0
  51. package/include/wifi.h +67 -0
  52. package/index.d.ts +1940 -0
  53. package/jsx.d.ts +1 -0
  54. package/package.json +70 -0
  55. package/runtime/ble.ts +49 -0
  56. package/runtime/color.ts +21 -0
  57. package/runtime/compiler.ts +43 -0
  58. package/runtime/host.ts +1083 -0
  59. package/runtime/images.ts +129 -0
  60. package/runtime/primitives.ts +49 -0
  61. package/runtime-surface.ts +180 -0
  62. package/runtime.cpp +431 -0
  63. package/runtime.ts +34 -0
  64. package/scripts/build-gea-vite-geatsc.mjs +4514 -0
  65. package/scripts/dotenv-defines.mjs +98 -0
  66. package/scripts/gea-bundle-type-hints.mjs +546 -0
  67. package/scripts/gea-embedded-compat-staging.mjs +17 -0
  68. package/scripts/gea-embedded-compat-transform.mjs +427 -0
  69. package/scripts/gea-vite-module-graph-plugin.mjs +578 -0
  70. package/scripts/generate-gea-embedded-assets.mjs +296 -0
  71. package/scripts/generate-gea-embedded-fonts.mjs +1252 -0
  72. package/scripts/generate-installed-app-plan.mjs +55 -0
  73. package/scripts/native-object-cache.sh +244 -0
package/index.d.ts ADDED
@@ -0,0 +1,1940 @@
1
+ /// <reference path="./css.d.ts" />
2
+
3
+ // Opt-in native 64-bit integer. Structurally a `number` (assign/read without
4
+ // casts), but where a binding, parameter, field, or return is annotated `int`,
5
+ // geatsc lowers it to a C++ `long long` and emits exact integer arithmetic
6
+ // instead of `double` — avoiding software-emulated double math on FPU-less
7
+ // targets (ESP32-S3/P4) and the 2^53 double-faithfulness analysis. The caller
8
+ // takes responsibility for 64-bit (wrapping) integer semantics. Declared GLOBAL
9
+ // (like GeaEmbeddedImage) so it needs no import — a type-only import is dropped
10
+ // by the gea source transform, leaving `: int` unresolved (it would box).
11
+ declare const __gea_int_brand: unique symbol
12
+ declare global {
13
+ type int = number & { readonly [__gea_int_brand]?: never }
14
+ }
15
+
16
+ // Opt-in single-precision float. Structurally a `number` (assign/read without
17
+ // casts), but where a binding, parameter, field, or return is annotated `f32`,
18
+ // geatsc lowers it to `gea_f32` (a `float`) and computes on the hardware FPU
19
+ // instead of software-emulated `double`. ALWAYS single-precision on every
20
+ // target (portable, predictable rounding). Branded (`number & {…}`) so the
21
+ // checker keeps it distinct — a plain `type f32 = number` loses its alias
22
+ // symbol. Declared GLOBAL (like `int`) so it needs no import.
23
+ declare const __gea_f32_brand: unique symbol
24
+ declare global {
25
+ type f32 = number & { readonly [__gea_f32_brand]?: never }
26
+ type float32 = number & { readonly [__gea_f32_brand]?: never }
27
+
28
+ interface ImportMetaEnv {
29
+ readonly [key: string]: string
30
+ }
31
+
32
+ interface ImportMeta {
33
+ readonly env: ImportMetaEnv
34
+ }
35
+ }
36
+
37
+ export interface GeaJsxElement {}
38
+
39
+ export interface GeaJsxElementChildrenAttribute {
40
+ children: {}
41
+ }
42
+
43
+ export interface GeaIntrinsicElements {
44
+ body: NativeViewProps
45
+ canvas: CanvasProps
46
+ camera: CameraProps
47
+ div: NativeViewProps
48
+ span: TextProps
49
+ // The inline-level tags `LayoutNodePass`/`isInlineLevelTag` (engine/ui/layout.cpp)
50
+ // already classifies. They are ordinary view nodes that the layout flows in a
51
+ // row rather than a column; leaving them undeclared did not stop them
52
+ // rendering, it only made every use of one a checker error.
53
+ a: TextProps
54
+ b: TextProps
55
+ i: TextProps
56
+ em: TextProps
57
+ strong: TextProps
58
+ small: TextProps
59
+ label: TextProps
60
+ code: TextProps
61
+ u: TextProps
62
+ sub: TextProps
63
+ sup: TextProps
64
+ mark: TextProps
65
+ p: NativeViewProps
66
+ h1: NativeViewProps
67
+ h2: NativeViewProps
68
+ h3: NativeViewProps
69
+ h4: NativeViewProps
70
+ h5: NativeViewProps
71
+ h6: NativeViewProps
72
+ audio: AudioProps
73
+ button: NativeButtonProps
74
+ img: ImageProps
75
+ input: NativeInputElementProps
76
+ textarea: NativeTextAreaElementProps
77
+ 'virtual-list': NativeVirtualListProps
78
+ }
79
+
80
+ export interface GeaWindow {
81
+ readonly innerWidth: number
82
+ readonly innerHeight: number
83
+ }
84
+
85
+ declare global {
86
+ const document: Document
87
+ const window: GeaWindow
88
+
89
+ /**
90
+ * The engine's own console (`gea::host::console`), declared here because the
91
+ * host is what backs it.
92
+ *
93
+ * Four example apps carried this same declaration in their own `env.d.ts` /
94
+ * `css.d.ts`, each with the note that the name comes from gea's engine and
95
+ * not from lib.dom -- which is exactly the argument for declaring it once, in
96
+ * the file that speaks for the framework. A per-app copy also gives the global
97
+ * an anonymous object type, so nothing can recognize it as the host path it
98
+ * is, and a compiler with only that to go on emits a record global nothing
99
+ * defines (`geatsc-plugin-gea/src/host-shims.ts`, the `console` row).
100
+ *
101
+ * `log` and `error` only, because those are the two functions the host
102
+ * namespace actually defines. `warn`/`info`/`debug` would compile and then
103
+ * fail to link, which is worse than not being declared.
104
+ */
105
+ const console: {
106
+ log(message: string): void
107
+ error(message: string): void
108
+ }
109
+
110
+ function requestAnimationFrame(callback: (timestampMs: number) => void): number
111
+
112
+ // Native one-shot / repeating timers (gea::host::setTimeout/setInterval,
113
+ // engine/../host/include/host/timers.h), NOT Node's `NodeJS.Timeout` (there is
114
+ // no `dom` or `node` lib here, so without this declaration a `setTimeout`
115
+ // call has no ambient type at all). The native id is a plain numeric handle
116
+ // (`double setTimeout(TimerCallback, double)`), so both the id and the delay
117
+ // are `number`, and the callback takes no arguments (unlike the browser's,
118
+ // which forwards extra `setTimeout` arguments to it).
119
+ function setTimeout(callback: () => void, delayMs: number): number
120
+ function clearTimeout(id: number): void
121
+ function setInterval(callback: () => void, delayMs: number): number
122
+ function clearInterval(id: number): void
123
+
124
+ /**
125
+ * A colour already in this board's NATIVE pixel format (RGB565 on the amoled,
126
+ * RGBA8888 on an 8888 target) — what `rgb565()` returns. geatsc lowers it to
127
+ * a tiny native-pixel marker. Unlike the `0xRRGGBBAA` number `rgb()` returns,
128
+ * a `Rgb565` carries no 8-8-8-8 form, so the numeric canvas APIs draw it with
129
+ * ZERO conversion (the colour was born native).
130
+ */
131
+ type Rgb565 = number & { readonly __geaRgb565: unique symbol }
132
+
133
+ /**
134
+ * Author a colour directly in this board's native pixel format. An ambient host
135
+ * primitive (like `requestAnimationFrame`) — geatsc lowers it to the `constexpr
136
+ * pixel::nativeColorValue(r,g,b)`, so a literal `rgb565(255,0,0)` folds to a
137
+ * native pixel constant AT BUILD TIME (zero runtime cost) and a runtime call
138
+ * runs the one conversion once at the call site. The result is a `Rgb565`
139
+ * (native pixel), so it flows through `fillCircleRgb565`/`fillCirclesRgb565`/
140
+ * `fillStyle` with no per-draw `0xRRGGBBAA -> native` re-pack. Prefer it over
141
+ * `rgb()` for stored palettes drawn every frame (e.g.
142
+ * `const COLORS: Rgb565[] = [rgb565(...), ...]`).
143
+ */
144
+ function rgb565(r: number, g: number, b: number): Rgb565
145
+
146
+ interface CanvasRenderingContext2D {
147
+ drawImage(image: GeaEmbeddedImage, dx: number, dy: number): void
148
+ drawImage(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
149
+ // Draw an image masked to a circle inscribed in the destination box. Lets an
150
+ // OPAQUE image (a JPEG, which has no alpha channel) render as a round tile
151
+ // with no alpha plane: the blit skips the pixels outside the shape rather
152
+ // than blending them. A square opaque tile would otherwise paint its
153
+ // corners over whatever it overlaps.
154
+ // Width in px of `text` in the context's CURRENT font. textAlign is a no-op
155
+ // on this canvas and fillText anchors the TOP-LEFT at (x, y), so centring
156
+ // means subtracting half of this.
157
+ measureText(text: string): number
158
+ // Offset from fillText's y anchor to the vertical CENTRE of the text's ink.
159
+ // fillText anchors the top of the line box, which carries ascender and
160
+ // descender the glyphs do not fill, so subtracting half the font size sags.
161
+ measureTextInkCenter(text: string): number
162
+ drawImageCircle(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
163
+ drawImageRotated90CW(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
164
+ drawImageTiledX(image: GeaEmbeddedImage, dx: number, dy: number, width: number): void
165
+ }
166
+
167
+ namespace JSX {
168
+ interface Element extends GeaJsxElement {}
169
+ interface ElementChildrenAttribute extends GeaJsxElementChildrenAttribute {}
170
+ interface IntrinsicElements extends GeaIntrinsicElements {}
171
+ }
172
+ }
173
+
174
+ export type StyleLength =
175
+ | number
176
+ | `${number}px`
177
+ | `${number}%`
178
+ | `${number}vw`
179
+ | `${number}vh`
180
+ | `${number}dvw`
181
+ | `${number}dvh`
182
+ | `calc(${string})`
183
+ | `min(${string})`
184
+ | `max(${string})`
185
+ | `clamp(${string})`
186
+ export type StyleBox = StyleLength | string
187
+ export type ClassMap = Record<string, string | number | boolean | null | undefined>
188
+ export type ClassValue = string | ClassMap
189
+
190
+ export interface EmbeddedDefaults {
191
+ mirror?: boolean
192
+ wifi?: boolean
193
+ }
194
+
195
+ /** Initialize and advertise every BLEServer registered by the application. */
196
+ export function initBleServers(): void
197
+ /** Register a fully constructed BLEServer before calling initBleServers(). */
198
+ export function registerBleServer(server: BLEServer): void
199
+
200
+ export declare const defaults: EmbeddedDefaults
201
+
202
+ export interface TouchSample {
203
+ touching: boolean
204
+ x: number
205
+ y: number
206
+ }
207
+
208
+ export interface TouchHost {
209
+ read(): TouchSample
210
+ }
211
+
212
+ export declare const touch: TouchHost
213
+
214
+ export interface Style {
215
+ display?: 'block' | 'flex' | 'grid' | 'none'
216
+ flexDirection?: 'row' | 'column'
217
+ flexWrap?: 'nowrap' | 'wrap'
218
+ justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around'
219
+ alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch'
220
+ alignSelf?: 'auto' | 'flex-start' | 'center' | 'flex-end' | 'stretch'
221
+ gap?: StyleLength
222
+ width?: StyleLength
223
+ height?: StyleLength
224
+ minWidth?: StyleLength
225
+ minHeight?: StyleLength
226
+ maxWidth?: StyleLength
227
+ maxHeight?: StyleLength
228
+ flex?: number
229
+ padding?: StyleBox
230
+ paddingTop?: StyleLength
231
+ paddingRight?: StyleLength
232
+ paddingBottom?: StyleLength
233
+ paddingLeft?: StyleLength
234
+ margin?: StyleBox
235
+ marginTop?: StyleLength
236
+ marginRight?: StyleLength
237
+ marginBottom?: StyleLength
238
+ marginLeft?: StyleLength
239
+ position?: 'relative' | 'absolute'
240
+ top?: StyleLength
241
+ left?: StyleLength
242
+ right?: StyleLength
243
+ bottom?: StyleLength
244
+ zIndex?: number
245
+ backgroundColor?: string
246
+ color?: string
247
+ opacity?: number
248
+ blinkInterval?: number
249
+ borderWidth?: StyleLength
250
+ borderColor?: string
251
+ borderRadius?: StyleBox
252
+ borderTopLeftRadius?: StyleLength
253
+ borderTopRightRadius?: StyleLength
254
+ borderBottomRightRadius?: StyleLength
255
+ borderBottomLeftRadius?: StyleLength
256
+ fontFamily?: string
257
+ fontSize?: StyleLength
258
+ textAlign?: 'left' | 'center' | 'right'
259
+ overflow?: 'visible' | 'hidden' | 'scroll' | 'auto'
260
+ overflowX?: 'visible' | 'hidden' | 'scroll' | 'auto'
261
+ overflowY?: 'visible' | 'hidden' | 'scroll' | 'auto'
262
+ transform?: string | number
263
+ rotate?: string | number
264
+ scale?: string | number
265
+ translateX?: StyleLength
266
+ translateY?: StyleLength
267
+ transformOrigin?: string
268
+ filter?: string
269
+ }
270
+
271
+ export interface DataAttributes {
272
+ [name: `data-${string}`]: string | number | boolean | undefined
273
+ }
274
+
275
+ export interface Event {
276
+ readonly type: string
277
+ readonly target: EventTarget
278
+ readonly currentTarget: EventTarget
279
+ preventDefault(): void
280
+ stopPropagation(): void
281
+ }
282
+
283
+ export interface EventTarget {
284
+ addEventListener<K extends keyof GeaEventMap>(type: K, listener: (event: GeaEventMap[K]) => void): void
285
+ addEventListener(type: string, listener: (event: Event) => void): void
286
+ }
287
+
288
+ export interface Node extends EventTarget {
289
+ readonly nodeType?: number
290
+ appendChild(child: Node): void
291
+ remove(): void
292
+ }
293
+
294
+ export interface ScrollIntoViewOptions {
295
+ behavior?: 'auto' | 'instant' | 'smooth'
296
+ block?: 'start' | 'center' | 'end' | 'nearest'
297
+ inline?: 'start' | 'center' | 'end' | 'nearest'
298
+ }
299
+
300
+ export interface DOMTokenList {
301
+ add(...tokens: string[]): void
302
+ remove(...tokens: string[]): void
303
+ contains(token: string): boolean
304
+ toggle(token: string, force?: boolean): boolean
305
+ }
306
+
307
+ export interface Element extends Node {
308
+ className?: string
309
+ readonly children: readonly Element[]
310
+ scrollLeft?: number
311
+ scrollTop?: number
312
+ readonly classList: DOMTokenList
313
+ setAttribute(name: string, value: string | number | boolean): void
314
+ // Always a string: the engine answers `""` for an absent attribute, an
315
+ // out-of-range node and a null name alike (`NodeAttributeStore::get` /
316
+ // `Tree::getAttribute`, engine/ui/tree_events.cpp), so there is no absent
317
+ // value for a wider type to carry. Declared `any` it was a dynamic value in
318
+ // every program that read one -- a box for a `const char *`.
319
+ getAttribute(name: string): string
320
+ removeAttribute(name: string): void
321
+ hasAttribute(name: string): boolean
322
+ toggleAttribute(name: string, force?: boolean): boolean
323
+ scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void
324
+ // Text-input focus: `focus()` makes this element the tree's active input,
325
+ // which is what raises the virtual keyboard and starts the caret blink;
326
+ // `blur()` clears it again (and does nothing when this element was not the
327
+ // one focused). Backed by `NodeHandle::focus()`/`blur()` over
328
+ // `Tree::setActiveInput` (engine/ui/node.cpp).
329
+ focus(): void
330
+ blur(): void
331
+ }
332
+
333
+ export interface VirtualListElement extends Element {
334
+ scrollTop: number
335
+ /**
336
+ * Measured row height in CSS px — the rendered layout height of the first
337
+ * slot child (driven entirely by the slot template's CSS). Read this to
338
+ * position/window the recycled slots from one source of truth instead of
339
+ * recomputing an item height in JS and hand-syncing it to the CSS. 0 until
340
+ * the first slot has been laid out.
341
+ */
342
+ readonly rowHeight: number
343
+ }
344
+
345
+ export interface Document {
346
+ readonly body: Element
347
+ getElementById(id: string): Element | null
348
+ querySelector(selector: string): Element | null
349
+ querySelectorAll(selector: string): readonly Element[]
350
+ createElement(tag: 'audio'): HTMLAudioElement
351
+ createElement(tag: 'virtual-list'): VirtualListElement
352
+ createElement(tag: string): Element
353
+ createTextNode(text: string): Node
354
+ createDocumentFragment(): Node
355
+ }
356
+
357
+ export interface PressEventTarget extends Element {
358
+ dataset: Record<string, any>
359
+ }
360
+
361
+ export interface PointerEvent extends Event {
362
+ readonly target: Element
363
+ readonly currentTarget: Element
364
+ // Identifies the finger/contact: the primary pointer is 1, the first extra
365
+ // finger is 2, etc. (web convention, mirrored by the device touch runtime).
366
+ // Multi-touch handlers route gestures by this id.
367
+ readonly pointerId: number
368
+ readonly x: number
369
+ readonly y: number
370
+ readonly clientX: number
371
+ readonly clientY: number
372
+ readonly pageX: number
373
+ readonly pageY: number
374
+ readonly screenX: number
375
+ readonly screenY: number
376
+ }
377
+
378
+ export interface PressEvent extends PointerEvent {
379
+ readonly pressId: number
380
+ readonly pressValue: number
381
+ readonly target: PressEventTarget
382
+ readonly currentTarget: PressEventTarget
383
+ }
384
+
385
+ export type PressEventArgument = number & PressEvent
386
+ export type PressHandler = (event: PressEventArgument) => void
387
+
388
+ export interface TouchPoint {
389
+ readonly identifier: number
390
+ readonly target: Element
391
+ readonly screenX: number
392
+ readonly screenY: number
393
+ readonly clientX: number
394
+ readonly clientY: number
395
+ readonly pageX: number
396
+ readonly pageY: number
397
+ }
398
+
399
+ export interface TouchEvent extends PointerEvent {
400
+ readonly type: 'touchstart' | 'touchmove' | 'touchend'
401
+ readonly touches: readonly TouchPoint[]
402
+ readonly targetTouches: readonly TouchPoint[]
403
+ readonly changedTouches: readonly TouchPoint[]
404
+ }
405
+
406
+ export interface RotaryEvent extends Event {
407
+ readonly type: 'rotary'
408
+ readonly target: Element
409
+ readonly currentTarget: Element
410
+ readonly delta: number
411
+ }
412
+
413
+ /**
414
+ * Every event type `addEventListener` can be given a typed handler for, which
415
+ * is exactly the set the engine's own `eventTypeIndex`
416
+ * (engine/ui/tree_events.cpp) resolves to a listener slot. A type absent here
417
+ * falls to the `(type: string, listener: (event: Event) => void)` overload,
418
+ * where a handler that reads anything more than `Event` no longer typechecks --
419
+ * which is what `sky-hop` hit binding `pointerdown`, and why `maps` had to cast
420
+ * its document to a structural shim to bind pan/zoom at all.
421
+ *
422
+ * The pointer trio is not a web-only spelling: `eventTypeIndex` gives
423
+ * `pointerdown`/`pointermove`/`pointerup` the very slots their `touch*`
424
+ * siblings use, so one handler runs whether a browser raised a pointer event or
425
+ * the device controller synthesized a touch. `NativeTouchEventAttributes`
426
+ * below already says so for the JSX props; this is the same fact for the
427
+ * imperative API.
428
+ *
429
+ * `keydown` is the engine's hardware-button/shortcut event and carries a
430
+ * `KeyEvent`; `input` carries an `InputEvent`. Both are dispatched off-tree by
431
+ * the runtime (`dispatch_key_input`, and `virtual_keyboard.cpp`'s synthetic
432
+ * pair). `keyup` and `pointercancel` are deliberately ABSENT: `eventTypeIndex`
433
+ * returns -1 for both, so `addEventListener` drops them and the handler would
434
+ * never run. Declaring them would promise a callback the engine does not make.
435
+ */
436
+ export interface GeaEventMap {
437
+ click: PressEvent
438
+ touchstart: TouchEvent
439
+ touchmove: TouchEvent
440
+ touchend: TouchEvent
441
+ pointerdown: PointerEvent
442
+ pointermove: PointerEvent
443
+ pointerup: PointerEvent
444
+ input: InputEvent
445
+ keydown: KeyEvent
446
+ rotary: RotaryEvent
447
+ }
448
+
449
+ export type TouchEventHandler = (event: TouchEvent) => void
450
+ export type PointerEventHandler = (event: PointerEvent) => void
451
+ export type RotaryEventHandler = (event: RotaryEvent) => void
452
+
453
+ export interface NativeTouchEventAttributes {
454
+ onTouchStart?: TouchEventHandler
455
+ onTouchEnd?: TouchEventHandler
456
+ onTouchMove?: TouchEventHandler
457
+ // Pointer events are the engine's cross-platform input API, not a web-only
458
+ // spelling: `eventTypeIndex` (engine/ui/tree_events.cpp:64) gives
459
+ // `pointerdown`/`pointermove`/`pointerup` the very slots their `touch*`
460
+ // siblings use, so the same handler runs whether the browser raised a pointer
461
+ // event or the device controller synthesized a touch. Declaring them here is
462
+ // what makes the pair typeable -- `maps` binds all three and had no prop to
463
+ // bind them to, which reads as an app error for something the engine has
464
+ // supported all along.
465
+ onPointerDown?: PointerEventHandler
466
+ onPointerMove?: PointerEventHandler
467
+ onPointerUp?: PointerEventHandler
468
+ }
469
+
470
+ export interface NativeEventAttributes extends NativeTouchEventAttributes {
471
+ onClick?: PressHandler
472
+ onPress?: PressHandler
473
+ onKeyDown?: (event: KeyEvent) => void
474
+ onRotary?: RotaryEventHandler
475
+ }
476
+
477
+ export interface InputEventTarget extends Element {
478
+ value: string
479
+ }
480
+
481
+ export interface InputEvent {
482
+ target: InputEventTarget
483
+ currentTarget: InputEventTarget
484
+ }
485
+
486
+ export interface KeyEvent {
487
+ keyCode: number
488
+ which: number
489
+ }
490
+
491
+ export interface ViewProps extends DataAttributes {
492
+ id?: string
493
+ class?: ClassValue
494
+ style?: Style
495
+ pressId?: number
496
+ pressValue?: number
497
+ onClick?: PressHandler
498
+ onPress?: PressHandler
499
+ children?: any
500
+ }
501
+
502
+ export interface NativeViewProps extends DataAttributes, NativeEventAttributes {
503
+ key?: string | number
504
+ /** Accepted on both targets so a shared component can carry it; the native tree has no accessibility layer and ignores it. */
505
+ role?: string
506
+ id?: string
507
+ class?: ClassValue
508
+ style?: Style
509
+ ref?: GeaElement | null
510
+ pressId?: number
511
+ pressValue?: number
512
+ momentum?: boolean | string
513
+ children?: any
514
+ }
515
+
516
+ /**
517
+ * The props of an inline text element (`span`, `b`, `i`, ...).
518
+ *
519
+ * `Document::createElement` builds every one of these with `createView()` --
520
+ * the same node a `div` gets, differing only in the tag name it stores -- so an
521
+ * inline element accepts exactly what a view accepts: an `id`, `data-*`
522
+ * attributes, event handlers, a `ref`. Declaring a narrower set here did not
523
+ * make those unsupported, it only made them untypeable: `<span id="x"
524
+ * data-anim="fade">` is what the CSS animation system reads, and it was a
525
+ * checker error while working perfectly at runtime.
526
+ */
527
+ export interface TextProps extends NativeViewProps {}
528
+
529
+ export interface GeaNode extends Node {}
530
+ export interface GeaElement extends Element {}
531
+
532
+ export interface GeaCanvasElement extends GeaElement {
533
+ getContext(kind: '2d'): CanvasRenderingContext2D
534
+ }
535
+
536
+ export interface CanvasProps extends DataAttributes, NativeEventAttributes {
537
+ id?: string
538
+ class?: ClassValue
539
+ style?: Style
540
+ width?: number | string
541
+ height?: number | string
542
+ ref?: GeaCanvasElement | null
543
+ children?: any
544
+ }
545
+
546
+ /**
547
+ * A live camera preview surface. It is a real native leaf element — size and
548
+ * position it with CSS exactly like a `<canvas>` (full screen or a small
549
+ * rounded sub-rect). Opens the requested camera on first render and streams
550
+ * each frame into its computed box. Pair it with the imperative `Camera` class
551
+ * (capture, record, exposure/white-balance/focus controls) — both operate on
552
+ * the one active camera.
553
+ */
554
+ export interface CameraProps extends DataAttributes {
555
+ class?: ClassValue
556
+ style?: Style
557
+ /** Which camera to open. Defaults to "back". */
558
+ facing?: CameraFacing
559
+ /** Specific device id (overrides `facing`); from `Camera.getDevices()`. */
560
+ device?: string
561
+ /** Capture resolution hint; the closest supported size is chosen. */
562
+ width?: number | string
563
+ height?: number | string
564
+ /** Mirror the preview horizontally (default: true when facing="front"). */
565
+ mirror?: boolean
566
+ /** How the frame fills the element box. Defaults to "cover". */
567
+ fit?: 'cover' | 'contain' | 'fill'
568
+ ref?: GeaCameraElement | null
569
+ children?: any
570
+ }
571
+
572
+ export interface GeaCameraElement extends GeaElement {}
573
+
574
+ export interface AudioProps extends DataAttributes {
575
+ id?: string
576
+ class?: ClassValue
577
+ style?: Style
578
+ src?: string
579
+ autoplay?: boolean | string | number
580
+ controls?: boolean | string | number
581
+ loop?: boolean | string | number
582
+ ref?: HTMLAudioElement | null
583
+ children?: any
584
+ }
585
+
586
+ export type ImageSource = string | ArrayBuffer | Uint8Array | GeaEmbeddedImage
587
+
588
+ export interface ImageProps extends DataAttributes {
589
+ id?: string
590
+ class?: ClassValue
591
+ // Optional: a static template can leave this unset and apply the source
592
+ // imperatively via `element.setAttribute('src', path)` after mount (see
593
+ // `Document::createImage` / `ImageElement::create` in engine/ui/image.h,
594
+ // which creates a blank image node with no id until one is assigned) — the
595
+ // e-reader's CoverArt does exactly this to stay natively mounted instead of
596
+ // dropping into a reactive-binding bridge.
597
+ src?: ImageSource
598
+ style?: Style
599
+ ref?: GeaElement | null
600
+ fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down'
601
+ playing?: boolean
602
+ loop?: number | 'infinite'
603
+ onLoad?: () => void
604
+ onError?: (error: string) => void
605
+ onFrame?: (frame: number) => void
606
+ }
607
+
608
+ export interface GeaEmbeddedImage {
609
+ readonly width: number
610
+ readonly height: number
611
+ readonly frameCount: number
612
+ readonly isAnimated: boolean
613
+ play(): void
614
+ pause(): void
615
+ seek(frame: number): void
616
+ dispose(): void
617
+ }
618
+
619
+ export interface LoadImageOptions {
620
+ /**
621
+ * Treat the image as opaque-with-color-key transparency: anti-aliased edges
622
+ * (0 < alpha < 255) are snapped to fully opaque, but pixels with alpha == 0
623
+ * remain transparent. If the source has no alpha == 0 pixels, the alpha
624
+ * buffer is dropped entirely so blits hit a row-memcpy fast path. Use for
625
+ * tile/sprite art where you don't need anti-aliased blending.
626
+ */
627
+ opaque?: boolean
628
+ }
629
+
630
+ // Decoding is synchronous (the bytes are already in memory), so this returns the
631
+ // native `GeaEmbeddedImage` handle directly rather than a Promise — `await` still
632
+ // works and keeps the value (and any `GeaEmbeddedImage[]` cache) a concrete
633
+ // struct instead of a boxed gea_cpp_value.
634
+ export declare function loadImage(src: string | ArrayBuffer | Uint8Array, options?: LoadImageOptions): GeaEmbeddedImage
635
+ export declare function loadImageWithOpaque(src: Uint8Array, opaque: boolean): GeaEmbeddedImage
636
+
637
+ // Load + decode an image directly from a persistent-cache file (e.g. a microSD
638
+ // tile cache at /sdcard/...). Synchronous; the returned handle has `width === 0`
639
+ // when the file is missing or fails to decode, so treat that as a cache miss and
640
+ // fetch over the network instead.
641
+ export declare function loadImageFile(path: string, options?: LoadImageOptions): GeaEmbeddedImage
642
+
643
+ // Load + decode a BUILD-EMBEDDED asset by its path relative to the app directory
644
+ // (e.g. 'assets/icons/00-music.png') — the same bytes `<img src="...">` uses.
645
+ // That JSX path lowers a string LITERAL directly to the generated asset symbol,
646
+ // so a canvas app choosing among N bundled images at runtime cannot use it; this
647
+ // goes through the generated lookup table instead. Decode is deferred to first
648
+ // draw and memoized per asset. `width === 0` means no such asset was bundled.
649
+ export declare function loadAssetImage(path: string): GeaEmbeddedImage
650
+
651
+ // Persist raw bytes to the file cache (creating parent directories). Returns
652
+ // false when no writable storage is mounted (so callers can ignore the result).
653
+ export declare function writeCacheFile(path: string, bytes: Uint8Array): boolean
654
+
655
+ // Read a whole file's raw bytes from the persistent cache (e.g. a .pmtiles
656
+ // pushed to /sdcard). Empty Uint8Array when the file is absent / no storage.
657
+ export declare function readCacheFile(path: string): Uint8Array
658
+
659
+ // Read a .pmtiles archive flashed into the spare ota_1 partition over USB
660
+ // (esptool). Empty Uint8Array when none present (or off-device).
661
+ export declare function readMapArchive(): Uint8Array
662
+
663
+ // Read a byte range [offset, offset+length) from a persistent-cache file
664
+ // (e.g. one tile out of a .pmtiles on microSD) without loading the whole file.
665
+ export declare function readFileRange(path: string, offset: number, length: number): Uint8Array
666
+
667
+ // List a directory's regular-file names (non-recursive) from persistent storage
668
+ // (e.g. the EPUBs in /sdcard/books). Empty array when the directory is missing
669
+ // or no storage is mounted.
670
+ export declare function listCacheFiles(path: string): string[]
671
+
672
+ // Synchronous HTTP GET over WiFi — the body as bytes (e.g. an MVT tile) or text
673
+ // (e.g. a TileJSON). Empty when WiFi is down or the request fails.
674
+ export declare function fetchBytes(url: string): Uint8Array
675
+ export declare function fetchText(url: string): string
676
+
677
+ // Wrap an already-decoded image-store slot id (e.g. one delivered by the async
678
+ // tile loader's poll/imageId) in the native GeaEmbeddedImage handle.
679
+ export declare function imageFromId(id: number): GeaEmbeddedImage
680
+
681
+ export class Store {}
682
+
683
+ export class BLEServer extends Store {
684
+ constructor(deviceName: string, appearance: number, macAddress: string)
685
+ deviceName: string
686
+ appearance: number
687
+ macAddress: string
688
+ onConnected(): void
689
+ onDisconnected(): void
690
+ onBound(): void
691
+ startAdvertising(): void
692
+ stopAdvertising(): void
693
+ }
694
+
695
+ export class Component<RootElement extends GeaElement = GeaElement, Props = void> {
696
+ readonly el: RootElement | null
697
+ // `Props` is what a `<Tag ... />` hands this component, stated by the
698
+ // component itself -- the same shape React's `Component<Props>` has, and for
699
+ // the same reason. It replaces `template(...args: any[]): any`, a declaration
700
+ // saying the framework does not know what a template takes or produces; a
701
+ // compiler is obliged to believe that, so every component in the corpus
702
+ // carried a boxed argument array and a boxed result.
703
+ //
704
+ // The default is `void` because the overwhelming majority of templates take
705
+ // nothing, and the parameter is OPTIONAL for a physical reason. `void` in a
706
+ // stored position -- which a parameter slot is -- is `undefined`; that is
707
+ // what `representation/primitives.ts`'s `storedCarrier` says, and the ABI
708
+ // projected from the callable's own type applies it. The binding cell the
709
+ // body initializes does not, so a REQUIRED `props: void` is bound as `void`
710
+ // against an ABI declaring `undefined` and the whole translation unit is
711
+ // refused (`parameter 0 is bound as "void" but the ABI declares
712
+ // "undefined"`). Writing `props?: Props` makes the declared type
713
+ // `void | undefined`, which is `undefined` on both sides, and the question
714
+ // never arises. An override that declares no parameter at all is legal
715
+ // either way -- fewer parameters always is -- so `template() {}` needs no
716
+ // type argument and no change.
717
+ //
718
+ // A component that DOES take props states them: `class BookCover extends
719
+ // Component<GeaElement, { book: BookRow }>`. That is not optional -- a method
720
+ // override's parameters are checked bivariantly, and `undefined` is not
721
+ // assignable to a props object nor it to `undefined`, so an unstated props
722
+ // type is a checker error rather than a silent boxing.
723
+ // `gea-embedded-compat-transform` states it for the function components it
724
+ // rewrites into this shape.
725
+ //
726
+ // The result is `GeaJsxElement | null`, not `RootElement`: what a template
727
+ // evaluates to is `JSX.Element`, which this file declares as extending
728
+ // `GeaJsxElement` (line 27) -- an element in the JSX sense, which is NOT the
729
+ // same thing as the mounted host node `el` points at. `null` is the base's
730
+ // own stub answer in `runtime/compiler.ts`.
731
+ template(props?: Props): GeaJsxElement | null
732
+ }
733
+
734
+ // Opt-in base for a component that holds its OWN reactive state — its instance
735
+ // fields are reactive and its methods/getters can mutate/derive them, so a
736
+ // component can be self-contained without a separate `Store` singleton. On the
737
+ // embedded target this compiles to a lean component-as-store (typed state +
738
+ // compile-time-wired reactivity), so plain `Component` subclasses keep zero
739
+ // reactive overhead. Extend this only when the component manages its own state.
740
+ export class ReactiveComponent<
741
+ RootElement extends GeaElement = GeaElement,
742
+ Props = void
743
+ > extends Component<RootElement, Props> {}
744
+
745
+ export interface BluetoothKeyboard {
746
+ tap(hidCode: number): void
747
+ down(modifier: number, hidCode: number): void
748
+ up(): void
749
+ }
750
+
751
+ export interface BluetoothMouse {
752
+ move(dx: number, dy: number, buttons?: number, wheel?: number): void
753
+ click(button: number): void
754
+ }
755
+
756
+ export interface BluetoothMidi {
757
+ /**
758
+ * Register the BLE-MIDI service (MIDI over GATT, service UUID
759
+ * 03B80E5A-EDE8-4B33-A751-6CE34EC4C700) with the BLE host and include it
760
+ * in advertising. Idempotent; call once before startAdvertising().
761
+ */
762
+ enable(): void
763
+ /** True when a connected central has subscribed to MIDI notifications. */
764
+ bound(): boolean
765
+ /**
766
+ * Send one already-framed BLE-MIDI packet (header byte, timestamp byte,
767
+ * then MIDI message bytes). Routed to whichever role is active: as a
768
+ * notification on our MIDI I/O characteristic (peripheral role, DAW
769
+ * connected to us) or as a Write Without Response to the peer's MIDI I/O
770
+ * characteristic (central role, we connected to a pedal/WIDI adapter).
771
+ * Max ATT_MTU-3 bytes.
772
+ */
773
+ send(bytes: number[]): void
774
+ /**
775
+ * Central role: start scanning for BLE-MIDI peripherals (advertised MIDI
776
+ * service UUID, or names starting with WIDI/PuckCC). Stops advertising —
777
+ * one role is active at a time. Poll results with scanCount()/scanNameAt().
778
+ */
779
+ startScan(): void
780
+ stopScan(): void
781
+ scanning(): boolean
782
+ scanCount(): number
783
+ scanNameAt(index: number): string
784
+ /** Connect to scan result `index` as a GATT client and resolve its MIDI characteristic. */
785
+ connect(index: number): void
786
+ disconnect(): void
787
+ }
788
+
789
+ export interface BluetoothHidHost {
790
+ /**
791
+ * HID host role: this device is the central and a remote HID peripheral
792
+ * (a keyboard/macro pad such as the XPPen ACK05) is the input source.
793
+ * Start scanning for HID peripherals (advertised HID service 0x1812).
794
+ * Poll results with scanCount()/scanNameAt().
795
+ */
796
+ startScan(): void
797
+ stopScan(): void
798
+ scanning(): boolean
799
+ scanCount(): number
800
+ scanNameAt(index: number): string
801
+ /**
802
+ * Connect to scan result `index`, discover its HID service, subscribe to
803
+ * every input-report characteristic, and initiate encryption/bonding.
804
+ */
805
+ connect(index: number): void
806
+ disconnect(): void
807
+ /** True while a hosted HID peripheral is connected and subscribed. */
808
+ bound(): boolean
809
+ /**
810
+ * Raw input-report queue, oldest first. Each entry is one notification:
811
+ * reportIdAt() gives the HID report ID it arrived on, reportLenAt()/
812
+ * reportByteAt() expose the payload. Drain with clearReports() after
813
+ * decoding — the queue is bounded and drops oldest on overflow.
814
+ */
815
+ reportCount(): number
816
+ reportIdAt(index: number): number
817
+ reportLenAt(index: number): number
818
+ reportByteAt(index: number, byteIndex: number): number
819
+ clearReports(): void
820
+ }
821
+
822
+ export interface BluetoothConnections {
823
+ /**
824
+ * Live registry of every concurrent BLE link this device holds, across
825
+ * roles. kind: 0 = HID central peer (a desktop using this device as
826
+ * keyboard/mouse), 1 = MIDI central peer (a DAW), 2 = HID peripheral we
827
+ * host (macro pad), 3 = BLE-MIDI peripheral we drive (pedal/WIDI).
828
+ */
829
+ count(): number
830
+ kindAt(index: number): number
831
+ nameAt(index: number): string
832
+ }
833
+
834
+ export interface BluetoothConfig {
835
+ /**
836
+ * Config service (custom GATT, service A1E0F000-1B2C-4D3E-9F80-1234567890AB).
837
+ * A Web Bluetooth portal reads the app's current config blob and, once
838
+ * paired with the 4-digit code shown on-device, writes a new one.
839
+ *
840
+ * Publish the current config blob (opaque UTF-8 JSON, <= 4096 bytes) so the
841
+ * portal can read it back. Bytes are 0..255; call whenever the app's config
842
+ * changes.
843
+ */
844
+ setDocument(bytes: number[]): void
845
+ /**
846
+ * Length of a committed inbound blob the portal wrote, or 0 if none pending.
847
+ * Poll each frame; on a non-zero length, read pendingByteAt(0..len-1), parse,
848
+ * apply, then consumePending().
849
+ */
850
+ pendingLength(): number
851
+ pendingByteAt(index: number): number
852
+ /** Drop the pending inbound blob after the app has read and applied it. */
853
+ consumePending(): void
854
+ /** True while the on-device pairing overlay should be shown. */
855
+ pairing(): boolean
856
+ /** The 4-digit pairing code to display, or "" when not pairing. */
857
+ pairCode(): string
858
+ /** Close the pairing overlay (user backed out / timed out). */
859
+ dismissPairing(): void
860
+ /**
861
+ * Device -> portal activity push. When a control fires on the device, call
862
+ * this with the control's index so a subscribed portal can light up the
863
+ * matching control live (CTRL notify, opcode 0xA0). No-op when no portal is
864
+ * subscribed to the config CTRL characteristic.
865
+ */
866
+ pushActivity(index: number): void
867
+ }
868
+
869
+ export interface BluetoothController {
870
+ readonly keyboard: BluetoothKeyboard
871
+ readonly mouse: BluetoothMouse
872
+ readonly midi: BluetoothMidi
873
+ readonly hidHost: BluetoothHidHost
874
+ readonly connections: BluetoothConnections
875
+ readonly config: BluetoothConfig
876
+ enabled(): boolean
877
+ setEnabled(enabled: boolean): void
878
+ connected(): boolean
879
+ bound(): boolean
880
+ batteryLevel(): number
881
+ mac(): string
882
+ deviceName(): string
883
+ init(deviceName: string, appearance?: number, macAddress?: string): void
884
+ startAdvertising(): void
885
+ stopAdvertising(): void
886
+ }
887
+
888
+ export declare const BLE: BluetoothController
889
+ export declare const bluetooth: BluetoothController
890
+
891
+ export interface AudioController {
892
+ getVolume(): number
893
+ setVolume(volume: number): void
894
+ }
895
+
896
+ export declare const Audio: AudioController
897
+
898
+ export interface CanvasRenderingContext2D {
899
+ fillStyle: string | number
900
+ strokeStyle: string | number
901
+ globalAlpha: number
902
+ lineWidth: number
903
+ font: string
904
+ textBaseline: string
905
+ textAlign: string
906
+ clear(): void
907
+ clearRect(x: number, y: number, width: number, height: number): void
908
+ fillRect(x: number, y: number, width: number, height: number): void
909
+ strokeRect(x: number, y: number, width: number, height: number): void
910
+ fillCircle(x: number, y: number, radius: number, fill?: string | number): void
911
+ strokeCircle(x: number, y: number, radius: number): void
912
+ fillCircleRgb565(x: number, y: number, radius: number, color: number): void
913
+ fillTriangleRgb565(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, color: number): void
914
+ fillCirclesRgb565(xs: ArrayLike<number>, ys: ArrayLike<number>, radius: number, colors: ArrayLike<number>, count?: number): void
915
+ fillCirclesRgb565Uniform(xs: ArrayLike<number>, ys: ArrayLike<number>, radius: number, color: number): void
916
+ /**
917
+ * Batched opaque triangles as ONE recorded present command. `order[0..count)`
918
+ * indexes the coordinate/colour arrays in paint (depth) order; colours are
919
+ * authored 0xRRGGBBAA. Replaces per-triangle fillTriangleRgb565 loops whose
920
+ * per-command record/dispatch overhead dominates at hundreds of triangles.
921
+ */
922
+ fillTrianglesRgb565Sorted(
923
+ x0s: ArrayLike<number>,
924
+ y0s: ArrayLike<number>,
925
+ x1s: ArrayLike<number>,
926
+ y1s: ArrayLike<number>,
927
+ x2s: ArrayLike<number>,
928
+ y2s: ArrayLike<number>,
929
+ colors: ArrayLike<number>,
930
+ order: ArrayLike<number>,
931
+ count?: number,
932
+ ): void
933
+ beginPath(): void
934
+ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number): void
935
+ moveTo(x: number, y: number): void
936
+ lineTo(x: number, y: number): void
937
+ closePath(): void
938
+ fill(): void
939
+ stroke(): void
940
+ fillText(text: string, x: number, y: number): void
941
+ // Width in px of `text` in the context's CURRENT font. textAlign is a no-op
942
+ // on this canvas and fillText anchors the TOP-LEFT at (x, y), so centring
943
+ // means subtracting half of this.
944
+ measureText(text: string): number
945
+ // Offset from fillText's y anchor to the vertical CENTRE of the text's ink.
946
+ // fillText anchors the top of the line box, which carries ascender and
947
+ // descender the glyphs do not fill, so subtracting half the font size sags.
948
+ measureTextInkCenter(text: string): number
949
+ drawImage(image: GeaEmbeddedImage, dx: number, dy: number): void
950
+ drawImage(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
951
+ // Draw an image masked to a circle inscribed in the destination box. Lets an
952
+ // OPAQUE image (a JPEG, which has no alpha channel) render as a round tile
953
+ // with no alpha plane: the blit skips the pixels outside the shape rather
954
+ // than blending them. A square opaque tile would otherwise paint its
955
+ // corners over whatever it overlaps.
956
+ drawImageCircle(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
957
+ drawImageRotated90CW(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
958
+ drawImageTiledX(image: GeaEmbeddedImage, dx: number, dy: number, width: number): void
959
+ flush(): void
960
+ beginBatch(): void
961
+ endBatch(): void
962
+ }
963
+
964
+ export interface DisplayFlushConfig {
965
+ rows: number
966
+ depth: number
967
+ }
968
+
969
+ export interface DisplayMemoryConfig {
970
+ commandBufferCommands?: number
971
+ backgroundCache?: boolean
972
+ }
973
+
974
+ // E-paper refresh policy + custom waveforms. All fields optional — omitted
975
+ // fields keep their current value. LUTs are the panel controller's raw
976
+ // 159-byte waveform tables ([] restores the board's vendor default).
977
+ // fastStreakWindowMs: updates arriving within this window of the previous
978
+ // refresh use the fast waveform; 0 disables fast mode. No-op on boards
979
+ // without an e-paper panel (and on web).
980
+ export interface DisplayEpaperRefreshConfig {
981
+ fullRefreshEveryPartials?: number
982
+ fullRefreshHardCapPartials?: number
983
+ fastStreakWindowMs?: number
984
+ partialLut?: number[]
985
+ fastLut?: number[]
986
+ /**
987
+ * Render the framebuffer in 4-level grayscale (white / light grey / dark
988
+ * grey / black) on e-paper panels that support it (e.g. SSD1681). Gray mode
989
+ * trades the fast partial waveform for a slower full refresh (~1 s) that
990
+ * drives 4 reflectance levels instead of 1-bit dithering. No-op on boards
991
+ * without grayscale support. Omit to leave the current mode unchanged.
992
+ */
993
+ grayscale?: boolean
994
+ /**
995
+ * Whether a partial update that covers the whole screen (e.g. an e-reader
996
+ * page turn) is promoted to a flashing full refresh. A reading app that
997
+ * prefers flash-free page turns sets false and relies on
998
+ * fullRefreshEveryPartials to clear ghosting. Omit to leave unchanged.
999
+ */
1000
+ fullOnCover?: boolean
1001
+ }
1002
+
1003
+ export type DisplayOrientation = 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary'
1004
+
1005
+ export type DisplayOrientationSupport = DisplayOrientation | 'portrait' | 'landscape' | 'all'
1006
+
1007
+ export type DisplayPixelFormat = 'rgb565' | 'rgb888' | 'rgb8888' | 'argb8888'
1008
+
1009
+ export interface DisplayController {
1010
+ readonly ctx: CanvasRenderingContext2D
1011
+ /** Logical viewport width in px after the current display orientation is applied. */
1012
+ readonly width: number
1013
+ /** Logical viewport height in px after the current display orientation is applied. */
1014
+ readonly height: number
1015
+ /** Physical panel width in px before display orientation is applied. */
1016
+ readonly nativeWidth: number
1017
+ /** Physical panel height in px before display orientation is applied. */
1018
+ readonly nativeHeight: number
1019
+ orientation: DisplayOrientation
1020
+ supportedOrientations: DisplayOrientationSupport[]
1021
+ autoRotate: boolean
1022
+ pixelFormat: DisplayPixelFormat
1023
+ readonly panelPixelFormat: DisplayPixelFormat
1024
+ readonly supportedPixelFormats: DisplayPixelFormat[]
1025
+ getBrightness(): number
1026
+ setBrightness(brightness: number): void
1027
+ getOrientation(): DisplayOrientation
1028
+ setOrientation(orientation: DisplayOrientation): void
1029
+ getSupportedOrientations(): DisplayOrientationSupport[]
1030
+ setSupportedOrientations(orientations: DisplayOrientationSupport | DisplayOrientationSupport[]): void
1031
+ getAutoRotate(): boolean
1032
+ setAutoRotate(enabled: boolean): void
1033
+ /**
1034
+ * Opt into tearing sync (TE/VBlank). When enabled on a board that wires a TE
1035
+ * line, frames are aligned to the panel's vertical blank so pans and animations
1036
+ * present without tearing. No-op on boards without a TE line. Off by default.
1037
+ */
1038
+ setVSync(on: boolean): void
1039
+ /**
1040
+ * Opt into the large-static-text raster cache. When enabled, big text
1041
+ * (font-size ≳ 40px — HUD badges, clocks, counters) is rasterized to a coverage
1042
+ * sprite once and blitted on subsequent frames instead of re-running the glyph
1043
+ * rasterizer every frame. Saves CPU/power when large text shares dirty regions
1044
+ * with moving content; no effect on small UI text. Off by default.
1045
+ */
1046
+ setTextRasterCache(on: boolean): void
1047
+ /**
1048
+ * Declare that text always draws over ONE solid color (0xRRGGBB). On packed
1049
+ * 4-bit grayscale targets (e-paper) each glyph is then pre-blended against
1050
+ * that backdrop once and stamped as straight byte copies — no per-pixel
1051
+ * framebuffer read or blend. Glyphs whose color equals the backdrop (e.g.
1052
+ * inverted rows) automatically fall back to the true blend. Pass a negative
1053
+ * value to disable. No-op on full-color targets.
1054
+ */
1055
+ setTextSolidBackdrop(rrggbb: number): void
1056
+ /**
1057
+ * Mark the next present as a full-screen change ("damage-all"). The present
1058
+ * then skips the dirty-rect diff and its persistent previous-frame copy — both
1059
+ * exist only to flush less when little changed, which is pure waste during a
1060
+ * full-screen pan (the diff would conclude "everything changed" and full-flush
1061
+ * anyway). Call it once per frame while actively panning/animating the whole
1062
+ * screen; stop calling it when motion settles so idle gets the cheap diff back.
1063
+ */
1064
+ invalidate(): void
1065
+ getPixelFormat(): DisplayPixelFormat
1066
+ setPixelFormat(format: DisplayPixelFormat): void
1067
+ getPanelPixelFormat(): DisplayPixelFormat
1068
+ getSupportedPixelFormats(): DisplayPixelFormat[]
1069
+ getDevicePixelRatio(): number
1070
+ setDevicePixelRatio(devicePixelRatio: number): void
1071
+ getFrameIntervalMs(): number
1072
+ setFrameIntervalMs(intervalMs: number): void
1073
+ getFrameRate(): number
1074
+ setFrameRate(fps: number): void
1075
+ setAA(samples: number): void
1076
+ setFlushConfig(config: DisplayFlushConfig): void
1077
+ setMemoryConfig(config: DisplayMemoryConfig): void
1078
+ setEpaperRefreshConfig(config: DisplayEpaperRefreshConfig): void
1079
+ epaperFullRefresh(): void
1080
+ }
1081
+
1082
+ export declare const Display: DisplayController
1083
+
1084
+ export interface EmbeddedMemoryStats {
1085
+ internalFree: number
1086
+ internalLargestFreeBlock: number
1087
+ internalMinimumFree: number
1088
+ psramFree: number
1089
+ currentTaskStackHighWaterMark: number
1090
+ allocationSramCount: number
1091
+ allocationPsramCount: number
1092
+ allocationSramBytes: number
1093
+ allocationPsramBytes: number
1094
+ allocationSramPeakBytes: number
1095
+ allocationPsramPeakBytes: number
1096
+ }
1097
+
1098
+ export interface EmbeddedMemoryConfig {
1099
+ geaMainStackBytes: number
1100
+ geaInitStackBytes: number
1101
+ appFrameStackWords: number
1102
+ appFrameStackBytes: number
1103
+ displayFlushConfiguredRows: number
1104
+ displayFlushConfiguredDepth: number
1105
+ displayFlushBufferMaxBytes: number
1106
+ displayFlushRows: number
1107
+ displayFlushDepth: number
1108
+ displayFlushBufferBytes: number
1109
+ }
1110
+
1111
+ export interface MemoryController {
1112
+ internalFree(): number
1113
+ internalLargestFreeBlock(): number
1114
+ internalMinimumFree(): number
1115
+ psramFree(): number
1116
+ currentTaskStackHighWaterMark(): number
1117
+ geaMainStackBytes(): number
1118
+ geaInitStackBytes(): number
1119
+ appFrameStackWords(): number
1120
+ appFrameStackBytes(): number
1121
+ displayFlushConfiguredRows(): number
1122
+ displayFlushConfiguredDepth(): number
1123
+ displayFlushBufferMaxBytes(): number
1124
+ displayFlushRows(): number
1125
+ displayFlushDepth(): number
1126
+ displayFlushBufferBytes(): number
1127
+ allocationSramCount(): number
1128
+ allocationPsramCount(): number
1129
+ allocationSramBytes(): number
1130
+ allocationPsramBytes(): number
1131
+ allocationSramPeakBytes(): number
1132
+ allocationPsramPeakBytes(): number
1133
+ stats(): EmbeddedMemoryStats
1134
+ config(): EmbeddedMemoryConfig
1135
+ }
1136
+
1137
+ export declare const Memory: MemoryController
1138
+
1139
+ export interface InputController {
1140
+ // Reads-and-clears the one-shot back-button-pressed flag. Set by the
1141
+ // platform launcher-button task. Apps poll this in their rAF loop and dismiss overlays.
1142
+ consumeBackButton(): boolean
1143
+ }
1144
+
1145
+ export declare const Input: InputController
1146
+
1147
+ export interface GpioController {
1148
+ // Drive a pin. Returns whether this board did it: a pin the chip does not
1149
+ // have, or a target with no GPIO at all, reports false rather than throwing,
1150
+ // so an app written for one board degrades on another instead of crashing.
1151
+ configureOutput(pin: number): boolean
1152
+ configureInput(pin: number, pullUp: boolean): boolean
1153
+ write(pin: number, level: boolean): boolean
1154
+ read(pin: number): boolean
1155
+ }
1156
+
1157
+ export declare const Gpio: GpioController
1158
+
1159
+ export interface LedController {
1160
+ // A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
1161
+ // modern ESP32 modules.
1162
+ //
1163
+ // set() is the default and usually the only call an app needs: it attaches
1164
+ // the pin if it is not already attached, writes the colour and clocks it out.
1165
+ // off() is set() with black, which is what "off" means on a WS2812.
1166
+ set(pin: number, r: number, g: number, b: number): boolean
1167
+ off(pin: number): boolean
1168
+ // The multi-pixel path, for a real strand where one transmit per frame beats
1169
+ // one per pixel. show() returns once the strand has latched the buffer.
1170
+ attach(pin: number, count: number): boolean
1171
+ setPixel(index: number, r: number, g: number, b: number): boolean
1172
+ show(): boolean
1173
+ // Hand the RMT channel back; the ESP32-S3 has four.
1174
+ detach(): void
1175
+ }
1176
+
1177
+ export declare const Led: LedController
1178
+
1179
+ export interface ClockController {
1180
+ // Milliseconds since the Unix epoch from the platform real-time clock
1181
+ // (gettimeofday). Reflects a host-set time (GEADEV SETTIME / the companion
1182
+ // app on launch). Unlike Date.now() — which reads a monotonic clock on
1183
+ // ESP32 — this advances as real wall-clock time once synced, and returns a
1184
+ // small value (< ~1.6e12) before the device clock has ever been set.
1185
+ epochMs(): number
1186
+ }
1187
+
1188
+ export declare const Clock: ClockController
1189
+
1190
+ export interface ProfilerController {
1191
+ // Monotonic microseconds from the platform high-resolution timer. Intended for
1192
+ // temporary app/runtime instrumentation; use Date.now() or Clock.epochMs() for
1193
+ // user-visible time.
1194
+ nowUs(): number
1195
+ /** Raw CPU cycle count (single register read; far cheaper than nowUs). 32-bit, wraps ~18s at 240MHz. */
1196
+ nowCycles(): number
1197
+ }
1198
+
1199
+ export declare const Profiler: ProfilerController
1200
+
1201
+ export interface GeaLocalStorage {
1202
+ // Browser-compatible localStorage: a synchronous key/value store that PERSISTS
1203
+ // across reboots / power cycles (backed by NVS on device). Reached through the
1204
+ // global `localStorage` — no import. Safe to read/write from anywhere,
1205
+ // including a store's init(). getItem returns '' for a missing key.
1206
+ readonly length: number
1207
+ getItem(key: string): string
1208
+ setItem(key: string, value: string): void
1209
+ removeItem(key: string): void
1210
+ clear(): void
1211
+ key(index: number): string
1212
+ }
1213
+
1214
+ declare global {
1215
+ const localStorage: GeaLocalStorage
1216
+ }
1217
+
1218
+ export interface BatteryController {
1219
+ // Battery charge percentage (0-100) read from the platform PMU.
1220
+ level(): number
1221
+ }
1222
+
1223
+ export declare const Battery: BatteryController
1224
+
1225
+ export interface NotifyController {
1226
+ // Transient host->app notification channel (e.g. pushed from the companion
1227
+ // via GEADEV NOTIFY). text() is the latest message; seq() increments on each
1228
+ // post so an app can detect a new one and show a transient banner.
1229
+ text(): string
1230
+ seq(): number
1231
+ }
1232
+
1233
+ export declare const Notify: NotifyController
1234
+
1235
+ export interface DeviceControlController {
1236
+ // Run a host shell command, return combined stdout+stderr. macOS companion
1237
+ // only — drives the device-control + install/uninstall scripts over USB.
1238
+ exec(command: string): string
1239
+ }
1240
+
1241
+ export declare const DeviceControl: DeviceControlController
1242
+
1243
+ export interface AppsController {
1244
+ launch(appId: string): number
1245
+ }
1246
+
1247
+ export declare const Apps: AppsController
1248
+
1249
+ export type OscillatorType = 'sine' | 'square' | 'sawtooth' | 'triangle'
1250
+
1251
+ export interface AudioParam {
1252
+ value: number
1253
+ setValueAtTime(value: number, startTime: number): void
1254
+ }
1255
+
1256
+ export interface AudioDestinationNode {}
1257
+
1258
+ export interface AudioBuffer {
1259
+ readonly sampleRate: number
1260
+ readonly length: number
1261
+ readonly duration: number
1262
+ readonly numberOfChannels: number
1263
+ copyFromChannel(destination: Float32Array<ArrayBuffer>, channelNumber: number, bufferOffset?: number): void
1264
+ copyToChannel(source: Float32Array<ArrayBuffer>, channelNumber: number, bufferOffset?: number): void
1265
+ getChannelData(channel: number): Float32Array<ArrayBuffer>
1266
+ }
1267
+
1268
+ export interface AudioBufferSourceNode {
1269
+ buffer: AudioBuffer
1270
+ connect(destination: AudioDestinationNode): AudioDestinationNode
1271
+ start(when?: number): void
1272
+ stop(when?: number): void
1273
+ }
1274
+
1275
+ export interface OscillatorNode {
1276
+ type: OscillatorType
1277
+ frequency: AudioParam
1278
+ connect(destination: AudioDestinationNode): AudioDestinationNode
1279
+ start(when?: number): void
1280
+ stop(when?: number): void
1281
+ }
1282
+
1283
+ export interface AudioContext {
1284
+ readonly currentTime: number
1285
+ readonly destination: AudioDestinationNode
1286
+ createOscillator(): OscillatorNode
1287
+ createBufferSource(): AudioBufferSourceNode
1288
+ decodeAudioData(audioData: ArrayBuffer | Uint8Array | GeaAudioBlob): Promise<AudioBuffer>
1289
+ }
1290
+
1291
+ export interface AudioContextConstructor {
1292
+ new (): AudioContext
1293
+ }
1294
+
1295
+ export declare const audioContext: AudioContext
1296
+
1297
+ export interface AccelerometerReading {
1298
+ x: number
1299
+ y: number
1300
+ z: number
1301
+ tiltX: number
1302
+ tiltY: number
1303
+ gyroscopeX: number
1304
+ gyroscopeY: number
1305
+ gyroscopeZ: number
1306
+ timestamp: number
1307
+ }
1308
+
1309
+ export interface AccelerometerController {
1310
+ readonly x: number
1311
+ readonly y: number
1312
+ readonly z: number
1313
+ readonly accelerationX: number
1314
+ readonly accelerationY: number
1315
+ readonly accelerationZ: number
1316
+ readonly gyroscopeX: number
1317
+ readonly gyroscopeY: number
1318
+ readonly gyroscopeZ: number
1319
+ readonly tiltX: number
1320
+ readonly tiltY: number
1321
+ start(): void
1322
+ close(): void
1323
+ calibrateBias(): void
1324
+ }
1325
+
1326
+ export declare const Accelerometer: AccelerometerController
1327
+
1328
+ export type CameraFacing = 'front' | 'back' | 'external'
1329
+ export type CameraFlashMode = 'off' | 'on' | 'auto' | 'torch'
1330
+
1331
+ export interface CameraOpenOptions {
1332
+ facing?: CameraFacing
1333
+ width?: number
1334
+ height?: number
1335
+ }
1336
+
1337
+ export interface CameraDevice {
1338
+ readonly id: string
1339
+ readonly facing: CameraFacing
1340
+ }
1341
+
1342
+ export interface CameraCaptureOptions {
1343
+ mirror?: boolean
1344
+ }
1345
+
1346
+ export interface CameraPhoto {
1347
+ readonly imageId: number
1348
+ readonly width: number
1349
+ readonly height: number
1350
+ readonly orientation: number
1351
+ dispose(): void
1352
+ }
1353
+
1354
+ export type CameraExposureMode = 'auto' | 'continuous' | 'locked' | 'manual'
1355
+ export type CameraWhiteBalanceMode = 'auto' | 'continuous' | 'locked'
1356
+ export type CameraFocusMode = 'auto' | 'continuous' | 'locked'
1357
+
1358
+ export interface CameraExposureOptions {
1359
+ mode?: CameraExposureMode
1360
+ /** Exposure compensation in EV stops (auto/continuous modes). */
1361
+ bias?: number
1362
+ /** ISO / sensor gain (manual mode). */
1363
+ iso?: number
1364
+ /** Shutter duration in milliseconds (manual mode). */
1365
+ durationMs?: number
1366
+ }
1367
+
1368
+ export interface CameraWhiteBalanceOptions {
1369
+ mode?: CameraWhiteBalanceMode
1370
+ /** Correlated colour temperature in Kelvin (locked mode). */
1371
+ temperature?: number
1372
+ /** Green/magenta tint (where supported). */
1373
+ tint?: number
1374
+ }
1375
+
1376
+ export interface CameraFocusOptions {
1377
+ mode?: CameraFocusMode
1378
+ /** Normalized focus point of interest [0..1] (where supported). */
1379
+ point?: { x: number; y: number }
1380
+ }
1381
+
1382
+ export interface CameraRecordOptions {
1383
+ /** Sink path for the clip (esp32-p4 writes Motion-JPEG). */
1384
+ path: string
1385
+ /** Target frames per second (default 15). */
1386
+ fps?: number
1387
+ }
1388
+
1389
+ export interface CameraClip {
1390
+ /** Recorded clip duration in milliseconds. */
1391
+ readonly durationMs: number
1392
+ /** The sink path the clip was written to. */
1393
+ readonly path: string
1394
+ }
1395
+
1396
+ export interface CameraController {
1397
+ isAvailable(): boolean
1398
+ hasPermission(): boolean
1399
+ requestPermission(): Promise<boolean>
1400
+ open(options?: CameraOpenOptions | CameraFacing): boolean
1401
+ close(): void
1402
+ isOpen(): boolean
1403
+ readonly facing: CameraFacing
1404
+ readonly width: number
1405
+ readonly height: number
1406
+ readonly orientation: number
1407
+ draw(x: number, y: number, destWidth?: number, destHeight?: number): void
1408
+ /** Capture a still frame. `capturePhoto` is the preferred alias of `capture`. */
1409
+ capture(options?: CameraCaptureOptions): Promise<CameraPhoto>
1410
+ capturePhoto(options?: CameraCaptureOptions): Promise<CameraPhoto>
1411
+ /** Record a video clip (esp32-p4: Motion-JPEG; iOS movie: later). */
1412
+ startRecording(options: CameraRecordOptions): Promise<void>
1413
+ stopRecording(): Promise<CameraClip>
1414
+ isRecording(): boolean
1415
+ setFlash(mode: CameraFlashMode): void
1416
+ setZoom(factor: number): void
1417
+ setMirror(mirror: boolean): void
1418
+ /** Exposure / AE controls. */
1419
+ setExposure(options: CameraExposureOptions): void
1420
+ /** White balance controls. */
1421
+ setWhiteBalance(options: CameraWhiteBalanceOptions): void
1422
+ /** Focus controls. */
1423
+ setFocus(options: CameraFocusOptions): void
1424
+ /** Torch / video light. */
1425
+ setTorch(mode: 'off' | 'on' | 'auto', level?: number): void
1426
+ /** Switch to another camera by facing or device id (reopens the stream). */
1427
+ switchCamera(target: CameraFacing | string): boolean
1428
+ getDevices(): CameraDevice[]
1429
+ }
1430
+
1431
+ export declare const Camera: CameraController
1432
+
1433
+ export interface GeolocationCoordinates {
1434
+ readonly latitude: number
1435
+ readonly longitude: number
1436
+ readonly altitude: number
1437
+ readonly accuracy: number
1438
+ readonly altitudeAccuracy: number
1439
+ readonly heading: number
1440
+ readonly speed: number
1441
+ }
1442
+
1443
+ export interface GeolocationPosition {
1444
+ readonly coords: GeolocationCoordinates
1445
+ readonly timestamp: number
1446
+ readonly hasFix: boolean
1447
+ }
1448
+
1449
+ export interface GeolocationPositionError {
1450
+ readonly code: number
1451
+ readonly message: string
1452
+ }
1453
+
1454
+ export interface PositionOptions {
1455
+ enableHighAccuracy?: boolean
1456
+ timeout?: number
1457
+ maximumAge?: number
1458
+ }
1459
+
1460
+ export interface GeolocationController {
1461
+ hasFix(): boolean
1462
+ currentPosition(): GeolocationPosition
1463
+ coords(): GeolocationCoordinates
1464
+ latitude(): number
1465
+ longitude(): number
1466
+ accuracy(): number
1467
+ getCurrentPosition(
1468
+ success: (position: GeolocationPosition) => void,
1469
+ error?: (error: GeolocationPositionError) => void,
1470
+ options?: PositionOptions
1471
+ ): void
1472
+ watchPosition(
1473
+ success: (position: GeolocationPosition) => void,
1474
+ error?: (error: GeolocationPositionError) => void,
1475
+ options?: PositionOptions
1476
+ ): number
1477
+ clearWatch(id: number): void
1478
+ }
1479
+
1480
+ export declare const Geolocation: GeolocationController
1481
+ export declare const geolocation: GeolocationController
1482
+
1483
+ export interface WiFiController {
1484
+ enabled(): boolean
1485
+ setEnabled(enabled: boolean): void
1486
+ connected(): boolean
1487
+ rssi(): number
1488
+ ssid(): string
1489
+ ip(): string
1490
+ mac(): string
1491
+ configure(ssid: string, password: string): void
1492
+ waitForConnection(timeoutMs: number): boolean
1493
+ startScan(): void
1494
+ scanning(): boolean
1495
+ scanCount(): number
1496
+ scanSsidAt(index: number): string
1497
+ scanRssiAt(index: number): number
1498
+ scanSecuredAt(index: number): boolean
1499
+ }
1500
+
1501
+ export declare const WiFi: WiFiController
1502
+ export declare const wifi: WiFiController
1503
+
1504
+ export interface ButtonProps {
1505
+ class?: ClassValue
1506
+ style?: Style
1507
+ pressId?: number
1508
+ pressValue?: number
1509
+ onClick?: PressHandler
1510
+ onPress?: PressHandler
1511
+ children?: any
1512
+ }
1513
+
1514
+ export interface NativeButtonProps extends NativeEventAttributes {
1515
+ key?: string | number
1516
+ class?: ClassValue
1517
+ style?: Style
1518
+ ref?: GeaElement | null
1519
+ pressId?: number
1520
+ pressValue?: number
1521
+ children?: any
1522
+ }
1523
+
1524
+ export interface NativeVirtualListProps extends NativeEventAttributes {
1525
+ id?: string
1526
+ class?: ClassValue
1527
+ style?: Style
1528
+ ref?: GeaElement | null
1529
+ children?: any
1530
+ momentum?: boolean | string
1531
+ itemCount?: number | string
1532
+ itemHeight?: number | string
1533
+ labelPrefix?: string
1534
+ showPixels?: boolean | string
1535
+ 'item-count'?: number | string
1536
+ 'item-height'?: number | string
1537
+ 'label-prefix'?: string
1538
+ 'show-pixels'?: boolean | string
1539
+ }
1540
+
1541
+ export interface InputElementProps {
1542
+ class?: ClassValue
1543
+ style?: Style
1544
+ // The three shapes the renderer actually materializes: `range` becomes an
1545
+ // NSSlider and `checkbox` an NSSwitch, everything else an NSTextField
1546
+ // (`apple/targets/macos/main/macos_renderer.mm`, `makeViewForType`). The union
1547
+ // had drifted to the text-field spellings alone, so an app writing the switch
1548
+ // the renderer has always drawn did not typecheck.
1549
+ type?: 'text' | 'password' | 'button' | 'range' | 'checkbox'
1550
+ // Optional, because the renderer treats it as one: every reader guards with
1551
+ // `if (value && value[0])`, and `applySwitchProps` never reads it at all --
1552
+ // a switch's state is `checked`. Requiring it made `<input type="checkbox">`
1553
+ // impossible to write. The cost is that a text field with a forgotten value
1554
+ // no longer fails to typecheck; the union arm that would keep both is a
1555
+ // larger change than the drift being fixed here.
1556
+ value?: string
1557
+ // Read back off the node by the renderer as attributes, hence the string form
1558
+ // alongside the natural one -- `min`/`max` bound the slider, `checked` is the
1559
+ // switch's state and is written back on toggle.
1560
+ min?: number | string
1561
+ max?: number | string
1562
+ checked?: boolean | string
1563
+ placeholder?: string
1564
+ autoFocus?: boolean | number
1565
+ pressId?: number
1566
+ pressValue?: number
1567
+ onClick?: PressHandler
1568
+ onInput?: (event: InputEvent) => void
1569
+ onFocus?: () => void
1570
+ onBlur?: () => void
1571
+ onKeyDown?: (event: KeyEvent) => void
1572
+ input?: (value: string) => void
1573
+ focus?: () => void
1574
+ blur?: () => void
1575
+ keydown?: (keyCode: number) => void
1576
+ }
1577
+
1578
+ export interface NativeInputElementProps extends InputElementProps {
1579
+ ref?: GeaElement | null
1580
+ }
1581
+
1582
+ export interface TextAreaElementProps {
1583
+ class?: ClassValue
1584
+ style?: Style
1585
+ value: string
1586
+ placeholder?: string
1587
+ onInput?: (event: InputEvent) => void
1588
+ onFocus?: () => void
1589
+ onBlur?: () => void
1590
+ }
1591
+
1592
+ export interface NativeTextAreaElementProps extends TextAreaElementProps {
1593
+ ref?: GeaElement | null
1594
+ }
1595
+
1596
+ export declare function mount(component: new () => Component): void
1597
+
1598
+ /**
1599
+ * Pack 8-bit channels into the canonical canvas colour number (0xRRGGBBAA, red in
1600
+ * the high byte). Pass the result to the numeric canvas colour APIs
1601
+ * (`fillCircleRgb565`, `fillTriangleRgb565`, …). geatsc lowers it to this board's
1602
+ * native pixel — no per-app pixel packing, and literal calls fold at build time.
1603
+ */
1604
+ export declare function rgb(r: number, g: number, b: number): number
1605
+ export declare function rgba(r: number, g: number, b: number, a: number): number
1606
+
1607
+ export interface FetchHeadersInit {
1608
+ [name: string]: string
1609
+ }
1610
+
1611
+ export interface FetchRequestInit {
1612
+ method?: string
1613
+ headers?: FetchHeadersInit
1614
+ body?: string | ArrayBuffer | Uint8Array
1615
+ }
1616
+
1617
+ export interface FetchHeaders {
1618
+ get(name: string): string | null
1619
+ has(name: string): boolean
1620
+ }
1621
+
1622
+ export interface FetchResponse {
1623
+ readonly ok: boolean
1624
+ readonly status: number
1625
+ readonly statusText: string
1626
+ readonly headers: FetchHeaders
1627
+ /** Raw response body bytes (binary-safe). */
1628
+ readonly body: Uint8Array
1629
+ text(): string
1630
+ json(): unknown
1631
+ /** Binary body — feed straight to `loadImage`. Both spellings are equivalent. */
1632
+ arrayBuffer(): Uint8Array
1633
+ bytes(): Uint8Array
1634
+ }
1635
+
1636
+ export declare function fetchAsync(url: string, init?: FetchRequestInit): number
1637
+ export declare function fetchReady(id: number): boolean
1638
+ export declare function fetchResult(id: number): FetchResponse
1639
+ export declare function fetchRelease(id: number): void
1640
+
1641
+ /**
1642
+ * Streaming multipart file upload (POST). The request body is sent as
1643
+ * `prefix` + the file at `filePath` (streamed from disk in chunks, never loaded
1644
+ * into RAM) + `suffix`, so a large recording uploads without an OOM. `auth` and
1645
+ * `contentType` set the Authorization / Content-Type headers (pass '' to skip).
1646
+ * Async: returns a job id, polled/collected with fetchReady/fetchResult/fetchRelease.
1647
+ */
1648
+ export declare function fetchUploadFileAsync(
1649
+ url: string,
1650
+ auth: string,
1651
+ contentType: string,
1652
+ prefix: string,
1653
+ filePath: string,
1654
+ suffix: string
1655
+ ): number
1656
+
1657
+ /**
1658
+ * Progress (0..1) of the single in-flight streaming upload, or -1 when no upload
1659
+ * is active. Sync uploads one note at a time, so this tracks the current one.
1660
+ */
1661
+ /**
1662
+ * Streaming download: GET `url`, writing the response body to `destPath` on the
1663
+ * device (SD), in chunks — never buffered whole in RAM (unlike `fetch`, capped
1664
+ * at 2 MB). Async: returns a job id, polled with fetchReady/fetchResult/fetchRelease.
1665
+ * Progress shares the fetchUploadProgress/fetchUploadSent/fetchUploadTotal counters.
1666
+ */
1667
+ export declare function fetchDownloadFileAsync(url: string, auth: string, destPath: string): number
1668
+ export declare function fetchUploadProgress(id: number): number
1669
+ /** Bytes streamed / total bytes of the active upload (0 when idle) — for MB and speed readouts. */
1670
+ export declare function fetchUploadSent(id: number): number
1671
+ export declare function fetchUploadTotal(id: number): number
1672
+
1673
+ declare global {
1674
+ function fetch(url: string, init?: FetchRequestInit): FetchResponse
1675
+ function fetchAsync(url: string, init?: FetchRequestInit): number
1676
+ function fetchUploadFileAsync(
1677
+ url: string,
1678
+ auth: string,
1679
+ contentType: string,
1680
+ prefix: string,
1681
+ filePath: string,
1682
+ suffix: string
1683
+ ): number
1684
+ function fetchDownloadFileAsync(url: string, auth: string, destPath: string): number
1685
+ function fetchUploadProgress(id: number): number
1686
+ function fetchUploadSent(id: number): number
1687
+ function fetchUploadTotal(id: number): number
1688
+ function fetchReady(id: number): boolean
1689
+ function fetchResult(id: number): FetchResponse
1690
+ function fetchRelease(id: number): void
1691
+ function atob(data: string): string
1692
+ function btoa(data: string | ArrayBuffer | Uint8Array): string
1693
+ }
1694
+
1695
+ export interface WebSocketMessageEvent {
1696
+ readonly data: string
1697
+ }
1698
+
1699
+ export interface WebSocketCloseEvent {
1700
+ readonly code: number
1701
+ readonly reason: string
1702
+ readonly wasClean: boolean
1703
+ }
1704
+
1705
+ export interface WebSocketErrorEvent {
1706
+ readonly message: string
1707
+ }
1708
+
1709
+ export interface WebSocketInstance {
1710
+ readonly url: string
1711
+ readonly readyState: number
1712
+ onopen: (() => void) | null
1713
+ onmessage: ((event: WebSocketMessageEvent) => void) | null
1714
+ onclose: ((event: WebSocketCloseEvent) => void) | null
1715
+ onerror: ((event: WebSocketErrorEvent) => void) | null
1716
+ send(data: string): void
1717
+ close(code?: number, reason?: string): void
1718
+ }
1719
+
1720
+ export interface WebSocketConstructor {
1721
+ new (url: string): WebSocketInstance
1722
+ readonly CONNECTING: 0
1723
+ readonly OPEN: 1
1724
+ readonly CLOSING: 2
1725
+ readonly CLOSED: 3
1726
+ }
1727
+
1728
+ declare global {
1729
+ const WebSocket: WebSocketConstructor
1730
+ }
1731
+
1732
+ /**
1733
+ * A device-hosted HTTP server, modeled after Node's `http` package. The device
1734
+ * connects to Wi-Fi (STA) and serves over its LAN IP, so a phone or laptop on
1735
+ * the same network can browse and download from it.
1736
+ *
1737
+ * Unlike Node, the handler does not get a mutable `res`: it RETURNS a plain
1738
+ * reply object. This keeps every request a pure value crossing the JS<->native
1739
+ * boundary (like a WebSocket message), and lets large responses stream straight
1740
+ * off disk — a reply that sets `file` streams that path on the server worker
1741
+ * without ever loading the bytes into JS.
1742
+ */
1743
+ export interface IncomingMessage {
1744
+ /** "GET", "POST", ... */
1745
+ readonly method: string
1746
+ /** Full request target including the query string (e.g. "/n/3.wav?x=1"). */
1747
+ readonly url: string
1748
+ /** Request path with the query stripped (e.g. "/n/3.wav"). */
1749
+ readonly path: string
1750
+ /** Raw query string after '?', or "" when absent. */
1751
+ readonly query: string
1752
+ }
1753
+
1754
+ export interface HttpReply {
1755
+ /** HTTP status code. Defaults to 200. */
1756
+ status?: number
1757
+ /** Response Content-Type. Defaults to "text/plain". */
1758
+ contentType?: string
1759
+ /** Inline response body. Ignored when `file` is set. */
1760
+ body?: string
1761
+ /** Stream this file path straight from disk; large files never enter JS. */
1762
+ file?: string
1763
+ /** When set, send as a download with this attachment filename. */
1764
+ download?: string
1765
+ }
1766
+
1767
+ export type HttpRequestHandler = (req: IncomingMessage) => HttpReply
1768
+
1769
+ export interface HttpServer {
1770
+ /** Start listening on `port`. Returns false if the socket could not bind. */
1771
+ listen(port: number): boolean
1772
+ /** Stop the server and release the port. */
1773
+ close(): void
1774
+ /** Numeric handle — persist this (not the object) to close later via `http.close`. */
1775
+ id(): number
1776
+ }
1777
+
1778
+ export interface HttpModule {
1779
+ createServer(handler: HttpRequestHandler): HttpServer
1780
+ /** Stop + release a server by the handle from `HttpServer.id()`. */
1781
+ close(handle: number): void
1782
+ }
1783
+
1784
+ export declare const http: HttpModule
1785
+
1786
+ export interface MediaStreamTrack {
1787
+ readonly id: string
1788
+ readonly kind: 'audio'
1789
+ enabled: boolean
1790
+ readonly readyState: 'live' | 'ended'
1791
+ stop(): void
1792
+ }
1793
+
1794
+ export interface MediaStream {
1795
+ readonly id: string
1796
+ getAudioTracks(): MediaStreamTrack[]
1797
+ getTracks(): MediaStreamTrack[]
1798
+ }
1799
+
1800
+ export interface MediaStreamConstructor {
1801
+ new (): MediaStream
1802
+ }
1803
+
1804
+ export interface MediaStreamConstraints {
1805
+ audio?: boolean | { sampleRate?: number; channelCount?: number; echoCancellation?: boolean }
1806
+ video?: false
1807
+ }
1808
+
1809
+ export interface MediaDevices {
1810
+ getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>
1811
+ }
1812
+
1813
+ export interface GeaAudioBlob extends Blob {
1814
+ readonly path: string
1815
+ }
1816
+
1817
+ export interface MediaRecorderDataAvailableEvent {
1818
+ readonly data: GeaAudioBlob
1819
+ }
1820
+
1821
+ export type MediaRecorderState = 'inactive' | 'recording' | 'paused'
1822
+
1823
+ export interface MediaRecorderOptions {
1824
+ mimeType?: string
1825
+ audioBitsPerSecond?: number
1826
+ path?: string
1827
+ }
1828
+
1829
+ export interface MediaRecorder {
1830
+ readonly stream: MediaStream
1831
+ readonly mimeType: string
1832
+ readonly state: MediaRecorderState
1833
+ ondataavailable: ((event: MediaRecorderDataAvailableEvent) => void) | null
1834
+ onstop: (() => void) | null
1835
+ start(timeslice?: number): void
1836
+ stop(): void
1837
+ }
1838
+
1839
+ export interface MediaRecorderConstructor {
1840
+ new (stream: MediaStream, options?: MediaRecorderOptions): MediaRecorder
1841
+ isTypeSupported(type: string): boolean
1842
+ }
1843
+
1844
+ export interface HTMLAudioElement extends Element {
1845
+ src: string
1846
+ currentTime: number
1847
+ volume: number
1848
+ loop: boolean
1849
+ play(): void
1850
+ pause(): void
1851
+ }
1852
+
1853
+ export interface AudioConstructor {
1854
+ new (src?: string): HTMLAudioElement
1855
+ }
1856
+
1857
+ declare global {
1858
+ interface Blob {
1859
+ readonly path: string
1860
+ }
1861
+
1862
+ interface MediaRecorderOptions {
1863
+ path?: string
1864
+ }
1865
+
1866
+ interface MediaRecorderDataAvailableEvent {
1867
+ readonly data: GeaAudioBlob
1868
+ }
1869
+
1870
+ interface BaseAudioContext {
1871
+ decodeAudioData(audioData: Uint8Array | GeaAudioBlob): Promise<AudioBuffer>
1872
+ }
1873
+
1874
+ interface Navigator {
1875
+ readonly mediaDevices: MediaDevices
1876
+ readonly geolocation: GeolocationController
1877
+ }
1878
+ const navigator: Navigator
1879
+ const MediaStream: MediaStreamConstructor
1880
+ const MediaRecorder: MediaRecorderConstructor
1881
+ const Audio: AudioConstructor
1882
+ const AudioContext: AudioContextConstructor
1883
+ }
1884
+
1885
+ export interface RTCSessionDescriptionInit {
1886
+ type: 'offer' | 'answer' | 'pranswer' | 'rollback'
1887
+ sdp?: string
1888
+ }
1889
+
1890
+ export interface RTCIceCandidateInit {
1891
+ candidate: string
1892
+ sdpMid?: string | null
1893
+ sdpMLineIndex?: number | null
1894
+ }
1895
+
1896
+ export interface RTCIceServer {
1897
+ urls: string | string[]
1898
+ username?: string
1899
+ credential?: string
1900
+ }
1901
+
1902
+ export interface RTCConfiguration {
1903
+ iceServers?: RTCIceServer[]
1904
+ }
1905
+
1906
+ export interface RTCPeerConnectionIceEvent {
1907
+ readonly candidate: RTCIceCandidateInit | null
1908
+ }
1909
+
1910
+ export interface RTCTrackEvent {
1911
+ readonly track: MediaStreamTrack
1912
+ readonly streams: readonly MediaStream[]
1913
+ }
1914
+
1915
+ export interface RTCPeerConnectionInstance {
1916
+ readonly connectionState: 'new' | 'connecting' | 'connected' | 'disconnected' | 'failed' | 'closed'
1917
+ readonly iceConnectionState: 'new' | 'checking' | 'connected' | 'completed' | 'failed' | 'disconnected' | 'closed'
1918
+
1919
+ onicecandidate: ((event: RTCPeerConnectionIceEvent) => void) | null
1920
+ ontrack: ((event: RTCTrackEvent) => void) | null
1921
+ onconnectionstatechange: (() => void) | null
1922
+ oniceconnectionstatechange: (() => void) | null
1923
+
1924
+ addTrack(track: MediaStreamTrack, stream: MediaStream): void
1925
+ createOffer(): Promise<RTCSessionDescriptionInit>
1926
+ createAnswer(): Promise<RTCSessionDescriptionInit>
1927
+ setLocalDescription(desc: RTCSessionDescriptionInit): Promise<void>
1928
+ setRemoteDescription(desc: RTCSessionDescriptionInit): Promise<void>
1929
+ addIceCandidate(candidate: RTCIceCandidateInit): Promise<void>
1930
+ close(): void
1931
+ }
1932
+
1933
+ export interface RTCPeerConnectionConstructor {
1934
+ new (config?: RTCConfiguration): RTCPeerConnectionInstance
1935
+ }
1936
+
1937
+ declare global {
1938
+ const RTCPeerConnection: RTCPeerConnectionConstructor
1939
+ function Image(props: ImageProps): GeaJsxElement
1940
+ }