react-native-gtkx 0.3.0-alpha.1 → 0.4.0-alpha.2
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 +1 -1
- package/dist/adw/index.d.ts +3 -2
- package/dist/adw/index.js +21 -4
- package/dist/adw/index.js.map +1 -1
- package/dist/adw/widgets.generated.d.ts +1 -1
- package/dist/adw/widgets.generated.js +2 -2
- package/dist/adw/widgets.generated.js.map +1 -1
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/event.d.ts +15 -0
- package/dist/animated/event.js +80 -0
- package/dist/animated/event.js.map +1 -0
- package/dist/animated/index.d.ts +4 -0
- package/dist/animated/index.js +8 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/spring.d.ts +10 -0
- package/dist/animated/spring.js +20 -6
- package/dist/animated/spring.js.map +1 -1
- package/dist/animated/timing.d.ts +10 -0
- package/dist/animated/timing.js +21 -13
- package/dist/animated/timing.js.map +1 -1
- package/dist/apis/host.gtkx.js +70 -11
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/common/navigation-stack.d.ts +6 -3
- package/dist/common/navigation-stack.js +8 -1
- package/dist/common/navigation-stack.js.map +1 -1
- package/dist/common/widget.js +2 -11
- package/dist/common/widget.js.map +1 -1
- package/dist/components/animated.d.ts +1 -0
- package/dist/components/animated.js +28 -4
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.js +32 -8
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/request-animation-frame.d.ts +33 -0
- package/dist/components/request-animation-frame.js +83 -0
- package/dist/components/request-animation-frame.js.map +1 -0
- package/dist/components/scroll-view.js +60 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/virtualized-list.js +1 -4
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/dnd/autoscroll.d.ts +44 -0
- package/dist/dnd/autoscroll.js +176 -0
- package/dist/dnd/autoscroll.js.map +1 -0
- package/dist/dnd/grid-order.d.ts +45 -0
- package/dist/dnd/grid-order.js +151 -0
- package/dist/dnd/grid-order.js.map +1 -0
- package/dist/dnd/grid.d.ts +44 -0
- package/dist/dnd/grid.js +397 -0
- package/dist/dnd/grid.js.map +1 -0
- package/dist/dnd/gtk-controllers.d.ts +16 -2
- package/dist/dnd/gtk-controllers.js +23 -3
- package/dist/dnd/gtk-controllers.js.map +1 -1
- package/dist/dnd/index.d.ts +4 -2
- package/dist/dnd/index.js +9 -2
- package/dist/dnd/index.js.map +1 -1
- package/dist/dnd/order-state.d.ts +14 -0
- package/dist/dnd/order-state.js +55 -0
- package/dist/dnd/order-state.js.map +1 -0
- package/dist/dnd/order.d.ts +19 -0
- package/dist/dnd/order.js +19 -0
- package/dist/dnd/order.js.map +1 -1
- package/dist/dnd/sortable.d.ts +29 -4
- package/dist/dnd/sortable.js +308 -115
- package/dist/dnd/sortable.js.map +1 -1
- package/dist/dnd/types.d.ts +236 -14
- package/dist/dnd/types.js +30 -0
- package/dist/dnd/types.js.map +1 -1
- package/dist/gesture-handler-compat/attach-context.d.ts +29 -0
- package/dist/gesture-handler-compat/attach-context.js +90 -0
- package/dist/gesture-handler-compat/attach-context.js.map +1 -0
- package/dist/gesture-handler-compat/detector-runtime.d.ts +14 -3
- package/dist/gesture-handler-compat/detector-runtime.js +15 -5
- package/dist/gesture-handler-compat/detector-runtime.js.map +1 -1
- package/dist/gesture-handler-compat/detector.js +57 -2
- package/dist/gesture-handler-compat/detector.js.map +1 -1
- package/dist/gesture-handler-compat/gesture-state-manager.d.ts +5 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js +77 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js.map +1 -0
- package/dist/gesture-handler-compat/index.d.ts +2 -2
- package/dist/gesture-handler-compat/index.js +38 -33
- package/dist/gesture-handler-compat/index.js.map +1 -1
- package/dist/gesture-handler-compat/recognizer.d.ts +12 -1
- package/dist/gesture-handler-compat/recognizer.js +23 -9
- package/dist/gesture-handler-compat/recognizer.js.map +1 -1
- package/dist/gesture-handler-compat/tag-registry.d.ts +14 -0
- package/dist/gesture-handler-compat/tag-registry.js +19 -0
- package/dist/gesture-handler-compat/tag-registry.js.map +1 -0
- package/dist/globals/index.d.ts +47 -0
- package/dist/globals/index.js +181 -0
- package/dist/globals/index.js.map +1 -0
- package/dist/globals/install.d.ts +1 -0
- package/dist/globals/install.js +14 -0
- package/dist/globals/install.js.map +1 -0
- package/dist/gtkx/bridge/adw-namespace.d.ts +2 -0
- package/dist/gtkx/bridge/adw-namespace.js +26 -0
- package/dist/gtkx/bridge/adw-namespace.js.map +1 -0
- package/dist/gtkx/bridge/adw.d.ts +23 -0
- package/dist/gtkx/bridge/adw.js +93 -0
- package/dist/gtkx/bridge/adw.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-parse.d.ts +11 -0
- package/dist/gtkx/bridge/color-scheme-parse.js +42 -0
- package/dist/gtkx/bridge/color-scheme-parse.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-portal.d.ts +24 -0
- package/dist/gtkx/bridge/color-scheme-portal.js +172 -0
- package/dist/gtkx/bridge/color-scheme-portal.js.map +1 -0
- package/dist/gtkx/bridge/core.d.ts +25 -0
- package/dist/gtkx/bridge/core.js +69 -0
- package/dist/gtkx/bridge/core.js.map +1 -0
- package/dist/gtkx/bridge/drag-layer.d.ts +27 -0
- package/dist/gtkx/bridge/drag-layer.js +177 -0
- package/dist/gtkx/bridge/drag-layer.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +1 -27
- package/dist/gtkx/bridge/index.js +12 -71
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.adw.d.ts +1 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js +16 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -0
- package/dist/gtkx/bridge/widgets.generated.d.ts +0 -1
- package/dist/gtkx/bridge/widgets.generated.js +3 -3
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +41 -20
- package/dist/mcp/data/generated.js +46 -20
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/metro/index.d.ts +1 -0
- package/dist/metro/index.js +12 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/sidebar.js +2 -45
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/reanimated-compat/animatable-value.d.ts +158 -0
- package/dist/reanimated-compat/animatable-value.js +316 -0
- package/dist/reanimated-compat/animatable-value.js.map +1 -0
- package/dist/reanimated-compat/animation.d.ts +34 -13
- package/dist/reanimated-compat/animation.js +97 -25
- package/dist/reanimated-compat/animation.js.map +1 -1
- package/dist/reanimated-compat/mutable.js +6 -6
- package/dist/reanimated-compat/mutable.js.map +1 -1
- package/dist/reanimated-compat/updater-animations.js +32 -12
- package/dist/reanimated-compat/updater-animations.js.map +1 -1
- package/dist/runner/host-dev.js +5 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +9 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/vite/index.js +130 -0
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/gtkx/bridge/theme.d.ts +0 -4
- package/dist/gtkx/bridge/theme.js +0 -5
- package/dist/gtkx/bridge/theme.js.map +0 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// The global-environment parity seam: values react-native installs at
|
|
2
|
+
// startup (Libraries/Core/setUp*.js, plus @react-native/js-polyfills, which
|
|
3
|
+
// every Metro RN bundle carries unconditionally) that this platform's own
|
|
4
|
+
// runtime — Node — does not provide natively. Both toolchains resolve
|
|
5
|
+
// `react-native` to this package (see ../aliases/index.ts), and ./install.ts
|
|
6
|
+
// calls every installer below once from the top of ../index.ts, so they run
|
|
7
|
+
// before any app code on the vite dev path AND the Metro/run-linux host
|
|
8
|
+
// alike — the one place both toolchains share. A later
|
|
9
|
+
// requestAnimationFrame/cancelAnimationFrame install belongs in this same
|
|
10
|
+
// file, next to these, not in a second module.
|
|
11
|
+
//
|
|
12
|
+
// Each install is idempotent and guards on the global already existing —
|
|
13
|
+
// exactly RN's own polyfillGlobal discipline
|
|
14
|
+
// (Libraries/Utilities/PolyfillFunctions.js) — so calling an installer
|
|
15
|
+
// twice, or a global already supplied by the toolchain (Metro's
|
|
16
|
+
// error-guard.js beats us to ErrorUtils), is a no-op rather than a clobber.
|
|
17
|
+
//
|
|
18
|
+
// What is deliberately NOT installed here — XMLHttpRequest, FileReader,
|
|
19
|
+
// RN-shaped FormData file entries — is documented in docs/api.md; Node
|
|
20
|
+
// already provides fetch/Blob/File/FormData/URL/AbortController/
|
|
21
|
+
// structuredClone/TextEncoder/atob-btoa/performance/crypto natively, with
|
|
22
|
+
// RN-equivalent behaviour for the pure-JS surface these APIs expose.
|
|
23
|
+
//
|
|
24
|
+
// No import from ../apis/index (the gtkx bridge) on purpose: unit tests
|
|
25
|
+
// import these installers directly, the same way ../apis/*'s create*
|
|
26
|
+
// factories are tested with a mock host instead of the wired singletons.
|
|
27
|
+
// The one installer that needs a real dependency (installAlertGlobal needs
|
|
28
|
+
// Alert.alert) takes it as a parameter instead — ./install.ts supplies the
|
|
29
|
+
// real one.
|
|
30
|
+
const hasGlobal = (name) => typeof globalThis[name] !== "undefined";
|
|
31
|
+
const setGlobal = (name, value) => {
|
|
32
|
+
;
|
|
33
|
+
globalThis[name] = value;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The very first thing react-native's own setup chain does
|
|
37
|
+
* (Libraries/Core/setUpGlobals.js, run before anything else in
|
|
38
|
+
* InitializeCore): `global.window = global` and `global.self = global`.
|
|
39
|
+
* Node has neither. This exists for the same reason RN sets it on a
|
|
40
|
+
* platform with no DOM either — an isomorphic library's
|
|
41
|
+
* `typeof window !== "undefined"` check (often used to mean "not a
|
|
42
|
+
* server/SSR context, safe to run browser-shaped init") should read the
|
|
43
|
+
* same way here as on any other RN platform.
|
|
44
|
+
*/
|
|
45
|
+
export const installWindowAndSelfGlobals = () => {
|
|
46
|
+
if (!hasGlobal("window")) {
|
|
47
|
+
setGlobal("window", globalThis);
|
|
48
|
+
}
|
|
49
|
+
if (!hasGlobal("self")) {
|
|
50
|
+
setGlobal("self", globalThis);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* `navigator.product === "ReactNative"` is the ecosystem's standard
|
|
55
|
+
* environment-detection idiom (react-native's own
|
|
56
|
+
* Libraries/Core/setUpNavigator.js). Node >= 21 already ships a minimal
|
|
57
|
+
* `navigator` (userAgent only, no `product`), so this mirrors RN's own
|
|
58
|
+
* fallback exactly rather than assuming a bare object needs creating.
|
|
59
|
+
*/
|
|
60
|
+
export const installNavigatorProductGlobal = () => {
|
|
61
|
+
const nav = globalThis.navigator;
|
|
62
|
+
if (nav === undefined) {
|
|
63
|
+
setGlobal("navigator", { product: "ReactNative" });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (nav.product !== "ReactNative") {
|
|
67
|
+
Object.defineProperty(nav, "product", {
|
|
68
|
+
value: "ReactNative",
|
|
69
|
+
writable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
enumerable: true,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* RN's real requestIdleCallback (Libraries/Core/setUpTimers.js) is a
|
|
77
|
+
* TurboModule with true native idle scheduling. Node has none — this is
|
|
78
|
+
* the standard web-fallback shape (the shape every "requestidlecallback
|
|
79
|
+
* polyfill" package on npm uses): fire on the next macrotask, report a
|
|
80
|
+
* fixed 50ms budget, never report a timeout. Good enough for "runs off
|
|
81
|
+
* this tick, eventually" — not a scheduling primitive. See docs/api.md.
|
|
82
|
+
*/
|
|
83
|
+
export const installIdleCallbackGlobals = () => {
|
|
84
|
+
if (!hasGlobal("requestIdleCallback")) {
|
|
85
|
+
setGlobal("requestIdleCallback", (callback, options) => {
|
|
86
|
+
const start = Date.now();
|
|
87
|
+
return setTimeout(() => {
|
|
88
|
+
callback({
|
|
89
|
+
didTimeout: false,
|
|
90
|
+
timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),
|
|
91
|
+
});
|
|
92
|
+
}, options?.timeout ?? 1);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (!hasGlobal("cancelIdleCallback")) {
|
|
96
|
+
setGlobal("cancelIdleCallback", (handle) => {
|
|
97
|
+
clearTimeout(handle);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* RN's global.alert (Libraries/Core/setUpAlert.js) forwards to
|
|
103
|
+
* Alert.alert('Alert', text). Dependency-injected so unit tests exercise it
|
|
104
|
+
* without pulling in the gtkx bridge (see ../apis/index.ts) — installGlobals
|
|
105
|
+
* below does the real wiring.
|
|
106
|
+
*/
|
|
107
|
+
export const installAlertGlobal = (alert) => {
|
|
108
|
+
if (hasGlobal("alert")) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
setGlobal("alert", (text) => {
|
|
112
|
+
alert("Alert", String(text));
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* RN's own setUpErrorHandling.js requires `global.ErrorUtils` to already
|
|
117
|
+
* exist — on a real RN app it does, installed by @react-native/js-polyfills'
|
|
118
|
+
* error-guard.js, which @react-native/metro-config's getDefaultConfig
|
|
119
|
+
* prepends to EVERY Metro bundle unconditionally (independent of, and not
|
|
120
|
+
* disabled by, this platform's own `getModulesRunBeforeMainModule: () =>
|
|
121
|
+
* []`). Confirmed empirically (rn-globals-audit): the Metro/run-linux host
|
|
122
|
+
* already has ErrorUtils, the vite path does not — this closes that gap so
|
|
123
|
+
* both toolchains agree. A faithful port of the real polyfill: a default
|
|
124
|
+
* handler that rethrows, exactly react-native's un-hooked behaviour.
|
|
125
|
+
*/
|
|
126
|
+
export const installErrorUtilsGlobal = () => {
|
|
127
|
+
if (hasGlobal("ErrorUtils")) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let inGuard = 0;
|
|
131
|
+
let globalHandler = (error) => {
|
|
132
|
+
throw error;
|
|
133
|
+
};
|
|
134
|
+
const ErrorUtils = {
|
|
135
|
+
setGlobalHandler(handler) {
|
|
136
|
+
globalHandler = handler;
|
|
137
|
+
},
|
|
138
|
+
getGlobalHandler() {
|
|
139
|
+
return globalHandler;
|
|
140
|
+
},
|
|
141
|
+
reportError(error) {
|
|
142
|
+
globalHandler(error, false);
|
|
143
|
+
},
|
|
144
|
+
reportFatalError(error) {
|
|
145
|
+
globalHandler(error, true);
|
|
146
|
+
},
|
|
147
|
+
applyWithGuard(fn, context, args) {
|
|
148
|
+
try {
|
|
149
|
+
inGuard += 1;
|
|
150
|
+
return fn.apply(context, args ?? []);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
ErrorUtils.reportError(error);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
inGuard -= 1;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
applyWithGuardIfNeeded(fn, context, args) {
|
|
161
|
+
if (ErrorUtils.inGuard()) {
|
|
162
|
+
return fn.apply(context, args ?? []);
|
|
163
|
+
}
|
|
164
|
+
return ErrorUtils.applyWithGuard(fn, context, args);
|
|
165
|
+
},
|
|
166
|
+
inGuard() {
|
|
167
|
+
return inGuard > 0;
|
|
168
|
+
},
|
|
169
|
+
// react-native's real `guard` also takes a debugging `name` label; this
|
|
170
|
+
// port has no crash reporter to hand it to, so it is dropped rather than
|
|
171
|
+
// kept and ignored.
|
|
172
|
+
guard(fn) {
|
|
173
|
+
if (typeof fn !== "function") {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return (...args) => ErrorUtils.applyWithGuard(fn, undefined, args);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
setGlobal("ErrorUtils", ErrorUtils);
|
|
180
|
+
};
|
|
181
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/globals/index.ts"],"names":[],"mappings":"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","sourcesContent":["// The global-environment parity seam: values react-native installs at\n// startup (Libraries/Core/setUp*.js, plus @react-native/js-polyfills, which\n// every Metro RN bundle carries unconditionally) that this platform's own\n// runtime — Node — does not provide natively. Both toolchains resolve\n// `react-native` to this package (see ../aliases/index.ts), and ./install.ts\n// calls every installer below once from the top of ../index.ts, so they run\n// before any app code on the vite dev path AND the Metro/run-linux host\n// alike — the one place both toolchains share. A later\n// requestAnimationFrame/cancelAnimationFrame install belongs in this same\n// file, next to these, not in a second module.\n//\n// Each install is idempotent and guards on the global already existing —\n// exactly RN's own polyfillGlobal discipline\n// (Libraries/Utilities/PolyfillFunctions.js) — so calling an installer\n// twice, or a global already supplied by the toolchain (Metro's\n// error-guard.js beats us to ErrorUtils), is a no-op rather than a clobber.\n//\n// What is deliberately NOT installed here — XMLHttpRequest, FileReader,\n// RN-shaped FormData file entries — is documented in docs/api.md; Node\n// already provides fetch/Blob/File/FormData/URL/AbortController/\n// structuredClone/TextEncoder/atob-btoa/performance/crypto natively, with\n// RN-equivalent behaviour for the pure-JS surface these APIs expose.\n//\n// No import from ../apis/index (the gtkx bridge) on purpose: unit tests\n// import these installers directly, the same way ../apis/*'s create*\n// factories are tested with a mock host instead of the wired singletons.\n// The one installer that needs a real dependency (installAlertGlobal needs\n// Alert.alert) takes it as a parameter instead — ./install.ts supplies the\n// real one.\n\ntype IdleDeadline = { didTimeout: boolean; timeRemaining: () => number }\ntype RequestIdleCallbackOptions = { timeout?: number }\ntype IdleCallbackHandle = ReturnType<typeof setTimeout>\ntype ErrorHandler = (error: unknown, isFatal: boolean) => void\n\nconst hasGlobal = (name: string): boolean =>\n typeof (globalThis as Record<string, unknown>)[name] !== \"undefined\"\n\nconst setGlobal = (name: string, value: unknown): void => {\n ;(globalThis as Record<string, unknown>)[name] = value\n}\n\n/**\n * The very first thing react-native's own setup chain does\n * (Libraries/Core/setUpGlobals.js, run before anything else in\n * InitializeCore): `global.window = global` and `global.self = global`.\n * Node has neither. This exists for the same reason RN sets it on a\n * platform with no DOM either — an isomorphic library's\n * `typeof window !== \"undefined\"` check (often used to mean \"not a\n * server/SSR context, safe to run browser-shaped init\") should read the\n * same way here as on any other RN platform.\n */\nexport const installWindowAndSelfGlobals = (): void => {\n if (!hasGlobal(\"window\")) {\n setGlobal(\"window\", globalThis)\n }\n if (!hasGlobal(\"self\")) {\n setGlobal(\"self\", globalThis)\n }\n}\n\n/**\n * `navigator.product === \"ReactNative\"` is the ecosystem's standard\n * environment-detection idiom (react-native's own\n * Libraries/Core/setUpNavigator.js). Node >= 21 already ships a minimal\n * `navigator` (userAgent only, no `product`), so this mirrors RN's own\n * fallback exactly rather than assuming a bare object needs creating.\n */\nexport const installNavigatorProductGlobal = (): void => {\n const nav = (globalThis as Record<string, unknown>).navigator as\n Record<string, unknown> | undefined\n if (nav === undefined) {\n setGlobal(\"navigator\", { product: \"ReactNative\" })\n return\n }\n if (nav.product !== \"ReactNative\") {\n Object.defineProperty(nav, \"product\", {\n value: \"ReactNative\",\n writable: true,\n configurable: true,\n enumerable: true,\n })\n }\n}\n\n/**\n * RN's real requestIdleCallback (Libraries/Core/setUpTimers.js) is a\n * TurboModule with true native idle scheduling. Node has none — this is\n * the standard web-fallback shape (the shape every \"requestidlecallback\n * polyfill\" package on npm uses): fire on the next macrotask, report a\n * fixed 50ms budget, never report a timeout. Good enough for \"runs off\n * this tick, eventually\" — not a scheduling primitive. See docs/api.md.\n */\nexport const installIdleCallbackGlobals = (): void => {\n if (!hasGlobal(\"requestIdleCallback\")) {\n setGlobal(\n \"requestIdleCallback\",\n (\n callback: (deadline: IdleDeadline) => void,\n options?: RequestIdleCallbackOptions,\n ): IdleCallbackHandle => {\n const start = Date.now()\n return setTimeout(() => {\n callback({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),\n })\n }, options?.timeout ?? 1)\n },\n )\n }\n if (!hasGlobal(\"cancelIdleCallback\")) {\n setGlobal(\"cancelIdleCallback\", (handle: IdleCallbackHandle): void => {\n clearTimeout(handle)\n })\n }\n}\n\n/**\n * RN's global.alert (Libraries/Core/setUpAlert.js) forwards to\n * Alert.alert('Alert', text). Dependency-injected so unit tests exercise it\n * without pulling in the gtkx bridge (see ../apis/index.ts) — installGlobals\n * below does the real wiring.\n */\nexport const installAlertGlobal = (\n alert: (title: string, message?: string) => void,\n): void => {\n if (hasGlobal(\"alert\")) {\n return\n }\n setGlobal(\"alert\", (text: unknown): void => {\n alert(\"Alert\", String(text))\n })\n}\n\n/**\n * RN's own setUpErrorHandling.js requires `global.ErrorUtils` to already\n * exist — on a real RN app it does, installed by @react-native/js-polyfills'\n * error-guard.js, which @react-native/metro-config's getDefaultConfig\n * prepends to EVERY Metro bundle unconditionally (independent of, and not\n * disabled by, this platform's own `getModulesRunBeforeMainModule: () =>\n * []`). Confirmed empirically (rn-globals-audit): the Metro/run-linux host\n * already has ErrorUtils, the vite path does not — this closes that gap so\n * both toolchains agree. A faithful port of the real polyfill: a default\n * handler that rethrows, exactly react-native's un-hooked behaviour.\n */\nexport const installErrorUtilsGlobal = (): void => {\n if (hasGlobal(\"ErrorUtils\")) {\n return\n }\n let inGuard = 0\n let globalHandler: ErrorHandler = (error) => {\n throw error\n }\n // Mirrors react-native's own loose `Fn<Args, Return> = (...Args) => Return`\n // (Libraries/vendor/core/ErrorUtils.js): a guard wraps ANY function.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type AnyFn = (...args: any[]) => unknown\n const ErrorUtils = {\n setGlobalHandler(handler: ErrorHandler): void {\n globalHandler = handler\n },\n getGlobalHandler(): ErrorHandler {\n return globalHandler\n },\n reportError(error: unknown): void {\n globalHandler(error, false)\n },\n reportFatalError(error: unknown): void {\n globalHandler(error, true)\n },\n applyWithGuard<TFn extends AnyFn>(\n fn: TFn,\n context?: unknown,\n args?: Parameters<TFn>,\n ): ReturnType<TFn> | null {\n try {\n inGuard += 1\n return fn.apply(context, args ?? []) as ReturnType<TFn>\n } catch (error) {\n ErrorUtils.reportError(error)\n return null\n } finally {\n inGuard -= 1\n }\n },\n applyWithGuardIfNeeded<TFn extends AnyFn>(\n fn: TFn,\n context?: unknown,\n args?: Parameters<TFn>,\n ): ReturnType<TFn> | null {\n if (ErrorUtils.inGuard()) {\n return fn.apply(context, args ?? []) as ReturnType<TFn>\n }\n return ErrorUtils.applyWithGuard(fn, context, args)\n },\n inGuard(): boolean {\n return inGuard > 0\n },\n // react-native's real `guard` also takes a debugging `name` label; this\n // port has no crash reporter to hand it to, so it is dropped rather than\n // kept and ignored.\n guard<TFn extends AnyFn>(\n fn: TFn,\n ): ((...args: Parameters<TFn>) => ReturnType<TFn> | null) | null {\n if (typeof fn !== \"function\") {\n return null\n }\n return (...args: Parameters<TFn>): ReturnType<TFn> | null =>\n ErrorUtils.applyWithGuard(fn, undefined, args)\n },\n }\n setGlobal(\"ErrorUtils\", ErrorUtils)\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const installGlobals: () => void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// The real wiring for ./index.ts's installers: pulled in only from the top
|
|
2
|
+
// of ../index.ts, never from a unit test, exactly the split ../apis/index.ts
|
|
3
|
+
// draws between its create* factories (mock-host-testable) and itself (the
|
|
4
|
+
// file that binds them to gtkxHost, "it pulls in the gtkx bridge").
|
|
5
|
+
import { Alert } from "../apis/index";
|
|
6
|
+
import { installAlertGlobal, installErrorUtilsGlobal, installIdleCallbackGlobals, installNavigatorProductGlobal, installWindowAndSelfGlobals, } from "./index";
|
|
7
|
+
export const installGlobals = () => {
|
|
8
|
+
installWindowAndSelfGlobals();
|
|
9
|
+
installNavigatorProductGlobal();
|
|
10
|
+
installIdleCallbackGlobals();
|
|
11
|
+
installErrorUtilsGlobal();
|
|
12
|
+
installAlertGlobal(Alert.alert);
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/globals/install.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,oEAAoE;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,SAAS,CAAA;AAEhB,MAAM,CAAC,MAAM,cAAc,GAAG,GAAS,EAAE;IACvC,2BAA2B,EAAE,CAAA;IAC7B,6BAA6B,EAAE,CAAA;IAC/B,0BAA0B,EAAE,CAAA;IAC5B,uBAAuB,EAAE,CAAA;IACzB,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AACjC,CAAC,CAAA","sourcesContent":["// The real wiring for ./index.ts's installers: pulled in only from the top\n// of ../index.ts, never from a unit test, exactly the split ../apis/index.ts\n// draws between its create* factories (mock-host-testable) and itself (the\n// file that binds them to gtkxHost, \"it pulls in the gtkx bridge\").\nimport { Alert } from \"../apis/index\"\nimport {\n installAlertGlobal,\n installErrorUtilsGlobal,\n installIdleCallbackGlobals,\n installNavigatorProductGlobal,\n installWindowAndSelfGlobals,\n} from \"./index\"\n\nexport const installGlobals = (): void => {\n installWindowAndSelfGlobals()\n installNavigatorProductGlobal()\n installIdleCallbackGlobals()\n installErrorUtilsGlobal()\n installAlertGlobal(Alert.alert)\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// A plain, EAGER, static re-export of the real `@gtkx/gi/adw` namespace —
|
|
2
|
+
// deliberately NOT behind adw.ts's probe.
|
|
3
|
+
//
|
|
4
|
+
// Why this needs to exist separately from adw.ts: react-native-gtkx/adw's
|
|
5
|
+
// public `Adw` export (toast.tsx, window.tsx and adwaita-stack.tsx in the
|
|
6
|
+
// examples all import it as a plain named value off that one subpath, then
|
|
7
|
+
// use it BOTH as a value — `Adw.Toast.new(title)` — and as a namespace in
|
|
8
|
+
// type position — `Ref<Adw.ToastOverlay | null>`) needs that same duality
|
|
9
|
+
// gtkx's own `import * as Adw from "@gtkx/gi/adw"` gives for free. That
|
|
10
|
+
// duality is a property of the IMPORT STATEMENT itself: TypeScript only
|
|
11
|
+
// treats an identifier as usable in both spaces when it traces back to an
|
|
12
|
+
// actual namespace import, forwarded by a bare re-export (`export { X }
|
|
13
|
+
// from` a specifier) — introduce a local binding anywhere in the chain (an
|
|
14
|
+
// `import type` alongside a `const`, or a value synthesized by calling a
|
|
15
|
+
// function) and the type half is lost, which is exactly what broke when
|
|
16
|
+
// adw/index.ts first tried to build `Adw` from adw.ts's requireAdwGi().
|
|
17
|
+
//
|
|
18
|
+
// So this file exists purely to be the START of that re-export chain, and
|
|
19
|
+
// nothing imports it except adw/index.ts (and only for this one name) —
|
|
20
|
+
// never gtkx/bridge/core.ts, app-registry.tsx or host.gtkx.ts, which is
|
|
21
|
+
// exactly what keeps the plain-GTK profile buildable: those three reach for
|
|
22
|
+
// adw.ts's LAZY probe instead, and this file's static import never enters
|
|
23
|
+
// their module graph.
|
|
24
|
+
import * as Adw from "@gtkx/gi/adw";
|
|
25
|
+
export { Adw };
|
|
26
|
+
//# sourceMappingURL=adw-namespace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adw-namespace.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/adw-namespace.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0CAA0C;AAC1C,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AACxE,2EAA2E;AAC3E,yEAAyE;AACzE,wEAAwE;AACxE,wEAAwE;AACxE,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,4EAA4E;AAC5E,0EAA0E;AAC1E,sBAAsB;AACtB,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AAEnC,OAAO,EAAE,GAAG,EAAE,CAAA","sourcesContent":["// A plain, EAGER, static re-export of the real `@gtkx/gi/adw` namespace —\n// deliberately NOT behind adw.ts's probe.\n//\n// Why this needs to exist separately from adw.ts: react-native-gtkx/adw's\n// public `Adw` export (toast.tsx, window.tsx and adwaita-stack.tsx in the\n// examples all import it as a plain named value off that one subpath, then\n// use it BOTH as a value — `Adw.Toast.new(title)` — and as a namespace in\n// type position — `Ref<Adw.ToastOverlay | null>`) needs that same duality\n// gtkx's own `import * as Adw from \"@gtkx/gi/adw\"` gives for free. That\n// duality is a property of the IMPORT STATEMENT itself: TypeScript only\n// treats an identifier as usable in both spaces when it traces back to an\n// actual namespace import, forwarded by a bare re-export (`export { X }\n// from` a specifier) — introduce a local binding anywhere in the chain (an\n// `import type` alongside a `const`, or a value synthesized by calling a\n// function) and the type half is lost, which is exactly what broke when\n// adw/index.ts first tried to build `Adw` from adw.ts's requireAdwGi().\n//\n// So this file exists purely to be the START of that re-export chain, and\n// nothing imports it except adw/index.ts (and only for this one name) —\n// never gtkx/bridge/core.ts, app-registry.tsx or host.gtkx.ts, which is\n// exactly what keeps the plain-GTK profile buildable: those three reach for\n// adw.ts's LAZY probe instead, and this file's static import never enters\n// their module graph.\nimport * as Adw from \"@gtkx/gi/adw\"\n\nexport { Adw }\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type * as AdwGi from "@gtkx/gi/adw";
|
|
2
|
+
import type * as AdwJsx from "@gtkx/jsx/adw";
|
|
3
|
+
/**
|
|
4
|
+
* True when this app's codegen store actually generated Adwaita bindings —
|
|
5
|
+
* i.e. `"Adw-1"` is in this app's gtkx.config.ts `libraries`. Synchronous
|
|
6
|
+
* from every caller's point of view on the Metro/SEA host and the
|
|
7
|
+
* plain-GTK profile (both resolved above with nothing to await); on vite
|
|
8
|
+
* build/dev and vitest this reads whatever the fire-and-forget probe above
|
|
9
|
+
* has settled to by the time it is called — see the module doc for why
|
|
10
|
+
* that is reliably "already settled" for every real call site in this
|
|
11
|
+
* package, not a formal guarantee.
|
|
12
|
+
*/
|
|
13
|
+
export declare const adwAvailable: () => boolean;
|
|
14
|
+
/** The real `@gtkx/gi/adw` namespace (enums, GObject classes — `Adw.StyleManager`,
|
|
15
|
+
* `Adw.ColorScheme`, `Adw.ResponseAppearance`, ...), or the loud throw above. */
|
|
16
|
+
export declare const requireAdwGi: (feature: string) => typeof AdwGi;
|
|
17
|
+
/** The real `@gtkx/jsx/adw` namespace (raw JSX elements — `AdwApplicationWindow`,
|
|
18
|
+
* `AdwNavigationView`, `AdwNavigationPage`, ...), or the loud throw above. */
|
|
19
|
+
export declare const requireAdwJsx: (feature: string) => typeof AdwJsx;
|
|
20
|
+
export type { AdwGi as Adw };
|
|
21
|
+
export type ColorScheme = "light" | "dark";
|
|
22
|
+
export declare const styleManager: () => AdwGi.StyleManager;
|
|
23
|
+
export declare const colorScheme: () => ColorScheme;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Assembled rather than written as one literal: used only as a lookup key
|
|
2
|
+
// into global.__hostModules (an object property read, not an import), which
|
|
3
|
+
// the run-linux host populates keyed by these exact strings
|
|
4
|
+
// (HOST_MODULE_EXTERNALS in src/metro/index.ts). Kept opaque to a plain text
|
|
5
|
+
// search for consistency with the rest of this file, not because anything
|
|
6
|
+
// would fail resolving it — fromHostModules never imports.
|
|
7
|
+
const GI_ADW = ["@gtkx", "gi", "adw"].join("/");
|
|
8
|
+
const JSX_ADW = ["@gtkx", "jsx", "adw"].join("/");
|
|
9
|
+
// The run-linux (Metro) host already resolved every external — including
|
|
10
|
+
// these two, when the app declared "Adw-1" — into global.__hostModules
|
|
11
|
+
// BEFORE the bundle ever runs (see src/runner/host.ts) — synchronously, as
|
|
12
|
+
// far as this module is concerned, and the authoritative answer on that
|
|
13
|
+
// toolchain: reading it directly is both correct and avoids a second,
|
|
14
|
+
// redundant resolution attempt Metro's bundler cannot serve anyway (no
|
|
15
|
+
// "virtual:gtkx-config" loader hook outside Vite's own pipeline or the
|
|
16
|
+
// host's own module.registerHooks).
|
|
17
|
+
// Returns undefined (not false) when there is no host indirection in play
|
|
18
|
+
// at all — the vite dev/build toolchain, where every spike and example in
|
|
19
|
+
// this repo runs — so the caller knows to fall through to the real probe.
|
|
20
|
+
const fromHostModules = () => {
|
|
21
|
+
const hostModules = globalThis.__hostModules;
|
|
22
|
+
if (!hostModules) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const gi = hostModules[GI_ADW];
|
|
26
|
+
const jsx = hostModules[JSX_ADW];
|
|
27
|
+
return gi && jsx
|
|
28
|
+
? { gi: gi, jsx: jsx }
|
|
29
|
+
: null;
|
|
30
|
+
};
|
|
31
|
+
const hostModulesResult = fromHostModules();
|
|
32
|
+
let cached;
|
|
33
|
+
if (hostModulesResult !== undefined) {
|
|
34
|
+
cached = hostModulesResult;
|
|
35
|
+
}
|
|
36
|
+
else if (typeof __GTKX_ADW_AVAILABLE__ !== "undefined" &&
|
|
37
|
+
!__GTKX_ADW_AVAILABLE__) {
|
|
38
|
+
cached = null;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Fire-and-forget — see the module doc above for why this can no longer
|
|
42
|
+
// be a module-scope `await` and why starting it here, unconditionally,
|
|
43
|
+
// still settles in time for every real caller.
|
|
44
|
+
Promise.all([import("@gtkx/gi/adw"), import("@gtkx/jsx/adw")])
|
|
45
|
+
.then(([gi, jsx]) => {
|
|
46
|
+
cached = { gi: gi, jsx: jsx };
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {
|
|
49
|
+
cached = null;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* True when this app's codegen store actually generated Adwaita bindings —
|
|
54
|
+
* i.e. `"Adw-1"` is in this app's gtkx.config.ts `libraries`. Synchronous
|
|
55
|
+
* from every caller's point of view on the Metro/SEA host and the
|
|
56
|
+
* plain-GTK profile (both resolved above with nothing to await); on vite
|
|
57
|
+
* build/dev and vitest this reads whatever the fire-and-forget probe above
|
|
58
|
+
* has settled to by the time it is called — see the module doc for why
|
|
59
|
+
* that is reliably "already settled" for every real call site in this
|
|
60
|
+
* package, not a formal guarantee.
|
|
61
|
+
*/
|
|
62
|
+
export const adwAvailable = () => cached !== null && cached !== undefined;
|
|
63
|
+
const NOT_AVAILABLE = ' requires "Adw-1" in this app\'s gtkx.config.ts `libraries` — see ' +
|
|
64
|
+
"docs/api.md (the plain-GTK profile) for what needs Adw unconditionally " +
|
|
65
|
+
"and what falls back without it.";
|
|
66
|
+
/** The repo's loud named throw for a feature that reached for Adw and found
|
|
67
|
+
* none. Every Adw-dependent call site in the seam funnels through this, but
|
|
68
|
+
* most no longer reach it in practice: Alert, Appearance and chrome:
|
|
69
|
+
* "content" all check `adwAvailable()` themselves first and take a
|
|
70
|
+
* plain-GTK fallback instead (see .claude/epics/adw-optional/002.md,
|
|
71
|
+
* 003.md, 004.md) — this throw is what is left for the features with no
|
|
72
|
+
* fallback at all (NavigationStack, react-native-gtkx/adw's own
|
|
73
|
+
* AdwBreakpoint family). */
|
|
74
|
+
const requireAdw = (feature) => {
|
|
75
|
+
if (!cached) {
|
|
76
|
+
throw new Error(`[react-native-gtkx] ${feature}${NOT_AVAILABLE}`);
|
|
77
|
+
}
|
|
78
|
+
return cached;
|
|
79
|
+
};
|
|
80
|
+
/** The real `@gtkx/gi/adw` namespace (enums, GObject classes — `Adw.StyleManager`,
|
|
81
|
+
* `Adw.ColorScheme`, `Adw.ResponseAppearance`, ...), or the loud throw above. */
|
|
82
|
+
export const requireAdwGi = (feature) => requireAdw(feature).gi;
|
|
83
|
+
/** The real `@gtkx/jsx/adw` namespace (raw JSX elements — `AdwApplicationWindow`,
|
|
84
|
+
* `AdwNavigationView`, `AdwNavigationPage`, ...), or the loud throw above. */
|
|
85
|
+
export const requireAdwJsx = (feature) => requireAdw(feature).jsx;
|
|
86
|
+
// Folded in from the pre-seam ./theme.ts: both are pure Adw, both were only
|
|
87
|
+
// ever called from apis/host.gtkx.ts, and keeping them as their own
|
|
88
|
+
// (formerly eagerly-imported) file would just move the eager `import * as
|
|
89
|
+
// Adw` problem one hop over instead of removing it.
|
|
90
|
+
export const styleManager = () => requireAdwGi("Appearance").StyleManager.getDefault();
|
|
91
|
+
// The Appearance API subscribes to notify::dark on the style manager.
|
|
92
|
+
export const colorScheme = () => styleManager().getDark() ? "dark" : "light";
|
|
93
|
+
//# sourceMappingURL=adw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adw.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/adw.ts"],"names":[],"mappings":"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","sourcesContent":["// The ONLY module allowed to import @gtkx/gi/adw and @gtkx/jsx/adw — see\n// .claude/epics/adw-optional/001.md. Everything the RN core needs lives in\n// ./core instead, with zero Adw imports, so an app whose gtkx.config.ts\n// declares `libraries: [\"Gtk-4.0\"]` alone (no \"Adw-1\") never reaches either\n// specifier: a codegen store generated that way has no \"./adw\" entry in\n// either package's `exports` map AT ALL (empirically verified: neither\n// directory even exists on disk), so a STATIC import of either one fails\n// the whole build — `gtkx build`'s own \"gtkx:undeclared-library\" vite\n// plugin throws while resolving it, unconditionally, whether or not the\n// import is ever reached at runtime.\n//\n// __GTKX_ADW_AVAILABLE__ (declared ambient just below) is a build-time\n// constant the vite preset injects (src/vite/index.ts, see its own doc for\n// the full story — .claude/epics/adw-optional/006.md is the regression this\n// exists to fix): true only when THIS app's codegen store actually has an\n// \"adw\" entry. The probe below guards on it and, only inside that guard,\n// imports @gtkx/gi/adw and @gtkx/jsx/adw as LITERAL specifiers —\n// deliberately, unlike a runtime-assembled string. A hidden specifier is\n// invisible to Rollup's build graph, which sounds like the safer choice,\n// but it is ALSO invisible to everything that graph gives every OTHER gtkx\n// import for free: `resolve.dedupe` (RC4-WORKAROUND(runtime-dedupe) in\n// docs/gtkx-rc4-notes.md) and `gtkx build`'s own asset pipeline for the\n// native addon (`@gtkx/cli`'s `gtkx:native` plugin, which rewrites every\n// STATICALLY reachable `@gtkx/native` import onto the single `dist/gtkx.node`\n// it emits) — a hidden specifier resolves a SECOND, independent copy from\n// node_modules at runtime instead, and two distinct native addons\n// double-initialize the gtkx runtime and abort the process\n// (`g_log_set_writer_func() called multiple times`, proven with a core dump\n// showing both `.node` files mapped into one process). A literal specifier\n// gets the exact same static treatment as every other gtkx import; when the\n// build-time constant folds to `false` instead, dead-code elimination\n// removes the whole guarded body — literal specifiers included — before\n// Rollup's graph walk ever starts, so `gtkx:undeclared-library` never\n// throws on the plain-GTK profile either.\n//\n// No module-scope await, anywhere — the one thing that actually changed\n// here (.claude/epics/adw-optional/007-sea-tla.md): this file used to\n// resolve Adw with a TOP-LEVEL AWAIT (both the #132 guarded literal imports\n// and the fallback probe), so every call site below could stay synchronous\n// without an explicit wait of its own. That broke `react-native\n// build-linux` outright — reproduced empirically in the VM, WITH OR\n// WITHOUT --sea/--standalone (all three share the same Metro `bundle()`\n// call, `--dev false`): Metro's own minifier (metro-minify-terser, i.e.\n// terser) parses each module's compiled factory as a plain SCRIPT, not an\n// ES module — `await` is just an ordinary identifier there, not a keyword,\n// so \"await probeViaDynamicImport()\" is a hard syntax error (\"Unexpected\n// token: name (probeViaDynamicImport)\"), independent of the SEA/rolldown\n// CJS-format restriction the task file also names (CJS has no TLA either —\n// the same file would have failed a second time even past Metro).\n//\n// Tried and reverted: resolving gi through a synchronous `require()` (via\n// `createRequire(import.meta.url)`) instead of a dynamic import — it does\n// answer synchronously under vite build/dev and vitest, but\n// `import.meta.url` is ITSELF ESM-only syntax, exactly like top-level\n// await: Metro's minifier happened not to reject it at minify time (unlike\n// `await`), but Node's own parser does, the moment the Metro/SEA host\n// actually executes the bundle via `vm.runInThisContext` (which runs the\n// whole bundle as a plain SCRIPT, not a module) — \"SyntaxError: Cannot use\n// 'import.meta' outside a module\", reproduced building and running\n// examples/hn-app's SEA artifact. Same class of bug as the TLA one this\n// file exists to fix, so back to a dynamic import for both gi and jsx —\n// their own literal specifiers, unlike ours, are genuinely fine either way\n// since Metro/the SEA host never reach this branch at all (see\n// fromHostModules below) and every other toolchain is real ESM.\n//\n// So: a single, ordinary (non-async, non-top-level-await) function starts\n// BOTH imports together — Promise.all, exactly like the old probe — the\n// moment this module itself finishes evaluating, fire-and-forget (no\n// module-scope await to block on this time). No formal ordering guarantee\n// versus the old TLA probe's, but starting it as early as this module can\n// (not lazily, on the first actual `requireAdwJsx()`/`requireAdwGi()` call)\n// gives it a real, if informal, chance to have already settled — verified\n// against this package's own gtk test suite, including the one caller with\n// the least slack of all: react-native-gtkx/adw's own `adw/index.ts` calls\n// `requireAdwJsx()` eagerly, at ITS OWN module scope, the moment it is\n// imported (immediately after this file). Under Vite's SSR module runner\n// (dev and vitest both), every static import a transformed module has is\n// itself an awaited call in that runner, cached or not — so `adw/index.ts`\n// evaluating is always at least one module-boundary await after this\n// file's own module body, including the Promise.all it starts, finished\n// running. tests/gtk/bridge/auxiliary-elements.gtk.test.tsx (imports\n// react-native-gtkx/adw) and tests/gtk/adw/* are all green under this.\n//\n// The Metro/SEA host fast path (fromHostModules, below) takes priority over\n// all of this and never touches import() at all: `global.__hostModules` is\n// already populated — both gi and jsx together, when present — before the\n// run-linux host or the SEA entry's own async wrapper (src/sea/bundle.ts's\n// `buildEntrySource`, itself already just an ordinary async FUNCTION, never\n// a module-scope await) ever executes the bundle that reaches this file.\nimport type * as AdwGi from \"@gtkx/gi/adw\"\nimport type * as AdwJsx from \"@gtkx/jsx/adw\"\n\n// Assembled rather than written as one literal: used only as a lookup key\n// into global.__hostModules (an object property read, not an import), which\n// the run-linux host populates keyed by these exact strings\n// (HOST_MODULE_EXTERNALS in src/metro/index.ts). Kept opaque to a plain text\n// search for consistency with the rest of this file, not because anything\n// would fail resolving it — fromHostModules never imports.\nconst GI_ADW = [\"@gtkx\", \"gi\", \"adw\"].join(\"/\")\nconst JSX_ADW = [\"@gtkx\", \"jsx\", \"adw\"].join(\"/\")\n\ntype AdwModules = {\n gi: typeof AdwGi\n jsx: typeof AdwJsx\n}\n\ntype HostModulesGlobal = typeof globalThis & {\n __hostModules?: Record<string, unknown>\n}\n\n// The run-linux (Metro) host already resolved every external — including\n// these two, when the app declared \"Adw-1\" — into global.__hostModules\n// BEFORE the bundle ever runs (see src/runner/host.ts) — synchronously, as\n// far as this module is concerned, and the authoritative answer on that\n// toolchain: reading it directly is both correct and avoids a second,\n// redundant resolution attempt Metro's bundler cannot serve anyway (no\n// \"virtual:gtkx-config\" loader hook outside Vite's own pipeline or the\n// host's own module.registerHooks).\n// Returns undefined (not false) when there is no host indirection in play\n// at all — the vite dev/build toolchain, where every spike and example in\n// this repo runs — so the caller knows to fall through to the real probe.\nconst fromHostModules = (): AdwModules | null | undefined => {\n const hostModules = (globalThis as HostModulesGlobal).__hostModules\n if (!hostModules) {\n return undefined\n }\n const gi = hostModules[GI_ADW]\n const jsx = hostModules[JSX_ADW]\n return gi && jsx\n ? { gi: gi as typeof AdwGi, jsx: jsx as typeof AdwJsx }\n : null\n}\n\nconst hostModulesResult = fromHostModules()\n\n// Skips the probe outright when the vite preset has POSITIVELY told us Adw\n// is not in this app's codegen store (see the module doc above for why a\n// literal specifier below needs this guard to exist at all). `typeof`, not\n// a direct reference: safe on any toolchain that never defines the constant\n// (a bare `vitest` \"unit\" project, or a consumer not using our preset) —\n// reads as \"undefined\", so the guard is skipped and the probe runs for\n// real. Only an explicit `false` short-circuits.\ndeclare const __GTKX_ADW_AVAILABLE__: boolean | undefined\n\nlet cached: AdwModules | null | undefined\n\nif (hostModulesResult !== undefined) {\n cached = hostModulesResult\n} else if (\n typeof __GTKX_ADW_AVAILABLE__ !== \"undefined\" &&\n !__GTKX_ADW_AVAILABLE__\n) {\n cached = null\n} else {\n // Fire-and-forget — see the module doc above for why this can no longer\n // be a module-scope `await` and why starting it here, unconditionally,\n // still settles in time for every real caller.\n Promise.all([import(\"@gtkx/gi/adw\"), import(\"@gtkx/jsx/adw\")])\n .then(([gi, jsx]) => {\n cached = { gi: gi as typeof AdwGi, jsx: jsx as typeof AdwJsx }\n })\n .catch(() => {\n cached = null\n })\n}\n\n/**\n * True when this app's codegen store actually generated Adwaita bindings —\n * i.e. `\"Adw-1\"` is in this app's gtkx.config.ts `libraries`. Synchronous\n * from every caller's point of view on the Metro/SEA host and the\n * plain-GTK profile (both resolved above with nothing to await); on vite\n * build/dev and vitest this reads whatever the fire-and-forget probe above\n * has settled to by the time it is called — see the module doc for why\n * that is reliably \"already settled\" for every real call site in this\n * package, not a formal guarantee.\n */\nexport const adwAvailable = (): boolean =>\n cached !== null && cached !== undefined\n\nconst NOT_AVAILABLE =\n ' requires \"Adw-1\" in this app\\'s gtkx.config.ts `libraries` — see ' +\n \"docs/api.md (the plain-GTK profile) for what needs Adw unconditionally \" +\n \"and what falls back without it.\"\n\n/** The repo's loud named throw for a feature that reached for Adw and found\n * none. Every Adw-dependent call site in the seam funnels through this, but\n * most no longer reach it in practice: Alert, Appearance and chrome:\n * \"content\" all check `adwAvailable()` themselves first and take a\n * plain-GTK fallback instead (see .claude/epics/adw-optional/002.md,\n * 003.md, 004.md) — this throw is what is left for the features with no\n * fallback at all (NavigationStack, react-native-gtkx/adw's own\n * AdwBreakpoint family). */\nconst requireAdw = (feature: string): AdwModules => {\n if (!cached) {\n throw new Error(`[react-native-gtkx] ${feature}${NOT_AVAILABLE}`)\n }\n return cached\n}\n\n/** The real `@gtkx/gi/adw` namespace (enums, GObject classes — `Adw.StyleManager`,\n * `Adw.ColorScheme`, `Adw.ResponseAppearance`, ...), or the loud throw above. */\nexport const requireAdwGi = (feature: string): typeof AdwGi =>\n requireAdw(feature).gi\n\n/** The real `@gtkx/jsx/adw` namespace (raw JSX elements — `AdwApplicationWindow`,\n * `AdwNavigationView`, `AdwNavigationPage`, ...), or the loud throw above. */\nexport const requireAdwJsx = (feature: string): typeof AdwJsx =>\n requireAdw(feature).jsx\n\n// Type-only (erased at build — no runtime import, so this costs nothing on\n// the plain profile): re-exported so a consumer like common/navigation-stack.tsx\n// can spell `Ref<Adw.NavigationView | null>` without importing @gtkx/gi/adw\n// itself, which the eslint fence (no @gtkx/* outside src/gtkx/bridge/**)\n// would refuse anyway.\nexport type { AdwGi as Adw }\n\nexport type ColorScheme = \"light\" | \"dark\"\n\n// Folded in from the pre-seam ./theme.ts: both are pure Adw, both were only\n// ever called from apis/host.gtkx.ts, and keeping them as their own\n// (formerly eagerly-imported) file would just move the eager `import * as\n// Adw` problem one hop over instead of removing it.\nexport const styleManager = (): AdwGi.StyleManager =>\n requireAdwGi(\"Appearance\").StyleManager.getDefault()\n\n// The Appearance API subscribes to notify::dark on the style manager.\nexport const colorScheme = (): ColorScheme =>\n styleManager().getDark() ? \"dark\" : \"light\"\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ColorScheme = "light" | "dark";
|
|
2
|
+
export type VariantLike = {
|
|
3
|
+
getTypeString(): string;
|
|
4
|
+
getVariant(): VariantLike;
|
|
5
|
+
getUint32(): number;
|
|
6
|
+
getString(): [string, number];
|
|
7
|
+
};
|
|
8
|
+
export declare const unwrapVariant: (variant: VariantLike) => VariantLike;
|
|
9
|
+
export declare const parseColorSchemeValue: (raw: number) => ColorScheme;
|
|
10
|
+
export declare const parsePortalReadReply: (reply: VariantLike) => ColorScheme;
|
|
11
|
+
export declare const parseSettingChangedValue: (value: VariantLike) => ColorScheme;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The pure, GTK-free half of color-scheme-portal.ts: parsing a
|
|
2
|
+
// org.freedesktop.portal.Settings reply/signal payload into "light" | "dark".
|
|
3
|
+
// Deliberately has NO @gtkx/gi imports (not even type-only ones — see
|
|
4
|
+
// VariantLike below) so it is importable from tests/unit (cross-platform,
|
|
5
|
+
// no GTK, no codegen store) — see
|
|
6
|
+
// tests/unit/gtkx/color-scheme-parse.test.ts and
|
|
7
|
+
// .claude/epics/adw-optional/004.md's note on why the real D-Bus round trip
|
|
8
|
+
// itself cannot be integration-tested in this repo's headless session.
|
|
9
|
+
// org.freedesktop.portal.Settings.Read's "value" out-arg is declared as
|
|
10
|
+
// variant ("v"); a long-standing, widely reported implementation quirk
|
|
11
|
+
// (flatpak/xdg-desktop-portal#789) double-boxes it, so a real reply is
|
|
12
|
+
// Variant[Variant[uint32]] as often as it is Variant[uint32] depending on
|
|
13
|
+
// the backend (xdg-desktop-portal-gnome, -kde, ...) and version. Peeling
|
|
14
|
+
// while the type is still "v" handles both without having to guess which
|
|
15
|
+
// one a given portal implementation does.
|
|
16
|
+
export const unwrapVariant = (variant) => {
|
|
17
|
+
let current = variant;
|
|
18
|
+
// A defensive bound, not a real limit — no correct implementation nests
|
|
19
|
+
// more than one extra layer; this only stops a corrupt/adversarial reply
|
|
20
|
+
// from looping forever.
|
|
21
|
+
for (let guard = 0; guard < 8 && current.getTypeString() === "v"; guard += 1) {
|
|
22
|
+
current = current.getVariant();
|
|
23
|
+
}
|
|
24
|
+
return current;
|
|
25
|
+
};
|
|
26
|
+
// The portal's own enum for org.freedesktop.appearance's color-scheme key:
|
|
27
|
+
// 0 = no preference, 1 = prefer dark, 2 = prefer light. The spec requires
|
|
28
|
+
// unknown values to be treated the same as 0 — mapped to "light" here,
|
|
29
|
+
// matching Gtk.Settings' own gtk-application-prefer-dark-theme default.
|
|
30
|
+
export const parseColorSchemeValue = (raw) => raw === 1 ? "dark" : "light";
|
|
31
|
+
// reply is "(v)" — Settings.Read's single out-arg, boxed once more by the
|
|
32
|
+
// tuple every D-Bus method reply is wrapped in; the caller passes
|
|
33
|
+
// reply.getChildValue(0) (a real GLib.Variant call, not part of VariantLike
|
|
34
|
+
// — trivial enough it does not need its own seam), everything after that
|
|
35
|
+
// goes through the parser above.
|
|
36
|
+
export const parsePortalReadReply = (reply) => parseColorSchemeValue(unwrapVariant(reply).getUint32());
|
|
37
|
+
// SettingChanged's payload is (namespace: s, key: s, value: v) — value goes
|
|
38
|
+
// through the same unwrap-then-read path as a Read reply. The caller passes
|
|
39
|
+
// just the value child (parameters.getChildValue(2)); namespace/key
|
|
40
|
+
// filtering happens in the caller with the real Variant's getString().
|
|
41
|
+
export const parseSettingChangedValue = (value) => parseColorSchemeValue(unwrapVariant(value).getUint32());
|
|
42
|
+
//# sourceMappingURL=color-scheme-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-scheme-parse.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/color-scheme-parse.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,8EAA8E;AAC9E,sEAAsE;AACtE,0EAA0E;AAC1E,kCAAkC;AAClC,iDAAiD;AACjD,4EAA4E;AAC5E,uEAAuE;AAcvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,0EAA0E;AAC1E,yEAAyE;AACzE,yEAAyE;AACzE,0CAA0C;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAoB,EAAe,EAAE;IACjE,IAAI,OAAO,GAAG,OAAO,CAAA;IACrB,wEAAwE;IACxE,yEAAyE;IACzE,wBAAwB;IACxB,KACE,IAAI,KAAK,GAAG,CAAC,EACb,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,aAAa,EAAE,KAAK,GAAG,EAC5C,KAAK,IAAI,CAAC,EACV,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAA;IAChC,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,2EAA2E;AAC3E,0EAA0E;AAC1E,uEAAuE;AACvE,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAW,EAAe,EAAE,CAChE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;AAE9B,0EAA0E;AAC1E,kEAAkE;AAClE,4EAA4E;AAC5E,yEAAyE;AACzE,iCAAiC;AACjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAkB,EAAe,EAAE,CACtE,qBAAqB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,CAAA;AAEzD,4EAA4E;AAC5E,4EAA4E;AAC5E,oEAAoE;AACpE,uEAAuE;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAkB,EAAe,EAAE,CAC1E,qBAAqB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,CAAA","sourcesContent":["// The pure, GTK-free half of color-scheme-portal.ts: parsing a\n// org.freedesktop.portal.Settings reply/signal payload into \"light\" | \"dark\".\n// Deliberately has NO @gtkx/gi imports (not even type-only ones — see\n// VariantLike below) so it is importable from tests/unit (cross-platform,\n// no GTK, no codegen store) — see\n// tests/unit/gtkx/color-scheme-parse.test.ts and\n// .claude/epics/adw-optional/004.md's note on why the real D-Bus round trip\n// itself cannot be integration-tested in this repo's headless session.\n\nexport type ColorScheme = \"light\" | \"dark\"\n\n// A structural (not nominal) slice of GLib.Variant — just enough of it to\n// parse a Settings.Read reply or a SettingChanged payload, so this file can\n// be unit-tested with a plain object instead of a real GLib.Variant.\nexport type VariantLike = {\n getTypeString(): string\n getVariant(): VariantLike\n getUint32(): number\n getString(): [string, number]\n}\n\n// org.freedesktop.portal.Settings.Read's \"value\" out-arg is declared as\n// variant (\"v\"); a long-standing, widely reported implementation quirk\n// (flatpak/xdg-desktop-portal#789) double-boxes it, so a real reply is\n// Variant[Variant[uint32]] as often as it is Variant[uint32] depending on\n// the backend (xdg-desktop-portal-gnome, -kde, ...) and version. Peeling\n// while the type is still \"v\" handles both without having to guess which\n// one a given portal implementation does.\nexport const unwrapVariant = (variant: VariantLike): VariantLike => {\n let current = variant\n // A defensive bound, not a real limit — no correct implementation nests\n // more than one extra layer; this only stops a corrupt/adversarial reply\n // from looping forever.\n for (\n let guard = 0;\n guard < 8 && current.getTypeString() === \"v\";\n guard += 1\n ) {\n current = current.getVariant()\n }\n return current\n}\n\n// The portal's own enum for org.freedesktop.appearance's color-scheme key:\n// 0 = no preference, 1 = prefer dark, 2 = prefer light. The spec requires\n// unknown values to be treated the same as 0 — mapped to \"light\" here,\n// matching Gtk.Settings' own gtk-application-prefer-dark-theme default.\nexport const parseColorSchemeValue = (raw: number): ColorScheme =>\n raw === 1 ? \"dark\" : \"light\"\n\n// reply is \"(v)\" — Settings.Read's single out-arg, boxed once more by the\n// tuple every D-Bus method reply is wrapped in; the caller passes\n// reply.getChildValue(0) (a real GLib.Variant call, not part of VariantLike\n// — trivial enough it does not need its own seam), everything after that\n// goes through the parser above.\nexport const parsePortalReadReply = (reply: VariantLike): ColorScheme =>\n parseColorSchemeValue(unwrapVariant(reply).getUint32())\n\n// SettingChanged's payload is (namespace: s, key: s, value: v) — value goes\n// through the same unwrap-then-read path as a Read reply. The caller passes\n// just the value child (parameters.getChildValue(2)); namespace/key\n// filtering happens in the caller with the real Variant's getString().\nexport const parseSettingChangedValue = (value: VariantLike): ColorScheme =>\n parseColorSchemeValue(unwrapVariant(value).getUint32())\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SubscriptionHandle } from "../../contracts";
|
|
2
|
+
import { type ColorScheme } from "./color-scheme-parse";
|
|
3
|
+
export type { ColorScheme };
|
|
4
|
+
/** Always "light" | "dark" — see the module doc for how it is sourced. */
|
|
5
|
+
export declare const getPlainColorScheme: () => ColorScheme;
|
|
6
|
+
/**
|
|
7
|
+
* The plain profile's setColorScheme. What Adw's setColorScheme forces is
|
|
8
|
+
* already only ever an app-local override — AdwStyleManager is per-process,
|
|
9
|
+
* never a system-wide write — so writing Gtk.Settings'
|
|
10
|
+
* gtk-application-prefer-dark-theme here (also strictly app-local: it never
|
|
11
|
+
* reaches outside this process either) is not a lesser feature, just the
|
|
12
|
+
* plain-GTK lever that does the same job. There is no honest way to make
|
|
13
|
+
* this profile change the DESKTOP's own preference — no portal write API
|
|
14
|
+
* exists (the portal is read + notify only, by design) — so "local-only" is
|
|
15
|
+
* the whole story, not a fallback for a system-wide write that almost
|
|
16
|
+
* worked.
|
|
17
|
+
*
|
|
18
|
+
* null ("follow the system") re-applies the last value actually observed
|
|
19
|
+
* from the portal, if any. With no portal reachable at all there is no
|
|
20
|
+
* other system source to revert to, so it is a deliberate no-op — staying
|
|
21
|
+
* at whatever the toggle already reads rather than guessing a default.
|
|
22
|
+
*/
|
|
23
|
+
export declare const setPlainColorScheme: (scheme: ColorScheme | null) => void;
|
|
24
|
+
export declare const onPlainColorSchemeChange: (notify: () => void) => SubscriptionHandle;
|