react-x11 2.8.3 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/src/Reconciler.js +7 -0
- package/src/cocoa/app.js +35 -0
- package/src/cocoa/calendar.js +186 -0
- package/src/cocoa/context2d.js +220 -4
- package/src/cocoa/native.js +20 -3
- package/src/cocoa/permissions.js +14 -5
- package/src/cocoa/window.js +21 -5
- package/src/debug.js +8 -2
- package/src/desktopcalendar.js +1415 -0
- package/src/desktopcalendarhooks.js +259 -0
- package/src/glnodes.js +63 -4
- package/src/index.d.ts +11 -1
- package/src/index.js +9 -0
- package/src/node.d.ts +214 -2
- package/src/nodes.js +208 -13
- package/src/pacing.js +476 -0
- package/src/permissionhooks.js +9 -6
- package/src/permissions.js +12 -2
- package/src/trace-registry.js +4 -3
- package/src/types/desktopcalendar.d.ts +207 -0
- package/src/types/elements.d.ts +54 -0
- package/src/types/permissions.d.ts +12 -4
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calendars the user's desktop already has — EventKit on macOS,
|
|
3
|
+
* Evolution Data Server on a freedesktop session. See
|
|
4
|
+
* docs/desktop-calendar.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NtkApp } from './nodes.js';
|
|
8
|
+
import type { PermissionStatus } from './permissions.js';
|
|
9
|
+
|
|
10
|
+
/** Which rung answered: the EventKit bridge, an `osascript` child holding
|
|
11
|
+
* an `EKEventStore`, or Evolution Data Server over the session bus. */
|
|
12
|
+
export type CalendarBackend = 'cocoa' | 'osascript' | 'eds';
|
|
13
|
+
|
|
14
|
+
/** One calendar the desktop knows about. */
|
|
15
|
+
export interface DesktopCalendarInfo {
|
|
16
|
+
uid: string;
|
|
17
|
+
/** The name shown in the desktop's own calendar UI. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Whether the desktop has it switched on. Always true on macOS, which
|
|
20
|
+
* has no such state to report. */
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
/** `'#3584e4'` — the colour that UI draws it in, worth carrying into a
|
|
23
|
+
* day marker. */
|
|
24
|
+
color?: string;
|
|
25
|
+
/** `caldav`, `google`, `local`, `exchange`, `subscription`, `birthday`…
|
|
26
|
+
* the store's own word, lower-cased. */
|
|
27
|
+
backend?: string;
|
|
28
|
+
readOnly: boolean;
|
|
29
|
+
/** The account it came from, where one is named — a GNOME Online Accounts
|
|
30
|
+
* entry, or an `EKSource` (`'iCloud'`, `'Google'`). */
|
|
31
|
+
account?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** One occurrence, with any recurrence already expanded. */
|
|
35
|
+
export interface DesktopEvent {
|
|
36
|
+
uid: string;
|
|
37
|
+
summary: string;
|
|
38
|
+
location?: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
start: Date;
|
|
41
|
+
/** **Exclusive**, on every rung: an all-day event on the 10th ends at
|
|
42
|
+
* midnight on the 11th. The macOS rungs normalise what EventKit reports
|
|
43
|
+
* (the last second of the last day) so a caller never has to ask. */
|
|
44
|
+
end: Date;
|
|
45
|
+
allDay: boolean;
|
|
46
|
+
recurring: boolean;
|
|
47
|
+
calendar: { uid: string; name: string; color?: string };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A calendar that would not answer. One broken backend is not a failure. */
|
|
51
|
+
export interface DesktopCalendarError {
|
|
52
|
+
calendar: DesktopCalendarInfo;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface EventsResult {
|
|
57
|
+
events: DesktopEvent[];
|
|
58
|
+
/** Empty on the macOS rungs, where there is one store. */
|
|
59
|
+
errors: DesktopCalendarError[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* What `watch` reports. Deliberately thin: re-query rather than patch.
|
|
64
|
+
*
|
|
65
|
+
* `'changed'` with a null calendar and no count is EventKit's — its
|
|
66
|
+
* notification names nothing, and a rung must not invent a count.
|
|
67
|
+
*/
|
|
68
|
+
export interface CalendarChange {
|
|
69
|
+
calendar: DesktopCalendarInfo | null;
|
|
70
|
+
kind: 'ObjectsAdded' | 'ObjectsModified' | 'ObjectsRemoved' | 'changed';
|
|
71
|
+
count: number | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface EventsOptions {
|
|
75
|
+
/** Restrict to these calendars. Default: every enabled one. */
|
|
76
|
+
calendars?: DesktopCalendarInfo[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Nothing on this machine can read a calendar. Raised only by
|
|
81
|
+
* `desktopCalendar({ required: true })`; the default answer is `null`,
|
|
82
|
+
* because a machine with no calendar service is an ordinary machine.
|
|
83
|
+
*/
|
|
84
|
+
export declare class NoCalendarServiceError extends Error {
|
|
85
|
+
readonly name: 'NoCalendarServiceError';
|
|
86
|
+
readonly cause?: unknown;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The app may not read the calendars. `status` says why: `'denied'` and
|
|
91
|
+
* `'restricted'` and `'write-only'` are decisions, and `'prompt'` is a
|
|
92
|
+
* request that came back with nothing — TCC declining to ask, which the
|
|
93
|
+
* hook reports as `'unavailable'` rather than as a refusal.
|
|
94
|
+
*/
|
|
95
|
+
export declare class CalendarAccessError extends Error {
|
|
96
|
+
readonly name: 'CalendarAccessError';
|
|
97
|
+
readonly status: PermissionStatus;
|
|
98
|
+
readonly cause?: unknown;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** A handle on the desktop's calendars. `close()` it. */
|
|
102
|
+
export interface DesktopCalendar {
|
|
103
|
+
readonly backend: CalendarBackend;
|
|
104
|
+
/** The grant, without prompting; `'granted'` on a rung with no such gate. */
|
|
105
|
+
access(): Promise<PermissionStatus>;
|
|
106
|
+
/** Raise the system's prompt where there is one. Reads do this on the
|
|
107
|
+
* first one, so a picker the user never opens never prompts. */
|
|
108
|
+
requestAccess(): Promise<PermissionStatus>;
|
|
109
|
+
/** System Settings › Privacy & Security › Calendars. `false` off macOS. */
|
|
110
|
+
openSettings(): Promise<boolean>;
|
|
111
|
+
listCalendars(): Promise<DesktopCalendarInfo[]>;
|
|
112
|
+
/** Occurrences in `[from, to)`, sorted by start. Rejects with
|
|
113
|
+
* {@link CalendarAccessError} where the user said no. */
|
|
114
|
+
eventsBetween(
|
|
115
|
+
from: Date,
|
|
116
|
+
to: Date,
|
|
117
|
+
options?: EventsOptions,
|
|
118
|
+
): Promise<EventsResult>;
|
|
119
|
+
/** Call `onChange` when something moves; the returned function stops. */
|
|
120
|
+
watch(
|
|
121
|
+
from: Date,
|
|
122
|
+
to: Date,
|
|
123
|
+
onChange: (change: CalendarChange) => void,
|
|
124
|
+
options?: EventsOptions,
|
|
125
|
+
): Promise<() => Promise<void>>;
|
|
126
|
+
close(): Promise<void>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface DesktopCalendarOptions {
|
|
130
|
+
/** The connection whose backend to ask, when there are several. */
|
|
131
|
+
app?: NtkApp;
|
|
132
|
+
/** Pin one rung and fail rather than fall through it. */
|
|
133
|
+
backend?: CalendarBackend;
|
|
134
|
+
/** Reject with {@link NoCalendarServiceError} instead of answering null. */
|
|
135
|
+
required?: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The desktop's calendars, on the best rung this machine has — or `null`,
|
|
140
|
+
* which is an ordinary answer about a machine.
|
|
141
|
+
*/
|
|
142
|
+
export declare function desktopCalendar(
|
|
143
|
+
options?: DesktopCalendarOptions,
|
|
144
|
+
): Promise<DesktopCalendar | null>;
|
|
145
|
+
|
|
146
|
+
/** Which rung this machine lands on, without reading anything, spawning
|
|
147
|
+
* anything or prompting. */
|
|
148
|
+
export declare function calendarBackend(
|
|
149
|
+
options?: Pick<DesktopCalendarOptions, 'app'>,
|
|
150
|
+
): Promise<CalendarBackend | null>;
|
|
151
|
+
|
|
152
|
+
/** The local calendar day a `Date` falls on, as `'YYYY-MM-DD'` — the key
|
|
153
|
+
* `byDay` uses and `<Calendar dayContent>` is handed. */
|
|
154
|
+
export declare function dayKey(date: Date): string;
|
|
155
|
+
|
|
156
|
+
/** Occurrences grouped by the local day they appear on. An event lands
|
|
157
|
+
* under every day it touches, not only under its start. */
|
|
158
|
+
export declare function byDay(
|
|
159
|
+
events: DesktopEvent[],
|
|
160
|
+
): Map<string, DesktopEvent[]>;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* `'idle'` before anything is asked, `'denied'` for the user's refusal and
|
|
164
|
+
* `'unavailable'` for the machine's silence — separate words, because only
|
|
165
|
+
* one of them has a Settings switch behind it.
|
|
166
|
+
*/
|
|
167
|
+
export type DesktopCalendarStatus =
|
|
168
|
+
'idle' | 'loading' | 'ready' | 'denied' | 'unavailable';
|
|
169
|
+
|
|
170
|
+
export interface UseDesktopCalendarEventsOptions {
|
|
171
|
+
/** Start of the window to read, inclusive. */
|
|
172
|
+
from: Date;
|
|
173
|
+
/** End of the window, exclusive. */
|
|
174
|
+
to: Date;
|
|
175
|
+
/** Restrict to these calendar uids. Default: every enabled calendar. */
|
|
176
|
+
calendars?: string[];
|
|
177
|
+
/** Re-query when the desktop says something changed. */
|
|
178
|
+
watch?: boolean;
|
|
179
|
+
/** Set false to hold off entirely — and to not prompt: a picker that is
|
|
180
|
+
* not open yet has no business asking for the user's calendar. */
|
|
181
|
+
enabled?: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface UseDesktopCalendarEventsResult {
|
|
185
|
+
events: DesktopEvent[];
|
|
186
|
+
/** The same events, keyed by `'YYYY-MM-DD'`, ready for `dayContent`. */
|
|
187
|
+
byDay: Map<string, DesktopEvent[]>;
|
|
188
|
+
/** Every calendar found, for a legend or a filter. */
|
|
189
|
+
calendars: DesktopCalendarInfo[];
|
|
190
|
+
/** Calendars that would not answer while others did. Not fatal. */
|
|
191
|
+
errors: DesktopCalendarError[];
|
|
192
|
+
status: DesktopCalendarStatus;
|
|
193
|
+
/** Which rung answered, once one has. */
|
|
194
|
+
backend: CalendarBackend | null;
|
|
195
|
+
/** Why there are no events, when there is a reason worth showing. */
|
|
196
|
+
error: Error | null;
|
|
197
|
+
/** Re-query now. */
|
|
198
|
+
refresh: () => void;
|
|
199
|
+
/** System Settings › Privacy & Security › Calendars — for `'denied'`, and
|
|
200
|
+
* for no other status. */
|
|
201
|
+
openSettings: () => Promise<boolean>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** The user's desktop calendar events, as rendering state. */
|
|
205
|
+
export declare function useDesktopCalendarEvents(
|
|
206
|
+
options: UseDesktopCalendarEventsOptions,
|
|
207
|
+
): UseDesktopCalendarEventsResult;
|
package/src/types/elements.d.ts
CHANGED
|
@@ -278,6 +278,47 @@ export type WindowType =
|
|
|
278
278
|
| 'desktop'
|
|
279
279
|
| (string & {});
|
|
280
280
|
|
|
281
|
+
/**
|
|
282
|
+
* How a window paces its frames when its content changes faster than the
|
|
283
|
+
* display refreshes — a terminal under a flood, a chart on a socket, a
|
|
284
|
+
* simulation. Priced in CPU time, not counted in updates: a frame costs
|
|
285
|
+
* what the flush (and on macOS the present) took of this thread, and a
|
|
286
|
+
* claim waits only while recent frames have spent more than their share.
|
|
287
|
+
* A scroll that is mostly blits keeps the display's rate; only a stream of
|
|
288
|
+
* expensive frames earns a wait (docs/elements.md "frameRate").
|
|
289
|
+
*
|
|
290
|
+
* - `'display'` — every frame the clock gives, after React's own
|
|
291
|
+
* batching. **The default.**
|
|
292
|
+
* - `'adaptive'` — paints stay under a quarter of the time while busy, and
|
|
293
|
+
* the screen is never more than 50ms behind (a 20fps floor). Idle claims
|
|
294
|
+
* and cheap frames never wait.
|
|
295
|
+
* - `'throughput'` — a tenth, a 10fps floor and a 30fps ceiling: fewer,
|
|
296
|
+
* later frames for a window whose output matters more than its display.
|
|
297
|
+
* - a number — a ceiling in frames per second, nothing else.
|
|
298
|
+
* - `{ budget, minFps, maxFps }` — the three numbers the presets are made
|
|
299
|
+
* of, any subset; the rest are `'display'`'s (so name a `minFps` with a
|
|
300
|
+
* `budget` below 1).
|
|
301
|
+
*
|
|
302
|
+
* `createRoot({ frameRate })` sets the default for every window of a root,
|
|
303
|
+
* and `REACT_X11_FRAME_RATE` overrides both from the environment.
|
|
304
|
+
*/
|
|
305
|
+
export type FrameRate =
|
|
306
|
+
| 'display'
|
|
307
|
+
| 'adaptive'
|
|
308
|
+
| 'throughput'
|
|
309
|
+
| number
|
|
310
|
+
| {
|
|
311
|
+
/** Share of wall time paints may take while busy, above 0 and at
|
|
312
|
+
* most 1. */
|
|
313
|
+
budget?: number;
|
|
314
|
+
/** The floor: the screen is never more than `1000 / minFps` ms behind
|
|
315
|
+
* the last paint. 0 for none. */
|
|
316
|
+
minFps?: number;
|
|
317
|
+
/** A ceiling in frames per second, cheap frames included. 0 for
|
|
318
|
+
* none. */
|
|
319
|
+
maxFps?: number;
|
|
320
|
+
};
|
|
321
|
+
|
|
281
322
|
export interface WindowProps
|
|
282
323
|
extends
|
|
283
324
|
CommonProps,
|
|
@@ -289,6 +330,10 @@ export interface WindowProps
|
|
|
289
330
|
ref?: Ref<NtkWindow>;
|
|
290
331
|
/** Window title (UTF-8, via `WM_NAME` + `_NET_WM_NAME`). */
|
|
291
332
|
title?: string;
|
|
333
|
+
/** How this window paces its frames under a stream of changes — see
|
|
334
|
+
* {@link FrameRate}. `'display'` unless the root or the environment says
|
|
335
|
+
* otherwise. A `<popup>` paces itself the same way. */
|
|
336
|
+
frameRate?: FrameRate;
|
|
292
337
|
/**
|
|
293
338
|
* Created but never self-mapped: this window is waiting to be embedded
|
|
294
339
|
* (XEmbed / `<foreign>` on the other side), and from the reparent on,
|
|
@@ -865,6 +910,15 @@ export interface GlAreaProps extends DrawnProps<DrawnNode> {
|
|
|
865
910
|
clearColor?: Color | [number, number, number, number];
|
|
866
911
|
/** `'demand'` (default) redraws on change; `'always'` runs continuously. */
|
|
867
912
|
frameLoop?: FrameLoop;
|
|
913
|
+
/**
|
|
914
|
+
* How the surface's frames are paced when they are expensive — the same
|
|
915
|
+
* vocabulary as {@link WindowProps.frameRate}, priced by what `onDraw`
|
|
916
|
+
* and the swap cost this thread. Defaults to the owning window's, so a
|
|
917
|
+
* `<window frameRate="adaptive">` paces the scene inside it too; set it
|
|
918
|
+
* here for a scene that wants every frame (`'display'`) while the window
|
|
919
|
+
* around it streams, or the other way round.
|
|
920
|
+
*/
|
|
921
|
+
frameRate?: FrameRate;
|
|
868
922
|
/** Visual spec for ntk's `chooseGLXConfig`, e.g. `{ DEPTH_SIZE: 24 }`. */
|
|
869
923
|
glx?: Record<string, unknown>;
|
|
870
924
|
/** Runs once, with the context current: one-time state, uploads, lists. */
|
|
@@ -12,7 +12,9 @@ export type PermissionKind =
|
|
|
12
12
|
| 'accessibility'
|
|
13
13
|
| 'input-monitoring'
|
|
14
14
|
| 'automation'
|
|
15
|
-
| 'location'
|
|
15
|
+
| 'location'
|
|
16
|
+
| 'calendars'
|
|
17
|
+
| 'reminders';
|
|
16
18
|
|
|
17
19
|
/** The panes `openPrivacySettings` reaches: every kind, plus the two with no
|
|
18
20
|
* API because reading the folder is the prompt. */
|
|
@@ -23,10 +25,12 @@ export type PrivacyPane =
|
|
|
23
25
|
* `'granted'`, `'denied'` and `'restricted'` (MDM or parental controls — the
|
|
24
26
|
* user cannot grant it) are the platform's; `'prompt'` is not decided yet, a
|
|
25
27
|
* request would ask; `'unknown'` is "nothing here can say", a fact about the
|
|
26
|
-
* machine rather than the permission.
|
|
28
|
+
* machine rather than the permission. `'write-only'` is macOS 14's partial
|
|
29
|
+
* grant for `calendars`/`reminders`: a grant to a writer, a refusal to a
|
|
30
|
+
* reader, and only the caller knows which it is.
|
|
27
31
|
*/
|
|
28
32
|
export type PermissionStatus =
|
|
29
|
-
'granted' | 'denied' | 'restricted' | 'prompt' | 'unknown';
|
|
33
|
+
'granted' | 'denied' | 'restricted' | 'prompt' | 'write-only' | 'unknown';
|
|
30
34
|
|
|
31
35
|
export interface PermissionOptions {
|
|
32
36
|
/** The connection whose backend to ask, when there are several. */
|
|
@@ -34,6 +38,10 @@ export interface PermissionOptions {
|
|
|
34
38
|
/** `automation` only: the bundle id of the app to send Apple Events to.
|
|
35
39
|
* Only a running target has an answer. */
|
|
36
40
|
target?: string;
|
|
41
|
+
/** `calendars` only: which grant to ask for. `'write-only'` is macOS 14's
|
|
42
|
+
* narrower prompt — the app may save events it cannot read. `reminders`
|
|
43
|
+
* has no such grant and refuses one. */
|
|
44
|
+
access?: 'full' | 'write-only';
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
/**
|
|
@@ -96,5 +104,5 @@ export interface Permission {
|
|
|
96
104
|
/** A permission for a component: the status as render state. */
|
|
97
105
|
export declare function usePermission(
|
|
98
106
|
kind: PermissionKind,
|
|
99
|
-
options?: Pick<PermissionOptions, 'target'>,
|
|
107
|
+
options?: Pick<PermissionOptions, 'target' | 'access'>,
|
|
100
108
|
): Permission;
|