@nativedesktop/react 0.1.2 → 0.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.
@@ -1,5 +1,5 @@
1
1
  export declare const NDP_VERSION = 1;
2
- export type WidgetName = "Window" | "Box" | "Label" | "Button" | "TextInput" | "TextArea" | "Checkbox" | "Radio" | "Select" | "Slider" | "ProgressBar" | "Image" | "ScrollView" | "Separator" | "Spinner" | "TabView" | "Grid" | "ListView" | "WebView" | "NativeView" | "SplitView" | "HeaderBar" | "ToolbarView" | "SearchInput" | "SourceList" | "SourceTree" | "Menubar" | "Menu" | "MenuItem" | "SettingsGroup" | "Row" | "SwitchRow" | "Clamp" | "Switch" | "ToggleButton" | "SegmentedControl" | "NumberInput" | "LinkButton" | "LevelIndicator" | "ColorPicker" | "Banner" | "MenuButton" | "SplitButton" | "Popover" | "Expander" | "StatusPage" | "ToastOverlay" | "DatePicker" | "Table" | "TreeView" | "FontPicker" | "Video" | "TrayItem" | "ShareButton" | "Terminal" | "Paned" | "CommandPalette";
2
+ export type WidgetName = "Window" | "Box" | "Label" | "Button" | "TextInput" | "TextArea" | "Checkbox" | "Radio" | "Select" | "Slider" | "ProgressBar" | "Image" | "ScrollView" | "Separator" | "Spinner" | "TabView" | "Grid" | "ListView" | "WebView" | "NativeView" | "SplitView" | "HeaderBar" | "ToolbarView" | "SearchInput" | "SourceList" | "SourceTree" | "Menubar" | "Menu" | "MenuItem" | "SettingsGroup" | "Row" | "SwitchRow" | "Clamp" | "Switch" | "ToggleButton" | "SegmentedControl" | "NumberInput" | "LinkButton" | "LevelIndicator" | "ColorPicker" | "Banner" | "MenuButton" | "SplitButton" | "Popover" | "Expander" | "StatusPage" | "ToastOverlay" | "DatePicker" | "Table" | "TreeView" | "FontPicker" | "Video" | "TrayItem" | "ShareButton" | "Terminal" | "Paned" | "CommandPalette" | "Avatar" | "Badge" | "Tag" | "Kbd" | "ComboBox" | "Breadcrumb" | "Dialog" | "Sheet" | "RichText" | "ProgressCircle" | "Skeleton" | "Chart" | "CodeEditor";
3
3
  /** Child runtime identity, carried inside the hello frame. */
4
4
  export interface Runtime {
5
5
  name: string;
@@ -5,12 +5,14 @@ export interface Geometry {
5
5
  w: number;
6
6
  h: number;
7
7
  }
8
- /** SourceList's per-row shape on the wire (M11 SourceList Wave 1) — camelCase iconName, matching the schema prop shape. testID carries SourceTree's per-node testID (null for SourceList rows, which have none). */
8
+ /** The per-row shape on the wire, shared by every row-driven widget (SourceList `items`, SourceTree `nodes`, CommandPalette `items`) — camelCase iconName, matching the schema prop shape. testID carries SourceTree's per-node testID (null for SourceList rows, which have none); id and subtitle carry the row's own identity and secondary line where the widget's item type has them (CommandPaletteItem, SourceTreeNode), so a drive can name a row instead of counting to it. */
9
9
  export interface RowJson {
10
10
  title: string;
11
11
  badge: string | null;
12
12
  iconName: string | null;
13
13
  testID: string | null;
14
+ id: string | null;
15
+ subtitle: string | null;
14
16
  }
15
17
  /** One tree-snapshot node. itemCount is ListView's row count (M5c-D4), null for every widget that isn't data-driven; rows is SourceList's ordered row data, null for every widget that isn't row-driven. role/enabled/focused/value are the accessibility-tree fields (M16): role is the widget's schema-declared automation role (null when the type declares none); enabled/focused/value come from a live per-node backend probe and default to true/false/null on backends without the probe. */
16
18
  export interface JsonNode {
@@ -62,7 +64,7 @@ export interface ScrollResult {
62
64
  x: number;
63
65
  y: number;
64
66
  }
65
- /** Exactly one selector: textContains | refVisible | testId. With testId, `state` (default "present", one of present|gone|visible|enabled|disabled|focused) picks the predicate and countAtLeast/valueEquals/valueContains refine it. valueEquals/valueContains compare against the node's a11y value STRING RENDERING (numbers stringified, bools "true"/"false") so one predicate works for TextInput and Slider alike. Every predicate is evaluated host-side on the retained tree plus the live a11y probe, once per ~50ms tick — never a getTree round trip. */
67
+ /** Exactly one selector: textContains | refVisible | testId. With testId, `state` (default "present", one of present|gone|visible|enabled|disabled|focused) picks the predicate and countAtLeast/valueEquals/valueContains refine it. valueEquals/valueContains compare against the node's a11y value STRING RENDERING (numbers stringified, bools "true"/"false") so one predicate works for TextInput and Slider alike. urlContains/pageTitleContains/pageTextContains are PAGE predicates and refine a testId selector the same way; the node they name must be a WebView. url and title come from the engine directly, but pageTextContains INJECTS JAVASCRIPT (document.body.innerText) into the page world, re-probed at most once per 250ms and matched against the last answer — so a match can lag the page by one probe. Every predicate is evaluated host-side on the retained tree plus the live probes, once per ~50ms tick — never a getTree round trip. */
66
68
  export interface WaitCondition {
67
69
  textContains?: string;
68
70
  refVisible?: number;
@@ -71,6 +73,9 @@ export interface WaitCondition {
71
73
  countAtLeast?: number;
72
74
  valueEquals?: string;
73
75
  valueContains?: string;
76
+ urlContains?: string;
77
+ pageTitleContains?: string;
78
+ pageTextContains?: string;
74
79
  }
75
80
  export interface ResolveResult {
76
81
  ref: number | null;
@@ -104,6 +109,22 @@ export interface DragResult {
104
109
  export interface KeysResult {
105
110
  dispatched: boolean;
106
111
  }
112
+ /** Live page state read off the engine on the UI thread (WebKitGTK's uri/title/is-loading/can-go-back/can-go-forward, WKWebView's url/title/isLoading/canGoBack/canGoForward) — no page JavaScript, no app cooperation. url/title are null before the first commit. */
113
+ export interface WebViewInfo {
114
+ ref: number;
115
+ url: string | null;
116
+ title: string | null;
117
+ loading: boolean;
118
+ canGoBack: boolean;
119
+ canGoForward: boolean;
120
+ }
121
+ /** `value` is the result's STRING rendering (the engine's own JSValue-to-string), the same shape the javaScriptResult event carries — never a typed JSON value. On a thrown exception `ok` is false and `error` carries the engine's message. */
122
+ export interface WebViewEvalResult {
123
+ ref: number;
124
+ ok: boolean;
125
+ value: string | null;
126
+ error: string | null;
127
+ }
107
128
  /** Full tree snapshot with stable refs, testIDs, text, logical geometry, and accessibility state (role/enabled/focused/value). window (if given) must be a Window node ref and scopes the snapshot to that window's subtree; absent, the root/first window is used and other windows' nodes attach as orphans. */
108
129
  export interface GetTreeParams {
109
130
  window?: number;
@@ -181,6 +202,21 @@ export interface PointerParams {
181
202
  clickCount?: number;
182
203
  window?: number;
183
204
  }
205
+ /** Live page state for a WebView node: {url, title, loading, canGoBack, canGoForward}. Read straight off the engine, so a drive can assert what a page did without the app forwarding its navigate/titleChanged events. Target by exactly one of ref / testId (window optionally scopes testId resolution); a target that is not a WebView answers -32602. */
206
+ export interface WebviewInfoParams {
207
+ ref?: number;
208
+ testId?: string;
209
+ window?: number;
210
+ }
211
+ /** Evaluates `code` in a WebView node's page and answers the result's string rendering. `world` picks an isolated content world by name (absent/empty = the page's own world), matching the executeJavaScript command. The engine's evaluation is asynchronous: the host starts it, then polls its own UI thread until it settles or timeoutMs elapses (-32002). A thrown exception is a RESULT with ok:false, not an RPC error. Target by exactly one of ref / testId; a target that is not a WebView answers -32602. */
212
+ export interface WebviewEvalParams {
213
+ ref?: number;
214
+ testId?: string;
215
+ window?: number;
216
+ code: string;
217
+ world?: string;
218
+ timeoutMs?: number;
219
+ }
184
220
  /** Press-move-release gesture. Endpoints are widget refs (their centers; fromRef/toRef must share a window) or explicit coordinates (fromX/fromY/toX/toY in the target window, default root). The full down/dragged.../up sequence is posted as one batch so native mouse-tracking loops (slider thumbs, split dividers) consume it like a real drag. Unsupported on GTK (-32003). */
185
221
  export interface DragParams {
186
222
  fromRef?: number;
@@ -252,6 +288,14 @@ export interface RpcMethods {
252
288
  params: PointerParams;
253
289
  result: PointerResult;
254
290
  };
291
+ webviewInfo: {
292
+ params: WebviewInfoParams;
293
+ result: WebViewInfo;
294
+ };
295
+ webviewEval: {
296
+ params: WebviewEvalParams;
297
+ result: WebViewEvalResult;
298
+ };
255
299
  drag: {
256
300
  params: DragParams;
257
301
  result: DragResult;
@@ -280,6 +324,11 @@ export declare const RPC_ERRORS: {
280
324
  readonly code: -32003;
281
325
  readonly message: "input synthesis unsupported on this backend";
282
326
  };
327
+ /** data: {window}. getTree/screenshot only: params.window named a real Window ref (e.g. from windows()) that has since closed, distinguishing a benign concurrent-close race from invalidParams (a malformed request). Every other window-scoped method resolves a closed window into its own no-match result instead of this error. */
328
+ readonly windowGone: {
329
+ readonly code: -32004;
330
+ readonly message: "window closed";
331
+ };
283
332
  readonly methodNotFound: {
284
333
  readonly code: -32601;
285
334
  readonly message: "method not found";
@@ -8,7 +8,7 @@ export declare const intrinsicToName: Record<string, string>;
8
8
  export interface WidgetEvent {
9
9
  name: string;
10
10
  handler: string;
11
- payload: "none" | "text" | "checked" | "value" | "index" | "data";
11
+ payload: "none" | "text" | "checked" | "value" | "index" | "indexes" | "data" | "position" | "dragPoint";
12
12
  }
13
13
  export declare const widgetEvents: Record<string, WidgetEvent[]>;
14
14
  export declare const handlerPropNames: Record<string, string[]>;
@@ -18,9 +18,20 @@ export declare const widgetCommands: Record<string, readonly string[]>;
18
18
  /** Compile-time command-name map (only widgets with commands appear). */
19
19
  export type WidgetCommandNames = {
20
20
  "window": "showAlert" | "openFile" | "saveFile" | "showAbout" | "showTabOverview" | "present";
21
- "webview": "goBack" | "goForward" | "reload" | "stop" | "executeJavaScript" | "setZoom" | "setUserAgent" | "openDevTools";
21
+ "button": "focus";
22
+ "textinput": "focus";
23
+ "textarea": "focus";
24
+ "checkbox": "focus";
25
+ "radio": "focus";
26
+ "select": "focus";
27
+ "webview": "goBack" | "goForward" | "reload" | "stop" | "executeJavaScript" | "setZoom" | "setUserAgent" | "openDevTools" | "addUserScript" | "removeUserScript" | "clearUserScripts" | "registerScriptMessage" | "unregisterScriptMessage" | "respondScheme" | "getCookies" | "setCookie" | "deleteCookie" | "findStart" | "findNext" | "findPrevious" | "findStop" | "saveSession" | "restoreSession" | "setMuted" | "setContextMenuItems" | "focus";
28
+ "searchinput": "focus";
29
+ "switch": "focus";
30
+ "numberinput": "focus";
22
31
  "toastoverlay": "showToast" | "dismissToast";
23
32
  "terminal": "copy" | "paste" | "selectAll" | "clearSelection" | "focus";
33
+ "combobox": "focus";
34
+ "codeeditor": "focus";
24
35
  };
25
36
  /** Platform availability (schema `platforms`): null = every platform. A
26
37
  * widget listing only some platforms renders natively there and is an
@@ -2,21 +2,22 @@ export { render, sendCommand, sendNativeCommand, createPortal, createPool, moveN
2
2
  export type { Pool } from "./renderer.ts";
3
3
  export { Platform, hasWidget, hasCommand } from "./platform.ts";
4
4
  export type { Backend, OS } from "./platform.ts";
5
- export { Spacing, ContentMargin } from "./metrics.ts";
5
+ export { Spacing, ContentMargin, ContentWidth } from "./metrics.ts";
6
6
  export type { SpacingScale } from "./metrics.ts";
7
7
  export { getAppDataDir, ensureAppDataDir } from "./paths.ts";
8
8
  export { defineNativeComponent } from "./native-component.ts";
9
9
  export type { NativeComponentOptions, NativeComponentProps, NativeComponentRef } from "./native-component.ts";
10
- export { executeJavaScript, onJavaScriptResult } from "./webview.ts";
10
+ export { executeJavaScript, onJavaScriptResult, getCookies, onCookiesResult, saveSession, onSessionSaved, setContextMenuItems, } from "./webview.ts";
11
+ export type { Cookie, ContextMenuContext, ContextMenuItem, ContextMenuItemClick } from "./webview.ts";
11
12
  export { showAlert, openFile, saveFile, showAbout, showTabOverview, onAlertResult, onOpenFileResult, onSaveFileResult, } from "./dialogs.ts";
12
13
  export type { DialogButton, ShowAlertOptions, AlertResult, OpenFileOptions as WindowOpenFileOptions, OpenFileResult as WindowOpenFileResult, SaveFileOptions as WindowSaveFileOptions, SaveFileResult as WindowSaveFileResult, ShowAboutOptions, } from "./dialogs.ts";
13
14
  export { showToast, dismissToast, onToastButtonClicked, onToastDismissed } from "./toast.ts";
14
15
  export type { ToastPriority, ShowToastOptions, ToastResult } from "./toast.ts";
15
16
  export type { Instance } from "./host-config.ts";
16
- export type { NdNodeRef, WidgetType, TableColumn, TableRow, TreeNode, SourceTreeAction, SourceTreeNode } from "./generated/intrinsics.ts";
17
+ export type { NdNodeRef, WidgetType, TableColumn, TableRow, TreeNode, SourceTreeAction, SourceTreeNode, CommandPaletteItem, ChartPoint, ChartSeries, CodeDiagnostic, } from "./generated/intrinsics.ts";
17
18
  export { performRefresh, registerExports, fullReload } from "./hmr.ts";
18
- export { useState, useEffect, useLayoutEffect, useMemo, useCallback, useRef, useContext, useReducer, useTransition, useDeferredValue, useSyncExternalStore, useId, use, startTransition, memo, forwardRef, createContext, Suspense, Fragment, } from "./dev-react.ts";
19
- export { dialog, clipboard, notifications, recentDocuments, credentials, app, system, audio } from "./system.ts";
19
+ export { useState, useEffect, useLayoutEffect, useMemo, useCallback, useRef, useContext, useReducer, useTransition, useDeferredValue, useSyncExternalStore, useId, use, startTransition, memo, forwardRef, createContext, Suspense, Fragment, Activity, } from "./dev-react.ts";
20
+ export { dialog, clipboard, notifications, recentDocuments, credentials, app, system, audio, webviewEngine } from "./system.ts";
20
21
  export type { FileFilter, OpenFileOptions, SaveFileOptions, MessageLevel, MessageOptions, NotificationOptions, Appearance, AppearanceInfo, AudioPlayOptions, AudioState, AudioStateEvent, AudioSpectrumEvent, } from "./system.ts";
21
22
  export { openExternal, openPath, revealPath } from "./shell.ts";
22
23
  export { onUnhandledError, setUnhandledErrorPolicy } from "./errors.ts";
@@ -10,3 +10,19 @@ export declare const Spacing: SpacingScale;
10
10
  * binding (a number export cannot be a getter): read it at render time, not
11
11
  * into a module-scope constant. */
12
12
  export declare let ContentMargin: number;
13
+ /** The platform's reading measure for settings-shaped content — the width to
14
+ * cap a content column at, as `<clamp maximumSize>`.
15
+ *
16
+ * A column, not a widget: the cap belongs to whatever holds the column so
17
+ * every child shares it. This matters on AppKit, where `<settingsgroup>` is a
18
+ * SwiftUI grouped `Form` and that style caps and centers its own card inside
19
+ * whatever width it is handed. A `<codeeditor>` or `<table>` beside it has no
20
+ * such cap, so the two disagree by however wide the pane happens to be, and
21
+ * neither `maxWidth: .infinity` nor a wider frame turns the Form's cap off.
22
+ * Clamping the column below the cap settles it: the Form stops capping, its
23
+ * card fills the column, and its siblings line up with it. GTK has the same
24
+ * shape natively — AdwPreferencesGroup fills its AdwClamp — so one tree reads
25
+ * correctly on both.
26
+ *
27
+ * A live binding for the same reason `ContentMargin` is. */
28
+ export declare let ContentWidth: number;
@@ -126,6 +126,42 @@ export declare const system: {
126
126
  /** Subscribes to system appearance/accent changes. Returns an unsubscribe function. */
127
127
  onAppearanceChange(handler: (info: AppearanceInfo) => void): () => void;
128
128
  };
129
+ export interface RegisterSchemeOptions {
130
+ /**
131
+ * Lets pages read the scheme cross-origin. GTK marks the scheme CORS-enabled
132
+ * on the WebKitSecurityManager; without it a `fetch()` from another origin is
133
+ * refused before any response header is consulted. **AppKit ignores this**
134
+ * (see the asymmetry note on `registerScheme`).
135
+ */
136
+ corsEnabled?: boolean;
137
+ /**
138
+ * Makes the scheme's origins secure contexts, which is what `crypto.subtle`,
139
+ * IndexedDB and service workers require. **GTK only** — WebKit's Cocoa API
140
+ * has no public equivalent.
141
+ */
142
+ secure?: boolean;
143
+ }
144
+ /** Engine-level `<webview>` configuration. `core:webview` is default-granted. */
145
+ export declare const webviewEngine: {
146
+ /**
147
+ * Registers a custom URI scheme (`crx`, `app`, …) with the web engine.
148
+ * Requests for it arrive as the `schemeRequest` event on the `<webview>`
149
+ * that made them, and the app answers with the `respondScheme` command.
150
+ *
151
+ * MUST be called before the first `<webview>` mounts: WebKit binds scheme
152
+ * handlers to a configuration/context that is frozen once a view exists.
153
+ * Calling it later rejects with "registerScheme must be called before the
154
+ * first <webview> mounts".
155
+ *
156
+ * Backend asymmetry: `corsEnabled` and `secure` take effect on GTK only.
157
+ * WebKitGTK exposes a WebKitSecurityManager for both; WebKit's Cocoa API
158
+ * keeps that registry as SPI, so on AppKit the options are accepted and
159
+ * ignored. Cross-origin reads still work there through `respondScheme`'s
160
+ * `Access-Control-Allow-Origin` header — a secure context cannot be granted
161
+ * at all.
162
+ */
163
+ registerScheme(scheme: string, options?: RegisterSchemeOptions): Promise<void>;
164
+ };
129
165
  export interface AudioPlayOptions {
130
166
  path?: string;
131
167
  url?: string;
@@ -1,5 +1,46 @@
1
1
  import type { NdNodeRef } from "./generated/intrinsics.ts";
2
- export declare function executeJavaScript(node: NdNodeRef<"webview">, code: string): Promise<string>;
2
+ export declare function executeJavaScript(node: NdNodeRef<"webview">, code: string, world?: string): Promise<string>;
3
3
  export declare function onJavaScriptResult(e: {
4
4
  data: unknown;
5
5
  }): void;
6
+ export interface Cookie {
7
+ name: string;
8
+ value: string;
9
+ domain: string;
10
+ path: string;
11
+ secure: boolean;
12
+ httpOnly: boolean;
13
+ /** Unix seconds, or null for a session cookie. */
14
+ expires: number | null;
15
+ sameSite: "None" | "Lax" | "Strict";
16
+ }
17
+ export declare function getCookies(node: NdNodeRef<"webview">, url?: string): Promise<Cookie[]>;
18
+ export declare function onCookiesResult(e: {
19
+ data: unknown;
20
+ }): void;
21
+ export declare function saveSession(node: NdNodeRef<"webview">): Promise<string>;
22
+ export declare function onSessionSaved(e: {
23
+ data: unknown;
24
+ }): void;
25
+ export type ContextMenuContext = "all" | "page" | "link" | "image" | "selection" | "editable";
26
+ export interface ContextMenuItem {
27
+ id?: string;
28
+ label?: string;
29
+ type?: "normal" | "checkbox" | "radio" | "separator";
30
+ checked?: boolean;
31
+ enabled?: boolean;
32
+ contexts?: ContextMenuContext[];
33
+ targetUrlGlobs?: string[];
34
+ children?: ContextMenuItem[];
35
+ }
36
+ export interface ContextMenuItemClick {
37
+ id: string;
38
+ pageUrl: string;
39
+ linkUrl?: string;
40
+ imageUrl?: string;
41
+ selectionText?: string;
42
+ editable: boolean;
43
+ checked?: boolean;
44
+ wasChecked?: boolean;
45
+ }
46
+ export declare function setContextMenuItems(node: NdNodeRef<"webview">, items: ContextMenuItem[]): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativedesktop/react",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/FormalSnake/NativeDesktop#readme",