@gtkx/react 0.1.21 → 0.1.23
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 +139 -322
- package/dist/codegen/jsx-generator.d.ts +2 -1
- package/dist/codegen/jsx-generator.js +13 -2
- package/dist/predicates.d.ts +4 -3
- package/dist/reconciler.d.ts +1 -1
- package/dist/reconciler.js +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,391 +1,208 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/HEAD/logo.svg" alt="GTKX Logo" width="128" height="128">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">GTKX</h1>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Build native GTK4 desktop applications with React and TypeScript</strong>
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://eugeniodepalo.github.io/gtkx">Documentation</a> ·
|
|
13
|
+
<a href="#quick-start">Quick Start</a> ·
|
|
14
|
+
<a href="#examples">Examples</a>
|
|
15
|
+
</p>
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
---
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
GTKX bridges React's component model with GTK4's native widget system. Write familiar React code and render it as native Linux desktop applications with full access to GTK4 widgets, signals, and styling.
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
- Node.js 20+
|
|
19
|
-
- Rust toolchain (for building `@gtkx/native`)
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# Fedora
|
|
23
|
-
sudo dnf install gtk4-devel
|
|
21
|
+
## Features
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
- **React Components** — Use React hooks, state, and component patterns you already know
|
|
24
|
+
- **Type-Safe** — Full TypeScript support with auto-generated types from GTK4 introspection data
|
|
25
|
+
- **Native Performance** — Direct FFI bindings to GTK4 via Rust and libffi
|
|
26
|
+
- **CSS-in-JS Styling** — Emotion-style `css` template literals for GTK widgets
|
|
27
|
+
- **Testing Library** — Familiar `screen`, `userEvent`, and query APIs for testing components
|
|
28
28
|
|
|
29
29
|
## Quick Start
|
|
30
30
|
|
|
31
|
-
```tsx
|
|
32
|
-
import { ApplicationWindow, Button, Box, Label, quit, render } from "@gtkx/react";
|
|
33
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
34
|
-
import { useState } from "react";
|
|
35
|
-
|
|
36
|
-
const Counter = () => {
|
|
37
|
-
const [count, setCount] = useState(0);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<Box valign={Gtk.Align.CENTER} halign={Gtk.Align.CENTER} spacing={10}>
|
|
41
|
-
<Label.Root label={`Count: ${count}`} cssClasses={["title-2"]} />
|
|
42
|
-
<Button label="Increment" onClicked={() => setCount(c => c + 1)} />
|
|
43
|
-
</Box>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// Export app instance for use in dialogs
|
|
48
|
-
export const app = render(
|
|
49
|
-
<ApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
|
|
50
|
-
<Counter />
|
|
51
|
-
</ApplicationWindow>,
|
|
52
|
-
"com.example.myapp"
|
|
53
|
-
);
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Run with:
|
|
57
|
-
|
|
58
31
|
```bash
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## API
|
|
63
|
-
|
|
64
|
-
### `render(element, applicationId)`
|
|
65
|
-
|
|
66
|
-
Renders a React element tree as a GTK4 application. Returns the GTK Application instance.
|
|
67
|
-
|
|
68
|
-
- `element` — Root React element (typically `ApplicationWindow`)
|
|
69
|
-
- `applicationId` — Unique identifier in reverse-DNS format (e.g., `com.example.app`)
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
export const app = render(<App />, "com.example.myapp");
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### `quit()`
|
|
76
|
-
|
|
77
|
-
Signals the application to close. Returns `false` (useful as a direct event handler for `onCloseRequest`).
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
<ApplicationWindow onCloseRequest={quit}>...</ApplicationWindow>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### `createPortal(element)`
|
|
84
|
-
|
|
85
|
-
Renders a React element outside the normal component tree (useful for dialogs).
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
{showDialog && createPortal(<AboutDialog ... />)}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### `createRef()`
|
|
92
|
-
|
|
93
|
-
Creates a reference for FFI output parameters.
|
|
94
|
-
|
|
95
|
-
```tsx
|
|
96
|
-
import { createRef } from "@gtkx/react";
|
|
97
|
-
|
|
98
|
-
const ref = createRef();
|
|
99
|
-
someGtkFunction(ref);
|
|
100
|
-
console.log(ref.value);
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Widgets
|
|
104
|
-
|
|
105
|
-
### Container Widgets
|
|
106
|
-
|
|
107
|
-
**ApplicationWindow** — Main application window
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
<ApplicationWindow title="Window Title" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
|
|
111
|
-
{children}
|
|
112
|
-
</ApplicationWindow>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Box** — Arranges children in a row or column
|
|
116
|
-
|
|
117
|
-
```tsx
|
|
118
|
-
<Box orientation={Gtk.Orientation.VERTICAL} spacing={10}>
|
|
119
|
-
<Button label="First" />
|
|
120
|
-
<Button label="Second" />
|
|
121
|
-
</Box>
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**Grid** — Arranges children in a grid layout
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
<Grid.Root columnSpacing={10} rowSpacing={10}>
|
|
128
|
-
<Grid.Child column={0} row={0}><Button label="(0,0)" /></Grid.Child>
|
|
129
|
-
<Grid.Child column={1} row={0}><Button label="(1,0)" /></Grid.Child>
|
|
130
|
-
<Grid.Child column={0} row={1} columnSpan={2}>
|
|
131
|
-
<Button label="Spans 2 columns" hexpand />
|
|
132
|
-
</Grid.Child>
|
|
133
|
-
</Grid.Root>
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**ScrolledWindow** — Adds scrollbars to content
|
|
137
|
-
|
|
138
|
-
```tsx
|
|
139
|
-
<ScrolledWindow vexpand hexpand>
|
|
140
|
-
<TextView />
|
|
141
|
-
</ScrolledWindow>
|
|
142
|
-
```
|
|
32
|
+
# Install dependencies
|
|
33
|
+
pnpm add @gtkx/react react
|
|
143
34
|
|
|
144
|
-
|
|
35
|
+
# For styling (optional)
|
|
36
|
+
pnpm add @gtkx/css
|
|
145
37
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<Paned.StartChild>
|
|
149
|
-
<Box>{/* Left */}</Box>
|
|
150
|
-
</Paned.StartChild>
|
|
151
|
-
<Paned.EndChild>
|
|
152
|
-
<Box>{/* Right */}</Box>
|
|
153
|
-
</Paned.EndChild>
|
|
154
|
-
</Paned.Root>
|
|
38
|
+
# For testing (optional)
|
|
39
|
+
pnpm add -D @gtkx/testing
|
|
155
40
|
```
|
|
156
41
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
**Button** — Clickable button
|
|
42
|
+
Create your first app:
|
|
160
43
|
|
|
161
44
|
```tsx
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
**ToggleButton** — Button with on/off state
|
|
45
|
+
// index.tsx
|
|
46
|
+
import { render } from "@gtkx/react";
|
|
47
|
+
import { App } from "./app.js";
|
|
166
48
|
|
|
167
|
-
|
|
168
|
-
<ToggleButton.Root label={active ? "ON" : "OFF"} active={active} onToggled={() => setActive(a => !a)} />
|
|
49
|
+
render(<App />, "org.example.MyApp");
|
|
169
50
|
```
|
|
170
51
|
|
|
171
|
-
**CheckButton** — Checkbox with label
|
|
172
|
-
|
|
173
52
|
```tsx
|
|
174
|
-
|
|
175
|
-
|
|
53
|
+
// app.tsx
|
|
54
|
+
import { ApplicationWindow, Box, Button, Label, quit } from "@gtkx/react";
|
|
55
|
+
import { Orientation } from "@gtkx/ffi/gtk";
|
|
56
|
+
import { useState } from "react";
|
|
176
57
|
|
|
177
|
-
|
|
58
|
+
export const App = () => {
|
|
59
|
+
const [count, setCount] = useState(0);
|
|
178
60
|
|
|
179
|
-
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
61
|
+
return (
|
|
62
|
+
<ApplicationWindow
|
|
63
|
+
title="My App"
|
|
64
|
+
defaultWidth={400}
|
|
65
|
+
defaultHeight={300}
|
|
66
|
+
onCloseRequest={quit}
|
|
67
|
+
>
|
|
68
|
+
<Box orientation={Orientation.VERTICAL} spacing={12} margin={20}>
|
|
69
|
+
<Label.Root label={`Count: ${count}`} />
|
|
70
|
+
<Button
|
|
71
|
+
label="Increment"
|
|
72
|
+
onClicked={() => setCount((c) => c + 1)}
|
|
73
|
+
/>
|
|
74
|
+
</Box>
|
|
75
|
+
</ApplicationWindow>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
187
78
|
```
|
|
188
79
|
|
|
189
|
-
|
|
80
|
+
Run with:
|
|
190
81
|
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
<PasswordEntry placeholderText="Password..." />
|
|
194
|
-
<SearchEntry placeholderText="Search..." />
|
|
82
|
+
```bash
|
|
83
|
+
pnpm tsx index.tsx
|
|
195
84
|
```
|
|
196
85
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
```tsx
|
|
200
|
-
// Adjustment args: value, lower, upper, stepIncrement, pageIncrement, pageSize
|
|
201
|
-
const adjustment = useMemo(() => new Gtk.Adjustment(50, 0, 100, 1, 10, 0), []);
|
|
202
|
-
<SpinButton adjustment={adjustment} onValueChanged={(self) => setValue(self.getValue())} />
|
|
203
|
-
```
|
|
86
|
+
## Styling
|
|
204
87
|
|
|
205
|
-
|
|
88
|
+
Use `@gtkx/css` for CSS-in-JS styling:
|
|
206
89
|
|
|
207
90
|
```tsx
|
|
208
|
-
|
|
209
|
-
|
|
91
|
+
import { css } from "@gtkx/css";
|
|
92
|
+
import { Button } from "@gtkx/react";
|
|
210
93
|
|
|
211
|
-
|
|
94
|
+
const primaryButton = css`
|
|
95
|
+
padding: 16px 32px;
|
|
96
|
+
border-radius: 24px;
|
|
97
|
+
background: linear-gradient(135deg, #3584e4, #9141ac);
|
|
98
|
+
color: white;
|
|
99
|
+
font-weight: bold;
|
|
100
|
+
`;
|
|
212
101
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
<Label.Root label="Hello, World!" cssClasses={["title-2"]} />
|
|
102
|
+
const MyButton = () => (
|
|
103
|
+
<Button label="Click me" cssClasses={[primaryButton]} />
|
|
104
|
+
);
|
|
217
105
|
```
|
|
218
106
|
|
|
219
|
-
|
|
107
|
+
GTK also provides built-in CSS classes like `suggested-action`, `destructive-action`, `card`, and `heading`.
|
|
220
108
|
|
|
221
|
-
|
|
222
|
-
<ProgressBar fraction={0.5} showText />
|
|
223
|
-
```
|
|
109
|
+
## Testing
|
|
224
110
|
|
|
225
|
-
|
|
111
|
+
Use `@gtkx/testing` for Testing Library-style component tests:
|
|
226
112
|
|
|
227
113
|
```tsx
|
|
228
|
-
|
|
229
|
-
|
|
114
|
+
import { cleanup, render, screen, userEvent, fireEvent } from "@gtkx/testing";
|
|
115
|
+
import { AccessibleRole } from "@gtkx/ffi/gtk";
|
|
116
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
117
|
+
import { App } from "./app.js";
|
|
230
118
|
|
|
231
|
-
|
|
119
|
+
describe("Counter", () => {
|
|
120
|
+
afterEach(() => cleanup());
|
|
232
121
|
|
|
233
|
-
|
|
122
|
+
it("increments count when clicking button", async () => {
|
|
123
|
+
render(<App />);
|
|
234
124
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
</HeaderBar.TitleWidget>
|
|
240
|
-
</HeaderBar.Root>
|
|
241
|
-
```
|
|
125
|
+
const button = await screen.findByRole(AccessibleRole.BUTTON, {
|
|
126
|
+
name: "Increment",
|
|
127
|
+
});
|
|
128
|
+
await userEvent.click(button);
|
|
242
129
|
|
|
243
|
-
|
|
130
|
+
await screen.findByText("Count: 1");
|
|
131
|
+
});
|
|
244
132
|
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
<CenterBox.StartWidget><Button label="Left" /></CenterBox.StartWidget>
|
|
248
|
-
<CenterBox.CenterWidget><Label.Root label="Center" /></CenterBox.CenterWidget>
|
|
249
|
-
<CenterBox.EndWidget><Button label="Right" /></CenterBox.EndWidget>
|
|
250
|
-
</CenterBox.Root>
|
|
251
|
-
```
|
|
133
|
+
it("can also use fireEvent for synchronous events", async () => {
|
|
134
|
+
render(<App />);
|
|
252
135
|
|
|
253
|
-
|
|
136
|
+
const button = await screen.findByRole(AccessibleRole.BUTTON, {
|
|
137
|
+
name: "Increment",
|
|
138
|
+
});
|
|
139
|
+
fireEvent.click(button);
|
|
254
140
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<ListBox selectionMode={Gtk.SelectionMode.SINGLE}>
|
|
259
|
-
<ListBoxRow><Label.Root label="Item 1" /></ListBoxRow>
|
|
260
|
-
<ListBoxRow><Label.Root label="Item 2" /></ListBoxRow>
|
|
261
|
-
</ListBox>
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**DropDown** — Dropdown selector
|
|
265
|
-
|
|
266
|
-
```tsx
|
|
267
|
-
<DropDown.Root
|
|
268
|
-
itemLabel={(item) => item.name}
|
|
269
|
-
onSelectionChanged={(item, index) => setSelected(item)}
|
|
270
|
-
>
|
|
271
|
-
{items.map(item => <DropDown.Item key={item.id} item={item} />)}
|
|
272
|
-
</DropDown.Root>
|
|
141
|
+
await screen.findByText("Count: 1");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
273
144
|
```
|
|
274
145
|
|
|
275
|
-
|
|
146
|
+
### Available APIs
|
|
276
147
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}}>
|
|
282
|
-
{items.map(item => <ListView.Item item={item} key={item.id} />)}
|
|
283
|
-
</ListView.Root>
|
|
284
|
-
```
|
|
148
|
+
**Queries** - Find elements in the rendered tree:
|
|
149
|
+
- `getBy*` / `getAllBy*` - Throws if not found
|
|
150
|
+
- `queryBy*` / `queryAllBy*` - Returns null/empty array if not found
|
|
151
|
+
- `findBy*` / `findAllBy*` - Async, waits for element
|
|
285
152
|
|
|
286
|
-
|
|
153
|
+
Query types: `ByRole`, `ByText`, `ByLabelText`, `ByTestId`
|
|
287
154
|
|
|
288
|
-
**
|
|
155
|
+
**User Interactions**:
|
|
156
|
+
- `userEvent.click(element)` - Simulate click
|
|
157
|
+
- `userEvent.dblClick(element)` - Simulate double click
|
|
158
|
+
- `userEvent.type(element, text)` - Type text into input
|
|
159
|
+
- `userEvent.clear(element)` - Clear input text
|
|
160
|
+
- `userEvent.setup()` - Create reusable instance
|
|
289
161
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
version="1.0.0"
|
|
295
|
-
onCloseRequest={() => { setShowAbout(false); return false; }}
|
|
296
|
-
/>
|
|
297
|
-
)}
|
|
298
|
-
```
|
|
162
|
+
**Low-level Events**:
|
|
163
|
+
- `fireEvent(element, signalName)` - Emit any GTK signal
|
|
164
|
+
- `fireEvent.click(element)` - Emit clicked signal
|
|
165
|
+
- `fireEvent.activate(element)` - Emit activate signal
|
|
299
166
|
|
|
300
|
-
**
|
|
167
|
+
**Utilities**:
|
|
168
|
+
- `waitFor(callback)` - Wait for condition
|
|
169
|
+
- `waitForElementToBeRemoved(element)` - Wait for element removal
|
|
301
170
|
|
|
302
|
-
|
|
303
|
-
import { app } from "./index.js";
|
|
304
|
-
|
|
305
|
-
const openFile = async () => {
|
|
306
|
-
const dialog = new Gtk.FileDialog();
|
|
307
|
-
dialog.setTitle("Open File");
|
|
308
|
-
try {
|
|
309
|
-
const file = await dialog.open(app.getActiveWindow());
|
|
310
|
-
console.log(file.getPath());
|
|
311
|
-
} catch {
|
|
312
|
-
// Cancelled
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
```
|
|
171
|
+
## Examples
|
|
316
172
|
|
|
317
|
-
|
|
173
|
+
### Counter
|
|
318
174
|
|
|
319
|
-
|
|
175
|
+
A minimal counter app demonstrating state management:
|
|
320
176
|
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
<Frame.LabelWidget>
|
|
324
|
-
<Label.Root label="Custom Label" />
|
|
325
|
-
</Frame.LabelWidget>
|
|
326
|
-
<Frame.Child>
|
|
327
|
-
<Box>{/* Content */}</Box>
|
|
328
|
-
</Frame.Child>
|
|
329
|
-
</Frame.Root>
|
|
330
|
-
|
|
331
|
-
<Expander.Root label="Click to expand">
|
|
332
|
-
<Expander.Child>
|
|
333
|
-
<Box>Hidden content</Box>
|
|
334
|
-
</Expander.Child>
|
|
335
|
-
</Expander.Root>
|
|
177
|
+
```bash
|
|
178
|
+
turbo start --filter=counter-example
|
|
336
179
|
```
|
|
337
180
|
|
|
338
|
-
|
|
181
|
+
### GTK4 Demo
|
|
339
182
|
|
|
340
|
-
|
|
183
|
+
A comprehensive showcase of GTK4 widgets and features:
|
|
341
184
|
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
<Box orientation={Gtk.Orientation.VERTICAL} />
|
|
346
|
-
<ListBox selectionMode={Gtk.SelectionMode.SINGLE} />
|
|
347
|
-
<Revealer transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN} />
|
|
185
|
+
```bash
|
|
186
|
+
turbo start --filter=gtk4-demo
|
|
348
187
|
```
|
|
349
188
|
|
|
350
|
-
##
|
|
351
|
-
|
|
352
|
-
Standard React hooks work as expected:
|
|
353
|
-
|
|
354
|
-
```tsx
|
|
355
|
-
import { useState, useEffect, useRef } from "react";
|
|
189
|
+
## Packages
|
|
356
190
|
|
|
357
|
-
|
|
358
|
-
|
|
191
|
+
| Package | Description |
|
|
192
|
+
|---------|-------------|
|
|
193
|
+
| [@gtkx/react](packages/react) | React reconciler and JSX components |
|
|
194
|
+
| [@gtkx/ffi](packages/ffi) | TypeScript FFI bindings for GTK4 |
|
|
195
|
+
| [@gtkx/native](packages/native) | Rust native module for FFI bridge |
|
|
196
|
+
| [@gtkx/css](packages/css) | CSS-in-JS styling for GTK widgets |
|
|
197
|
+
| [@gtkx/testing](packages/testing) | Testing utilities for GTKX components |
|
|
198
|
+
| [@gtkx/gir](packages/gir) | GObject Introspection parser for codegen |
|
|
359
199
|
|
|
360
|
-
|
|
361
|
-
console.log(`Count: ${count}`);
|
|
362
|
-
}, [count]);
|
|
200
|
+
## Requirements
|
|
363
201
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
<Button label="Increment" onClicked={() => setCount(c => c + 1)} />
|
|
368
|
-
</Box>
|
|
369
|
-
);
|
|
370
|
-
};
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
## Architecture
|
|
374
|
-
|
|
375
|
-
```
|
|
376
|
-
┌─────────────────────────────────┐
|
|
377
|
-
│ Your React Application │
|
|
378
|
-
├─────────────────────────────────┤
|
|
379
|
-
│ @gtkx/react (React Reconciler) │
|
|
380
|
-
├─────────────────────────────────┤
|
|
381
|
-
│ @gtkx/ffi (TypeScript FFI) │
|
|
382
|
-
├─────────────────────────────────┤
|
|
383
|
-
│ @gtkx/native (Rust Bridge) │
|
|
384
|
-
├─────────────────────────────────┤
|
|
385
|
-
│ GTK4 / GLib │
|
|
386
|
-
└─────────────────────────────────┘
|
|
387
|
-
```
|
|
202
|
+
- Node.js 20+
|
|
203
|
+
- GTK4 development libraries
|
|
204
|
+
- Linux (GTK4 is Linux-native)
|
|
388
205
|
|
|
389
206
|
## License
|
|
390
207
|
|
|
391
|
-
[MPL-2.0](
|
|
208
|
+
[MPL-2.0](LICENSE)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { GirClass, GirNamespace, TypeMapper } from "@gtkx/gir";
|
|
2
|
-
|
|
2
|
+
interface JsxGeneratorOptions {
|
|
3
3
|
prettierConfig?: unknown;
|
|
4
4
|
}
|
|
5
5
|
export declare class JsxGenerator {
|
|
@@ -35,3 +35,4 @@ export declare class JsxGenerator {
|
|
|
35
35
|
private generateJsxNamespace;
|
|
36
36
|
private formatCode;
|
|
37
37
|
}
|
|
38
|
+
export {};
|
|
@@ -478,6 +478,7 @@ ${widgetPropsContent}
|
|
|
478
478
|
isListWidget(widget.name) ||
|
|
479
479
|
isDropDownWidget(widget.name) ||
|
|
480
480
|
isGridWidget(widget.name);
|
|
481
|
+
const docComment = widget.doc ? formatDoc(widget.doc).trimEnd() : "";
|
|
481
482
|
if (hasMeaningfulSlots) {
|
|
482
483
|
const valueMembers = [
|
|
483
484
|
`Root: "${widgetName}.Root" as const`,
|
|
@@ -486,10 +487,20 @@ ${widgetPropsContent}
|
|
|
486
487
|
...(isDropDownWidget(widget.name) ? [`Item: "${widgetName}.Item" as const`] : []),
|
|
487
488
|
...(isGridWidget(widget.name) ? [`Child: "${widgetName}.Child" as const`] : []),
|
|
488
489
|
];
|
|
489
|
-
|
|
490
|
+
if (docComment) {
|
|
491
|
+
lines.push(`${docComment}\nexport const ${widgetName} = {\n\t${valueMembers.join(",\n\t")},\n};`);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
lines.push(`export const ${widgetName} = {\n\t${valueMembers.join(",\n\t")},\n};`);
|
|
495
|
+
}
|
|
490
496
|
}
|
|
491
497
|
else {
|
|
492
|
-
|
|
498
|
+
if (docComment) {
|
|
499
|
+
lines.push(`${docComment}\nexport const ${widgetName} = "${widgetName}" as const;`);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
lines.push(`export const ${widgetName} = "${widgetName}" as const;`);
|
|
503
|
+
}
|
|
493
504
|
}
|
|
494
505
|
}
|
|
495
506
|
return `${lines.join("\n")}\n`;
|
package/dist/predicates.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
-
|
|
2
|
+
interface Appendable extends Gtk.Widget {
|
|
3
3
|
append(child: unknown): void;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
interface SingleChild extends Gtk.Widget {
|
|
6
6
|
setChild(child: unknown): void;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
interface Removable extends Gtk.Widget {
|
|
9
9
|
remove(child: unknown): void;
|
|
10
10
|
}
|
|
11
11
|
export declare const isAppendable: (widget: Gtk.Widget) => widget is Appendable;
|
|
12
12
|
export declare const isSingleChild: (widget: Gtk.Widget) => widget is SingleChild;
|
|
13
13
|
export declare const isRemovable: (widget: Gtk.Widget) => widget is Removable;
|
|
14
|
+
export {};
|
package/dist/reconciler.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ type SuspenseInstance = never;
|
|
|
8
8
|
type PublicInstance = Gtk.Widget;
|
|
9
9
|
type FormInstance = never;
|
|
10
10
|
type ReconcilerInstance = ReactReconciler.Reconciler<Container, Node, TextInstance, SuspenseInstance, FormInstance, PublicInstance>;
|
|
11
|
-
|
|
11
|
+
declare class Reconciler {
|
|
12
12
|
private instance;
|
|
13
13
|
private app;
|
|
14
14
|
constructor();
|
package/dist/reconciler.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Build GTK4 desktop applications with React and TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtk",
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"react-reconciler": "0.33.0",
|
|
43
|
-
"@gtkx/ffi": "0.1.
|
|
43
|
+
"@gtkx/ffi": "0.1.23"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@gtkx/gir": "0.1.
|
|
46
|
+
"@gtkx/gir": "0.1.23"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": "^19"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
-
"build": "tsc -b",
|
|
52
|
+
"build": "tsc -b && cp ../../README.md .",
|
|
53
53
|
"codegen": "tsx scripts/codegen.ts",
|
|
54
|
-
"test": "xvfb-run -a vitest run"
|
|
54
|
+
"test": "GDK_BACKEND=x11 xvfb-run -a vitest run"
|
|
55
55
|
}
|
|
56
56
|
}
|