@gtkx/cli 0.21.0 → 1.0.0-rc.2

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.
Files changed (358) hide show
  1. package/README.md +173 -0
  2. package/bin/gtkx-dev-runner.js +9 -0
  3. package/dist/builder.d.ts +5 -52
  4. package/dist/builder.d.ts.map +1 -1
  5. package/dist/builder.js +24 -57
  6. package/dist/builder.js.map +1 -1
  7. package/dist/cli.d.ts +1 -2
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +24 -122
  10. package/dist/cli.js.map +1 -1
  11. package/dist/codegen/freshness.d.ts +11 -0
  12. package/dist/codegen/freshness.d.ts.map +1 -0
  13. package/dist/codegen/freshness.js +45 -0
  14. package/dist/codegen/freshness.js.map +1 -0
  15. package/dist/codegen/report.d.ts +4 -0
  16. package/dist/codegen/report.d.ts.map +1 -0
  17. package/dist/codegen/report.js +18 -0
  18. package/dist/codegen/report.js.map +1 -0
  19. package/dist/codegen/run-codegen.d.ts +35 -0
  20. package/dist/codegen/run-codegen.d.ts.map +1 -0
  21. package/dist/codegen/run-codegen.js +162 -0
  22. package/dist/codegen/run-codegen.js.map +1 -0
  23. package/dist/codegen/store-resolver.d.ts +24 -0
  24. package/dist/codegen/store-resolver.d.ts.map +1 -0
  25. package/dist/codegen/store-resolver.js +72 -0
  26. package/dist/codegen/store-resolver.js.map +1 -0
  27. package/dist/commands/build.d.ts +10 -0
  28. package/dist/commands/build.d.ts.map +1 -0
  29. package/dist/commands/build.js +34 -0
  30. package/dist/commands/build.js.map +1 -0
  31. package/dist/commands/codegen.d.ts +10 -0
  32. package/dist/commands/codegen.d.ts.map +1 -0
  33. package/dist/commands/codegen.js +42 -0
  34. package/dist/commands/codegen.js.map +1 -0
  35. package/dist/commands/dev.d.ts +6 -0
  36. package/dist/commands/dev.d.ts.map +1 -0
  37. package/dist/commands/dev.js +22 -0
  38. package/dist/commands/dev.js.map +1 -0
  39. package/dist/commands/docs.d.ts +20 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +74 -0
  42. package/dist/commands/docs.js.map +1 -0
  43. package/dist/dev/icon-dir.d.ts +3 -0
  44. package/dist/dev/icon-dir.d.ts.map +1 -0
  45. package/dist/dev/icon-dir.js +16 -0
  46. package/dist/dev/icon-dir.js.map +1 -0
  47. package/dist/dev/refresh-tracker.d.ts +7 -0
  48. package/dist/dev/refresh-tracker.d.ts.map +1 -0
  49. package/dist/dev/refresh-tracker.js +19 -0
  50. package/dist/dev/refresh-tracker.js.map +1 -0
  51. package/dist/dev/runner-deps.d.ts +4 -0
  52. package/dist/dev/runner-deps.d.ts.map +1 -0
  53. package/dist/dev/runner-deps.js +55 -0
  54. package/dist/dev/runner-deps.js.map +1 -0
  55. package/dist/dev/runner-main.d.ts +3 -0
  56. package/dist/dev/runner-main.d.ts.map +1 -0
  57. package/dist/dev/runner-main.js +29 -0
  58. package/dist/dev/runner-main.js.map +1 -0
  59. package/dist/dev/runner.d.ts +24 -0
  60. package/dist/dev/runner.d.ts.map +1 -0
  61. package/dist/dev/runner.js +119 -0
  62. package/dist/dev/runner.js.map +1 -0
  63. package/dist/dev/schema-dir.d.ts +3 -0
  64. package/dist/dev/schema-dir.d.ts.map +1 -0
  65. package/dist/dev/schema-dir.js +12 -0
  66. package/dist/dev/schema-dir.js.map +1 -0
  67. package/dist/dev/supervisor.d.ts +18 -0
  68. package/dist/dev/supervisor.d.ts.map +1 -0
  69. package/dist/dev/supervisor.js +182 -0
  70. package/dist/dev/supervisor.js.map +1 -0
  71. package/dist/dev/vite-dev-server.d.ts +21 -0
  72. package/dist/dev/vite-dev-server.d.ts.map +1 -0
  73. package/dist/dev/vite-dev-server.js +13 -0
  74. package/dist/dev/vite-dev-server.js.map +1 -0
  75. package/dist/internal/banner.d.ts +4 -0
  76. package/dist/internal/banner.d.ts.map +1 -0
  77. package/dist/internal/banner.js +9 -0
  78. package/dist/internal/banner.js.map +1 -0
  79. package/dist/internal/data-dir.d.ts +4 -0
  80. package/dist/internal/data-dir.d.ts.map +1 -0
  81. package/dist/internal/data-dir.js +52 -0
  82. package/dist/internal/data-dir.js.map +1 -0
  83. package/dist/internal/entry-arg.d.ts +20 -0
  84. package/dist/internal/entry-arg.d.ts.map +1 -0
  85. package/dist/internal/entry-arg.js +36 -0
  86. package/dist/internal/entry-arg.js.map +1 -0
  87. package/dist/internal/errors.d.ts +5 -0
  88. package/dist/internal/errors.d.ts.map +1 -0
  89. package/dist/internal/errors.js +29 -0
  90. package/dist/internal/errors.js.map +1 -0
  91. package/dist/internal/list-files.d.ts +7 -0
  92. package/dist/internal/list-files.d.ts.map +1 -0
  93. package/dist/internal/list-files.js +15 -0
  94. package/dist/internal/list-files.js.map +1 -0
  95. package/dist/internal/resolve-cli-tool.d.ts +3 -0
  96. package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
  97. package/dist/internal/resolve-cli-tool.js +27 -0
  98. package/dist/internal/resolve-cli-tool.js.map +1 -0
  99. package/dist/internal/staging-dir.d.ts +4 -0
  100. package/dist/internal/staging-dir.d.ts.map +1 -0
  101. package/dist/internal/staging-dir.js +17 -0
  102. package/dist/internal/staging-dir.js.map +1 -0
  103. package/dist/internal/xdg-data-dirs.d.ts +3 -0
  104. package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
  105. package/dist/internal/xdg-data-dirs.js +10 -0
  106. package/dist/internal/xdg-data-dirs.js.map +1 -0
  107. package/dist/mcp/client.d.ts +28 -0
  108. package/dist/mcp/client.d.ts.map +1 -0
  109. package/dist/mcp/client.js +182 -0
  110. package/dist/mcp/client.js.map +1 -0
  111. package/dist/mcp/handlers.d.ts +9 -0
  112. package/dist/mcp/handlers.d.ts.map +1 -0
  113. package/dist/mcp/handlers.js +147 -0
  114. package/dist/mcp/handlers.js.map +1 -0
  115. package/dist/mcp/index.d.ts +4 -0
  116. package/dist/mcp/index.d.ts.map +1 -0
  117. package/dist/mcp/index.js +34 -0
  118. package/dist/mcp/index.js.map +1 -0
  119. package/dist/mcp/serialize-widget.d.ts +10 -0
  120. package/dist/mcp/serialize-widget.d.ts.map +1 -0
  121. package/dist/mcp/serialize-widget.js +23 -0
  122. package/dist/mcp/serialize-widget.js.map +1 -0
  123. package/dist/mcp/testing-loader.d.ts +5 -0
  124. package/dist/mcp/testing-loader.d.ts.map +1 -0
  125. package/dist/mcp/testing-loader.js +47 -0
  126. package/dist/mcp/testing-loader.js.map +1 -0
  127. package/dist/mcp/widget-registry.d.ts +14 -0
  128. package/dist/mcp/widget-registry.d.ts.map +1 -0
  129. package/dist/mcp/widget-registry.js +39 -0
  130. package/dist/mcp/widget-registry.js.map +1 -0
  131. package/dist/refresh-globals.d.ts +2 -0
  132. package/dist/refresh-globals.d.ts.map +1 -0
  133. package/dist/refresh-globals.js +8 -0
  134. package/dist/refresh-globals.js.map +1 -0
  135. package/dist/refresh-runtime.d.ts +10 -26
  136. package/dist/refresh-runtime.d.ts.map +1 -1
  137. package/dist/refresh-runtime.js +26 -48
  138. package/dist/refresh-runtime.js.map +1 -1
  139. package/dist/settings/compile.d.ts +3 -0
  140. package/dist/settings/compile.d.ts.map +1 -0
  141. package/dist/settings/compile.js +21 -0
  142. package/dist/settings/compile.js.map +1 -0
  143. package/dist/settings/parser.d.ts +24 -0
  144. package/dist/settings/parser.d.ts.map +1 -0
  145. package/dist/settings/parser.js +121 -0
  146. package/dist/settings/parser.js.map +1 -0
  147. package/dist/settings/render.d.ts +5 -0
  148. package/dist/settings/render.d.ts.map +1 -0
  149. package/dist/settings/render.js +122 -0
  150. package/dist/settings/render.js.map +1 -0
  151. package/dist/settings/schema.d.ts +13 -0
  152. package/dist/settings/schema.d.ts.map +1 -0
  153. package/dist/settings/schema.js +121 -0
  154. package/dist/settings/schema.js.map +1 -0
  155. package/dist/vite-plugins/asset-extensions.d.ts +5 -0
  156. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
  157. package/dist/vite-plugins/asset-extensions.js +28 -0
  158. package/dist/vite-plugins/asset-extensions.js.map +1 -0
  159. package/dist/vite-plugins/built-url.d.ts +4 -0
  160. package/dist/vite-plugins/built-url.d.ts.map +1 -0
  161. package/dist/vite-plugins/built-url.js +36 -0
  162. package/dist/vite-plugins/built-url.js.map +1 -0
  163. package/dist/vite-plugins/css.d.ts +4 -0
  164. package/dist/vite-plugins/css.d.ts.map +1 -0
  165. package/dist/vite-plugins/css.js +38 -0
  166. package/dist/vite-plugins/css.js.map +1 -0
  167. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +8 -0
  168. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
  169. package/dist/vite-plugins/fast-refresh/refresh-filter.js +19 -0
  170. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
  171. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +6 -0
  172. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
  173. package/dist/vite-plugins/fast-refresh/swc-refresh.js +70 -0
  174. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
  175. package/dist/vite-plugins/icons.d.ts +4 -0
  176. package/dist/vite-plugins/icons.d.ts.map +1 -0
  177. package/dist/vite-plugins/icons.js +51 -0
  178. package/dist/vite-plugins/icons.js.map +1 -0
  179. package/dist/vite-plugins/index.d.ts +4 -0
  180. package/dist/vite-plugins/index.d.ts.map +1 -0
  181. package/dist/vite-plugins/index.js +22 -0
  182. package/dist/vite-plugins/index.js.map +1 -0
  183. package/dist/vite-plugins/native.d.ts +4 -0
  184. package/dist/vite-plugins/native.d.ts.map +1 -0
  185. package/dist/vite-plugins/native.js +62 -0
  186. package/dist/vite-plugins/native.js.map +1 -0
  187. package/dist/vite-plugins/react-compiler.d.ts +5 -0
  188. package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
  189. package/dist/vite-plugins/react-compiler.js +62 -0
  190. package/dist/vite-plugins/react-compiler.js.map +1 -0
  191. package/dist/vite-plugins/react-dom-prebundle.d.ts +4 -0
  192. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
  193. package/dist/vite-plugins/react-dom-prebundle.js +12 -0
  194. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
  195. package/dist/vite-plugins/resource-init-module.d.ts +6 -0
  196. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
  197. package/dist/vite-plugins/resource-init-module.js +51 -0
  198. package/dist/vite-plugins/resource-init-module.js.map +1 -0
  199. package/dist/vite-plugins/resource-shared.d.ts +8 -0
  200. package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
  201. package/dist/vite-plugins/resource-shared.js +28 -0
  202. package/dist/vite-plugins/resource-shared.js.map +1 -0
  203. package/dist/vite-plugins/resources.d.ts +5 -0
  204. package/dist/vite-plugins/resources.d.ts.map +1 -0
  205. package/dist/vite-plugins/resources.js +260 -0
  206. package/dist/vite-plugins/resources.js.map +1 -0
  207. package/dist/vite-plugins/settings-worker-env.d.ts +4 -0
  208. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
  209. package/dist/vite-plugins/settings-worker-env.js +23 -0
  210. package/dist/vite-plugins/settings-worker-env.js.map +1 -0
  211. package/dist/vite-plugins/settings.d.ts +4 -0
  212. package/dist/vite-plugins/settings.d.ts.map +1 -0
  213. package/dist/vite-plugins/settings.js +214 -0
  214. package/dist/vite-plugins/settings.js.map +1 -0
  215. package/dist/vite-plugins/strip-query.d.ts +3 -0
  216. package/dist/vite-plugins/strip-query.d.ts.map +1 -0
  217. package/dist/vite-plugins/strip-query.js +6 -0
  218. package/dist/vite-plugins/strip-query.js.map +1 -0
  219. package/dist/vite-plugins/undeclared-library.d.ts +4 -0
  220. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
  221. package/dist/vite-plugins/undeclared-library.js +50 -0
  222. package/dist/vite-plugins/undeclared-library.js.map +1 -0
  223. package/dist/vite-plugins/virtual-module.d.ts +22 -0
  224. package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
  225. package/dist/vite-plugins/virtual-module.js +20 -0
  226. package/dist/vite-plugins/virtual-module.js.map +1 -0
  227. package/dist/vitest-plugin.d.ts +10 -0
  228. package/dist/vitest-plugin.d.ts.map +1 -0
  229. package/dist/vitest-plugin.js +12 -0
  230. package/dist/vitest-plugin.js.map +1 -0
  231. package/env.d.ts +91 -8
  232. package/package.json +30 -38
  233. package/src/babel-modules.d.ts +11 -0
  234. package/src/builder.ts +31 -86
  235. package/src/cli.ts +42 -0
  236. package/src/codegen/freshness.ts +65 -0
  237. package/src/codegen/report.ts +28 -0
  238. package/src/codegen/run-codegen.ts +260 -0
  239. package/src/codegen/store-resolver.ts +116 -0
  240. package/src/commands/build.ts +38 -0
  241. package/src/commands/codegen.ts +49 -0
  242. package/src/commands/dev.ts +24 -0
  243. package/src/commands/docs.ts +95 -0
  244. package/src/dev/icon-dir.ts +21 -0
  245. package/src/dev/refresh-tracker.ts +25 -0
  246. package/src/dev/runner-deps.ts +65 -0
  247. package/src/dev/runner-main.ts +34 -0
  248. package/src/dev/runner.ts +185 -0
  249. package/src/dev/schema-dir.ts +16 -0
  250. package/src/dev/supervisor.ts +276 -0
  251. package/src/dev/vite-dev-server.ts +36 -0
  252. package/src/internal/banner.ts +13 -0
  253. package/src/internal/data-dir.ts +70 -0
  254. package/src/internal/entry-arg.ts +46 -0
  255. package/src/internal/errors.ts +36 -0
  256. package/src/internal/list-files.ts +23 -0
  257. package/src/internal/resolve-cli-tool.ts +37 -0
  258. package/src/internal/staging-dir.ts +19 -0
  259. package/src/internal/xdg-data-dirs.ts +13 -0
  260. package/src/mcp/client.ts +235 -0
  261. package/src/mcp/handlers.ts +213 -0
  262. package/src/mcp/index.ts +46 -0
  263. package/src/mcp/serialize-widget.ts +41 -0
  264. package/src/mcp/testing-loader.ts +73 -0
  265. package/src/mcp/widget-registry.ts +49 -0
  266. package/src/refresh-globals.ts +10 -0
  267. package/src/refresh-runtime.ts +30 -55
  268. package/src/settings/compile.ts +22 -0
  269. package/src/settings/parser.ts +193 -0
  270. package/src/settings/render.ts +167 -0
  271. package/src/settings/schema.ts +169 -0
  272. package/src/vite-plugins/asset-extensions.ts +29 -0
  273. package/src/vite-plugins/built-url.ts +50 -0
  274. package/src/vite-plugins/css.ts +53 -0
  275. package/src/vite-plugins/fast-refresh/refresh-filter.ts +26 -0
  276. package/src/vite-plugins/fast-refresh/swc-refresh.ts +92 -0
  277. package/src/vite-plugins/icons.ts +74 -0
  278. package/src/vite-plugins/index.ts +25 -0
  279. package/src/vite-plugins/native.ts +83 -0
  280. package/src/vite-plugins/react-compiler.ts +84 -0
  281. package/src/vite-plugins/react-dom-prebundle.ts +17 -0
  282. package/src/vite-plugins/resource-init-module.ts +57 -0
  283. package/src/vite-plugins/resource-shared.ts +40 -0
  284. package/src/vite-plugins/resources.ts +366 -0
  285. package/src/vite-plugins/settings-worker-env.ts +30 -0
  286. package/src/vite-plugins/settings.ts +286 -0
  287. package/src/vite-plugins/strip-query.ts +7 -0
  288. package/src/vite-plugins/undeclared-library.ts +78 -0
  289. package/src/vite-plugins/virtual-module.ts +44 -0
  290. package/src/vitest-plugin.ts +13 -0
  291. package/dist/create.d.ts +0 -55
  292. package/dist/create.d.ts.map +0 -1
  293. package/dist/create.js +0 -236
  294. package/dist/create.js.map +0 -1
  295. package/dist/dev-server.d.ts +0 -37
  296. package/dist/dev-server.d.ts.map +0 -1
  297. package/dist/dev-server.js +0 -130
  298. package/dist/dev-server.js.map +0 -1
  299. package/dist/index.d.ts +0 -4
  300. package/dist/index.d.ts.map +0 -1
  301. package/dist/index.js +0 -4
  302. package/dist/index.js.map +0 -1
  303. package/dist/mcp-client.d.ts +0 -31
  304. package/dist/mcp-client.d.ts.map +0 -1
  305. package/dist/mcp-client.js +0 -430
  306. package/dist/mcp-client.js.map +0 -1
  307. package/dist/templates.d.ts +0 -9
  308. package/dist/templates.d.ts.map +0 -1
  309. package/dist/templates.js +0 -16
  310. package/dist/templates.js.map +0 -1
  311. package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
  312. package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
  313. package/dist/vite-plugin-gtkx-assets.js +0 -57
  314. package/dist/vite-plugin-gtkx-assets.js.map +0 -1
  315. package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
  316. package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
  317. package/dist/vite-plugin-gtkx-built-url.js +0 -43
  318. package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
  319. package/dist/vite-plugin-gtkx-gsettings.d.ts +0 -28
  320. package/dist/vite-plugin-gtkx-gsettings.d.ts.map +0 -1
  321. package/dist/vite-plugin-gtkx-gsettings.js +0 -147
  322. package/dist/vite-plugin-gtkx-gsettings.js.map +0 -1
  323. package/dist/vite-plugin-gtkx-native.d.ts +0 -14
  324. package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
  325. package/dist/vite-plugin-gtkx-native.js +0 -53
  326. package/dist/vite-plugin-gtkx-native.js.map +0 -1
  327. package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
  328. package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
  329. package/dist/vite-plugin-gtkx-refresh.js +0 -37
  330. package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
  331. package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
  332. package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
  333. package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
  334. package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
  335. package/src/cli.tsx +0 -154
  336. package/src/create.ts +0 -326
  337. package/src/dev-server.tsx +0 -162
  338. package/src/index.ts +0 -3
  339. package/src/mcp-client.ts +0 -518
  340. package/src/templates.ts +0 -26
  341. package/src/vite-plugin-gtkx-assets.ts +0 -69
  342. package/src/vite-plugin-gtkx-built-url.ts +0 -48
  343. package/src/vite-plugin-gtkx-gsettings.ts +0 -168
  344. package/src/vite-plugin-gtkx-native.ts +0 -64
  345. package/src/vite-plugin-gtkx-refresh.ts +0 -54
  346. package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
  347. package/templates/claude/EXAMPLES.md.ejs +0 -744
  348. package/templates/claude/SKILL.md.ejs +0 -93
  349. package/templates/claude/WIDGETS.md.ejs +0 -820
  350. package/templates/config/vitest.config.ts.ejs +0 -11
  351. package/templates/gitignore.ejs +0 -4
  352. package/templates/package.json.ejs +0 -16
  353. package/templates/src/app.tsx.ejs +0 -32
  354. package/templates/src/dev.tsx.ejs +0 -5
  355. package/templates/src/gtkx-env.d.ts.ejs +0 -1
  356. package/templates/src/index.tsx.ejs +0 -5
  357. package/templates/tests/app.test.tsx.ejs +0 -16
  358. package/templates/tsconfig.json.ejs +0 -14
@@ -1,744 +0,0 @@
1
- # GTKX Code Examples
2
-
3
- ## App Structure
4
-
5
- ### Minimal App
6
-
7
- ```tsx
8
- import { GtkApplicationWindow, GtkLabel, render, quit } from "@gtkx/react";
9
-
10
- const App = () => (
11
- <GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300} onClose={quit}>
12
- <GtkLabel label="Hello, World!" />
13
- </GtkApplicationWindow>
14
- );
15
-
16
- render(<App />, "com.example.hello");
17
- ```
18
-
19
- ### Modern Adwaita App
20
-
21
- ```tsx
22
- import * as Gtk from "@gtkx/ffi/gtk";
23
- import {
24
- AdwApplicationWindow,
25
- AdwHeaderBar,
26
- AdwToolbarView,
27
- AdwWindowTitle,
28
- AdwStatusPage,
29
- GtkButton,
30
- quit,
31
- x,
32
- } from "@gtkx/react";
33
-
34
- export const App = () => (
35
- <AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
36
- <AdwToolbarView>
37
- <x.ContainerSlot for={AdwToolbarView} id="addTopBar">
38
- <AdwHeaderBar>
39
- <x.Slot for={AdwHeaderBar} id="titleWidget">
40
- <AdwWindowTitle title="My App" subtitle="Welcome" />
41
- </x.Slot>
42
- </AdwHeaderBar>
43
- </x.ContainerSlot>
44
- <AdwStatusPage
45
- iconName="applications-system-symbolic"
46
- title="Welcome"
47
- description="Get started with your GTKX app"
48
- vexpand
49
- >
50
- <GtkButton label="Get Started" cssClasses={["suggested-action", "pill"]} halign={Gtk.Align.CENTER} />
51
- </AdwStatusPage>
52
- </AdwToolbarView>
53
- </AdwApplicationWindow>
54
- );
55
-
56
- export const appId = "com.example.myapp";
57
- ```
58
-
59
- ---
60
-
61
- ## State Management
62
-
63
- ### Controlled Form
64
-
65
- ```tsx
66
- import * as Gtk from "@gtkx/ffi/gtk";
67
- import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel, x } from "@gtkx/react";
68
- import { useState } from "react";
69
-
70
- const LoginForm = () => {
71
- const [email, setEmail] = useState("");
72
- const [password, setPassword] = useState("");
73
-
74
- const handleSubmit = () => {
75
- console.log("Login:", { email, password });
76
- };
77
-
78
- return (
79
- <GtkGrid rowSpacing={12} columnSpacing={12}>
80
- <x.GridChild column={0} row={0}>
81
- <GtkLabel label="Email:" halign={Gtk.Align.END} />
82
- </x.GridChild>
83
- <x.GridChild column={1} row={0}>
84
- <GtkEntry text={email} onChanged={(e) => setEmail(e.getText())} hexpand />
85
- </x.GridChild>
86
- <x.GridChild column={0} row={1}>
87
- <GtkLabel label="Password:" halign={Gtk.Align.END} />
88
- </x.GridChild>
89
- <x.GridChild column={1} row={1}>
90
- <GtkEntry text={password} onChanged={(e) => setPassword(e.getText())} visibility={false} hexpand />
91
- </x.GridChild>
92
- <x.GridChild column={0} row={2} columnSpan={2}>
93
- <GtkButton label="Login" onClicked={handleSubmit} cssClasses={["suggested-action"]} halign={Gtk.Align.END} />
94
- </x.GridChild>
95
- </GtkGrid>
96
- );
97
- };
98
- ```
99
-
100
- ### List with CRUD Operations
101
-
102
- ```tsx
103
- import * as Gtk from "@gtkx/ffi/gtk";
104
- import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow } from "@gtkx/react";
105
- import { useCallback, useState } from "react";
106
-
107
- interface Todo {
108
- id: string;
109
- text: string;
110
- }
111
-
112
- let nextId = 1;
113
-
114
- const TodoList = () => {
115
- const [todos, setTodos] = useState<Todo[]>([]);
116
- const [input, setInput] = useState("");
117
-
118
- const addTodo = useCallback(() => {
119
- if (!input.trim()) return;
120
- setTodos((prev) => [...prev, { id: String(nextId++), text: input }]);
121
- setInput("");
122
- }, [input]);
123
-
124
- const deleteTodo = useCallback((id: string) => {
125
- setTodos((prev) => prev.filter((t) => t.id !== id));
126
- }, []);
127
-
128
- return (
129
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
130
- <GtkBox spacing={8}>
131
- <GtkEntry text={input} onChanged={(e) => setInput(e.getText())} hexpand placeholderText="New todo..." />
132
- <GtkButton label="Add" onClicked={addTodo} cssClasses={["suggested-action"]} />
133
- </GtkBox>
134
- <GtkScrolledWindow vexpand cssClasses={["card"]}>
135
- <GtkListView
136
- items={todos.map((todo) => ({ id: todo.id, value: todo }))}
137
- renderItem={(todo: Todo) => (
138
- <GtkBox spacing={8} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
139
- <GtkLabel label={todo.text} hexpand halign={Gtk.Align.START} />
140
- <GtkButton iconName="edit-delete-symbolic" cssClasses={["flat"]} onClicked={() => deleteTodo(todo.id)} />
141
- </GtkBox>
142
- )}
143
- />
144
- </GtkScrolledWindow>
145
- </GtkBox>
146
- );
147
- };
148
- ```
149
-
150
- ---
151
-
152
- ## Navigation Patterns
153
-
154
- ### Stack with Sidebar Navigation
155
-
156
- ```tsx
157
- import * as Gtk from "@gtkx/ffi/gtk";
158
- import { GtkBox, GtkLabel, GtkListView, GtkPaned, GtkScrolledWindow, GtkStack, x } from "@gtkx/react";
159
- import { useState } from "react";
160
-
161
- interface Page {
162
- id: string;
163
- name: string;
164
- }
165
-
166
- const pages: Page[] = [
167
- { id: "home", name: "Home" },
168
- { id: "settings", name: "Settings" },
169
- { id: "about", name: "About" },
170
- ];
171
-
172
- const SidebarNav = () => {
173
- const [currentPage, setCurrentPage] = useState("home");
174
-
175
- return (
176
- <GtkPaned position={200}>
177
- <x.Slot for={GtkPaned} id="startChild">
178
- <GtkScrolledWindow cssClasses={["sidebar"]}>
179
- <GtkListView
180
- selected={[currentPage]}
181
- selectionMode={Gtk.SelectionMode.SINGLE}
182
- onSelectionChanged={(ids) => setCurrentPage(ids[0])}
183
- items={pages.map((page) => ({ id: page.id, value: page }))}
184
- renderItem={(page: Page) => (
185
- <GtkLabel label={page.name} halign={Gtk.Align.START} marginStart={12} marginTop={8} marginBottom={8} />
186
- )}
187
- />
188
- </GtkScrolledWindow>
189
- </x.Slot>
190
- <x.Slot for={GtkPaned} id="endChild">
191
- <GtkStack page={currentPage}>
192
- <x.StackPage id="home"><GtkLabel label="Home Content" vexpand /></x.StackPage>
193
- <x.StackPage id="settings"><GtkLabel label="Settings Content" vexpand /></x.StackPage>
194
- <x.StackPage id="about"><GtkLabel label="About Content" vexpand /></x.StackPage>
195
- </GtkStack>
196
- </x.Slot>
197
- </GtkPaned>
198
- );
199
- };
200
- ```
201
-
202
- ### Header Bar with Back Navigation
203
-
204
- ```tsx
205
- import * as Gtk from "@gtkx/ffi/gtk";
206
- import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow, quit, x } from "@gtkx/react";
207
- import { useState } from "react";
208
-
209
- const AppWithNavigation = () => {
210
- const [page, setPage] = useState("home");
211
-
212
- return (
213
- <GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400} onClose={quit}>
214
- <x.Slot for={GtkWindow} id="titlebar">
215
- <GtkHeaderBar>
216
- <x.ContainerSlot for={GtkHeaderBar} id="packStart">
217
- {page !== "home" && <GtkButton iconName="go-previous-symbolic" onClicked={() => setPage("home")} />}
218
- </x.ContainerSlot>
219
- <x.Slot for={GtkHeaderBar} id="titleWidget">
220
- <GtkLabel label={page === "home" ? "Home" : "Details"} cssClasses={["title"]} />
221
- </x.Slot>
222
- </GtkHeaderBar>
223
- </x.Slot>
224
- <GtkStack page={page}>
225
- <x.StackPage id="home">
226
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
227
- <GtkLabel label="Welcome" />
228
- <GtkButton label="Go to Details" onClicked={() => setPage("details")} />
229
- </GtkBox>
230
- </x.StackPage>
231
- <x.StackPage id="details">
232
- <GtkLabel label="Details Page Content" vexpand />
233
- </x.StackPage>
234
- </GtkStack>
235
- </GtkApplicationWindow>
236
- );
237
- };
238
- ```
239
-
240
- ---
241
-
242
- ## Settings Page
243
-
244
- ```tsx
245
- import * as Gtk from "@gtkx/ffi/gtk";
246
- import {
247
- AdwPreferencesPage,
248
- AdwPreferencesGroup,
249
- AdwActionRow,
250
- AdwSwitchRow,
251
- AdwExpanderRow,
252
- AdwEntryRow,
253
- GtkImage,
254
- GtkScrolledWindow,
255
- x,
256
- } from "@gtkx/react";
257
- import { useState } from "react";
258
-
259
- const SettingsPage = () => {
260
- const [darkMode, setDarkMode] = useState(false);
261
- const [notifications, setNotifications] = useState(true);
262
- const [username, setUsername] = useState("");
263
-
264
- return (
265
- <GtkScrolledWindow vexpand>
266
- <AdwPreferencesPage title="Settings">
267
- <AdwPreferencesGroup title="Appearance">
268
- <AdwSwitchRow title="Dark Mode" subtitle="Use dark color scheme" active={darkMode} onActivated={() => setDarkMode(!darkMode)} />
269
- </AdwPreferencesGroup>
270
-
271
- <AdwPreferencesGroup title="Account">
272
- <AdwEntryRow title="Username" text={username} onChanged={(e) => setUsername(e.getText())} />
273
- <AdwActionRow title="Profile" subtitle="Manage your profile">
274
- <x.Slot for={AdwActionRow} id="activatableWidget">
275
- <GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
276
- </x.Slot>
277
- </AdwActionRow>
278
- </AdwPreferencesGroup>
279
-
280
- <AdwPreferencesGroup title="Notifications">
281
- <AdwExpanderRow title="Notification Settings" subtitle="Configure alerts">
282
- <AdwSwitchRow title="Sound" active />
283
- <AdwSwitchRow title="Badges" active />
284
- <AdwSwitchRow title="Lock Screen" />
285
- </AdwExpanderRow>
286
- </AdwPreferencesGroup>
287
- </AdwPreferencesPage>
288
- </GtkScrolledWindow>
289
- );
290
- };
291
- ```
292
-
293
- ---
294
-
295
- ## Data Table with Sorting
296
-
297
- ```tsx
298
- import * as Gtk from "@gtkx/ffi/gtk";
299
- import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
300
- import { useMemo, useState } from "react";
301
-
302
- interface FileItem {
303
- id: string;
304
- name: string;
305
- size: number;
306
- modified: string;
307
- }
308
-
309
- const files: FileItem[] = [
310
- { id: "1", name: "document.pdf", size: 1024, modified: "2024-01-15" },
311
- { id: "2", name: "image.png", size: 2048, modified: "2024-01-14" },
312
- { id: "3", name: "notes.txt", size: 512, modified: "2024-01-13" },
313
- ];
314
-
315
- const FileTable = () => {
316
- const [sortColumn, setSortColumn] = useState("name");
317
- const [sortOrder, setSortOrder] = useState(Gtk.SortType.ASCENDING);
318
-
319
- const sortedFiles = useMemo(() => {
320
- const sorted = [...files].sort((a, b) => {
321
- const aVal = a[sortColumn as keyof FileItem];
322
- const bVal = b[sortColumn as keyof FileItem];
323
- const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
324
- return sortOrder === Gtk.SortType.ASCENDING ? cmp : -cmp;
325
- });
326
- return sorted;
327
- }, [sortColumn, sortOrder]);
328
-
329
- const handleSort = (column: string, order: Gtk.SortType) => {
330
- setSortColumn(column);
331
- setSortOrder(order);
332
- };
333
-
334
- return (
335
- <GtkScrolledWindow vexpand cssClasses={["card"]}>
336
- <GtkColumnView
337
- estimatedRowHeight={48}
338
- sortColumn={sortColumn}
339
- sortOrder={sortOrder}
340
- onSortChanged={handleSort}
341
- items={sortedFiles.map((file) => ({ id: file.id, value: file }))}
342
- >
343
- <x.ColumnViewColumn title="Name" id="name" expand sortable renderCell={(f: FileItem) => <GtkLabel label={f.name} />} />
344
- <x.ColumnViewColumn title="Size" id="size" fixedWidth={100} sortable renderCell={(f: FileItem) => <GtkLabel label={`${f.size} KB`} />} />
345
- <x.ColumnViewColumn title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f: FileItem) => <GtkLabel label={f.modified} />} />
346
- </GtkColumnView>
347
- </GtkScrolledWindow>
348
- );
349
- };
350
- ```
351
-
352
- ---
353
-
354
- ## Menu with Keyboard Shortcuts
355
-
356
- ```tsx
357
- import * as Gtk from "@gtkx/ffi/gtk";
358
- import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, quit, x } from "@gtkx/react";
359
- import { useState } from "react";
360
-
361
- const MenuDemo = () => {
362
- const [lastAction, setLastAction] = useState<string | null>(null);
363
-
364
- return (
365
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
366
- <GtkMenuButton label="File" halign={Gtk.Align.START}>
367
- <x.Slot for={GtkMenuButton} id="popover">
368
- <GtkPopoverMenu>
369
- <x.MenuSection>
370
- <x.MenuItem id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
371
- <x.MenuItem id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
372
- <x.MenuItem id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
373
- </x.MenuSection>
374
- <x.MenuSection>
375
- <x.MenuSubmenu label="Export">
376
- <x.MenuItem id="pdf" label="As PDF" onActivate={() => setLastAction("Export PDF")} />
377
- <x.MenuItem id="csv" label="As CSV" onActivate={() => setLastAction("Export CSV")} />
378
- </x.MenuSubmenu>
379
- </x.MenuSection>
380
- <x.MenuSection>
381
- <x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
382
- </x.MenuSection>
383
- </GtkPopoverMenu>
384
- </x.Slot>
385
- </GtkMenuButton>
386
- <GtkLabel label={`Last action: ${lastAction ?? "(none)"}`} />
387
- </GtkBox>
388
- );
389
- };
390
- ```
391
-
392
- ---
393
-
394
- ## Async Data Loading
395
-
396
- ```tsx
397
- import * as Gtk from "@gtkx/ffi/gtk";
398
- import { AdwSpinner, GtkBox, GtkLabel, GtkListView, GtkScrolledWindow } from "@gtkx/react";
399
- import { useEffect, useState } from "react";
400
-
401
- interface User {
402
- id: string;
403
- name: string;
404
- email: string;
405
- }
406
-
407
- const AsyncList = () => {
408
- const [users, setUsers] = useState<User[]>([]);
409
- const [loading, setLoading] = useState(true);
410
- const [error, setError] = useState<string | null>(null);
411
-
412
- useEffect(() => {
413
- const fetchUsers = async () => {
414
- try {
415
- const response = await fetch("https://api.example.com/users");
416
- const data = await response.json();
417
- setUsers(data);
418
- } catch (error) {
419
- setError(error instanceof Error ? error.message : "Failed to load");
420
- } finally {
421
- setLoading(false);
422
- }
423
- };
424
- fetchUsers();
425
- }, []);
426
-
427
- if (loading) {
428
- return (
429
- <GtkBox vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
430
- <AdwSpinner widthRequest={32} heightRequest={32} />
431
- </GtkBox>
432
- );
433
- }
434
-
435
- if (error) {
436
- return <GtkLabel label={`Error: ${error}`} cssClasses={["error"]} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} />;
437
- }
438
-
439
- return (
440
- <GtkScrolledWindow vexpand>
441
- <GtkListView
442
- items={users.map((user) => ({ id: user.id, value: user }))}
443
- renderItem={(user: User) => (
444
- <GtkBox orientation={Gtk.Orientation.VERTICAL} marginStart={12} marginTop={8} marginBottom={8}>
445
- <GtkLabel label={user.name} halign={Gtk.Align.START} cssClasses={["heading"]} />
446
- <GtkLabel label={user.email} halign={Gtk.Align.START} cssClasses={["dim-label"]} />
447
- </GtkBox>
448
- )}
449
- />
450
- </GtkScrolledWindow>
451
- );
452
- };
453
- ```
454
-
455
- ---
456
-
457
- ## Reusable Component Pattern
458
-
459
- ```tsx
460
- import * as Gtk from "@gtkx/ffi/gtk";
461
- import { GtkBox, GtkButton, GtkLabel } from "@gtkx/react";
462
- import type { ReactNode } from "react";
463
-
464
- interface CardProps {
465
- title: string;
466
- children: ReactNode;
467
- onAction?: () => void;
468
- actionLabel?: string;
469
- }
470
-
471
- const Card = ({ title, children, onAction, actionLabel }: CardProps) => (
472
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={8} cssClasses={["card"]} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
473
- <GtkLabel label={title} cssClasses={["title-4"]} halign={Gtk.Align.START} />
474
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={4}>
475
- {children}
476
- </GtkBox>
477
- {onAction && actionLabel && (
478
- <GtkButton label={actionLabel} onClicked={onAction} halign={Gtk.Align.END} cssClasses={["flat"]} />
479
- )}
480
- </GtkBox>
481
- );
482
-
483
- const CardDemo = () => (
484
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
485
- <Card title="Welcome" actionLabel="Learn More" onAction={() => console.log("clicked")}>
486
- <GtkLabel label="This is a reusable card component." wrap />
487
- </Card>
488
- <Card title="Features">
489
- <GtkLabel label="Build native GTK apps with React." wrap />
490
- </Card>
491
- </GtkBox>
492
- );
493
- ```
494
-
495
- ---
496
-
497
- ## Navigation with AdwNavigationView
498
-
499
- ```tsx
500
- import * as Gtk from "@gtkx/ffi/gtk";
501
- import {
502
- AdwApplicationWindow,
503
- AdwHeaderBar,
504
- AdwNavigationView,
505
- AdwToolbarView,
506
- GtkBox,
507
- GtkButton,
508
- GtkLabel,
509
- quit,
510
- x,
511
- } from "@gtkx/react";
512
- import { useState } from "react";
513
-
514
- const NavigationDemo = () => {
515
- const [history, setHistory] = useState(["home"]);
516
-
517
- const push = (page: string) => setHistory([...history, page]);
518
- const pop = () => setHistory(history.slice(0, -1));
519
-
520
- return (
521
- <AdwApplicationWindow title="Navigation Demo" defaultWidth={600} defaultHeight={400} onClose={quit}>
522
- <AdwNavigationView history={history} onHistoryChanged={setHistory}>
523
- <x.NavigationPage for={AdwNavigationView} id="home" title="Home">
524
- <AdwToolbarView>
525
- <x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
526
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
527
- <GtkLabel label="Welcome!" cssClasses={["title-1"]} />
528
- <GtkButton label="Go to Settings" onClicked={() => push("settings")} cssClasses={["suggested-action"]} />
529
- </GtkBox>
530
- </AdwToolbarView>
531
- </x.NavigationPage>
532
- <x.NavigationPage for={AdwNavigationView} id="settings" title="Settings" canPop>
533
- <AdwToolbarView>
534
- <x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
535
- <GtkLabel label="Settings page content" vexpand />
536
- </AdwToolbarView>
537
- </x.NavigationPage>
538
- </AdwNavigationView>
539
- </AdwApplicationWindow>
540
- );
541
- };
542
- ```
543
-
544
- ---
545
-
546
- ## Sidebar/Content Split with AdwNavigationSplitView
547
-
548
- ```tsx
549
- import * as Gtk from "@gtkx/ffi/gtk";
550
- import {
551
- AdwActionRow,
552
- AdwApplicationWindow,
553
- AdwHeaderBar,
554
- AdwNavigationSplitView,
555
- AdwToolbarView,
556
- GtkBox,
557
- GtkImage,
558
- GtkLabel,
559
- GtkListBox,
560
- GtkScrolledWindow,
561
- quit,
562
- x,
563
- } from "@gtkx/react";
564
- import { useState } from "react";
565
-
566
- interface Item {
567
- id: string;
568
- title: string;
569
- icon: string;
570
- }
571
-
572
- const items: Item[] = [
573
- { id: "inbox", title: "Inbox", icon: "mail-unread-symbolic" },
574
- { id: "starred", title: "Starred", icon: "starred-symbolic" },
575
- { id: "sent", title: "Sent", icon: "mail-send-symbolic" },
576
- ];
577
-
578
- const SplitViewDemo = () => {
579
- const [selected, setSelected] = useState(items[0]);
580
-
581
- return (
582
- <AdwApplicationWindow title="Split View Demo" defaultWidth={800} defaultHeight={500} onClose={quit}>
583
- <AdwNavigationSplitView sidebarWidthFraction={0.33} minSidebarWidth={200} maxSidebarWidth={300}>
584
- <x.NavigationPage for={AdwNavigationSplitView} id="sidebar" title="Mail">
585
- <AdwToolbarView>
586
- <x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
587
- <GtkScrolledWindow vexpand>
588
- <GtkListBox
589
- cssClasses={["navigation-sidebar"]}
590
- onRowSelected={(row) => {
591
- if (!row) return;
592
- const item = items[row.getIndex()];
593
- if (item) setSelected(item);
594
- }}
595
- >
596
- {items.map((item) => (
597
- <AdwActionRow key={item.id} title={item.title}>
598
- <x.ContainerSlot for={AdwActionRow} id="addPrefix">
599
- <GtkImage iconName={item.icon} />
600
- </x.ContainerSlot>
601
- </AdwActionRow>
602
- ))}
603
- </GtkListBox>
604
- </GtkScrolledWindow>
605
- </AdwToolbarView>
606
- </x.NavigationPage>
607
-
608
- <x.NavigationPage for={AdwNavigationSplitView} id="content" title={selected?.title ?? ""}>
609
- <AdwToolbarView>
610
- <x.ContainerSlot for={AdwToolbarView} id="addTopBar"><AdwHeaderBar /></x.ContainerSlot>
611
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} vexpand>
612
- <GtkImage iconName={selected?.icon ?? ""} iconSize={Gtk.IconSize.LARGE} />
613
- <GtkLabel label={selected?.title ?? ""} cssClasses={["title-2"]} />
614
- </GtkBox>
615
- </AdwToolbarView>
616
- </x.NavigationPage>
617
- </AdwNavigationSplitView>
618
- </AdwApplicationWindow>
619
- );
620
- };
621
- ```
622
-
623
- ---
624
-
625
- ## File Browser with GtkListView (tree)
626
-
627
- ```tsx
628
- import * as Gtk from "@gtkx/ffi/gtk";
629
- import { GtkBox, GtkImage, GtkLabel, GtkListView, GtkScrolledWindow } from "@gtkx/react";
630
- import { useState } from "react";
631
-
632
- interface FileNode {
633
- id: string;
634
- name: string;
635
- isDirectory: boolean;
636
- children?: FileNode[];
637
- }
638
-
639
- const files: FileNode[] = [
640
- {
641
- id: "src",
642
- name: "src",
643
- isDirectory: true,
644
- children: [
645
- { id: "src/app.tsx", name: "app.tsx", isDirectory: false },
646
- { id: "src/index.tsx", name: "index.tsx", isDirectory: false },
647
- ],
648
- },
649
- { id: "package.json", name: "package.json", isDirectory: false },
650
- ];
651
-
652
- const FileBrowser = () => {
653
- const [selected, setSelected] = useState<string | null>(null);
654
-
655
- return (
656
- <GtkScrolledWindow vexpand cssClasses={["card"]}>
657
- <GtkListView
658
- estimatedItemHeight={36}
659
- vexpand
660
- autoexpand={false}
661
- selectionMode={Gtk.SelectionMode.SINGLE}
662
- selected={selected ? [selected] : []}
663
- onSelectionChanged={(ids) => setSelected(ids[0] ?? null)}
664
- items={files.map((file) => ({
665
- id: file.id,
666
- value: file,
667
- children: file.children?.map((child) => ({ id: child.id, value: child })),
668
- }))}
669
- renderItem={(item: FileNode) => (
670
- <GtkBox spacing={8}>
671
- <GtkImage iconName={item.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
672
- <GtkLabel label={item.name} halign={Gtk.Align.START} />
673
- </GtkBox>
674
- )}
675
- />
676
- </GtkScrolledWindow>
677
- );
678
- };
679
- ```
680
-
681
- ---
682
-
683
- ## Stack with Programmatic Navigation
684
-
685
- ```tsx
686
- import * as Gtk from "@gtkx/ffi/gtk";
687
- import { GtkBox, GtkButton, GtkLabel, GtkStack, x } from "@gtkx/react";
688
- import { useState } from "react";
689
-
690
- const StackNavigation = () => {
691
- const [page, setPage] = useState("home");
692
-
693
- return (
694
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
695
- <GtkBox spacing={6} halign={Gtk.Align.CENTER}>
696
- <GtkButton label="Home" onClicked={() => setPage("home")} />
697
- <GtkButton label="Settings" onClicked={() => setPage("settings")} />
698
- </GtkBox>
699
- <GtkStack page={page} onPageChanged={setPage} vexpand>
700
- <x.StackPage id="home">
701
- <GtkLabel label="Home Content" />
702
- </x.StackPage>
703
- <x.StackPage id="settings">
704
- <GtkLabel label="Settings Content" />
705
- </x.StackPage>
706
- </GtkStack>
707
- </GtkBox>
708
- );
709
- };
710
- ```
711
-
712
- ---
713
-
714
- ## Animated Card with Toggle
715
-
716
- ```tsx
717
- import * as Gtk from "@gtkx/ffi/gtk";
718
- import { GtkBox, GtkButton, GtkLabel, x } from "@gtkx/react";
719
- import { useState } from "react";
720
-
721
- const AnimatedCard = () => {
722
- const [visible, setVisible] = useState(true);
723
-
724
- return (
725
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
726
- <GtkButton label={visible ? "Hide Card" : "Show Card"} onClicked={() => setVisible(!visible)} halign={Gtk.Align.START} />
727
- {visible && (
728
- <x.Animation
729
- initial={{ opacity: 0, scale: 0.8, translateY: -20 }}
730
- animate={{ opacity: 1, scale: 1, translateY: 0 }}
731
- exit={{ opacity: 0, scale: 0.8, translateY: 20 }}
732
- transition={{ mode: "spring", damping: 0.7, stiffness: 200 }}
733
- animateOnMount
734
- >
735
- <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={8} cssClasses={["card"]} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
736
- <GtkLabel label="Animated Card" cssClasses={["title-4"]} halign={Gtk.Align.START} />
737
- <GtkLabel label="This card animates in with a spring effect and fades out when dismissed." wrap />
738
- </GtkBox>
739
- </x.Animation>
740
- )}
741
- </GtkBox>
742
- );
743
- };
744
- ```