@gtkx/cli 0.13.2 → 0.14.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.
package/dist/dev-server.d.ts
CHANGED
package/dist/dev-server.js
CHANGED
|
@@ -21,7 +21,7 @@ import { swcSsrRefresh } from "./vite-plugin-swc-ssr-refresh.js";
|
|
|
21
21
|
* import { render } from "@gtkx/react";
|
|
22
22
|
*
|
|
23
23
|
* const server = await createDevServer({
|
|
24
|
-
*
|
|
24
|
+
* entry: "./src/dev.tsx",
|
|
25
25
|
* });
|
|
26
26
|
*
|
|
27
27
|
* const mod = await server.ssrLoadModule("./src/dev.tsx");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "CLI for GTKX - create and develop GTK4 React applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
@@ -58,19 +58,19 @@
|
|
|
58
58
|
"ejs": "^3.1.10",
|
|
59
59
|
"react-refresh": "^0.18.0",
|
|
60
60
|
"vite": "^7.3.1",
|
|
61
|
-
"@gtkx/ffi": "0.
|
|
62
|
-
"@gtkx/mcp": "0.
|
|
63
|
-
"@gtkx/react": "0.
|
|
61
|
+
"@gtkx/ffi": "0.14.0",
|
|
62
|
+
"@gtkx/mcp": "0.14.0",
|
|
63
|
+
"@gtkx/react": "0.14.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.
|
|
69
|
+
"@gtkx/testing": "0.14.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"react": "^19",
|
|
73
|
-
"@gtkx/testing": "0.
|
|
73
|
+
"@gtkx/testing": "0.14.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependenciesMeta": {
|
|
76
76
|
"@gtkx/testing": {
|
|
@@ -127,14 +127,14 @@ const TodoList = () => {
|
|
|
127
127
|
|
|
128
128
|
return (
|
|
129
129
|
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
|
|
130
|
-
<GtkBox
|
|
130
|
+
<GtkBox spacing={8}>
|
|
131
131
|
<GtkEntry text={input} onChanged={(e) => setInput(e.getText())} hexpand placeholderText="New todo..." />
|
|
132
132
|
<GtkButton label="Add" onClicked={addTodo} cssClasses={["suggested-action"]} />
|
|
133
133
|
</GtkBox>
|
|
134
134
|
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
135
135
|
<x.ListView<Todo>
|
|
136
136
|
renderItem={(todo) => (
|
|
137
|
-
<GtkBox
|
|
137
|
+
<GtkBox spacing={8} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
|
|
138
138
|
<GtkLabel label={todo?.text ?? ""} hexpand halign={Gtk.Align.START} />
|
|
139
139
|
<GtkButton iconName="edit-delete-symbolic" cssClasses={["flat"]} onClicked={() => todo && deleteTodo(todo.id)} />
|
|
140
140
|
</GtkBox>
|
|
@@ -176,7 +176,7 @@ const SidebarNav = () => {
|
|
|
176
176
|
const [currentPage, setCurrentPage] = useState("home");
|
|
177
177
|
|
|
178
178
|
return (
|
|
179
|
-
<GtkPaned
|
|
179
|
+
<GtkPaned position={200}>
|
|
180
180
|
<x.Slot for={GtkPaned} id="startChild">
|
|
181
181
|
<GtkScrolledWindow cssClasses={["sidebar"]}>
|
|
182
182
|
<x.ListView<Page>
|
|
@@ -287,7 +287,7 @@ const SettingsPage = () => {
|
|
|
287
287
|
<AdwExpanderRow title="Notification Settings" subtitle="Configure alerts">
|
|
288
288
|
<AdwSwitchRow title="Sound" active />
|
|
289
289
|
<AdwSwitchRow title="Badges" active />
|
|
290
|
-
<AdwSwitchRow title="Lock Screen"
|
|
290
|
+
<AdwSwitchRow title="Lock Screen" />
|
|
291
291
|
</AdwExpanderRow>
|
|
292
292
|
</AdwPreferencesGroup>
|
|
293
293
|
</AdwPreferencesPage>
|
|
@@ -549,6 +549,85 @@ const NavigationDemo = () => {
|
|
|
549
549
|
|
|
550
550
|
---
|
|
551
551
|
|
|
552
|
+
## Sidebar/Content Split with AdwNavigationSplitView
|
|
553
|
+
|
|
554
|
+
```tsx
|
|
555
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
556
|
+
import {
|
|
557
|
+
AdwActionRow,
|
|
558
|
+
AdwApplicationWindow,
|
|
559
|
+
AdwHeaderBar,
|
|
560
|
+
AdwNavigationSplitView,
|
|
561
|
+
AdwToolbarView,
|
|
562
|
+
GtkBox,
|
|
563
|
+
GtkImage,
|
|
564
|
+
GtkLabel,
|
|
565
|
+
GtkListBox,
|
|
566
|
+
GtkScrolledWindow,
|
|
567
|
+
quit,
|
|
568
|
+
x,
|
|
569
|
+
} from "@gtkx/react";
|
|
570
|
+
import { useState } from "react";
|
|
571
|
+
|
|
572
|
+
interface Item {
|
|
573
|
+
id: string;
|
|
574
|
+
title: string;
|
|
575
|
+
icon: string;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const items: Item[] = [
|
|
579
|
+
{ id: "inbox", title: "Inbox", icon: "mail-unread-symbolic" },
|
|
580
|
+
{ id: "starred", title: "Starred", icon: "starred-symbolic" },
|
|
581
|
+
{ id: "sent", title: "Sent", icon: "mail-send-symbolic" },
|
|
582
|
+
];
|
|
583
|
+
|
|
584
|
+
const SplitViewDemo = () => {
|
|
585
|
+
const [selected, setSelected] = useState(items[0]);
|
|
586
|
+
|
|
587
|
+
return (
|
|
588
|
+
<AdwApplicationWindow title="Split View Demo" defaultWidth={800} defaultHeight={500} onClose={quit}>
|
|
589
|
+
<AdwNavigationSplitView sidebarWidthFraction={0.33} minSidebarWidth={200} maxSidebarWidth={300}>
|
|
590
|
+
<x.NavigationPage id="sidebar" title="Mail">
|
|
591
|
+
<AdwToolbarView>
|
|
592
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
593
|
+
<GtkScrolledWindow vexpand>
|
|
594
|
+
<GtkListBox
|
|
595
|
+
cssClasses={["navigation-sidebar"]}
|
|
596
|
+
onRowSelected={(_, row) => {
|
|
597
|
+
if (!row) return;
|
|
598
|
+
const item = items[row.getIndex()];
|
|
599
|
+
if (item) setSelected(item);
|
|
600
|
+
}}
|
|
601
|
+
>
|
|
602
|
+
{items.map((item) => (
|
|
603
|
+
<AdwActionRow key={item.id} title={item.title}>
|
|
604
|
+
<x.ActionRowPrefix>
|
|
605
|
+
<GtkImage iconName={item.icon} />
|
|
606
|
+
</x.ActionRowPrefix>
|
|
607
|
+
</AdwActionRow>
|
|
608
|
+
))}
|
|
609
|
+
</GtkListBox>
|
|
610
|
+
</GtkScrolledWindow>
|
|
611
|
+
</AdwToolbarView>
|
|
612
|
+
</x.NavigationPage>
|
|
613
|
+
|
|
614
|
+
<x.NavigationPage id="content" title={selected?.title ?? ""}>
|
|
615
|
+
<AdwToolbarView>
|
|
616
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
617
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} vexpand>
|
|
618
|
+
<GtkImage iconName={selected?.icon ?? ""} iconSize={Gtk.IconSize.LARGE} />
|
|
619
|
+
<GtkLabel label={selected?.title ?? ""} cssClasses={["title-2"]} />
|
|
620
|
+
</GtkBox>
|
|
621
|
+
</AdwToolbarView>
|
|
622
|
+
</x.NavigationPage>
|
|
623
|
+
</AdwNavigationSplitView>
|
|
624
|
+
</AdwApplicationWindow>
|
|
625
|
+
);
|
|
626
|
+
};
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
552
631
|
## File Browser with TreeListView
|
|
553
632
|
|
|
554
633
|
```tsx
|
|
@@ -589,7 +668,7 @@ const FileBrowser = () => {
|
|
|
589
668
|
selected={selected ? [selected] : []}
|
|
590
669
|
onSelectionChanged={(ids) => setSelected(ids[0] ?? null)}
|
|
591
670
|
renderItem={(item) => (
|
|
592
|
-
<GtkBox
|
|
671
|
+
<GtkBox spacing={8}>
|
|
593
672
|
<GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
|
|
594
673
|
<GtkLabel label={item?.name ?? ""} halign={Gtk.Align.START} />
|
|
595
674
|
</GtkBox>
|
|
@@ -52,7 +52,7 @@ GTK signals map to `on<SignalName>` props: `clicked` → `onClicked`, `toggled`
|
|
|
52
52
|
Some widgets require children in specific slots:
|
|
53
53
|
|
|
54
54
|
```tsx
|
|
55
|
-
<GtkPaned
|
|
55
|
+
<GtkPaned>
|
|
56
56
|
<x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
|
|
57
57
|
<x.Slot for={GtkPaned} id="endChild"><Content /></x.Slot>
|
|
58
58
|
</GtkPaned>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
Linear layout (horizontal or vertical).
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
|
-
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}
|
|
23
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
24
24
|
{children}
|
|
25
25
|
</GtkBox>
|
|
26
26
|
```
|
|
@@ -68,7 +68,7 @@ Custom tab widget:
|
|
|
68
68
|
```tsx
|
|
69
69
|
<x.NotebookPage>
|
|
70
70
|
<x.NotebookPageTab>
|
|
71
|
-
<GtkBox
|
|
71
|
+
<GtkBox spacing={4}>
|
|
72
72
|
<GtkImage iconName="folder-symbolic" />
|
|
73
73
|
<GtkLabel label="Files" />
|
|
74
74
|
</GtkBox>
|
|
@@ -81,7 +81,7 @@ Custom tab widget:
|
|
|
81
81
|
Resizable split with draggable divider. **Requires Slot components.**
|
|
82
82
|
|
|
83
83
|
```tsx
|
|
84
|
-
<GtkPaned
|
|
84
|
+
<GtkPaned position={280} shrinkStartChild={false}>
|
|
85
85
|
<x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
|
|
86
86
|
<x.Slot for={GtkPaned} id="endChild"><MainContent /></x.Slot>
|
|
87
87
|
</GtkPaned>
|
|
@@ -99,6 +99,22 @@ Stack widgets on top of each other. First child is base layer, additional childr
|
|
|
99
99
|
</GtkOverlay>
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
### GtkFixed
|
|
103
|
+
Absolute positioning. Use `x.FixedChild` wrapper for children.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<GtkFixed>
|
|
107
|
+
<x.FixedChild x={20} y={30}>
|
|
108
|
+
<GtkLabel label="Top Left" />
|
|
109
|
+
</x.FixedChild>
|
|
110
|
+
<x.FixedChild x={200} y={100}>
|
|
111
|
+
<GtkLabel label="Middle" />
|
|
112
|
+
</x.FixedChild>
|
|
113
|
+
</GtkFixed>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**FixedChild props:** `x`, `y` (pixel coordinates)
|
|
117
|
+
|
|
102
118
|
### GtkScrolledWindow
|
|
103
119
|
Scrollable container.
|
|
104
120
|
|
|
@@ -193,7 +209,7 @@ Hierarchical tree with expand/collapse.
|
|
|
193
209
|
selected={selectedId ? [selectedId] : []}
|
|
194
210
|
onSelectionChanged={(ids) => setSelectedId(ids[0])}
|
|
195
211
|
renderItem={(item, row) => (
|
|
196
|
-
<GtkBox
|
|
212
|
+
<GtkBox spacing={8}>
|
|
197
213
|
<GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
|
|
198
214
|
<GtkLabel label={item?.name ?? ""} />
|
|
199
215
|
</GtkBox>
|
|
@@ -256,7 +272,6 @@ Slider with optional marks.
|
|
|
256
272
|
|
|
257
273
|
```tsx
|
|
258
274
|
<GtkScale
|
|
259
|
-
orientation={Gtk.Orientation.HORIZONTAL}
|
|
260
275
|
drawValue
|
|
261
276
|
valuePos={Gtk.PositionType.TOP}
|
|
262
277
|
>
|
|
@@ -466,7 +481,7 @@ Expandable settings row with optional action widget.
|
|
|
466
481
|
</x.ExpanderRowAction>
|
|
467
482
|
<x.ExpanderRowRow>
|
|
468
483
|
<AdwSwitchRow title="Option 1" active />
|
|
469
|
-
<AdwSwitchRow title="Option 2"
|
|
484
|
+
<AdwSwitchRow title="Option 2" />
|
|
470
485
|
</x.ExpanderRowRow>
|
|
471
486
|
</AdwExpanderRow>
|
|
472
487
|
```
|
|
@@ -512,6 +527,39 @@ const [history, setHistory] = useState(["home"]);
|
|
|
512
527
|
|
|
513
528
|
**NavigationPage props:** `id` (required), `title`, `canPop`. Control navigation via `history` array.
|
|
514
529
|
|
|
530
|
+
### AdwNavigationSplitView
|
|
531
|
+
Sidebar/content split layout for master-detail interfaces.
|
|
532
|
+
|
|
533
|
+
```tsx
|
|
534
|
+
const [selected, setSelected] = useState(items[0]);
|
|
535
|
+
|
|
536
|
+
<AdwNavigationSplitView sidebarWidthFraction={0.33} minSidebarWidth={200} maxSidebarWidth={300}>
|
|
537
|
+
<x.NavigationPage id="sidebar" title="Sidebar">
|
|
538
|
+
<AdwToolbarView>
|
|
539
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
540
|
+
<GtkListBox cssClasses={["navigation-sidebar"]} onRowSelected={(_, row) => {
|
|
541
|
+
if (!row) return;
|
|
542
|
+
const item = items[row.getIndex()];
|
|
543
|
+
if (item) setSelected(item);
|
|
544
|
+
}}>
|
|
545
|
+
{items.map((item) => <AdwActionRow key={item.id} title={item.title} />)}
|
|
546
|
+
</GtkListBox>
|
|
547
|
+
</AdwToolbarView>
|
|
548
|
+
</x.NavigationPage>
|
|
549
|
+
|
|
550
|
+
<x.NavigationPage id="content" title={selected?.title ?? ""}>
|
|
551
|
+
<AdwToolbarView>
|
|
552
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
553
|
+
<GtkLabel label={selected?.title ?? ""} />
|
|
554
|
+
</AdwToolbarView>
|
|
555
|
+
</x.NavigationPage>
|
|
556
|
+
</AdwNavigationSplitView>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Props:** `sidebarWidthFraction`, `minSidebarWidth`, `maxSidebarWidth`, `collapsed`, `showContent`.
|
|
560
|
+
**NavigationPage slots:** Use `id="sidebar"` for left pane, `id="content"` for right pane.
|
|
561
|
+
**Selection:** Use `GtkListBox` with `onRowSelected` (single click) not `onRowActivated` (double click).
|
|
562
|
+
|
|
515
563
|
### Other Adwaita Widgets
|
|
516
564
|
|
|
517
565
|
| Widget | Description |
|
|
@@ -521,3 +569,47 @@ const [history, setHistory] = useState(["home"]);
|
|
|
521
569
|
| `AdwSpinner` | Loading indicator |
|
|
522
570
|
| `AdwWindowTitle` | Title + subtitle for header bars |
|
|
523
571
|
| `AdwButtonRow` | Button styled as list row |
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Drag and Drop
|
|
576
|
+
|
|
577
|
+
All widgets support drag-and-drop through props. Use `onDragPrepare`, `onDragBegin`, and `onDragEnd` to make a widget draggable, and `dropTypes`, `onDrop`, `onDropEnter`, and `onDropLeave` to accept drops.
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
import * as Gdk from "@gtkx/ffi/gdk";
|
|
581
|
+
import * as GObject from "@gtkx/ffi/gobject";
|
|
582
|
+
import { typeFromName } from "@gtkx/ffi/gobject";
|
|
583
|
+
import { GtkButton, GtkBox, GtkLabel } from "@gtkx/react";
|
|
584
|
+
import { useState } from "react";
|
|
585
|
+
|
|
586
|
+
const DraggableButton = ({ label }: { label: string }) => {
|
|
587
|
+
const stringType = typeFromName("gchararray");
|
|
588
|
+
const value = new GObject.Value();
|
|
589
|
+
value.init(stringType);
|
|
590
|
+
value.setString(label);
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<GtkButton
|
|
594
|
+
label={label}
|
|
595
|
+
onDragPrepare={() => Gdk.ContentProvider.newForValue(value)}
|
|
596
|
+
/>
|
|
597
|
+
);
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
const DropZone = () => {
|
|
601
|
+
const [dropped, setDropped] = useState<string | null>(null);
|
|
602
|
+
const stringType = typeFromName("gchararray");
|
|
603
|
+
|
|
604
|
+
return (
|
|
605
|
+
<GtkBox
|
|
606
|
+
dropTypes={[stringType]}
|
|
607
|
+
onDrop={(value: GObject.Value) => {
|
|
608
|
+
setDropped(value.getString());
|
|
609
|
+
return true;
|
|
610
|
+
}}
|
|
611
|
+
>
|
|
612
|
+
<GtkLabel label={dropped ?? "Drop here"} />
|
|
613
|
+
</GtkBox>
|
|
614
|
+
);
|
|
615
|
+
};
|