@gtkx/cli 0.12.0 → 0.13.0

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.
@@ -85,12 +85,12 @@ export const createDevServer = async (options) => {
85
85
  server.close();
86
86
  });
87
87
  server.watcher.on("change", async (changedPath) => {
88
- console.log(`[gtkx] File changed: ${changedPath}`);
89
88
  try {
90
89
  const module = server.moduleGraph.getModuleById(changedPath);
91
90
  if (!module) {
92
91
  return;
93
92
  }
93
+ console.log(`[gtkx] File changed: ${changedPath}`);
94
94
  invalidateModuleAndImporters(changedPath);
95
95
  const newMod = (await server.ssrLoadModule(changedPath));
96
96
  moduleExports.set(changedPath, { ...newMod });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI for GTKX - create and develop GTK4 React applications",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -57,20 +57,20 @@
57
57
  "citty": "^0.1.6",
58
58
  "ejs": "^3.1.10",
59
59
  "react-refresh": "^0.18.0",
60
- "vite": "^7.3.0",
61
- "@gtkx/mcp": "0.12.0",
62
- "@gtkx/ffi": "0.12.0",
63
- "@gtkx/react": "0.12.0"
60
+ "vite": "^7.3.1",
61
+ "@gtkx/ffi": "0.13.0",
62
+ "@gtkx/mcp": "0.13.0",
63
+ "@gtkx/react": "0.13.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/ejs": "^3.1.5",
67
67
  "@types/react-refresh": "^0.14.7",
68
68
  "memfs": "^4.51.1",
69
- "@gtkx/testing": "0.12.0"
69
+ "@gtkx/testing": "0.13.0"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "react": "^19",
73
- "@gtkx/testing": "0.12.0"
73
+ "@gtkx/testing": "0.13.0"
74
74
  },
75
75
  "peerDependenciesMeta": {
76
76
  "@gtkx/testing": {
@@ -8,7 +8,7 @@
8
8
  import { GtkApplicationWindow, GtkLabel, render, quit } from "@gtkx/react";
9
9
 
10
10
  const App = () => (
11
- <GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300} onCloseRequest={quit}>
11
+ <GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300} onClose={quit}>
12
12
  <GtkLabel label="Hello, World!" />
13
13
  </GtkApplicationWindow>
14
14
  );
@@ -27,21 +27,20 @@ import {
27
27
  AdwWindowTitle,
28
28
  AdwStatusPage,
29
29
  GtkButton,
30
- Slot,
31
- Toolbar,
32
30
  quit,
31
+ x,
33
32
  } from "@gtkx/react";
34
33
 
35
34
  export const App = () => (
36
- <AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
35
+ <AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
37
36
  <AdwToolbarView>
38
- <Toolbar.Top>
37
+ <x.ToolbarTop>
39
38
  <AdwHeaderBar>
40
- <Slot for={AdwHeaderBar} id="titleWidget">
39
+ <x.Slot for={AdwHeaderBar} id="titleWidget">
41
40
  <AdwWindowTitle title="My App" subtitle="Welcome" />
42
- </Slot>
41
+ </x.Slot>
43
42
  </AdwHeaderBar>
44
- </Toolbar.Top>
43
+ </x.ToolbarTop>
45
44
  <AdwStatusPage
46
45
  iconName="applications-system-symbolic"
47
46
  title="Welcome"
@@ -65,7 +64,7 @@ export const appId = "com.example.myapp";
65
64
 
66
65
  ```tsx
67
66
  import * as Gtk from "@gtkx/ffi/gtk";
68
- import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel, GridChild } from "@gtkx/react";
67
+ import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel, x } from "@gtkx/react";
69
68
  import { useState } from "react";
70
69
 
71
70
  const LoginForm = () => {
@@ -78,21 +77,21 @@ const LoginForm = () => {
78
77
 
79
78
  return (
80
79
  <GtkGrid rowSpacing={12} columnSpacing={12}>
81
- <GridChild column={0} row={0}>
80
+ <x.GridChild column={0} row={0}>
82
81
  <GtkLabel label="Email:" halign={Gtk.Align.END} />
83
- </GridChild>
84
- <GridChild column={1} row={0}>
82
+ </x.GridChild>
83
+ <x.GridChild column={1} row={0}>
85
84
  <GtkEntry text={email} onChanged={(e) => setEmail(e.getText())} hexpand />
86
- </GridChild>
87
- <GridChild column={0} row={1}>
85
+ </x.GridChild>
86
+ <x.GridChild column={0} row={1}>
88
87
  <GtkLabel label="Password:" halign={Gtk.Align.END} />
89
- </GridChild>
90
- <GridChild column={1} row={1}>
88
+ </x.GridChild>
89
+ <x.GridChild column={1} row={1}>
91
90
  <GtkEntry text={password} onChanged={(e) => setPassword(e.getText())} visibility={false} hexpand />
92
- </GridChild>
93
- <GridChild column={0} row={2} columnSpan={2}>
91
+ </x.GridChild>
92
+ <x.GridChild column={0} row={2} columnSpan={2}>
94
93
  <GtkButton label="Login" onClicked={handleSubmit} cssClasses={["suggested-action"]} halign={Gtk.Align.END} />
95
- </GridChild>
94
+ </x.GridChild>
96
95
  </GtkGrid>
97
96
  );
98
97
  };
@@ -102,7 +101,7 @@ const LoginForm = () => {
102
101
 
103
102
  ```tsx
104
103
  import * as Gtk from "@gtkx/ffi/gtk";
105
- import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow, ListView, ListItem } from "@gtkx/react";
104
+ import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
106
105
  import { useCallback, useState } from "react";
107
106
 
108
107
  interface Todo {
@@ -133,7 +132,7 @@ const TodoList = () => {
133
132
  <GtkButton label="Add" onClicked={addTodo} cssClasses={["suggested-action"]} />
134
133
  </GtkBox>
135
134
  <GtkScrolledWindow vexpand cssClasses={["card"]}>
136
- <ListView<Todo>
135
+ <x.ListView<Todo>
137
136
  renderItem={(todo) => (
138
137
  <GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
139
138
  <GtkLabel label={todo?.text ?? ""} hexpand halign={Gtk.Align.START} />
@@ -142,9 +141,9 @@ const TodoList = () => {
142
141
  )}
143
142
  >
144
143
  {todos.map((todo) => (
145
- <ListItem key={todo.id} id={todo.id} value={todo} />
144
+ <x.ListItem key={todo.id} id={todo.id} value={todo} />
146
145
  ))}
147
- </ListView>
146
+ </x.ListView>
148
147
  </GtkScrolledWindow>
149
148
  </GtkBox>
150
149
  );
@@ -159,7 +158,7 @@ const TodoList = () => {
159
158
 
160
159
  ```tsx
161
160
  import * as Gtk from "@gtkx/ffi/gtk";
162
- import { GtkBox, GtkLabel, GtkPaned, GtkScrolledWindow, GtkStack, ListView, ListItem, Slot, StackPage } from "@gtkx/react";
161
+ import { GtkBox, GtkLabel, GtkPaned, GtkScrolledWindow, GtkStack, x } from "@gtkx/react";
163
162
  import { useState } from "react";
164
163
 
165
164
  interface Page {
@@ -178,9 +177,9 @@ const SidebarNav = () => {
178
177
 
179
178
  return (
180
179
  <GtkPaned orientation={Gtk.Orientation.HORIZONTAL} position={200}>
181
- <Slot for={GtkPaned} id="startChild">
180
+ <x.Slot for={GtkPaned} id="startChild">
182
181
  <GtkScrolledWindow cssClasses={["sidebar"]}>
183
- <ListView<Page>
182
+ <x.ListView<Page>
184
183
  selected={[currentPage]}
185
184
  selectionMode={Gtk.SelectionMode.SINGLE}
186
185
  onSelectionChanged={(ids) => setCurrentPage(ids[0])}
@@ -189,18 +188,18 @@ const SidebarNav = () => {
189
188
  )}
190
189
  >
191
190
  {pages.map((page) => (
192
- <ListItem key={page.id} id={page.id} value={page} />
191
+ <x.ListItem key={page.id} id={page.id} value={page} />
193
192
  ))}
194
- </ListView>
193
+ </x.ListView>
195
194
  </GtkScrolledWindow>
196
- </Slot>
197
- <Slot for={GtkPaned} id="endChild">
198
- <GtkStack visibleChildName={currentPage}>
199
- <StackPage name="home"><GtkLabel label="Home Content" vexpand /></StackPage>
200
- <StackPage name="settings"><GtkLabel label="Settings Content" vexpand /></StackPage>
201
- <StackPage name="about"><GtkLabel label="About Content" vexpand /></StackPage>
195
+ </x.Slot>
196
+ <x.Slot for={GtkPaned} id="endChild">
197
+ <GtkStack page={currentPage}>
198
+ <x.StackPage id="home"><GtkLabel label="Home Content" vexpand /></x.StackPage>
199
+ <x.StackPage id="settings"><GtkLabel label="Settings Content" vexpand /></x.StackPage>
200
+ <x.StackPage id="about"><GtkLabel label="About Content" vexpand /></x.StackPage>
202
201
  </GtkStack>
203
- </Slot>
202
+ </x.Slot>
204
203
  </GtkPaned>
205
204
  );
206
205
  };
@@ -210,34 +209,34 @@ const SidebarNav = () => {
210
209
 
211
210
  ```tsx
212
211
  import * as Gtk from "@gtkx/ffi/gtk";
213
- import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow, Pack, Slot, StackPage, quit } from "@gtkx/react";
212
+ import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow, quit, x } from "@gtkx/react";
214
213
  import { useState } from "react";
215
214
 
216
215
  const AppWithNavigation = () => {
217
216
  const [page, setPage] = useState("home");
218
217
 
219
218
  return (
220
- <GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400} onCloseRequest={quit}>
221
- <Slot for={GtkWindow} id="titlebar">
219
+ <GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400} onClose={quit}>
220
+ <x.Slot for={GtkWindow} id="titlebar">
222
221
  <GtkHeaderBar>
223
- <Pack.Start>
222
+ <x.PackStart>
224
223
  {page !== "home" && <GtkButton iconName="go-previous-symbolic" onClicked={() => setPage("home")} />}
225
- </Pack.Start>
226
- <Slot for={GtkHeaderBar} id="titleWidget">
224
+ </x.PackStart>
225
+ <x.Slot for={GtkHeaderBar} id="titleWidget">
227
226
  <GtkLabel label={page === "home" ? "Home" : "Details"} cssClasses={["title"]} />
228
- </Slot>
227
+ </x.Slot>
229
228
  </GtkHeaderBar>
230
- </Slot>
231
- <GtkStack visibleChildName={page}>
232
- <StackPage name="home">
229
+ </x.Slot>
230
+ <GtkStack page={page}>
231
+ <x.StackPage id="home">
233
232
  <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
234
233
  <GtkLabel label="Welcome" />
235
234
  <GtkButton label="Go to Details" onClicked={() => setPage("details")} />
236
235
  </GtkBox>
237
- </StackPage>
238
- <StackPage name="details">
236
+ </x.StackPage>
237
+ <x.StackPage id="details">
239
238
  <GtkLabel label="Details Page Content" vexpand />
240
- </StackPage>
239
+ </x.StackPage>
241
240
  </GtkStack>
242
241
  </GtkApplicationWindow>
243
242
  );
@@ -259,7 +258,7 @@ import {
259
258
  AdwEntryRow,
260
259
  GtkImage,
261
260
  GtkScrolledWindow,
262
- Slot,
261
+ x,
263
262
  } from "@gtkx/react";
264
263
  import { useState } from "react";
265
264
 
@@ -278,9 +277,9 @@ const SettingsPage = () => {
278
277
  <AdwPreferencesGroup title="Account">
279
278
  <AdwEntryRow title="Username" text={username} onChanged={(e) => setUsername(e.getText())} />
280
279
  <AdwActionRow title="Profile" subtitle="Manage your profile">
281
- <Slot for={AdwActionRow} id="activatableWidget">
280
+ <x.Slot for={AdwActionRow} id="activatableWidget">
282
281
  <GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
283
- </Slot>
282
+ </x.Slot>
284
283
  </AdwActionRow>
285
284
  </AdwPreferencesGroup>
286
285
 
@@ -303,7 +302,7 @@ const SettingsPage = () => {
303
302
 
304
303
  ```tsx
305
304
  import * as Gtk from "@gtkx/ffi/gtk";
306
- import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow, ColumnViewColumn, ListItem } from "@gtkx/react";
305
+ import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
307
306
  import { useMemo, useState } from "react";
308
307
 
309
308
  interface FileItem {
@@ -340,12 +339,12 @@ const FileTable = () => {
340
339
 
341
340
  return (
342
341
  <GtkScrolledWindow vexpand cssClasses={["card"]}>
343
- <GtkColumnView sortColumn={sortColumn} sortOrder={sortOrder} onSortChange={handleSort}>
344
- <ColumnViewColumn<FileItem> title="Name" id="name" expand sortable renderCell={(f) => <GtkLabel label={f?.name ?? ""} />} />
345
- <ColumnViewColumn<FileItem> title="Size" id="size" fixedWidth={100} sortable renderCell={(f) => <GtkLabel label={`${f?.size ?? 0} KB`} />} />
346
- <ColumnViewColumn<FileItem> title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f) => <GtkLabel label={f?.modified ?? ""} />} />
342
+ <GtkColumnView estimatedRowHeight={48} sortColumn={sortColumn} sortOrder={sortOrder} onSortChange={handleSort}>
343
+ <x.ColumnViewColumn<FileItem> title="Name" id="name" expand sortable renderCell={(f) => <GtkLabel label={f?.name ?? ""} />} />
344
+ <x.ColumnViewColumn<FileItem> title="Size" id="size" fixedWidth={100} sortable renderCell={(f) => <GtkLabel label={`${f?.size ?? 0} KB`} />} />
345
+ <x.ColumnViewColumn<FileItem> title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f) => <GtkLabel label={f?.modified ?? ""} />} />
347
346
  {sortedFiles.map((file) => (
348
- <ListItem key={file.id} id={file.id} value={file} />
347
+ <x.ListItem key={file.id} id={file.id} value={file} />
349
348
  ))}
350
349
  </GtkColumnView>
351
350
  </GtkScrolledWindow>
@@ -359,7 +358,7 @@ const FileTable = () => {
359
358
 
360
359
  ```tsx
361
360
  import * as Gtk from "@gtkx/ffi/gtk";
362
- import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, Menu, Slot, quit } from "@gtkx/react";
361
+ import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, quit, x } from "@gtkx/react";
363
362
  import { useState } from "react";
364
363
 
365
364
  const MenuDemo = () => {
@@ -368,24 +367,24 @@ const MenuDemo = () => {
368
367
  return (
369
368
  <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
370
369
  <GtkMenuButton label="File" halign={Gtk.Align.START}>
371
- <Slot for={GtkMenuButton} id="popover">
370
+ <x.Slot for={GtkMenuButton} id="popover">
372
371
  <GtkPopoverMenu>
373
- <Menu.Section>
374
- <Menu.Item id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
375
- <Menu.Item id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
376
- <Menu.Item id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
377
- </Menu.Section>
378
- <Menu.Section>
379
- <Menu.Submenu label="Export">
380
- <Menu.Item id="pdf" label="As PDF" onActivate={() => setLastAction("Export PDF")} />
381
- <Menu.Item id="csv" label="As CSV" onActivate={() => setLastAction("Export CSV")} />
382
- </Menu.Submenu>
383
- </Menu.Section>
384
- <Menu.Section>
385
- <Menu.Item id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
386
- </Menu.Section>
372
+ <x.MenuSection>
373
+ <x.MenuItem id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
374
+ <x.MenuItem id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
375
+ <x.MenuItem id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
376
+ </x.MenuSection>
377
+ <x.MenuSection>
378
+ <x.MenuSubmenu label="Export">
379
+ <x.MenuItem id="pdf" label="As PDF" onActivate={() => setLastAction("Export PDF")} />
380
+ <x.MenuItem id="csv" label="As CSV" onActivate={() => setLastAction("Export CSV")} />
381
+ </x.MenuSubmenu>
382
+ </x.MenuSection>
383
+ <x.MenuSection>
384
+ <x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
385
+ </x.MenuSection>
387
386
  </GtkPopoverMenu>
388
- </Slot>
387
+ </x.Slot>
389
388
  </GtkMenuButton>
390
389
  <GtkLabel label={`Last action: ${lastAction ?? "(none)"}`} />
391
390
  </GtkBox>
@@ -399,7 +398,7 @@ const MenuDemo = () => {
399
398
 
400
399
  ```tsx
401
400
  import * as Gtk from "@gtkx/ffi/gtk";
402
- import { AdwSpinner, GtkBox, GtkLabel, GtkScrolledWindow, ListView, ListItem } from "@gtkx/react";
401
+ import { AdwSpinner, GtkBox, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
403
402
  import { useEffect, useState } from "react";
404
403
 
405
404
  interface User {
@@ -419,8 +418,8 @@ const AsyncList = () => {
419
418
  const response = await fetch("https://api.example.com/users");
420
419
  const data = await response.json();
421
420
  setUsers(data);
422
- } catch (err) {
423
- setError(err instanceof Error ? err.message : "Failed to load");
421
+ } catch (error) {
422
+ setError(error instanceof Error ? error.message : "Failed to load");
424
423
  } finally {
425
424
  setLoading(false);
426
425
  }
@@ -442,7 +441,7 @@ const AsyncList = () => {
442
441
 
443
442
  return (
444
443
  <GtkScrolledWindow vexpand>
445
- <ListView<User>
444
+ <x.ListView<User>
446
445
  renderItem={(user) => (
447
446
  <GtkBox orientation={Gtk.Orientation.VERTICAL} marginStart={12} marginTop={8} marginBottom={8}>
448
447
  <GtkLabel label={user?.name ?? ""} halign={Gtk.Align.START} cssClasses={["heading"]} />
@@ -451,9 +450,9 @@ const AsyncList = () => {
451
450
  )}
452
451
  >
453
452
  {users.map((user) => (
454
- <ListItem key={user.id} id={user.id} value={user} />
453
+ <x.ListItem key={user.id} id={user.id} value={user} />
455
454
  ))}
456
- </ListView>
455
+ </x.ListView>
457
456
  </GtkScrolledWindow>
458
457
  );
459
458
  };
@@ -14,7 +14,7 @@ import { GtkApplicationWindow, GtkBox, GtkButton, render, quit } from "@gtkx/rea
14
14
  import * as Gtk from "@gtkx/ffi/gtk";
15
15
 
16
16
  const App = () => (
17
- <GtkApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
17
+ <GtkApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
18
18
  <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
19
19
  Hello, GTKX!
20
20
  <GtkButton label="Quit" onClicked={quit} />
@@ -53,8 +53,8 @@ Some widgets require children in specific slots:
53
53
 
54
54
  ```tsx
55
55
  <GtkPaned orientation={Gtk.Orientation.HORIZONTAL}>
56
- <Slot for={GtkPaned} id="startChild"><Sidebar /></Slot>
57
- <Slot for={GtkPaned} id="endChild"><Content /></Slot>
56
+ <x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
57
+ <x.Slot for={GtkPaned} id="endChild"><Content /></x.Slot>
58
58
  </GtkPaned>
59
59
  ```
60
60
 
@@ -62,8 +62,8 @@ Some widgets require children in specific slots:
62
62
 
63
63
  ```tsx
64
64
  <GtkHeaderBar>
65
- <Pack.Start><GtkButton iconName="go-previous-symbolic" /></Pack.Start>
66
- <Pack.End><GtkMenuButton iconName="open-menu-symbolic" /></Pack.End>
65
+ <x.PackStart><GtkButton iconName="go-previous-symbolic" /></x.PackStart>
66
+ <x.PackEnd><GtkMenuButton iconName="open-menu-symbolic" /></x.PackEnd>
67
67
  </GtkHeaderBar>
68
68
  ```
69
69
 
@@ -30,9 +30,9 @@ Linear layout (horizontal or vertical).
30
30
 
31
31
  ```tsx
32
32
  <GtkGrid rowSpacing={8} columnSpacing={12}>
33
- <GridChild column={0} row={0}><GtkLabel label="Name:" /></GridChild>
34
- <GridChild column={1} row={0}><GtkEntry hexpand /></GridChild>
35
- <GridChild column={0} row={1} columnSpan={2}><GtkButton label="Submit" /></GridChild>
33
+ <x.GridChild column={0} row={0}><GtkLabel label="Name:" /></x.GridChild>
34
+ <x.GridChild column={1} row={0}><GtkEntry hexpand /></x.GridChild>
35
+ <x.GridChild column={0} row={1} columnSpan={2}><GtkButton label="Submit" /></x.GridChild>
36
36
  </GtkGrid>
37
37
  ```
38
38
 
@@ -42,39 +42,39 @@ Linear layout (horizontal or vertical).
42
42
  Page container, shows one child at a time.
43
43
 
44
44
  ```tsx
45
- <GtkStack visibleChildName="page1" transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}>
46
- <StackPage name="page1" title="First" iconName="document-new">
45
+ <GtkStack page="page1" transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}>
46
+ <x.StackPage id="page1" title="First" iconName="document-new">
47
47
  <Content1 />
48
- </StackPage>
49
- <StackPage name="page2" title="Second">
48
+ </x.StackPage>
49
+ <x.StackPage id="page2" title="Second">
50
50
  <Content2 />
51
- </StackPage>
51
+ </x.StackPage>
52
52
  </GtkStack>
53
53
  ```
54
54
 
55
- **StackPage props:** `name` (required), `title`, `iconName`, `needsAttention`, `badgeNumber` (AdwViewStack only)
55
+ **StackPage props:** `id` (optional), `title`, `iconName`, `needsAttention`, `badgeNumber` (AdwViewStack only)
56
56
 
57
57
  ### GtkNotebook
58
58
  Tabbed container with visible tabs.
59
59
 
60
60
  ```tsx
61
61
  <GtkNotebook>
62
- <Notebook.Page label="Tab 1"><Content1 /></Notebook.Page>
63
- <Notebook.Page label="Tab 2"><Content2 /></Notebook.Page>
62
+ <x.NotebookPage label="Tab 1"><Content1 /></x.NotebookPage>
63
+ <x.NotebookPage label="Tab 2"><Content2 /></x.NotebookPage>
64
64
  </GtkNotebook>
65
65
  ```
66
66
 
67
67
  Custom tab widget:
68
68
  ```tsx
69
- <Notebook.Page>
70
- <Notebook.PageTab>
69
+ <x.NotebookPage>
70
+ <x.NotebookPageTab>
71
71
  <GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={4}>
72
72
  <GtkImage iconName="folder-symbolic" />
73
73
  <GtkLabel label="Files" />
74
74
  </GtkBox>
75
- </Notebook.PageTab>
75
+ </x.NotebookPageTab>
76
76
  <Content />
77
- </Notebook.Page>
77
+ </x.NotebookPage>
78
78
  ```
79
79
 
80
80
  ### GtkPaned
@@ -82,8 +82,8 @@ Resizable split with draggable divider. **Requires Slot components.**
82
82
 
83
83
  ```tsx
84
84
  <GtkPaned orientation={Gtk.Orientation.HORIZONTAL} position={280} shrinkStartChild={false}>
85
- <Slot for={GtkPaned} id="startChild"><Sidebar /></Slot>
86
- <Slot for={GtkPaned} id="endChild"><MainContent /></Slot>
85
+ <x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
86
+ <x.Slot for={GtkPaned} id="endChild"><MainContent /></x.Slot>
87
87
  </GtkPaned>
88
88
  ```
89
89
 
@@ -93,9 +93,9 @@ Stack widgets on top of each other. First child is base layer, additional childr
93
93
  ```tsx
94
94
  <GtkOverlay>
95
95
  <GtkButton label="Notifications" />
96
- <OverlayChild>
96
+ <x.OverlayChild>
97
97
  <GtkLabel label="3" cssClasses={["badge"]} halign={Gtk.Align.END} valign={Gtk.Align.START} />
98
- </OverlayChild>
98
+ </x.OverlayChild>
99
99
  </GtkOverlay>
100
100
  ```
101
101
 
@@ -118,22 +118,24 @@ All virtual list widgets use `ListItem` children and a `renderItem` function.
118
118
  High-performance scrollable list with selection.
119
119
 
120
120
  ```tsx
121
- <ListView<Item>
121
+ <x.ListView<Item>
122
+ estimatedItemHeight={48}
122
123
  vexpand
123
124
  selected={selectedId ? [selectedId] : []}
124
125
  selectionMode={Gtk.SelectionMode.SINGLE}
125
126
  onSelectionChanged={(ids) => setSelectedId(ids[0])}
126
127
  renderItem={(item) => <GtkLabel label={item?.name ?? ""} />}
127
128
  >
128
- {items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
129
- </ListView>
129
+ {items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
130
+ </x.ListView>
130
131
  ```
131
132
 
132
133
  ### GridView
133
134
  Grid-based virtual scrolling.
134
135
 
135
136
  ```tsx
136
- <GridView<Item>
137
+ <x.GridView<Item>
138
+ estimatedItemHeight={100}
137
139
  minColumns={2}
138
140
  maxColumns={4}
139
141
  renderItem={(item) => (
@@ -143,16 +145,16 @@ Grid-based virtual scrolling.
143
145
  </GtkBox>
144
146
  )}
145
147
  >
146
- {items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
147
- </GridView>
148
+ {items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
149
+ </x.GridView>
148
150
  ```
149
151
 
150
152
  ### GtkColumnView
151
153
  Table with sortable columns.
152
154
 
153
155
  ```tsx
154
- <GtkColumnView sortColumn="name" sortOrder={Gtk.SortType.ASCENDING} onSortChange={handleSort}>
155
- <ColumnViewColumn<Item>
156
+ <GtkColumnView estimatedRowHeight={48} sortColumn="name" sortOrder={Gtk.SortType.ASCENDING} onSortChange={handleSort}>
157
+ <x.ColumnViewColumn<Item>
156
158
  title="Name"
157
159
  id="name"
158
160
  expand
@@ -160,13 +162,13 @@ Table with sortable columns.
160
162
  sortable
161
163
  renderCell={(item) => <GtkLabel label={item?.name ?? ""} />}
162
164
  />
163
- <ColumnViewColumn<Item>
165
+ <x.ColumnViewColumn<Item>
164
166
  title="Size"
165
167
  id="size"
166
168
  fixedWidth={100}
167
169
  renderCell={(item) => <GtkLabel label={item?.size ?? ""} />}
168
170
  />
169
- {items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
171
+ {items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
170
172
  </GtkColumnView>
171
173
  ```
172
174
 
@@ -175,7 +177,7 @@ Selection dropdown.
175
177
 
176
178
  ```tsx
177
179
  <GtkDropDown selectedId={selectedId} onSelectionChanged={setSelectedId}>
178
- {options.map(opt => <SimpleListItem key={opt.id} id={opt.id} value={opt.label} />)}
180
+ {options.map(opt => <x.SimpleListItem key={opt.id} id={opt.id} value={opt.label} />)}
179
181
  </GtkDropDown>
180
182
  ```
181
183
 
@@ -247,11 +249,11 @@ Title bar with packed widgets. Use `Pack.Start`, `Pack.End`, and `Slot` for titl
247
249
 
248
250
  ```tsx
249
251
  <GtkHeaderBar>
250
- <Pack.Start><GtkButton iconName="go-previous-symbolic" /></Pack.Start>
251
- <Slot for={GtkHeaderBar} id="titleWidget">
252
+ <x.PackStart><GtkButton iconName="go-previous-symbolic" /></x.PackStart>
253
+ <x.Slot for={GtkHeaderBar} id="titleWidget">
252
254
  <GtkLabel label="Title" cssClasses={["title"]} />
253
- </Slot>
254
- <Pack.End><GtkMenuButton iconName="open-menu-symbolic" /></Pack.End>
255
+ </x.Slot>
256
+ <x.PackEnd><GtkMenuButton iconName="open-menu-symbolic" /></x.PackEnd>
255
257
  </GtkHeaderBar>
256
258
  ```
257
259
 
@@ -260,8 +262,8 @@ Bottom action bar.
260
262
 
261
263
  ```tsx
262
264
  <GtkActionBar>
263
- <Pack.Start><GtkButton label="Cancel" /></Pack.Start>
264
- <Pack.End><GtkButton label="Save" cssClasses={["suggested-action"]} /></Pack.End>
265
+ <x.PackStart><GtkButton label="Cancel" /></x.PackStart>
266
+ <x.PackEnd><GtkButton label="Save" cssClasses={["suggested-action"]} /></x.PackEnd>
265
267
  </GtkActionBar>
266
268
  ```
267
269
 
@@ -273,23 +275,23 @@ Bottom action bar.
273
275
 
274
276
  ```tsx
275
277
  <GtkMenuButton iconName="open-menu-symbolic">
276
- <Slot for={GtkMenuButton} id="popover">
278
+ <x.Slot for={GtkMenuButton} id="popover">
277
279
  <GtkPopoverMenu>
278
- <Menu.Section>
279
- <Menu.Item id="new" label="New" onActivate={handleNew} accels="<Control>n" />
280
- <Menu.Item id="open" label="Open" onActivate={handleOpen} />
281
- </Menu.Section>
282
- <Menu.Section>
283
- <Menu.Submenu label="Export">
284
- <Menu.Item id="pdf" label="PDF" onActivate={exportPdf} />
285
- <Menu.Item id="csv" label="CSV" onActivate={exportCsv} />
286
- </Menu.Submenu>
287
- </Menu.Section>
288
- <Menu.Section>
289
- <Menu.Item id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
290
- </Menu.Section>
280
+ <x.MenuSection>
281
+ <x.MenuItem id="new" label="New" onActivate={handleNew} accels="<Control>n" />
282
+ <x.MenuItem id="open" label="Open" onActivate={handleOpen} />
283
+ </x.MenuSection>
284
+ <x.MenuSection>
285
+ <x.MenuSubmenu label="Export">
286
+ <x.MenuItem id="pdf" label="PDF" onActivate={exportPdf} />
287
+ <x.MenuItem id="csv" label="CSV" onActivate={exportCsv} />
288
+ </x.MenuSubmenu>
289
+ </x.MenuSection>
290
+ <x.MenuSection>
291
+ <x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
292
+ </x.MenuSection>
291
293
  </GtkPopoverMenu>
292
- </Slot>
294
+ </x.Slot>
293
295
  </GtkMenuButton>
294
296
  ```
295
297
 
@@ -306,7 +308,7 @@ Bottom action bar.
306
308
  title="App"
307
309
  defaultWidth={800}
308
310
  defaultHeight={600}
309
- onCloseRequest={quit}
311
+ onClose={quit}
310
312
  >
311
313
  <Content />
312
314
  </GtkApplicationWindow>
@@ -315,9 +317,9 @@ Bottom action bar.
315
317
  Custom titlebar:
316
318
  ```tsx
317
319
  <GtkApplicationWindow ...>
318
- <Slot for={GtkWindow} id="titlebar">
320
+ <x.Slot for={GtkWindow} id="titlebar">
319
321
  <GtkHeaderBar />
320
- </Slot>
322
+ </x.Slot>
321
323
  <Content />
322
324
  </GtkApplicationWindow>
323
325
  ```
@@ -332,19 +334,19 @@ Import: `import * as Adw from "@gtkx/ffi/adw";`
332
334
  Modern app structure.
333
335
 
334
336
  ```tsx
335
- <AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
337
+ <AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onClose={quit}>
336
338
  <AdwToolbarView>
337
- <Toolbar.Top>
339
+ <x.ToolbarTop>
338
340
  <AdwHeaderBar>
339
- <Slot for={AdwHeaderBar} id="titleWidget">
341
+ <x.Slot for={AdwHeaderBar} id="titleWidget">
340
342
  <AdwWindowTitle title="App" subtitle="Description" />
341
- </Slot>
343
+ </x.Slot>
342
344
  </AdwHeaderBar>
343
- </Toolbar.Top>
345
+ </x.ToolbarTop>
344
346
  <MainContent />
345
- <Toolbar.Bottom>
347
+ <x.ToolbarBottom>
346
348
  <GtkActionBar />
347
- </Toolbar.Bottom>
349
+ </x.ToolbarBottom>
348
350
  </AdwToolbarView>
349
351
  </AdwApplicationWindow>
350
352
  ```
@@ -373,9 +375,9 @@ Settings UI.
373
375
  <AdwPreferencesGroup title="Appearance" description="Customize look">
374
376
  <AdwSwitchRow title="Dark Mode" active={dark} onActivated={() => setDark(!dark)} />
375
377
  <AdwActionRow title="Theme" subtitle="Select color">
376
- <Slot for={AdwActionRow} id="activatableWidget">
378
+ <x.Slot for={AdwActionRow} id="activatableWidget">
377
379
  <GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
378
- </Slot>
380
+ </x.Slot>
379
381
  </AdwActionRow>
380
382
  </AdwPreferencesGroup>
381
383
  </AdwPreferencesPage>
@@ -1,17 +1,32 @@
1
- import { useState } from "react";
2
1
  import * as Gtk from "@gtkx/ffi/gtk";
3
2
  import { GtkApplicationWindow, GtkBox, GtkButton, GtkLabel, quit } from "@gtkx/react";
3
+ import { useState } from "react";
4
4
 
5
- export default function App() {
5
+ export const App = () => {
6
6
  const [count, setCount] = useState(0);
7
7
 
8
8
  return (
9
- <GtkApplicationWindow title="<%= title %>" defaultWidth={400} defaultHeight={300} onCloseRequest={quit}>
10
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={20} marginTop={40} marginStart={40} marginEnd={40}>
11
- Welcome to GTKX!
12
- <GtkLabel label={`Count: ${count}`} />
13
- <GtkButton label="Increment" onClicked={() => setCount((c) => c + 1)} />
9
+ <GtkApplicationWindow title="<%= title %>" defaultWidth={400} defaultHeight={300} onClose={quit}>
10
+ <GtkBox
11
+ orientation={Gtk.Orientation.VERTICAL}
12
+ spacing={20}
13
+ marginTop={40}
14
+ marginBottom={40}
15
+ marginStart={40}
16
+ marginEnd={40}
17
+ valign={Gtk.Align.CENTER}
18
+ halign={Gtk.Align.CENTER}
19
+ >
20
+ <GtkLabel label="Welcome to GTKX!" cssClasses={["title-1"]} />
21
+ <GtkLabel label={`Count: ${count}`} cssClasses={["title-2"]} />
22
+ <GtkButton
23
+ label="Increment"
24
+ onClicked={() => setCount((c) => c + 1)}
25
+ cssClasses={["suggested-action", "pill"]}
26
+ />
14
27
  </GtkBox>
15
28
  </GtkApplicationWindow>
16
29
  );
17
- }
30
+ };
31
+
32
+ export default App;
@@ -1,4 +1,5 @@
1
1
  import { render } from "@gtkx/react";
2
- import App from "./app.js";
2
+ import pkg from "../package.json" with { type: "json" };
3
+ import { App } from "./app.js";
3
4
 
4
- render(<App />, "<%= appId %>");
5
+ render(<App />, pkg.gtkx.appId);