create-pebble-app 0.1.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.
- package/index.js +201 -0
- package/package.json +16 -0
package/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-pebble-app — scaffold a new react-pebble project.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx create-pebble-app my-watchface
|
|
8
|
+
* npx create-pebble-app my-watchface --counter (counter template)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
12
|
+
import { join, resolve } from 'node:path';
|
|
13
|
+
import { execSync } from 'node:child_process';
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const projectName = args.find(a => !a.startsWith('--'));
|
|
17
|
+
const template = args.includes('--counter') ? 'counter' : 'watchface';
|
|
18
|
+
|
|
19
|
+
if (!projectName) {
|
|
20
|
+
console.log(`
|
|
21
|
+
Usage: npx create-pebble-app <project-name> [--counter]
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
npx create-pebble-app my-watchface
|
|
25
|
+
npx create-pebble-app my-counter --counter
|
|
26
|
+
`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const dir = resolve(projectName);
|
|
31
|
+
|
|
32
|
+
if (existsSync(dir)) {
|
|
33
|
+
console.error(`Error: directory "${projectName}" already exists.`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`\n Creating ${projectName}...\n`);
|
|
38
|
+
|
|
39
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
40
|
+
|
|
41
|
+
// package.json
|
|
42
|
+
writeFileSync(join(dir, 'package.json'), JSON.stringify({
|
|
43
|
+
name: projectName,
|
|
44
|
+
version: '0.1.0',
|
|
45
|
+
private: true,
|
|
46
|
+
type: 'module',
|
|
47
|
+
scripts: {
|
|
48
|
+
build: 'vite build',
|
|
49
|
+
deploy: 'vite build',
|
|
50
|
+
typecheck: 'tsc --noEmit',
|
|
51
|
+
},
|
|
52
|
+
dependencies: {
|
|
53
|
+
'react-pebble': '^0.1.0',
|
|
54
|
+
},
|
|
55
|
+
devDependencies: {
|
|
56
|
+
'typescript': '^5.0.0',
|
|
57
|
+
'vite': '^8.0.0',
|
|
58
|
+
},
|
|
59
|
+
}, null, 2) + '\n');
|
|
60
|
+
|
|
61
|
+
// vite.config.ts
|
|
62
|
+
writeFileSync(join(dir, 'vite.config.ts'), `import { defineConfig } from 'vite';
|
|
63
|
+
import { pebblePiu } from 'react-pebble/plugin';
|
|
64
|
+
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
plugins: [
|
|
67
|
+
pebblePiu({
|
|
68
|
+
entry: 'src/App.tsx',
|
|
69
|
+
deploy: true,
|
|
70
|
+
}),
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
`);
|
|
74
|
+
|
|
75
|
+
// tsconfig.json
|
|
76
|
+
writeFileSync(join(dir, 'tsconfig.json'), JSON.stringify({
|
|
77
|
+
compilerOptions: {
|
|
78
|
+
target: 'es2022',
|
|
79
|
+
module: 'esnext',
|
|
80
|
+
moduleResolution: 'bundler',
|
|
81
|
+
lib: ['es2022'],
|
|
82
|
+
jsx: 'react-jsx',
|
|
83
|
+
jsxImportSource: 'preact',
|
|
84
|
+
strict: true,
|
|
85
|
+
esModuleInterop: true,
|
|
86
|
+
skipLibCheck: true,
|
|
87
|
+
noEmit: true,
|
|
88
|
+
},
|
|
89
|
+
include: ['src'],
|
|
90
|
+
}, null, 2) + '\n');
|
|
91
|
+
|
|
92
|
+
// .gitignore
|
|
93
|
+
writeFileSync(join(dir, '.gitignore'), `node_modules/
|
|
94
|
+
dist/
|
|
95
|
+
.pebble-build/
|
|
96
|
+
*.log
|
|
97
|
+
`);
|
|
98
|
+
|
|
99
|
+
// src/App.tsx
|
|
100
|
+
if (template === 'counter') {
|
|
101
|
+
writeFileSync(join(dir, 'src/App.tsx'), `import { render, Text, Rect, Group } from 'react-pebble';
|
|
102
|
+
import { useButton, useState } from 'react-pebble/hooks';
|
|
103
|
+
|
|
104
|
+
function Counter() {
|
|
105
|
+
const [count, setCount] = useState(0);
|
|
106
|
+
|
|
107
|
+
useButton('up', () => setCount((c: number) => c + 1));
|
|
108
|
+
useButton('down', () => setCount((c: number) => c - 1));
|
|
109
|
+
useButton('select', () => setCount(0));
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Group>
|
|
113
|
+
<Rect x={0} y={0} w={200} h={228} fill="black" />
|
|
114
|
+
<Rect x={0} y={0} w={200} h={32} fill="white" />
|
|
115
|
+
<Text x={4} y={6} w={192} font="gothic18Bold" color="black">
|
|
116
|
+
Counter
|
|
117
|
+
</Text>
|
|
118
|
+
<Text x={0} y={80} w={200} font="bitham42Bold" color="white" align="center">
|
|
119
|
+
{count.toString()}
|
|
120
|
+
</Text>
|
|
121
|
+
<Text x={0} y={180} w={200} font="gothic14" color="lightGray" align="center">
|
|
122
|
+
UP/DOWN to count, SELECT to reset
|
|
123
|
+
</Text>
|
|
124
|
+
</Group>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function main() {
|
|
129
|
+
return render(<Counter />);
|
|
130
|
+
}
|
|
131
|
+
`);
|
|
132
|
+
} else {
|
|
133
|
+
writeFileSync(join(dir, 'src/App.tsx'), `import { render, Text, Rect, Circle, Group } from 'react-pebble';
|
|
134
|
+
import { useTime } from 'react-pebble/hooks';
|
|
135
|
+
|
|
136
|
+
function WatchFace() {
|
|
137
|
+
const time = useTime();
|
|
138
|
+
const hours = time.getHours().toString().padStart(2, '0');
|
|
139
|
+
const minutes = time.getMinutes().toString().padStart(2, '0');
|
|
140
|
+
const seconds = time.getSeconds().toString().padStart(2, '0');
|
|
141
|
+
|
|
142
|
+
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
143
|
+
const dateStr = days[time.getDay()] + ' ' + time.getDate();
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Group>
|
|
147
|
+
<Rect x={0} y={0} w={200} h={228} fill="black" />
|
|
148
|
+
|
|
149
|
+
{/* Hour markers */}
|
|
150
|
+
<Circle x={92} y={10} r={6} fill="white" />
|
|
151
|
+
<Circle x={174} y={100} r={6} fill="white" />
|
|
152
|
+
<Circle x={92} y={190} r={6} fill="white" />
|
|
153
|
+
<Circle x={10} y={100} r={6} fill="white" />
|
|
154
|
+
|
|
155
|
+
{/* Center dot */}
|
|
156
|
+
<Circle x={90} y={100} r={10} fill="red" />
|
|
157
|
+
|
|
158
|
+
{/* Time */}
|
|
159
|
+
<Text x={0} y={70} w={200} font="bitham42Bold" color="white" align="center">
|
|
160
|
+
{hours}:{minutes}
|
|
161
|
+
</Text>
|
|
162
|
+
|
|
163
|
+
{/* Seconds */}
|
|
164
|
+
<Text x={0} y={122} w={200} font="gothic24" color="lightGray" align="center">
|
|
165
|
+
{seconds}
|
|
166
|
+
</Text>
|
|
167
|
+
|
|
168
|
+
{/* Date */}
|
|
169
|
+
<Text x={0} y={158} w={200} font="gothic18" color="white" align="center">
|
|
170
|
+
{dateStr}
|
|
171
|
+
</Text>
|
|
172
|
+
</Group>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function main() {
|
|
177
|
+
return render(<WatchFace />);
|
|
178
|
+
}
|
|
179
|
+
`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Install dependencies
|
|
183
|
+
console.log(' Installing dependencies...\n');
|
|
184
|
+
try {
|
|
185
|
+
execSync('npm install', { cwd: dir, stdio: 'inherit' });
|
|
186
|
+
} catch {
|
|
187
|
+
console.log('\n npm install failed — you can run it manually.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(`
|
|
191
|
+
Done! Created ${projectName}/
|
|
192
|
+
|
|
193
|
+
Next steps:
|
|
194
|
+
cd ${projectName}
|
|
195
|
+
npx vite build Build + deploy to Pebble emulator
|
|
196
|
+
|
|
197
|
+
Your app is in src/App.tsx — edit it and rebuild.
|
|
198
|
+
The Vite plugin handles everything: compile → scaffold → deploy.
|
|
199
|
+
|
|
200
|
+
Requires: Pebble SDK v4.9+ (pebble --version)
|
|
201
|
+
`);
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-pebble-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a new react-pebble watchface or app project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-pebble-app": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": ["index.js"],
|
|
10
|
+
"keywords": ["pebble", "smartwatch", "create", "scaffold", "watchface"],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/eddiemoore/react-pebble"
|
|
15
|
+
}
|
|
16
|
+
}
|