@gtkx/runtime 2.0.0-beta.6 → 2.0.0-beta.8
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 +43 -40
- package/dist/lifecycle.js +1 -1
- package/dist/lifecycle.js.map +1 -1
- package/package.json +7 -5
- package/src/lifecycle.ts +1 -1
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<h1 align="center">GTKX</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
The React framework for Linux
|
|
9
|
-
Write native
|
|
10
|
-
Real
|
|
8
|
+
The React framework for Linux.<br />
|
|
9
|
+
Write native GNOME applications with React and TypeScript.
|
|
10
|
+
Real Adwaita and GTK4 widgets with standard web tooling.
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
|
|
39
39
|
## Demo
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
This app starts with an Adwaita application shell, renders native GTK4 widgets inside it, and uses ordinary React hooks and events:
|
|
42
42
|
|
|
43
43
|
```tsx
|
|
44
44
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
45
|
-
import {
|
|
45
|
+
import { AdwApplication, AdwApplicationWindow, AdwHeaderBar, AdwToolbarView } from "@gtkx/jsx/adw";
|
|
46
|
+
import { GtkBox, GtkButton, GtkLabel } from "@gtkx/jsx/gtk";
|
|
46
47
|
import { createRoot, quit } from "@gtkx/react";
|
|
47
48
|
import { useState } from "react";
|
|
48
49
|
|
|
@@ -50,38 +51,40 @@ const Counter = () => {
|
|
|
50
51
|
const [count, setCount] = useState(0);
|
|
51
52
|
|
|
52
53
|
return (
|
|
53
|
-
<
|
|
54
|
+
<AdwApplicationWindow
|
|
54
55
|
title="Hello GTKX"
|
|
55
56
|
defaultWidth={400}
|
|
56
57
|
defaultHeight={300}
|
|
57
58
|
onCloseRequest={quit}
|
|
58
59
|
>
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
<AdwToolbarView topBar={<AdwHeaderBar />}>
|
|
61
|
+
<GtkBox
|
|
62
|
+
orientation={Gtk.Orientation.VERTICAL}
|
|
63
|
+
spacing={20}
|
|
64
|
+
marginTop={40}
|
|
65
|
+
marginBottom={40}
|
|
66
|
+
marginStart={40}
|
|
67
|
+
marginEnd={40}
|
|
68
|
+
valign={Gtk.Align.CENTER}
|
|
69
|
+
halign={Gtk.Align.CENTER}
|
|
70
|
+
>
|
|
71
|
+
<GtkLabel cssClasses={["title-1"]}>Welcome to GTKX!</GtkLabel>
|
|
72
|
+
<GtkLabel cssClasses={["title-2"]}>{`Count: ${String(count)}`}</GtkLabel>
|
|
73
|
+
<GtkButton
|
|
74
|
+
label="Increment"
|
|
75
|
+
onClicked={() => setCount((c) => c + 1)}
|
|
76
|
+
cssClasses={["suggested-action", "pill"]}
|
|
77
|
+
/>
|
|
78
|
+
</GtkBox>
|
|
79
|
+
</AdwToolbarView>
|
|
80
|
+
</AdwApplicationWindow>
|
|
78
81
|
);
|
|
79
82
|
};
|
|
80
83
|
|
|
81
84
|
const App = () => (
|
|
82
|
-
<
|
|
85
|
+
<AdwApplication>
|
|
83
86
|
<Counter />
|
|
84
|
-
</
|
|
87
|
+
</AdwApplication>
|
|
85
88
|
);
|
|
86
89
|
|
|
87
90
|
createRoot().render(<App />);
|
|
@@ -91,9 +94,9 @@ This is the [`hello-world`](https://github.com/gtkx-org/gtkx/tree/main/examples/
|
|
|
91
94
|
|
|
92
95
|
## Why GTKX
|
|
93
96
|
|
|
94
|
-
###
|
|
97
|
+
### Adwaita-first application development
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
GTKX uses Adwaita as the foundation for applications, windows, navigation, dialogs, and adaptive layouts, with the complete GTK4 toolkit underneath. It adds a declarative React layer and an integrated TypeScript toolchain to the GNOME platform:
|
|
97
100
|
|
|
98
101
|
- a React reconciler that exposes every GObject as a JSX element,
|
|
99
102
|
- a CLI for scaffolding, development, and production builds,
|
|
@@ -102,17 +105,17 @@ GTK4 is mature, and GtkBuilder XML can lay out a static interface, but nothing r
|
|
|
102
105
|
- a Testing Library-style API for querying and driving your widgets in tests,
|
|
103
106
|
- and a Model Context Protocol (MCP) server that exposes your live app to AI agents.
|
|
104
107
|
|
|
105
|
-
###
|
|
108
|
+
### Native GNOME widgets, without a compatibility layer
|
|
106
109
|
|
|
107
|
-
React Native and similar frameworks hide the native toolkit so one API can run everywhere. GTKX
|
|
110
|
+
React Native and similar frameworks hide the native toolkit so one API can run everywhere. GTKX renders real Adwaita and GTK4 widgets and exposes any other GObject-Introspection library on your system. It is GNOME-native and Linux-only by design.
|
|
108
111
|
|
|
109
112
|
### Why Node.js, and why generated bindings
|
|
110
113
|
|
|
111
114
|
GTKX runs on Node.js, which puts native modules, the npm ecosystem, and the tooling built for Node.js APIs within reach. GJS is GNOME's own runtime, built on SpiderMonkey rather than V8; node-gtk runs on Node.js but is lightly maintained, on the older nan/V8 ABI rather than N-API, and still centered on GTK3. The [Why GTKX guide](https://gtkx.dev/guide/why-gtkx) covers what running on Node.js means day to day.
|
|
112
115
|
|
|
113
|
-
GTKX generates the TypeScript types and
|
|
116
|
+
GTKX generates the TypeScript types and native FFI calls from the same GObject-Introspection data, so the types cannot drift from the calls they back. `Adw-1` is the sole default GIR root; its GIR include pulls in `Gtk-4.0`, so codegen covers both complete API surfaces. Neither belongs in a GTKX 2 `libraries` list.
|
|
114
117
|
|
|
115
|
-
At runtime, the native Rust core calls straight into the system
|
|
118
|
+
At runtime, the native Rust core calls straight into the system Adwaita, GTK4, and GLib libraries through libffi, without loading libgirepository at all.
|
|
116
119
|
|
|
117
120
|
## Quick start
|
|
118
121
|
|
|
@@ -121,10 +124,10 @@ GTKX is Linux-only and needs Node.js 26.7 or later. See [Requirements](#requirem
|
|
|
121
124
|
Scaffold a new app with the `create-gtkx` initializer:
|
|
122
125
|
|
|
123
126
|
```sh
|
|
124
|
-
npm create gtkx
|
|
127
|
+
npm create gtkx@beta
|
|
125
128
|
```
|
|
126
129
|
|
|
127
|
-
The same command works with other package managers: `pnpm create gtkx` or `yarn create gtkx`.
|
|
130
|
+
The same command works with other package managers: `pnpm create gtkx@beta` or `yarn create gtkx@beta`.
|
|
128
131
|
|
|
129
132
|
Then run your new app:
|
|
130
133
|
|
|
@@ -145,7 +148,7 @@ The documentation at **[gtkx.dev](https://gtkx.dev)** includes a step-by-step tu
|
|
|
145
148
|
|
|
146
149
|
GTKX is Linux-only. You need:
|
|
147
150
|
|
|
148
|
-
- Linux with the
|
|
151
|
+
- Linux with the Adwaita (1.8 or later), GTK4 (4.20 or later), and GLib development libraries
|
|
149
152
|
- Node.js 26.7 or later
|
|
150
153
|
|
|
151
154
|
The `@gtkx/native` addon ships prebuilt for x64 and arm64 glibc Linux; other targets need to build it from the GTKX repository, which requires a Rust toolchain.
|
|
@@ -154,10 +157,10 @@ The `@gtkx/native` addon ships prebuilt for x64 and arm64 glibc Linux; other tar
|
|
|
154
157
|
|
|
155
158
|
Explore the [example apps](https://github.com/gtkx-org/gtkx/tree/main/examples):
|
|
156
159
|
|
|
157
|
-
- [`hello-world`](https://github.com/gtkx-org/gtkx/tree/main/examples/hello-world): the counter above.
|
|
158
|
-
- [`gtk-demo`](https://github.com/gtkx-org/gtkx/tree/main/examples/gtk-demo):
|
|
159
|
-
- [`browser`](https://github.com/gtkx-org/gtkx/tree/main/examples/browser):
|
|
160
|
-
- [`animations`](https://github.com/gtkx-org/gtkx/tree/main/examples/animations):
|
|
160
|
+
- [`hello-world`](https://github.com/gtkx-org/gtkx/tree/main/examples/hello-world): the Adwaita counter above.
|
|
161
|
+
- [`gtk-demo`](https://github.com/gtkx-org/gtkx/tree/main/examples/gtk-demo): the official GTK4 widget showcase in an Adwaita application shell, covering lists, dialogs, gestures, CSS, and OpenGL.
|
|
162
|
+
- [`browser`](https://github.com/gtkx-org/gtkx/tree/main/examples/browser): an Adwaita browser built around `WebKitWebView`.
|
|
163
|
+
- [`animations`](https://github.com/gtkx-org/gtkx/tree/main/examples/animations): an Adwaita showcase for `@gtkx/animated`, with React Spring animations driven by the GTK frame clock.
|
|
161
164
|
- [`navigation`](https://github.com/gtkx-org/gtkx/tree/main/examples/navigation): a tour of `@gtkx/navigation`, React Navigation's stack, tab, and drawer navigators rendered with libadwaita.
|
|
162
165
|
- [`tutorial`](https://github.com/gtkx-org/gtkx/tree/main/examples/tutorial): the Tasks app the documentation builds.
|
|
163
166
|
|
package/dist/lifecycle.js
CHANGED
|
@@ -88,7 +88,7 @@ const getApplicationInstance = (application) => {
|
|
|
88
88
|
const runApplication = (application, argv) => {
|
|
89
89
|
if (!isDerivedApplication(application)) {
|
|
90
90
|
throw new Error("runApplication: this application was not built by GTKX, so its command line cannot be " +
|
|
91
|
-
"parsed and it cannot be shut down safely; render <
|
|
91
|
+
"parsed and it cannot be shut down safely; render <AdwApplication> or <GtkApplication>, " +
|
|
92
92
|
"or construct it with createApplication from @gtkx/runtime");
|
|
93
93
|
}
|
|
94
94
|
claimDefaultApplication(application);
|
package/dist/lifecycle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { keepAlive, quit as nativeQuit } from \"@gtkx/native\";\nimport { isDerivedApplication, type LocalCommandLineApplication, shutDownThroughRun } from \"./application-class.js\";\nimport { claimDefaultApplication, releaseDefaultApplication } from \"./default-application.js\";\n\n/**\n * The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any\n * application subclass satisfies it structurally; the window members only `Gtk.Application` defines are\n * optional.\n */\ntype ApplicationLike = {\n /** Returns whether the application has already been registered. */\n getIsRegistered(): boolean;\n /** Returns whether another process owns the application ID, making this one a remote instance. */\n getIsRemote?(): boolean;\n /** Registers the application with the session, returning whether it succeeded. */\n register(cancellable: null): boolean;\n /** Emits `activate`, bringing up the application's initial user interface. */\n activate(): void;\n /**\n * Runs GLib's own `g_application_run`, whose tail emits `shutdown`, destroys the application\n * implementation and clears the registration.\n */\n run(argv: string[]): number;\n /** Returns the windows currently attached to the application. */\n getWindows?(): object[];\n /** Detaches a window, so it no longer holds the application open. */\n removeWindow?(window: object): void;\n /** Connects a handler to the application's `activate` or `shutdown` signal. */\n on(signal: \"activate\" | \"shutdown\", handler: () => void): unknown;\n /** Emits one of the application's own signals. */\n emit(signal: \"shutdown\"): unknown;\n};\n\ntype ApplicationInstance = \"primary\" | \"remote\" | \"shutDown\" | \"unregistered\";\n\n/** What {@link runApplication} reports about the process it just started. */\ntype RunApplicationResult = {\n /** Whether this process owns the application ID and may build a user interface. */\n isPrimary: boolean;\n /** The status GLib determined for the command line, which the process should exit with. */\n exitStatus: number;\n};\n\nconst shutdownCallbacks: (() => void)[] = [];\nconst startedApplications: WeakSet<object> = new WeakSet();\nconst registeredApplications: WeakSet<object> = new WeakSet();\nconst shutDownApplications: WeakSet<object> = new WeakSet();\n/**\n * Runs every registered exit callback and shuts down the native runtime. Safe to\n * call more than once; only the first call takes effect.\n */\nconst quit: () => void = createQuit();\n\nfunction createQuit(): () => void {\n let hasQuit = false;\n\n return () => {\n if (hasQuit) {\n return;\n }\n\n hasQuit = true;\n\n for (const callback of shutdownCallbacks) {\n callback();\n }\n\n nativeQuit();\n };\n}\n\n/**\n * Registers a callback to run once when the process quits, before the native\n * runtime is torn down.\n *\n * @param callback Invoked during shutdown.\n */\nconst onExit = (callback: () => void): void => {\n shutdownCallbacks.push(callback);\n};\n\nconst startApplication = (application: ApplicationLike & LocalCommandLineApplication, argv: string[]): number => {\n startedApplications.add(application);\n\n application.on(\"activate\", () => {\n keepAlive(true);\n });\n\n application.on(\"shutdown\", () => {\n keepAlive(false);\n });\n\n return application.runLocalCommandLine(argv)[2];\n};\n\nconst restartApplication = (application: ApplicationLike): number => {\n shutDownApplications.delete(application);\n\n if (!application.register(null)) {\n return 1;\n }\n\n application.activate();\n\n return 0;\n};\n\nconst getApplicationInstance = (application: ApplicationLike): ApplicationInstance => {\n if (!application.getIsRegistered()) {\n return registeredApplications.has(application) ? \"shutDown\" : \"unregistered\";\n }\n\n return application.getIsRemote?.() === true ? \"remote\" : \"primary\";\n};\n\n/**\n * Hands `argv` to GLib's own command line handling, which parses the application's options, prints\n * `--help`, runs `handle-local-options`, registers the application, and either activates it or\n * forwards the command line to the process that already owns the application ID. The runtime is\n * held alive while the application is active and released on shutdown.\n *\n * `g_application_run()` is not what starts the application, because it would drive its own main loop\n * and freeze Node; only the local command line handling it delegates to runs here. {@link\n * quitApplication} calls it once no window holds the application open, to reach the teardown only it\n * performs.\n *\n * GLib parses a given application's command line at most once, so starting an application that has\n * already run registers and activates it instead of reading `argv` again.\n *\n * When another process already owns the application ID, this one registers as a remote instance: no\n * user interface may be built here, because a remote application has no `GtkApplicationImpl` and\n * attaching a window to it crashes.\n *\n * The application also claims the process-wide default `Gio.Application.getDefault()` returns, before\n * its command line is parsed, so an `activate` handler and anything the host reads afterwards see the\n * application being started rather than an earlier one. That holds whether the start ends registered,\n * remote or unregistered: a refused command line still has to be reportable through the default, which\n * is how `gtkx dev` learns which application its entry mounted. {@link quitApplication} gives the\n * default back up, since GLib drops it only at finalize.\n *\n * @param application The application to start.\n * @param argv The command line, whose first entry names the program as `--help` should print it.\n * @returns Whether this process may build a user interface, and the status to exit with.\n */\nconst runApplication = (application: ApplicationLike, argv: string[]): RunApplicationResult => {\n if (!isDerivedApplication(application)) {\n throw new Error(\n \"runApplication: this application was not built by GTKX, so its command line cannot be \" +\n \"parsed and it cannot be shut down safely; render <GtkApplication> or <AdwApplication>, \" +\n \"or construct it with createApplication from @gtkx/runtime\",\n );\n }\n\n claimDefaultApplication(application);\n\n const exitStatus = startedApplications.has(application)\n ? restartApplication(application)\n : startApplication(application, argv);\n\n const instance = getApplicationInstance(application);\n\n if (instance !== \"unregistered\") {\n registeredApplications.add(application);\n }\n\n const isPrimary = instance === \"primary\";\n\n if (isPrimary) {\n keepAlive(true);\n }\n\n return { isPrimary, exitStatus };\n};\n\nconst tearDownApplication = (application: ApplicationLike): void => {\n if (!application.getIsRegistered() || shutDownApplications.has(application)) {\n return;\n }\n\n const windows = application.getWindows?.() ?? [];\n\n for (const window of windows) {\n application.removeWindow?.(window);\n }\n\n shutDownThroughRun(application);\n\n if (application.getIsRegistered()) {\n shutDownApplications.add(application);\n application.emit(\"shutdown\");\n }\n};\n\n/**\n * Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,\n * destroys the application implementation and releases the D-Bus registration. Does nothing for an\n * application that is not registered, so a repeated call is a no-op.\n *\n * GLib's own shutdown is reachable once per application: reaching it marks the application as\n * quitting, which GLib never undoes. An application that has already quit falls back to emitting\n * `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.\n *\n * The process-wide default {@link runApplication} claimed is given up whichever of those paths the\n * application took, including the ones that have no teardown left to do, so\n * `Gio.Application.getDefault()` never hands back an application that has been torn down or that\n * never registered in the first place. GLib clears that default only at finalize, which a\n * garbage-collected wrapper reaches arbitrarily late or never.\n *\n * @param application The application to shut down.\n */\nconst quitApplication = (application: ApplicationLike): void => {\n tearDownApplication(application);\n releaseDefaultApplication(application);\n};\n\nexport {\n getApplicationInstance,\n onExit,\n quit,\n runApplication,\n quitApplication,\n type ApplicationInstance,\n type RunApplicationResult,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { keepAlive, quit as nativeQuit } from \"@gtkx/native\";\nimport { isDerivedApplication, type LocalCommandLineApplication, shutDownThroughRun } from \"./application-class.js\";\nimport { claimDefaultApplication, releaseDefaultApplication } from \"./default-application.js\";\n\n/**\n * The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any\n * application subclass satisfies it structurally; the window members only `Gtk.Application` defines are\n * optional.\n */\ntype ApplicationLike = {\n /** Returns whether the application has already been registered. */\n getIsRegistered(): boolean;\n /** Returns whether another process owns the application ID, making this one a remote instance. */\n getIsRemote?(): boolean;\n /** Registers the application with the session, returning whether it succeeded. */\n register(cancellable: null): boolean;\n /** Emits `activate`, bringing up the application's initial user interface. */\n activate(): void;\n /**\n * Runs GLib's own `g_application_run`, whose tail emits `shutdown`, destroys the application\n * implementation and clears the registration.\n */\n run(argv: string[]): number;\n /** Returns the windows currently attached to the application. */\n getWindows?(): object[];\n /** Detaches a window, so it no longer holds the application open. */\n removeWindow?(window: object): void;\n /** Connects a handler to the application's `activate` or `shutdown` signal. */\n on(signal: \"activate\" | \"shutdown\", handler: () => void): unknown;\n /** Emits one of the application's own signals. */\n emit(signal: \"shutdown\"): unknown;\n};\n\ntype ApplicationInstance = \"primary\" | \"remote\" | \"shutDown\" | \"unregistered\";\n\n/** What {@link runApplication} reports about the process it just started. */\ntype RunApplicationResult = {\n /** Whether this process owns the application ID and may build a user interface. */\n isPrimary: boolean;\n /** The status GLib determined for the command line, which the process should exit with. */\n exitStatus: number;\n};\n\nconst shutdownCallbacks: (() => void)[] = [];\nconst startedApplications: WeakSet<object> = new WeakSet();\nconst registeredApplications: WeakSet<object> = new WeakSet();\nconst shutDownApplications: WeakSet<object> = new WeakSet();\n/**\n * Runs every registered exit callback and shuts down the native runtime. Safe to\n * call more than once; only the first call takes effect.\n */\nconst quit: () => void = createQuit();\n\nfunction createQuit(): () => void {\n let hasQuit = false;\n\n return () => {\n if (hasQuit) {\n return;\n }\n\n hasQuit = true;\n\n for (const callback of shutdownCallbacks) {\n callback();\n }\n\n nativeQuit();\n };\n}\n\n/**\n * Registers a callback to run once when the process quits, before the native\n * runtime is torn down.\n *\n * @param callback Invoked during shutdown.\n */\nconst onExit = (callback: () => void): void => {\n shutdownCallbacks.push(callback);\n};\n\nconst startApplication = (application: ApplicationLike & LocalCommandLineApplication, argv: string[]): number => {\n startedApplications.add(application);\n\n application.on(\"activate\", () => {\n keepAlive(true);\n });\n\n application.on(\"shutdown\", () => {\n keepAlive(false);\n });\n\n return application.runLocalCommandLine(argv)[2];\n};\n\nconst restartApplication = (application: ApplicationLike): number => {\n shutDownApplications.delete(application);\n\n if (!application.register(null)) {\n return 1;\n }\n\n application.activate();\n\n return 0;\n};\n\nconst getApplicationInstance = (application: ApplicationLike): ApplicationInstance => {\n if (!application.getIsRegistered()) {\n return registeredApplications.has(application) ? \"shutDown\" : \"unregistered\";\n }\n\n return application.getIsRemote?.() === true ? \"remote\" : \"primary\";\n};\n\n/**\n * Hands `argv` to GLib's own command line handling, which parses the application's options, prints\n * `--help`, runs `handle-local-options`, registers the application, and either activates it or\n * forwards the command line to the process that already owns the application ID. The runtime is\n * held alive while the application is active and released on shutdown.\n *\n * `g_application_run()` is not what starts the application, because it would drive its own main loop\n * and freeze Node; only the local command line handling it delegates to runs here. {@link\n * quitApplication} calls it once no window holds the application open, to reach the teardown only it\n * performs.\n *\n * GLib parses a given application's command line at most once, so starting an application that has\n * already run registers and activates it instead of reading `argv` again.\n *\n * When another process already owns the application ID, this one registers as a remote instance: no\n * user interface may be built here, because a remote application has no `GtkApplicationImpl` and\n * attaching a window to it crashes.\n *\n * The application also claims the process-wide default `Gio.Application.getDefault()` returns, before\n * its command line is parsed, so an `activate` handler and anything the host reads afterwards see the\n * application being started rather than an earlier one. That holds whether the start ends registered,\n * remote or unregistered: a refused command line still has to be reportable through the default, which\n * is how `gtkx dev` learns which application its entry mounted. {@link quitApplication} gives the\n * default back up, since GLib drops it only at finalize.\n *\n * @param application The application to start.\n * @param argv The command line, whose first entry names the program as `--help` should print it.\n * @returns Whether this process may build a user interface, and the status to exit with.\n */\nconst runApplication = (application: ApplicationLike, argv: string[]): RunApplicationResult => {\n if (!isDerivedApplication(application)) {\n throw new Error(\n \"runApplication: this application was not built by GTKX, so its command line cannot be \" +\n \"parsed and it cannot be shut down safely; render <AdwApplication> or <GtkApplication>, \" +\n \"or construct it with createApplication from @gtkx/runtime\",\n );\n }\n\n claimDefaultApplication(application);\n\n const exitStatus = startedApplications.has(application)\n ? restartApplication(application)\n : startApplication(application, argv);\n\n const instance = getApplicationInstance(application);\n\n if (instance !== \"unregistered\") {\n registeredApplications.add(application);\n }\n\n const isPrimary = instance === \"primary\";\n\n if (isPrimary) {\n keepAlive(true);\n }\n\n return { isPrimary, exitStatus };\n};\n\nconst tearDownApplication = (application: ApplicationLike): void => {\n if (!application.getIsRegistered() || shutDownApplications.has(application)) {\n return;\n }\n\n const windows = application.getWindows?.() ?? [];\n\n for (const window of windows) {\n application.removeWindow?.(window);\n }\n\n shutDownThroughRun(application);\n\n if (application.getIsRegistered()) {\n shutDownApplications.add(application);\n application.emit(\"shutdown\");\n }\n};\n\n/**\n * Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,\n * destroys the application implementation and releases the D-Bus registration. Does nothing for an\n * application that is not registered, so a repeated call is a no-op.\n *\n * GLib's own shutdown is reachable once per application: reaching it marks the application as\n * quitting, which GLib never undoes. An application that has already quit falls back to emitting\n * `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.\n *\n * The process-wide default {@link runApplication} claimed is given up whichever of those paths the\n * application took, including the ones that have no teardown left to do, so\n * `Gio.Application.getDefault()` never hands back an application that has been torn down or that\n * never registered in the first place. GLib clears that default only at finalize, which a\n * garbage-collected wrapper reaches arbitrarily late or never.\n *\n * @param application The application to shut down.\n */\nconst quitApplication = (application: ApplicationLike): void => {\n tearDownApplication(application);\n releaseDefaultApplication(application);\n};\n\nexport {\n getApplicationInstance,\n onExit,\n quit,\n runApplication,\n quitApplication,\n type ApplicationInstance,\n type RunApplicationResult,\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/runtime",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
4
|
-
"description": "FFI runtime for
|
|
3
|
+
"version": "2.0.0-beta.8",
|
|
4
|
+
"description": "FFI runtime for generated Adwaita, GTK, and GObject bindings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
7
7
|
"gtk",
|
|
8
8
|
"gtk4",
|
|
9
|
+
"adwaita",
|
|
10
|
+
"gnome",
|
|
9
11
|
"ffi",
|
|
10
12
|
"bindings",
|
|
11
13
|
"typescript",
|
|
@@ -64,11 +66,11 @@
|
|
|
64
66
|
"node": ">=26.7.0"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|
|
67
|
-
"@gtkx/native": "2.0.0-beta.
|
|
68
|
-
"@gtkx/utils": "2.0.0-beta.
|
|
69
|
+
"@gtkx/native": "2.0.0-beta.8",
|
|
70
|
+
"@gtkx/utils": "2.0.0-beta.8"
|
|
69
71
|
},
|
|
70
72
|
"devDependencies": {
|
|
71
|
-
"@gtkx/vitest": "2.0.0-beta.
|
|
73
|
+
"@gtkx/vitest": "2.0.0-beta.8"
|
|
72
74
|
},
|
|
73
75
|
"scripts": {
|
|
74
76
|
"release": "tsx ../../scripts/release-package.ts"
|
package/src/lifecycle.ts
CHANGED
|
@@ -146,7 +146,7 @@ const runApplication = (application: ApplicationLike, argv: string[]): RunApplic
|
|
|
146
146
|
if (!isDerivedApplication(application)) {
|
|
147
147
|
throw new Error(
|
|
148
148
|
"runApplication: this application was not built by GTKX, so its command line cannot be " +
|
|
149
|
-
"parsed and it cannot be shut down safely; render <
|
|
149
|
+
"parsed and it cannot be shut down safely; render <AdwApplication> or <GtkApplication>, " +
|
|
150
150
|
"or construct it with createApplication from @gtkx/runtime",
|
|
151
151
|
);
|
|
152
152
|
}
|