react-pebble 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/dist/lib/compiler.cjs +3 -0
- package/dist/lib/compiler.cjs.map +1 -0
- package/dist/lib/compiler.js +54 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/components.cjs +2 -0
- package/dist/lib/components.cjs.map +1 -0
- package/dist/lib/components.js +80 -0
- package/dist/lib/components.js.map +1 -0
- package/dist/lib/hooks.cjs +2 -0
- package/dist/lib/hooks.cjs.map +1 -0
- package/dist/lib/hooks.js +99 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/index.cjs +2 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.js +585 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/platform.cjs +2 -0
- package/dist/lib/platform.cjs.map +1 -0
- package/dist/lib/platform.js +52 -0
- package/dist/lib/platform.js.map +1 -0
- package/dist/lib/plugin.cjs +60 -0
- package/dist/lib/plugin.cjs.map +1 -0
- package/dist/lib/plugin.js +102 -0
- package/dist/lib/plugin.js.map +1 -0
- package/dist/lib/src/compiler/index.d.ts +40 -0
- package/dist/lib/src/components/index.d.ts +129 -0
- package/dist/lib/src/hooks/index.d.ts +75 -0
- package/dist/lib/src/index.d.ts +36 -0
- package/dist/lib/src/pebble-dom-shim.d.ts +45 -0
- package/dist/lib/src/pebble-dom.d.ts +59 -0
- package/dist/lib/src/pebble-output.d.ts +44 -0
- package/dist/lib/src/pebble-reconciler.d.ts +16 -0
- package/dist/lib/src/pebble-render.d.ts +31 -0
- package/dist/lib/src/platform.d.ts +30 -0
- package/dist/lib/src/plugin/index.d.ts +20 -0
- package/package.json +90 -0
- package/scripts/compile-to-piu.ts +1794 -0
- package/scripts/deploy.sh +46 -0
- package/src/compiler/index.ts +114 -0
- package/src/components/index.tsx +280 -0
- package/src/hooks/index.ts +311 -0
- package/src/index.ts +126 -0
- package/src/pebble-dom-shim.ts +266 -0
- package/src/pebble-dom.ts +190 -0
- package/src/pebble-output.ts +310 -0
- package/src/pebble-reconciler.ts +54 -0
- package/src/pebble-render.ts +311 -0
- package/src/platform.ts +50 -0
- package/src/plugin/index.ts +274 -0
- package/src/types/moddable.d.ts +156 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.cjs","names":[],"sources":["../../src/platform.ts"],"sourcesContent":["/**\n * src/platform.ts — Screen dimensions and platform constants.\n *\n * The compiler sets these before rendering so components can use\n * SCREEN.width / SCREEN.height instead of hardcoding pixel values.\n *\n * Usage:\n * import { SCREEN } from 'react-pebble';\n * <Rect x={0} y={0} w={SCREEN.width} h={SCREEN.height} fill=\"black\" />\n */\n\nexport interface PebblePlatform {\n name: string;\n width: number;\n height: number;\n isRound: boolean;\n}\n\nexport const PLATFORMS: Record<string, PebblePlatform> = {\n emery: { name: 'emery', width: 200, height: 228, isRound: false },\n gabbro: { name: 'gabbro', width: 200, height: 228, isRound: false },\n // These are NOT supported by Alloy, but listed for reference/future Rocky.js:\n basalt: { name: 'basalt', width: 144, height: 168, isRound: false },\n chalk: { name: 'chalk', width: 180, height: 180, isRound: true },\n diorite: { name: 'diorite', width: 144, height: 168, isRound: false },\n aplite: { name: 'aplite', width: 144, height: 168, isRound: false },\n};\n\n/**\n * Current screen dimensions. Set by the compiler before rendering.\n * Components import this and use SCREEN.width / SCREEN.height for\n * responsive layouts.\n */\nexport const SCREEN = {\n width: 200,\n height: 228,\n isRound: false,\n platform: 'emery',\n};\n\n/** @internal — called by the compiler to set the platform. */\nexport function _setPlatform(platform: string): void {\n const p = PLATFORMS[platform];\n if (p) {\n SCREEN.width = p.width;\n SCREEN.height = p.height;\n SCREEN.isRound = p.isRound;\n SCREEN.platform = p.name;\n }\n}\n"],"mappings":"mEAkBA,IAAa,EAA4C,CACvD,MAAO,CAAE,KAAM,QAAS,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAO,CACjE,OAAQ,CAAE,KAAM,SAAU,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAO,CAEnE,OAAQ,CAAE,KAAM,SAAU,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAO,CACnE,MAAO,CAAE,KAAM,QAAS,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAM,CAChE,QAAS,CAAE,KAAM,UAAW,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAO,CACrE,OAAQ,CAAE,KAAM,SAAU,MAAO,IAAK,OAAQ,IAAK,QAAS,GAAO,CACpE,CAOY,EAAS,CACpB,MAAO,IACP,OAAQ,IACR,QAAS,GACT,SAAU,QACX,CAGD,SAAgB,EAAa,EAAwB,CACnD,IAAM,EAAI,EAAU,GAChB,IACF,EAAO,MAAQ,EAAE,MACjB,EAAO,OAAS,EAAE,OAClB,EAAO,QAAU,EAAE,QACnB,EAAO,SAAW,EAAE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/platform.ts
|
|
2
|
+
var e = {
|
|
3
|
+
emery: {
|
|
4
|
+
name: "emery",
|
|
5
|
+
width: 200,
|
|
6
|
+
height: 228,
|
|
7
|
+
isRound: !1
|
|
8
|
+
},
|
|
9
|
+
gabbro: {
|
|
10
|
+
name: "gabbro",
|
|
11
|
+
width: 200,
|
|
12
|
+
height: 228,
|
|
13
|
+
isRound: !1
|
|
14
|
+
},
|
|
15
|
+
basalt: {
|
|
16
|
+
name: "basalt",
|
|
17
|
+
width: 144,
|
|
18
|
+
height: 168,
|
|
19
|
+
isRound: !1
|
|
20
|
+
},
|
|
21
|
+
chalk: {
|
|
22
|
+
name: "chalk",
|
|
23
|
+
width: 180,
|
|
24
|
+
height: 180,
|
|
25
|
+
isRound: !0
|
|
26
|
+
},
|
|
27
|
+
diorite: {
|
|
28
|
+
name: "diorite",
|
|
29
|
+
width: 144,
|
|
30
|
+
height: 168,
|
|
31
|
+
isRound: !1
|
|
32
|
+
},
|
|
33
|
+
aplite: {
|
|
34
|
+
name: "aplite",
|
|
35
|
+
width: 144,
|
|
36
|
+
height: 168,
|
|
37
|
+
isRound: !1
|
|
38
|
+
}
|
|
39
|
+
}, t = {
|
|
40
|
+
width: 200,
|
|
41
|
+
height: 228,
|
|
42
|
+
isRound: !1,
|
|
43
|
+
platform: "emery"
|
|
44
|
+
};
|
|
45
|
+
function n(n) {
|
|
46
|
+
let r = e[n];
|
|
47
|
+
r && (t.width = r.width, t.height = r.height, t.isRound = r.isRound, t.platform = r.name);
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { e as PLATFORMS, t as SCREEN, n as _setPlatform };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","names":[],"sources":["../../src/platform.ts"],"sourcesContent":["/**\n * src/platform.ts — Screen dimensions and platform constants.\n *\n * The compiler sets these before rendering so components can use\n * SCREEN.width / SCREEN.height instead of hardcoding pixel values.\n *\n * Usage:\n * import { SCREEN } from 'react-pebble';\n * <Rect x={0} y={0} w={SCREEN.width} h={SCREEN.height} fill=\"black\" />\n */\n\nexport interface PebblePlatform {\n name: string;\n width: number;\n height: number;\n isRound: boolean;\n}\n\nexport const PLATFORMS: Record<string, PebblePlatform> = {\n emery: { name: 'emery', width: 200, height: 228, isRound: false },\n gabbro: { name: 'gabbro', width: 200, height: 228, isRound: false },\n // These are NOT supported by Alloy, but listed for reference/future Rocky.js:\n basalt: { name: 'basalt', width: 144, height: 168, isRound: false },\n chalk: { name: 'chalk', width: 180, height: 180, isRound: true },\n diorite: { name: 'diorite', width: 144, height: 168, isRound: false },\n aplite: { name: 'aplite', width: 144, height: 168, isRound: false },\n};\n\n/**\n * Current screen dimensions. Set by the compiler before rendering.\n * Components import this and use SCREEN.width / SCREEN.height for\n * responsive layouts.\n */\nexport const SCREEN = {\n width: 200,\n height: 228,\n isRound: false,\n platform: 'emery',\n};\n\n/** @internal — called by the compiler to set the platform. */\nexport function _setPlatform(platform: string): void {\n const p = PLATFORMS[platform];\n if (p) {\n SCREEN.width = p.width;\n SCREEN.height = p.height;\n SCREEN.isRound = p.isRound;\n SCREEN.platform = p.name;\n }\n}\n"],"mappings":";AAkBA,IAAa,IAA4C;CACvD,OAAO;EAAE,MAAM;EAAS,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAO;CACjE,QAAQ;EAAE,MAAM;EAAU,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAO;CAEnE,QAAQ;EAAE,MAAM;EAAU,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAO;CACnE,OAAO;EAAE,MAAM;EAAS,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAM;CAChE,SAAS;EAAE,MAAM;EAAW,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAO;CACrE,QAAQ;EAAE,MAAM;EAAU,OAAO;EAAK,QAAQ;EAAK,SAAS;EAAO;CACpE,EAOY,IAAS;CACpB,OAAO;CACP,QAAQ;CACR,SAAS;CACT,UAAU;CACX;AAGD,SAAgB,EAAa,GAAwB;CACnD,IAAM,IAAI,EAAU;AACpB,CAAI,MACF,EAAO,QAAQ,EAAE,OACjB,EAAO,SAAS,EAAE,QAClB,EAAO,UAAU,EAAE,SACnB,EAAO,WAAW,EAAE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./compiler.cjs`);let t=require(`node:child_process`),n=require(`node:path`),r=require(`node:url`),i=require(`node:fs`),a=require(`node:crypto`);(0,n.dirname)((0,r.fileURLToPath)({}.url));function o(r){let a=(0,n.resolve)(r.buildDir??`.pebble-build`);return{name:`react-pebble-piu`,apply:`build`,async closeBundle(){let o=e=>console.log(`[react-pebble] ${e}`);o(`Compiling ${r.entry}...`);let c=await e.compileToPiu({entry:r.entry,settleMs:r.settleMs,platform:r.platform,logger:o});o(`Scaffolding pebble project in ${a}...`),s(a,{watchface:!c.hasButtons,messageKeys:c.messageKeys});let l=(0,n.join)(a,`src`,`embeddedjs`,`main.js`);if((0,i.mkdirSync)((0,n.dirname)(l),{recursive:!0}),(0,i.writeFileSync)(l,c.code),o(`Wrote ${c.code.split(`
|
|
2
|
+
`).length} lines to ${l}`),r.deploy){let e=r.emulator??r.platform??`emery`;o(`Running pebble build...`);try{(0,t.execSync)(`pebble build`,{cwd:a,stdio:`inherit`}),o(`Installing to ${e} emulator...`),(0,t.execSync)(`pebble kill 2>/dev/null; pebble wipe 2>/dev/null; sleep 2`,{cwd:a,stdio:`ignore`}),(0,t.execSync)(`pebble install --emulator ${e}`,{cwd:a,stdio:`inherit`,timeout:3e4}),o(`Deployed to ${e}. Run 'cd ${a} && pebble logs' for live output.`)}catch(e){throw o(`Deploy failed — is the Pebble SDK installed? (pebble --version)`),e}}else o(`Done. To deploy:\n cd ${a} && pebble build && pebble install --emulator emery`)}}}function s(e,t){(0,i.mkdirSync)((0,n.join)(e,`src`,`embeddedjs`),{recursive:!0}),(0,i.mkdirSync)((0,n.join)(e,`src`,`c`),{recursive:!0}),(0,i.mkdirSync)((0,n.join)(e,`src`,`pkjs`),{recursive:!0});let r,o=(0,n.join)(e,`package.json`);if((0,i.existsSync)(o))try{r=JSON.parse((0,i.readFileSync)(o,`utf-8`))?.pebble?.uuid??(0,a.randomUUID)()}catch{r=(0,a.randomUUID)()}else r=(0,a.randomUUID)();let s={name:`react-pebble-app`,author:`react-pebble`,version:`1.0.0`,keywords:[`pebble-app`],private:!0,dependencies:{},pebble:{displayName:`react-pebble-app`,uuid:r,projectType:`moddable`,sdkVersion:`3`,enableMultiJS:!0,targetPlatforms:[`emery`,`gabbro`],watchapp:{watchface:t.watchface},messageKeys:t.messageKeys.length>0?t.messageKeys:[`dummy`],resources:{media:[]}}};(0,i.writeFileSync)(o,JSON.stringify(s,null,2)+`
|
|
3
|
+
`);let l=(0,n.join)(e,`wscript`);(0,i.existsSync)(l)||(0,i.writeFileSync)(l,c);let u=(0,n.join)(e,`src`,`c`,`mdbl.c`);(0,i.existsSync)(u)||(0,i.writeFileSync)(u,`#include <pebble.h>
|
|
4
|
+
|
|
5
|
+
int main(void) {
|
|
6
|
+
Window *w = window_create();
|
|
7
|
+
window_stack_push(w, true);
|
|
8
|
+
|
|
9
|
+
moddable_createMachine(NULL);
|
|
10
|
+
|
|
11
|
+
window_destroy(w);
|
|
12
|
+
}
|
|
13
|
+
`);let d=(0,n.join)(e,`src`,`embeddedjs`,`manifest.json`);(0,i.existsSync)(d)||(0,i.writeFileSync)(d,JSON.stringify({include:[`$(MODDABLE)/examples/manifest_mod.json`],modules:{"*":`./main.js`}},null,2)+`
|
|
14
|
+
`);let f=(0,n.join)(e,`src`,`pkjs`,`index.js`);t.messageKeys.length>0&&t.messageKeys[0]!==`dummy`?(0,i.writeFileSync)(f,`// Phone-side PebbleKit JS — sends data to watch via AppMessage.
|
|
15
|
+
// Replace the mock data below with a real API fetch.
|
|
16
|
+
|
|
17
|
+
Pebble.addEventListener("ready", function () {
|
|
18
|
+
console.log("Phone JS ready.");
|
|
19
|
+
// TODO: fetch real data and send via Pebble.sendAppMessage({ ${t.messageKeys[0]}: jsonString });
|
|
20
|
+
});
|
|
21
|
+
`):(0,i.existsSync)(f)||(0,i.writeFileSync)(f,`Pebble.addEventListener("ready", function(e) {
|
|
22
|
+
console.log("PebbleKit JS ready.");
|
|
23
|
+
});
|
|
24
|
+
`)}var c=`#
|
|
25
|
+
# Pebble SDK build configuration (auto-generated by react-pebble plugin)
|
|
26
|
+
#
|
|
27
|
+
import os.path
|
|
28
|
+
|
|
29
|
+
top = '.'
|
|
30
|
+
out = 'build'
|
|
31
|
+
|
|
32
|
+
def options(ctx):
|
|
33
|
+
ctx.load('pebble_sdk')
|
|
34
|
+
|
|
35
|
+
def configure(ctx):
|
|
36
|
+
ctx.load('pebble_sdk')
|
|
37
|
+
|
|
38
|
+
def build(ctx):
|
|
39
|
+
ctx.load('pebble_sdk')
|
|
40
|
+
build_worker = os.path.exists('worker_src')
|
|
41
|
+
binaries = []
|
|
42
|
+
cached_env = ctx.env
|
|
43
|
+
for platform in ctx.env.TARGET_PLATFORMS:
|
|
44
|
+
ctx.env = ctx.all_envs[platform]
|
|
45
|
+
ctx.set_group(ctx.env.PLATFORM_NAME)
|
|
46
|
+
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
|
|
47
|
+
ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')
|
|
48
|
+
if build_worker:
|
|
49
|
+
worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
|
|
50
|
+
binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})
|
|
51
|
+
ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf, bin_type='worker')
|
|
52
|
+
else:
|
|
53
|
+
binaries.append({'platform': platform, 'app_elf': app_elf})
|
|
54
|
+
ctx.env = cached_env
|
|
55
|
+
ctx.set_group('bundle')
|
|
56
|
+
ctx.pbl_bundle(binaries=binaries,
|
|
57
|
+
js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json', 'src/common/**/*.js']),
|
|
58
|
+
js_entry_file='src/pkjs/index.js')
|
|
59
|
+
`;exports.pebblePiu=o;
|
|
60
|
+
//# sourceMappingURL=plugin.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs","names":[],"sources":["../../src/plugin/index.ts"],"sourcesContent":["/**\n * src/plugin/index.ts — Vite plugin for react-pebble.\n *\n * Compiles JSX components to piu, scaffolds the Pebble project, and\n * optionally builds + deploys to the emulator. Users add this to their\n * vite.config.ts and run `vite build`.\n *\n * Usage:\n * import { pebblePiu } from 'react-pebble/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * pebblePiu({\n * entry: 'src/App.tsx',\n * settleMs: 200,\n * deploy: true,\n * }),\n * ],\n * });\n */\n\nimport type { Plugin } from 'vite';\nimport { execSync } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { randomUUID } from 'node:crypto';\nimport { compileToPiu } from '../compiler/index.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface PebblePiuOptions {\n /** Path to the entry .tsx file (relative to project root) */\n entry: string;\n /** Milliseconds to wait for async effects before snapshotting */\n settleMs?: number;\n /** Target platform — sets screen dimensions (default: 'emery') */\n platform?: string;\n /** Directory for the generated Pebble project (default: '.pebble-build') */\n buildDir?: string;\n /** Auto-run pebble build + install after compilation */\n deploy?: boolean;\n /** Emulator platform for deploy (default: same as platform) */\n emulator?: string;\n}\n\n/**\n * Vite plugin that compiles react-pebble JSX to piu and scaffolds a\n * Pebble project for deployment.\n */\nexport function pebblePiu(options: PebblePiuOptions): Plugin {\n const buildDir = resolve(options.buildDir ?? '.pebble-build');\n\n return {\n name: 'react-pebble-piu',\n apply: 'build',\n\n async closeBundle() {\n const log = (msg: string) => console.log(`[react-pebble] ${msg}`);\n\n // 1. Compile JSX → piu\n log(`Compiling ${options.entry}...`);\n const result = await compileToPiu({\n entry: options.entry,\n settleMs: options.settleMs,\n platform: options.platform,\n logger: log,\n });\n\n // 2. Scaffold pebble project\n log(`Scaffolding pebble project in ${buildDir}...`);\n scaffoldPebbleProject(buildDir, {\n watchface: !result.hasButtons,\n messageKeys: result.messageKeys,\n });\n\n // 3. Write compiled output\n const outputPath = join(buildDir, 'src', 'embeddedjs', 'main.js');\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, result.code);\n log(`Wrote ${result.code.split('\\n').length} lines to ${outputPath}`);\n\n // 4. Optionally build + deploy\n if (options.deploy) {\n const emu = options.emulator ?? options.platform ?? 'emery';\n log('Running pebble build...');\n try {\n execSync('pebble build', { cwd: buildDir, stdio: 'inherit' });\n log(`Installing to ${emu} emulator...`);\n execSync('pebble kill 2>/dev/null; pebble wipe 2>/dev/null; sleep 2', {\n cwd: buildDir,\n stdio: 'ignore',\n });\n execSync(`pebble install --emulator ${emu}`, {\n cwd: buildDir,\n stdio: 'inherit',\n timeout: 30000,\n });\n log(`Deployed to ${emu}. Run 'cd ${buildDir} && pebble logs' for live output.`);\n } catch (err) {\n log('Deploy failed — is the Pebble SDK installed? (pebble --version)');\n throw err;\n }\n } else {\n log(`Done. To deploy:\\n cd ${buildDir} && pebble build && pebble install --emulator emery`);\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pebble project scaffolding\n// ---------------------------------------------------------------------------\n\ninterface ScaffoldOptions {\n watchface: boolean;\n messageKeys: string[];\n}\n\nfunction scaffoldPebbleProject(dir: string, options: ScaffoldOptions): void {\n mkdirSync(join(dir, 'src', 'embeddedjs'), { recursive: true });\n mkdirSync(join(dir, 'src', 'c'), { recursive: true });\n mkdirSync(join(dir, 'src', 'pkjs'), { recursive: true });\n\n // Preserve UUID across builds (or generate a new one)\n let uuid: string;\n const pkgPath = join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const existing = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n uuid = existing?.pebble?.uuid ?? randomUUID();\n } catch {\n uuid = randomUUID();\n }\n } else {\n uuid = randomUUID();\n }\n\n // package.json\n const pkg = {\n name: 'react-pebble-app',\n author: 'react-pebble',\n version: '1.0.0',\n keywords: ['pebble-app'],\n private: true,\n dependencies: {},\n pebble: {\n displayName: 'react-pebble-app',\n uuid,\n projectType: 'moddable',\n sdkVersion: '3',\n enableMultiJS: true,\n targetPlatforms: ['emery', 'gabbro'],\n watchapp: { watchface: options.watchface },\n messageKeys: options.messageKeys.length > 0 ? options.messageKeys : ['dummy'],\n resources: { media: [] },\n },\n };\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n');\n\n // wscript (static — Pebble SDK build config)\n const wscriptPath = join(dir, 'wscript');\n if (!existsSync(wscriptPath)) {\n writeFileSync(wscriptPath, WSCRIPT_TEMPLATE);\n }\n\n // C stub (static)\n const cPath = join(dir, 'src', 'c', 'mdbl.c');\n if (!existsSync(cPath)) {\n writeFileSync(\n cPath,\n `#include <pebble.h>\n\nint main(void) {\n Window *w = window_create();\n window_stack_push(w, true);\n\n moddable_createMachine(NULL);\n\n window_destroy(w);\n}\n`,\n );\n }\n\n // Moddable manifest (static)\n const manifestPath = join(dir, 'src', 'embeddedjs', 'manifest.json');\n if (!existsSync(manifestPath)) {\n writeFileSync(\n manifestPath,\n JSON.stringify(\n {\n include: ['$(MODDABLE)/examples/manifest_mod.json'],\n modules: { '*': './main.js' },\n },\n null,\n 2,\n ) + '\\n',\n );\n }\n\n // Phone-side JS\n const pkjsPath = join(dir, 'src', 'pkjs', 'index.js');\n if (options.messageKeys.length > 0 && options.messageKeys[0] !== 'dummy') {\n writeFileSync(\n pkjsPath,\n `// Phone-side PebbleKit JS — sends data to watch via AppMessage.\n// Replace the mock data below with a real API fetch.\n\nPebble.addEventListener(\"ready\", function () {\n console.log(\"Phone JS ready.\");\n // TODO: fetch real data and send via Pebble.sendAppMessage({ ${options.messageKeys[0]}: jsonString });\n});\n`,\n );\n } else if (!existsSync(pkjsPath)) {\n writeFileSync(\n pkjsPath,\n `Pebble.addEventListener(\"ready\", function(e) {\n console.log(\"PebbleKit JS ready.\");\n});\n`,\n );\n }\n}\n\n// Named export only — avoids Vite's MIXED_EXPORTS warning.\n// Users import as: import { pebblePiu } from 'react-pebble/plugin';\n\n// ---------------------------------------------------------------------------\n// Inline templates (no external file dependencies)\n// ---------------------------------------------------------------------------\n\nconst WSCRIPT_TEMPLATE = `#\n# Pebble SDK build configuration (auto-generated by react-pebble plugin)\n#\nimport os.path\n\ntop = '.'\nout = 'build'\n\ndef options(ctx):\n ctx.load('pebble_sdk')\n\ndef configure(ctx):\n ctx.load('pebble_sdk')\n\ndef build(ctx):\n ctx.load('pebble_sdk')\n build_worker = os.path.exists('worker_src')\n binaries = []\n cached_env = ctx.env\n for platform in ctx.env.TARGET_PLATFORMS:\n ctx.env = ctx.all_envs[platform]\n ctx.set_group(ctx.env.PLATFORM_NAME)\n app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)\n ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')\n if build_worker:\n worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)\n binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})\n ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf, bin_type='worker')\n else:\n binaries.append({'platform': platform, 'app_elf': app_elf})\n ctx.env = cached_env\n ctx.set_group('bundle')\n ctx.pbl_bundle(binaries=binaries,\n js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json', 'src/common/**/*.js']),\n js_entry_file='src/pkjs/index.js')\n`;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { compileToPiu as e } from "./compiler.js";
|
|
2
|
+
import { execSync as t } from "node:child_process";
|
|
3
|
+
import { dirname as n, join as r, resolve as i } from "node:path";
|
|
4
|
+
import { fileURLToPath as a } from "node:url";
|
|
5
|
+
import { existsSync as o, mkdirSync as s, readFileSync as c, writeFileSync as l } from "node:fs";
|
|
6
|
+
import { randomUUID as u } from "node:crypto";
|
|
7
|
+
//#region src/plugin/index.ts
|
|
8
|
+
n(a(import.meta.url));
|
|
9
|
+
function d(a) {
|
|
10
|
+
let o = i(a.buildDir ?? ".pebble-build");
|
|
11
|
+
return {
|
|
12
|
+
name: "react-pebble-piu",
|
|
13
|
+
apply: "build",
|
|
14
|
+
async closeBundle() {
|
|
15
|
+
let i = (e) => console.log(`[react-pebble] ${e}`);
|
|
16
|
+
i(`Compiling ${a.entry}...`);
|
|
17
|
+
let c = await e({
|
|
18
|
+
entry: a.entry,
|
|
19
|
+
settleMs: a.settleMs,
|
|
20
|
+
platform: a.platform,
|
|
21
|
+
logger: i
|
|
22
|
+
});
|
|
23
|
+
i(`Scaffolding pebble project in ${o}...`), f(o, {
|
|
24
|
+
watchface: !c.hasButtons,
|
|
25
|
+
messageKeys: c.messageKeys
|
|
26
|
+
});
|
|
27
|
+
let u = r(o, "src", "embeddedjs", "main.js");
|
|
28
|
+
if (s(n(u), { recursive: !0 }), l(u, c.code), i(`Wrote ${c.code.split("\n").length} lines to ${u}`), a.deploy) {
|
|
29
|
+
let e = a.emulator ?? a.platform ?? "emery";
|
|
30
|
+
i("Running pebble build...");
|
|
31
|
+
try {
|
|
32
|
+
t("pebble build", {
|
|
33
|
+
cwd: o,
|
|
34
|
+
stdio: "inherit"
|
|
35
|
+
}), i(`Installing to ${e} emulator...`), t("pebble kill 2>/dev/null; pebble wipe 2>/dev/null; sleep 2", {
|
|
36
|
+
cwd: o,
|
|
37
|
+
stdio: "ignore"
|
|
38
|
+
}), t(`pebble install --emulator ${e}`, {
|
|
39
|
+
cwd: o,
|
|
40
|
+
stdio: "inherit",
|
|
41
|
+
timeout: 3e4
|
|
42
|
+
}), i(`Deployed to ${e}. Run 'cd ${o} && pebble logs' for live output.`);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw i("Deploy failed — is the Pebble SDK installed? (pebble --version)"), e;
|
|
45
|
+
}
|
|
46
|
+
} else i(`Done. To deploy:\n cd ${o} && pebble build && pebble install --emulator emery`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function f(e, t) {
|
|
51
|
+
s(r(e, "src", "embeddedjs"), { recursive: !0 }), s(r(e, "src", "c"), { recursive: !0 }), s(r(e, "src", "pkjs"), { recursive: !0 });
|
|
52
|
+
let n, i = r(e, "package.json");
|
|
53
|
+
if (o(i)) try {
|
|
54
|
+
n = JSON.parse(c(i, "utf-8"))?.pebble?.uuid ?? u();
|
|
55
|
+
} catch {
|
|
56
|
+
n = u();
|
|
57
|
+
}
|
|
58
|
+
else n = u();
|
|
59
|
+
let a = {
|
|
60
|
+
name: "react-pebble-app",
|
|
61
|
+
author: "react-pebble",
|
|
62
|
+
version: "1.0.0",
|
|
63
|
+
keywords: ["pebble-app"],
|
|
64
|
+
private: !0,
|
|
65
|
+
dependencies: {},
|
|
66
|
+
pebble: {
|
|
67
|
+
displayName: "react-pebble-app",
|
|
68
|
+
uuid: n,
|
|
69
|
+
projectType: "moddable",
|
|
70
|
+
sdkVersion: "3",
|
|
71
|
+
enableMultiJS: !0,
|
|
72
|
+
targetPlatforms: ["emery", "gabbro"],
|
|
73
|
+
watchapp: { watchface: t.watchface },
|
|
74
|
+
messageKeys: t.messageKeys.length > 0 ? t.messageKeys : ["dummy"],
|
|
75
|
+
resources: { media: [] }
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
l(i, JSON.stringify(a, null, 2) + "\n");
|
|
79
|
+
let d = r(e, "wscript");
|
|
80
|
+
o(d) || l(d, p);
|
|
81
|
+
let f = r(e, "src", "c", "mdbl.c");
|
|
82
|
+
o(f) || l(f, "#include <pebble.h>\n\nint main(void) {\n Window *w = window_create();\n window_stack_push(w, true);\n\n moddable_createMachine(NULL);\n\n window_destroy(w);\n}\n");
|
|
83
|
+
let m = r(e, "src", "embeddedjs", "manifest.json");
|
|
84
|
+
o(m) || l(m, JSON.stringify({
|
|
85
|
+
include: ["$(MODDABLE)/examples/manifest_mod.json"],
|
|
86
|
+
modules: { "*": "./main.js" }
|
|
87
|
+
}, null, 2) + "\n");
|
|
88
|
+
let h = r(e, "src", "pkjs", "index.js");
|
|
89
|
+
t.messageKeys.length > 0 && t.messageKeys[0] !== "dummy" ? l(h, `// Phone-side PebbleKit JS — sends data to watch via AppMessage.
|
|
90
|
+
// Replace the mock data below with a real API fetch.
|
|
91
|
+
|
|
92
|
+
Pebble.addEventListener("ready", function () {
|
|
93
|
+
console.log("Phone JS ready.");
|
|
94
|
+
// TODO: fetch real data and send via Pebble.sendAppMessage({ ${t.messageKeys[0]}: jsonString });
|
|
95
|
+
});
|
|
96
|
+
`) : o(h) || l(h, "Pebble.addEventListener(\"ready\", function(e) {\n console.log(\"PebbleKit JS ready.\");\n});\n");
|
|
97
|
+
}
|
|
98
|
+
var p = "#\n# Pebble SDK build configuration (auto-generated by react-pebble plugin)\n#\nimport os.path\n\ntop = '.'\nout = 'build'\n\ndef options(ctx):\n ctx.load('pebble_sdk')\n\ndef configure(ctx):\n ctx.load('pebble_sdk')\n\ndef build(ctx):\n ctx.load('pebble_sdk')\n build_worker = os.path.exists('worker_src')\n binaries = []\n cached_env = ctx.env\n for platform in ctx.env.TARGET_PLATFORMS:\n ctx.env = ctx.all_envs[platform]\n ctx.set_group(ctx.env.PLATFORM_NAME)\n app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)\n ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')\n if build_worker:\n worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)\n binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})\n ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf, bin_type='worker')\n else:\n binaries.append({'platform': platform, 'app_elf': app_elf})\n ctx.env = cached_env\n ctx.set_group('bundle')\n ctx.pbl_bundle(binaries=binaries,\n js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json', 'src/common/**/*.js']),\n js_entry_file='src/pkjs/index.js')\n";
|
|
99
|
+
//#endregion
|
|
100
|
+
export { d as pebblePiu };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../src/plugin/index.ts"],"sourcesContent":["/**\n * src/plugin/index.ts — Vite plugin for react-pebble.\n *\n * Compiles JSX components to piu, scaffolds the Pebble project, and\n * optionally builds + deploys to the emulator. Users add this to their\n * vite.config.ts and run `vite build`.\n *\n * Usage:\n * import { pebblePiu } from 'react-pebble/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * pebblePiu({\n * entry: 'src/App.tsx',\n * settleMs: 200,\n * deploy: true,\n * }),\n * ],\n * });\n */\n\nimport type { Plugin } from 'vite';\nimport { execSync } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { randomUUID } from 'node:crypto';\nimport { compileToPiu } from '../compiler/index.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface PebblePiuOptions {\n /** Path to the entry .tsx file (relative to project root) */\n entry: string;\n /** Milliseconds to wait for async effects before snapshotting */\n settleMs?: number;\n /** Target platform — sets screen dimensions (default: 'emery') */\n platform?: string;\n /** Directory for the generated Pebble project (default: '.pebble-build') */\n buildDir?: string;\n /** Auto-run pebble build + install after compilation */\n deploy?: boolean;\n /** Emulator platform for deploy (default: same as platform) */\n emulator?: string;\n}\n\n/**\n * Vite plugin that compiles react-pebble JSX to piu and scaffolds a\n * Pebble project for deployment.\n */\nexport function pebblePiu(options: PebblePiuOptions): Plugin {\n const buildDir = resolve(options.buildDir ?? '.pebble-build');\n\n return {\n name: 'react-pebble-piu',\n apply: 'build',\n\n async closeBundle() {\n const log = (msg: string) => console.log(`[react-pebble] ${msg}`);\n\n // 1. Compile JSX → piu\n log(`Compiling ${options.entry}...`);\n const result = await compileToPiu({\n entry: options.entry,\n settleMs: options.settleMs,\n platform: options.platform,\n logger: log,\n });\n\n // 2. Scaffold pebble project\n log(`Scaffolding pebble project in ${buildDir}...`);\n scaffoldPebbleProject(buildDir, {\n watchface: !result.hasButtons,\n messageKeys: result.messageKeys,\n });\n\n // 3. Write compiled output\n const outputPath = join(buildDir, 'src', 'embeddedjs', 'main.js');\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, result.code);\n log(`Wrote ${result.code.split('\\n').length} lines to ${outputPath}`);\n\n // 4. Optionally build + deploy\n if (options.deploy) {\n const emu = options.emulator ?? options.platform ?? 'emery';\n log('Running pebble build...');\n try {\n execSync('pebble build', { cwd: buildDir, stdio: 'inherit' });\n log(`Installing to ${emu} emulator...`);\n execSync('pebble kill 2>/dev/null; pebble wipe 2>/dev/null; sleep 2', {\n cwd: buildDir,\n stdio: 'ignore',\n });\n execSync(`pebble install --emulator ${emu}`, {\n cwd: buildDir,\n stdio: 'inherit',\n timeout: 30000,\n });\n log(`Deployed to ${emu}. Run 'cd ${buildDir} && pebble logs' for live output.`);\n } catch (err) {\n log('Deploy failed — is the Pebble SDK installed? (pebble --version)');\n throw err;\n }\n } else {\n log(`Done. To deploy:\\n cd ${buildDir} && pebble build && pebble install --emulator emery`);\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pebble project scaffolding\n// ---------------------------------------------------------------------------\n\ninterface ScaffoldOptions {\n watchface: boolean;\n messageKeys: string[];\n}\n\nfunction scaffoldPebbleProject(dir: string, options: ScaffoldOptions): void {\n mkdirSync(join(dir, 'src', 'embeddedjs'), { recursive: true });\n mkdirSync(join(dir, 'src', 'c'), { recursive: true });\n mkdirSync(join(dir, 'src', 'pkjs'), { recursive: true });\n\n // Preserve UUID across builds (or generate a new one)\n let uuid: string;\n const pkgPath = join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const existing = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n uuid = existing?.pebble?.uuid ?? randomUUID();\n } catch {\n uuid = randomUUID();\n }\n } else {\n uuid = randomUUID();\n }\n\n // package.json\n const pkg = {\n name: 'react-pebble-app',\n author: 'react-pebble',\n version: '1.0.0',\n keywords: ['pebble-app'],\n private: true,\n dependencies: {},\n pebble: {\n displayName: 'react-pebble-app',\n uuid,\n projectType: 'moddable',\n sdkVersion: '3',\n enableMultiJS: true,\n targetPlatforms: ['emery', 'gabbro'],\n watchapp: { watchface: options.watchface },\n messageKeys: options.messageKeys.length > 0 ? options.messageKeys : ['dummy'],\n resources: { media: [] },\n },\n };\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n');\n\n // wscript (static — Pebble SDK build config)\n const wscriptPath = join(dir, 'wscript');\n if (!existsSync(wscriptPath)) {\n writeFileSync(wscriptPath, WSCRIPT_TEMPLATE);\n }\n\n // C stub (static)\n const cPath = join(dir, 'src', 'c', 'mdbl.c');\n if (!existsSync(cPath)) {\n writeFileSync(\n cPath,\n `#include <pebble.h>\n\nint main(void) {\n Window *w = window_create();\n window_stack_push(w, true);\n\n moddable_createMachine(NULL);\n\n window_destroy(w);\n}\n`,\n );\n }\n\n // Moddable manifest (static)\n const manifestPath = join(dir, 'src', 'embeddedjs', 'manifest.json');\n if (!existsSync(manifestPath)) {\n writeFileSync(\n manifestPath,\n JSON.stringify(\n {\n include: ['$(MODDABLE)/examples/manifest_mod.json'],\n modules: { '*': './main.js' },\n },\n null,\n 2,\n ) + '\\n',\n );\n }\n\n // Phone-side JS\n const pkjsPath = join(dir, 'src', 'pkjs', 'index.js');\n if (options.messageKeys.length > 0 && options.messageKeys[0] !== 'dummy') {\n writeFileSync(\n pkjsPath,\n `// Phone-side PebbleKit JS — sends data to watch via AppMessage.\n// Replace the mock data below with a real API fetch.\n\nPebble.addEventListener(\"ready\", function () {\n console.log(\"Phone JS ready.\");\n // TODO: fetch real data and send via Pebble.sendAppMessage({ ${options.messageKeys[0]}: jsonString });\n});\n`,\n );\n } else if (!existsSync(pkjsPath)) {\n writeFileSync(\n pkjsPath,\n `Pebble.addEventListener(\"ready\", function(e) {\n console.log(\"PebbleKit JS ready.\");\n});\n`,\n );\n }\n}\n\n// Named export only — avoids Vite's MIXED_EXPORTS warning.\n// Users import as: import { pebblePiu } from 'react-pebble/plugin';\n\n// ---------------------------------------------------------------------------\n// Inline templates (no external file dependencies)\n// ---------------------------------------------------------------------------\n\nconst WSCRIPT_TEMPLATE = `#\n# Pebble SDK build configuration (auto-generated by react-pebble plugin)\n#\nimport os.path\n\ntop = '.'\nout = 'build'\n\ndef options(ctx):\n ctx.load('pebble_sdk')\n\ndef configure(ctx):\n ctx.load('pebble_sdk')\n\ndef build(ctx):\n ctx.load('pebble_sdk')\n build_worker = os.path.exists('worker_src')\n binaries = []\n cached_env = ctx.env\n for platform in ctx.env.TARGET_PLATFORMS:\n ctx.env = ctx.all_envs[platform]\n ctx.set_group(ctx.env.PLATFORM_NAME)\n app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)\n ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')\n if build_worker:\n worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)\n binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})\n ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf, bin_type='worker')\n else:\n binaries.append({'platform': platform, 'app_elf': app_elf})\n ctx.env = cached_env\n ctx.set_group('bundle')\n ctx.pbl_bundle(binaries=binaries,\n js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json', 'src/common/**/*.js']),\n js_entry_file='src/pkjs/index.js')\n`;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/compiler/index.ts — react-pebble compile-to-piu library API.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the compile-to-piu.ts script as a programmatic API. The script
|
|
5
|
+
* runs as a subprocess (it uses module-level state that requires process
|
|
6
|
+
* isolation). A future refactoring will inline the logic as a pure function.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { compileToPiu } from 'react-pebble/compiler';
|
|
10
|
+
* const result = await compileToPiu({ entry: 'examples/watchface.tsx' });
|
|
11
|
+
* console.log(result.code); // piu Application.template JS
|
|
12
|
+
*/
|
|
13
|
+
export interface CompileOptions {
|
|
14
|
+
/** Path to the entry .tsx file (relative to cwd or absolute) */
|
|
15
|
+
entry: string;
|
|
16
|
+
/** Milliseconds to wait for async effects (useEffect/setTimeout) */
|
|
17
|
+
settleMs?: number;
|
|
18
|
+
/** Target platform (default: 'emery') */
|
|
19
|
+
platform?: string;
|
|
20
|
+
/** Logger for diagnostic messages */
|
|
21
|
+
logger?: (msg: string) => void;
|
|
22
|
+
/** Project root directory (default: process.cwd()) */
|
|
23
|
+
projectRoot?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CompileResult {
|
|
26
|
+
/** The compiled piu JavaScript code */
|
|
27
|
+
code: string;
|
|
28
|
+
/** Whether the component uses useButton (needs watchapp mode) */
|
|
29
|
+
hasButtons: boolean;
|
|
30
|
+
/** Message keys used by useMessage hooks */
|
|
31
|
+
messageKeys: string[];
|
|
32
|
+
/** Diagnostic messages from the compiler */
|
|
33
|
+
diagnostics: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compile a Preact component to piu Application.template code.
|
|
37
|
+
*
|
|
38
|
+
* Internally runs scripts/compile-to-piu.ts as a subprocess.
|
|
39
|
+
*/
|
|
40
|
+
export declare function compileToPiu(options: CompileOptions): Promise<CompileResult>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ComponentChildren } from 'preact';
|
|
2
|
+
import { PebbleButtonHandler } from '../hooks/index.js';
|
|
3
|
+
type ReactNode = ComponentChildren;
|
|
4
|
+
export interface PositionProps {
|
|
5
|
+
x?: number;
|
|
6
|
+
y?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SizeProps {
|
|
9
|
+
w?: number;
|
|
10
|
+
h?: number;
|
|
11
|
+
/** Alias for `w`. */
|
|
12
|
+
width?: number;
|
|
13
|
+
/** Alias for `h`. */
|
|
14
|
+
height?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ButtonHandlerProps {
|
|
17
|
+
onUp?: PebbleButtonHandler;
|
|
18
|
+
onDown?: PebbleButtonHandler;
|
|
19
|
+
onSelect?: PebbleButtonHandler;
|
|
20
|
+
onBack?: PebbleButtonHandler;
|
|
21
|
+
onLongUp?: PebbleButtonHandler;
|
|
22
|
+
onLongDown?: PebbleButtonHandler;
|
|
23
|
+
onLongSelect?: PebbleButtonHandler;
|
|
24
|
+
}
|
|
25
|
+
export type ColorName = 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange' | 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray' | (string & {});
|
|
26
|
+
export type FontName = 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold' | 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold' | 'bitham30Black' | 'bitham42Bold' | 'bitham42Light' | 'bitham34MediumNumbers' | 'bitham42MediumNumbers' | 'robotoCondensed21' | 'roboto21' | 'droid28' | 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42' | (string & {});
|
|
27
|
+
export type Alignment = 'left' | 'center' | 'right';
|
|
28
|
+
declare module 'preact' {
|
|
29
|
+
namespace JSX {
|
|
30
|
+
interface IntrinsicElements {
|
|
31
|
+
'pbl-root': IntrinsicPblProps;
|
|
32
|
+
'pbl-rect': IntrinsicPblProps;
|
|
33
|
+
'pbl-circle': IntrinsicPblProps;
|
|
34
|
+
'pbl-text': IntrinsicPblProps;
|
|
35
|
+
'pbl-line': IntrinsicPblProps;
|
|
36
|
+
'pbl-image': IntrinsicPblProps;
|
|
37
|
+
'pbl-group': IntrinsicPblProps;
|
|
38
|
+
'pbl-statusbar': IntrinsicPblProps;
|
|
39
|
+
'pbl-actionbar': IntrinsicPblProps;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
interface IntrinsicPblProps {
|
|
44
|
+
children?: ReactNode;
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
export interface WindowProps extends ButtonHandlerProps {
|
|
48
|
+
backgroundColor?: ColorName;
|
|
49
|
+
fullscreen?: boolean;
|
|
50
|
+
children?: ReactNode;
|
|
51
|
+
}
|
|
52
|
+
export declare function Window({ children, ...props }: WindowProps): import('preact').VNode<import('preact').ClassAttributes<HTMLElement> & {
|
|
53
|
+
backgroundColor?: ColorName;
|
|
54
|
+
fullscreen?: boolean;
|
|
55
|
+
onUp?: PebbleButtonHandler;
|
|
56
|
+
onDown?: PebbleButtonHandler;
|
|
57
|
+
onSelect?: PebbleButtonHandler;
|
|
58
|
+
onBack?: PebbleButtonHandler;
|
|
59
|
+
onLongUp?: PebbleButtonHandler;
|
|
60
|
+
onLongDown?: PebbleButtonHandler;
|
|
61
|
+
onLongSelect?: PebbleButtonHandler;
|
|
62
|
+
}>;
|
|
63
|
+
export interface RectProps extends PositionProps, SizeProps {
|
|
64
|
+
fill?: ColorName;
|
|
65
|
+
stroke?: ColorName;
|
|
66
|
+
strokeWidth?: number;
|
|
67
|
+
children?: ReactNode;
|
|
68
|
+
}
|
|
69
|
+
export declare function Rect({ children, ...props }: RectProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
70
|
+
export interface CircleProps extends PositionProps {
|
|
71
|
+
r?: number;
|
|
72
|
+
/** Alias for `r`. */
|
|
73
|
+
radius?: number;
|
|
74
|
+
fill?: ColorName;
|
|
75
|
+
stroke?: ColorName;
|
|
76
|
+
strokeWidth?: number;
|
|
77
|
+
}
|
|
78
|
+
export declare function Circle(props: CircleProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
79
|
+
export interface TextProps extends PositionProps, SizeProps {
|
|
80
|
+
font?: FontName;
|
|
81
|
+
color?: ColorName;
|
|
82
|
+
align?: Alignment;
|
|
83
|
+
children?: ReactNode;
|
|
84
|
+
}
|
|
85
|
+
export declare function Text({ children, ...props }: TextProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
86
|
+
export interface LineProps extends PositionProps {
|
|
87
|
+
x2?: number;
|
|
88
|
+
y2?: number;
|
|
89
|
+
color?: ColorName;
|
|
90
|
+
strokeWidth?: number;
|
|
91
|
+
}
|
|
92
|
+
export declare function Line(props: LineProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
93
|
+
export interface ImageProps extends PositionProps {
|
|
94
|
+
bitmap: unknown;
|
|
95
|
+
}
|
|
96
|
+
export declare function Image(props: ImageProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
97
|
+
export interface GroupProps extends PositionProps {
|
|
98
|
+
children?: ReactNode;
|
|
99
|
+
}
|
|
100
|
+
export declare function Group({ children, ...props }: GroupProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
101
|
+
export interface StatusBarProps {
|
|
102
|
+
color?: ColorName;
|
|
103
|
+
backgroundColor?: ColorName;
|
|
104
|
+
separator?: 'dotted' | 'line' | 'none';
|
|
105
|
+
}
|
|
106
|
+
export declare function StatusBar(props: StatusBarProps): import('preact').VNode<import('preact').Attributes & StatusBarProps>;
|
|
107
|
+
export interface ActionBarProps {
|
|
108
|
+
upIcon?: unknown;
|
|
109
|
+
selectIcon?: unknown;
|
|
110
|
+
downIcon?: unknown;
|
|
111
|
+
backgroundColor?: ColorName;
|
|
112
|
+
}
|
|
113
|
+
export declare function ActionBar(props: ActionBarProps): import('preact').VNode<import('preact').Attributes & ActionBarProps>;
|
|
114
|
+
export interface CardProps extends PositionProps {
|
|
115
|
+
title: ReactNode;
|
|
116
|
+
body?: ReactNode;
|
|
117
|
+
titleFont?: FontName;
|
|
118
|
+
bodyFont?: FontName;
|
|
119
|
+
w?: number;
|
|
120
|
+
}
|
|
121
|
+
export declare function Card({ title, body, titleFont, bodyFont, x, y, w, ...props }: CardProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
122
|
+
export interface BadgeProps extends PositionProps {
|
|
123
|
+
r?: number;
|
|
124
|
+
color?: ColorName;
|
|
125
|
+
textColor?: ColorName;
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
}
|
|
128
|
+
export declare function Badge({ x, y, r, color, textColor, children, }: BadgeProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
|
|
129
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { PebbleApp } from '../pebble-render.js';
|
|
2
|
+
/**
|
|
3
|
+
* hooks/index.ts — React hooks for react-pebble.
|
|
4
|
+
*
|
|
5
|
+
* This set intentionally covers only what the Alloy runtime can support
|
|
6
|
+
* today. Sensor / connectivity hooks (battery, BT connection, accelerometer,
|
|
7
|
+
* phone↔watch messaging) used to live here based on a fictional `Pebble`
|
|
8
|
+
* global; they've been removed until we've identified the real Moddable
|
|
9
|
+
* module shape for each one.
|
|
10
|
+
*
|
|
11
|
+
* See `pebble-render.ts` for the runtime wiring that lets `useTime` and
|
|
12
|
+
* `useButton` actually fire on-device — hooks publish to registries that the
|
|
13
|
+
* renderer connects to Moddable's `watch` event source.
|
|
14
|
+
*/
|
|
15
|
+
type UseStateFn = <T>(init: T | (() => T)) => [T, (v: T | ((p: T) => T)) => void];
|
|
16
|
+
export declare function useState<T>(init: T | (() => T)): [T, (v: T | ((p: T) => T)) => void];
|
|
17
|
+
/** @internal — called by the compiler to intercept useState calls. */
|
|
18
|
+
export declare function _setUseStateImpl(impl: UseStateFn): void;
|
|
19
|
+
/** @internal — restore original useState after compilation. */
|
|
20
|
+
export declare function _restoreUseState(): void;
|
|
21
|
+
export type PebbleButton = 'up' | 'down' | 'select' | 'back';
|
|
22
|
+
export type PebbleButtonHandler = () => void;
|
|
23
|
+
export declare const PebbleAppContext: import('preact').Context<PebbleApp | null>;
|
|
24
|
+
export declare function useApp(): PebbleApp;
|
|
25
|
+
type ButtonRegistryKey = PebbleButton | `long_${PebbleButton}`;
|
|
26
|
+
interface ButtonRegistryShape {
|
|
27
|
+
_listeners: Map<ButtonRegistryKey, Set<PebbleButtonHandler>>;
|
|
28
|
+
subscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;
|
|
29
|
+
unsubscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;
|
|
30
|
+
emit(button: ButtonRegistryKey): void;
|
|
31
|
+
}
|
|
32
|
+
export declare const ButtonRegistry: ButtonRegistryShape;
|
|
33
|
+
export declare function useButton(button: PebbleButton, handler: PebbleButtonHandler): void;
|
|
34
|
+
export declare function useLongButton(button: PebbleButton, handler: PebbleButtonHandler): void;
|
|
35
|
+
export declare function useTime(intervalMs?: number): Date;
|
|
36
|
+
export declare function useFormattedTime(format?: string): string;
|
|
37
|
+
export declare function useInterval(callback: () => void, delay: number | null): void;
|
|
38
|
+
export interface ListNavigationOptions {
|
|
39
|
+
wrap?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ListNavigationResult<T> {
|
|
42
|
+
index: number;
|
|
43
|
+
item: T | undefined;
|
|
44
|
+
next: () => void;
|
|
45
|
+
prev: () => void;
|
|
46
|
+
setIndex: (index: number) => void;
|
|
47
|
+
}
|
|
48
|
+
export declare function useListNavigation<T>(items: readonly T[], options?: ListNavigationOptions): ListNavigationResult<T>;
|
|
49
|
+
export interface UseMessageOptions<T> {
|
|
50
|
+
/** Message key name (must match PebbleKit JS sendAppMessage key) */
|
|
51
|
+
key: string;
|
|
52
|
+
/** Mock data returned at compile time so the compiler can render the loaded state */
|
|
53
|
+
mockData: T;
|
|
54
|
+
/** Delay in ms before mock data appears (for SETTLE_MS) */
|
|
55
|
+
mockDelay?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface UseMessageResult<T> {
|
|
58
|
+
data: T | null;
|
|
59
|
+
loading: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Load data from the phone at runtime via Pebble's Message API.
|
|
63
|
+
*
|
|
64
|
+
* At compile time (Node mock mode): returns mockData after mockDelay ms.
|
|
65
|
+
* At runtime (Alloy): the compiler emits a Message subscription that
|
|
66
|
+
* populates data when the phone sends it.
|
|
67
|
+
*
|
|
68
|
+
* Usage:
|
|
69
|
+
* const { data, loading } = useMessage({
|
|
70
|
+
* key: 'items',
|
|
71
|
+
* mockData: [{ title: 'Fix bug', status: 'Open' }],
|
|
72
|
+
* });
|
|
73
|
+
*/
|
|
74
|
+
export declare function useMessage<T>(options: UseMessageOptions<T>): UseMessageResult<T>;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-pebble — A React renderer for Pebble Alloy (Moddable XS / Poco).
|
|
3
|
+
*
|
|
4
|
+
* Usage on Alloy:
|
|
5
|
+
* import Poco from 'commodetto/Poco';
|
|
6
|
+
* import React from 'react';
|
|
7
|
+
* import { render, Group, Rect, Text } from 'react-pebble';
|
|
8
|
+
* import { useTime } from 'react-pebble/hooks';
|
|
9
|
+
*
|
|
10
|
+
* function WatchFace() {
|
|
11
|
+
* const time = useTime();
|
|
12
|
+
* return (
|
|
13
|
+
* <Group>
|
|
14
|
+
* <Rect x={0} y={0} w={200} h={228} fill="black" />
|
|
15
|
+
* <Text x={0} y={90} w={200} font="bitham42Bold" color="white" align="center">
|
|
16
|
+
* {time.getHours()}:{time.getMinutes().toString().padStart(2, '0')}
|
|
17
|
+
* </Text>
|
|
18
|
+
* </Group>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* render(<WatchFace />, { poco: Poco });
|
|
23
|
+
*/
|
|
24
|
+
export { SCREEN, PLATFORMS, _setPlatform } from './platform.js';
|
|
25
|
+
export type { PebblePlatform } from './platform.js';
|
|
26
|
+
export { render } from './pebble-render.js';
|
|
27
|
+
export type { PebbleApp, PebblePlatformInfo, RenderOptions, RenderOptionsExt, DrawCall, } from './pebble-render.js';
|
|
28
|
+
export { Window, Rect, Circle, Text, Line, Image, Group, StatusBar, ActionBar, Card, Badge, } from './components/index.js';
|
|
29
|
+
export type { WindowProps, RectProps, CircleProps, TextProps, LineProps, ImageProps, GroupProps, StatusBarProps, ActionBarProps, CardProps, BadgeProps, PositionProps, SizeProps, ButtonHandlerProps, ColorName, FontName, Alignment, } from './components/index.js';
|
|
30
|
+
export { useApp, useButton, useLongButton, useTime, useFormattedTime, useInterval, useListNavigation, ButtonRegistry, PebbleAppContext, } from './hooks/index.js';
|
|
31
|
+
export type { PebbleButton, PebbleButtonHandler, ListNavigationOptions, ListNavigationResult, } from './hooks/index.js';
|
|
32
|
+
export { default as reconciler } from './pebble-reconciler.js';
|
|
33
|
+
export { ELEMENT_TYPES, appendChildNode, createNode, createTextNode, findRoot, getTextContent, insertBeforeNode, removeChildNode, setAttribute, setTextNodeValue, walkTree, } from './pebble-dom.js';
|
|
34
|
+
export type { AnyNode, DOMElement, ElementType, NodeProps, TextNode, Visitor, } from './pebble-dom.js';
|
|
35
|
+
export { PocoRenderer, COLOR_PALETTE, FONT_PALETTE, resolveColorName, resolveFontName, } from './pebble-output.js';
|
|
36
|
+
export type { RGB, FontSpec, RenderOptions as PocoRenderOptions, } from './pebble-output.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AnyNode, DOMElement, TextNode } from './pebble-dom.js';
|
|
2
|
+
interface ShimNodeBase {
|
|
3
|
+
readonly nodeType: number;
|
|
4
|
+
nodeName: string;
|
|
5
|
+
parentNode: ShimElement | null;
|
|
6
|
+
childNodes: ShimNode[];
|
|
7
|
+
firstChild: ShimNode | null;
|
|
8
|
+
nextSibling: ShimNode | null;
|
|
9
|
+
/** The backing pebble-dom node. */
|
|
10
|
+
readonly _pbl: AnyNode;
|
|
11
|
+
}
|
|
12
|
+
export interface ShimElement extends ShimNodeBase {
|
|
13
|
+
readonly nodeType: 1;
|
|
14
|
+
readonly localName: string;
|
|
15
|
+
readonly tagName: string;
|
|
16
|
+
readonly _pbl: DOMElement;
|
|
17
|
+
attributes: Record<string, unknown>;
|
|
18
|
+
appendChild<T extends ShimNode>(child: T): T;
|
|
19
|
+
insertBefore<T extends ShimNode>(child: T, ref: ShimNode | null): T;
|
|
20
|
+
removeChild<T extends ShimNode>(child: T): T;
|
|
21
|
+
remove(): void;
|
|
22
|
+
setAttribute(name: string, value: unknown): void;
|
|
23
|
+
removeAttribute(name: string): void;
|
|
24
|
+
getAttribute(name: string): unknown;
|
|
25
|
+
addEventListener(name: string, handler: (...args: unknown[]) => unknown): void;
|
|
26
|
+
removeEventListener(name: string, handler: (...args: unknown[]) => unknown): void;
|
|
27
|
+
}
|
|
28
|
+
export interface ShimText extends ShimNodeBase {
|
|
29
|
+
readonly nodeType: 3;
|
|
30
|
+
readonly _pbl: TextNode;
|
|
31
|
+
data: string;
|
|
32
|
+
nodeValue: string;
|
|
33
|
+
textContent: string;
|
|
34
|
+
}
|
|
35
|
+
export type ShimNode = ShimElement | ShimText;
|
|
36
|
+
export declare function createShimElement(tag: string): ShimElement;
|
|
37
|
+
export declare function createShimText(data: string): ShimText;
|
|
38
|
+
export interface ShimDocument {
|
|
39
|
+
createElement(tag: string): ShimElement;
|
|
40
|
+
createElementNS(ns: string | null, tag: string): ShimElement;
|
|
41
|
+
createTextNode(data: string): ShimText;
|
|
42
|
+
}
|
|
43
|
+
export declare const shimDocument: ShimDocument;
|
|
44
|
+
export declare function createShimRoot(): ShimElement;
|
|
45
|
+
export {};
|