@gtkx/cli 0.12.1 → 0.13.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/cli",
3
- "version": "0.12.1",
3
+ "version": "0.13.1",
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/ffi": "0.12.1",
62
- "@gtkx/mcp": "0.12.1",
63
- "@gtkx/react": "0.12.1"
60
+ "vite": "^7.3.1",
61
+ "@gtkx/ffi": "0.13.1",
62
+ "@gtkx/mcp": "0.13.1",
63
+ "@gtkx/react": "0.13.1"
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.1"
69
+ "@gtkx/testing": "0.13.1"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "react": "^19",
73
- "@gtkx/testing": "0.12.1"
73
+ "@gtkx/testing": "0.13.1"
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 {
@@ -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
  };
@@ -498,3 +497,144 @@ const CardDemo = () => (
498
497
  </GtkBox>
499
498
  );
500
499
  ```
500
+
501
+ ---
502
+
503
+ ## Navigation with AdwNavigationView
504
+
505
+ ```tsx
506
+ import * as Gtk from "@gtkx/ffi/gtk";
507
+ import {
508
+ AdwApplicationWindow,
509
+ AdwHeaderBar,
510
+ AdwNavigationView,
511
+ AdwToolbarView,
512
+ GtkBox,
513
+ GtkButton,
514
+ GtkLabel,
515
+ quit,
516
+ x,
517
+ } from "@gtkx/react";
518
+ import { useState } from "react";
519
+
520
+ const NavigationDemo = () => {
521
+ const [history, setHistory] = useState(["home"]);
522
+
523
+ const push = (page: string) => setHistory([...history, page]);
524
+ const pop = () => setHistory(history.slice(0, -1));
525
+
526
+ return (
527
+ <AdwApplicationWindow title="Navigation Demo" defaultWidth={600} defaultHeight={400} onClose={quit}>
528
+ <AdwNavigationView history={history} onHistoryChanged={setHistory}>
529
+ <x.NavigationPage id="home" title="Home">
530
+ <AdwToolbarView>
531
+ <x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
532
+ <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
533
+ <GtkLabel label="Welcome!" cssClasses={["title-1"]} />
534
+ <GtkButton label="Go to Settings" onClicked={() => push("settings")} cssClasses={["suggested-action"]} />
535
+ </GtkBox>
536
+ </AdwToolbarView>
537
+ </x.NavigationPage>
538
+ <x.NavigationPage id="settings" title="Settings" canPop>
539
+ <AdwToolbarView>
540
+ <x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
541
+ <GtkLabel label="Settings page content" vexpand />
542
+ </AdwToolbarView>
543
+ </x.NavigationPage>
544
+ </AdwNavigationView>
545
+ </AdwApplicationWindow>
546
+ );
547
+ };
548
+ ```
549
+
550
+ ---
551
+
552
+ ## File Browser with TreeListView
553
+
554
+ ```tsx
555
+ import * as Gtk from "@gtkx/ffi/gtk";
556
+ import { GtkBox, GtkImage, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
557
+ import { useState } from "react";
558
+
559
+ interface FileNode {
560
+ id: string;
561
+ name: string;
562
+ isDirectory: boolean;
563
+ children?: FileNode[];
564
+ }
565
+
566
+ const files: FileNode[] = [
567
+ {
568
+ id: "src",
569
+ name: "src",
570
+ isDirectory: true,
571
+ children: [
572
+ { id: "src/app.tsx", name: "app.tsx", isDirectory: false },
573
+ { id: "src/index.tsx", name: "index.tsx", isDirectory: false },
574
+ ],
575
+ },
576
+ { id: "package.json", name: "package.json", isDirectory: false },
577
+ ];
578
+
579
+ const FileBrowser = () => {
580
+ const [selected, setSelected] = useState<string | null>(null);
581
+
582
+ return (
583
+ <GtkScrolledWindow vexpand cssClasses={["card"]}>
584
+ <x.TreeListView<FileNode>
585
+ estimatedItemHeight={36}
586
+ vexpand
587
+ autoexpand={false}
588
+ selectionMode={Gtk.SelectionMode.SINGLE}
589
+ selected={selected ? [selected] : []}
590
+ onSelectionChanged={(ids) => setSelected(ids[0] ?? null)}
591
+ renderItem={(item) => (
592
+ <GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
593
+ <GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
594
+ <GtkLabel label={item?.name ?? ""} halign={Gtk.Align.START} />
595
+ </GtkBox>
596
+ )}
597
+ >
598
+ {files.map((file) => (
599
+ <x.TreeListItem key={file.id} id={file.id} value={file}>
600
+ {file.children?.map((child) => (
601
+ <x.TreeListItem key={child.id} id={child.id} value={child} />
602
+ ))}
603
+ </x.TreeListItem>
604
+ ))}
605
+ </x.TreeListView>
606
+ </GtkScrolledWindow>
607
+ );
608
+ };
609
+ ```
610
+
611
+ ---
612
+
613
+ ## Toggle Group View Switcher
614
+
615
+ ```tsx
616
+ import * as Gtk from "@gtkx/ffi/gtk";
617
+ import { AdwToggleGroup, GtkBox, GtkLabel, GtkStack, x } from "@gtkx/react";
618
+ import { useState } from "react";
619
+
620
+ const ViewSwitcher = () => {
621
+ const [view, setView] = useState("list");
622
+
623
+ return (
624
+ <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
625
+ <AdwToggleGroup active={view} onToggled={setView} halign={Gtk.Align.CENTER}>
626
+ <x.Toggle id="list" iconName="view-list-symbolic" tooltip="List view" />
627
+ <x.Toggle id="grid" iconName="view-grid-symbolic" tooltip="Grid view" />
628
+ </AdwToggleGroup>
629
+ <GtkStack page={view} vexpand>
630
+ <x.StackPage id="list">
631
+ <GtkLabel label="List View Content" />
632
+ </x.StackPage>
633
+ <x.StackPage id="grid">
634
+ <GtkLabel label="Grid View Content" />
635
+ </x.StackPage>
636
+ </GtkStack>
637
+ </GtkBox>
638
+ );
639
+ };
640
+ ```
@@ -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,10 +177,40 @@ 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
 
184
+ ### TreeListView
185
+ Hierarchical tree with expand/collapse.
186
+
187
+ ```tsx
188
+ <x.TreeListView<FileNode>
189
+ estimatedItemHeight={48}
190
+ vexpand
191
+ autoexpand={false}
192
+ selectionMode={Gtk.SelectionMode.SINGLE}
193
+ selected={selectedId ? [selectedId] : []}
194
+ onSelectionChanged={(ids) => setSelectedId(ids[0])}
195
+ renderItem={(item, row) => (
196
+ <GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
197
+ <GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
198
+ <GtkLabel label={item?.name ?? ""} />
199
+ </GtkBox>
200
+ )}
201
+ >
202
+ {files.map(file => (
203
+ <x.TreeListItem key={file.id} id={file.id} value={file}>
204
+ {file.children?.map(child => (
205
+ <x.TreeListItem key={child.id} id={child.id} value={child} />
206
+ ))}
207
+ </x.TreeListItem>
208
+ ))}
209
+ </x.TreeListView>
210
+ ```
211
+
212
+ **TreeListItem props:** `id`, `value`, `indentForDepth`, `indentForIcon`, `hideExpander`, nested `children`
213
+
182
214
  ---
183
215
 
184
216
  ## Inputs
@@ -219,6 +251,44 @@ Numeric input with increment/decrement.
219
251
  <GtkSpinButton value={count} onValueChanged={(sb) => setCount(sb.getValue())} />
220
252
  ```
221
253
 
254
+ ### GtkScale
255
+ Slider with optional marks.
256
+
257
+ ```tsx
258
+ <GtkScale
259
+ orientation={Gtk.Orientation.HORIZONTAL}
260
+ drawValue
261
+ valuePos={Gtk.PositionType.TOP}
262
+ >
263
+ <x.ScaleMark value={0} label="Min" />
264
+ <x.ScaleMark value={50} />
265
+ <x.ScaleMark value={100} label="Max" />
266
+ </GtkScale>
267
+ ```
268
+
269
+ **ScaleMark props:** `value` (required), `position`, `label`
270
+
271
+ ### GtkCalendar
272
+ Date picker with markable days.
273
+
274
+ ```tsx
275
+ <GtkCalendar onDaySelected={(cal) => setDate(cal.getDate())}>
276
+ <x.CalendarMark day={15} />
277
+ <x.CalendarMark day={20} />
278
+ </GtkCalendar>
279
+ ```
280
+
281
+ ### GtkLevelBar
282
+ Progress/level indicator with customizable thresholds.
283
+
284
+ ```tsx
285
+ <GtkLevelBar value={0.6}>
286
+ <x.LevelBarOffset id="low" value={0.25} />
287
+ <x.LevelBarOffset id="high" value={0.75} />
288
+ <x.LevelBarOffset id="full" value={1.0} />
289
+ </GtkLevelBar>
290
+ ```
291
+
222
292
  ---
223
293
 
224
294
  ## Display
@@ -247,11 +317,11 @@ Title bar with packed widgets. Use `Pack.Start`, `Pack.End`, and `Slot` for titl
247
317
 
248
318
  ```tsx
249
319
  <GtkHeaderBar>
250
- <Pack.Start><GtkButton iconName="go-previous-symbolic" /></Pack.Start>
251
- <Slot for={GtkHeaderBar} id="titleWidget">
320
+ <x.PackStart><GtkButton iconName="go-previous-symbolic" /></x.PackStart>
321
+ <x.Slot for={GtkHeaderBar} id="titleWidget">
252
322
  <GtkLabel label="Title" cssClasses={["title"]} />
253
- </Slot>
254
- <Pack.End><GtkMenuButton iconName="open-menu-symbolic" /></Pack.End>
323
+ </x.Slot>
324
+ <x.PackEnd><GtkMenuButton iconName="open-menu-symbolic" /></x.PackEnd>
255
325
  </GtkHeaderBar>
256
326
  ```
257
327
 
@@ -260,8 +330,8 @@ Bottom action bar.
260
330
 
261
331
  ```tsx
262
332
  <GtkActionBar>
263
- <Pack.Start><GtkButton label="Cancel" /></Pack.Start>
264
- <Pack.End><GtkButton label="Save" cssClasses={["suggested-action"]} /></Pack.End>
333
+ <x.PackStart><GtkButton label="Cancel" /></x.PackStart>
334
+ <x.PackEnd><GtkButton label="Save" cssClasses={["suggested-action"]} /></x.PackEnd>
265
335
  </GtkActionBar>
266
336
  ```
267
337
 
@@ -273,23 +343,23 @@ Bottom action bar.
273
343
 
274
344
  ```tsx
275
345
  <GtkMenuButton iconName="open-menu-symbolic">
276
- <Slot for={GtkMenuButton} id="popover">
346
+ <x.Slot for={GtkMenuButton} id="popover">
277
347
  <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>
348
+ <x.MenuSection>
349
+ <x.MenuItem id="new" label="New" onActivate={handleNew} accels="<Control>n" />
350
+ <x.MenuItem id="open" label="Open" onActivate={handleOpen} />
351
+ </x.MenuSection>
352
+ <x.MenuSection>
353
+ <x.MenuSubmenu label="Export">
354
+ <x.MenuItem id="pdf" label="PDF" onActivate={exportPdf} />
355
+ <x.MenuItem id="csv" label="CSV" onActivate={exportCsv} />
356
+ </x.MenuSubmenu>
357
+ </x.MenuSection>
358
+ <x.MenuSection>
359
+ <x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
360
+ </x.MenuSection>
291
361
  </GtkPopoverMenu>
292
- </Slot>
362
+ </x.Slot>
293
363
  </GtkMenuButton>
294
364
  ```
295
365
 
@@ -306,7 +376,7 @@ Bottom action bar.
306
376
  title="App"
307
377
  defaultWidth={800}
308
378
  defaultHeight={600}
309
- onCloseRequest={quit}
379
+ onClose={quit}
310
380
  >
311
381
  <Content />
312
382
  </GtkApplicationWindow>
@@ -315,9 +385,9 @@ Bottom action bar.
315
385
  Custom titlebar:
316
386
  ```tsx
317
387
  <GtkApplicationWindow ...>
318
- <Slot for={GtkWindow} id="titlebar">
388
+ <x.Slot for={GtkWindow} id="titlebar">
319
389
  <GtkHeaderBar />
320
- </Slot>
390
+ </x.Slot>
321
391
  <Content />
322
392
  </GtkApplicationWindow>
323
393
  ```
@@ -332,19 +402,19 @@ Import: `import * as Adw from "@gtkx/ffi/adw";`
332
402
  Modern app structure.
333
403
 
334
404
  ```tsx
335
- <AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
405
+ <AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onClose={quit}>
336
406
  <AdwToolbarView>
337
- <Toolbar.Top>
407
+ <x.ToolbarTop>
338
408
  <AdwHeaderBar>
339
- <Slot for={AdwHeaderBar} id="titleWidget">
409
+ <x.Slot for={AdwHeaderBar} id="titleWidget">
340
410
  <AdwWindowTitle title="App" subtitle="Description" />
341
- </Slot>
411
+ </x.Slot>
342
412
  </AdwHeaderBar>
343
- </Toolbar.Top>
413
+ </x.ToolbarTop>
344
414
  <MainContent />
345
- <Toolbar.Bottom>
415
+ <x.ToolbarBottom>
346
416
  <GtkActionBar />
347
- </Toolbar.Bottom>
417
+ </x.ToolbarBottom>
348
418
  </AdwToolbarView>
349
419
  </AdwApplicationWindow>
350
420
  ```
@@ -373,24 +443,36 @@ Settings UI.
373
443
  <AdwPreferencesGroup title="Appearance" description="Customize look">
374
444
  <AdwSwitchRow title="Dark Mode" active={dark} onActivated={() => setDark(!dark)} />
375
445
  <AdwActionRow title="Theme" subtitle="Select color">
376
- <Slot for={AdwActionRow} id="activatableWidget">
446
+ <x.ActionRowPrefix>
447
+ <GtkImage iconName="preferences-color-symbolic" />
448
+ </x.ActionRowPrefix>
449
+ <x.ActionRowSuffix>
377
450
  <GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
378
- </Slot>
451
+ </x.ActionRowSuffix>
379
452
  </AdwActionRow>
380
453
  </AdwPreferencesGroup>
381
454
  </AdwPreferencesPage>
382
455
  ```
383
456
 
457
+ **ActionRow children:** Use `x.ActionRowPrefix` for left widgets, `x.ActionRowSuffix` for right widgets, or `x.Slot for={AdwActionRow} id="activatableWidget"` for clickable suffix.
458
+
384
459
  ### AdwExpanderRow
385
- Expandable settings row.
460
+ Expandable settings row with optional action widget.
386
461
 
387
462
  ```tsx
388
463
  <AdwExpanderRow title="Advanced" subtitle="More options">
389
- <AdwSwitchRow title="Option 1" active />
390
- <AdwSwitchRow title="Option 2" active={false} />
464
+ <x.ExpanderRowAction>
465
+ <GtkButton iconName="emblem-system-symbolic" cssClasses={["flat"]} />
466
+ </x.ExpanderRowAction>
467
+ <x.ExpanderRowRow>
468
+ <AdwSwitchRow title="Option 1" active />
469
+ <AdwSwitchRow title="Option 2" active={false} />
470
+ </x.ExpanderRowRow>
391
471
  </AdwExpanderRow>
392
472
  ```
393
473
 
474
+ **ExpanderRow slots:** `x.ExpanderRowRow` for nested rows, `x.ExpanderRowAction` for header action widget. Direct children also work for simple cases.
475
+
394
476
  ### AdwEntryRow / AdwPasswordEntryRow
395
477
  Input in list row.
396
478
 
@@ -399,6 +481,37 @@ Input in list row.
399
481
  <AdwPasswordEntryRow title="Password" />
400
482
  ```
401
483
 
484
+ ### AdwToggleGroup
485
+ Segmented button group for mutually exclusive options.
486
+
487
+ ```tsx
488
+ <AdwToggleGroup active={viewMode} onToggled={setViewMode}>
489
+ <x.Toggle id="list" iconName="view-list-symbolic" tooltip="List view" />
490
+ <x.Toggle id="grid" iconName="view-grid-symbolic" tooltip="Grid view" />
491
+ <x.Toggle id="flow" label="Flow" />
492
+ </AdwToggleGroup>
493
+ ```
494
+
495
+ **Toggle props:** `id` (optional), `label`, `iconName`, `tooltip`, `enabled`
496
+
497
+ ### AdwNavigationView
498
+ Stack-based navigation with history.
499
+
500
+ ```tsx
501
+ const [history, setHistory] = useState(["home"]);
502
+
503
+ <AdwNavigationView history={history} onHistoryChanged={setHistory}>
504
+ <x.NavigationPage id="home" title="Home">
505
+ <GtkButton label="Go to Details" onClicked={() => setHistory([...history, "details"])} />
506
+ </x.NavigationPage>
507
+ <x.NavigationPage id="details" title="Details" canPop>
508
+ <GtkLabel label="Details content" />
509
+ </x.NavigationPage>
510
+ </AdwNavigationView>
511
+ ```
512
+
513
+ **NavigationPage props:** `id` (required), `title`, `canPop`. Control navigation via `history` array.
514
+
402
515
  ### Other Adwaita Widgets
403
516
 
404
517
  | Widget | Description |
@@ -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);