@geastack/cli 0.1.5 → 0.1.7
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 +57 -32
- package/src/gea.mjs +1 -1
- 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
|
@@ -22,9 +22,10 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
22
22
|
const stdout = io.stdout || console.log
|
|
23
23
|
const env = io.env || process.env
|
|
24
24
|
const cwd = io.cwd || process.cwd()
|
|
25
|
+
const commandName = io.commandName || 'create-geastack'
|
|
25
26
|
|
|
26
27
|
if (flag(parsed, 'help') || parsed.positionals[0] === 'help') {
|
|
27
|
-
stdout(usage())
|
|
28
|
+
stdout(usage(commandName))
|
|
28
29
|
return 0
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -42,10 +43,7 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
42
43
|
const coreDependency = option(parsed, 'core-dependency') || '^0.1.4'
|
|
43
44
|
const cliDependency = option(parsed, 'cli-dependency') || `^${cliVersion}`
|
|
44
45
|
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
|
-
}
|
|
46
|
+
const entry = projectRelativePath(starter.starter?.entry || starter.example?.entry || 'index.tsx', 'entry')
|
|
49
47
|
|
|
50
48
|
fs.mkdirSync(targetDir, { recursive: true })
|
|
51
49
|
if (starter.kind === 'empty') {
|
|
@@ -70,26 +68,28 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
70
68
|
|
|
71
69
|
const sourcePackage = readPackageJson(targetDir)
|
|
72
70
|
const sourceManifest = sourcePackage.gea || starter.starter?.manifest || starter.example?.manifest || {}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
const requestedTargets = optionList(parsed, 'targets')
|
|
72
|
+
const targets = requestedTargets.length > 0
|
|
73
|
+
? targetManifest(requestedTargets)
|
|
74
|
+
: starter.kind === 'empty'
|
|
75
|
+
? targetManifest(['web'])
|
|
76
|
+
: targetManifestFromObject(sourceManifest.targets)
|
|
76
77
|
writeJson(path.join(targetDir, 'package.json'), packageJson({
|
|
77
78
|
appId,
|
|
78
79
|
displayName,
|
|
79
80
|
targets,
|
|
80
81
|
entry,
|
|
81
|
-
bleOta: flag(parsed, 'ble-ota'),
|
|
82
82
|
coreDependency,
|
|
83
83
|
cliDependency,
|
|
84
84
|
sourcePackage,
|
|
85
85
|
sourceManifest
|
|
86
86
|
}))
|
|
87
|
-
ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName, entry))
|
|
87
|
+
if (targets.web) ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName, entry))
|
|
88
88
|
fs.mkdirSync(path.join(targetDir, '.gea'), { recursive: true })
|
|
89
89
|
writeJson(path.join(targetDir, '.gea', 'boards.json'), {})
|
|
90
90
|
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 }))
|
|
91
|
+
if (targets.web) ensureFile(path.join(targetDir, 'vite.config.ts'), viteConfigTs)
|
|
92
|
+
fs.writeFileSync(path.join(targetDir, 'README.md'), readme({ appId, displayName, starter, targets }))
|
|
93
93
|
|
|
94
94
|
const installDependencies = shouldInstallDependencies(parsed, io)
|
|
95
95
|
if (installDependencies) {
|
|
@@ -157,11 +157,23 @@ async function resolveStarter(ctx, parsed, io) {
|
|
|
157
157
|
async function chooseStarterMode({ interactive, prompt, bundled, examples }) {
|
|
158
158
|
if (!interactive) return bundled.length > 0 ? 'counter' : 'empty'
|
|
159
159
|
return choose(prompt, {
|
|
160
|
-
message: '
|
|
160
|
+
message: 'What do you want to build?',
|
|
161
161
|
choices: [
|
|
162
|
-
...(bundled.length > 0 ? [{
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
...(bundled.length > 0 ? [{
|
|
163
|
+
value: 'counter',
|
|
164
|
+
label: 'Embedded component counter',
|
|
165
|
+
description: 'Touchscreen +/− counter with local state and BLE updates, ready for ESP32.'
|
|
166
|
+
}] : []),
|
|
167
|
+
{
|
|
168
|
+
value: 'empty',
|
|
169
|
+
label: 'Blank application',
|
|
170
|
+
description: 'A minimal screen for building your own Gea application.'
|
|
171
|
+
},
|
|
172
|
+
...(examples.length > 0 ? [{
|
|
173
|
+
value: 'example',
|
|
174
|
+
label: 'Example application',
|
|
175
|
+
description: 'Choose a complete application from the GeaStack example gallery.'
|
|
176
|
+
}] : [])
|
|
165
177
|
],
|
|
166
178
|
defaultValue: bundled.length > 0 ? 'counter' : 'empty'
|
|
167
179
|
})
|
|
@@ -213,7 +225,7 @@ function targetManifestFromObject(targets = {}) {
|
|
|
213
225
|
}
|
|
214
226
|
}
|
|
215
227
|
|
|
216
|
-
function packageJson({ appId, displayName, targets, entry,
|
|
228
|
+
function packageJson({ appId, displayName, targets, entry, coreDependency, cliDependency, sourcePackage = {}, sourceManifest = {} }) {
|
|
217
229
|
const geaManifest = {
|
|
218
230
|
...sourceManifest,
|
|
219
231
|
id: appId,
|
|
@@ -221,7 +233,6 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
221
233
|
entry,
|
|
222
234
|
targets
|
|
223
235
|
}
|
|
224
|
-
if (bleOta) geaManifest.ota = { ...(geaManifest.ota || {}), ble: true }
|
|
225
236
|
if (!geaManifest.runtime || geaManifest.runtime === 'gea') delete geaManifest.runtime
|
|
226
237
|
|
|
227
238
|
return {
|
|
@@ -231,8 +242,7 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
231
242
|
type: 'module',
|
|
232
243
|
scripts: {
|
|
233
244
|
...(sourcePackage.scripts || {}),
|
|
234
|
-
dev: 'gea dev',
|
|
235
|
-
build: 'gea build --target web',
|
|
245
|
+
...(targets.web ? { dev: 'gea dev', build: 'gea build --target web' } : {}),
|
|
236
246
|
check: 'tsc --noEmit'
|
|
237
247
|
},
|
|
238
248
|
dependencies: {
|
|
@@ -244,7 +254,7 @@ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependenc
|
|
|
244
254
|
...(sourcePackage.devDependencies || {}),
|
|
245
255
|
'@geastack/cli': cliDependency,
|
|
246
256
|
typescript: sourcePackage.devDependencies?.typescript || 'latest',
|
|
247
|
-
vite: sourcePackage.devDependencies?.vite || 'latest'
|
|
257
|
+
...(targets.web ? { vite: sourcePackage.devDependencies?.vite || 'latest' } : {})
|
|
248
258
|
},
|
|
249
259
|
gea: geaManifest,
|
|
250
260
|
license: 'MIT'
|
|
@@ -358,12 +368,22 @@ export default defineConfig({
|
|
|
358
368
|
`
|
|
359
369
|
}
|
|
360
370
|
|
|
361
|
-
function readme({ appId, displayName, starter }) {
|
|
371
|
+
function readme({ appId, displayName, starter, targets }) {
|
|
362
372
|
const starterLine = starter.kind === 'example'
|
|
363
373
|
? `Started from GitHub example: \`${starter.example.name}\` (\`${starter.example.id}\`).`
|
|
364
374
|
: starter.kind === 'bundled'
|
|
365
375
|
? `Started from bundled starter: \`${starter.starter.name}\` (\`${starter.starter.id}\`).`
|
|
366
|
-
: 'Started from
|
|
376
|
+
: 'Started from a blank app.'
|
|
377
|
+
const commands = targets.web
|
|
378
|
+
? `npx gea setup
|
|
379
|
+
npx gea dev
|
|
380
|
+
npx gea build --target web`
|
|
381
|
+
: targets.esp32
|
|
382
|
+
? `npx gea setup
|
|
383
|
+
npx gea build --board <alias>
|
|
384
|
+
npx gea flash --board <alias> --monitor`
|
|
385
|
+
: `npx gea setup
|
|
386
|
+
npx gea build`
|
|
367
387
|
return `# ${displayName}
|
|
368
388
|
|
|
369
389
|
GeaStack app id: \`${appId}\`.
|
|
@@ -371,9 +391,7 @@ GeaStack app id: \`${appId}\`.
|
|
|
371
391
|
${starterLine}
|
|
372
392
|
|
|
373
393
|
\`\`\`sh
|
|
374
|
-
|
|
375
|
-
npx gea dev
|
|
376
|
-
npx gea build --target web
|
|
394
|
+
${commands}
|
|
377
395
|
\`\`\`
|
|
378
396
|
`
|
|
379
397
|
}
|
|
@@ -431,21 +449,28 @@ function ensureJson(filePath, create) {
|
|
|
431
449
|
if (!exists(filePath)) writeJson(filePath, create())
|
|
432
450
|
}
|
|
433
451
|
|
|
434
|
-
function usage() {
|
|
452
|
+
function usage(commandName = 'create-geastack') {
|
|
435
453
|
return `Usage:
|
|
436
|
-
|
|
454
|
+
${commandName} <name>
|
|
455
|
+
|
|
456
|
+
Run without options for guided setup. The CLI asks what you want to build and
|
|
457
|
+
derives the source layout, targets, and runtime configuration from your choice.
|
|
437
458
|
|
|
438
459
|
Options:
|
|
439
|
-
--starter counter|
|
|
460
|
+
--starter counter|blank|example
|
|
440
461
|
--example <example-id>
|
|
462
|
+
--no-install
|
|
463
|
+
|
|
464
|
+
Automation options:
|
|
465
|
+
--dir <path>
|
|
466
|
+
--id <app-id>
|
|
467
|
+
--name <display-name>
|
|
441
468
|
--examples-repo <git-url-or-local-path>
|
|
442
469
|
--examples-ref <git-ref>
|
|
443
470
|
--targets web,esp32,rp2350,geaos,macos,ios,android
|
|
444
|
-
--entry <relative-path>
|
|
445
|
-
--ble-ota
|
|
446
471
|
--core-dependency <specifier>
|
|
447
472
|
--cli-dependency <specifier>
|
|
448
|
-
--install
|
|
473
|
+
--install
|
|
449
474
|
--dry-run
|
|
450
475
|
--yes
|
|
451
476
|
--force
|
package/src/gea.mjs
CHANGED
|
@@ -40,7 +40,7 @@ export async function runGea(argv, io = {}) {
|
|
|
40
40
|
if (command === 'create') {
|
|
41
41
|
const createArgs = argv.slice(1)
|
|
42
42
|
if (option(parsed, 'install') === undefined) createArgs.push('--install')
|
|
43
|
-
return runCreateGeastack(createArgs, io)
|
|
43
|
+
return runCreateGeastack(createArgs, { ...io, commandName: 'gea create' })
|
|
44
44
|
}
|
|
45
45
|
if (!command || command === 'help' || flag(parsed, 'help')) {
|
|
46
46
|
stdout(usage())
|
|
@@ -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
|
-
}
|