@gjsify/create-app 0.1.10 → 0.1.12

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 (40) hide show
  1. package/dist-templates/adw-canvas2d/package.json +27 -0
  2. package/dist-templates/adw-canvas2d/src/draw.ts +27 -0
  3. package/dist-templates/adw-canvas2d/src/index.ts +19 -0
  4. package/dist-templates/adw-canvas2d/src/main-window.blp +19 -0
  5. package/dist-templates/adw-canvas2d/src/main-window.ts +37 -0
  6. package/dist-templates/adw-canvas2d/tsconfig.json +20 -0
  7. package/dist-templates/adw-game/package.json +29 -0
  8. package/dist-templates/adw-game/src/game.ts +25 -0
  9. package/dist-templates/adw-game/src/index.ts +19 -0
  10. package/dist-templates/adw-game/src/main-window.blp +19 -0
  11. package/dist-templates/adw-game/src/main-window.ts +55 -0
  12. package/dist-templates/adw-game/tsconfig.json +20 -0
  13. package/dist-templates/adw-webgl/package.json +29 -0
  14. package/dist-templates/adw-webgl/src/index.ts +19 -0
  15. package/dist-templates/adw-webgl/src/main-window.blp +19 -0
  16. package/dist-templates/adw-webgl/src/main-window.ts +37 -0
  17. package/dist-templates/adw-webgl/src/scene.ts +35 -0
  18. package/dist-templates/adw-webgl/tsconfig.json +20 -0
  19. package/{templates → dist-templates/cli}/package.json +6 -2
  20. package/dist-templates/cli/src/index.ts +37 -0
  21. package/{templates → dist-templates/cli}/tsconfig.json +1 -1
  22. package/dist-templates/gtk-minimal/package.json +23 -0
  23. package/dist-templates/gtk-minimal/src/index.ts +40 -0
  24. package/dist-templates/gtk-minimal/tsconfig.json +14 -0
  25. package/dist-templates/web-server-express/package.json +24 -0
  26. package/dist-templates/web-server-express/src/index.ts +27 -0
  27. package/dist-templates/web-server-express/tsconfig.json +14 -0
  28. package/dist-templates/web-server-hono/package.json +24 -0
  29. package/dist-templates/web-server-hono/src/index.ts +21 -0
  30. package/dist-templates/web-server-hono/tsconfig.json +14 -0
  31. package/lib/create.d.ts +14 -1
  32. package/lib/create.js +91 -24
  33. package/lib/discover-templates.d.ts +14 -0
  34. package/lib/discover-templates.js +45 -0
  35. package/lib/index.d.ts +2 -1
  36. package/lib/index.js +42 -4
  37. package/lib/prompt-template.d.ts +6 -0
  38. package/lib/prompt-template.js +73 -0
  39. package/package.json +6 -2
  40. package/templates/src/index.ts +0 -64
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "Adwaita app with HTML Canvas 2D rendering (Blueprint UI).",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@girs/adw-1": "^1.10.0-4.0.0-rc.3",
15
+ "@girs/gio-2.0": "^2.88.0-4.0.0-rc.3",
16
+ "@girs/gjs": "^4.0.0-rc.3",
17
+ "@girs/gobject-2.0": "^2.88.0-4.0.0-rc.3",
18
+ "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.3",
19
+ "@gjsify/cli": "^0.1.12",
20
+ "@gjsify/esbuild-plugin-blueprint": "^0.1.12",
21
+ "@types/node": "^25.6.0",
22
+ "typescript": "^6.0.2"
23
+ },
24
+ "dependencies": {
25
+ "@gjsify/canvas2d": "^0.1.12"
26
+ }
27
+ }
@@ -0,0 +1,27 @@
1
+ export function startAnimation(canvas: HTMLCanvasElement): void {
2
+ const ctx = canvas.getContext('2d');
3
+ if (!ctx) return;
4
+
5
+ let t = 0;
6
+ const loop = () => {
7
+ const { width, height } = canvas;
8
+ ctx.fillStyle = '#1e1e2e';
9
+ ctx.fillRect(0, 0, width, height);
10
+
11
+ const cx = width / 2 + Math.cos(t * 0.02) * (width / 3);
12
+ const cy = height / 2 + Math.sin(t * 0.03) * (height / 3);
13
+
14
+ const grad = ctx.createRadialGradient(cx, cy, 10, cx, cy, 120);
15
+ grad.addColorStop(0, '#f5c2e7');
16
+ grad.addColorStop(1, 'rgba(245, 194, 231, 0)');
17
+ ctx.fillStyle = grad;
18
+ ctx.beginPath();
19
+ ctx.arc(cx, cy, 120, 0, Math.PI * 2);
20
+ ctx.fill();
21
+
22
+ t++;
23
+ requestAnimationFrame(loop);
24
+ };
25
+
26
+ requestAnimationFrame(loop);
27
+ }
@@ -0,0 +1,19 @@
1
+ import '@girs/gjs';
2
+ import '@girs/gtk-4.0';
3
+
4
+ import Adw from 'gi://Adw?version=1';
5
+ import Gio from 'gi://Gio?version=2.0';
6
+ import { MainWindow } from './main-window.js';
7
+
8
+ const app = new Adw.Application({
9
+ application_id: 'org.gjsify.example',
10
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
11
+ });
12
+
13
+ app.connect('activate', () => {
14
+ let win = app.get_active_window();
15
+ if (!win) win = new MainWindow(app);
16
+ win.present();
17
+ });
18
+
19
+ app.run([]);
@@ -0,0 +1,19 @@
1
+ using Gtk 4.0;
2
+ using Adw 1;
3
+
4
+ template $MainWindow: Adw.ApplicationWindow {
5
+ default-width: 800;
6
+ default-height: 600;
7
+ title: "new-gjsify-app — Canvas 2D";
8
+
9
+ content: Gtk.Box {
10
+ orientation: vertical;
11
+
12
+ Adw.HeaderBar {}
13
+
14
+ Gtk.Box canvasContainer {
15
+ hexpand: true;
16
+ vexpand: true;
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,37 @@
1
+ import GObject from 'gi://GObject?version=2.0';
2
+ import type Gtk from 'gi://Gtk?version=4.0';
3
+ import Adw from 'gi://Adw?version=1';
4
+ import { Canvas2DWidget } from '@gjsify/canvas2d';
5
+ import { startAnimation } from './draw.js';
6
+ import Template from './main-window.blp';
7
+
8
+ export class MainWindow extends Adw.ApplicationWindow {
9
+ declare private _canvasContainer: Gtk.Box;
10
+
11
+ static {
12
+ GObject.registerClass(
13
+ {
14
+ GTypeName: 'MainWindow',
15
+ Template,
16
+ InternalChildren: ['canvasContainer'],
17
+ },
18
+ this,
19
+ );
20
+ }
21
+
22
+ constructor(application: Adw.Application) {
23
+ super({ application });
24
+
25
+ const canvasWidget = new Canvas2DWidget();
26
+ canvasWidget.set_hexpand(true);
27
+ canvasWidget.set_vexpand(true);
28
+ canvasWidget.installGlobals();
29
+ this._canvasContainer.append(canvasWidget);
30
+
31
+ canvasWidget.onReady((canvas) => {
32
+ canvas.width = canvasWidget.get_allocated_width();
33
+ canvas.height = canvasWidget.get_allocated_height();
34
+ startAnimation(canvas);
35
+ });
36
+ }
37
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": [
9
+ "node",
10
+ "@girs/gjs",
11
+ "@girs/gtk-4.0",
12
+ "@girs/adw-1",
13
+ "@gjsify/esbuild-plugin-blueprint/types"
14
+ ],
15
+ "esModuleInterop": true,
16
+ "strict": true,
17
+ "skipLibCheck": true
18
+ },
19
+ "include": ["src"]
20
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "Adwaita game shell using Excalibur.js, WebGL → Canvas2D fallback.",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js --globals auto,dom",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js --globals auto,dom && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@girs/adw-1": "^1.10.0-4.0.0-rc.3",
15
+ "@girs/gio-2.0": "^2.88.0-4.0.0-rc.3",
16
+ "@girs/gjs": "^4.0.0-rc.3",
17
+ "@girs/gobject-2.0": "^2.88.0-4.0.0-rc.3",
18
+ "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.3",
19
+ "@gjsify/canvas2d": "^0.1.12",
20
+ "@gjsify/cli": "^0.1.12",
21
+ "@gjsify/esbuild-plugin-blueprint": "^0.1.12",
22
+ "@types/node": "^25.6.0",
23
+ "typescript": "^6.0.2"
24
+ },
25
+ "dependencies": {
26
+ "@gjsify/webgl": "^0.1.12",
27
+ "excalibur": "0.32.0"
28
+ }
29
+ }
@@ -0,0 +1,25 @@
1
+ import * as ex from 'excalibur';
2
+
3
+ export async function startGame(canvas: HTMLCanvasElement): Promise<ex.Engine> {
4
+ const engine = new ex.Engine({
5
+ canvasElement: canvas as unknown as HTMLCanvasElement,
6
+ width: canvas.width,
7
+ height: canvas.height,
8
+ backgroundColor: ex.Color.fromHex('#1e1e2e'),
9
+ suppressPlayButton: true,
10
+ });
11
+
12
+ const player = new ex.Actor({
13
+ pos: ex.vec(canvas.width / 2, canvas.height / 2),
14
+ width: 60,
15
+ height: 60,
16
+ color: ex.Color.fromHex('#89b4fa'),
17
+ });
18
+ player.actions.repeatForever((ctx) => {
19
+ ctx.rotateBy(Math.PI, 1);
20
+ });
21
+
22
+ engine.add(player);
23
+ await engine.start();
24
+ return engine;
25
+ }
@@ -0,0 +1,19 @@
1
+ import '@girs/gjs';
2
+ import '@girs/gtk-4.0';
3
+
4
+ import Adw from 'gi://Adw?version=1';
5
+ import Gio from 'gi://Gio?version=2.0';
6
+ import { MainWindow } from './main-window.js';
7
+
8
+ const app = new Adw.Application({
9
+ application_id: 'org.gjsify.example',
10
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
11
+ });
12
+
13
+ app.connect('activate', () => {
14
+ let win = app.get_active_window();
15
+ if (!win) win = new MainWindow(app);
16
+ win.present();
17
+ });
18
+
19
+ app.run([]);
@@ -0,0 +1,19 @@
1
+ using Gtk 4.0;
2
+ using Adw 1;
3
+
4
+ template $MainWindow: Adw.ApplicationWindow {
5
+ default-width: 800;
6
+ default-height: 600;
7
+ title: "new-gjsify-app — Game";
8
+
9
+ content: Gtk.Box {
10
+ orientation: vertical;
11
+
12
+ Adw.HeaderBar {}
13
+
14
+ Gtk.Box canvasContainer {
15
+ hexpand: true;
16
+ vexpand: true;
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,55 @@
1
+ import GObject from 'gi://GObject?version=2.0';
2
+ import type Gtk from 'gi://Gtk?version=4.0';
3
+ import Adw from 'gi://Adw?version=1';
4
+ import { CanvasWebGLWidget } from '@gjsify/webgl';
5
+ import { Canvas2DWidget } from '@gjsify/canvas2d';
6
+ import { startGame } from './game.js';
7
+ import Template from './main-window.blp';
8
+
9
+ export class MainWindow extends Adw.ApplicationWindow {
10
+ declare private _canvasContainer: Gtk.Box;
11
+
12
+ static {
13
+ GObject.registerClass(
14
+ {
15
+ GTypeName: 'MainWindow',
16
+ Template,
17
+ InternalChildren: ['canvasContainer'],
18
+ },
19
+ this,
20
+ );
21
+ }
22
+
23
+ constructor(application: Adw.Application) {
24
+ super({ application });
25
+ this._startWithWidget(false);
26
+ }
27
+
28
+ private _startWithWidget(useFallback: boolean): void {
29
+ let child = this._canvasContainer.get_first_child();
30
+ while (child) {
31
+ this._canvasContainer.remove(child);
32
+ child = this._canvasContainer.get_first_child();
33
+ }
34
+
35
+ const widget = useFallback ? new Canvas2DWidget() : new CanvasWebGLWidget();
36
+ widget.set_hexpand(true);
37
+ widget.set_vexpand(true);
38
+ widget.installGlobals();
39
+ this._canvasContainer.append(widget);
40
+
41
+ widget.onReady((canvas) => {
42
+ widget.grab_focus();
43
+ canvas.width = widget.get_allocated_width();
44
+ canvas.height = widget.get_allocated_height();
45
+ startGame(canvas).catch((err: Error) => {
46
+ if (useFallback) {
47
+ console.error('Canvas 2D fallback also failed:', err.message);
48
+ } else {
49
+ console.error('WebGL start failed, trying Canvas 2D fallback:', err.message);
50
+ this._startWithWidget(true);
51
+ }
52
+ });
53
+ });
54
+ }
55
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": [
9
+ "node",
10
+ "@girs/gjs",
11
+ "@girs/gtk-4.0",
12
+ "@girs/adw-1",
13
+ "@gjsify/esbuild-plugin-blueprint/types"
14
+ ],
15
+ "esModuleInterop": true,
16
+ "strict": true,
17
+ "skipLibCheck": true
18
+ },
19
+ "include": ["src"]
20
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "Adwaita app with WebGL + three.js (Blueprint UI).",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@girs/adw-1": "^1.10.0-4.0.0-rc.3",
15
+ "@girs/gio-2.0": "^2.88.0-4.0.0-rc.3",
16
+ "@girs/gjs": "^4.0.0-rc.3",
17
+ "@girs/gobject-2.0": "^2.88.0-4.0.0-rc.3",
18
+ "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.3",
19
+ "@gjsify/cli": "^0.1.12",
20
+ "@gjsify/esbuild-plugin-blueprint": "^0.1.12",
21
+ "@types/node": "^25.6.0",
22
+ "@types/three": "^0.183.1",
23
+ "typescript": "^6.0.2"
24
+ },
25
+ "dependencies": {
26
+ "@gjsify/webgl": "^0.1.12",
27
+ "three": "0.183.2"
28
+ }
29
+ }
@@ -0,0 +1,19 @@
1
+ import '@girs/gjs';
2
+ import '@girs/gtk-4.0';
3
+
4
+ import Adw from 'gi://Adw?version=1';
5
+ import Gio from 'gi://Gio?version=2.0';
6
+ import { MainWindow } from './main-window.js';
7
+
8
+ const app = new Adw.Application({
9
+ application_id: 'org.gjsify.example',
10
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
11
+ });
12
+
13
+ app.connect('activate', () => {
14
+ let win = app.get_active_window();
15
+ if (!win) win = new MainWindow(app);
16
+ win.present();
17
+ });
18
+
19
+ app.run([]);
@@ -0,0 +1,19 @@
1
+ using Gtk 4.0;
2
+ using Adw 1;
3
+
4
+ template $MainWindow: Adw.ApplicationWindow {
5
+ default-width: 800;
6
+ default-height: 600;
7
+ title: "new-gjsify-app — WebGL";
8
+
9
+ content: Gtk.Box {
10
+ orientation: vertical;
11
+
12
+ Adw.HeaderBar {}
13
+
14
+ Gtk.Box canvasContainer {
15
+ hexpand: true;
16
+ vexpand: true;
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,37 @@
1
+ import GObject from 'gi://GObject?version=2.0';
2
+ import type Gtk from 'gi://Gtk?version=4.0';
3
+ import Adw from 'gi://Adw?version=1';
4
+ import { CanvasWebGLWidget } from '@gjsify/webgl';
5
+ import { startScene } from './scene.js';
6
+ import Template from './main-window.blp';
7
+
8
+ export class MainWindow extends Adw.ApplicationWindow {
9
+ declare private _canvasContainer: Gtk.Box;
10
+
11
+ static {
12
+ GObject.registerClass(
13
+ {
14
+ GTypeName: 'MainWindow',
15
+ Template,
16
+ InternalChildren: ['canvasContainer'],
17
+ },
18
+ this,
19
+ );
20
+ }
21
+
22
+ constructor(application: Adw.Application) {
23
+ super({ application });
24
+
25
+ const glArea = new CanvasWebGLWidget();
26
+ glArea.set_hexpand(true);
27
+ glArea.set_vexpand(true);
28
+ glArea.installGlobals();
29
+ this._canvasContainer.append(glArea);
30
+
31
+ glArea.onReady((canvas) => {
32
+ canvas.width = glArea.get_allocated_width();
33
+ canvas.height = glArea.get_allocated_height();
34
+ startScene(canvas);
35
+ });
36
+ }
37
+ }
@@ -0,0 +1,35 @@
1
+ import * as THREE from 'three';
2
+
3
+ export function startScene(canvas: HTMLCanvasElement): void {
4
+ // Three.js r163+ requires WebGL2.
5
+ const gl2 = canvas.getContext('webgl2') as WebGL2RenderingContext | null;
6
+ if (!gl2) throw new Error('WebGL2 context unavailable');
7
+
8
+ const renderer = new THREE.WebGLRenderer({ canvas, context: gl2 });
9
+ renderer.setSize(canvas.width, canvas.height, false);
10
+ renderer.setClearColor(0x1e1e2e);
11
+
12
+ const scene = new THREE.Scene();
13
+ const camera = new THREE.PerspectiveCamera(60, canvas.width / canvas.height, 0.1, 100);
14
+ camera.position.z = 3;
15
+
16
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
17
+ const material = new THREE.MeshNormalMaterial();
18
+ const cube = new THREE.Mesh(geometry, material);
19
+ scene.add(cube);
20
+
21
+ canvas.addEventListener('resize', () => {
22
+ renderer.setSize(canvas.width, canvas.height, false);
23
+ camera.aspect = canvas.width / canvas.height;
24
+ camera.updateProjectionMatrix();
25
+ });
26
+
27
+ const loop = () => {
28
+ cube.rotation.x += 0.01;
29
+ cube.rotation.y += 0.013;
30
+ renderer.render(scene, camera);
31
+ requestAnimationFrame(loop);
32
+ };
33
+
34
+ requestAnimationFrame(loop);
35
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": [
9
+ "node",
10
+ "@girs/gjs",
11
+ "@girs/gtk-4.0",
12
+ "@girs/adw-1",
13
+ "@gjsify/esbuild-plugin-blueprint/types"
14
+ ],
15
+ "esModuleInterop": true,
16
+ "strict": true,
17
+ "skipLibCheck": true
18
+ },
19
+ "include": ["src"]
20
+ }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "new-gjsify-app",
3
+ "description": "Command-line tool using yargs (Node.js + GJS).",
3
4
  "version": "0.1.10",
4
5
  "type": "module",
5
6
  "private": true,
@@ -10,11 +11,14 @@
10
11
  "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
11
12
  },
12
13
  "devDependencies": {
13
- "@gjsify/cli": "^0.1.10",
14
+ "@gjsify/cli": "^0.1.12",
15
+ "@gjsify/node-globals": "^0.1.12",
16
+ "@gjsify/runtime": "^0.1.12",
14
17
  "@types/node": "^25.6.0",
18
+ "@types/yargs": "^17.0.35",
15
19
  "typescript": "^6.0.2"
16
20
  },
17
21
  "dependencies": {
18
- "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.2"
22
+ "yargs": "^18.0.0"
19
23
  }
20
24
  }
@@ -0,0 +1,37 @@
1
+ import { runtimeName } from '@gjsify/runtime';
2
+ import yargs from 'yargs';
3
+ import { hideBin } from 'yargs/helpers';
4
+
5
+ const c = {
6
+ reset: '\x1b[0m',
7
+ bold: '\x1b[1m',
8
+ dim: '\x1b[2m',
9
+ cyan: '\x1b[36m',
10
+ green: '\x1b[32m',
11
+ };
12
+
13
+ yargs(hideBin(process.argv))
14
+ .scriptName('new-gjsify-app')
15
+ .usage(`\n${c.bold}new-gjsify-app${c.reset} — a gjsify CLI starter`)
16
+ .command(
17
+ 'greet [name]',
18
+ 'Print a greeting',
19
+ (y) => y.positional('name', { type: 'string', default: 'world' }),
20
+ (args) => {
21
+ console.log(`${c.cyan}Hello, ${c.bold}${args.name}${c.reset}${c.cyan}!${c.reset}`);
22
+ },
23
+ )
24
+ .command(
25
+ 'info',
26
+ 'Show runtime info',
27
+ () => {},
28
+ () => {
29
+ console.log(`${c.dim}runtime :${c.reset} ${c.green}${runtimeName}${c.reset}`);
30
+ console.log(`${c.dim}platform:${c.reset} ${process.platform}`);
31
+ console.log(`${c.dim}pid :${c.reset} ${process.pid}`);
32
+ },
33
+ )
34
+ .demandCommand(1, 'Pass --help to see available commands.')
35
+ .strict()
36
+ .help()
37
+ .parse();
@@ -5,7 +5,7 @@
5
5
  "target": "ESNext",
6
6
  "module": "NodeNext",
7
7
  "moduleResolution": "NodeNext",
8
- "types": ["node", "@girs/gtk-4.0"],
8
+ "types": ["node"],
9
9
  "esModuleInterop": true,
10
10
  "strict": true,
11
11
  "skipLibCheck": true
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "Minimal GTK4 app — Gtk.Window + Gtk.Label (no Adwaita, no Blueprint).",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@gjsify/cli": "^0.1.12",
15
+ "@types/node": "^25.6.0",
16
+ "typescript": "^6.0.2"
17
+ },
18
+ "dependencies": {
19
+ "@girs/gio-2.0": "^2.88.0-4.0.0-rc.3",
20
+ "@girs/glib-2.0": "^2.88.0-4.0.0-rc.3",
21
+ "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.3"
22
+ }
23
+ }
@@ -0,0 +1,40 @@
1
+ import Gtk from 'gi://Gtk?version=4.0';
2
+ import Gio from 'gi://Gio?version=2.0';
3
+
4
+ const app = new Gtk.Application({
5
+ applicationId: 'org.gjsify.example',
6
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
7
+ });
8
+
9
+ app.connect('activate', () => {
10
+ const window = new Gtk.ApplicationWindow({
11
+ application: app,
12
+ title: 'new-gjsify-app',
13
+ defaultWidth: 480,
14
+ defaultHeight: 280,
15
+ });
16
+
17
+ const box = new Gtk.Box({
18
+ orientation: Gtk.Orientation.VERTICAL,
19
+ spacing: 12,
20
+ marginTop: 24,
21
+ marginBottom: 24,
22
+ marginStart: 24,
23
+ marginEnd: 24,
24
+ });
25
+
26
+ const title = new Gtk.Label({ label: 'Hello from gjsify!' });
27
+ title.add_css_class('title-2');
28
+
29
+ const hint = new Gtk.Label({
30
+ label: `Running on ${process.platform} · PID ${process.pid}`,
31
+ xalign: 0.5,
32
+ });
33
+
34
+ box.append(title);
35
+ box.append(hint);
36
+ window.set_child(box);
37
+ window.present();
38
+ });
39
+
40
+ app.run([]);
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": ["node", "@girs/gtk-4.0", "@girs/gio-2.0", "@girs/glib-2.0"],
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "HTTP server using Express (familiar Node.js stack).",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@gjsify/cli": "^0.1.12",
15
+ "@gjsify/node-globals": "^0.1.12",
16
+ "@gjsify/runtime": "^0.1.12",
17
+ "@types/express": "^5.0.6",
18
+ "@types/node": "^25.6.0",
19
+ "typescript": "^6.0.2"
20
+ },
21
+ "dependencies": {
22
+ "express": "^5.2.1"
23
+ }
24
+ }
@@ -0,0 +1,27 @@
1
+ import { runtimeName } from '@gjsify/runtime';
2
+ import express, { type Request, type Response } from 'express';
3
+
4
+ const app = express();
5
+ app.use(express.json());
6
+
7
+ app.get('/', (_req: Request, res: Response) => {
8
+ res.type('html').send(`<!doctype html>
9
+ <html>
10
+ <head><meta charset="utf-8"><title>new-gjsify-app</title></head>
11
+ <body>
12
+ <h1>new-gjsify-app</h1>
13
+ <p>Runtime: <strong>${runtimeName}</strong></p>
14
+ <p>Try <a href="/api/ping">GET /api/ping</a></p>
15
+ </body>
16
+ </html>`);
17
+ });
18
+
19
+ app.get('/api/ping', (_req: Request, res: Response) => {
20
+ res.json({ ok: true, runtime: runtimeName, time: new Date().toISOString() });
21
+ });
22
+
23
+ const PORT = parseInt(process.env.PORT ?? '3000', 10);
24
+ app.listen(PORT, () => {
25
+ console.log(`Express server running at http://localhost:${PORT}`);
26
+ console.log(`Runtime: ${runtimeName}`);
27
+ });
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": ["node"],
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "new-gjsify-app",
3
+ "description": "HTTP server using Hono (Web-standard fetch-style API).",
4
+ "version": "0.1.10",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "check": "tsc --noEmit",
9
+ "build": "gjsify build src/index.ts --outfile dist/index.js",
10
+ "start": "gjsify run dist/index.js",
11
+ "dev": "gjsify build src/index.ts --outfile dist/index.js && gjsify run dist/index.js"
12
+ },
13
+ "devDependencies": {
14
+ "@gjsify/cli": "^0.1.12",
15
+ "@gjsify/node-globals": "^0.1.12",
16
+ "@gjsify/runtime": "^0.1.12",
17
+ "@types/node": "^25.6.0",
18
+ "typescript": "^6.0.2"
19
+ },
20
+ "dependencies": {
21
+ "@hono/node-server": "^1.19.14",
22
+ "hono": "^4.12.14"
23
+ }
24
+ }
@@ -0,0 +1,21 @@
1
+ import { runtimeName } from '@gjsify/runtime';
2
+ import { Hono } from 'hono';
3
+ import { serve } from '@hono/node-server';
4
+
5
+ const app = new Hono();
6
+
7
+ app.get('/', (c) =>
8
+ c.json({
9
+ name: 'new-gjsify-app',
10
+ runtime: runtimeName,
11
+ endpoints: ['GET /', 'GET /api/ping'],
12
+ }),
13
+ );
14
+
15
+ app.get('/api/ping', (c) => c.json({ ok: true, time: new Date().toISOString() }));
16
+
17
+ const PORT = parseInt(process.env.PORT ?? '3000', 10);
18
+ serve({ fetch: app.fetch, port: PORT }, (info) => {
19
+ console.log(`Hono server running at http://localhost:${info.port}`);
20
+ console.log(`Runtime: ${runtimeName}`);
21
+ });
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "src",
4
+ "outDir": "dist",
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "types": ["node"],
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"]
14
+ }
package/lib/create.d.ts CHANGED
@@ -1 +1,14 @@
1
- export declare function createProject(projectName: string): Promise<void>;
1
+ export { discoverTemplates, findTemplate } from './discover-templates.js';
2
+ export type { TemplateInfo } from './discover-templates.js';
3
+ export interface CreateProjectOptions {
4
+ projectName: string;
5
+ /** Template short name, e.g. "gtk-minimal". If omitted, the caller is responsible for providing one via prompt. */
6
+ template: string;
7
+ /** Allow scaffolding into an existing non-empty directory. */
8
+ force?: boolean;
9
+ /** Run `npm install` in the scaffolded directory after writing files. */
10
+ install?: boolean;
11
+ }
12
+ /** npm package names: lowercase, digits, -, _, .; no leading . or _. */
13
+ export declare function sanitizeProjectName(raw: string): string;
14
+ export declare function createProject(options: CreateProjectOptions): Promise<void>;
package/lib/create.js CHANGED
@@ -1,33 +1,100 @@
1
- import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from 'node:fs';
2
- import { resolve, join, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- const __dirname = dirname(fileURLToPath(import.meta.url));
5
- export async function createProject(projectName) {
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, cpSync } from 'node:fs';
3
+ import { resolve, join } from 'node:path';
4
+ import { discoverTemplates, findTemplate } from './discover-templates.js';
5
+ export { discoverTemplates, findTemplate } from './discover-templates.js';
6
+ /** Sentinel replaced by the user's project name in every text file under the template. */
7
+ const PROJECT_NAME_SENTINEL = 'new-gjsify-app';
8
+ /** File extensions we treat as text and scan for the sentinel. */
9
+ const TEXT_FILE_EXT = new Set([
10
+ '.json', '.md', '.ts', '.tsx', '.js', '.mjs', '.cjs',
11
+ '.blp', '.html', '.css', '.scss', '.xml', '.ui', '.txt',
12
+ ]);
13
+ /** npm package names: lowercase, digits, -, _, .; no leading . or _. */
14
+ export function sanitizeProjectName(raw) {
15
+ const trimmed = raw.trim();
16
+ if (!trimmed)
17
+ throw new Error('Project name cannot be empty.');
18
+ const cleaned = trimmed
19
+ .toLowerCase()
20
+ .replace(/[^a-z0-9._-]+/g, '-')
21
+ .replace(/^[._-]+/, '')
22
+ .replace(/[._-]+$/, '');
23
+ if (!cleaned)
24
+ throw new Error(`"${raw}" is not a valid npm package name.`);
25
+ return cleaned;
26
+ }
27
+ function isDirEmpty(path) {
28
+ if (!existsSync(path))
29
+ return true;
30
+ return readdirSync(path).length === 0;
31
+ }
32
+ export async function createProject(options) {
33
+ const projectName = sanitizeProjectName(options.projectName);
34
+ const { template, force = false, install = false } = options;
35
+ const info = findTemplate(template);
36
+ if (!info) {
37
+ const available = discoverTemplates().map((t) => t.name).join(', ');
38
+ throw new Error(`Unknown template "${template}". Available templates: ${available || '(none — run "yarn build" first)'}`);
39
+ }
6
40
  const targetDir = resolve(process.cwd(), projectName);
7
- if (existsSync(targetDir)) {
8
- console.error(`Error: Directory "${projectName}" already exists.`);
41
+ if (existsSync(targetDir) && !isDirEmpty(targetDir) && !force) {
42
+ console.error(`Error: Directory "${projectName}" exists and is not empty. Use --force to scaffold into it anyway.`);
9
43
  process.exit(1);
10
44
  }
11
- console.log(`Creating new Gjsify project in ${targetDir}...`);
12
- // Create directory structure
13
- mkdirSync(join(targetDir, 'src'), { recursive: true });
14
- // Copy template files
15
- const templatesDir = resolve(__dirname, '..', 'templates');
16
- // Generate package.json from template (replaces sentinel name with project name)
17
- const packageJsonTemplate = readFileSync(join(templatesDir, 'package.json'), 'utf-8');
18
- const packageJson = packageJsonTemplate.replace(/new-gjsify-app/g, projectName);
19
- writeFileSync(join(targetDir, 'package.json'), packageJson);
20
- // Copy tsconfig.json
21
- cpSync(join(templatesDir, 'tsconfig.json'), join(targetDir, 'tsconfig.json'));
22
- // Copy src/index.ts
23
- cpSync(join(templatesDir, 'src', 'index.ts'), join(targetDir, 'src', 'index.ts'));
45
+ console.log(`Creating new Gjsify project in ${targetDir} (template: ${info.name})...`);
46
+ mkdirSync(targetDir, { recursive: true });
47
+ cpSync(info.path, targetDir, { recursive: true });
48
+ substituteProjectName(targetDir, projectName);
49
+ if (install) {
50
+ console.log('Running npm install...');
51
+ const result = spawnSync('npm', ['install', '--no-audit', '--no-fund'], {
52
+ cwd: targetDir,
53
+ stdio: 'inherit',
54
+ });
55
+ if (result.status !== 0) {
56
+ console.warn('npm install failed; re-run it manually in the project directory.');
57
+ }
58
+ }
59
+ printNextSteps(projectName, info, install);
60
+ }
61
+ /**
62
+ * Walk the scaffolded tree and replace the sentinel in every text file.
63
+ * Skips node_modules / dist / lib and non-text files by extension.
64
+ */
65
+ function substituteProjectName(rootDir, projectName) {
66
+ const skipDirs = new Set(['node_modules', 'dist', 'lib']);
67
+ const stack = [rootDir];
68
+ while (stack.length > 0) {
69
+ const dir = stack.pop();
70
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
71
+ const full = join(dir, entry.name);
72
+ if (entry.isDirectory()) {
73
+ if (!skipDirs.has(entry.name))
74
+ stack.push(full);
75
+ continue;
76
+ }
77
+ if (!entry.isFile())
78
+ continue;
79
+ const dot = entry.name.lastIndexOf('.');
80
+ const ext = dot >= 0 ? entry.name.slice(dot) : '';
81
+ if (!TEXT_FILE_EXT.has(ext))
82
+ continue;
83
+ const content = readFileSync(full, 'utf-8');
84
+ if (!content.includes(PROJECT_NAME_SENTINEL))
85
+ continue;
86
+ writeFileSync(full, content.replaceAll(PROJECT_NAME_SENTINEL, projectName));
87
+ }
88
+ }
89
+ }
90
+ function printNextSteps(projectName, template, installed) {
24
91
  console.log('');
25
- console.log('Project created successfully!');
92
+ console.log(`Project created from template "${template.name}".`);
26
93
  console.log('');
27
94
  console.log('Next steps:');
28
95
  console.log(` cd ${projectName}`);
29
- console.log(' npm install');
30
- console.log(' npm run build');
31
- console.log(' npm start');
96
+ if (!installed)
97
+ console.log(' npm install');
98
+ console.log(' npm run dev');
32
99
  console.log('');
33
100
  }
@@ -0,0 +1,14 @@
1
+ export interface TemplateInfo {
2
+ /** Short name, e.g. "gtk-minimal" */
3
+ name: string;
4
+ /** One-line description (from template package.json `description` or a built-in fallback) */
5
+ description: string;
6
+ /** Absolute path to the template directory inside dist-templates/ */
7
+ path: string;
8
+ }
9
+ /**
10
+ * Discover all shipped templates by scanning dist-templates/ next to this file.
11
+ * Returns templates sorted alphabetically by name.
12
+ */
13
+ export declare function discoverTemplates(): TemplateInfo[];
14
+ export declare function findTemplate(name: string): TemplateInfo | undefined;
@@ -0,0 +1,45 @@
1
+ import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const FALLBACK_DESCRIPTIONS = {
5
+ 'gtk-minimal': 'Minimal GTK4 app — Gtk.Window + Gtk.Label (no Adwaita, no Blueprint).',
6
+ 'cli': 'Command-line tool using yargs (Node.js + GJS).',
7
+ 'adw-canvas2d': 'Adwaita app with HTML Canvas 2D rendering (Blueprint UI).',
8
+ 'adw-webgl': 'Adwaita app with WebGL + three.js (Blueprint UI).',
9
+ 'adw-game': 'Adwaita game shell using Excalibur.js, WebGL → Canvas2D fallback.',
10
+ 'web-server-hono': 'HTTP server using Hono (Web-standard fetch-style API).',
11
+ 'web-server-express': 'HTTP server using Express (familiar Node.js stack).',
12
+ };
13
+ /**
14
+ * Discover all shipped templates by scanning dist-templates/ next to this file.
15
+ * Returns templates sorted alphabetically by name.
16
+ */
17
+ export function discoverTemplates() {
18
+ const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
19
+ const templatesRoot = join(pkgRoot, 'dist-templates');
20
+ if (!existsSync(templatesRoot))
21
+ return [];
22
+ const templates = [];
23
+ for (const name of readdirSync(templatesRoot)) {
24
+ const path = join(templatesRoot, name);
25
+ const pkgJsonPath = join(path, 'package.json');
26
+ if (!statSync(path).isDirectory() || !existsSync(pkgJsonPath))
27
+ continue;
28
+ let description = FALLBACK_DESCRIPTIONS[name] ?? '';
29
+ try {
30
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
31
+ if (typeof pkg.description === 'string' && pkg.description.trim()) {
32
+ description = pkg.description;
33
+ }
34
+ }
35
+ catch {
36
+ // Fall back to built-in description
37
+ }
38
+ templates.push({ name, description, path });
39
+ }
40
+ templates.sort((a, b) => a.name.localeCompare(b.name));
41
+ return templates;
42
+ }
43
+ export function findTemplate(name) {
44
+ return discoverTemplates().find((t) => t.name === name);
45
+ }
package/lib/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export { createProject } from './create.js';
2
+ export { createProject, sanitizeProjectName, type CreateProjectOptions } from './create.js';
3
+ export { discoverTemplates, findTemplate, type TemplateInfo } from './discover-templates.js';
package/lib/index.js CHANGED
@@ -2,18 +2,56 @@
2
2
  import yargs from 'yargs';
3
3
  import { hideBin } from 'yargs/helpers';
4
4
  import { createProject } from './create.js';
5
+ import { discoverTemplates } from './discover-templates.js';
6
+ import { promptTemplate } from './prompt-template.js';
7
+ const templates = discoverTemplates();
8
+ const templateChoices = templates.map((t) => t.name);
5
9
  void yargs(hideBin(process.argv))
6
10
  .scriptName('@gjsify/create-app')
7
11
  .usage('$0 [project-name]', 'Create a new Gjsify project', (yargs) => {
8
- return yargs.positional('project-name', {
12
+ return yargs
13
+ .positional('project-name', {
9
14
  describe: 'Name of the project directory to create',
10
15
  type: 'string',
11
- default: 'my-gjs-app'
16
+ default: 'my-gjs-app',
17
+ })
18
+ .option('template', {
19
+ alias: 't',
20
+ describe: 'Template to scaffold from',
21
+ type: 'string',
22
+ choices: templateChoices.length > 0 ? templateChoices : undefined,
23
+ })
24
+ .option('force', {
25
+ alias: 'f',
26
+ describe: 'Scaffold into a non-empty directory',
27
+ type: 'boolean',
28
+ default: false,
29
+ })
30
+ .option('install', {
31
+ describe: 'Run npm install after scaffolding',
32
+ type: 'boolean',
33
+ default: false,
12
34
  });
13
35
  }, async (argv) => {
14
36
  const projectName = argv['project-name'];
15
- await createProject(projectName);
37
+ let template = argv['template'];
38
+ if (!template) {
39
+ if (!process.stdin.isTTY) {
40
+ const list = templateChoices.join(', ');
41
+ console.error(`Error: --template is required in non-interactive mode. Available templates: ${list || '(none)'}`);
42
+ process.exit(1);
43
+ }
44
+ const picked = await promptTemplate(templates);
45
+ template = picked.name;
46
+ }
47
+ await createProject({
48
+ projectName,
49
+ template,
50
+ force: argv['force'],
51
+ install: argv['install'],
52
+ });
16
53
  })
17
54
  .help()
18
55
  .argv;
19
- export { createProject } from './create.js';
56
+ export { createProject, sanitizeProjectName } from './create.js';
57
+ export { discoverTemplates, findTemplate } from './discover-templates.js';
@@ -0,0 +1,6 @@
1
+ import type { TemplateInfo } from './discover-templates.js';
2
+ /**
3
+ * Interactive template picker. Returns the selected TemplateInfo.
4
+ * Throws if stdin is not a TTY — caller should check process.stdin.isTTY first.
5
+ */
6
+ export declare function promptTemplate(templates: TemplateInfo[]): Promise<TemplateInfo>;
@@ -0,0 +1,73 @@
1
+ import { stdin, stdout } from 'node:process';
2
+ import { emitKeypressEvents } from 'node:readline';
3
+ const c = {
4
+ reset: '\x1b[0m',
5
+ bold: '\x1b[1m',
6
+ dim: '\x1b[2m',
7
+ cyan: '\x1b[36m',
8
+ green: '\x1b[32m',
9
+ };
10
+ /**
11
+ * Render a templates list with one row highlighted.
12
+ * First call: just draws. Subsequent calls move the cursor back up first.
13
+ */
14
+ function render(templates, selected, firstRender) {
15
+ if (!firstRender) {
16
+ // Move cursor up over the previously drawn rows
17
+ stdout.write(`\x1b[${templates.length}A`);
18
+ }
19
+ for (let i = 0; i < templates.length; i++) {
20
+ const t = templates[i];
21
+ const marker = i === selected ? `${c.cyan}❯${c.reset}` : ' ';
22
+ const label = i === selected ? `${c.bold}${c.green}${t.name}${c.reset}` : t.name;
23
+ // Clear line, then write content
24
+ stdout.write(`\x1b[2K\r ${marker} ${label} ${c.dim}${t.description}${c.reset}\n`);
25
+ }
26
+ }
27
+ /**
28
+ * Interactive template picker. Returns the selected TemplateInfo.
29
+ * Throws if stdin is not a TTY — caller should check process.stdin.isTTY first.
30
+ */
31
+ export function promptTemplate(templates) {
32
+ if (templates.length === 0) {
33
+ return Promise.reject(new Error('No templates available.'));
34
+ }
35
+ return new Promise((resolve, reject) => {
36
+ let selected = 0;
37
+ stdout.write(`${c.bold}Select a template${c.reset} ${c.dim}(↑/↓ to navigate, Enter to confirm, Ctrl+C to cancel)${c.reset}\n`);
38
+ render(templates, selected, true);
39
+ emitKeypressEvents(stdin);
40
+ if (stdin.isTTY)
41
+ stdin.setRawMode(true);
42
+ stdin.resume();
43
+ const cleanup = () => {
44
+ stdin.removeListener('keypress', onKeypress);
45
+ if (stdin.isTTY)
46
+ stdin.setRawMode(false);
47
+ stdin.pause();
48
+ };
49
+ const onKeypress = (_str, key) => {
50
+ if (!key)
51
+ return;
52
+ if (key.ctrl && key.name === 'c') {
53
+ cleanup();
54
+ stdout.write('\n');
55
+ reject(new Error('Cancelled by user'));
56
+ return;
57
+ }
58
+ if (key.name === 'up' || key.name === 'k') {
59
+ selected = (selected - 1 + templates.length) % templates.length;
60
+ render(templates, selected, false);
61
+ }
62
+ else if (key.name === 'down' || key.name === 'j') {
63
+ selected = (selected + 1) % templates.length;
64
+ render(templates, selected, false);
65
+ }
66
+ else if (key.name === 'return' || key.name === 'enter') {
67
+ cleanup();
68
+ resolve(templates[selected]);
69
+ }
70
+ };
71
+ stdin.on('keypress', onKeypress);
72
+ });
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/create-app",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Create a new Gjsify project",
5
5
  "type": "module",
6
6
  "main": "lib/create.js",
@@ -10,6 +10,10 @@
10
10
  "types": "./lib/create.d.ts",
11
11
  "import": "./lib/create.js"
12
12
  },
13
+ "./prompt": {
14
+ "types": "./lib/prompt-template.d.ts",
15
+ "import": "./lib/prompt-template.js"
16
+ },
13
17
  "./package.json": "./package.json"
14
18
  },
15
19
  "bin": "./lib/index.js",
@@ -28,7 +32,7 @@
28
32
  ],
29
33
  "files": [
30
34
  "lib",
31
- "templates"
35
+ "dist-templates"
32
36
  ],
33
37
  "dependencies": {
34
38
  "yargs": "^18.0.0"
@@ -1,64 +0,0 @@
1
- import Gtk from 'gi://Gtk?version=4.0'
2
-
3
- // Globals (process, crypto, Buffer, etc.) are automatically detected
4
- // and injected by `gjsify build` via --globals auto (the default).
5
-
6
- const app = new Gtk.Application({
7
- applicationId: 'org.gjsify.example',
8
- })
9
-
10
- app.connect('activate', () => {
11
- const window = new Gtk.ApplicationWindow({
12
- application: app,
13
- title: 'new-gjsify-app',
14
- defaultWidth: 480,
15
- defaultHeight: 280,
16
- })
17
-
18
- const box = new Gtk.Box({
19
- orientation: Gtk.Orientation.VERTICAL,
20
- spacing: 16,
21
- margin_top: 24,
22
- margin_bottom: 24,
23
- margin_start: 24,
24
- margin_end: 24,
25
- })
26
-
27
- // Node.js — process API
28
- const platformLabel = new Gtk.Label({
29
- label: `Platform: ${process.platform} | PID: ${process.pid}`,
30
- xalign: 0,
31
- })
32
-
33
- // Web Crypto API — UUID
34
- const uuidLabel = new Gtk.Label({
35
- label: 'UUID: —',
36
- xalign: 0,
37
- })
38
-
39
- // Node.js — Buffer API
40
- const base64Label = new Gtk.Label({
41
- label: 'Base64: —',
42
- xalign: 0,
43
- })
44
-
45
- // Button wires everything together
46
- const button = new Gtk.Button({ label: '↺ Generate UUID' })
47
- button.connect('clicked', () => {
48
- const uuid = crypto.randomUUID() // Web Crypto
49
- const base64 = Buffer.from(uuid).toString('base64') // Node.js Buffer
50
- uuidLabel.set_label(`UUID: ${uuid}`)
51
- base64Label.set_label(`Base64: ${base64}`)
52
- })
53
-
54
- box.append(platformLabel)
55
- box.append(new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL }))
56
- box.append(button)
57
- box.append(uuidLabel)
58
- box.append(base64Label)
59
-
60
- window.set_child(box)
61
- window.present()
62
- })
63
-
64
- app.run([])