@solidrt/core 0.0.35 → 0.0.37
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/package.json +2 -2
- package/src/core.ts +16 -5
- package/src/index.ts +3 -3
- package/src/runtime-modules.d.ts +47 -9
- package/src/types.d.ts +10 -0
- package/src/window.ts +61 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"colord": "^2.9.3"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@solidrt/flux-types": "0.0.
|
|
30
|
+
"@solidrt/flux-types": "0.0.37"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@solidjs/signals": "2.0.0-beta.20",
|
package/src/core.ts
CHANGED
|
@@ -64,16 +64,27 @@ export interface BoundingBox {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Returns the node's
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
67
|
+
* Returns the node's bounding box from the most recently computed layout,
|
|
68
|
+
* relative to its nearest positioning context (an ancestor with an explicit
|
|
69
|
+
* `position="relative"`, falling back to the window), or `null` if the node
|
|
70
|
+
* has no layout or has not been laid out yet. This is a snapshot read, not
|
|
71
|
+
* reactive: call it inside `onLayout` (or an event handler) to get values for
|
|
72
|
+
* the current frame. Transforms anywhere in the chain (including the node's
|
|
73
|
+
* own) compose fully; the box is the axis-aligned bounds of the transformed
|
|
74
|
+
* quad.
|
|
72
75
|
*/
|
|
73
76
|
export function getBoundingBox(node: { id: number }): BoundingBox | null {
|
|
74
77
|
return tree.getBoundingBox(node.id)
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Like getBoundingBox, but always window-relative (getBoundingClientRect
|
|
82
|
+
* semantics), the frame pointer event clientX/clientY are reported in.
|
|
83
|
+
*/
|
|
84
|
+
export function getBoundingBoxViewport(node: { id: number }): BoundingBox | null {
|
|
85
|
+
return tree.getBoundingBoxViewport(node.id)
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
/**
|
|
78
89
|
* Measures the rendered size of `text` in layout pixels under the given font
|
|
79
90
|
* options (family, size, weight, style, maxLines), without adding it to the
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from "./renderer"
|
|
2
|
-
export { setFocus, getFocusedNodeId, measureText, getBoundingBox } from "./core"
|
|
2
|
+
export { setFocus, getFocusedNodeId, measureText, getBoundingBox, getBoundingBoxViewport } from "./core"
|
|
3
3
|
export type { BoundingBox } from "./core"
|
|
4
4
|
export { parseColor, mixColors, brightness, createLinearGradient, createRadialGradient } from "./color"
|
|
5
5
|
export type { Gradient, GradientStop } from "./color"
|
|
6
|
-
export { onFrame, onLayout, onResize, onWindowFocus, onWindowBlur } from "./window"
|
|
7
|
-
export {
|
|
6
|
+
export { onFrame, onLayout, onResize, onWindowFocus, onWindowBlur, onBack, exit } from "./window"
|
|
7
|
+
export type { BackEvent } from "./window"
|
|
8
8
|
export { windowSize, safeArea, displayScale, windowFocused, keyboardHeight } from "./window"
|
|
9
9
|
export { env } from "./environment"
|
|
10
10
|
export type { InputDevices, SystemTheme, Orientation } from "./environment"
|
package/src/runtime-modules.d.ts
CHANGED
|
@@ -31,6 +31,17 @@ declare module "srt:events" {
|
|
|
31
31
|
export function once(event: string, callback: (data: any) => void): () => void
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// The running application's own surface (lattice), present in every build.
|
|
35
|
+
declare module "srt:app" {
|
|
36
|
+
/**
|
|
37
|
+
* Leave the current app, unconditionally: back to the launcher in a dev
|
|
38
|
+
* client, quit when standalone or at the launcher root (on Android the
|
|
39
|
+
* client backgrounds instead of dying). The default action of an
|
|
40
|
+
* unprevented `back` event; prefer the @solidrt/core re-export.
|
|
41
|
+
*/
|
|
42
|
+
export function exit(): void
|
|
43
|
+
}
|
|
44
|
+
|
|
34
45
|
// Dev-server control surface (lattice). Present only in dev/go builds; in other
|
|
35
46
|
// builds `available` is false and the functions are no-ops.
|
|
36
47
|
declare module "srt:dev" {
|
|
@@ -63,18 +74,28 @@ declare module "srt:apps" {
|
|
|
63
74
|
export type InstalledApp = { id: string; name: string; version: string }
|
|
64
75
|
/** Installed apps, sorted by name. */
|
|
65
76
|
export function list(): InstalledApp[]
|
|
66
|
-
/**
|
|
67
|
-
|
|
77
|
+
/**
|
|
78
|
+
* A stored version: id (manifest hash), bytes on disk, whether it is the
|
|
79
|
+
* current one, and the SolidRT (CLI) release that built it per its
|
|
80
|
+
* manifest ("unknown" from an in-repo CLI or when the manifest predates
|
|
81
|
+
* the field).
|
|
82
|
+
*/
|
|
83
|
+
export type AppVersion = { id: string; size: number; current: boolean; solidrtVersion: string }
|
|
68
84
|
/** One file in a listing: a relative path and its size in bytes. */
|
|
69
85
|
export type AppFile = { path: string; size: number }
|
|
86
|
+
/**
|
|
87
|
+
* One fetch-cache entry: the cached (resolved) url, the response content
|
|
88
|
+
* type (lowercased, parameters stripped; absent when the response had
|
|
89
|
+
* none) and the entry's size on disk.
|
|
90
|
+
*/
|
|
91
|
+
export type AppCacheEntry = { url: string; type?: string; size: number }
|
|
70
92
|
/**
|
|
71
93
|
* Usage details for one installed app: total bytes of its stored versions
|
|
72
|
-
* (assets shared between versions via hardlinks count in each)
|
|
73
|
-
* data sandbox, plus the stored versions (current
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* divergence from the manifest is visible.
|
|
94
|
+
* (assets shared between versions via hardlinks count in each), of its
|
|
95
|
+
* data sandbox and of its fetch cache, plus the stored versions (current
|
|
96
|
+
* first, then newest first) and three listings: `files` and `data` are
|
|
97
|
+
* disk walks of the current version dir and the data sandbox (sorted by
|
|
98
|
+
* path), `cache` is the fetch cache's entries (sorted by url).
|
|
78
99
|
*/
|
|
79
100
|
export type AppInfo = {
|
|
80
101
|
id: string
|
|
@@ -82,10 +103,11 @@ declare module "srt:apps" {
|
|
|
82
103
|
version: string
|
|
83
104
|
installSize: number
|
|
84
105
|
dataSize: number
|
|
106
|
+
cacheSize: number
|
|
85
107
|
versions: AppVersion[]
|
|
86
|
-
assets: AppFile[]
|
|
87
108
|
files: AppFile[]
|
|
88
109
|
data: AppFile[]
|
|
110
|
+
cache: AppCacheEntry[]
|
|
89
111
|
}
|
|
90
112
|
/** Usage details for an installed app. Throws when the app is not installed. */
|
|
91
113
|
export function info(id: string): AppInfo
|
|
@@ -100,6 +122,22 @@ declare module "srt:apps" {
|
|
|
100
122
|
* the app is not installed.
|
|
101
123
|
*/
|
|
102
124
|
export function remove(id: string): void
|
|
125
|
+
/**
|
|
126
|
+
* Delete the app's fetch cache. Clearing a missing or empty cache is a
|
|
127
|
+
* no-op; the id does not need to be installed, so a removed app's
|
|
128
|
+
* leftover cache is still clearable.
|
|
129
|
+
*/
|
|
130
|
+
export function clearCache(id: string): void
|
|
131
|
+
/**
|
|
132
|
+
* Build identity of this runtime, for the launcher's settings screen. Not
|
|
133
|
+
* app-specific, but surfaced here since the launcher already imports this
|
|
134
|
+
* module. `version` is the release version (git describe; "0.0.0-dev" in a
|
|
135
|
+
* plain build), `profile` is "debug" or "release", `platform` is the OS
|
|
136
|
+
* (std::env::consts::OS, e.g. "linux", "android", "windows", "macos").
|
|
137
|
+
*/
|
|
138
|
+
export const version: string
|
|
139
|
+
export const profile: string
|
|
140
|
+
export const platform: string
|
|
103
141
|
}
|
|
104
142
|
|
|
105
143
|
// Frame draw (lattice runner). renderFrame() synchronously renders the current
|
package/src/types.d.ts
CHANGED
|
@@ -198,8 +198,18 @@ export interface WheelEvent extends PointerEvent {
|
|
|
198
198
|
deltaY: number
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// Key events use the W3C UI Events vocabulary: `key` is the logical,
|
|
202
|
+
// layout-dependent value ("a", "!", "Enter", "ArrowLeft"); `code` is the
|
|
203
|
+
// physical, layout-independent key position ("KeyA", "Digit1", "NumpadEnter").
|
|
204
|
+
// Printable characters for text entry arrive via onTextInput, not here.
|
|
201
205
|
export interface KeyEvent {
|
|
202
206
|
key: string
|
|
207
|
+
code: string
|
|
208
|
+
repeat: boolean
|
|
209
|
+
shiftKey: boolean
|
|
210
|
+
ctrlKey: boolean
|
|
211
|
+
altKey: boolean
|
|
212
|
+
metaKey: boolean
|
|
203
213
|
}
|
|
204
214
|
|
|
205
215
|
export interface TextEvent {
|
package/src/window.ts
CHANGED
|
@@ -2,30 +2,30 @@ import { createSignal, onCleanup, onSettled, flush } from "@solidjs/signals"
|
|
|
2
2
|
import { requestFrame } from "flux:rendertree"
|
|
3
3
|
import { renderFrame } from "srt:render"
|
|
4
4
|
import { on, once } from "srt:events"
|
|
5
|
+
import { exit } from "srt:app"
|
|
5
6
|
import { getEventHandler, getFocusedNodeId, setFocus } from "./core"
|
|
6
7
|
import { scanForOrphans } from "./renderer"
|
|
7
8
|
|
|
8
|
-
// ------ Pointer capture -----------------
|
|
9
|
-
|
|
10
|
-
// Active pointer captures, pointerId -> nodeId. While a pointer is captured, its
|
|
11
|
-
// move/up events dispatch straight to the captured node (bypassing hit testing),
|
|
12
|
-
// so a drag keeps working when the pointer drifts off the element. Modeled on the
|
|
13
|
-
// web setPointerCapture; capture auto-releases on pointerUp.
|
|
14
|
-
let pointerCaptures = new Map<number, number>()
|
|
15
|
-
|
|
16
9
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
10
|
+
* Leaves the current app, unconditionally: back to the launcher in a dev
|
|
11
|
+
* client, quitting when standalone or at the launcher itself (on Android the
|
|
12
|
+
* client backgrounds instead of dying). The default action of an unprevented
|
|
13
|
+
* `back` event; call it directly to exit programmatically, e.g. after
|
|
14
|
+
* intercepting back for an unsaved-changes dialog.
|
|
20
15
|
*/
|
|
21
|
-
export
|
|
22
|
-
pointerCaptures.set(pointerId, nodeId)
|
|
23
|
-
}
|
|
16
|
+
export { exit }
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
// ------ Pointer routing -----------------
|
|
19
|
+
|
|
20
|
+
// Hit path frozen at pointerDown, pointerId -> targets. While a pointer has an
|
|
21
|
+
// active down, its moves and up dispatch along this path (same leaf-to-root
|
|
22
|
+
// bubble) no matter where the pointer currently is, so every node under the
|
|
23
|
+
// original down observes the whole gesture: a drag keeps working off-element,
|
|
24
|
+
// and an ancestor recognizer (a scroller's pan) sees the moves it needs to
|
|
25
|
+
// take over mid-gesture via the arena. There is no exclusive pointer capture;
|
|
26
|
+
// gesture ownership is claim-based, above this layer. Enter/leave stay
|
|
27
|
+
// hover-driven, and moves with no active down follow the live hit path.
|
|
28
|
+
let downPaths = new Map<number, number[]>()
|
|
29
29
|
|
|
30
30
|
// ------ Animation frames ----------------
|
|
31
31
|
|
|
@@ -189,6 +189,30 @@ export function onWindowBlur(fn: () => void) {
|
|
|
189
189
|
return unsubscribe
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// ------ Back ----------------
|
|
193
|
+
|
|
194
|
+
export type BackEvent = { preventDefault: () => void }
|
|
195
|
+
|
|
196
|
+
// App handlers for the window-level back event, run in registration order.
|
|
197
|
+
// Kept in a local registry (not per-handler bus subscriptions) so the default
|
|
198
|
+
// action runs exactly once, after every handler has had its say.
|
|
199
|
+
let backHandlers = new Set<(e: BackEvent) => void>()
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Calls `fn` on the user's back intent (Android back button/gesture, the
|
|
203
|
+
* desktop dev chord). Call `e.preventDefault()` when back means in-app
|
|
204
|
+
* navigation right now (close a modal, previous screen); unprevented, the
|
|
205
|
+
* default action runs: exit(). Apps without a handler exit on back
|
|
206
|
+
* everywhere, which is the correct zero-effort default.
|
|
207
|
+
* Returns a cleanup function; also auto-cleans within a reactive scope.
|
|
208
|
+
*/
|
|
209
|
+
export function onBack(fn: (e: BackEvent) => void) {
|
|
210
|
+
backHandlers.add(fn)
|
|
211
|
+
let cleanup = () => backHandlers.delete(fn)
|
|
212
|
+
onCleanup(cleanup)
|
|
213
|
+
return cleanup
|
|
214
|
+
}
|
|
215
|
+
|
|
192
216
|
// ------ Window ----------------
|
|
193
217
|
|
|
194
218
|
export function attachWindow(_nodeId: number) {
|
|
@@ -201,6 +225,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
201
225
|
let unsubWheel: () => void = null!
|
|
202
226
|
let unsubKeyDown: () => void = null!
|
|
203
227
|
let unsubKeyUp: () => void = null!
|
|
228
|
+
let unsubBack: () => void = null!
|
|
204
229
|
let unsubTextInput: () => void = null!
|
|
205
230
|
let unsubKeyboardVisibility: () => void = null!
|
|
206
231
|
let unsubRefreshRate: () => void = null!
|
|
@@ -244,6 +269,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
244
269
|
unsubDown = on(
|
|
245
270
|
"pointerDown",
|
|
246
271
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
272
|
+
downPaths.set(e.pointerId, targets)
|
|
247
273
|
bubble(targets, "onPointerDown", e)
|
|
248
274
|
// Outside-tap blur. Read focus AFTER per-node handlers so a tap that
|
|
249
275
|
// moves focus to a new node is not immediately blurred again.
|
|
@@ -255,28 +281,15 @@ export function attachWindow(_nodeId: number) {
|
|
|
255
281
|
)
|
|
256
282
|
|
|
257
283
|
unsubUp = on("pointerUp", ({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
258
|
-
let
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
e.stopPropagation = () => {}
|
|
262
|
-
getEventHandler(captured, "onPointerUp")?.(e)
|
|
263
|
-
pointerCaptures.delete(e.pointerId)
|
|
264
|
-
return
|
|
265
|
-
}
|
|
266
|
-
bubble(targets, "onPointerUp", e)
|
|
284
|
+
let frozen = downPaths.get(e.pointerId)
|
|
285
|
+
downPaths.delete(e.pointerId)
|
|
286
|
+
bubble(frozen ?? targets, "onPointerUp", e)
|
|
267
287
|
})
|
|
268
288
|
|
|
269
289
|
unsubMove = on(
|
|
270
290
|
"pointerMove",
|
|
271
291
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
272
|
-
|
|
273
|
-
if (captured != null) {
|
|
274
|
-
// Captured drags get moves anywhere over the window, bypassing hit test.
|
|
275
|
-
e.stopPropagation = () => {}
|
|
276
|
-
getEventHandler(captured, "onPointerMove")?.(e)
|
|
277
|
-
return
|
|
278
|
-
}
|
|
279
|
-
bubble(targets, "onPointerMove", e)
|
|
292
|
+
bubble(downPaths.get(e.pointerId) ?? targets, "onPointerMove", e)
|
|
280
293
|
},
|
|
281
294
|
)
|
|
282
295
|
|
|
@@ -326,6 +339,18 @@ export function attachWindow(_nodeId: number) {
|
|
|
326
339
|
}
|
|
327
340
|
})
|
|
328
341
|
|
|
342
|
+
unsubBack = on("back", () => {
|
|
343
|
+
let prevented = false
|
|
344
|
+
let e: BackEvent = {
|
|
345
|
+
preventDefault: () => {
|
|
346
|
+
prevented = true
|
|
347
|
+
},
|
|
348
|
+
}
|
|
349
|
+
// Copy first: a handler may unregister (itself or others) mid-dispatch.
|
|
350
|
+
for (let fn of [...backHandlers]) fn(e)
|
|
351
|
+
if (!prevented) exit()
|
|
352
|
+
})
|
|
353
|
+
|
|
329
354
|
unsubTextInput = on("textInput", (e: any) => {
|
|
330
355
|
let id = getFocusedNodeId()
|
|
331
356
|
if (id != null) {
|
|
@@ -361,6 +386,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
361
386
|
if (unsubWheel) unsubWheel()
|
|
362
387
|
if (unsubKeyDown) unsubKeyDown()
|
|
363
388
|
if (unsubKeyUp) unsubKeyUp()
|
|
389
|
+
if (unsubBack) unsubBack()
|
|
364
390
|
if (unsubTextInput) unsubTextInput()
|
|
365
391
|
if (unsubKeyboardVisibility) unsubKeyboardVisibility()
|
|
366
392
|
if (unsubRefreshRate) unsubRefreshRate()
|