@geastack/cli 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/docs/ESP32-WAVESHARE-AMOLED-QUICKSTART.md +8 -5
- package/docs/NPX-COMMANDS.md +8 -8
- package/docs/SPEC.md +4 -3
- package/package.json +1 -1
- package/src/create-geastack.mjs +42 -27
- package/starters/bundled/counter/package.json +12 -11
- package/starters/bundled/counter/src/App.tsx +28 -0
- package/starters/bundled/counter/src/index.tsx +4 -0
- package/starters/bundled/counter/src/styles.css +20 -0
- package/starters/bundled/counter/tsconfig.json +14 -0
- package/starters/catalog.json +6 -6
- package/starters/bundled/counter/index.tsx +0 -25
- package/starters/bundled/counter/store.ts +0 -7
- package/starters/bundled/counter/styles.css +0 -39
package/README.md
CHANGED
|
@@ -29,12 +29,12 @@ flowchart TD
|
|
|
29
29
|
create["gea create my-app"] --> identity["Resolve app identity<br/>argument, optional --id, optional --name"]
|
|
30
30
|
identity --> starter{"Starter app?"}
|
|
31
31
|
|
|
32
|
-
starter -->|"
|
|
32
|
+
starter -->|"Embedded component counter"| counter["Copy the touchscreen counter used in the embedded tutorial"]
|
|
33
33
|
|
|
34
|
-
starter -->|"
|
|
34
|
+
starter -->|"Blank application"| empty["Generate a minimal browser application"]
|
|
35
35
|
empty --> emptyFiles["index.tsx, styles.css, index.html,<br/>tsconfig.json, vite.config.ts"]
|
|
36
36
|
|
|
37
|
-
starter -->|"
|
|
37
|
+
starter -->|"Example application"| pickExample["Pick from the example gallery<br/>web, ESP32, GeaOS, iOS, macOS, Android"]
|
|
38
38
|
pickExample --> fetchExample["Fetch selected app from GitHub"]
|
|
39
39
|
fetchExample --> copyExample["Copy fetched example files"]
|
|
40
40
|
copyExample --> rewriteExample["Rewrite package name and gea manifest"]
|
|
@@ -38,13 +38,16 @@ cd my-app
|
|
|
38
38
|
When the wizard asks what to create, choose:
|
|
39
39
|
|
|
40
40
|
```text
|
|
41
|
-
? What do you want to
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
? What do you want to build?
|
|
42
|
+
Embedded component counter
|
|
43
|
+
Touchscreen +/− counter with local state and BLE updates, ready for ESP32.
|
|
44
|
+
Blank application
|
|
45
|
+
A minimal screen for building your own Gea application.
|
|
46
|
+
Example application
|
|
47
|
+
Choose a complete application from the GeaStack example gallery.
|
|
45
48
|
```
|
|
46
49
|
|
|
47
|
-
For the fastest first flash, choose `
|
|
50
|
+
For the fastest first flash, choose `Embedded component counter`.
|
|
48
51
|
|
|
49
52
|
That creates a tiny JSX app and installs all of its project dependencies.
|
|
50
53
|
|
package/docs/NPX-COMMANDS.md
CHANGED
|
@@ -44,20 +44,20 @@ npx @geastack/create-geastack my-app
|
|
|
44
44
|
In an interactive terminal, `create-geastack` asks:
|
|
45
45
|
|
|
46
46
|
```text
|
|
47
|
-
|
|
48
|
-
1.
|
|
49
|
-
2.
|
|
50
|
-
3.
|
|
47
|
+
What do you want to build?
|
|
48
|
+
1. Embedded component counter
|
|
49
|
+
2. Blank application
|
|
50
|
+
3. Example application
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
`
|
|
54
|
-
|
|
53
|
+
`Embedded component counter` is the bundled tutorial starter. It uses local
|
|
54
|
+
component state, targets ESP32, and enables BLE updates.
|
|
55
55
|
|
|
56
|
-
`
|
|
56
|
+
`Blank application` creates the smallest browser starter: `index.tsx`, `styles.css`,
|
|
57
57
|
`index.html`, `tsconfig.json`, `vite.config.ts`, `package.json`, and
|
|
58
58
|
`.gea/boards.json`.
|
|
59
59
|
|
|
60
|
-
`
|
|
60
|
+
`Example application` opens an example picker populated from the hard-coded
|
|
61
61
|
`examples/catalog.json` shipped inside `@geastack/cli`. The catalog contains web,
|
|
62
62
|
ESP32, GeaOS, iOS, macOS, and Android examples with names, descriptions, target
|
|
63
63
|
flags, and GitHub paths. When the user selects an example, the CLI fetches that app from
|
package/docs/SPEC.md
CHANGED
|
@@ -130,9 +130,10 @@ Scaffolds a new app folder with:
|
|
|
130
130
|
|
|
131
131
|
Interactive starter choices must explain the tradeoff in-line:
|
|
132
132
|
|
|
133
|
-
- `
|
|
134
|
-
|
|
135
|
-
- `
|
|
133
|
+
- `Embedded component counter`: create a touchscreen counter with local
|
|
134
|
+
component state, an ESP32 target, and BLE updates enabled.
|
|
135
|
+
- `Blank application`: generate a minimal browser application as a clean slate.
|
|
136
|
+
- `Example application`: fetch a selected app from `geastack/examples`, then rewrite
|
|
136
137
|
package name, app id, dependencies, and board config for the new project.
|
|
137
138
|
|
|
138
139
|
Rich example choices come from the hard-coded `examples/catalog.json`
|
package/package.json
CHANGED
package/src/create-geastack.mjs
CHANGED
|
@@ -42,10 +42,7 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
42
42
|
const coreDependency = option(parsed, 'core-dependency') || '^0.1.4'
|
|
43
43
|
const cliDependency = option(parsed, 'cli-dependency') || `^${cliVersion}`
|
|
44
44
|
const starter = await resolveStarter(ctx, parsed, io)
|
|
45
|
-
const entry = projectRelativePath(
|
|
46
|
-
if (option(parsed, 'entry') && starter.kind !== 'empty') {
|
|
47
|
-
fail('--entry can only be used with --starter empty.', ExitCode.usage)
|
|
48
|
-
}
|
|
45
|
+
const entry = projectRelativePath(starter.starter?.entry || starter.example?.entry || 'index.tsx', 'entry')
|
|
49
46
|
|
|
50
47
|
fs.mkdirSync(targetDir, { recursive: true })
|
|
51
48
|
if (starter.kind === 'empty') {
|
|
@@ -70,26 +67,28 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
70
67
|
|
|
71
68
|
const sourcePackage = readPackageJson(targetDir)
|
|
72
69
|
const sourceManifest = sourcePackage.gea || starter.starter?.manifest || starter.example?.manifest || {}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const requestedTargets = optionList(parsed, 'targets')
|
|
71
|
+
const targets = requestedTargets.length > 0
|
|
72
|
+
? targetManifest(requestedTargets)
|
|
73
|
+
: starter.kind === 'empty'
|
|
74
|
+
? targetManifest(['web'])
|
|
75
|
+
: targetManifestFromObject(sourceManifest.targets)
|
|
76
76
|
writeJson(path.join(targetDir, 'package.json'), packageJson({
|
|
77
77
|
appId,
|
|
78
78
|
displayName,
|
|
79
79
|
targets,
|
|
80
80
|
entry,
|
|
81
|
-
bleOta: flag(parsed, 'ble-ota'),
|
|
82
81
|
coreDependency,
|
|
83
82
|
cliDependency,
|
|
84
83
|
sourcePackage,
|
|
85
84
|
sourceManifest
|
|
86
85
|
}))
|
|
87
|
-
ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName, entry))
|
|
86
|
+
if (targets.web) ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName, entry))
|
|
88
87
|
fs.mkdirSync(path.join(targetDir, '.gea'), { recursive: true })
|
|
89
88
|
writeJson(path.join(targetDir, '.gea', 'boards.json'), {})
|
|
90
89
|
ensureJson(path.join(targetDir, 'tsconfig.json'), tsconfigJson)
|
|
91
|
-
ensureFile(path.join(targetDir, 'vite.config.ts'), viteConfigTs)
|
|
92
|
-
fs.writeFileSync(path.join(targetDir, 'README.md'), readme({ appId, displayName, starter }))
|
|
90
|
+
if (targets.web) ensureFile(path.join(targetDir, 'vite.config.ts'), viteConfigTs)
|
|
91
|
+
fs.writeFileSync(path.join(targetDir, 'README.md'), readme({ appId, displayName, starter, targets }))
|
|
93
92
|
|
|
94
93
|
const installDependencies = shouldInstallDependencies(parsed, io)
|
|
95
94
|
if (installDependencies) {
|
|
@@ -157,11 +156,23 @@ async function resolveStarter(ctx, parsed, io) {
|
|
|
157
156
|
async function chooseStarterMode({ interactive, prompt, bundled, examples }) {
|
|
158
157
|
if (!interactive) return bundled.length > 0 ? 'counter' : 'empty'
|
|
159
158
|
return choose(prompt, {
|
|
160
|
-
message: '
|
|
159
|
+
message: 'What do you want to build?',
|
|
161
160
|
choices: [
|
|
162
|
-
...(bundled.length > 0 ? [{
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
...(bundled.length > 0 ? [{
|
|
162
|
+
value: 'counter',
|
|
163
|
+
label: 'Embedded component counter',
|
|
164
|
+
description: 'Touchscreen +/− counter with local state and BLE updates, ready for ESP32.'
|
|
165
|
+
}] : []),
|
|
166
|
+
{
|
|
167
|
+
value: 'empty',
|
|
168
|
+
label: 'Blank application',
|
|
169
|
+
description: 'A minimal screen for building your own Gea application.'
|
|
170
|
+
},
|
|
171
|
+
...(examples.length > 0 ? [{
|
|
172
|
+
value: 'example',
|
|
173
|
+
label: 'Example application',
|
|
174
|
+
description: 'Choose a complete application from the GeaStack example gallery.'
|
|
175
|
+
}] : [])
|
|
165
176
|
],
|
|
166
177
|
defaultValue: bundled.length > 0 ? 'counter' : 'empty'
|
|
167
178
|
})
|
|
@@ -213,7 +224,7 @@ function targetManifestFromObject(targets = {}) {
|
|
|
213
224
|
}
|
|
214
225
|
}
|
|
215
226
|
|
|
216
|
-
function packageJson({ appId, displayName, targets, entry,
|
|
227
|
+
function packageJson({ appId, displayName, targets, entry, coreDependency, cliDependency, sourcePackage = {}, sourceManifest = {} }) {
|
|
217
228
|
const geaManifest = {
|
|
218
229
|
...sourceManifest,
|
|
219
230
|
id: appId,
|
|
@@ -221,7 +232,6 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
221
232
|
entry,
|
|
222
233
|
targets
|
|
223
234
|
}
|
|
224
|
-
if (bleOta) geaManifest.ota = { ...(geaManifest.ota || {}), ble: true }
|
|
225
235
|
if (!geaManifest.runtime || geaManifest.runtime === 'gea') delete geaManifest.runtime
|
|
226
236
|
|
|
227
237
|
return {
|
|
@@ -231,8 +241,7 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
231
241
|
type: 'module',
|
|
232
242
|
scripts: {
|
|
233
243
|
...(sourcePackage.scripts || {}),
|
|
234
|
-
dev: 'gea dev',
|
|
235
|
-
build: 'gea build --target web',
|
|
244
|
+
...(targets.web ? { dev: 'gea dev', build: 'gea build --target web' } : {}),
|
|
236
245
|
check: 'tsc --noEmit'
|
|
237
246
|
},
|
|
238
247
|
dependencies: {
|
|
@@ -244,7 +253,7 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
244
253
|
...(sourcePackage.devDependencies || {}),
|
|
245
254
|
'@geastack/cli': cliDependency,
|
|
246
255
|
typescript: sourcePackage.devDependencies?.typescript || 'latest',
|
|
247
|
-
vite: sourcePackage.devDependencies?.vite || 'latest'
|
|
256
|
+
...(targets.web ? { vite: sourcePackage.devDependencies?.vite || 'latest' } : {})
|
|
248
257
|
},
|
|
249
258
|
gea: geaManifest,
|
|
250
259
|
license: 'MIT'
|
|
@@ -358,12 +367,22 @@ export default defineConfig({
|
|
|
358
367
|
`
|
|
359
368
|
}
|
|
360
369
|
|
|
361
|
-
function readme({ appId, displayName, starter }) {
|
|
370
|
+
function readme({ appId, displayName, starter, targets }) {
|
|
362
371
|
const starterLine = starter.kind === 'example'
|
|
363
372
|
? `Started from GitHub example: \`${starter.example.name}\` (\`${starter.example.id}\`).`
|
|
364
373
|
: starter.kind === 'bundled'
|
|
365
374
|
? `Started from bundled starter: \`${starter.starter.name}\` (\`${starter.starter.id}\`).`
|
|
366
|
-
: 'Started from
|
|
375
|
+
: 'Started from a blank app.'
|
|
376
|
+
const commands = targets.web
|
|
377
|
+
? `npx gea setup
|
|
378
|
+
npx gea dev
|
|
379
|
+
npx gea build --target web`
|
|
380
|
+
: targets.esp32
|
|
381
|
+
? `npx gea setup
|
|
382
|
+
npx gea build --board <alias>
|
|
383
|
+
npx gea flash --board <alias> --monitor`
|
|
384
|
+
: `npx gea setup
|
|
385
|
+
npx gea build`
|
|
367
386
|
return `# ${displayName}
|
|
368
387
|
|
|
369
388
|
GeaStack app id: \`${appId}\`.
|
|
@@ -371,9 +390,7 @@ GeaStack app id: \`${appId}\`.
|
|
|
371
390
|
${starterLine}
|
|
372
391
|
|
|
373
392
|
\`\`\`sh
|
|
374
|
-
|
|
375
|
-
npx gea dev
|
|
376
|
-
npx gea build --target web
|
|
393
|
+
${commands}
|
|
377
394
|
\`\`\`
|
|
378
395
|
`
|
|
379
396
|
}
|
|
@@ -441,8 +458,6 @@ Options:
|
|
|
441
458
|
--examples-repo <git-url-or-local-path>
|
|
442
459
|
--examples-ref <git-ref>
|
|
443
460
|
--targets web,esp32,rp2350,geaos,macos,ios,android
|
|
444
|
-
--entry <relative-path>
|
|
445
|
-
--ble-ota
|
|
446
461
|
--core-dependency <specifier>
|
|
447
462
|
--cli-dependency <specifier>
|
|
448
463
|
--install / --no-install
|
|
@@ -3,30 +3,31 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "
|
|
6
|
+
"description": "Touchscreen counter with component-local reactive state.",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "vite build",
|
|
9
8
|
"check": "tsc --noEmit"
|
|
10
9
|
},
|
|
11
10
|
"dependencies": {
|
|
12
|
-
"@geajs/core": "
|
|
11
|
+
"@geajs/core": "latest"
|
|
13
12
|
},
|
|
14
13
|
"devDependencies": {
|
|
15
|
-
"typescript": "latest"
|
|
16
|
-
"vite": "latest"
|
|
14
|
+
"typescript": "latest"
|
|
17
15
|
},
|
|
18
16
|
"gea": {
|
|
19
17
|
"id": "counter",
|
|
20
|
-
"name": "Counter",
|
|
21
|
-
"description": "
|
|
22
|
-
"entry": "index.tsx",
|
|
18
|
+
"name": "Component Counter",
|
|
19
|
+
"description": "Touchscreen counter with component-local reactive state.",
|
|
20
|
+
"entry": "src/index.tsx",
|
|
23
21
|
"targets": {
|
|
24
|
-
"web":
|
|
22
|
+
"web": false,
|
|
25
23
|
"esp32": true,
|
|
26
|
-
"rp2350":
|
|
27
|
-
"geaos":
|
|
24
|
+
"rp2350": false,
|
|
25
|
+
"geaos": false,
|
|
28
26
|
"macos": false,
|
|
29
27
|
"ios": false
|
|
28
|
+
},
|
|
29
|
+
"ota": {
|
|
30
|
+
"ble": true
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactiveComponent } from '@geastack/core'
|
|
2
|
+
import './styles.css'
|
|
3
|
+
|
|
4
|
+
export class App extends ReactiveComponent {
|
|
5
|
+
count = 0
|
|
6
|
+
|
|
7
|
+
decrement() {
|
|
8
|
+
this.count = this.count - 1
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
increment() {
|
|
12
|
+
this.count = this.count + 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
template() {
|
|
16
|
+
return (
|
|
17
|
+
<div class="screen">
|
|
18
|
+
<span class="eyebrow">GEASTACK</span>
|
|
19
|
+
<span class="title">Component state</span>
|
|
20
|
+
<span class="count">{this.count}</span>
|
|
21
|
+
<div class="controls">
|
|
22
|
+
<button class="button secondary" onClick={() => this.decrement()}>−</button>
|
|
23
|
+
<button class="button primary" onClick={() => this.increment()}>+</button>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.screen {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
width: 100vw;
|
|
5
|
+
height: 100vh;
|
|
6
|
+
padding: 28px;
|
|
7
|
+
gap: 16px;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
background-color: #080b12;
|
|
11
|
+
color: #f8fafc;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.eyebrow { color: #2dd4bf; font-size: 13px; }
|
|
15
|
+
.title { color: #cbd5e1; font-size: 22px; }
|
|
16
|
+
.count { color: #ffffff; font-size: 76px; line-height: 1; }
|
|
17
|
+
.controls { display: flex; flex-direction: row; gap: 16px; }
|
|
18
|
+
.button { width: 92px; height: 58px; border-radius: 14px; border-width: 2px; font-size: 30px; }
|
|
19
|
+
.secondary { color: #f8fafc; background-color: #1e293b; border-color: #475569; }
|
|
20
|
+
.primary { color: #042f2e; background-color: #2dd4bf; border-color: #5eead4; }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "preserve",
|
|
11
|
+
"types": []
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"]
|
|
14
|
+
}
|
package/starters/catalog.json
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
"starters": [
|
|
4
4
|
{
|
|
5
5
|
"id": "counter",
|
|
6
|
-
"name": "Counter",
|
|
7
|
-
"description": "
|
|
6
|
+
"name": "Component Counter",
|
|
7
|
+
"description": "Touchscreen counter with component-local reactive state and BLE updates.",
|
|
8
8
|
"path": "bundled/counter",
|
|
9
|
-
"entry": "index.tsx",
|
|
9
|
+
"entry": "src/index.tsx",
|
|
10
10
|
"targets": {
|
|
11
|
-
"web":
|
|
11
|
+
"web": false,
|
|
12
12
|
"esp32": true,
|
|
13
|
-
"rp2350":
|
|
14
|
-
"geaos":
|
|
13
|
+
"rp2350": false,
|
|
14
|
+
"geaos": false,
|
|
15
15
|
"macos": false,
|
|
16
16
|
"ios": false,
|
|
17
17
|
"android": false
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { mount } from '@geastack/core'
|
|
2
|
-
import { counter } from './store'
|
|
3
|
-
import './styles.css'
|
|
4
|
-
|
|
5
|
-
function App() {
|
|
6
|
-
function increment() {
|
|
7
|
-
const value = counter.increment()
|
|
8
|
-
const count = document.querySelector('[data-count]')
|
|
9
|
-
if (count) count.textContent = String(value)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<body class="app">
|
|
14
|
-
<main class="panel">
|
|
15
|
-
<p class="eyebrow">GeaStack</p>
|
|
16
|
-
<h1>Counter</h1>
|
|
17
|
-
<button class="button" onClick={increment}>
|
|
18
|
-
Count <span data-count>0</span>
|
|
19
|
-
</button>
|
|
20
|
-
</main>
|
|
21
|
-
</body>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
mount(App)
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
.app {
|
|
2
|
-
width: 100vw;
|
|
3
|
-
height: 100vh;
|
|
4
|
-
margin: 0;
|
|
5
|
-
display: flex;
|
|
6
|
-
align-items: center;
|
|
7
|
-
justify-content: center;
|
|
8
|
-
background: #101418;
|
|
9
|
-
color: #f8fafc;
|
|
10
|
-
font-family: Inter, system-ui, sans-serif;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.panel {
|
|
14
|
-
width: min(320px, calc(100vw - 32px));
|
|
15
|
-
padding: 24px;
|
|
16
|
-
border: 1px solid #2dd4bf;
|
|
17
|
-
background: #182026;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.eyebrow {
|
|
21
|
-
margin: 0 0 8px;
|
|
22
|
-
color: #2dd4bf;
|
|
23
|
-
font-size: 12px;
|
|
24
|
-
text-transform: uppercase;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
h1 {
|
|
28
|
-
margin: 0 0 16px;
|
|
29
|
-
font-size: 28px;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.button {
|
|
33
|
-
min-width: 120px;
|
|
34
|
-
min-height: 40px;
|
|
35
|
-
border: 0;
|
|
36
|
-
background: #2dd4bf;
|
|
37
|
-
color: #081018;
|
|
38
|
-
font: inherit;
|
|
39
|
-
}
|