@fluxlay/react 1.2.0 → 1.3.0

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/dist/index.d.mts CHANGED
@@ -1,7 +1,259 @@
1
1
  import * as _$react from "react";
2
+ import { RefObject } from "react";
2
3
  import { FitAddon } from "@xterm/addon-fit";
3
4
  import * as _$_xterm_xterm0 from "@xterm/xterm";
4
5
  import { ITheme, Terminal } from "@xterm/xterm";
6
+ //#region ../sdk-core/dist/index.d.mts
7
+ //#region src/caret-position.d.ts
8
+ /**
9
+ * Compute the viewport-relative pixel rect of the text caret in a focused
10
+ * `<input>` or `<textarea>` element. Used by the IME pipeline to (a) draw a
11
+ * fake blinking caret while the field has lost OS focus to the IME proxy
12
+ * window, and (b) reposition the IME candidate panel so it tracks the caret
13
+ * as the user types.
14
+ *
15
+ * Implementation: the standard "mirror div" technique. We construct a hidden
16
+ * `<div>` styled identically to the field, set its text content to the value
17
+ * up to the caret, append a zero-width marker `<span>`, and read the marker's
18
+ * `getBoundingClientRect` to get the caret pixel position. The mirror div is
19
+ * removed immediately after measurement, so there is no persistent DOM
20
+ * footprint.
21
+ */
22
+ /** A caret rect in viewport-relative CSS pixels (top-left origin). */
23
+ interface CaretRect {
24
+ x: number;
25
+ y: number;
26
+ height: number;
27
+ }
28
+ /** A selection rect in viewport-relative CSS pixels (top-left origin). */
29
+ interface SelectionRect {
30
+ x: number;
31
+ y: number;
32
+ width: number;
33
+ height: number;
34
+ }
35
+ declare function measureCaretRect(el: HTMLInputElement | HTMLTextAreaElement, position?: number): CaretRect | null;
36
+ /**
37
+ * Reverse mapping: given a viewport-relative point, find the character
38
+ * index in `el.value` whose caret rect lies closest to it. Used to
39
+ * implement mouse-driven selection in environments where the browser's
40
+ * native drag-selection is broken (e.g. wallpaper webviews on macOS,
41
+ * which can't become the OS key window so AppKit drops mouseDragged
42
+ * events from the field).
43
+ *
44
+ * Strategy:
45
+ * 1. Binary-search a position whose caret y matches `clientY` (within
46
+ * one line height tolerance). For `<input>` (single-line) this step
47
+ * is skipped — every position shares the same y.
48
+ * 2. Within that row, binary-search by x for the position whose caret
49
+ * rect is closest to `clientX`.
50
+ *
51
+ * Cost: O(log² n) calls to {@link measureCaretRect}, each of which
52
+ * builds a one-shot mirror div. Acceptable for pointer-event-driven
53
+ * code (one call per pointermove tick, coalesced via rAF by the caller
54
+ * if needed).
55
+ */
56
+ declare function caretIndexFromClientPoint(el: HTMLInputElement | HTMLTextAreaElement, clientX: number, clientY: number): number;
57
+ /**
58
+ * Compute viewport-relative pixel rects covering the selected range
59
+ * `[start, end)` within a focused field. Returns an empty array if the
60
+ * range is collapsed.
61
+ *
62
+ * Approach: measure caret rects at start and end via the same mirror-div
63
+ * technique. If both fall on the same visual line (same y), emit a single
64
+ * rect spanning x_start → x_end. Otherwise (multi-line in `<textarea>`),
65
+ * emit three rects approximating the selection band:
66
+ * 1. start-line tail: x_start → field right edge
67
+ * 2. middle band: full field width × (end_y - start_y - lineHeight)
68
+ * 3. end-line head: field left → x_end
69
+ *
70
+ * The middle band over-approximates wrapped lines (it covers the entire
71
+ * width regardless of where each line actually ended), which matches the
72
+ * intent of a continuous "block selection" highlight. Padding/border
73
+ * insets are not respected — the band uses the field's bounding rect for
74
+ * left/right, so the highlight may bleed slightly into the border on
75
+ * fields with thick borders. This is intentional to keep the
76
+ * implementation cheap; visual fidelity is acceptable.
77
+ */
78
+ declare function measureSelectionRects(el: HTMLInputElement | HTMLTextAreaElement, start: number, end: number): SelectionRect[]; //#endregion
79
+ //#region src/coords.d.ts
80
+ /**
81
+ * Coordinate conversions between fluxlay's normalized OS space and CSS pixels.
82
+ *
83
+ * fluxlay reports pointer coordinates in the range `[-1, 1]` with the Y axis
84
+ * flipped (positive Y points upward, mirroring a mathematical coordinate
85
+ * system). The DOM, in contrast, uses CSS pixels with Y growing downward and
86
+ * the origin at the top-left of the viewport.
87
+ *
88
+ * Wallpaper windows always cover the entire screen, so `window.innerWidth` /
89
+ * `innerHeight` is the right reference frame for both directions.
90
+ */
91
+ /**
92
+ * Convert a normalized fluxlay coordinate (`[-1, 1]`, Y-up) to CSS pixels
93
+ * (Y-down) within the current window.
94
+ */
95
+ declare function normalizedToPixel(nx: number, ny: number): {
96
+ x: number;
97
+ y: number;
98
+ };
99
+ /**
100
+ * Convert a CSS pixel coordinate (Y-down) within the current window to the
101
+ * normalized fluxlay coordinate space (`[-1, 1]`, Y-up).
102
+ */
103
+ declare function pixelToNormalized(x: number, y: number): {
104
+ x: number;
105
+ y: number;
106
+ }; //#endregion
107
+ //#region src/file-url.d.ts
108
+ /**
109
+ * Build a URL the wallpaper webview can use to load a local file referenced
110
+ * by an `image` or `file` property value.
111
+ *
112
+ * The file is served by the Fluxlay desktop app via `GET /v1/file` and is
113
+ * restricted to paths currently assigned to an `image` / `file` property of
114
+ * the active wallpaper. Returns `null` when the path is missing.
115
+ */
116
+ declare function getPropertyFileUrl(path: string | null | undefined): string | null; //#endregion
117
+ //#region src/ime-inline.d.ts
118
+ /**
119
+ * Predicate: is `el` an element on which we want to auto-handle IME?
120
+ *
121
+ * Matches text-input `<input>`, `<textarea>`, and `[contenteditable]` nodes.
122
+ * Numeric / range / date / etc. inputs are excluded since they have no IME
123
+ * use case.
124
+ */
125
+ declare function isImeAutoTarget(el: EventTarget | null): el is HTMLElement;
126
+ /** Options for {@link ImeInlineController}. */
127
+ interface ImeInlineControllerOptions {
128
+ /**
129
+ * Render a thin blinking caret overlay at the focused field's caret
130
+ * position. Required for the auto-IME path because the IME proxy window
131
+ * steals OS focus from the wallpaper webview, which makes the native
132
+ * caret disappear. Default: `false`.
133
+ */
134
+ showCaret?: boolean;
135
+ /**
136
+ * Notified whenever the caret position changes (or `null` when the
137
+ * controller detaches). Used by the auto-IME path to forward caret
138
+ * coordinates to the backend so the IME candidate panel tracks the
139
+ * caret. Coordinates are viewport-relative CSS pixels.
140
+ */
141
+ onCaretChange?: (rect: CaretRect | null) => void;
142
+ }
143
+ /**
144
+ * Stateful controller that mirrors an IME composition into a focused
145
+ * input / textarea so it appears inline (matching native browser IME UX).
146
+ *
147
+ * Usage:
148
+ * 1. Call {@link setTarget} with the focused element when focus changes
149
+ * (and `null` on blur).
150
+ * 2. Call {@link updateComposition} on every composition stream update.
151
+ * 3. Call {@link commit} when an IME commit event arrives.
152
+ *
153
+ * The controller mutates `target.value` directly via the platform-native
154
+ * value setter and dispatches an `input` event so React controlled-input
155
+ * change tracking still fires.
156
+ *
157
+ * `[contenteditable]` targets only support {@link commit} (text is inserted
158
+ * via `document.execCommand("insertText", ...)`); inline composition for
159
+ * arbitrary DOM nodes would require positional text-range manipulation
160
+ * that is out of scope for this helper.
161
+ */
162
+ declare class ImeInlineController {
163
+ private target;
164
+ /**
165
+ * The range within `target.value` currently occupied by the in-progress
166
+ * composition. `null` while no composition is active.
167
+ */
168
+ private tracked;
169
+ private readonly showCaret;
170
+ private readonly onCaretChange?;
171
+ private overlay;
172
+ private selectionOverlay;
173
+ /**
174
+ * The "sticky" visual x coordinate for vertical caret motion. When the
175
+ * user presses Up/Down repeatedly, native textareas remember the
176
+ * column they started from so traversing through a short line and
177
+ * back to a long line returns the caret to the original column. We
178
+ * track the same here, resetting whenever any non-vertical motion or
179
+ * text mutation occurs.
180
+ */
181
+ private desiredVerticalX;
182
+ private caretListenersAttached;
183
+ private rafHandle;
184
+ private readonly onSelectionChange;
185
+ private readonly onTargetScroll;
186
+ private readonly onWindowResize;
187
+ /**
188
+ * Mouse-drag selection state. Browsers normally handle drag-select
189
+ * natively, but in environments where the host window can't become
190
+ * the OS key window (e.g. macOS wallpaper webviews) AppKit drops
191
+ * mouseDragged events from the field, so the browser never sees them
192
+ * and selection doesn't extend. We re-implement drag selection in JS
193
+ * by listening to pointermove on the target while a primary pointer
194
+ * is pressed, computing the caret index from the pointer coordinates,
195
+ * and calling `setSelectionRange` ourselves. Anchor is captured from
196
+ * the field's `selectionStart` after the browser handles the initial
197
+ * pointerdown (so single-click positioning still flows through the
198
+ * browser unchanged).
199
+ */
200
+ private dragState;
201
+ private readonly onTargetPointerDown;
202
+ private readonly onTargetPointerMove;
203
+ private readonly onTargetPointerUp;
204
+ private readonly onTargetPointerCancel;
205
+ constructor(options?: ImeInlineControllerOptions);
206
+ /** Returns the element this controller is currently tracking, if any. */
207
+ getTarget(): HTMLElement | null;
208
+ /**
209
+ * Switch the controller to a new target (or detach with `null`). If a
210
+ * composition was in progress on the previous target, the inline text is
211
+ * removed before switching so we don't leave half-typed glyphs behind.
212
+ */
213
+ setTarget(el: HTMLElement | null): void;
214
+ /**
215
+ * Apply a composition stream update. `composition` is the current marked
216
+ * text (or `null` when composition has ended). `cursor` is the caret
217
+ * position within the marked text in code units.
218
+ *
219
+ * On the first non-null update the controller captures the element's
220
+ * current `selectionStart` as the composition's anchor and inserts the
221
+ * text there. Subsequent updates replace the anchored range with the new
222
+ * composition text. A `null` update removes the inline range entirely
223
+ * (cancellation path; a commit handler should null-out the tracked
224
+ * range before this fires).
225
+ */
226
+ updateComposition(composition: string | null, cursor: number): void;
227
+ /**
228
+ * Apply a non-character key command (Backspace, arrow keys, Enter, Tab,
229
+ * etc.) that arrived from the macOS IME proxy because the proxy panel was
230
+ * the key window when the user pressed the key. `kind` is the Cocoa
231
+ * selector name forwarded by `doCommandBySelector:`
232
+ * (e.g. `deleteBackward:`, `moveLeft:`, `insertNewline:`).
233
+ *
234
+ * No-op for `[contenteditable]` targets — implementing arrow / delete
235
+ * semantics for arbitrary DOM is out of scope; wallpaper authors who need
236
+ * that should manage focus/composition manually.
237
+ */
238
+ applyCommand(kind: string): void;
239
+ private applyCommandInner;
240
+ /**
241
+ * Apply a commit. If a composition is currently inlined, its range is
242
+ * replaced with `text`; otherwise `text` is inserted at the current
243
+ * caret position (this branch covers direct insertion paths such as
244
+ * typing in English mode, where the OS IME never enters composition).
245
+ */
246
+ commit(text: string): void;
247
+ private attachCaretListeners;
248
+ private detachCaretListeners;
249
+ private scheduleCaretRefresh;
250
+ private refreshCaret;
251
+ private clearCaret;
252
+ private ensureOverlay;
253
+ private ensureSelectionOverlay;
254
+ private resolveCaretColor;
255
+ private resolveSelectionColor;
256
+ } //#endregion
5
257
  //#region src/transport/index.d.ts
6
258
  /** The result returned after executing a shell command. */
7
259
  interface ShellResult {
@@ -16,6 +268,77 @@ interface ShellResult {
16
268
  /** Signal number that terminated the process, or `null` if it exited normally. */
17
269
  signal: number | null;
18
270
  }
271
+ /** Mouse cursor position reported by the Fluxlay backend. */
272
+ interface MousePosition {
273
+ /** Horizontal position of the cursor in screen coordinates. */
274
+ x: number;
275
+ /** Vertical position of the cursor in screen coordinates. */
276
+ y: number;
277
+ /** Label of the window that received the mouse event. */
278
+ windowLabel: string;
279
+ }
280
+ /** Mouse button identifier. */
281
+ type MouseButton = "left" | "right" | "middle" | "other";
282
+ /** Mouse button press / release event in window-normalized coordinates ([-1, 1]). */
283
+ interface MouseButtonEvent {
284
+ type: "button";
285
+ /** Which button was pressed/released. */
286
+ button: MouseButton;
287
+ /** True for press, false for release. */
288
+ pressed: boolean;
289
+ /** Horizontal position in normalized window coordinates. */
290
+ x: number;
291
+ /** Vertical position in normalized window coordinates. */
292
+ y: number;
293
+ /** Label of the window the event was normalized against. */
294
+ windowLabel: string;
295
+ }
296
+ /** Mouse wheel / trackpad scroll event in window-normalized coordinates ([-1, 1]). */
297
+ interface MouseWheelEvent {
298
+ type: "wheel";
299
+ /** Horizontal scroll delta (OS-defined units). */
300
+ deltaX: number;
301
+ /** Vertical scroll delta (OS-defined units). */
302
+ deltaY: number;
303
+ /** Cursor X at scroll time, in normalized window coordinates. */
304
+ x: number;
305
+ /** Cursor Y at scroll time, in normalized window coordinates. */
306
+ y: number;
307
+ /** Label of the window the event was normalized against. */
308
+ windowLabel: string;
309
+ }
310
+ /** Aggregate of mouse interaction events streamed from the Fluxlay backend. */
311
+ type MouseInputEvent = MouseButtonEvent | MouseWheelEvent;
312
+ /** Modifier key state at the time of a keyboard event. */
313
+ interface KeyModifiers {
314
+ shift: boolean;
315
+ control: boolean;
316
+ alt: boolean;
317
+ /** macOS Command key / Windows logo key. */
318
+ meta: boolean;
319
+ }
320
+ /**
321
+ * Keyboard key press / release event.
322
+ *
323
+ * `code` follows the Web `KeyboardEvent.code` convention identifying a physical
324
+ * key independent of layout (e.g. `"KeyA"`, `"Space"`, `"Digit1"`,
325
+ * `"ArrowLeft"`, `"ShiftLeft"`). Unmapped keys come through as
326
+ * `"Unidentified"`.
327
+ */
328
+ interface KeyEvent {
329
+ /** Physical key identifier (Web `KeyboardEvent.code` style). */
330
+ code: string;
331
+ /** True for key down, false for key up. */
332
+ pressed: boolean;
333
+ /**
334
+ * True when the OS reports this event as an auto-repeat. On Windows the
335
+ * low-level hook does not surface a repeat flag, so this is derived by
336
+ * tracking the pressed-key set.
337
+ */
338
+ repeat: boolean;
339
+ /** Modifier key state at the time of the event. */
340
+ modifiers: KeyModifiers;
341
+ }
19
342
  /** System audio information streamed from the Fluxlay backend. */
20
343
  interface AudioInfo {
21
344
  /** RMS (Root Mean Square) volume level (0.0 - 1.0). */
@@ -125,6 +448,237 @@ interface MediaMetadataInfo {
125
448
  type PropertyValue = number | string | boolean | string[] | null;
126
449
  /** Custom property values set by the end user via the Fluxlay settings UI. */
127
450
  type PropertyValues = Record<string, PropertyValue>;
451
+ /** Wallpaper manifest information visible to the SDK. */
452
+ interface ManifestInfo {
453
+ /** Permission strings declared in `fluxlay.yaml` (kebab-case). */
454
+ permissions: string[];
455
+ }
456
+ /** IME composition update (text being composed but not yet committed). */
457
+ interface ImeCompositionPayload {
458
+ /** Current composition text. Empty string means no active composition. */
459
+ text: string;
460
+ /** Caret position within `text`, in code points. */
461
+ cursor: number;
462
+ /** Selection range within `text`, in code points, or null when no selection. */
463
+ selection: [number, number] | null;
464
+ }
465
+ /**
466
+ * Event emitted by the `ime-commit-stream`. Multiplexes two distinct event
467
+ * kinds into one stream so we stay below WKWebView's 6-connections-per-host
468
+ * cap:
469
+ *
470
+ * - `commit`: text the IME finalized (Enter etc.)
471
+ * - `command`: a non-character key (Backspace, arrow keys, Enter, Tab, etc.)
472
+ * that was pressed while the macOS IME proxy panel was the key window. The
473
+ * proxy can't apply these to the wallpaper's `<input>` directly, so it
474
+ * forwards the Cocoa selector name (e.g. `deleteBackward:`, `moveLeft:`)
475
+ * here for the SDK to apply.
476
+ */
477
+ type ImeCommitPayload = {
478
+ type: "commit";
479
+ text: string;
480
+ } | {
481
+ type: "command";
482
+ kind: string;
483
+ };
484
+ /**
485
+ * Map of backend `invoke` method names to the deserialized response type.
486
+ *
487
+ * Adding a new backend endpoint? Append it here so `Transport.invoke` infers
488
+ * the return type from the method name.
489
+ */
490
+ interface InvokeMethodMap {
491
+ "run-script": ShellResult;
492
+ properties: PropertyValues;
493
+ manifest: ManifestInfo;
494
+ "ime-activate": Record<string, never>;
495
+ "ime-deactivate": Record<string, never>;
496
+ "ime-update-caret": Record<string, never>;
497
+ }
498
+ /**
499
+ * Map of backend stream names to the per-payload type emitted by
500
+ * `Transport.subscribe`.
501
+ *
502
+ * Adding a new stream? Append it here so the callback's payload is inferred
503
+ * from the event name.
504
+ */
505
+ interface SubscribeEventMap {
506
+ "audio-stream": AudioInfo;
507
+ "ime-commit-stream": ImeCommitPayload;
508
+ "ime-composition-stream": ImeCompositionPayload;
509
+ "keyboard-stream": KeyEvent;
510
+ "media-metadata-stream": MediaMetadataInfo;
511
+ "mouse-event-stream": MouseInputEvent;
512
+ "mouse-stream": MousePosition;
513
+ "properties-stream": PropertyValues;
514
+ "system-monitor-stream": SystemMonitorInfo;
515
+ }
516
+ /** Abstract interface for communicating with the Fluxlay backend. */
517
+ interface Transport {
518
+ /**
519
+ * Sends a request to the backend and returns the deserialized response.
520
+ * The return type is inferred from the method name via {@link InvokeMethodMap}.
521
+ *
522
+ * @param method The backend endpoint name (must be a key of `InvokeMethodMap`).
523
+ * @param params Request payload serialized as JSON.
524
+ */
525
+ invoke<M extends keyof InvokeMethodMap>(method: M, params: Record<string, any>): Promise<InvokeMethodMap[M]>;
526
+ /**
527
+ * Opens a streaming subscription to a backend event and invokes `callback`
528
+ * for each received payload. The payload type is inferred from the event
529
+ * name via {@link SubscribeEventMap}.
530
+ *
531
+ * @param event The backend event / endpoint name (must be a key of `SubscribeEventMap`).
532
+ * @param callback Function called with each deserialized payload.
533
+ * @param params Optional query parameters appended to the request URL.
534
+ * @returns A cleanup function that cancels the subscription when called.
535
+ */
536
+ subscribe<E extends keyof SubscribeEventMap>(event: E, callback: (payload: SubscribeEventMap[E]) => void, params?: Record<string, any>): () => void;
537
+ } //#endregion
538
+ //#region src/shell.d.ts
539
+ /** Options that control how a shell command is executed. */
540
+ interface ShellOptions {
541
+ /** Number of columns (character width) of the pseudo-terminal. */
542
+ columns?: number;
543
+ /** Number of rows (line height) of the pseudo-terminal. */
544
+ lines?: number;
545
+ }
546
+ /**
547
+ * Runs a pre-declared shell command from fluxlay.yaml.
548
+ *
549
+ * @param commandId The ID of the command declared in the manifest's `shell` section.
550
+ * @param options Options for command execution.
551
+ * @returns The standard output and error of the command.
552
+ * @throws An error if the command fails or the ID is invalid.
553
+ */
554
+ declare function runShell(commandId: string, options?: ShellOptions): Promise<ShellResult>; //#endregion
555
+ //#region src/transport/http.d.ts
556
+ /**
557
+ * HTTP-based implementation of the {@link Transport} interface.
558
+ *
559
+ * Communicates with the Fluxlay backend over `http://127.0.0.1:<port>/v1`.
560
+ * The port is read from the `fluxlay_port` query parameter of the current URL,
561
+ * defaulting to `1421` when the parameter is absent.
562
+ */
563
+ declare class HttpTransport implements Transport {
564
+ /** Resolves the base URL of the Fluxlay backend API from the current page URL. */
565
+ private get baseUrl();
566
+ /**
567
+ * In-flight invoke promises keyed by a cache key composed of the method name,
568
+ * window label, and serialized parameters.
569
+ * Prevents duplicate requests when the same call is made concurrently.
570
+ */
571
+ private pendingInvokes;
572
+ /**
573
+ * Active SSE/NDJSON subscriptions keyed by event + params, with reference
574
+ * counting. Multiple `subscribe()` calls for the same event share a single
575
+ * physical fetch stream; the underlying request is aborted only after every
576
+ * caller has unsubscribed.
577
+ *
578
+ * This keeps wallpapers below WKWebView's 6-connections-per-host limit,
579
+ * which would otherwise queue subsequent `invoke()` POSTs forever once 6
580
+ * long-lived streams are open.
581
+ */
582
+ private subscriptions;
583
+ /**
584
+ * Sends a POST request to the backend endpoint identified by `method` and
585
+ * returns the deserialized JSON response.
586
+ *
587
+ * If an identical request (same method, window label, and parameters) is
588
+ * already in flight, the existing promise is returned instead of sending a
589
+ * duplicate request.
590
+ *
591
+ * @param method The backend endpoint name.
592
+ * @param params Request payload serialized as JSON.
593
+ * @returns A promise that resolves to the response value of type `T`.
594
+ * @throws An error if the HTTP response status is not OK.
595
+ */
596
+ invoke<M extends keyof InvokeMethodMap>(method: M, params: Record<string, any>): Promise<InvokeMethodMap[M]>;
597
+ /**
598
+ * Opens a streaming GET request to the backend event endpoint and invokes
599
+ * `callback` for each newline-delimited JSON (NDJSON) payload received.
600
+ *
601
+ * The stream is cancelled when the returned cleanup function is called.
602
+ *
603
+ * @param event The backend event / endpoint name.
604
+ * @param callback Function called with each deserialized NDJSON payload.
605
+ * @param params Optional query parameters appended to the request URL.
606
+ * @returns A cleanup function that aborts the stream when called.
607
+ */
608
+ subscribe<E extends keyof SubscribeEventMap>(event: E, callback: (payload: SubscribeEventMap[E]) => void, params?: Record<string, any>): () => void;
609
+ } //#endregion
610
+ //#region src/transport/default.d.ts
611
+ /**
612
+ * Process-wide singleton {@link HttpTransport}. All SDK code paths that need
613
+ * to talk to the fluxlay backend should reuse this instance instead of
614
+ * instantiating their own — `HttpTransport.subscribe` ref-counts physical
615
+ * fetch streams per URL, so sharing the transport keeps the wallpaper below
616
+ * WKWebView's 6-connections-per-host limit even when multiple hooks /
617
+ * Providers / auto-handlers subscribe to overlapping events.
618
+ */
619
+ declare const defaultTransport: HttpTransport; //#endregion
620
+ //#region src/themes/index.d.ts
621
+ /**
622
+ * Collection of pre-built xterm color themes.
623
+ *
624
+ * Pass any of these values to the `theme` option of {@link useTerminal}.
625
+ *
626
+ * @example
627
+ * ```tsx
628
+ * const { terminalRef, instance } = useTerminal({
629
+ * theme: TerminalThemes.dracula,
630
+ * });
631
+ * ```
632
+ */
633
+ declare const TerminalThemes: {
634
+ /** Dark themes with a transparent background. */readonly dark: {
635
+ /** Default dark theme using Tailwind color palette. */readonly default: _$_xterm_xterm0.ITheme; /** Modern dark theme using Slate/Rose color palette. */
636
+ readonly modern: _$_xterm_xterm0.ITheme;
637
+ }; /** Light themes. */
638
+ readonly light: {
639
+ /** Default light theme. */readonly default: _$_xterm_xterm0.ITheme; /** Modern light theme. */
640
+ readonly modern: _$_xterm_xterm0.ITheme;
641
+ }; /** Nord theme — an arctic, north-bluish color palette. */
642
+ readonly nord: _$_xterm_xterm0.ITheme; /** Dracula theme — a dark theme with vivid accent colors. */
643
+ readonly dracula: _$_xterm_xterm0.ITheme; /** Gruvbox themes — a retro groove color scheme. */
644
+ readonly gruvbox: {
645
+ /** Gruvbox dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Gruvbox light variant. */
646
+ readonly light: _$_xterm_xterm0.ITheme;
647
+ }; /** Solarized themes — a precision color scheme with reduced brightness. */
648
+ readonly solarized: {
649
+ /** Solarized dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Solarized light variant. */
650
+ readonly light: _$_xterm_xterm0.ITheme;
651
+ }; /** One Dark theme — based on Atom's One Dark syntax theme. */
652
+ readonly oneDark: _$_xterm_xterm0.ITheme; /** Catppuccin themes — a soothing pastel color scheme. */
653
+ readonly catppuccin: {
654
+ /** Catppuccin Latte — the light variant. */readonly latte: _$_xterm_xterm0.ITheme; /** Catppuccin Frappé — a cool medium-dark variant. */
655
+ readonly frappe: _$_xterm_xterm0.ITheme; /** Catppuccin Macchiato — a warm medium-dark variant. */
656
+ readonly macchiato: _$_xterm_xterm0.ITheme; /** Catppuccin Mocha — the darkest variant. */
657
+ readonly mocha: _$_xterm_xterm0.ITheme;
658
+ }; /** Monokai Pro theme — a refined dark theme inspired by Monokai. */
659
+ readonly monokaiPro: _$_xterm_xterm0.ITheme; /** Tokyo Night theme — a dark theme inspired by Tokyo city lights. */
660
+ readonly tokyoNight: _$_xterm_xterm0.ITheme; /** Night Owl theme — optimized for late-night coding with low brightness. */
661
+ readonly nightOwl: _$_xterm_xterm0.ITheme; /** Everforest themes — a green-tinted theme inspired by forests. */
662
+ readonly everforest: {
663
+ /** Everforest dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Everforest light variant. */
664
+ readonly light: _$_xterm_xterm0.ITheme;
665
+ };
666
+ }; //#endregion
667
+ //#endregion
668
+ //#region src/hooks/use-active-element.d.ts
669
+ /**
670
+ * Reactively track `document.activeElement`.
671
+ *
672
+ * Returns the currently focused element, or `null` when nothing is focused
673
+ * (i.e. focus is on `<body>` or the document itself). Subscribes to
674
+ * `focusin` / `focusout` so the value updates whenever focus moves anywhere
675
+ * in the document.
676
+ *
677
+ * Useful on wallpapers for debugging / visualizing which element synthetic
678
+ * keyboard input is being routed to, since WebKit doesn't paint a focus ring
679
+ * on non-key windows.
680
+ */
681
+ declare function useActiveElement(): Element | null;
128
682
  //#endregion
129
683
  //#region src/hooks/use-audio.d.ts
130
684
  /** Options for the audio stream. */
@@ -144,16 +698,100 @@ interface AudioOptions {
144
698
  */
145
699
  declare function useAudio(options?: AudioOptions): AudioInfo;
146
700
  //#endregion
147
- //#region src/lib/file-url.d.ts
701
+ //#region src/hooks/use-ime-input.d.ts
148
702
  /**
149
- * Build a URL the wallpaper webview can use to load a local file referenced
150
- * by an `image` or `file` property value.
703
+ * Callback fired when the IME commits text (the user finalized their input,
704
+ * e.g. by pressing Enter to confirm a Japanese conversion).
705
+ */
706
+ type ImeCommitHandler = (text: string) => void;
707
+ /**
708
+ * Return value of {@link useImeInput}.
151
709
  *
152
- * The file is served by the Fluxlay desktop app via `GET /v1/file` and is
153
- * restricted to paths currently assigned to an `image` / `file` property of
154
- * the active wallpaper. Returns `null` when the path is missing.
710
+ * The wallpaper must call `activate()` to start receiving IME input typically
711
+ * in response to the user clicking an in-wallpaper "input field". Until
712
+ * activated, the proxy window stays in the background and does not steal focus
713
+ * from other applications.
714
+ */
715
+ interface ImeInputApi {
716
+ /** Current composition text, or null when no composition is active. */
717
+ composition: string | null;
718
+ /** Caret position within `composition`, in code points. */
719
+ cursor: number;
720
+ /**
721
+ * Request the IME proxy window to take focus and start delivering input.
722
+ *
723
+ * Pass an `HTMLElement` (typically your `<input>` ref) or an explicit
724
+ * `{ x, y, height }` rect in screen-relative CSS pixels to position the
725
+ * IME candidate window near the caret. Without an argument the candidate
726
+ * appears in the middle of the active screen.
727
+ */
728
+ activate: (anchor?: HTMLElement | {
729
+ x: number;
730
+ y: number;
731
+ height: number;
732
+ }) => void;
733
+ /** Release the IME proxy window's focus and stop delivering input. */
734
+ deactivate: () => void;
735
+ /**
736
+ * Register a handler that fires every time the IME commits text.
737
+ * Returns a cleanup function that removes the handler.
738
+ */
739
+ onCommit: (handler: ImeCommitHandler) => () => void;
740
+ }
741
+ /**
742
+ * React hook that exposes IME (Input Method Editor) text input for wallpapers
743
+ * that need to accept Japanese / Chinese / Korean / etc. text.
744
+ *
745
+ * Wallpapers MUST declare `permissions: [ime-input]` in their `fluxlay.yaml`.
746
+ * The backend rejects every IME request from wallpapers without the permission
747
+ * with HTTP 403, in which case this hook stays a quiet no-op (no streams flow,
748
+ * activate / deactivate are accepted by the SDK but produce nothing).
749
+ *
750
+ * The hook is independent of `useKeyboard`: while the IME proxy is active
751
+ * (between `activate()` and `deactivate()`), `useKeyboard` events are
752
+ * suppressed by the backend so IME candidate operations (arrows, Enter, Esc)
753
+ * do not double-fire as raw key events.
155
754
  */
156
- declare function getPropertyFileUrl(path: string | null | undefined): string | null;
755
+ declare function useImeInput(): ImeInputApi;
756
+ //#endregion
757
+ //#region src/hooks/use-is-focused.d.ts
758
+ /**
759
+ * Track whether the element pointed to by `ref` currently has DOM focus.
760
+ *
761
+ * Why this exists: wallpaper windows are not the OS key window, so WebKit
762
+ * suppresses the visual `:focus` / `:focus-visible` styling that authors
763
+ * normally rely on. To render a focus ring on a wallpaper you have to mirror
764
+ * focus state into React and toggle a class manually. This hook encapsulates
765
+ * that pattern — subscribe to `focus`/`blur` and expose a boolean.
766
+ *
767
+ * The hook also seeds the initial value from `document.activeElement` so the
768
+ * state is correct even if focus was already on the element at mount time.
769
+ */
770
+ declare function useIsFocused(ref: RefObject<HTMLElement | null>): boolean;
771
+ //#endregion
772
+ //#region src/hooks/use-keyboard.d.ts
773
+ /**
774
+ * Callbacks for global keyboard events streamed from the Fluxlay backend.
775
+ *
776
+ * Keyboard events are not tied to a specific wallpaper window — every
777
+ * subscriber on every window receives every key press/release while the user
778
+ * is anywhere on the desktop.
779
+ */
780
+ interface KeyboardHandlers {
781
+ onKeyDown?: (event: KeyEvent) => void;
782
+ onKeyUp?: (event: KeyEvent) => void;
783
+ }
784
+ /**
785
+ * React hook that subscribes to global keyboard events from the Fluxlay
786
+ * backend. Convenient for wallpapers that implement game-like interactions.
787
+ *
788
+ * Platform notes:
789
+ * - macOS: requires the user to grant Fluxlay "Input Monitoring" permission.
790
+ * - Windows: the global hook may be flagged by antivirus software; key events
791
+ * that target a process running at a higher integrity level (e.g. an app
792
+ * launched as Administrator) are not delivered, due to UIPI.
793
+ */
794
+ declare function useKeyboard(handlers: KeyboardHandlers): void;
157
795
  //#endregion
158
796
  //#region src/hooks/use-media-metadata.d.ts
159
797
  /** Options for the media metadata stream. */
@@ -194,6 +832,34 @@ declare function useMousePosition(): {
194
832
  y: number;
195
833
  };
196
834
  //#endregion
835
+ //#region src/hooks/use-mouse-events.d.ts
836
+ /**
837
+ * Callbacks invoked when the Fluxlay backend reports mouse interaction events
838
+ * (clicks, button releases, wheel/trackpad scrolls) inside the current
839
+ * wallpaper window. Coordinates are normalized to `[-1, 1]` with the Y axis
840
+ * inverted to match a standard math coordinate system.
841
+ *
842
+ * Pass only the callbacks you need; missing ones are simply ignored.
843
+ */
844
+ interface MouseEventHandlers {
845
+ onButton?: (event: MouseButtonEvent) => void;
846
+ onWheel?: (event: MouseWheelEvent) => void;
847
+ }
848
+ /**
849
+ * React hook that subscribes to mouse click and wheel events streamed from the
850
+ * Fluxlay backend. The Y axis is inverted so positive Y points upward, just
851
+ * like {@link useMousePosition}.
852
+ *
853
+ * The subscription is scoped to the window identified by the `window_label`
854
+ * query parameter of the current URL, defaulting to `"main"`.
855
+ *
856
+ * On macOS, mouse click events require the user to grant the Fluxlay app
857
+ * "Input Monitoring" permission in System Settings. On Windows the global
858
+ * mouse hook may be flagged by some antivirus software; events from windows
859
+ * running with elevated privileges are also not delivered (UIPI).
860
+ */
861
+ declare function useMouseEvents(handlers: MouseEventHandlers): void;
862
+ //#endregion
197
863
  //#region src/hooks/use-properties.d.ts
198
864
  /**
199
865
  * React hook that provides the current custom property values defined
@@ -207,24 +873,6 @@ declare function useMousePosition(): {
207
873
  */
208
874
  declare function useProperties<T extends PropertyValues = PropertyValues>(): T;
209
875
  //#endregion
210
- //#region src/shell.d.ts
211
- /** Options that control how a shell command is executed. */
212
- interface ShellOptions {
213
- /** Number of columns (character width) of the pseudo-terminal. */
214
- columns?: number;
215
- /** Number of rows (line height) of the pseudo-terminal. */
216
- lines?: number;
217
- }
218
- /**
219
- * Runs a pre-declared shell command from fluxlay.yaml.
220
- *
221
- * @param commandId The ID of the command declared in the manifest's `shell` section.
222
- * @param options Options for command execution.
223
- * @returns The standard output and error of the command.
224
- * @throws An error if the command fails or the ID is invalid.
225
- */
226
- declare function runShell(commandId: string, options?: ShellOptions): Promise<ShellResult>;
227
- //#endregion
228
876
  //#region src/hooks/use-terminal.d.ts
229
877
  /** Options for the {@link useTerminal} hook. */
230
878
  interface UseTerminalOptions {
@@ -354,52 +1002,4 @@ interface SystemMonitorOptions {
354
1002
  */
355
1003
  declare function useSystemMonitor(options?: SystemMonitorOptions): SystemMonitorInfo;
356
1004
  //#endregion
357
- //#region src/themes/index.d.ts
358
- /**
359
- * Collection of pre-built xterm color themes.
360
- *
361
- * Pass any of these values to the `theme` option of {@link useTerminal}.
362
- *
363
- * @example
364
- * ```tsx
365
- * const { terminalRef, instance } = useTerminal({
366
- * theme: TerminalThemes.dracula,
367
- * });
368
- * ```
369
- */
370
- declare const TerminalThemes: {
371
- /** Dark themes with a transparent background. */readonly dark: {
372
- /** Default dark theme using Tailwind color palette. */readonly default: _$_xterm_xterm0.ITheme; /** Modern dark theme using Slate/Rose color palette. */
373
- readonly modern: _$_xterm_xterm0.ITheme;
374
- }; /** Light themes. */
375
- readonly light: {
376
- /** Default light theme. */readonly default: _$_xterm_xterm0.ITheme; /** Modern light theme. */
377
- readonly modern: _$_xterm_xterm0.ITheme;
378
- }; /** Nord theme — an arctic, north-bluish color palette. */
379
- readonly nord: _$_xterm_xterm0.ITheme; /** Dracula theme — a dark theme with vivid accent colors. */
380
- readonly dracula: _$_xterm_xterm0.ITheme; /** Gruvbox themes — a retro groove color scheme. */
381
- readonly gruvbox: {
382
- /** Gruvbox dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Gruvbox light variant. */
383
- readonly light: _$_xterm_xterm0.ITheme;
384
- }; /** Solarized themes — a precision color scheme with reduced brightness. */
385
- readonly solarized: {
386
- /** Solarized dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Solarized light variant. */
387
- readonly light: _$_xterm_xterm0.ITheme;
388
- }; /** One Dark theme — based on Atom's One Dark syntax theme. */
389
- readonly oneDark: _$_xterm_xterm0.ITheme; /** Catppuccin themes — a soothing pastel color scheme. */
390
- readonly catppuccin: {
391
- /** Catppuccin Latte — the light variant. */readonly latte: _$_xterm_xterm0.ITheme; /** Catppuccin Frappé — a cool medium-dark variant. */
392
- readonly frappe: _$_xterm_xterm0.ITheme; /** Catppuccin Macchiato — a warm medium-dark variant. */
393
- readonly macchiato: _$_xterm_xterm0.ITheme; /** Catppuccin Mocha — the darkest variant. */
394
- readonly mocha: _$_xterm_xterm0.ITheme;
395
- }; /** Monokai Pro theme — a refined dark theme inspired by Monokai. */
396
- readonly monokaiPro: _$_xterm_xterm0.ITheme; /** Tokyo Night theme — a dark theme inspired by Tokyo city lights. */
397
- readonly tokyoNight: _$_xterm_xterm0.ITheme; /** Night Owl theme — optimized for late-night coding with low brightness. */
398
- readonly nightOwl: _$_xterm_xterm0.ITheme; /** Everforest themes — a green-tinted theme inspired by forests. */
399
- readonly everforest: {
400
- /** Everforest dark variant. */readonly dark: _$_xterm_xterm0.ITheme; /** Everforest light variant. */
401
- readonly light: _$_xterm_xterm0.ITheme;
402
- };
403
- };
404
- //#endregion
405
- export { AudioOptions, MediaMetadataOptions, ShellOptions, type ShellResult, SystemMonitorOptions, TerminalInstance, TerminalThemes, UseShellOptions, UseTerminalOptions, getPropertyFileUrl, runShell, useAudio, useMediaMetadata, useMousePosition, useProperties, useShell, useSystemMonitor, useTerminal };
1005
+ export { AudioInfo, AudioOptions, CaretRect, DiskInfo, HttpTransport, ImeCommitHandler, type ImeCommitPayload, type ImeCompositionPayload, ImeInlineController, ImeInlineControllerOptions, ImeInputApi, InvokeMethodMap, type KeyEvent, type KeyModifiers, KeyboardHandlers, ManifestInfo, MediaMetadataInfo, MediaMetadataOptions, type MouseButton, type MouseButtonEvent, MouseEventHandlers, type MouseInputEvent, MousePosition, type MouseWheelEvent, PropertyValue, PropertyValues, SelectionRect, ShellOptions, type ShellResult, SubscribeEventMap, SystemMonitorInfo, SystemMonitorOptions, TerminalInstance, TerminalThemes, Transport, UseShellOptions, UseTerminalOptions, caretIndexFromClientPoint, defaultTransport, getPropertyFileUrl, isImeAutoTarget, measureCaretRect, measureSelectionRects, normalizedToPixel, pixelToNormalized, runShell, useActiveElement, useAudio, useImeInput, useIsFocused, useKeyboard, useMediaMetadata, useMouseEvents, useMousePosition, useProperties, useShell, useSystemMonitor, useTerminal };