react-pebble 0.1.1 → 0.2.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/LICENSE +21 -0
- package/dist/lib/compiler.cjs +2 -2
- package/dist/lib/compiler.cjs.map +1 -1
- package/dist/lib/compiler.js +4 -1
- package/dist/lib/compiler.js.map +1 -1
- package/dist/lib/components.cjs +1 -1
- package/dist/lib/components.cjs.map +1 -1
- package/dist/lib/components.js +44 -5
- package/dist/lib/components.js.map +1 -1
- package/dist/lib/hooks.cjs +1 -1
- package/dist/lib/hooks.cjs.map +1 -1
- package/dist/lib/hooks.js +198 -3
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.js +231 -108
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/plugin.cjs +25 -5
- package/dist/lib/plugin.cjs.map +1 -1
- package/dist/lib/plugin.js +62 -35
- package/dist/lib/plugin.js.map +1 -1
- package/dist/lib/src/compiler/index.d.ts +2 -0
- package/dist/lib/src/components/index.d.ts +28 -1
- package/dist/lib/src/hooks/index.d.ts +182 -0
- package/dist/lib/src/index.d.ts +4 -4
- package/dist/lib/src/pebble-output.d.ts +15 -0
- package/dist/lib/src/plugin/index.d.ts +6 -0
- package/package.json +10 -11
- package/scripts/compile-to-piu.ts +315 -26
- package/scripts/deploy.sh +0 -0
- package/scripts/test-emulator.sh +371 -0
- package/src/compiler/index.ts +8 -1
- package/src/components/index.tsx +75 -1
- package/src/hooks/index.ts +507 -19
- package/src/index.ts +26 -0
- package/src/pebble-output.ts +408 -48
- package/src/plugin/index.ts +101 -49
- package/src/types/moddable.d.ts +26 -4
package/src/plugin/index.ts
CHANGED
|
@@ -41,6 +41,12 @@ export interface PebblePiuOptions {
|
|
|
41
41
|
settleMs?: number;
|
|
42
42
|
/** Target platform — sets screen dimensions (default: 'emery') */
|
|
43
43
|
platform?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Target platforms for multi-platform builds (e.g. ['emery', 'gabbro']).
|
|
46
|
+
* When set, builds once per platform into `{buildDir}-{platform}/`.
|
|
47
|
+
* Overrides the `platform` option.
|
|
48
|
+
*/
|
|
49
|
+
targetPlatforms?: string[];
|
|
44
50
|
/** Directory for the generated Pebble project (default: '.pebble-build') */
|
|
45
51
|
buildDir?: string;
|
|
46
52
|
/** Auto-run pebble build + install after compilation */
|
|
@@ -63,51 +69,60 @@ export function pebblePiu(options: PebblePiuOptions): Plugin {
|
|
|
63
69
|
async closeBundle() {
|
|
64
70
|
const log = (msg: string) => console.log(`[react-pebble] ${msg}`);
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
stdio: 'inherit'
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
72
|
+
const platforms = options.targetPlatforms ?? [options.platform ?? 'emery'];
|
|
73
|
+
|
|
74
|
+
for (const platform of platforms) {
|
|
75
|
+
const platBuildDir = platforms.length > 1
|
|
76
|
+
? resolve(`${options.buildDir ?? '.pebble-build'}-${platform}`)
|
|
77
|
+
: buildDir;
|
|
78
|
+
|
|
79
|
+
// 1. Compile JSX → piu
|
|
80
|
+
log(`Compiling ${options.entry} for ${platform}...`);
|
|
81
|
+
const result = await compileToPiu({
|
|
82
|
+
entry: options.entry,
|
|
83
|
+
settleMs: options.settleMs,
|
|
84
|
+
platform,
|
|
85
|
+
logger: log,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// 2. Scaffold pebble project
|
|
89
|
+
log(`Scaffolding pebble project in ${platBuildDir}...`);
|
|
90
|
+
scaffoldPebbleProject(platBuildDir, {
|
|
91
|
+
watchface: !result.hasButtons,
|
|
92
|
+
messageKeys: result.messageKeys,
|
|
93
|
+
mockDataSource: result.mockDataSource,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// 3. Write compiled output
|
|
97
|
+
const outputPath = join(platBuildDir, 'src', 'embeddedjs', 'main.js');
|
|
98
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
99
|
+
writeFileSync(outputPath, result.code);
|
|
100
|
+
log(`Wrote ${result.code.split('\n').length} lines to ${outputPath}`);
|
|
101
|
+
|
|
102
|
+
// 4. Optionally build + deploy
|
|
103
|
+
if (options.deploy) {
|
|
104
|
+
const emu = options.emulator ?? platform;
|
|
105
|
+
log(`Running pebble build for ${platform}...`);
|
|
106
|
+
try {
|
|
107
|
+
execSync('pebble build', { cwd: platBuildDir, stdio: 'inherit' });
|
|
108
|
+
log(`Installing to ${emu} emulator...`);
|
|
109
|
+
execSync('pebble kill 2>/dev/null; pebble wipe 2>/dev/null; sleep 2', {
|
|
110
|
+
cwd: platBuildDir,
|
|
111
|
+
stdio: 'ignore',
|
|
112
|
+
});
|
|
113
|
+
execSync(`pebble install --emulator ${emu}`, {
|
|
114
|
+
cwd: platBuildDir,
|
|
115
|
+
stdio: 'inherit',
|
|
116
|
+
timeout: 30000,
|
|
117
|
+
});
|
|
118
|
+
log(`Deployed to ${emu}. Run 'cd ${platBuildDir} && pebble logs' for live output.`);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
log(`Deploy failed for ${platform} — is the Pebble SDK installed? (pebble --version)`);
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
log(`Done (${platform}). To deploy:\n cd ${platBuildDir} && pebble build && pebble install --emulator ${platform}`);
|
|
108
125
|
}
|
|
109
|
-
} else {
|
|
110
|
-
log(`Done. To deploy:\n cd ${buildDir} && pebble build && pebble install --emulator emery`);
|
|
111
126
|
}
|
|
112
127
|
},
|
|
113
128
|
};
|
|
@@ -120,6 +135,8 @@ export function pebblePiu(options: PebblePiuOptions): Plugin {
|
|
|
120
135
|
interface ScaffoldOptions {
|
|
121
136
|
watchface: boolean;
|
|
122
137
|
messageKeys: string[];
|
|
138
|
+
/** TypeScript source of mock data (for generating phone-side JS) */
|
|
139
|
+
mockDataSource?: string | null;
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
function scaffoldPebbleProject(dir: string, options: ScaffoldOptions): void {
|
|
@@ -207,17 +224,52 @@ int main(void) {
|
|
|
207
224
|
// Phone-side JS
|
|
208
225
|
const pkjsPath = join(dir, 'src', 'pkjs', 'index.js');
|
|
209
226
|
if (options.messageKeys.length > 0 && options.messageKeys[0] !== 'dummy') {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
227
|
+
const key = options.messageKeys[0]!;
|
|
228
|
+
const mockSrc = options.mockDataSource;
|
|
229
|
+
if (mockSrc) {
|
|
230
|
+
// Generate working phone-side JS that sends the mock data
|
|
231
|
+
// Strip TypeScript type annotations from the mock data source
|
|
232
|
+
const cleanMockSrc = mockSrc
|
|
233
|
+
.replace(/as\s+const/g, '')
|
|
234
|
+
.replace(/:\s*\w+(\[\])?/g, '')
|
|
235
|
+
.replace(/<[^>]+>/g, '');
|
|
236
|
+
writeFileSync(
|
|
237
|
+
pkjsPath,
|
|
238
|
+
`// Phone-side PebbleKit JS — sends mock data to watch via AppMessage.
|
|
213
239
|
// Replace the mock data below with a real API fetch.
|
|
214
240
|
|
|
241
|
+
Pebble.addEventListener("ready", function () {
|
|
242
|
+
console.log("Phone JS ready — sending data to watch...");
|
|
243
|
+
|
|
244
|
+
// Mock data (from example). Replace with fetch() for real data.
|
|
245
|
+
var data = ${cleanMockSrc};
|
|
246
|
+
|
|
247
|
+
// Send to watch after a short delay (wait for Message subscription)
|
|
248
|
+
setTimeout(function() {
|
|
249
|
+
Pebble.sendAppMessage(
|
|
250
|
+
{ "${key}": JSON.stringify(data) },
|
|
251
|
+
function() { console.log("Data sent to watch successfully."); },
|
|
252
|
+
function(e) { console.log("Send failed: " + JSON.stringify(e)); }
|
|
253
|
+
);
|
|
254
|
+
}, 2000);
|
|
255
|
+
});
|
|
256
|
+
`,
|
|
257
|
+
);
|
|
258
|
+
} else {
|
|
259
|
+
writeFileSync(
|
|
260
|
+
pkjsPath,
|
|
261
|
+
`// Phone-side PebbleKit JS — sends data to watch via AppMessage.
|
|
262
|
+
// Replace the placeholder below with a real API fetch.
|
|
263
|
+
|
|
215
264
|
Pebble.addEventListener("ready", function () {
|
|
216
265
|
console.log("Phone JS ready.");
|
|
217
|
-
//
|
|
266
|
+
// Example: fetch data and send to watch
|
|
267
|
+
// var data = { key: "value" };
|
|
268
|
+
// Pebble.sendAppMessage({ "${key}": JSON.stringify(data) });
|
|
218
269
|
});
|
|
219
270
|
`,
|
|
220
|
-
|
|
271
|
+
);
|
|
272
|
+
}
|
|
221
273
|
} else if (!existsSync(pkjsPath)) {
|
|
222
274
|
writeFileSync(
|
|
223
275
|
pkjsPath,
|
package/src/types/moddable.d.ts
CHANGED
|
@@ -44,13 +44,20 @@ interface WatchButtonPayload {
|
|
|
44
44
|
[key: string]: unknown;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
interface WatchConnected {
|
|
48
|
+
/** Whether the Pebble companion app is connected. */
|
|
49
|
+
app: boolean;
|
|
50
|
+
/** Whether PebbleKit JS is available. */
|
|
51
|
+
pebblekit: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
interface ModdableWatch {
|
|
48
|
-
addEventListener(event: WatchEvent, handler: (
|
|
49
|
-
removeEventListener(event: WatchEvent, handler: (
|
|
55
|
+
addEventListener(event: WatchEvent, handler: (...args: any[]) => void): void;
|
|
56
|
+
removeEventListener(event: WatchEvent, handler: (...args: any[]) => void): void;
|
|
50
57
|
/** Unexplained method on the prototype — possibly the internal dispatch path. */
|
|
51
58
|
do?: (...args: unknown[]) => unknown;
|
|
52
|
-
/** Connection / pairing state
|
|
53
|
-
connected?:
|
|
59
|
+
/** Connection / pairing state. */
|
|
60
|
+
connected?: WatchConnected;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
declare const watch: ModdableWatch | undefined;
|
|
@@ -148,6 +155,21 @@ declare module 'commodetto/PocoCore' {
|
|
|
148
155
|
export { default } from 'commodetto/Poco';
|
|
149
156
|
}
|
|
150
157
|
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Battery — Alloy provides static properties on a Battery object.
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
interface PebbleBattery {
|
|
163
|
+
/** Battery percentage (0–100). */
|
|
164
|
+
readonly percent: number;
|
|
165
|
+
/** Whether the watch is currently charging. */
|
|
166
|
+
readonly charging: boolean;
|
|
167
|
+
/** Whether the watch is plugged in. */
|
|
168
|
+
readonly plugged: boolean;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
declare const Battery: PebbleBattery | undefined;
|
|
172
|
+
|
|
151
173
|
// Outline extension module — adds blendOutline / blendPolygon to Poco.prototype.
|
|
152
174
|
// Importing this module has the side-effect of installing those methods.
|
|
153
175
|
declare module 'commodetto/outline/PocoOutline' {
|