@gtkx/cli 0.6.0 → 0.7.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.
Files changed (2) hide show
  1. package/dist/create.js +50 -46
  2. package/package.json +4 -4
package/dist/create.js CHANGED
@@ -573,7 +573,7 @@ const generateExamplesMd = () => {
573
573
  ### Basic App with State
574
574
 
575
575
  \`\`\`tsx
576
- import { Orientation } from "@gtkx/ffi/gtk";
576
+ import * as Gtk from "@gtkx/ffi/gtk";
577
577
  import { ApplicationWindow, Box, Label, quit } from "@gtkx/react";
578
578
  import { useCallback, useState } from "react";
579
579
 
@@ -602,7 +602,7 @@ export const App = () => {
602
602
 
603
603
  return (
604
604
  <ApplicationWindow title="Todo App" defaultWidth={400} defaultHeight={500} onCloseRequest={quit}>
605
- <Box orientation={Orientation.VERTICAL} spacing={16} marginTop={16} marginStart={16} marginEnd={16}>
605
+ <Box orientation={Gtk.Orientation.VERTICAL} spacing={16} marginTop={16} marginStart={16} marginEnd={16}>
606
606
  <Label label="Todo App" />
607
607
  </Box>
608
608
  </ApplicationWindow>
@@ -751,8 +751,8 @@ const MenuDemo = () => {
751
751
  ### List Item Component
752
752
 
753
753
  \`\`\`tsx
754
+ import * as Gtk from "@gtkx/ffi/gtk";
754
755
  import { Box, Button, CheckButton, Label } from "@gtkx/react";
755
- import { Orientation } from "@gtkx/ffi/gtk";
756
756
 
757
757
  interface Todo {
758
758
  id: number;
@@ -767,7 +767,7 @@ interface TodoItemProps {
767
767
  }
768
768
 
769
769
  export const TodoItem = ({ todo, onToggle, onDelete }: TodoItemProps) => (
770
- <Box orientation={Orientation.HORIZONTAL} spacing={8}>
770
+ <Box orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
771
771
  <CheckButton active={todo.completed} onToggled={() => onToggle(todo.id)} />
772
772
  <Label label={todo.text} hexpand cssClasses={todo.completed ? ["dim-label"] : []} />
773
773
  <Button iconName="edit-delete-symbolic" onClicked={() => onDelete(todo.id)} cssClasses={["flat"]} />
@@ -786,7 +786,7 @@ import { strict as assert } from "node:assert";`;
786
786
  const afterEachFn = testing === "node" ? "after" : "afterEach";
787
787
  const assertion = testing === "node" ? `assert.ok(button, "Button should be rendered");` : `expect(button).toBeDefined();`;
788
788
  return `${imports}
789
- import { AccessibleRole } from "@gtkx/ffi/gtk";
789
+ import * as Gtk from "@gtkx/ffi/gtk";
790
790
  import { cleanup, render, screen } from "@gtkx/testing";
791
791
  import App from "../src/app.js";
792
792
 
@@ -797,7 +797,7 @@ ${afterEachFn}(async () => {
797
797
  describe("App", () => {
798
798
  it("renders the increment button", async () => {
799
799
  await render(<App />, { wrapper: false });
800
- const button = await screen.findByRole(AccessibleRole.BUTTON, { name: "Increment" });
800
+ const button = await screen.findByRole(Gtk.AccessibleRole.BUTTON, { name: "Increment" });
801
801
  ${assertion}
802
802
  });
803
803
  });
@@ -882,15 +882,16 @@ const suggestAppId = (name) => {
882
882
  const sanitized = name.replace(/-/g, "");
883
883
  return `org.gtkx.${sanitized}`;
884
884
  };
885
- /**
886
- * Creates a new GTKX application with interactive prompts.
887
- * Scaffolds project structure, installs dependencies, and sets up configuration.
888
- * @param options - Pre-filled options to skip interactive prompts
889
- */
890
- export const createApp = async (options = {}) => {
891
- p.intro("Create GTKX App");
885
+ const checkCancelled = (value) => {
886
+ if (p.isCancel(value)) {
887
+ p.cancel("Operation cancelled");
888
+ process.exit(0);
889
+ }
890
+ return value;
891
+ };
892
+ const promptForOptions = async (options) => {
892
893
  const name = options.name ??
893
- (await p.text({
894
+ checkCancelled(await p.text({
894
895
  message: "Project name",
895
896
  placeholder: "my-app",
896
897
  validate: (value) => {
@@ -905,13 +906,9 @@ export const createApp = async (options = {}) => {
905
906
  return undefined;
906
907
  },
907
908
  }));
908
- if (p.isCancel(name)) {
909
- p.cancel("Operation cancelled");
910
- process.exit(0);
911
- }
912
909
  const defaultAppId = suggestAppId(name);
913
910
  const appId = options.appId ??
914
- (await p.text({
911
+ checkCancelled(await p.text({
915
912
  message: "App ID",
916
913
  placeholder: defaultAppId,
917
914
  initialValue: defaultAppId,
@@ -924,12 +921,8 @@ export const createApp = async (options = {}) => {
924
921
  return undefined;
925
922
  },
926
923
  }));
927
- if (p.isCancel(appId)) {
928
- p.cancel("Operation cancelled");
929
- process.exit(0);
930
- }
931
924
  const packageManager = options.packageManager ??
932
- (await p.select({
925
+ checkCancelled(await p.select({
933
926
  message: "Package manager",
934
927
  options: [
935
928
  { value: "pnpm", label: "pnpm", hint: "recommended" },
@@ -939,12 +932,8 @@ export const createApp = async (options = {}) => {
939
932
  ],
940
933
  initialValue: "pnpm",
941
934
  }));
942
- if (p.isCancel(packageManager)) {
943
- p.cancel("Operation cancelled");
944
- process.exit(0);
945
- }
946
935
  const testing = options.testing ??
947
- (await p.select({
936
+ checkCancelled(await p.select({
948
937
  message: "Testing framework",
949
938
  options: [
950
939
  { value: "vitest", label: "Vitest", hint: "recommended" },
@@ -954,22 +943,15 @@ export const createApp = async (options = {}) => {
954
943
  ],
955
944
  initialValue: "vitest",
956
945
  }));
957
- if (p.isCancel(testing)) {
958
- p.cancel("Operation cancelled");
959
- process.exit(0);
960
- }
961
946
  const claudeSkills = options.claudeSkills ??
962
- (await p.confirm({
947
+ checkCancelled(await p.confirm({
963
948
  message: "Include Claude Code skills?",
964
949
  initialValue: true,
965
950
  }));
966
- if (p.isCancel(claudeSkills)) {
967
- p.cancel("Operation cancelled");
968
- process.exit(0);
969
- }
970
- const projectPath = resolve(process.cwd(), name);
971
- const s = p.spinner();
972
- s.start("Creating project structure...");
951
+ return { name, appId, packageManager, testing, claudeSkills };
952
+ };
953
+ const scaffoldProject = (projectPath, resolved) => {
954
+ const { name, appId, testing, claudeSkills } = resolved;
973
955
  mkdirSync(projectPath, { recursive: true });
974
956
  mkdirSync(join(projectPath, "src"), { recursive: true });
975
957
  if (testing !== "none") {
@@ -998,9 +980,8 @@ export const createApp = async (options = {}) => {
998
980
  else if (testing === "node") {
999
981
  writeFileSync(join(projectPath, "tests", "app.test.tsx"), generateExampleTest(testing));
1000
982
  }
1001
- s.stop("Project structure created!");
1002
- const installSpinner = p.spinner();
1003
- installSpinner.start("Installing dependencies...");
983
+ };
984
+ const getDevDependencies = (testing) => {
1004
985
  const devDeps = [...DEV_DEPENDENCIES];
1005
986
  if (testing !== "none") {
1006
987
  devDeps.push(...TESTING_DEV_DEPENDENCIES[testing]);
@@ -1008,6 +989,11 @@ export const createApp = async (options = {}) => {
1008
989
  devDeps.push("tsx");
1009
990
  }
1010
991
  }
992
+ return devDeps;
993
+ };
994
+ const installDependencies = async (projectPath, name, packageManager, devDeps) => {
995
+ const installSpinner = p.spinner();
996
+ installSpinner.start("Installing dependencies...");
1011
997
  try {
1012
998
  const addCmd = getAddCommand(packageManager, DEPENDENCIES, false);
1013
999
  await runCommand(addCmd, projectPath);
@@ -1023,9 +1009,10 @@ export const createApp = async (options = {}) => {
1023
1009
  p.log.info(` ${getAddCommand(packageManager, DEPENDENCIES, false)}`);
1024
1010
  p.log.info(` ${getAddCommand(packageManager, devDeps, true)}`);
1025
1011
  }
1012
+ };
1013
+ const printNextSteps = (name, packageManager, testing) => {
1026
1014
  const runCmd = getRunCommand(packageManager);
1027
- const nextSteps = `cd ${name}
1028
- ${runCmd}`;
1015
+ const nextSteps = `cd ${name}\n${runCmd}`;
1029
1016
  const testingNote = testing !== "none"
1030
1017
  ? `
1031
1018
 
@@ -1035,3 +1022,20 @@ To run tests, you need xvfb installed:
1035
1022
  : "";
1036
1023
  p.note(`${nextSteps}${testingNote}`, "Next steps");
1037
1024
  };
1025
+ /**
1026
+ * Creates a new GTKX application with interactive prompts.
1027
+ * Scaffolds project structure, installs dependencies, and sets up configuration.
1028
+ * @param options - Pre-filled options to skip interactive prompts
1029
+ */
1030
+ export const createApp = async (options = {}) => {
1031
+ p.intro("Create GTKX App");
1032
+ const resolved = await promptForOptions(options);
1033
+ const projectPath = resolve(process.cwd(), resolved.name);
1034
+ const s = p.spinner();
1035
+ s.start("Creating project structure...");
1036
+ scaffoldProject(projectPath, resolved);
1037
+ s.stop("Project structure created!");
1038
+ const devDeps = getDevDependencies(resolved.testing);
1039
+ await installDependencies(projectPath, resolved.name, resolved.packageManager, devDeps);
1040
+ printNextSteps(resolved.name, resolved.packageManager, resolved.testing);
1041
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for GTKX - create and develop GTK4 React applications",
5
5
  "keywords": [
6
6
  "gtk",
@@ -46,9 +46,9 @@
46
46
  "@clack/prompts": "0.11.0",
47
47
  "@vitejs/plugin-react": "5.1.2",
48
48
  "citty": "0.1.6",
49
- "vite": "7.2.7",
50
- "@gtkx/ffi": "0.6.0",
51
- "@gtkx/react": "0.6.0"
49
+ "vite": "7.3.0",
50
+ "@gtkx/react": "0.7.0",
51
+ "@gtkx/ffi": "0.7.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^19"