@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.
- package/LICENSE +202 -0
- package/README.md +36 -0
- package/app_frame_perf.cpp +77 -0
- package/css.d.ts +1 -0
- package/events.cpp +67 -0
- package/gea_app_entry.cpp +317 -0
- package/gea_sources.mjs +126 -0
- package/gea_sources.sh +36 -0
- package/include/app.h +60 -0
- package/include/apps.h +46 -0
- package/include/audio.h +90 -0
- package/include/ble.h +212 -0
- package/include/bluetooth/hid_profile.h +92 -0
- package/include/camera.h +107 -0
- package/include/canvas.h +290 -0
- package/include/display.h +390 -0
- package/include/display_present.h +869 -0
- package/include/event.h +32 -0
- package/include/events.h +116 -0
- package/include/gea/embedded.h +13 -0
- package/include/gea/reactive_runtime.h +32 -0
- package/include/gea.h +14 -0
- package/include/gea_perf_config.h +96 -0
- package/include/geaos/abi.h +165 -0
- package/include/geolocation.h +126 -0
- package/include/gpio.h +61 -0
- package/include/graphics/font.h +122 -0
- package/include/i2c.h +20 -0
- package/include/image.h +156 -0
- package/include/imu.h +23 -0
- package/include/input.h +33 -0
- package/include/memory.h +52 -0
- package/include/pixel.h +907 -0
- package/include/platform/file_cache.h +18 -0
- package/include/power.h +11 -0
- package/include/runtime.h +83 -0
- package/include/services/app_runner.h +10 -0
- package/include/services/app_state.h +14 -0
- package/include/services/battery_service.h +12 -0
- package/include/services/bluetooth_service.h +13 -0
- package/include/services/diagnostics.h +76 -0
- package/include/services/diagnostics_platform.h +54 -0
- package/include/services/frame_scheduler.h +71 -0
- package/include/services/mirror.h +49 -0
- package/include/services/network_services.h +25 -0
- package/include/services/ota.h +32 -0
- package/include/services/runtime_log.h +11 -0
- package/include/services/storage_service.h +26 -0
- package/include/touch.h +38 -0
- package/include/ui/signal.h +149 -0
- package/include/wifi.h +67 -0
- package/index.d.ts +1940 -0
- package/jsx.d.ts +1 -0
- package/package.json +70 -0
- package/runtime/ble.ts +49 -0
- package/runtime/color.ts +21 -0
- package/runtime/compiler.ts +43 -0
- package/runtime/host.ts +1083 -0
- package/runtime/images.ts +129 -0
- package/runtime/primitives.ts +49 -0
- package/runtime-surface.ts +180 -0
- package/runtime.cpp +431 -0
- package/runtime.ts +34 -0
- package/scripts/build-gea-vite-geatsc.mjs +4514 -0
- package/scripts/dotenv-defines.mjs +98 -0
- package/scripts/gea-bundle-type-hints.mjs +546 -0
- package/scripts/gea-embedded-compat-staging.mjs +17 -0
- package/scripts/gea-embedded-compat-transform.mjs +427 -0
- package/scripts/gea-vite-module-graph-plugin.mjs +578 -0
- package/scripts/generate-gea-embedded-assets.mjs +296 -0
- package/scripts/generate-gea-embedded-fonts.mjs +1252 -0
- package/scripts/generate-installed-app-plan.mjs +55 -0
- 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
|
+
}
|