@solidrt/core 0.0.60 → 0.0.62

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/src/window.ts CHANGED
@@ -2,7 +2,7 @@ import { createSignal, getOwner, onCleanup, onSettled, runWithOwner, flush } fro
2
2
  import { requestFrame, setPointerLock } from "flux:rendertree"
3
3
  import { renderFrame } from "srt:render"
4
4
  import { on, once } from "srt:events"
5
- import { exit as nativeExit, background as nativeBackground } from "srt:app"
5
+ import { exit as nativeExit, background as nativeBackground, registerProtocolHandler as nativeRegisterProtocolHandler } from "srt:app"
6
6
  import { platform } from "flux:process"
7
7
  import { getEventHandler, focusedNode, setFocus, activateTextInput, setInterestRoot } from "./core"
8
8
  import { scanForOrphans, getNodePath } from "./renderer"
@@ -141,6 +141,20 @@ export function background() {
141
141
  nativeBackground()
142
142
  }
143
143
 
144
+ /**
145
+ * Makes this install the handler for the app's own scheme, so links like
146
+ * `com.example.app://settings` (the scheme is the appId) open the app from a
147
+ * browser, another app or the shell: `env.launchLink` when it starts the
148
+ * app, `onLink` when the app already runs. The web's name, simplified: no
149
+ * arguments, the scheme is fixed. A packed desktop app has to call it,
150
+ * since nothing installs a single executable (call it again after moving
151
+ * the executable); Android and macOS packages declare the scheme, so the
152
+ * call does nothing there. Throws when the registration cannot be written.
153
+ */
154
+ export function registerProtocolHandler() {
155
+ nativeRegisterProtocolHandler()
156
+ }
157
+
144
158
  // The default action of an unprevented back event: what the platform itself
145
159
  // does at the root of an app. Android backgrounds (since Android 12);
146
160
  // everywhere else back at the root ends the app.
@@ -169,15 +183,22 @@ let refreshRate = 60
169
183
 
170
184
  /**
171
185
  * Calls `fn` before every frame is painted: `tick` is the time in ms, `frame` is
172
- * the present count, and `rate` is the current refresh rate in Hz. `tick` is paced
173
- * by the runtime (one refresh period per present, slow-corrected toward the wall
174
- * clock) so animations driven off it stay smooth even when swap-return times
175
- * jitter, and it is continuous across hot reloads: every tick an instance
176
- * sees is on one timebase, so dt is well-defined from the second call on.
177
- * Timers freeze together with frame callbacks under the dev tools' clock
178
- * control, but measure their delays against the wall clock, not this paced
179
- * timeline. performance.now() is not on it either: it is real elapsed time
180
- * for measuring work; Date.now() is calendar time.
186
+ * the present count, and `rate` is the current refresh rate in Hz. `tick`
187
+ * advances by the display refreshes since the previous frame, as the runtime
188
+ * counts them: exactly one refresh period per frame while the app keeps the
189
+ * refresh rate, and whole multiples of it when a frame takes longer (the
190
+ * requestAnimationFrame contract; a 21 fps app on a 90 Hz display sees
191
+ * 44 and 56 ms deltas, because that is how long each frame was on screen).
192
+ * Deltas are therefore honest and never lag the wall clock; an app that
193
+ * wants motion smoother than its own frame rate steps its simulation at a
194
+ * fixed rate or averages the deltas itself. A gap over half a second (the
195
+ * app was backgrounded) advances one period, not the gap. The timebase is
196
+ * continuous across hot reloads: every tick an instance sees is on one
197
+ * timebase, so dt is well-defined from the second call on. Timers freeze
198
+ * together with frame callbacks under the dev tools' clock control, but
199
+ * measure their delays against the wall clock, not this frame timeline.
200
+ * performance.now() is not on it either: it is real elapsed time for
201
+ * measuring work; Date.now() is calendar time.
181
202
  * Returns a cleanup function; also auto-cleans within an owned scope. Outside
182
203
  * one - an effect's apply phase, an event handler - the returned cleanup is
183
204
  * the only handle, so return it from the apply (`return onFrame(...)`) to run
@@ -410,6 +431,23 @@ export function onWindowFocus(fn: () => void) {
410
431
  return unsubscribe
411
432
  }
412
433
 
434
+ /**
435
+ * Calls `fn` with a link handed to the running app from outside: a custom
436
+ * scheme link the OS routed here (another app, a QR code, a notification),
437
+ * or one a dev tool sent (`open_link` over MCP, `POST /__control__/link`).
438
+ * The raw string, exactly as it arrived; the app decides what it means, and
439
+ * it is untrusted input: validate before acting on any part of it. The link
440
+ * the app was *started* with is not an event (nothing was listening yet) but
441
+ * a fact: `env.launchLink`.
442
+ *
443
+ * Returns a cleanup function; also auto-cleans within an owned scope.
444
+ */
445
+ export function onLink(fn: (link: string) => void) {
446
+ let unsubscribe = on("link", (e: { link: string }) => fn(e.link))
447
+ if (getOwner()) onCleanup(unsubscribe)
448
+ return unsubscribe
449
+ }
450
+
413
451
  export function onWindowBlur(fn: () => void) {
414
452
  let unsubscribe = on("windowBlur", fn)
415
453
  if (getOwner()) onCleanup(unsubscribe)