@gtkx/ffi 0.3.1 → 0.3.3

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 CHANGED
@@ -5,242 +5,138 @@
5
5
  <h1 align="center">GTKX</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Build native GTK4 desktop applications with React and TypeScript</strong>
8
+ <strong>Build native GTK4 desktop apps with React</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://eugeniodepalo.github.io/gtkx">Documentation</a> ·
13
13
  <a href="#quick-start">Quick Start</a> ·
14
- <a href="#examples">Examples</a>
14
+ <a href="#examples">Examples</a> ·
15
+ <a href="#contributing">Contributing</a>
15
16
  </p>
16
17
 
17
18
  ---
18
19
 
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.
20
+ GTKX lets you build native Linux desktop applications using React and TypeScript. Write familiar React code that renders as native GTK4 widgets—no Electron, no web views.
20
21
 
21
22
  ## Features
22
23
 
23
- - **React Components** — Use React hooks, state, and component patterns you already know
24
- - **Hot Module Replacement** — Edit your code and see changes instantly, powered by Vite
25
- - **Native Performance** — Direct FFI bindings to GTK4 via Rust and libffi
26
- - **CLI & Scaffolding** — Get started in seconds with `npx @gtkx/cli@latest create`
27
- - **CSS-in-JS Styling** — Emotion-style `css` template literals for GTK widgets
28
- - **Testing Library** — Familiar `screen`, `userEvent`, and query APIs for testing components
24
+ - **React** — Hooks, state, props, and components you already know
25
+ - **Hot Reload** — Edit code and see changes instantly via Vite
26
+ - **Native** — Direct FFI bindings to GTK4 via Rust and libffi
27
+ - **CLI** — `npx @gtkx/cli@latest create` scaffolds a ready-to-go project
28
+ - **CSS-in-JS** — Emotion-style `css` template literals for GTK styling
29
+ - **Testing** — Testing Library-style `screen`, `userEvent`, and queries
29
30
 
30
31
  ## Quick Start
31
32
 
32
- Create a new GTKX app with a single command:
33
-
34
- ```bash
35
- npx @gtkx/cli@latest create
36
- ```
37
-
38
- This launches an interactive wizard that sets up your project with TypeScript, your preferred package manager, and optional testing support.
39
-
40
- You can also pass options directly:
41
-
42
- ```bash
43
- npx @gtkx/cli@latest create my-app --app-id com.example.myapp --pm pnpm --testing vitest
44
- ```
45
-
46
- Then start developing with HMR:
47
-
48
33
  ```bash
34
+ npx @gtkx/cli@latest create my-app
49
35
  cd my-app
50
36
  npm run dev
51
37
  ```
52
38
 
53
- Edit your code and see changes instantly without restarting the app!
54
-
55
- ### Manual Setup
39
+ Edit your code and see changes instantly—no restart needed.
56
40
 
57
- Alternatively, install packages directly:
58
-
59
- ```bash
60
- npm install @gtkx/cli @gtkx/react @gtkx/ffi react
61
- npm install -D @types/react typescript
62
- ```
63
-
64
- Create your first app:
41
+ ### Example
65
42
 
66
43
  ```tsx
67
- // src/app.tsx
44
+ import { render, ApplicationWindow, Box, Button, Label, quit } from "@gtkx/react";
45
+ import { Orientation } from "@gtkx/ffi/gtk";
68
46
  import { useState } from "react";
69
- import * as Gtk from "@gtkx/ffi/gtk";
70
- import { ApplicationWindow, Box, Button, Label, quit } from "@gtkx/react";
71
47
 
72
- export default function App() {
48
+ const App = () => {
73
49
  const [count, setCount] = useState(0);
74
50
 
75
51
  return (
76
- <ApplicationWindow
77
- title="My App"
78
- defaultWidth={400}
79
- defaultHeight={300}
80
- onCloseRequest={quit}
81
- >
82
- <Box
83
- orientation={Gtk.Orientation.VERTICAL}
84
- spacing={12}
85
- marginStart={20}
86
- marginEnd={20}
87
- marginTop={20}
88
- marginBottom={20}
89
- >
52
+ <ApplicationWindow title="Counter" onCloseRequest={quit}>
53
+ <Box orientation={Orientation.VERTICAL} spacing={12} margin={20}>
90
54
  <Label.Root label={`Count: ${count}`} />
91
55
  <Button label="Increment" onClicked={() => setCount((c) => c + 1)} />
92
56
  </Box>
93
57
  </ApplicationWindow>
94
58
  );
95
- }
96
-
97
- export const appId = "org.example.MyApp";
98
- ```
99
-
100
- ```tsx
101
- // src/index.tsx
102
- import { render } from "@gtkx/react";
103
- import App, { appId } from "./app.js";
104
-
105
- render(<App />, appId);
106
- ```
59
+ };
107
60
 
108
- Run with HMR:
109
-
110
- ```bash
111
- npx gtkx dev src/app.tsx
112
- ```
113
-
114
- Or without HMR (production):
115
-
116
- ```bash
117
- npx tsc -b && node dist/index.js
61
+ render(<App />, "org.example.Counter");
118
62
  ```
119
63
 
120
64
  ## Styling
121
65
 
122
- Use `@gtkx/css` for CSS-in-JS styling:
123
-
124
66
  ```tsx
125
67
  import { css } from "@gtkx/css";
126
68
  import { Button } from "@gtkx/react";
127
69
 
128
- const primaryButton = css`
70
+ const primary = css`
129
71
  padding: 16px 32px;
130
72
  border-radius: 24px;
131
73
  background: linear-gradient(135deg, #3584e4, #9141ac);
132
74
  color: white;
133
- font-weight: bold;
134
75
  `;
135
76
 
136
- const MyButton = () => <Button label="Click me" cssClasses={[primaryButton]} />;
77
+ <Button label="Click me" cssClasses={[primary]} />
137
78
  ```
138
79
 
139
- GTK also provides built-in CSS classes like `suggested-action`, `destructive-action`, `card`, and `heading`.
80
+ GTK also provides built-in classes like `suggested-action`, `destructive-action`, `card`, and `heading`.
140
81
 
141
82
  ## Testing
142
83
 
143
- Use `@gtkx/testing` for Testing Library-style component tests:
144
-
145
84
  ```tsx
146
- import { cleanup, render, screen, userEvent, fireEvent } from "@gtkx/testing";
85
+ import { cleanup, render, screen, userEvent } from "@gtkx/testing";
147
86
  import { AccessibleRole } from "@gtkx/ffi/gtk";
148
- import { App } from "./app.js";
149
87
 
150
- // Clean up after each test
151
- afterEach(async () => {
152
- await cleanup();
153
- });
88
+ afterEach(() => cleanup());
154
89
 
155
- test("increments count when clicking button", async () => {
90
+ test("increments count", async () => {
156
91
  await render(<App />);
157
92
 
158
- const button = await screen.findByRole(AccessibleRole.BUTTON, {
159
- name: "Increment",
160
- });
93
+ const button = await screen.findByRole(AccessibleRole.BUTTON, { name: "Increment" });
161
94
  await userEvent.click(button);
162
95
 
163
96
  await screen.findByText("Count: 1");
164
97
  });
165
-
166
- test("can also use fireEvent for low-level signals", async () => {
167
- await render(<App />);
168
-
169
- const button = await screen.findByRole(AccessibleRole.BUTTON, {
170
- name: "Increment",
171
- });
172
- fireEvent(button, "clicked");
173
-
174
- await screen.findByText("Count: 1");
175
- });
176
98
  ```
177
99
 
178
- ### Available APIs
179
-
180
- **Queries** - Find elements in the rendered tree (all async):
181
-
182
- - `findBy*` / `findAllBy*` - Waits for element to appear
183
-
184
- Query types: `ByRole`, `ByText`, `ByLabelText`, `ByTestId`
185
-
186
- **User Interactions**:
100
+ Queries: `findByRole`, `findByText`, `findByLabelText`, `findByTestId`
187
101
 
188
- - `userEvent.click(element)` - Simulate click
189
- - `userEvent.dblClick(element)` - Simulate double click
190
- - `userEvent.activate(element)` - Activate element (e.g., press Enter in input)
191
- - `userEvent.type(element, text)` - Type text into input
192
- - `userEvent.clear(element)` - Clear input text
193
- - `userEvent.tab(element, options?)` - Simulate Tab navigation
194
- - `userEvent.selectOptions(element, values)` - Select options in ComboBox/ListBox
195
- - `userEvent.deselectOptions(element, values)` - Deselect options in ListBox
196
-
197
- **Low-level Events**:
198
-
199
- - `fireEvent(element, signalName, ...args)` - Emit any GTK signal with optional arguments
200
-
201
- **Utilities**:
202
-
203
- - `waitFor(callback)` - Wait for condition
204
- - `waitForElementToBeRemoved(element)` - Wait for element removal
102
+ User events: `click`, `dblClick`, `type`, `clear`, `tab`, `selectOptions`
205
103
 
206
104
  ## Examples
207
105
 
208
- ### GTK4 Demo
209
-
210
- A comprehensive showcase of GTK4 widgets and features:
106
+ | Example | Description |
107
+ | ------- | ----------- |
108
+ | [gtk4-demo](examples/gtk4-demo) | Widget showcase |
109
+ | [todo](examples/todo) | Todo app with tests |
211
110
 
212
111
  ```bash
213
- cd examples/gtk4-demo
214
- pnpm dev
215
- ```
216
-
217
- ### Todo App
218
-
219
- A todo app demonstrating `@gtkx/testing` with realistic component tests:
220
-
221
- ```bash
222
- cd examples/todo
223
- pnpm dev
224
- pnpm test
112
+ cd examples/gtk4-demo && pnpm dev
225
113
  ```
226
114
 
227
115
  ## Packages
228
116
 
229
- | Package | Description |
230
- | --------------------------------- | -------------------------------------------------- |
231
- | [@gtkx/cli](packages/cli) | CLI for creating and developing GTKX apps with HMR |
232
- | [@gtkx/react](packages/react) | React reconciler and JSX components |
233
- | [@gtkx/ffi](packages/ffi) | TypeScript FFI bindings for GTK4 |
234
- | [@gtkx/native](packages/native) | Rust native module for FFI bridge |
235
- | [@gtkx/css](packages/css) | CSS-in-JS styling for GTK widgets |
236
- | [@gtkx/testing](packages/testing) | Testing utilities for GTKX components |
237
- | [@gtkx/gir](packages/gir) | GObject Introspection parser for codegen |
117
+ | Package | Description |
118
+ | ------- | ----------- |
119
+ | [@gtkx/cli](packages/cli) | CLI with HMR dev server |
120
+ | [@gtkx/react](packages/react) | React reconciler and JSX components |
121
+ | [@gtkx/ffi](packages/ffi) | TypeScript bindings for GTK4/GLib/GIO |
122
+ | [@gtkx/native](packages/native) | Rust native module (libffi bridge) |
123
+ | [@gtkx/css](packages/css) | CSS-in-JS styling |
124
+ | [@gtkx/testing](packages/testing) | Testing utilities |
125
+ | [@gtkx/gir](packages/gir) | GObject Introspection parser |
238
126
 
239
127
  ## Requirements
240
128
 
241
129
  - Node.js 20+
242
- - GTK4
243
- - Linux (GTK4 is Linux-native)
130
+ - GTK4 (`gtk4-devel` on Fedora, `libgtk-4-dev` on Ubuntu)
131
+ - Linux
132
+
133
+ ## Contributing
134
+
135
+ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
136
+
137
+ - [Report a bug](https://github.com/eugeniodepalo/gtkx/issues/new?template=bug_report.md)
138
+ - [Request a feature](https://github.com/eugeniodepalo/gtkx/issues/new?template=feature_request.md)
139
+ - [Read the Code of Conduct](CODE_OF_CONDUCT.md)
244
140
 
245
141
  ## License
246
142
 
@@ -572,14 +572,12 @@ export class CodeGenerator {
572
572
  }
573
573
  let current = cls.parent ? classMap.get(cls.parent) : undefined;
574
574
  while (current) {
575
- // Collect signals from the current parent class first
576
575
  for (const signal of current.signals) {
577
576
  if (!seenNames.has(signal.name)) {
578
577
  allSignals.push(signal);
579
578
  seenNames.add(signal.name);
580
579
  }
581
580
  }
582
- // Then check if this parent has a cross-namespace grandparent
583
581
  if (current.parent?.includes(".")) {
584
582
  return { signals: allSignals, hasCrossNamespaceParent: true };
585
583
  }
@@ -1111,6 +1109,8 @@ ${allArgs ? `${allArgs},` : ""}
1111
1109
  lines.push(` if (ptr === null) return null;`);
1112
1110
  }
1113
1111
  if (isCyclic) {
1112
+ // For cyclic types, return a minimal NativeObject wrapper. Cast is necessary
1113
+ // because the full type interface isn't available without circular imports.
1114
1114
  lines.push(` return { id: ptr } as unknown as ${baseReturnType};`);
1115
1115
  }
1116
1116
  else {
@@ -1263,6 +1263,8 @@ ${allArgs ? `${allArgs},` : ""}
1263
1263
  kind: "class",
1264
1264
  });
1265
1265
  }
1266
+ // Signal handler catch-all overload must use `any` for compatibility with typed overloads.
1267
+ // The typed overloads have specific return types, and unknown is not assignable to them.
1266
1268
  signalOverloads.push(` ${methodName}(signal: string, handler: (...args: any[]) => any, after?: boolean): number;`);
1267
1269
  this.usesType = true;
1268
1270
  this.usesGetObject = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/ffi",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Generated TypeScript FFI bindings for GTK4 libraries",
5
5
  "keywords": [
6
6
  "gtk",
@@ -46,10 +46,10 @@
46
46
  "dist"
47
47
  ],
48
48
  "dependencies": {
49
- "@gtkx/native": "0.3.1"
49
+ "@gtkx/native": "0.3.3"
50
50
  },
51
51
  "devDependencies": {
52
- "@gtkx/gir": "0.3.1"
52
+ "@gtkx/gir": "0.3.3"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsc -b && cp ../../README.md .",