@gtkx/cli 0.9.3 → 0.10.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/bin/gtkx.js +2 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -0
- package/dist/create.d.ts +37 -6
- package/dist/create.js +44 -844
- package/dist/dev-server.d.ts +26 -4
- package/dist/dev-server.js +64 -9
- package/dist/refresh-runtime.d.ts +9 -0
- package/dist/refresh-runtime.js +44 -0
- package/dist/templates.d.ts +8 -0
- package/dist/templates.js +18 -0
- package/dist/vite-plugin-gtkx-refresh.d.ts +7 -0
- package/dist/vite-plugin-gtkx-refresh.js +36 -0
- package/dist/vite-plugin-swc-ssr-refresh.d.ts +7 -0
- package/dist/vite-plugin-swc-ssr-refresh.js +45 -0
- package/package.json +14 -5
- package/templates/claude/EXAMPLES.md.ejs +364 -0
- package/templates/claude/SKILL.md.ejs +372 -0
- package/templates/claude/WIDGETS.md.ejs +531 -0
- package/templates/config/jest.config.js.ejs +19 -0
- package/templates/config/vitest.config.ts.ejs +13 -0
- package/templates/gitignore.ejs +4 -0
- package/templates/package.json.ejs +15 -0
- package/templates/src/app.tsx.ejs +17 -0
- package/templates/src/dev.tsx.ejs +5 -0
- package/templates/src/index.tsx.ejs +4 -0
- package/templates/tests/app.test.tsx.ejs +27 -0
- package/templates/tsconfig.json.ejs +14 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
# GTKX Widget Reference
|
|
2
|
+
|
|
3
|
+
## Container Widgets
|
|
4
|
+
|
|
5
|
+
### GtkBox
|
|
6
|
+
Linear layout container.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
10
|
+
Child 1
|
|
11
|
+
Child 2
|
|
12
|
+
</GtkBox>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Props:
|
|
16
|
+
- `orientation`: `Gtk.Orientation.HORIZONTAL` | `Gtk.Orientation.VERTICAL`
|
|
17
|
+
- `spacing`: number (pixels between children)
|
|
18
|
+
- `homogeneous`: boolean (equal child sizes)
|
|
19
|
+
|
|
20
|
+
### GtkGrid
|
|
21
|
+
2D grid layout with explicit positioning.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<GtkGrid rowSpacing={5} columnSpacing={5}>
|
|
25
|
+
<GridChild column={0} row={0}>Top-left</GridChild>
|
|
26
|
+
<GridChild column={1} row={0} columnSpan={2}>Spans 2 columns</GridChild>
|
|
27
|
+
</GtkGrid>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
GridChild props (consumed, not passed to GTK):
|
|
31
|
+
- `column`: number (0-indexed)
|
|
32
|
+
- `row`: number (0-indexed)
|
|
33
|
+
- `columnSpan`: number (default 1)
|
|
34
|
+
- `rowSpan`: number (default 1)
|
|
35
|
+
|
|
36
|
+
### GtkStack
|
|
37
|
+
Shows one child at a time, switchable by name.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<GtkStack visibleChildName="page1">
|
|
41
|
+
<StackPage name="page1" title="First" iconName="document-new">
|
|
42
|
+
<Content1 />
|
|
43
|
+
</StackPage>
|
|
44
|
+
<StackPage name="page2" title="Second">
|
|
45
|
+
<Content2 />
|
|
46
|
+
</StackPage>
|
|
47
|
+
</GtkStack>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
StackPage props (consumed):
|
|
51
|
+
- `name`: string (required, unique identifier)
|
|
52
|
+
- `title`: string (display title)
|
|
53
|
+
- `iconName`: string (icon name)
|
|
54
|
+
- `needsAttention`: boolean (show attention indicator)
|
|
55
|
+
- `visible`: boolean (visibility in switchers)
|
|
56
|
+
- `useUnderline`: boolean (mnemonic underlines in title)
|
|
57
|
+
- `badgeNumber`: number (badge on indicator, AdwViewStack only)
|
|
58
|
+
|
|
59
|
+
### GtkNotebook
|
|
60
|
+
Tabbed container with visible tabs.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<GtkNotebook>
|
|
64
|
+
<Notebook.Page label="Tab 1">
|
|
65
|
+
<Content1 />
|
|
66
|
+
</Notebook.Page>
|
|
67
|
+
<Notebook.Page label="Tab 2">
|
|
68
|
+
<Content2 />
|
|
69
|
+
</Notebook.Page>
|
|
70
|
+
</GtkNotebook>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Notebook.Page props (consumed):
|
|
74
|
+
- `label`: string (tab label, optional when using Notebook.PageTab)
|
|
75
|
+
|
|
76
|
+
Custom tab widgets with Notebook.PageTab:
|
|
77
|
+
```tsx
|
|
78
|
+
<Notebook.Page>
|
|
79
|
+
<Notebook.PageTab>
|
|
80
|
+
<GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={4}>
|
|
81
|
+
<GtkImage iconName="folder-symbolic" />
|
|
82
|
+
<GtkLabel label="Files" />
|
|
83
|
+
</GtkBox>
|
|
84
|
+
</Notebook.PageTab>
|
|
85
|
+
<Content />
|
|
86
|
+
</Notebook.Page>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### GtkPaned
|
|
90
|
+
Resizable split container with draggable divider.
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<GtkPaned
|
|
94
|
+
orientation={Gtk.Orientation.HORIZONTAL}
|
|
95
|
+
position={280}
|
|
96
|
+
shrinkStartChild={false}
|
|
97
|
+
shrinkEndChild={false}
|
|
98
|
+
>
|
|
99
|
+
<Slot for={GtkPaned} id="startChild">
|
|
100
|
+
<SidePanel />
|
|
101
|
+
</Slot>
|
|
102
|
+
<Slot for={GtkPaned} id="endChild">
|
|
103
|
+
<MainContent />
|
|
104
|
+
</Slot>
|
|
105
|
+
</GtkPaned>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Props:
|
|
109
|
+
- `orientation`: `Gtk.Orientation.HORIZONTAL` | `Gtk.Orientation.VERTICAL`
|
|
110
|
+
- `position`: number (divider position in pixels)
|
|
111
|
+
- `shrinkStartChild`: boolean
|
|
112
|
+
- `shrinkEndChild`: boolean
|
|
113
|
+
|
|
114
|
+
## Virtual Scrolling Widgets
|
|
115
|
+
|
|
116
|
+
### ListView
|
|
117
|
+
High-performance scrollable list with virtual rendering and selection support.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
<ListView<Item>
|
|
121
|
+
vexpand
|
|
122
|
+
selected={[selectedId]}
|
|
123
|
+
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
124
|
+
onSelectionChanged={(ids) => setSelectedId(ids[0])}
|
|
125
|
+
renderItem={(item) => (
|
|
126
|
+
<GtkLabel label={item?.text ?? ""} />
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
{items.map(item => (
|
|
130
|
+
<ListItem key={item.id} id={item.id} value={item} />
|
|
131
|
+
))}
|
|
132
|
+
</ListView>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Props:
|
|
136
|
+
- `renderItem`: `(item: T | null) => ReactElement` (required)
|
|
137
|
+
- `selected`: string[] (array of selected item IDs)
|
|
138
|
+
- `selectionMode`: `Gtk.SelectionMode.SINGLE` | `MULTIPLE` | `NONE`
|
|
139
|
+
- `onSelectionChanged`: `(ids: string[]) => void`
|
|
140
|
+
|
|
141
|
+
ListItem props:
|
|
142
|
+
- `id`: string (required, unique identifier for selection)
|
|
143
|
+
- `value`: T (the data item)
|
|
144
|
+
|
|
145
|
+
### GridView
|
|
146
|
+
Grid-based virtual scrolling. Same API as ListView but renders items in a grid.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<GridView<Item>
|
|
150
|
+
vexpand
|
|
151
|
+
minColumns={2}
|
|
152
|
+
maxColumns={4}
|
|
153
|
+
renderItem={(item) => (
|
|
154
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL}>
|
|
155
|
+
<GtkImage iconName={item?.icon ?? "image-missing"} />
|
|
156
|
+
<GtkLabel label={item?.name ?? ""} />
|
|
157
|
+
</GtkBox>
|
|
158
|
+
)}
|
|
159
|
+
>
|
|
160
|
+
{items.map(item => (
|
|
161
|
+
<ListItem key={item.id} id={item.id} value={item} />
|
|
162
|
+
))}
|
|
163
|
+
</GridView>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### GtkColumnView
|
|
167
|
+
Table with sortable columns.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<GtkColumnView
|
|
171
|
+
sortColumn="name"
|
|
172
|
+
sortOrder={Gtk.SortType.ASCENDING}
|
|
173
|
+
onSortChange={(column, order) => handleSort(column, order)}
|
|
174
|
+
>
|
|
175
|
+
<ColumnViewColumn<Item>
|
|
176
|
+
title="Name"
|
|
177
|
+
id="name"
|
|
178
|
+
expand
|
|
179
|
+
resizable
|
|
180
|
+
sortable
|
|
181
|
+
renderCell={(item) => (
|
|
182
|
+
<GtkLabel label={item?.name ?? ""} />
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
{items.map(item => (
|
|
186
|
+
<ListItem key={item.id} id={item.id} value={item} />
|
|
187
|
+
))}
|
|
188
|
+
</GtkColumnView>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
ColumnViewColumn props:
|
|
192
|
+
- `title`: string (column header)
|
|
193
|
+
- `id`: string (used for sorting)
|
|
194
|
+
- `expand`: boolean (fill available space)
|
|
195
|
+
- `resizable`: boolean (user can resize)
|
|
196
|
+
- `sortable`: boolean (clicking header triggers sort)
|
|
197
|
+
- `fixedWidth`: number (fixed width in pixels)
|
|
198
|
+
- `renderCell`: `(item: T | null) => ReactElement`
|
|
199
|
+
|
|
200
|
+
### GtkDropDown
|
|
201
|
+
String selection dropdown with controlled state.
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
<GtkDropDown selectedId={selectedId} onSelectionChanged={setSelectedId}>
|
|
205
|
+
{options.map(opt => (
|
|
206
|
+
<SimpleListItem key={opt.id} id={opt.id} value={opt.label} />
|
|
207
|
+
))}
|
|
208
|
+
</GtkDropDown>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Props:
|
|
212
|
+
- `selectedId`: string (controlled selected item ID)
|
|
213
|
+
- `onSelectionChanged`: `(id: string) => void`
|
|
214
|
+
|
|
215
|
+
SimpleListItem props:
|
|
216
|
+
- `id`: string (unique identifier)
|
|
217
|
+
- `value`: string (display text)
|
|
218
|
+
|
|
219
|
+
### GtkOverlay
|
|
220
|
+
Stack widgets on top of each other. First child is the base layer.
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
<GtkOverlay>
|
|
224
|
+
<GtkButton label="Main" widthRequest={120} heightRequest={40} />
|
|
225
|
+
<GtkLabel
|
|
226
|
+
label="3"
|
|
227
|
+
cssClasses={["badge"]}
|
|
228
|
+
halign={Gtk.Align.END}
|
|
229
|
+
valign={Gtk.Align.START}
|
|
230
|
+
marginEnd={4}
|
|
231
|
+
marginTop={4}
|
|
232
|
+
/>
|
|
233
|
+
</GtkOverlay>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use `halign` and `valign` to position overlay children at corners or edges.
|
|
237
|
+
|
|
238
|
+
## Header Widgets
|
|
239
|
+
|
|
240
|
+
### GtkHeaderBar
|
|
241
|
+
Title bar with packed widgets at start and end.
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
<GtkHeaderBar>
|
|
245
|
+
<Pack.Start>
|
|
246
|
+
<GtkButton iconName="go-previous-symbolic" />
|
|
247
|
+
</Pack.Start>
|
|
248
|
+
<Slot for={GtkHeaderBar} id="titleWidget">
|
|
249
|
+
<GtkLabel label="My App" cssClasses={["title"]} />
|
|
250
|
+
</Slot>
|
|
251
|
+
<Pack.End>
|
|
252
|
+
<GtkMenuButton iconName="open-menu-symbolic" />
|
|
253
|
+
</Pack.End>
|
|
254
|
+
</GtkHeaderBar>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### GtkActionBar
|
|
258
|
+
Bottom action bar with start/end packing.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<GtkActionBar>
|
|
262
|
+
<Pack.Start>
|
|
263
|
+
<GtkButton label="Cancel" />
|
|
264
|
+
</Pack.Start>
|
|
265
|
+
<Pack.End>
|
|
266
|
+
<GtkButton label="Save" cssClasses={["suggested-action"]} />
|
|
267
|
+
</Pack.End>
|
|
268
|
+
</GtkActionBar>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Input Widgets
|
|
272
|
+
|
|
273
|
+
### GtkEntry
|
|
274
|
+
Single-line text input. Requires two-way binding for controlled behavior.
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
const [text, setText] = useState("");
|
|
278
|
+
|
|
279
|
+
<GtkEntry
|
|
280
|
+
text={text}
|
|
281
|
+
onChanged={(entry) => setText(entry.getText())}
|
|
282
|
+
placeholderText="Enter text..."
|
|
283
|
+
/>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### GtkToggleButton
|
|
287
|
+
Toggle button with controlled state. Auto-prevents signal feedback loops.
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
const [active, setActive] = useState(false);
|
|
291
|
+
|
|
292
|
+
<GtkToggleButton
|
|
293
|
+
active={active}
|
|
294
|
+
onToggled={() => setActive(!active)}
|
|
295
|
+
label="Toggle me"
|
|
296
|
+
/>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Display Widgets
|
|
300
|
+
|
|
301
|
+
### GtkLabel
|
|
302
|
+
```tsx
|
|
303
|
+
<GtkLabel label="Hello World" halign={Gtk.Align.START} wrap useMarkup />
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### GtkButton
|
|
307
|
+
```tsx
|
|
308
|
+
<GtkButton label="Click me" onClicked={() => handleClick()} iconName="document-new" />
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### GtkMenuButton
|
|
312
|
+
```tsx
|
|
313
|
+
<GtkMenuButton label="Options" iconName="open-menu">
|
|
314
|
+
<Slot for={GtkMenuButton} id="popover">
|
|
315
|
+
<GtkPopoverMenu>
|
|
316
|
+
<Menu.Item id="action" label="Action" onActivate={handle} />
|
|
317
|
+
</GtkPopoverMenu>
|
|
318
|
+
</Slot>
|
|
319
|
+
</GtkMenuButton>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Menu Widgets
|
|
323
|
+
|
|
324
|
+
### GtkPopoverMenu
|
|
325
|
+
```tsx
|
|
326
|
+
<GtkPopoverMenu>
|
|
327
|
+
<Menu.Section>
|
|
328
|
+
<Menu.Item id="new" label="New" onActivate={handleNew} accels="<Control>n" />
|
|
329
|
+
</Menu.Section>
|
|
330
|
+
<Menu.Section>
|
|
331
|
+
<Menu.Submenu label="File">
|
|
332
|
+
<Menu.Item id="open" label="Open" onActivate={handleOpen} />
|
|
333
|
+
<Menu.Item id="save" label="Save" onActivate={handleSave} />
|
|
334
|
+
</Menu.Submenu>
|
|
335
|
+
</Menu.Section>
|
|
336
|
+
<Menu.Section>
|
|
337
|
+
<Menu.Item id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
|
|
338
|
+
</Menu.Section>
|
|
339
|
+
</GtkPopoverMenu>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Menu.Item
|
|
343
|
+
Props:
|
|
344
|
+
- `id`: string (required, unique identifier)
|
|
345
|
+
- `label`: string
|
|
346
|
+
- `onActivate`: `() => void`
|
|
347
|
+
- `accels`: string | string[] (e.g., "<Control>n")
|
|
348
|
+
|
|
349
|
+
### Menu.Section
|
|
350
|
+
Groups menu items with optional label.
|
|
351
|
+
|
|
352
|
+
### Menu.Submenu
|
|
353
|
+
Nested submenu.
|
|
354
|
+
|
|
355
|
+
## Window Widgets
|
|
356
|
+
|
|
357
|
+
### GtkApplicationWindow
|
|
358
|
+
```tsx
|
|
359
|
+
<GtkApplicationWindow
|
|
360
|
+
title="My App"
|
|
361
|
+
defaultWidth={800}
|
|
362
|
+
defaultHeight={600}
|
|
363
|
+
showMenubar
|
|
364
|
+
onCloseRequest={quit}
|
|
365
|
+
>
|
|
366
|
+
<MainContent />
|
|
367
|
+
</GtkApplicationWindow>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Adwaita (Libadwaita) Widgets
|
|
371
|
+
|
|
372
|
+
Import from `@gtkx/ffi/adw` for enums and `@gtkx/react` for components.
|
|
373
|
+
|
|
374
|
+
### AdwApplicationWindow
|
|
375
|
+
Modern application window with required content slot.
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
<AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
|
|
379
|
+
<Slot for={AdwApplicationWindow} id="content">
|
|
380
|
+
<AdwToolbarView>...</AdwToolbarView>
|
|
381
|
+
</Slot>
|
|
382
|
+
</AdwApplicationWindow>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### AdwToolbarView
|
|
386
|
+
Layout container for apps with top/bottom toolbars.
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
<AdwToolbarView>
|
|
390
|
+
<Toolbar.Top>
|
|
391
|
+
<AdwHeaderBar />
|
|
392
|
+
</Toolbar.Top>
|
|
393
|
+
<MainContent />
|
|
394
|
+
<Toolbar.Bottom>
|
|
395
|
+
<GtkActionBar />
|
|
396
|
+
</Toolbar.Bottom>
|
|
397
|
+
</AdwToolbarView>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### AdwHeaderBar
|
|
401
|
+
Modern header bar with title widget slot.
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
<AdwHeaderBar>
|
|
405
|
+
<GtkButton iconName="go-previous-symbolic" />
|
|
406
|
+
<Slot for={AdwHeaderBar} id="titleWidget">
|
|
407
|
+
<AdwWindowTitle title="App" subtitle="Description" />
|
|
408
|
+
</Slot>
|
|
409
|
+
</AdwHeaderBar>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### AdwStatusPage
|
|
413
|
+
Welcome, error, or empty state pages.
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
<AdwStatusPage
|
|
417
|
+
iconName="applications-system-symbolic"
|
|
418
|
+
title="Welcome"
|
|
419
|
+
description="Description text"
|
|
420
|
+
vexpand
|
|
421
|
+
>
|
|
422
|
+
<GtkButton label="Get Started" cssClasses={["suggested-action", "pill"]} />
|
|
423
|
+
</AdwStatusPage>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### AdwBanner
|
|
427
|
+
Dismissable notification banner.
|
|
428
|
+
|
|
429
|
+
Props:
|
|
430
|
+
- `title`: string
|
|
431
|
+
- `buttonLabel`: string
|
|
432
|
+
- `revealed`: boolean
|
|
433
|
+
- `onButtonClicked`: `() => void`
|
|
434
|
+
|
|
435
|
+
### AdwPreferencesPage / AdwPreferencesGroup
|
|
436
|
+
Settings UI layout.
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<AdwPreferencesPage title="Settings">
|
|
440
|
+
<AdwPreferencesGroup title="Section" description="Help text">
|
|
441
|
+
<AdwActionRow title="Item" subtitle="Description" />
|
|
442
|
+
<AdwSwitchRow title="Toggle" active={value} onActivate={toggle} />
|
|
443
|
+
</AdwPreferencesGroup>
|
|
444
|
+
</AdwPreferencesPage>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### AdwActionRow
|
|
448
|
+
List row with title, subtitle, and optional widgets.
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
<AdwActionRow title="Setting" subtitle="Description">
|
|
452
|
+
<Slot for={AdwActionRow} id="activatableWidget">
|
|
453
|
+
<GtkImage iconName="go-next-symbolic" />
|
|
454
|
+
</Slot>
|
|
455
|
+
</AdwActionRow>
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### AdwSwitchRow
|
|
459
|
+
Toggle switch in a list row.
|
|
460
|
+
|
|
461
|
+
Props:
|
|
462
|
+
- `title`: string
|
|
463
|
+
- `subtitle`: string
|
|
464
|
+
- `active`: boolean
|
|
465
|
+
- `onActivate`: `() => void`
|
|
466
|
+
|
|
467
|
+
### AdwExpanderRow
|
|
468
|
+
Expandable row with nested children.
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
<AdwExpanderRow title="Advanced" subtitle="More options" iconName="preferences-system-symbolic">
|
|
472
|
+
<AdwActionRow title="Child 1" />
|
|
473
|
+
<AdwSwitchRow title="Child 2" active />
|
|
474
|
+
</AdwExpanderRow>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### AdwEntryRow / AdwPasswordEntryRow
|
|
478
|
+
Text input in a list row.
|
|
479
|
+
|
|
480
|
+
```tsx
|
|
481
|
+
<AdwEntryRow title="Username" text={text} onChanged={(e) => setText(e.getText())} />
|
|
482
|
+
<AdwPasswordEntryRow title="Password" />
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### AdwButtonRow
|
|
486
|
+
Button styled as a list row.
|
|
487
|
+
|
|
488
|
+
```tsx
|
|
489
|
+
<AdwButtonRow title="Add Item" startIconName="list-add-symbolic" />
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### AdwClamp
|
|
493
|
+
Limits content width for readability.
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
<AdwClamp maximumSize={600}>
|
|
497
|
+
<GtkBox>...</GtkBox>
|
|
498
|
+
</AdwClamp>
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### AdwAvatar
|
|
502
|
+
User avatar with initials fallback.
|
|
503
|
+
|
|
504
|
+
Props:
|
|
505
|
+
- `size`: number (32, 48, 64, 80, etc.)
|
|
506
|
+
- `text`: string (for initials)
|
|
507
|
+
- `showInitials`: boolean
|
|
508
|
+
|
|
509
|
+
### AdwSpinner
|
|
510
|
+
Loading spinner.
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
<AdwSpinner widthRequest={32} heightRequest={32} />
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### AdwWindowTitle
|
|
517
|
+
Title and subtitle for header bars.
|
|
518
|
+
|
|
519
|
+
```tsx
|
|
520
|
+
<AdwWindowTitle title="App Name" subtitle="Page description" />
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
## Common Props
|
|
524
|
+
|
|
525
|
+
All widgets support:
|
|
526
|
+
- `hexpand` / `vexpand`: boolean (expand to fill space)
|
|
527
|
+
- `halign` / `valign`: `Gtk.Align.START` | `CENTER` | `END` | `FILL`
|
|
528
|
+
- `marginStart` / `marginEnd` / `marginTop` / `marginBottom`: number
|
|
529
|
+
- `sensitive`: boolean (enabled/disabled)
|
|
530
|
+
- `visible`: boolean
|
|
531
|
+
- `cssClasses`: string[]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** @type {import('jest').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
preset: "ts-jest/presets/default-esm",
|
|
4
|
+
testEnvironment: "node",
|
|
5
|
+
testMatch: ["**/tests/**/*.test.ts"],
|
|
6
|
+
extensionsToTreatAsEsm: [".ts", ".tsx"],
|
|
7
|
+
moduleNameMapper: {
|
|
8
|
+
"^(\\.{1,2}/.*)\\.js$": "$1",
|
|
9
|
+
},
|
|
10
|
+
transform: {
|
|
11
|
+
"^.+\\.tsx?$": [
|
|
12
|
+
"ts-jest",
|
|
13
|
+
{
|
|
14
|
+
useESM: true,
|
|
15
|
+
tsconfig: "tsconfig.json",
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "gtkx dev src/dev.tsx",
|
|
8
|
+
"build": "tsc -b",
|
|
9
|
+
"start": "node dist/index.js"<% if (testing !== 'none') { %>,
|
|
10
|
+
"test": "<% if (testing === 'vitest') { %>GDK_BACKEND=x11 GSK_RENDERER=cairo LIBGL_ALWAYS_SOFTWARE=1 xvfb-run -a vitest<% } else if (testing === 'jest') { %>GDK_BACKEND=x11 GSK_RENDERER=cairo LIBGL_ALWAYS_SOFTWARE=1 xvfb-run -a jest<% } else if (testing === 'node') { %>GDK_BACKEND=x11 GSK_RENDERER=cairo LIBGL_ALWAYS_SOFTWARE=1 xvfb-run -a node --import tsx --test tests/**/*.test.ts<% } %>"<% } %>
|
|
11
|
+
},
|
|
12
|
+
"gtkx": {
|
|
13
|
+
"appId": "<%= appId %>"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
|
+
import { GtkApplicationWindow, GtkBox, GtkButton, GtkLabel, quit } from "@gtkx/react";
|
|
4
|
+
|
|
5
|
+
export default function App() {
|
|
6
|
+
const [count, setCount] = useState(0);
|
|
7
|
+
|
|
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)} />
|
|
14
|
+
</GtkBox>
|
|
15
|
+
</GtkApplicationWindow>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<% if (testing === 'vitest') { -%>
|
|
2
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
3
|
+
<% } else if (testing === 'jest') { -%>
|
|
4
|
+
import { describe, it, expect, afterEach } from "@jest/globals";
|
|
5
|
+
<% } else { -%>
|
|
6
|
+
import { describe, it, after } from "node:test";
|
|
7
|
+
import { strict as assert } from "node:assert";
|
|
8
|
+
<% } -%>
|
|
9
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
10
|
+
import { cleanup, render, screen } from "@gtkx/testing";
|
|
11
|
+
import App from "../src/app.js";
|
|
12
|
+
|
|
13
|
+
<%= testing === 'node' ? 'after' : 'afterEach' %>(async () => {
|
|
14
|
+
await cleanup();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("App", () => {
|
|
18
|
+
it("renders the increment button", async () => {
|
|
19
|
+
await render(<App />, { wrapper: false });
|
|
20
|
+
const button = await screen.findByRole(Gtk.AccessibleRole.BUTTON, { name: "Increment" });
|
|
21
|
+
<% if (testing === 'node') { -%>
|
|
22
|
+
assert.ok(button, "Button should be rendered");
|
|
23
|
+
<% } else { -%>
|
|
24
|
+
expect(button).toBeDefined();
|
|
25
|
+
<% } -%>
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"rootDir": "src"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|