react-x11 1.2.0 → 2.0.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/README.md +330 -81
- package/package.json +132 -14
- package/src/ClickToComponent.js +9 -2
- package/src/DevToolsIntegration.js +440 -32
- package/src/Reconciler.js +463 -165
- package/src/a11y.js +1299 -0
- package/src/acceleratorhooks.js +86 -0
- package/src/accelerators.js +128 -0
- package/src/activate.js +168 -0
- package/src/anchor.js +360 -0
- package/src/appcontext.js +161 -0
- package/src/appearance.js +748 -0
- package/src/appearancehooks.js +96 -0
- package/src/apphooks.js +73 -0
- package/src/application.js +748 -0
- package/src/atspi.js +1953 -0
- package/src/bus.js +545 -0
- package/src/bushooks.js +136 -0
- package/src/clientmessage.js +140 -0
- package/src/clipboard.js +121 -0
- package/src/components/Button.js +148 -33
- package/src/components/Checkbox.js +66 -32
- package/src/components/Dialog.js +96 -29
- package/src/components/FileDialog.js +491 -0
- package/src/components/Icon.js +329 -0
- package/src/components/Menu.js +797 -133
- package/src/components/PasswordInput.js +434 -0
- package/src/components/ProgressBar.js +91 -12
- package/src/components/Radio.js +78 -35
- package/src/components/Select.js +255 -65
- package/src/components/Slider.js +102 -41
- package/src/components/SplitPane.js +206 -0
- package/src/components/Switch.js +100 -22
- package/src/components/Table.js +529 -0
- package/src/components/Tabs.js +211 -0
- package/src/components/Tooltip.js +528 -54
- package/src/components/anchor.js +154 -107
- package/src/components/change.js +34 -0
- package/src/components/dnd.js +103 -0
- package/src/components/index.js +16 -3
- package/src/components/keys.js +17 -20
- package/src/components/scribble.js +247 -0
- package/src/components/theme.js +350 -42
- package/src/components/typeahead.js +12 -2
- package/src/compose.js +868 -0
- package/src/compositing.js +224 -0
- package/src/dbusmenu.js +384 -0
- package/src/debug.d.ts +63 -0
- package/src/debug.js +642 -0
- package/src/decorations.js +486 -0
- package/src/desktopsettings.js +209 -0
- package/src/desktopsettingshooks.js +62 -0
- package/src/dnd.js +1725 -0
- package/src/editmenu.js +272 -0
- package/src/errors.js +98 -0
- package/src/events.js +1406 -162
- package/src/extensions.js +45 -0
- package/src/filedialog.js +375 -0
- package/src/filedialoghooks.js +132 -0
- package/src/fonthooks.js +64 -0
- package/src/fonts.js +301 -0
- package/src/foreignnodes.js +519 -0
- package/src/frame/child.js +33 -0
- package/src/frame/childmain.js +274 -0
- package/src/frame/env.js +140 -0
- package/src/frame/index.js +444 -0
- package/src/frame/lifecycle.js +67 -0
- package/src/frame/protocol.js +179 -0
- package/src/frames.js +73 -0
- package/src/glbackend.js +114 -0
- package/src/glnodes.js +125 -67
- package/src/globalmenu.js +683 -0
- package/src/host.d.ts +88 -0
- package/src/host.js +42 -0
- package/src/idle.js +490 -0
- package/src/idlehooks.js +100 -0
- package/src/imagesource.js +349 -0
- package/src/index.d.ts +475 -0
- package/src/index.js +83 -9
- package/src/inputtime.js +206 -0
- package/src/jsx-dev-runtime.d.ts +21 -0
- package/src/jsx-dev-runtime.js +2 -0
- package/src/jsx-runtime.d.ts +39 -0
- package/src/jsx-runtime.js +6 -0
- package/src/keyboard.js +256 -0
- package/src/keyboardstate.js +278 -0
- package/src/keyboardstatehooks.js +58 -0
- package/src/keysyms.d.ts +149 -0
- package/src/keysyms.js +270 -0
- package/src/locale.js +170 -0
- package/src/localehooks.js +47 -0
- package/src/menuitem.js +223 -0
- package/src/node.d.ts +597 -0
- package/src/node.js +44 -0
- package/src/nodes.js +9546 -690
- package/src/ntk.d.ts +44 -0
- package/src/ntk.js +25 -0
- package/src/paintcache.js +366 -0
- package/src/palette.js +380 -0
- package/src/pastestate.js +66 -0
- package/src/portal.js +461 -0
- package/src/priority.js +26 -0
- package/src/refresh/index.d.ts +40 -0
- package/src/refresh/index.js +122 -0
- package/src/refresh/loader.d.ts +37 -0
- package/src/refresh/loader.js +401 -0
- package/src/refresh/register.d.ts +5 -0
- package/src/refresh/register.js +13 -0
- package/src/registry.js +232 -0
- package/src/scale.js +626 -0
- package/src/scalehooks.js +27 -0
- package/src/screencolor.js +640 -0
- package/src/screencolorhooks.js +101 -0
- package/src/screens.js +754 -0
- package/src/screenshooks.js +137 -0
- package/src/startup.js +302 -0
- package/src/style.d.ts +126 -0
- package/src/style.js +33 -0
- package/src/styles.js +1482 -6
- package/src/svgnodes.js +306 -0
- package/src/testing/a11y.js +484 -0
- package/src/testing/components.js +414 -0
- package/src/testing/events.js +407 -0
- package/src/testing/harness.js +455 -0
- package/src/testing/index.d.ts +558 -0
- package/src/testing/index.js +85 -0
- package/src/testing/mock-app.js +463 -0
- package/src/testing/pixels.js +152 -0
- package/src/testing/queries.js +224 -0
- package/src/textrange.js +83 -0
- package/src/textselection.js +439 -0
- package/src/trace-registry.js +63 -0
- package/src/transfer.js +93 -0
- package/src/types/appearance.d.ts +84 -0
- package/src/types/application.d.ts +173 -0
- package/src/types/components.d.ts +820 -0
- package/src/types/dbus.d.ts +177 -0
- package/src/types/elements.d.ts +897 -0
- package/src/types/events.d.ts +549 -0
- package/src/types/filedialog.d.ts +260 -0
- package/src/types/fonts.d.ts +124 -0
- package/src/types/frame.d.ts +146 -0
- package/src/types/globalmenu.d.ts +42 -0
- package/src/types/nodes.d.ts +199 -0
- package/src/types/screencolor.d.ts +84 -0
- package/src/types/style.d.ts +434 -0
- package/src/types/system.d.ts +287 -0
- package/src/windowid.js +151 -0
- package/src/windowstate.js +393 -0
- package/src/xsettings.js +336 -0
- package/src/yoga.d.ts +55 -0
- package/src/yoga.js +135 -0
- package/src/components/Canvas3D.js +0 -28
- package/src/geometry3d.js +0 -223
- package/src/pointer3d.js +0 -158
- package/src/raycast3d.js +0 -146
- package/src/richnodes.js +0 -436
- package/src/scene3d.js +0 -683
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The file dialog and the portal machinery under it. See docs/filedialog.md.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ReactNode, RefObject } from 'react';
|
|
6
|
+
import type { MessageBus } from './dbus.js';
|
|
7
|
+
import type { DrawnNode, NtkWindow } from './nodes.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The shape of `AbortSignal` this uses.
|
|
11
|
+
*
|
|
12
|
+
* Named structurally rather than reaching for the global: these declarations
|
|
13
|
+
* compile with `types: []` and no `DOM` lib — adding `DOM` to get one name
|
|
14
|
+
* would also make `<div>` legal JSX, which owning the element namespace exists
|
|
15
|
+
* to prevent. A real `AbortSignal` satisfies this.
|
|
16
|
+
*/
|
|
17
|
+
export interface AbortSignalLike {
|
|
18
|
+
readonly aborted: boolean;
|
|
19
|
+
readonly reason?: unknown;
|
|
20
|
+
addEventListener(
|
|
21
|
+
type: 'abort',
|
|
22
|
+
listener: () => void,
|
|
23
|
+
options?: { once?: boolean },
|
|
24
|
+
): void;
|
|
25
|
+
removeEventListener(type: 'abort', listener: () => void): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Which rung of the ladder answered — or would. */
|
|
29
|
+
export type FileDialogBackend = 'portal' | 'osascript' | 'builtin';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* One entry in a dialog's type filter.
|
|
33
|
+
*
|
|
34
|
+
* Give `extensions` where you can: they translate to every backend. MIME types
|
|
35
|
+
* reach the portal exactly and macOS not at all — see docs/filedialog.md.
|
|
36
|
+
*/
|
|
37
|
+
export interface FileFilter {
|
|
38
|
+
name: string;
|
|
39
|
+
/** `['png', 'jpg']` — with or without the leading dot. */
|
|
40
|
+
extensions?: string[];
|
|
41
|
+
/** `['image/png']`. Portal only. */
|
|
42
|
+
mimeTypes?: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Anything a `<window>`/`<popup>` ref or XID can be given as. */
|
|
46
|
+
export type WindowTarget =
|
|
47
|
+
| NtkWindow
|
|
48
|
+
| DrawnNode
|
|
49
|
+
| RefObject<NtkWindow | DrawnNode | null>
|
|
50
|
+
| number
|
|
51
|
+
| null;
|
|
52
|
+
|
|
53
|
+
export interface FileDialogOptions {
|
|
54
|
+
title?: string;
|
|
55
|
+
/** Several files at once. Ignored when saving — that is always one file. */
|
|
56
|
+
multiple?: boolean;
|
|
57
|
+
filters?: FileFilter[];
|
|
58
|
+
/** Where the dialog opens. */
|
|
59
|
+
defaultFolder?: string;
|
|
60
|
+
/** The name a save dialog starts with. */
|
|
61
|
+
defaultName?: string;
|
|
62
|
+
/** The file a save dialog starts on, replacing `defaultName`. */
|
|
63
|
+
defaultPath?: string;
|
|
64
|
+
/** The confirm button's text — 'Import', 'Attach', … */
|
|
65
|
+
acceptLabel?: string;
|
|
66
|
+
/**
|
|
67
|
+
* The window the dialog belongs to. `transientFor` for the built-in dialog,
|
|
68
|
+
* `parent_window` for the portal; macOS has no cross-process equivalent and
|
|
69
|
+
* ignores it. Worth the one line — without it the dialog floats.
|
|
70
|
+
*/
|
|
71
|
+
parentWindow?: WindowTarget;
|
|
72
|
+
/** Abort the dialog. Closes the portal request or kills `osascript`. */
|
|
73
|
+
signal?: AbortSignalLike;
|
|
74
|
+
/**
|
|
75
|
+
* Force a rung instead of taking the best available one. The seam for a
|
|
76
|
+
* kiosk that must never show the desktop's dialog, and for tests.
|
|
77
|
+
*/
|
|
78
|
+
backend?: FileDialogBackend;
|
|
79
|
+
/** `false` lets the portal dialog be non-modal. Default `true`. */
|
|
80
|
+
modal?: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Nothing on this machine can show a file dialog, and the caller cannot draw
|
|
85
|
+
* one either. A **typed** rejection, so it reads as "fall back to your own UI"
|
|
86
|
+
* rather than as a crash — {@link useFileDialog} never throws it, because it
|
|
87
|
+
* has a tree to draw in.
|
|
88
|
+
*/
|
|
89
|
+
export declare class NoFileDialogError extends Error {
|
|
90
|
+
readonly name: 'NoFileDialogError';
|
|
91
|
+
readonly cause?: unknown;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** There is no portal here — no bus, no service, or a version too old. */
|
|
95
|
+
export declare class NoPortalError extends Error {
|
|
96
|
+
readonly name: 'NoPortalError';
|
|
97
|
+
readonly cause?: unknown;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The user dismissed the dialog. Surfaced as `null`, not thrown, by the
|
|
101
|
+
* functions below; exported for code driving {@link portalRequest} directly. */
|
|
102
|
+
export declare class PortalCancelledError extends Error {
|
|
103
|
+
readonly name: 'PortalCancelledError';
|
|
104
|
+
/** 1 when the user cancelled, 2 when it ended some other way. */
|
|
105
|
+
readonly response: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Which rung this machine lands on, without showing anything.
|
|
110
|
+
*
|
|
111
|
+
* Acquires a bus reference and releases it, so it is cheap but not free —
|
|
112
|
+
* cache it rather than calling it on a render path.
|
|
113
|
+
*/
|
|
114
|
+
export declare function fileDialogBackend(): Promise<FileDialogBackend>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Ask for existing files. Resolves to absolute paths, or `null` when the user
|
|
118
|
+
* cancelled.
|
|
119
|
+
*
|
|
120
|
+
* Rejects with {@link NoFileDialogError} where there is no portal and no
|
|
121
|
+
* `osascript`; {@link useFileDialog} draws its own dialog instead.
|
|
122
|
+
*/
|
|
123
|
+
export declare function openFile(
|
|
124
|
+
options?: FileDialogOptions,
|
|
125
|
+
): Promise<string[] | null>;
|
|
126
|
+
|
|
127
|
+
/** Ask where to write a file. One absolute path, which need not exist yet. */
|
|
128
|
+
export declare function saveFile(
|
|
129
|
+
options?: FileDialogOptions,
|
|
130
|
+
): Promise<string | null>;
|
|
131
|
+
|
|
132
|
+
/** Ask for directories. Absolute paths, or `null` when cancelled. */
|
|
133
|
+
export declare function selectFolder(
|
|
134
|
+
options?: FileDialogOptions,
|
|
135
|
+
): Promise<string[] | null>;
|
|
136
|
+
|
|
137
|
+
export interface FileDialogs {
|
|
138
|
+
openFile(options?: FileDialogOptions): Promise<string[] | null>;
|
|
139
|
+
saveFile(options?: FileDialogOptions): Promise<string | null>;
|
|
140
|
+
selectFolder(options?: FileDialogOptions): Promise<string[] | null>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The window this component is in, resolved when it is read rather than at
|
|
145
|
+
* render time — a `<window>` has no XID until the commit phase.
|
|
146
|
+
*
|
|
147
|
+
* Returns a **ref-like object**, so it drops into anything that already takes
|
|
148
|
+
* a ref (`parentWindow`, `transientFor`) and `windowIdOf()` resolves it.
|
|
149
|
+
* `useFileDialog()` uses it for you; reach for it directly when something
|
|
150
|
+
* else needs the owner window.
|
|
151
|
+
*
|
|
152
|
+
* Exact when the tree has one top-level window, which is nearly every app.
|
|
153
|
+
* With several it prefers the focused one and warns in development when it
|
|
154
|
+
* has to guess — see docs/filedialog.md.
|
|
155
|
+
*/
|
|
156
|
+
export declare function useTopLevelWindow(): {
|
|
157
|
+
readonly current: NtkWindow | DrawnNode | null;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* File dialogs that never run out of rungs: the desktop's own where there is
|
|
162
|
+
* one, `osascript` on macOS, and a browser react-x11 draws itself everywhere
|
|
163
|
+
* else. Cancelling resolves to `null` on all three.
|
|
164
|
+
*
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const { openFile } = useFileDialog({ parentWindow: windowRef });
|
|
167
|
+
* const files = await openFile({ filters: [{ name: 'Text', extensions: ['txt'] }] });
|
|
168
|
+
* if (files) load(files[0]);
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export declare function useFileDialog(
|
|
172
|
+
defaults?: FileDialogOptions,
|
|
173
|
+
): FileDialogs;
|
|
174
|
+
|
|
175
|
+
export interface FileDialogProps {
|
|
176
|
+
kind?: 'open' | 'save' | 'folder';
|
|
177
|
+
title?: string;
|
|
178
|
+
startFolder?: string;
|
|
179
|
+
defaultName?: string;
|
|
180
|
+
filters?: FileFilter[];
|
|
181
|
+
multiple?: boolean;
|
|
182
|
+
acceptLabel?: string;
|
|
183
|
+
/** An XID. Resolved for you when this is rendered by `useFileDialog()`. */
|
|
184
|
+
parentWindow?: number;
|
|
185
|
+
/** Called exactly once: the chosen paths, or `null` for cancel. */
|
|
186
|
+
onDone?: (paths: string[] | null) => void;
|
|
187
|
+
/** The palette, for when this is rendered on a root with no provider. */
|
|
188
|
+
theme?: Record<string, unknown>;
|
|
189
|
+
width?: number;
|
|
190
|
+
height?: number;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The built-in file browser, as a component. `useFileDialog()` renders this
|
|
195
|
+
* for you; reach for it directly only when this dialog is the one you want
|
|
196
|
+
* regardless of what the desktop offers.
|
|
197
|
+
*/
|
|
198
|
+
export declare function FileDialog(props: FileDialogProps): ReactNode;
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// The portal floor
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Is `name` reachable — owned now, **or activatable on demand**?
|
|
206
|
+
*
|
|
207
|
+
* `NameHasOwner` is the wrong question: `org.freedesktop.portal.Desktop` is
|
|
208
|
+
* D-Bus-activatable, so on a healthy desktop where no app has touched a portal
|
|
209
|
+
* yet it has no owner, and a feature gated on that takes the fallback path
|
|
210
|
+
* forever.
|
|
211
|
+
*/
|
|
212
|
+
export declare function hasService(
|
|
213
|
+
name: string,
|
|
214
|
+
busRef?: { bus: MessageBus; uniqueName: string },
|
|
215
|
+
): Promise<boolean>;
|
|
216
|
+
|
|
217
|
+
export interface PortalRequestOptions {
|
|
218
|
+
iface: string;
|
|
219
|
+
member: string;
|
|
220
|
+
/** `'x11:1a00007'`, or `''` for none. */
|
|
221
|
+
parentWindow?: string;
|
|
222
|
+
title?: string;
|
|
223
|
+
/**
|
|
224
|
+
* The method's full argument signature, ending in the options dict —
|
|
225
|
+
* `'sa{sv}'` for `Screenshot.PickColor`. Travels with `args`; omitting
|
|
226
|
+
* both means FileChooser's `'ssa{sv}'` with `[parentWindow, title]`.
|
|
227
|
+
*/
|
|
228
|
+
signature?: string;
|
|
229
|
+
/** The leading arguments the signature describes, before the options dict. */
|
|
230
|
+
args?: unknown[];
|
|
231
|
+
options?: Record<string, unknown>;
|
|
232
|
+
signal?: AbortSignalLike;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Call a portal method that answers through a `Request`, with the
|
|
237
|
+
* subscription in place **before** the call goes out — which is the whole
|
|
238
|
+
* reason this exists rather than each portal doing it slightly wrong.
|
|
239
|
+
*
|
|
240
|
+
* There is deliberately no timeout on the answer: a dialog can be open for an
|
|
241
|
+
* hour. Only the initial call is deadlined.
|
|
242
|
+
*/
|
|
243
|
+
export declare function portalRequest(
|
|
244
|
+
busRef: { bus: MessageBus; uniqueName: string },
|
|
245
|
+
options: PortalRequestOptions,
|
|
246
|
+
): Promise<{ response: number; results: Record<string, unknown> }>;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The version of one portal *interface*, or `0` when it is not there.
|
|
250
|
+
*
|
|
251
|
+
* {@link hasService} answers a different question: the portal service being
|
|
252
|
+
* reachable says nothing about which interfaces its backends provide.
|
|
253
|
+
* Capability lives in the interface's own `version` property — `PickColor`
|
|
254
|
+
* needs `org.freedesktop.portal.Screenshot` at 2, and XFCE's portal has no
|
|
255
|
+
* Screenshot interface at all.
|
|
256
|
+
*/
|
|
257
|
+
export declare function portalVersion(
|
|
258
|
+
iface: string,
|
|
259
|
+
busRef?: { bus: MessageBus; uniqueName: string },
|
|
260
|
+
): Promise<number>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font files an application opens for itself — reading one, and registering
|
|
3
|
+
* one so `fontFamily` can name it. See docs/styling.md#a-font-file-of-your-own.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NtkApp } from './nodes.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ntk's `Font`, as much of it as is worth declaring by hand.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately loose, for the same reason `react-x11/ntk` is: ntk ships no
|
|
12
|
+
* types, and a full mirror written here would drift out of date silently.
|
|
13
|
+
* The members below are the ones an application reads; everything else the
|
|
14
|
+
* class has is still there at runtime.
|
|
15
|
+
*/
|
|
16
|
+
export interface Font {
|
|
17
|
+
/** The family the file calls itself — not necessarily what it is
|
|
18
|
+
* registered as; see {@link loadFont}. */
|
|
19
|
+
readonly familyName: string;
|
|
20
|
+
readonly postscriptName: string;
|
|
21
|
+
readonly unitsPerEm: number;
|
|
22
|
+
/** Stable cache key — the same file opened twice has the same one. */
|
|
23
|
+
readonly key: string;
|
|
24
|
+
/** The path it was opened from, or null for a font opened from bytes. */
|
|
25
|
+
readonly path: string | null;
|
|
26
|
+
/** fontkit's own font object, for what this interface does not cover
|
|
27
|
+
* (`namedVariations`, the raw tables). */
|
|
28
|
+
readonly fk: Record<string, unknown> & {
|
|
29
|
+
namedVariations?: Record<string, Record<string, number>>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* `{}` for a static face, so `Object.keys(font.variationAxes).length` is
|
|
33
|
+
* the whole of "is this variable?".
|
|
34
|
+
*/
|
|
35
|
+
readonly variationAxes: Record<
|
|
36
|
+
string,
|
|
37
|
+
{ name?: string; min: number; default: number; max: number }
|
|
38
|
+
>;
|
|
39
|
+
/** Scaled to a pixel size. A metric the face never declared is `null`
|
|
40
|
+
* rather than 0 — a cap height it did not state is not a cap height of
|
|
41
|
+
* zero. */
|
|
42
|
+
metrics(size: number): {
|
|
43
|
+
ascent: number;
|
|
44
|
+
descent: number;
|
|
45
|
+
lineGap: number;
|
|
46
|
+
capHeight: number | null;
|
|
47
|
+
xHeight: number | null;
|
|
48
|
+
[key: string]: number | null;
|
|
49
|
+
};
|
|
50
|
+
hasGlyph(codepoint: number): boolean;
|
|
51
|
+
/** This face at a point in its design space: `variation({ wght: 460 })`. */
|
|
52
|
+
variation(settings: Record<string, number>): Font;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface OpenFontOptions {
|
|
56
|
+
/** Which face of a `.ttc` collection; the first otherwise. */
|
|
57
|
+
postscriptName?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface LoadFontOptions extends OpenFontOptions {
|
|
61
|
+
/** Register under this name instead of the font's own, and use it
|
|
62
|
+
* verbatim. */
|
|
63
|
+
family?: string;
|
|
64
|
+
/** Override the weight the file declares. */
|
|
65
|
+
weight?: number | string;
|
|
66
|
+
/** `'italic'` / `'oblique'`, overriding what the file declares. */
|
|
67
|
+
style?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LoadedFont {
|
|
71
|
+
readonly font: Font;
|
|
72
|
+
/** What to put in `fontFamily` — the font's own name, scoped to
|
|
73
|
+
* `Inter 2` only where a second file would otherwise be unreachable. */
|
|
74
|
+
readonly family: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read a font file — metrics, coverage, variation axes — through the
|
|
79
|
+
* connection's font cache, so it is read once however often this is called.
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* const font = openFont(app, '/usr/share/fonts/truetype/inter/Inter.ttf');
|
|
83
|
+
* font.metrics(30);
|
|
84
|
+
* font.variationAxes;
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* Nothing about the app changes: the face is not registered, so it is
|
|
88
|
+
* neither a `fontFamily` candidate nor part of the fallback chain. Use
|
|
89
|
+
* {@link loadFont} to draw with it.
|
|
90
|
+
*/
|
|
91
|
+
export function openFont(
|
|
92
|
+
app: NtkApp,
|
|
93
|
+
source: string | Uint8Array,
|
|
94
|
+
opts?: OpenFontOptions,
|
|
95
|
+
): Font;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Read a font file and register it, returning the face and the family name
|
|
99
|
+
* to draw it with.
|
|
100
|
+
*
|
|
101
|
+
* ```tsx
|
|
102
|
+
* const { family } = loadFont(app, '/path/to/Inter.ttf');
|
|
103
|
+
* <text style={{ fontFamily: family }}>Handgloves</text>;
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function loadFont(
|
|
107
|
+
app: NtkApp,
|
|
108
|
+
source: string | Uint8Array,
|
|
109
|
+
opts?: LoadFontOptions,
|
|
110
|
+
): LoadedFont;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* {@link loadFont} from a component, memoized per app and file. Null when
|
|
114
|
+
* `source` is null, so a picker can call it before anything is picked.
|
|
115
|
+
*
|
|
116
|
+
* ```tsx
|
|
117
|
+
* const picked = useFont(path);
|
|
118
|
+
* <text style={{ fontFamily: picked?.family }}>{sample}</text>;
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function useFont(
|
|
122
|
+
source?: string | Uint8Array | null,
|
|
123
|
+
opts?: LoadFontOptions,
|
|
124
|
+
): LoadedFont | null;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<Frame>` — a pane of this application in its own process — and the
|
|
3
|
+
* context bridge that follows the app into it. See docs/frame.md.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Context, ReactElement, ReactNode, Ref } from 'react';
|
|
7
|
+
import type { StyleProp } from './style.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The props bag a `<Frame>` sends its pane. Values cross by structured
|
|
11
|
+
* clone; functions become fire-and-forget stubs on the pane side, so a
|
|
12
|
+
* callback's return value is always `undefined` over there.
|
|
13
|
+
*/
|
|
14
|
+
export type FrameProps = Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
/** Why a pane is not running, for `fallback` and `onExit` to read. */
|
|
17
|
+
export interface FrameError extends Error {
|
|
18
|
+
/** Where it went wrong: `'spawn' | 'load' | 'connect' | 'handshake' |
|
|
19
|
+
* 'runtime' | 'send' | 'embed' | 'exit'`. */
|
|
20
|
+
phase?: string;
|
|
21
|
+
code?: number | null;
|
|
22
|
+
signal?: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface FrameHandle {
|
|
26
|
+
/** Start a fresh pane process; what `fallback` receives to retry with. */
|
|
27
|
+
restart(): void;
|
|
28
|
+
/** The pane's pid, `null` before the first spawn and after an exit. */
|
|
29
|
+
pid: number | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FrameComponentProps {
|
|
33
|
+
/**
|
|
34
|
+
* The pane module: its default export is mounted as the pane's root
|
|
35
|
+
* component. A `URL` (`new URL('./pane.js', import.meta.url)` — typed
|
|
36
|
+
* structurally so no DOM lib is needed) or an absolute path — never
|
|
37
|
+
* relative, which would resolve against react-x11's own files rather
|
|
38
|
+
* than the caller's.
|
|
39
|
+
*/
|
|
40
|
+
src: { href: string } | string;
|
|
41
|
+
/** Data for the pane, snapshotted and sent whole once per commit that
|
|
42
|
+
* changes it. Functions anywhere in the bag become RPC stubs. */
|
|
43
|
+
props?: FrameProps;
|
|
44
|
+
/** The embedded pane's rect in this tree — sized like any other child. */
|
|
45
|
+
style?: StyleProp;
|
|
46
|
+
/** X display for the pane process; defaults to the inherited `$DISPLAY`. */
|
|
47
|
+
display?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Which bridged contexts follow the app into this pane: `true` (default)
|
|
50
|
+
* for every registered one — the theme included — `false` for none, or an
|
|
51
|
+
* allowlist of keys.
|
|
52
|
+
*/
|
|
53
|
+
bridge?: boolean | string[];
|
|
54
|
+
/**
|
|
55
|
+
* What this rect shows when the pane crashed or could not start: an
|
|
56
|
+
* element, or a function of the error and a `restart` that respawns.
|
|
57
|
+
* Without one the rect is an empty box in its `style`.
|
|
58
|
+
*/
|
|
59
|
+
fallback?:
|
|
60
|
+
| ReactElement
|
|
61
|
+
| ((info: { error: FrameError | null; restart: () => void }) => ReactNode);
|
|
62
|
+
/** Whether the pane joins the tab order, like any `<foreign>`. */
|
|
63
|
+
focusable?: boolean;
|
|
64
|
+
/** The pane mounted and its window is being embedded. */
|
|
65
|
+
onStarted?(info: { pid: number | null; windowId: number }): void;
|
|
66
|
+
/** The pane process ended. `expected` is true when this side asked —
|
|
67
|
+
* unmount, `restart()`, a `src` change — and false for a crash. */
|
|
68
|
+
onExit?(info: {
|
|
69
|
+
code: number | null;
|
|
70
|
+
signal: string | null;
|
|
71
|
+
expected: boolean;
|
|
72
|
+
}): void;
|
|
73
|
+
/**
|
|
74
|
+
* Replace the fork with a transport of your own — the seam the tests use
|
|
75
|
+
* (a loopback pair into `runFrameChild`), and the door to running a pane
|
|
76
|
+
* somewhere other than a child process. Advanced; see docs/frame.md.
|
|
77
|
+
*/
|
|
78
|
+
transport?(options: { src: string; display?: string }): FrameTransport;
|
|
79
|
+
ref?: Ref<FrameHandle>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The parent's end of the wire to a pane. */
|
|
83
|
+
export interface FrameTransport {
|
|
84
|
+
/** May throw when the channel is gone or the message will not clone. */
|
|
85
|
+
send(msg: object): void;
|
|
86
|
+
onMessage(cb: (msg: object) => void): () => void;
|
|
87
|
+
onExit(
|
|
88
|
+
cb: (info: {
|
|
89
|
+
code?: number | null;
|
|
90
|
+
signal?: string | null;
|
|
91
|
+
error?: Error | null;
|
|
92
|
+
}) => void,
|
|
93
|
+
): () => void;
|
|
94
|
+
kill?(signal: string): void;
|
|
95
|
+
pid?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* A module of this application, mounted in its own process, its window
|
|
100
|
+
* embedded here (over `<foreign>`). Props cross as data, callbacks as
|
|
101
|
+
* fire-and-forget stubs, context through the bridge — and the process
|
|
102
|
+
* boundary contains the pane's failures, not its intentions: it is not a
|
|
103
|
+
* security boundary (docs/security.md).
|
|
104
|
+
*/
|
|
105
|
+
export function Frame(props: FrameComponentProps): ReactElement | null;
|
|
106
|
+
|
|
107
|
+
export interface FrameContext<T> {
|
|
108
|
+
/** The name this value travels under; unique per application. */
|
|
109
|
+
key: string;
|
|
110
|
+
/** The ordinary React context, for `useContext` and class consumers. */
|
|
111
|
+
Context: Context<T>;
|
|
112
|
+
/** The provider that also publishes the value to `<Frame>` panes. */
|
|
113
|
+
Provider: (props: { value: T; children?: ReactNode }) => ReactElement;
|
|
114
|
+
/** `useContext(Context)`, as a method. */
|
|
115
|
+
use(): T;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A React context whose value follows the app into its `<Frame>` panes:
|
|
120
|
+
* in-process an ordinary context, across a frame a structured-clone
|
|
121
|
+
* snapshot recreated as a real provider around the pane. One direction,
|
|
122
|
+
* data only — a value that will not clone is dropped with a warning, and
|
|
123
|
+
* dispatchers travel in `props` where the wiring is visible.
|
|
124
|
+
*
|
|
125
|
+
* Both sides must import the module that calls this — which the pane
|
|
126
|
+
* already does, since its components read the context from it.
|
|
127
|
+
*/
|
|
128
|
+
export function createFrameContext<T>(
|
|
129
|
+
key: string,
|
|
130
|
+
defaultValue: T,
|
|
131
|
+
options?: {
|
|
132
|
+
serialize?: (value: T) => unknown;
|
|
133
|
+
revive?: (wire: unknown) => T;
|
|
134
|
+
},
|
|
135
|
+
): FrameContext<T>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Runs when the host is letting this pane go — the `<Frame>` unmounted,
|
|
139
|
+
* the host app is exiting — bounded by the host's patience (it escalates
|
|
140
|
+
* to signals). May return a promise. Not called on a crash; outside a
|
|
141
|
+
* frame it never runs.
|
|
142
|
+
*/
|
|
143
|
+
export function useFrameClose(handler: () => void | Promise<void>): void;
|
|
144
|
+
|
|
145
|
+
/** Whether this process is a `<Frame>` pane. */
|
|
146
|
+
export function isFramed(): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The global menu — handing a window's menu bar to the desktop's panel.
|
|
3
|
+
*
|
|
4
|
+
* `MenuBar` does all of this itself. These names are for a component that
|
|
5
|
+
* draws its own bar and wants the same behaviour. See docs/globalmenu.md.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MenuBarMenu, MenuItem } from './components.js';
|
|
9
|
+
|
|
10
|
+
export interface GlobalMenuOptions {
|
|
11
|
+
/** Runs after the item's own `onSelect`, exactly as `MenuBar`'s does. */
|
|
12
|
+
onSelect?: (item: MenuItem) => void;
|
|
13
|
+
/**
|
|
14
|
+
* A submenu is about to be shown. dbusmenu wants a synchronous answer that
|
|
15
|
+
* React cannot give, so this is a notification rather than a chance to
|
|
16
|
+
* fill the menu in time — update state from it and the new items reach the
|
|
17
|
+
* panel one round trip later. See docs/globalmenu.md.
|
|
18
|
+
*/
|
|
19
|
+
onAboutToShow?: (item: MenuItem) => void;
|
|
20
|
+
/** `false` keeps the menu in the window. Defaults to `true`. */
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Is this window's menu being drawn by the desktop?
|
|
26
|
+
*
|
|
27
|
+
* `false` until proven otherwise, and the proof is a registrar answering
|
|
28
|
+
* `RegisterWindow` — so no bus, no panel, or a panel that refused the
|
|
29
|
+
* registration all keep the menu where the app drew it.
|
|
30
|
+
*
|
|
31
|
+
* ```tsx
|
|
32
|
+
* const exported = useGlobalMenu(menus, { onSelect });
|
|
33
|
+
* if (exported) return null;
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function useGlobalMenu(
|
|
37
|
+
menus: readonly MenuBarMenu[] | undefined,
|
|
38
|
+
options?: GlobalMenuOptions,
|
|
39
|
+
): boolean;
|
|
40
|
+
|
|
41
|
+
/** The bus name a panel owns while it is showing application menus. */
|
|
42
|
+
export const REGISTRAR_NAME: string;
|