@gtkx/react 0.1.11

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.
Files changed (45) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +390 -0
  3. package/dist/codegen/jsx-generator.d.ts +37 -0
  4. package/dist/codegen/jsx-generator.js +554 -0
  5. package/dist/factory.d.ts +3 -0
  6. package/dist/factory.js +59 -0
  7. package/dist/generated/jsx.d.ts +1598 -0
  8. package/dist/generated/jsx.js +264 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.js +14 -0
  11. package/dist/node.d.ts +13 -0
  12. package/dist/node.js +1 -0
  13. package/dist/nodes/action-bar.d.ts +27 -0
  14. package/dist/nodes/action-bar.js +88 -0
  15. package/dist/nodes/dialog.d.ts +19 -0
  16. package/dist/nodes/dialog.js +87 -0
  17. package/dist/nodes/dropdown.d.ts +41 -0
  18. package/dist/nodes/dropdown.js +163 -0
  19. package/dist/nodes/grid.d.ts +41 -0
  20. package/dist/nodes/grid.js +140 -0
  21. package/dist/nodes/list.d.ts +46 -0
  22. package/dist/nodes/list.js +165 -0
  23. package/dist/nodes/notebook.d.ts +25 -0
  24. package/dist/nodes/notebook.js +88 -0
  25. package/dist/nodes/overlay.d.ts +29 -0
  26. package/dist/nodes/overlay.js +109 -0
  27. package/dist/nodes/slot.d.ts +17 -0
  28. package/dist/nodes/slot.js +55 -0
  29. package/dist/nodes/text.d.ts +16 -0
  30. package/dist/nodes/text.js +31 -0
  31. package/dist/nodes/widget.d.ts +19 -0
  32. package/dist/nodes/widget.js +136 -0
  33. package/dist/portal.d.ts +3 -0
  34. package/dist/portal.js +11 -0
  35. package/dist/reconciler.d.ts +20 -0
  36. package/dist/reconciler.js +111 -0
  37. package/dist/render.d.ts +5 -0
  38. package/dist/render.js +12 -0
  39. package/dist/signal-utils.d.ts +4 -0
  40. package/dist/signal-utils.js +7 -0
  41. package/dist/types.d.ts +13 -0
  42. package/dist/types.js +1 -0
  43. package/dist/widget-capabilities.d.ts +46 -0
  44. package/dist/widget-capabilities.js +32 -0
  45. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,390 @@
1
+ # @gtkx/react
2
+
3
+ React integration layer for GTKX. This package provides a custom React reconciler that renders React components as native GTK4 widgets.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @gtkx/react react
9
+ ```
10
+
11
+ ### Peer Dependencies
12
+
13
+ - `react` ^19.0.0
14
+
15
+ ### System Requirements
16
+
17
+ - Linux with GTK4 libraries
18
+ - Node.js 20+
19
+ - Rust toolchain (for building `@gtkx/native`)
20
+
21
+ ```bash
22
+ # Fedora
23
+ sudo dnf install gtk4-devel
24
+
25
+ # Ubuntu/Debian
26
+ sudo apt install libgtk-4-dev
27
+ ```
28
+
29
+ ## Quick Start
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
+ ```bash
59
+ npx tsx src/index.tsx
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
+ ```
143
+
144
+ **Paned** — Resizable split container
145
+
146
+ ```tsx
147
+ <Paned.Root wideHandle>
148
+ <Paned.StartChild>
149
+ <Box>{/* Left */}</Box>
150
+ </Paned.StartChild>
151
+ <Paned.EndChild>
152
+ <Box>{/* Right */}</Box>
153
+ </Paned.EndChild>
154
+ </Paned.Root>
155
+ ```
156
+
157
+ ### Input Widgets
158
+
159
+ **Button** — Clickable button
160
+
161
+ ```tsx
162
+ <Button label="Click me" onClicked={() => console.log("Clicked!")} />
163
+ ```
164
+
165
+ **ToggleButton** — Button with on/off state
166
+
167
+ ```tsx
168
+ <ToggleButton.Root label={active ? "ON" : "OFF"} active={active} onToggled={() => setActive(a => !a)} />
169
+ ```
170
+
171
+ **CheckButton** — Checkbox with label
172
+
173
+ ```tsx
174
+ <CheckButton.Root label="Accept terms" active={checked} onToggled={() => setChecked(c => !c)} />
175
+ ```
176
+
177
+ **Switch** — On/off toggle (must return `true` from `onStateSet`)
178
+
179
+ ```tsx
180
+ <Switch
181
+ active={enabled}
182
+ onStateSet={() => {
183
+ setEnabled(e => !e);
184
+ return true;
185
+ }}
186
+ />
187
+ ```
188
+
189
+ **Entry** — Single-line text input
190
+
191
+ ```tsx
192
+ <Entry placeholderText="Enter text..." />
193
+ <PasswordEntry placeholderText="Password..." />
194
+ <SearchEntry placeholderText="Search..." />
195
+ ```
196
+
197
+ **SpinButton** — Numeric input with increment/decrement
198
+
199
+ ```tsx
200
+ const adjustment = useRef(new Gtk.Adjustment({ value: 50, lower: 0, upper: 100, stepIncrement: 1 }));
201
+ <SpinButton adjustment={adjustment.current.ptr} onValueChanged={() => setValue(adjustment.current.getValue())} />
202
+ ```
203
+
204
+ **Scale** — Horizontal or vertical slider
205
+
206
+ ```tsx
207
+ <Scale hexpand drawValue adjustment={adjustment.current.ptr} />
208
+ ```
209
+
210
+ ### Display Widgets
211
+
212
+ **Label** — Text display
213
+
214
+ ```tsx
215
+ <Label.Root label="Hello, World!" cssClasses={["title-2"]} />
216
+ ```
217
+
218
+ **ProgressBar** — Progress indicator
219
+
220
+ ```tsx
221
+ <ProgressBar fraction={0.5} showText />
222
+ ```
223
+
224
+ **Spinner** — Loading indicator
225
+
226
+ ```tsx
227
+ <Spinner spinning={isLoading} />
228
+ ```
229
+
230
+ ### Layout Widgets
231
+
232
+ **HeaderBar** — Title bar with buttons
233
+
234
+ ```tsx
235
+ <HeaderBar.Root>
236
+ <HeaderBar.TitleWidget>
237
+ <Label.Root label="App Title" />
238
+ </HeaderBar.TitleWidget>
239
+ </HeaderBar.Root>
240
+ ```
241
+
242
+ **CenterBox** — Positions children at start, center, and end
243
+
244
+ ```tsx
245
+ <CenterBox.Root>
246
+ <CenterBox.StartWidget><Button label="Left" /></CenterBox.StartWidget>
247
+ <CenterBox.CenterWidget><Label.Root label="Center" /></CenterBox.CenterWidget>
248
+ <CenterBox.EndWidget><Button label="Right" /></CenterBox.EndWidget>
249
+ </CenterBox.Root>
250
+ ```
251
+
252
+ ### List Widgets
253
+
254
+ **ListBox** — Simple vertical list
255
+
256
+ ```tsx
257
+ <ListBox selectionMode={Gtk.SelectionMode.SINGLE}>
258
+ <ListBoxRow><Label.Root label="Item 1" /></ListBoxRow>
259
+ <ListBoxRow><Label.Root label="Item 2" /></ListBoxRow>
260
+ </ListBox>
261
+ ```
262
+
263
+ **DropDown** — Dropdown selector
264
+
265
+ ```tsx
266
+ <DropDown.Root
267
+ itemLabel={(item) => item.name}
268
+ onSelectionChanged={(item, index) => setSelected(item)}
269
+ >
270
+ {items.map(item => <DropDown.Item key={item.id} item={item} />)}
271
+ </DropDown.Root>
272
+ ```
273
+
274
+ **ListView** — Virtualized list for large datasets
275
+
276
+ ```tsx
277
+ <ListView.Root renderItem={(item) => {
278
+ if (!item) return new Gtk.Box();
279
+ return new Gtk.Label({ label: item.name });
280
+ }}>
281
+ {items.map(item => <ListView.Item item={item} key={item.id} />)}
282
+ </ListView.Root>
283
+ ```
284
+
285
+ ### Dialog Widgets
286
+
287
+ **AboutDialog** — Application about dialog (React component)
288
+
289
+ ```tsx
290
+ {showAbout && createPortal(
291
+ <AboutDialog
292
+ programName="My App"
293
+ version="1.0.0"
294
+ onCloseRequest={() => { setShowAbout(false); return false; }}
295
+ />
296
+ )}
297
+ ```
298
+
299
+ **Async Dialogs** — AlertDialog, FileDialog, ColorDialog, FontDialog (imperative)
300
+
301
+ ```tsx
302
+ import { app } from "./index.js";
303
+
304
+ const openFile = async () => {
305
+ const dialog = new Gtk.FileDialog();
306
+ dialog.setTitle("Open File");
307
+ try {
308
+ const file = await dialog.open(app.getActiveWindow());
309
+ console.log(file.getPath());
310
+ } catch {
311
+ // Cancelled
312
+ }
313
+ };
314
+ ```
315
+
316
+ ## Named Slots
317
+
318
+ Some GTK widgets have named child positions, handled via compound components:
319
+
320
+ ```tsx
321
+ <Frame.Root>
322
+ <Frame.LabelWidget>
323
+ <Label.Root label="Custom Label" />
324
+ </Frame.LabelWidget>
325
+ <Frame.Child>
326
+ <Box>{/* Content */}</Box>
327
+ </Frame.Child>
328
+ </Frame.Root>
329
+
330
+ <Expander.Root label="Click to expand">
331
+ <Expander.Child>
332
+ <Box>Hidden content</Box>
333
+ </Expander.Child>
334
+ </Expander.Root>
335
+ ```
336
+
337
+ ## Using GTK Enums
338
+
339
+ Import enums from `@gtkx/ffi/gtk`:
340
+
341
+ ```tsx
342
+ import * as Gtk from "@gtkx/ffi/gtk";
343
+
344
+ <Box orientation={Gtk.Orientation.VERTICAL} />
345
+ <ListBox selectionMode={Gtk.SelectionMode.SINGLE} />
346
+ <Revealer transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN} />
347
+ ```
348
+
349
+ ## Hooks Support
350
+
351
+ Standard React hooks work as expected:
352
+
353
+ ```tsx
354
+ import { useState, useEffect, useRef } from "react";
355
+
356
+ const Counter = () => {
357
+ const [count, setCount] = useState(0);
358
+
359
+ useEffect(() => {
360
+ console.log(`Count: ${count}`);
361
+ }, [count]);
362
+
363
+ return (
364
+ <Box spacing={10}>
365
+ <Label.Root label={`Count: ${count}`} />
366
+ <Button label="Increment" onClicked={() => setCount(c => c + 1)} />
367
+ </Box>
368
+ );
369
+ };
370
+ ```
371
+
372
+ ## Architecture
373
+
374
+ ```
375
+ ┌─────────────────────────────────┐
376
+ │ Your React Application │
377
+ ├─────────────────────────────────┤
378
+ │ @gtkx/react (React Reconciler) │
379
+ ├─────────────────────────────────┤
380
+ │ @gtkx/ffi (TypeScript FFI) │
381
+ ├─────────────────────────────────┤
382
+ │ @gtkx/native (Rust Bridge) │
383
+ ├─────────────────────────────────┤
384
+ │ GTK4 / GLib │
385
+ └─────────────────────────────────┘
386
+ ```
387
+
388
+ ## License
389
+
390
+ [MPL-2.0](../../LICENSE)
@@ -0,0 +1,37 @@
1
+ import type { GirClass, GirNamespace, TypeMapper } from "@gtkx/gir";
2
+ export interface JsxGeneratorOptions {
3
+ prettierConfig?: unknown;
4
+ }
5
+ export declare class JsxGenerator {
6
+ private typeMapper;
7
+ private options;
8
+ private classMap;
9
+ private interfaceMap;
10
+ private namespace;
11
+ private usedExternalNamespaces;
12
+ private widgetPropertyNames;
13
+ private widgetSignalNames;
14
+ constructor(typeMapper: TypeMapper, options?: JsxGeneratorOptions);
15
+ generate(namespace: GirNamespace, classMap: Map<string, GirClass>): Promise<string>;
16
+ private generateImports;
17
+ private generateCommonTypes;
18
+ private generateWidgetPropsContent;
19
+ private buildContainerMetadata;
20
+ private findWidgets;
21
+ private analyzeContainerCapabilities;
22
+ private generateWidgetPropsInterfaces;
23
+ private generateWidgetProps;
24
+ private getParentPropsName;
25
+ private getRequiredConstructorParams;
26
+ private getConstructorParams;
27
+ private generateConstructorArgsMetadata;
28
+ private getAncestorInterfaces;
29
+ private findInheritedProperty;
30
+ private generateSignalHandler;
31
+ private getSignalParamFfiType;
32
+ private addNamespacePrefix;
33
+ private buildSignalHandlerType;
34
+ private generateExports;
35
+ private generateJsxNamespace;
36
+ private formatCode;
37
+ }