react-pebble 0.1.0 → 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 +21 -18
- 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 +346 -35
- package/scripts/deploy.sh +0 -0
- package/scripts/test-emulator.sh +371 -0
- package/src/compiler/index.ts +11 -3
- 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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eddie Moore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/lib/compiler.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:child_process`),t=require(`node:path`),n=require(`node:url`),r=require(`node:fs`);var i=(0,t.dirname)((0,n.fileURLToPath)({}.url));async function a(n){let a=n.logger??(()=>{}),o=n.projectRoot??process.cwd(),s=(0,t.
|
|
2
|
-
`).length} lines, buttons=${
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:child_process`),t=require(`node:path`),n=require(`node:url`),r=require(`node:fs`);var i=(0,t.dirname)((0,n.fileURLToPath)({}.url));async function a(n){let a=n.logger??(()=>{}),o=n.projectRoot??process.cwd(),s=(0,t.resolve)(o,n.entry),c=(0,t.basename)(s).replace(/\.[jt]sx?$/,``),l=(0,t.resolve)(i,`../../scripts/compile-to-piu.ts`);if(!(0,r.existsSync)(l))throw Error(`Compiler script not found at ${l}`);a(`Compiling ${c}...`);let u={...process.env,EXAMPLE:s};n.settleMs&&(u.SETTLE_MS=String(n.settleMs)),n.platform&&(u.PEBBLE_PLATFORM=n.platform);let d,f;try{d=(0,e.execSync)(`npx tsx "${l}"`,{cwd:o,env:u,encoding:`utf-8`,timeout:3e4,stdio:[`pipe`,`pipe`,`pipe`]});try{f=(0,e.execSync)(`npx tsx "${l}" 2>&1 1>/dev/null`,{cwd:o,env:u,encoding:`utf-8`,timeout:3e4})}catch{f=``}}catch(e){let t=e;throw Error(`Compilation failed for ${c}: ${t.stderr??t.message}`)}let p=f.includes(`Button bindings discovered:`)&&!f.includes(`Button bindings discovered: 0`),m=[],h=f.match(/useMessage detected: key="([^"]+)"/);h?.[1]&&m.push(h[1]);let g=null,_=f.match(/mockDataValue=([\s\S]*?)(?:\n[A-Z]|\n$)/);return _?.[1]&&(g=_[1].trim()),a(`Compiled ${c}: ${d.split(`
|
|
2
|
+
`).length} lines, buttons=${p}, messageKeys=[${m.join(`,`)}]`),{code:d,hasButtons:p,messageKeys:m,mockDataSource:g,diagnostics:f}}exports.compileToPiu=a;
|
|
3
3
|
//# sourceMappingURL=compiler.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/compiler/index.ts"],"sourcesContent":["/**\n * src/compiler/index.ts — react-pebble compile-to-piu library API.\n *\n * Wraps the compile-to-piu.ts script as a programmatic API. The script\n * runs as a subprocess (it uses module-level state that requires process\n * isolation). A future refactoring will inline the logic as a pure function.\n *\n * Usage:\n * import { compileToPiu } from 'react-pebble/compiler';\n * const result = await compileToPiu({ entry: 'examples/watchface.tsx' });\n * console.log(result.code); // piu Application.template JS\n */\n\nimport { execSync } from 'node:child_process';\nimport { resolve, dirname, basename } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface CompileOptions {\n /** Path to the entry .tsx file (relative to cwd or absolute) */\n entry: string;\n /** Milliseconds to wait for async effects (useEffect/setTimeout) */\n settleMs?: number;\n /** Target platform (default: 'emery') */\n platform?: string;\n /** Logger for diagnostic messages */\n logger?: (msg: string) => void;\n /** Project root directory (default: process.cwd()) */\n projectRoot?: string;\n}\n\nexport interface CompileResult {\n /** The compiled piu JavaScript code */\n code: string;\n /** Whether the component uses useButton (needs watchapp mode) */\n hasButtons: boolean;\n /** Message keys used by useMessage hooks */\n messageKeys: string[];\n /** Diagnostic messages from the compiler */\n diagnostics: string;\n}\n\n/**\n * Compile a Preact component to piu Application.template code.\n *\n * Internally runs scripts/compile-to-piu.ts as a subprocess.\n */\nexport async function compileToPiu(options: CompileOptions): Promise<CompileResult> {\n const log = options.logger ?? (() => {});\n const projectRoot = options.projectRoot ?? process.cwd();\n\n // Resolve the entry
|
|
1
|
+
{"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/compiler/index.ts"],"sourcesContent":["/**\n * src/compiler/index.ts — react-pebble compile-to-piu library API.\n *\n * Wraps the compile-to-piu.ts script as a programmatic API. The script\n * runs as a subprocess (it uses module-level state that requires process\n * isolation). A future refactoring will inline the logic as a pure function.\n *\n * Usage:\n * import { compileToPiu } from 'react-pebble/compiler';\n * const result = await compileToPiu({ entry: 'examples/watchface.tsx' });\n * console.log(result.code); // piu Application.template JS\n */\n\nimport { execSync } from 'node:child_process';\nimport { resolve, dirname, basename } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface CompileOptions {\n /** Path to the entry .tsx file (relative to cwd or absolute) */\n entry: string;\n /** Milliseconds to wait for async effects (useEffect/setTimeout) */\n settleMs?: number;\n /** Target platform (default: 'emery') */\n platform?: string;\n /** Logger for diagnostic messages */\n logger?: (msg: string) => void;\n /** Project root directory (default: process.cwd()) */\n projectRoot?: string;\n}\n\nexport interface CompileResult {\n /** The compiled piu JavaScript code */\n code: string;\n /** Whether the component uses useButton (needs watchapp mode) */\n hasButtons: boolean;\n /** Message keys used by useMessage hooks */\n messageKeys: string[];\n /** Mock data source from useMessage (for generating phone-side JS) */\n mockDataSource: string | null;\n /** Diagnostic messages from the compiler */\n diagnostics: string;\n}\n\n/**\n * Compile a Preact component to piu Application.template code.\n *\n * Internally runs scripts/compile-to-piu.ts as a subprocess.\n */\nexport async function compileToPiu(options: CompileOptions): Promise<CompileResult> {\n const log = options.logger ?? (() => {});\n const projectRoot = options.projectRoot ?? process.cwd();\n\n // Resolve the entry path — pass the full path so the script can find it\n // whether it's an internal example or an external project file\n const entryPath = resolve(projectRoot, options.entry);\n const exampleName = basename(entryPath).replace(/\\.[jt]sx?$/, '');\n\n // Find the compiler script\n const scriptPath = resolve(__dirname, '../../scripts/compile-to-piu.ts');\n if (!existsSync(scriptPath)) {\n throw new Error(`Compiler script not found at ${scriptPath}`);\n }\n\n log(`Compiling ${exampleName}...`);\n\n const env: Record<string, string> = {\n ...process.env as Record<string, string>,\n EXAMPLE: entryPath,\n };\n if (options.settleMs) {\n env.SETTLE_MS = String(options.settleMs);\n }\n if (options.platform) {\n env.PEBBLE_PLATFORM = options.platform;\n }\n\n // Run the compiler script and capture stdout (code) + stderr (diagnostics)\n let code: string;\n let diagnostics: string;\n try {\n code = execSync(`npx tsx \"${scriptPath}\"`, {\n cwd: projectRoot,\n env,\n encoding: 'utf-8',\n timeout: 30000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n // Re-run to capture stderr separately (execSync doesn't give both easily)\n try {\n diagnostics = execSync(`npx tsx \"${scriptPath}\" 2>&1 1>/dev/null`, {\n cwd: projectRoot,\n env,\n encoding: 'utf-8',\n timeout: 30000,\n });\n } catch {\n diagnostics = '';\n }\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new Error(`Compilation failed for ${exampleName}: ${e.stderr ?? e.message}`);\n }\n\n // Parse diagnostics to extract metadata\n const hasButtons = diagnostics.includes('Button bindings discovered:') &&\n !diagnostics.includes('Button bindings discovered: 0');\n const messageKeys: string[] = [];\n const msgMatch = diagnostics.match(/useMessage detected: key=\"([^\"]+)\"/);\n if (msgMatch?.[1]) messageKeys.push(msgMatch[1]);\n\n // Extract mock data source for phone-side JS generation\n let mockDataSource: string | null = null;\n const mockMatch = diagnostics.match(/mockDataValue=([\\s\\S]*?)(?:\\n[A-Z]|\\n$)/);\n if (mockMatch?.[1]) mockDataSource = mockMatch[1].trim();\n\n log(`Compiled ${exampleName}: ${code.split('\\n').length} lines, buttons=${hasButtons}, messageKeys=[${messageKeys.join(',')}]`);\n\n return { code, hasButtons, messageKeys, mockDataSource, diagnostics };\n}\n"],"mappings":"yKAkBA,IAAM,GAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,CAA8C,IAAI,CAAC,CAiCzD,eAAsB,EAAa,EAAiD,CAClF,IAAM,EAAM,EAAQ,aAAiB,IAC/B,EAAc,EAAQ,aAAe,QAAQ,KAAK,CAIlD,GAAA,EAAA,EAAA,SAAoB,EAAa,EAAQ,MAAM,CAC/C,GAAA,EAAA,EAAA,UAAuB,EAAU,CAAC,QAAQ,aAAc,GAAG,CAG3D,GAAA,EAAA,EAAA,SAAqB,EAAW,kCAAkC,CACxE,GAAI,EAAA,EAAA,EAAA,YAAY,EAAW,CACzB,MAAU,MAAM,gCAAgC,IAAa,CAG/D,EAAI,aAAa,EAAY,KAAK,CAElC,IAAM,EAA8B,CAClC,GAAG,QAAQ,IACX,QAAS,EACV,CACG,EAAQ,WACV,EAAI,UAAY,OAAO,EAAQ,SAAS,EAEtC,EAAQ,WACV,EAAI,gBAAkB,EAAQ,UAIhC,IAAI,EACA,EACJ,GAAI,CACF,GAAA,EAAA,EAAA,UAAgB,YAAY,EAAW,GAAI,CACzC,IAAK,EACL,MACA,SAAU,QACV,QAAS,IACT,MAAO,CAAC,OAAQ,OAAQ,OAAO,CAChC,CAAC,CAEF,GAAI,CACF,GAAA,EAAA,EAAA,UAAuB,YAAY,EAAW,oBAAqB,CACjE,IAAK,EACL,MACA,SAAU,QACV,QAAS,IACV,CAAC,MACI,CACN,EAAc,UAET,EAAK,CACZ,IAAM,EAAI,EACV,MAAU,MAAM,0BAA0B,EAAY,IAAI,EAAE,QAAU,EAAE,UAAU,CAIpF,IAAM,EAAa,EAAY,SAAS,8BAA8B,EACpE,CAAC,EAAY,SAAS,gCAAgC,CAClD,EAAwB,EAAE,CAC1B,EAAW,EAAY,MAAM,qCAAqC,CACpE,IAAW,IAAI,EAAY,KAAK,EAAS,GAAG,CAGhD,IAAI,EAAgC,KAC9B,EAAY,EAAY,MAAM,0CAA0C,CAK9E,OAJI,IAAY,KAAI,EAAiB,EAAU,GAAG,MAAM,EAExD,EAAI,YAAY,EAAY,IAAI,EAAK,MAAM;EAAK,CAAC,OAAO,kBAAkB,EAAW,iBAAiB,EAAY,KAAK,IAAI,CAAC,GAAG,CAExH,CAAE,OAAM,aAAY,cAAa,iBAAgB,cAAa"}
|
package/dist/lib/compiler.js
CHANGED
|
@@ -5,19 +5,19 @@ import { existsSync as a } from "node:fs";
|
|
|
5
5
|
//#region src/compiler/index.ts
|
|
6
6
|
var o = n(i(import.meta.url));
|
|
7
7
|
async function s(n) {
|
|
8
|
-
let i = n.logger ?? (() => {}), s = n.projectRoot ?? process.cwd(), c =
|
|
9
|
-
if (!a(
|
|
10
|
-
i(`Compiling ${
|
|
11
|
-
let
|
|
8
|
+
let i = n.logger ?? (() => {}), s = n.projectRoot ?? process.cwd(), c = r(s, n.entry), l = t(c).replace(/\.[jt]sx?$/, ""), u = r(o, "../../scripts/compile-to-piu.ts");
|
|
9
|
+
if (!a(u)) throw Error(`Compiler script not found at ${u}`);
|
|
10
|
+
i(`Compiling ${l}...`);
|
|
11
|
+
let d = {
|
|
12
12
|
...process.env,
|
|
13
13
|
EXAMPLE: c
|
|
14
14
|
};
|
|
15
|
-
n.settleMs && (
|
|
16
|
-
let
|
|
15
|
+
n.settleMs && (d.SETTLE_MS = String(n.settleMs)), n.platform && (d.PEBBLE_PLATFORM = n.platform);
|
|
16
|
+
let f, p;
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
f = e(`npx tsx "${u}"`, {
|
|
19
19
|
cwd: s,
|
|
20
|
-
env:
|
|
20
|
+
env: d,
|
|
21
21
|
encoding: "utf-8",
|
|
22
22
|
timeout: 3e4,
|
|
23
23
|
stdio: [
|
|
@@ -27,25 +27,28 @@ async function s(n) {
|
|
|
27
27
|
]
|
|
28
28
|
});
|
|
29
29
|
try {
|
|
30
|
-
|
|
30
|
+
p = e(`npx tsx "${u}" 2>&1 1>/dev/null`, {
|
|
31
31
|
cwd: s,
|
|
32
|
-
env:
|
|
32
|
+
env: d,
|
|
33
33
|
encoding: "utf-8",
|
|
34
34
|
timeout: 3e4
|
|
35
35
|
});
|
|
36
36
|
} catch {
|
|
37
|
-
|
|
37
|
+
p = "";
|
|
38
38
|
}
|
|
39
39
|
} catch (e) {
|
|
40
40
|
let t = e;
|
|
41
|
-
throw Error(`Compilation failed for ${
|
|
41
|
+
throw Error(`Compilation failed for ${l}: ${t.stderr ?? t.message}`);
|
|
42
42
|
}
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
let m = p.includes("Button bindings discovered:") && !p.includes("Button bindings discovered: 0"), h = [], g = p.match(/useMessage detected: key="([^"]+)"/);
|
|
44
|
+
g?.[1] && h.push(g[1]);
|
|
45
|
+
let _ = null, v = p.match(/mockDataValue=([\s\S]*?)(?:\n[A-Z]|\n$)/);
|
|
46
|
+
return v?.[1] && (_ = v[1].trim()), i(`Compiled ${l}: ${f.split("\n").length} lines, buttons=${m}, messageKeys=[${h.join(",")}]`), {
|
|
47
|
+
code: f,
|
|
48
|
+
hasButtons: m,
|
|
49
|
+
messageKeys: h,
|
|
50
|
+
mockDataSource: _,
|
|
51
|
+
diagnostics: p
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
//#endregion
|
package/dist/lib/compiler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.js","names":[],"sources":["../../src/compiler/index.ts"],"sourcesContent":["/**\n * src/compiler/index.ts — react-pebble compile-to-piu library API.\n *\n * Wraps the compile-to-piu.ts script as a programmatic API. The script\n * runs as a subprocess (it uses module-level state that requires process\n * isolation). A future refactoring will inline the logic as a pure function.\n *\n * Usage:\n * import { compileToPiu } from 'react-pebble/compiler';\n * const result = await compileToPiu({ entry: 'examples/watchface.tsx' });\n * console.log(result.code); // piu Application.template JS\n */\n\nimport { execSync } from 'node:child_process';\nimport { resolve, dirname, basename } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface CompileOptions {\n /** Path to the entry .tsx file (relative to cwd or absolute) */\n entry: string;\n /** Milliseconds to wait for async effects (useEffect/setTimeout) */\n settleMs?: number;\n /** Target platform (default: 'emery') */\n platform?: string;\n /** Logger for diagnostic messages */\n logger?: (msg: string) => void;\n /** Project root directory (default: process.cwd()) */\n projectRoot?: string;\n}\n\nexport interface CompileResult {\n /** The compiled piu JavaScript code */\n code: string;\n /** Whether the component uses useButton (needs watchapp mode) */\n hasButtons: boolean;\n /** Message keys used by useMessage hooks */\n messageKeys: string[];\n /** Diagnostic messages from the compiler */\n diagnostics: string;\n}\n\n/**\n * Compile a Preact component to piu Application.template code.\n *\n * Internally runs scripts/compile-to-piu.ts as a subprocess.\n */\nexport async function compileToPiu(options: CompileOptions): Promise<CompileResult> {\n const log = options.logger ?? (() => {});\n const projectRoot = options.projectRoot ?? process.cwd();\n\n // Resolve the entry
|
|
1
|
+
{"version":3,"file":"compiler.js","names":[],"sources":["../../src/compiler/index.ts"],"sourcesContent":["/**\n * src/compiler/index.ts — react-pebble compile-to-piu library API.\n *\n * Wraps the compile-to-piu.ts script as a programmatic API. The script\n * runs as a subprocess (it uses module-level state that requires process\n * isolation). A future refactoring will inline the logic as a pure function.\n *\n * Usage:\n * import { compileToPiu } from 'react-pebble/compiler';\n * const result = await compileToPiu({ entry: 'examples/watchface.tsx' });\n * console.log(result.code); // piu Application.template JS\n */\n\nimport { execSync } from 'node:child_process';\nimport { resolve, dirname, basename } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface CompileOptions {\n /** Path to the entry .tsx file (relative to cwd or absolute) */\n entry: string;\n /** Milliseconds to wait for async effects (useEffect/setTimeout) */\n settleMs?: number;\n /** Target platform (default: 'emery') */\n platform?: string;\n /** Logger for diagnostic messages */\n logger?: (msg: string) => void;\n /** Project root directory (default: process.cwd()) */\n projectRoot?: string;\n}\n\nexport interface CompileResult {\n /** The compiled piu JavaScript code */\n code: string;\n /** Whether the component uses useButton (needs watchapp mode) */\n hasButtons: boolean;\n /** Message keys used by useMessage hooks */\n messageKeys: string[];\n /** Mock data source from useMessage (for generating phone-side JS) */\n mockDataSource: string | null;\n /** Diagnostic messages from the compiler */\n diagnostics: string;\n}\n\n/**\n * Compile a Preact component to piu Application.template code.\n *\n * Internally runs scripts/compile-to-piu.ts as a subprocess.\n */\nexport async function compileToPiu(options: CompileOptions): Promise<CompileResult> {\n const log = options.logger ?? (() => {});\n const projectRoot = options.projectRoot ?? process.cwd();\n\n // Resolve the entry path — pass the full path so the script can find it\n // whether it's an internal example or an external project file\n const entryPath = resolve(projectRoot, options.entry);\n const exampleName = basename(entryPath).replace(/\\.[jt]sx?$/, '');\n\n // Find the compiler script\n const scriptPath = resolve(__dirname, '../../scripts/compile-to-piu.ts');\n if (!existsSync(scriptPath)) {\n throw new Error(`Compiler script not found at ${scriptPath}`);\n }\n\n log(`Compiling ${exampleName}...`);\n\n const env: Record<string, string> = {\n ...process.env as Record<string, string>,\n EXAMPLE: entryPath,\n };\n if (options.settleMs) {\n env.SETTLE_MS = String(options.settleMs);\n }\n if (options.platform) {\n env.PEBBLE_PLATFORM = options.platform;\n }\n\n // Run the compiler script and capture stdout (code) + stderr (diagnostics)\n let code: string;\n let diagnostics: string;\n try {\n code = execSync(`npx tsx \"${scriptPath}\"`, {\n cwd: projectRoot,\n env,\n encoding: 'utf-8',\n timeout: 30000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n // Re-run to capture stderr separately (execSync doesn't give both easily)\n try {\n diagnostics = execSync(`npx tsx \"${scriptPath}\" 2>&1 1>/dev/null`, {\n cwd: projectRoot,\n env,\n encoding: 'utf-8',\n timeout: 30000,\n });\n } catch {\n diagnostics = '';\n }\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new Error(`Compilation failed for ${exampleName}: ${e.stderr ?? e.message}`);\n }\n\n // Parse diagnostics to extract metadata\n const hasButtons = diagnostics.includes('Button bindings discovered:') &&\n !diagnostics.includes('Button bindings discovered: 0');\n const messageKeys: string[] = [];\n const msgMatch = diagnostics.match(/useMessage detected: key=\"([^\"]+)\"/);\n if (msgMatch?.[1]) messageKeys.push(msgMatch[1]);\n\n // Extract mock data source for phone-side JS generation\n let mockDataSource: string | null = null;\n const mockMatch = diagnostics.match(/mockDataValue=([\\s\\S]*?)(?:\\n[A-Z]|\\n$)/);\n if (mockMatch?.[1]) mockDataSource = mockMatch[1].trim();\n\n log(`Compiled ${exampleName}: ${code.split('\\n').length} lines, buttons=${hasButtons}, messageKeys=[${messageKeys.join(',')}]`);\n\n return { code, hasButtons, messageKeys, mockDataSource, diagnostics };\n}\n"],"mappings":";;;;;AAkBA,IAAM,IAAY,EAAQ,EAAc,OAAO,KAAK,IAAI,CAAC;AAiCzD,eAAsB,EAAa,GAAiD;CAClF,IAAM,IAAM,EAAQ,iBAAiB,KAC/B,IAAc,EAAQ,eAAe,QAAQ,KAAK,EAIlD,IAAY,EAAQ,GAAa,EAAQ,MAAM,EAC/C,IAAc,EAAS,EAAU,CAAC,QAAQ,cAAc,GAAG,EAG3D,IAAa,EAAQ,GAAW,kCAAkC;AACxE,KAAI,CAAC,EAAW,EAAW,CACzB,OAAU,MAAM,gCAAgC,IAAa;AAG/D,GAAI,aAAa,EAAY,KAAK;CAElC,IAAM,IAA8B;EAClC,GAAG,QAAQ;EACX,SAAS;EACV;AAID,CAHI,EAAQ,aACV,EAAI,YAAY,OAAO,EAAQ,SAAS,GAEtC,EAAQ,aACV,EAAI,kBAAkB,EAAQ;CAIhC,IAAI,GACA;AACJ,KAAI;AACF,MAAO,EAAS,YAAY,EAAW,IAAI;GACzC,KAAK;GACL;GACA,UAAU;GACV,SAAS;GACT,OAAO;IAAC;IAAQ;IAAQ;IAAO;GAChC,CAAC;AAEF,MAAI;AACF,OAAc,EAAS,YAAY,EAAW,qBAAqB;IACjE,KAAK;IACL;IACA,UAAU;IACV,SAAS;IACV,CAAC;UACI;AACN,OAAc;;UAET,GAAK;EACZ,IAAM,IAAI;AACV,QAAU,MAAM,0BAA0B,EAAY,IAAI,EAAE,UAAU,EAAE,UAAU;;CAIpF,IAAM,IAAa,EAAY,SAAS,8BAA8B,IACpE,CAAC,EAAY,SAAS,gCAAgC,EAClD,IAAwB,EAAE,EAC1B,IAAW,EAAY,MAAM,qCAAqC;AACxE,CAAI,IAAW,MAAI,EAAY,KAAK,EAAS,GAAG;CAGhD,IAAI,IAAgC,MAC9B,IAAY,EAAY,MAAM,0CAA0C;AAK9E,QAJI,IAAY,OAAI,IAAiB,EAAU,GAAG,MAAM,GAExD,EAAI,YAAY,EAAY,IAAI,EAAK,MAAM,KAAK,CAAC,OAAO,kBAAkB,EAAW,iBAAiB,EAAY,KAAK,IAAI,CAAC,GAAG,EAExH;EAAE;EAAM;EAAY;EAAa;EAAgB;EAAa"}
|
package/dist/lib/components.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={createElement:require(`preact`).h};function t({children:t,...n}){return e.createElement(`pbl-group`,n,t)}function n({children:t,...n}){return e.createElement(`pbl-rect`,n,t)}function r(t){return e.createElement(`pbl-circle`,t)}function i({children:t,...n}){return e.createElement(`pbl-text`,n,t)}function a(t){return e.createElement(`pbl-line`,t)}function o(t){return e.createElement(`pbl-image`,t)}function s({children:t,...n}){return e.createElement(`pbl-group`,n,t)}function c(t){return e.createElement(`pbl-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={createElement:require(`preact`).h};function t({children:t,...n}){return e.createElement(`pbl-group`,n,t)}function n({children:t,...n}){return e.createElement(`pbl-rect`,n,t)}function r(t){return e.createElement(`pbl-circle`,t)}function i({children:t,...n}){return e.createElement(`pbl-text`,n,t)}function a(t){return e.createElement(`pbl-line`,t)}function o(t){return e.createElement(`pbl-image`,t)}function s({children:t,...n}){return e.createElement(`pbl-group`,n,t)}function c({x:t=0,y:n=0,gap:r=0,children:i,...a}){let o=0,s=u(i).map((t,n)=>{if(!t||typeof t!=`object`)return t;let i=t;if(!i.type||!i.props)return t;let a=i.props.h??i.props.height??20,s=e.createElement(i.type,{...i.props,y:o,key:n});return o+=a+r,s});return e.createElement(`pbl-group`,{x:t,y:n,...a},...s)}function l({x:t=0,y:n=0,gap:r=0,children:i,...a}){let o=0,s=u(i).map((t,n)=>{if(!t||typeof t!=`object`)return t;let i=t;if(!i.type||!i.props)return t;let a=i.props.w??i.props.width??40,s=e.createElement(i.type,{...i.props,x:o,key:n});return o+=a+r,s});return e.createElement(`pbl-group`,{x:t,y:n,...a},...s)}function u(e){return e==null?[]:Array.isArray(e)?e.filter(Boolean):[e]}function d(t){return e.createElement(`pbl-statusbar`,t)}function f(t){return e.createElement(`pbl-actionbar`,t)}function p({title:t,body:n,titleFont:r,bodyFont:i,x:a=0,y:o=0,w:s=144,...c}){return e.createElement(`pbl-group`,{x:a,y:o,...c},e.createElement(`pbl-rect`,{x:0,y:0,w:s,h:28,fill:`white`}),e.createElement(`pbl-text`,{x:4,y:2,w:s-8,h:28,font:r??`gothic18Bold`,color:`black`},t),n?e.createElement(`pbl-text`,{x:4,y:32,w:s-8,h:120,font:i??`gothic14`,color:`white`},n):null)}function m({x:t=0,y:n=0,r=12,color:i=`red`,textColor:a=`white`,children:o}){return e.createElement(`pbl-group`,{x:t,y:n},e.createElement(`pbl-circle`,{x:0,y:0,r,fill:i}),e.createElement(`pbl-text`,{x:0,y:r-8,w:r*2,h:16,font:`gothic14Bold`,color:a,align:`center`},o))}exports.ActionBar=f,exports.Badge=m,exports.Card=p,exports.Circle=r,exports.Column=c,exports.Group=s,exports.Image=o,exports.Line=a,exports.Rect=n,exports.Row=l,exports.StatusBar=d,exports.Text=i,exports.Window=t;
|
|
2
2
|
//# sourceMappingURL=components.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.cjs","names":[],"sources":["../../src/components/index.tsx"],"sourcesContent":["/**\n * components/index.tsx — React component wrappers for Pebble primitives\n *\n * These provide a friendly JSX API that maps to the underlying\n * pbl-* element types the reconciler handles.\n */\n\nimport { h } from 'preact';\nimport type { ComponentChildren } from 'preact';\nimport type { PebbleButtonHandler } from '../hooks/index.js';\n\n// Use Preact's `h` as a stand-in for `React.createElement` so we can keep\n// the factory-based element construction that was in the react version.\nconst React = { createElement: h } as const;\n\n// Unify ReactNode with Preact's ComponentChildren so we don't have to\n// rename every prop type.\ntype ReactNode = ComponentChildren;\n\n// ---------------------------------------------------------------------------\n// Shared prop types\n// ---------------------------------------------------------------------------\n\nexport interface PositionProps {\n x?: number;\n y?: number;\n}\n\nexport interface SizeProps {\n w?: number;\n h?: number;\n /** Alias for `w`. */\n width?: number;\n /** Alias for `h`. */\n height?: number;\n}\n\nexport interface ButtonHandlerProps {\n onUp?: PebbleButtonHandler;\n onDown?: PebbleButtonHandler;\n onSelect?: PebbleButtonHandler;\n onBack?: PebbleButtonHandler;\n onLongUp?: PebbleButtonHandler;\n onLongDown?: PebbleButtonHandler;\n onLongSelect?: PebbleButtonHandler;\n}\n\nexport type ColorName =\n | 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange'\n | 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray'\n // Pass-through for raw GColor / hex values\n | (string & {});\n\nexport type FontName =\n | 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold'\n | 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold'\n | 'bitham30Black' | 'bitham42Bold' | 'bitham42Light'\n | 'bitham34MediumNumbers' | 'bitham42MediumNumbers'\n | 'robotoCondensed21' | 'roboto21' | 'droid28'\n | 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42'\n | (string & {});\n\nexport type Alignment = 'left' | 'center' | 'right';\n\n// ---------------------------------------------------------------------------\n// JSX intrinsic element declarations for the pbl-* tags\n// ---------------------------------------------------------------------------\n\ndeclare module 'preact' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'pbl-root': IntrinsicPblProps;\n 'pbl-rect': IntrinsicPblProps;\n 'pbl-circle': IntrinsicPblProps;\n 'pbl-text': IntrinsicPblProps;\n 'pbl-line': IntrinsicPblProps;\n 'pbl-image': IntrinsicPblProps;\n 'pbl-group': IntrinsicPblProps;\n 'pbl-statusbar': IntrinsicPblProps;\n 'pbl-actionbar': IntrinsicPblProps;\n }\n }\n}\n\ninterface IntrinsicPblProps {\n children?: ReactNode;\n // Loose escape hatch — wrappers below provide the typed surface.\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Layout primitives\n// ---------------------------------------------------------------------------\n\nexport interface WindowProps extends ButtonHandlerProps {\n backgroundColor?: ColorName;\n fullscreen?: boolean;\n children?: ReactNode;\n}\n\nexport function Window({ children, ...props }: WindowProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface RectProps extends PositionProps, SizeProps {\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n children?: ReactNode;\n}\n\nexport function Rect({ children, ...props }: RectProps) {\n return React.createElement('pbl-rect', props, children);\n}\n\nexport interface CircleProps extends PositionProps {\n r?: number;\n /** Alias for `r`. */\n radius?: number;\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Circle(props: CircleProps) {\n return React.createElement('pbl-circle', props);\n}\n\nexport interface TextProps extends PositionProps, SizeProps {\n font?: FontName;\n color?: ColorName;\n align?: Alignment;\n children?: ReactNode;\n}\n\nexport function Text({ children, ...props }: TextProps) {\n return React.createElement('pbl-text', props, children);\n}\n\nexport interface LineProps extends PositionProps {\n x2?: number;\n y2?: number;\n color?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Line(props: LineProps) {\n return React.createElement('pbl-line', props);\n}\n\nexport interface ImageProps extends PositionProps {\n bitmap: unknown;\n}\n\nexport function Image(props: ImageProps) {\n return React.createElement('pbl-image', props);\n}\n\nexport interface GroupProps extends PositionProps {\n children?: ReactNode;\n}\n\nexport function Group({ children, ...props }: GroupProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface StatusBarProps {\n color?: ColorName;\n backgroundColor?: ColorName;\n separator?: 'dotted' | 'line' | 'none';\n}\n\nexport function StatusBar(props: StatusBarProps) {\n return React.createElement('pbl-statusbar', props);\n}\n\nexport interface ActionBarProps {\n upIcon?: unknown;\n selectIcon?: unknown;\n downIcon?: unknown;\n backgroundColor?: ColorName;\n}\n\nexport function ActionBar(props: ActionBarProps) {\n return React.createElement('pbl-actionbar', props);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience composites\n// ---------------------------------------------------------------------------\n\nexport interface CardProps extends PositionProps {\n title: ReactNode;\n body?: ReactNode;\n titleFont?: FontName;\n bodyFont?: FontName;\n w?: number;\n}\n\nexport function Card({\n title,\n body,\n titleFont,\n bodyFont,\n x = 0,\n y = 0,\n w = 144,\n ...props\n}: CardProps) {\n const titleH = 28;\n const bodyY = titleH + 4;\n\n return React.createElement(\n 'pbl-group',\n { x, y, ...props },\n React.createElement('pbl-rect', { x: 0, y: 0, w, h: titleH, fill: 'white' }),\n React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: 2,\n w: w - 8,\n h: titleH,\n font: titleFont ?? 'gothic18Bold',\n color: 'black',\n },\n title,\n ),\n body\n ? React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: bodyY,\n w: w - 8,\n h: 120,\n font: bodyFont ?? 'gothic14',\n color: 'white',\n },\n body,\n )\n : null,\n );\n}\n\nexport interface BadgeProps extends PositionProps {\n r?: number;\n color?: ColorName;\n textColor?: ColorName;\n children?: ReactNode;\n}\n\nexport function Badge({\n x = 0,\n y = 0,\n r = 12,\n color = 'red',\n textColor = 'white',\n children,\n}: BadgeProps) {\n return React.createElement(\n 'pbl-group',\n { x, y },\n React.createElement('pbl-circle', { x: 0, y: 0, r, fill: color }),\n React.createElement(\n 'pbl-text',\n {\n x: 0,\n y: r - 8,\n w: r * 2,\n h: 16,\n font: 'gothic14Bold',\n color: textColor,\n align: 'center',\n },\n children,\n ),\n );\n}\n"],"mappings":"mEAaA,IAAM,EAAQ,CAAE,gCAAe,EAAG,CAwFlC,SAAgB,EAAO,CAAE,WAAU,GAAG,GAAsB,CAC1D,OAAO,EAAM,cAAc,YAAa,EAAO,EAAS,CAU1D,SAAgB,EAAK,CAAE,WAAU,GAAG,GAAoB,CACtD,OAAO,EAAM,cAAc,WAAY,EAAO,EAAS,CAYzD,SAAgB,EAAO,EAAoB,CACzC,OAAO,EAAM,cAAc,aAAc,EAAM,CAUjD,SAAgB,EAAK,CAAE,WAAU,GAAG,GAAoB,CACtD,OAAO,EAAM,cAAc,WAAY,EAAO,EAAS,CAUzD,SAAgB,EAAK,EAAkB,CACrC,OAAO,EAAM,cAAc,WAAY,EAAM,CAO/C,SAAgB,EAAM,EAAmB,CACvC,OAAO,EAAM,cAAc,YAAa,EAAM,CAOhD,SAAgB,EAAM,CAAE,WAAU,GAAG,GAAqB,CACxD,OAAO,EAAM,cAAc,YAAa,EAAO,EAAS,CAS1D,SAAgB,EAAU,EAAuB,CAC/C,OAAO,EAAM,cAAc,gBAAiB,EAAM,CAUpD,SAAgB,EAAU,EAAuB,CAC/C,OAAO,EAAM,cAAc,gBAAiB,EAAM,CAepD,SAAgB,EAAK,CACnB,QACA,OACA,YACA,WACA,IAAI,EACJ,IAAI,EACJ,IAAI,IACJ,GAAG,GACS,CAIZ,OAAO,EAAM,cACX,YACA,CAAE,IAAG,IAAG,GAAG,EAAO,CAClB,EAAM,cAAc,WAAY,CAAE,EAAG,EAAG,EAAG,EAAG,IAAG,EAAG,GAAQ,KAAM,QAAS,CAAC,CAC5E,EAAM,cACJ,WACA,CACE,EAAG,EACH,EAAG,EACH,EAAG,EAAI,EACP,EAAG,GACH,KAAM,GAAa,eACnB,MAAO,QACR,CACD,EACD,CACD,EACI,EAAM,cACJ,WACA,CACE,EAAG,EACH,EAAG,GACH,EAAG,EAAI,EACP,EAAG,IACH,KAAM,GAAY,WAClB,MAAO,QACR,CACD,EACD,CACD,KACL,CAUH,SAAgB,EAAM,CACpB,IAAI,EACJ,IAAI,EACJ,EAAI,GACJ,QAAQ,MACR,YAAY,QACZ,YACa,CACb,OAAO,EAAM,cACX,YACA,CAAE,IAAG,IAAG,CACR,EAAM,cAAc,aAAc,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,KAAM,EAAO,CAAC,CACjE,EAAM,cACJ,WACA,CACE,EAAG,EACH,EAAG,EAAI,EACP,EAAG,EAAI,EACP,EAAG,GACH,KAAM,eACN,MAAO,EACP,MAAO,SACR,CACD,EACD,CACF"}
|
|
1
|
+
{"version":3,"file":"components.cjs","names":[],"sources":["../../src/components/index.tsx"],"sourcesContent":["/**\n * components/index.tsx — React component wrappers for Pebble primitives\n *\n * These provide a friendly JSX API that maps to the underlying\n * pbl-* element types the reconciler handles.\n */\n\nimport { h } from 'preact';\nimport type { ComponentChildren } from 'preact';\nimport type { PebbleButtonHandler } from '../hooks/index.js';\n\n// Use Preact's `h` as a stand-in for `React.createElement` so we can keep\n// the factory-based element construction that was in the react version.\nconst React = { createElement: h } as const;\n\n// Unify ReactNode with Preact's ComponentChildren so we don't have to\n// rename every prop type.\ntype ReactNode = ComponentChildren;\n\n// ---------------------------------------------------------------------------\n// Shared prop types\n// ---------------------------------------------------------------------------\n\nexport interface PositionProps {\n x?: number;\n y?: number;\n}\n\nexport interface SizeProps {\n w?: number;\n h?: number;\n /** Alias for `w`. */\n width?: number;\n /** Alias for `h`. */\n height?: number;\n}\n\nexport interface ButtonHandlerProps {\n onUp?: PebbleButtonHandler;\n onDown?: PebbleButtonHandler;\n onSelect?: PebbleButtonHandler;\n onBack?: PebbleButtonHandler;\n onLongUp?: PebbleButtonHandler;\n onLongDown?: PebbleButtonHandler;\n onLongSelect?: PebbleButtonHandler;\n}\n\nexport type ColorName =\n | 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange'\n | 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray'\n // Pass-through for raw GColor / hex values\n | (string & {});\n\nexport type FontName =\n | 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold'\n | 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold'\n | 'bitham30Black' | 'bitham42Bold' | 'bitham42Light'\n | 'bitham34MediumNumbers' | 'bitham42MediumNumbers'\n | 'robotoCondensed21' | 'roboto21' | 'droid28'\n | 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42'\n | (string & {});\n\nexport type Alignment = 'left' | 'center' | 'right';\n\n// ---------------------------------------------------------------------------\n// JSX intrinsic element declarations for the pbl-* tags\n// ---------------------------------------------------------------------------\n\ndeclare module 'preact' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'pbl-root': IntrinsicPblProps;\n 'pbl-rect': IntrinsicPblProps;\n 'pbl-circle': IntrinsicPblProps;\n 'pbl-text': IntrinsicPblProps;\n 'pbl-line': IntrinsicPblProps;\n 'pbl-image': IntrinsicPblProps;\n 'pbl-group': IntrinsicPblProps;\n 'pbl-statusbar': IntrinsicPblProps;\n 'pbl-actionbar': IntrinsicPblProps;\n }\n }\n}\n\ninterface IntrinsicPblProps {\n children?: ReactNode;\n // Loose escape hatch — wrappers below provide the typed surface.\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Layout primitives\n// ---------------------------------------------------------------------------\n\nexport interface WindowProps extends ButtonHandlerProps {\n backgroundColor?: ColorName;\n fullscreen?: boolean;\n children?: ReactNode;\n}\n\nexport function Window({ children, ...props }: WindowProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface RectProps extends PositionProps, SizeProps {\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n borderRadius?: number;\n children?: ReactNode;\n}\n\nexport function Rect({ children, ...props }: RectProps) {\n return React.createElement('pbl-rect', props, children);\n}\n\nexport interface CircleProps extends PositionProps {\n r?: number;\n /** Alias for `r`. */\n radius?: number;\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Circle(props: CircleProps) {\n return React.createElement('pbl-circle', props);\n}\n\nexport interface TextProps extends PositionProps, SizeProps {\n font?: FontName;\n color?: ColorName;\n align?: Alignment;\n children?: ReactNode;\n}\n\nexport function Text({ children, ...props }: TextProps) {\n return React.createElement('pbl-text', props, children);\n}\n\nexport interface LineProps extends PositionProps {\n x2?: number;\n y2?: number;\n color?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Line(props: LineProps) {\n return React.createElement('pbl-line', props);\n}\n\nexport interface ImageProps extends PositionProps {\n bitmap: unknown;\n /** Rotation in radians. */\n rotation?: number;\n /** Scale factor (1 = original size). */\n scale?: number;\n}\n\nexport function Image(props: ImageProps) {\n return React.createElement('pbl-image', props);\n}\n\nexport interface GroupProps extends PositionProps, SizeProps {\n children?: ReactNode;\n}\n\nexport function Group({ children, ...props }: GroupProps) {\n return React.createElement('pbl-group', props, children);\n}\n\n// ---------------------------------------------------------------------------\n// Flow layout containers\n// ---------------------------------------------------------------------------\n\nexport interface ColumnProps extends PositionProps, SizeProps {\n /** Gap between children in pixels (default 0). */\n gap?: number;\n children?: ReactNode;\n}\n\n/**\n * Stacks children vertically, auto-computing each child's `y` offset.\n * Children should specify `h` (or `height`) for correct stacking;\n * children without a height are given a default of 20px.\n */\nexport function Column({ x = 0, y = 0, gap = 0, children, ...props }: ColumnProps) {\n let offsetY = 0;\n const mapped = toArray(children).map((child, i) => {\n if (!child || typeof child !== 'object') return child;\n const vnode = child as { type: unknown; props: Record<string, unknown> };\n if (!vnode.type || !vnode.props) return child;\n const childH = (vnode.props.h ?? vnode.props.height ?? 20) as number;\n const el = React.createElement(\n vnode.type as string,\n { ...(vnode.props as object), y: offsetY, key: i },\n );\n offsetY += childH + gap;\n return el;\n });\n\n return React.createElement('pbl-group', { x, y, ...props }, ...(mapped as ReactNode[]));\n}\n\nexport interface RowProps extends PositionProps, SizeProps {\n /** Gap between children in pixels (default 0). */\n gap?: number;\n children?: ReactNode;\n}\n\n/**\n * Stacks children horizontally, auto-computing each child's `x` offset.\n * Children should specify `w` (or `width`) for correct stacking;\n * children without a width are given a default of 40px.\n */\nexport function Row({ x = 0, y = 0, gap = 0, children, ...props }: RowProps) {\n let offsetX = 0;\n const mapped = toArray(children).map((child, i) => {\n if (!child || typeof child !== 'object') return child;\n const vnode = child as { type: unknown; props: Record<string, unknown> };\n if (!vnode.type || !vnode.props) return child;\n const childW = (vnode.props.w ?? vnode.props.width ?? 40) as number;\n const el = React.createElement(\n vnode.type as string,\n { ...(vnode.props as object), x: offsetX, key: i },\n );\n offsetX += childW + gap;\n return el;\n });\n\n return React.createElement('pbl-group', { x, y, ...props }, ...(mapped as ReactNode[]));\n}\n\n/** Flatten ComponentChildren into an array, filtering nulls. */\nfunction toArray(children: ReactNode): unknown[] {\n if (children == null) return [];\n if (Array.isArray(children)) return children.filter(Boolean);\n return [children];\n}\n\nexport interface StatusBarProps {\n color?: ColorName;\n backgroundColor?: ColorName;\n separator?: 'dotted' | 'line' | 'none';\n}\n\nexport function StatusBar(props: StatusBarProps) {\n return React.createElement('pbl-statusbar', props);\n}\n\nexport interface ActionBarProps {\n upIcon?: unknown;\n selectIcon?: unknown;\n downIcon?: unknown;\n backgroundColor?: ColorName;\n}\n\nexport function ActionBar(props: ActionBarProps) {\n return React.createElement('pbl-actionbar', props);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience composites\n// ---------------------------------------------------------------------------\n\nexport interface CardProps extends PositionProps {\n title: ReactNode;\n body?: ReactNode;\n titleFont?: FontName;\n bodyFont?: FontName;\n w?: number;\n}\n\nexport function Card({\n title,\n body,\n titleFont,\n bodyFont,\n x = 0,\n y = 0,\n w = 144,\n ...props\n}: CardProps) {\n const titleH = 28;\n const bodyY = titleH + 4;\n\n return React.createElement(\n 'pbl-group',\n { x, y, ...props },\n React.createElement('pbl-rect', { x: 0, y: 0, w, h: titleH, fill: 'white' }),\n React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: 2,\n w: w - 8,\n h: titleH,\n font: titleFont ?? 'gothic18Bold',\n color: 'black',\n },\n title,\n ),\n body\n ? React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: bodyY,\n w: w - 8,\n h: 120,\n font: bodyFont ?? 'gothic14',\n color: 'white',\n },\n body,\n )\n : null,\n );\n}\n\nexport interface BadgeProps extends PositionProps {\n r?: number;\n color?: ColorName;\n textColor?: ColorName;\n children?: ReactNode;\n}\n\nexport function Badge({\n x = 0,\n y = 0,\n r = 12,\n color = 'red',\n textColor = 'white',\n children,\n}: BadgeProps) {\n return React.createElement(\n 'pbl-group',\n { x, y },\n React.createElement('pbl-circle', { x: 0, y: 0, r, fill: color }),\n React.createElement(\n 'pbl-text',\n {\n x: 0,\n y: r - 8,\n w: r * 2,\n h: 16,\n font: 'gothic14Bold',\n color: textColor,\n align: 'center',\n },\n children,\n ),\n );\n}\n"],"mappings":"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"}
|
package/dist/lib/components.js
CHANGED
|
@@ -22,13 +22,52 @@ function s(e) {
|
|
|
22
22
|
function c({ children: e, ...n }) {
|
|
23
23
|
return t.createElement("pbl-group", n, e);
|
|
24
24
|
}
|
|
25
|
-
function l(e) {
|
|
25
|
+
function l({ x: e = 0, y: n = 0, gap: r = 0, children: i, ...a }) {
|
|
26
|
+
let o = 0, s = d(i).map((e, n) => {
|
|
27
|
+
if (!e || typeof e != "object") return e;
|
|
28
|
+
let i = e;
|
|
29
|
+
if (!i.type || !i.props) return e;
|
|
30
|
+
let a = i.props.h ?? i.props.height ?? 20, s = t.createElement(i.type, {
|
|
31
|
+
...i.props,
|
|
32
|
+
y: o,
|
|
33
|
+
key: n
|
|
34
|
+
});
|
|
35
|
+
return o += a + r, s;
|
|
36
|
+
});
|
|
37
|
+
return t.createElement("pbl-group", {
|
|
38
|
+
x: e,
|
|
39
|
+
y: n,
|
|
40
|
+
...a
|
|
41
|
+
}, ...s);
|
|
42
|
+
}
|
|
43
|
+
function u({ x: e = 0, y: n = 0, gap: r = 0, children: i, ...a }) {
|
|
44
|
+
let o = 0, s = d(i).map((e, n) => {
|
|
45
|
+
if (!e || typeof e != "object") return e;
|
|
46
|
+
let i = e;
|
|
47
|
+
if (!i.type || !i.props) return e;
|
|
48
|
+
let a = i.props.w ?? i.props.width ?? 40, s = t.createElement(i.type, {
|
|
49
|
+
...i.props,
|
|
50
|
+
x: o,
|
|
51
|
+
key: n
|
|
52
|
+
});
|
|
53
|
+
return o += a + r, s;
|
|
54
|
+
});
|
|
55
|
+
return t.createElement("pbl-group", {
|
|
56
|
+
x: e,
|
|
57
|
+
y: n,
|
|
58
|
+
...a
|
|
59
|
+
}, ...s);
|
|
60
|
+
}
|
|
61
|
+
function d(e) {
|
|
62
|
+
return e == null ? [] : Array.isArray(e) ? e.filter(Boolean) : [e];
|
|
63
|
+
}
|
|
64
|
+
function f(e) {
|
|
26
65
|
return t.createElement("pbl-statusbar", e);
|
|
27
66
|
}
|
|
28
|
-
function
|
|
67
|
+
function p(e) {
|
|
29
68
|
return t.createElement("pbl-actionbar", e);
|
|
30
69
|
}
|
|
31
|
-
function
|
|
70
|
+
function m({ title: e, body: n, titleFont: r, bodyFont: i, x: a = 0, y: o = 0, w: s = 144, ...c }) {
|
|
32
71
|
return t.createElement("pbl-group", {
|
|
33
72
|
x: a,
|
|
34
73
|
y: o,
|
|
@@ -55,7 +94,7 @@ function d({ title: e, body: n, titleFont: r, bodyFont: i, x: a = 0, y: o = 0, w
|
|
|
55
94
|
color: "white"
|
|
56
95
|
}, n) : null);
|
|
57
96
|
}
|
|
58
|
-
function
|
|
97
|
+
function h({ x: e = 0, y: n = 0, r = 12, color: i = "red", textColor: a = "white", children: o }) {
|
|
59
98
|
return t.createElement("pbl-group", {
|
|
60
99
|
x: e,
|
|
61
100
|
y: n
|
|
@@ -75,6 +114,6 @@ function f({ x: e = 0, y: n = 0, r = 12, color: i = "red", textColor: a = "white
|
|
|
75
114
|
}, o));
|
|
76
115
|
}
|
|
77
116
|
//#endregion
|
|
78
|
-
export {
|
|
117
|
+
export { p as ActionBar, h as Badge, m as Card, i as Circle, l as Column, c as Group, s as Image, o as Line, r as Rect, u as Row, f as StatusBar, a as Text, n as Window };
|
|
79
118
|
|
|
80
119
|
//# sourceMappingURL=components.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","names":[],"sources":["../../src/components/index.tsx"],"sourcesContent":["/**\n * components/index.tsx — React component wrappers for Pebble primitives\n *\n * These provide a friendly JSX API that maps to the underlying\n * pbl-* element types the reconciler handles.\n */\n\nimport { h } from 'preact';\nimport type { ComponentChildren } from 'preact';\nimport type { PebbleButtonHandler } from '../hooks/index.js';\n\n// Use Preact's `h` as a stand-in for `React.createElement` so we can keep\n// the factory-based element construction that was in the react version.\nconst React = { createElement: h } as const;\n\n// Unify ReactNode with Preact's ComponentChildren so we don't have to\n// rename every prop type.\ntype ReactNode = ComponentChildren;\n\n// ---------------------------------------------------------------------------\n// Shared prop types\n// ---------------------------------------------------------------------------\n\nexport interface PositionProps {\n x?: number;\n y?: number;\n}\n\nexport interface SizeProps {\n w?: number;\n h?: number;\n /** Alias for `w`. */\n width?: number;\n /** Alias for `h`. */\n height?: number;\n}\n\nexport interface ButtonHandlerProps {\n onUp?: PebbleButtonHandler;\n onDown?: PebbleButtonHandler;\n onSelect?: PebbleButtonHandler;\n onBack?: PebbleButtonHandler;\n onLongUp?: PebbleButtonHandler;\n onLongDown?: PebbleButtonHandler;\n onLongSelect?: PebbleButtonHandler;\n}\n\nexport type ColorName =\n | 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange'\n | 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray'\n // Pass-through for raw GColor / hex values\n | (string & {});\n\nexport type FontName =\n | 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold'\n | 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold'\n | 'bitham30Black' | 'bitham42Bold' | 'bitham42Light'\n | 'bitham34MediumNumbers' | 'bitham42MediumNumbers'\n | 'robotoCondensed21' | 'roboto21' | 'droid28'\n | 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42'\n | (string & {});\n\nexport type Alignment = 'left' | 'center' | 'right';\n\n// ---------------------------------------------------------------------------\n// JSX intrinsic element declarations for the pbl-* tags\n// ---------------------------------------------------------------------------\n\ndeclare module 'preact' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'pbl-root': IntrinsicPblProps;\n 'pbl-rect': IntrinsicPblProps;\n 'pbl-circle': IntrinsicPblProps;\n 'pbl-text': IntrinsicPblProps;\n 'pbl-line': IntrinsicPblProps;\n 'pbl-image': IntrinsicPblProps;\n 'pbl-group': IntrinsicPblProps;\n 'pbl-statusbar': IntrinsicPblProps;\n 'pbl-actionbar': IntrinsicPblProps;\n }\n }\n}\n\ninterface IntrinsicPblProps {\n children?: ReactNode;\n // Loose escape hatch — wrappers below provide the typed surface.\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Layout primitives\n// ---------------------------------------------------------------------------\n\nexport interface WindowProps extends ButtonHandlerProps {\n backgroundColor?: ColorName;\n fullscreen?: boolean;\n children?: ReactNode;\n}\n\nexport function Window({ children, ...props }: WindowProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface RectProps extends PositionProps, SizeProps {\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n children?: ReactNode;\n}\n\nexport function Rect({ children, ...props }: RectProps) {\n return React.createElement('pbl-rect', props, children);\n}\n\nexport interface CircleProps extends PositionProps {\n r?: number;\n /** Alias for `r`. */\n radius?: number;\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Circle(props: CircleProps) {\n return React.createElement('pbl-circle', props);\n}\n\nexport interface TextProps extends PositionProps, SizeProps {\n font?: FontName;\n color?: ColorName;\n align?: Alignment;\n children?: ReactNode;\n}\n\nexport function Text({ children, ...props }: TextProps) {\n return React.createElement('pbl-text', props, children);\n}\n\nexport interface LineProps extends PositionProps {\n x2?: number;\n y2?: number;\n color?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Line(props: LineProps) {\n return React.createElement('pbl-line', props);\n}\n\nexport interface ImageProps extends PositionProps {\n bitmap: unknown;\n}\n\nexport function Image(props: ImageProps) {\n return React.createElement('pbl-image', props);\n}\n\nexport interface GroupProps extends PositionProps {\n children?: ReactNode;\n}\n\nexport function Group({ children, ...props }: GroupProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface StatusBarProps {\n color?: ColorName;\n backgroundColor?: ColorName;\n separator?: 'dotted' | 'line' | 'none';\n}\n\nexport function StatusBar(props: StatusBarProps) {\n return React.createElement('pbl-statusbar', props);\n}\n\nexport interface ActionBarProps {\n upIcon?: unknown;\n selectIcon?: unknown;\n downIcon?: unknown;\n backgroundColor?: ColorName;\n}\n\nexport function ActionBar(props: ActionBarProps) {\n return React.createElement('pbl-actionbar', props);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience composites\n// ---------------------------------------------------------------------------\n\nexport interface CardProps extends PositionProps {\n title: ReactNode;\n body?: ReactNode;\n titleFont?: FontName;\n bodyFont?: FontName;\n w?: number;\n}\n\nexport function Card({\n title,\n body,\n titleFont,\n bodyFont,\n x = 0,\n y = 0,\n w = 144,\n ...props\n}: CardProps) {\n const titleH = 28;\n const bodyY = titleH + 4;\n\n return React.createElement(\n 'pbl-group',\n { x, y, ...props },\n React.createElement('pbl-rect', { x: 0, y: 0, w, h: titleH, fill: 'white' }),\n React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: 2,\n w: w - 8,\n h: titleH,\n font: titleFont ?? 'gothic18Bold',\n color: 'black',\n },\n title,\n ),\n body\n ? React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: bodyY,\n w: w - 8,\n h: 120,\n font: bodyFont ?? 'gothic14',\n color: 'white',\n },\n body,\n )\n : null,\n );\n}\n\nexport interface BadgeProps extends PositionProps {\n r?: number;\n color?: ColorName;\n textColor?: ColorName;\n children?: ReactNode;\n}\n\nexport function Badge({\n x = 0,\n y = 0,\n r = 12,\n color = 'red',\n textColor = 'white',\n children,\n}: BadgeProps) {\n return React.createElement(\n 'pbl-group',\n { x, y },\n React.createElement('pbl-circle', { x: 0, y: 0, r, fill: color }),\n React.createElement(\n 'pbl-text',\n {\n x: 0,\n y: r - 8,\n w: r * 2,\n h: 16,\n font: 'gothic14Bold',\n color: textColor,\n align: 'center',\n },\n children,\n ),\n );\n}\n"],"mappings":";;AAaA,IAAM,IAAQ,EAAE,eAAe,GAAG;AAwFlC,SAAgB,EAAO,EAAE,aAAU,GAAG,KAAsB;AAC1D,QAAO,EAAM,cAAc,aAAa,GAAO,EAAS;;AAU1D,SAAgB,EAAK,EAAE,aAAU,GAAG,KAAoB;AACtD,QAAO,EAAM,cAAc,YAAY,GAAO,EAAS;;AAYzD,SAAgB,EAAO,GAAoB;AACzC,QAAO,EAAM,cAAc,cAAc,EAAM;;AAUjD,SAAgB,EAAK,EAAE,aAAU,GAAG,KAAoB;AACtD,QAAO,EAAM,cAAc,YAAY,GAAO,EAAS;;AAUzD,SAAgB,EAAK,GAAkB;AACrC,QAAO,EAAM,cAAc,YAAY,EAAM;;AAO/C,SAAgB,EAAM,GAAmB;AACvC,QAAO,EAAM,cAAc,aAAa,EAAM;;AAOhD,SAAgB,EAAM,EAAE,aAAU,GAAG,KAAqB;AACxD,QAAO,EAAM,cAAc,aAAa,GAAO,EAAS;;AAS1D,SAAgB,EAAU,GAAuB;AAC/C,QAAO,EAAM,cAAc,iBAAiB,EAAM;;AAUpD,SAAgB,EAAU,GAAuB;AAC/C,QAAO,EAAM,cAAc,iBAAiB,EAAM;;AAepD,SAAgB,EAAK,EACnB,UACA,SACA,cACA,aACA,OAAI,GACJ,OAAI,GACJ,OAAI,KACJ,GAAG,KACS;AAIZ,QAAO,EAAM,cACX,aACA;EAAE;EAAG;EAAG,GAAG;EAAO,EAClB,EAAM,cAAc,YAAY;EAAE,GAAG;EAAG,GAAG;EAAG;EAAG,GAAG;EAAQ,MAAM;EAAS,CAAC,EAC5E,EAAM,cACJ,YACA;EACE,GAAG;EACH,GAAG;EACH,GAAG,IAAI;EACP,GAAG;EACH,MAAM,KAAa;EACnB,OAAO;EACR,EACD,EACD,EACD,IACI,EAAM,cACJ,YACA;EACE,GAAG;EACH,GAAG;EACH,GAAG,IAAI;EACP,GAAG;EACH,MAAM,KAAY;EAClB,OAAO;EACR,EACD,EACD,GACD,KACL;;AAUH,SAAgB,EAAM,EACpB,OAAI,GACJ,OAAI,GACJ,IAAI,IACJ,WAAQ,OACR,eAAY,SACZ,eACa;AACb,QAAO,EAAM,cACX,aACA;EAAE;EAAG;EAAG,EACR,EAAM,cAAc,cAAc;EAAE,GAAG;EAAG,GAAG;EAAG;EAAG,MAAM;EAAO,CAAC,EACjE,EAAM,cACJ,YACA;EACE,GAAG;EACH,GAAG,IAAI;EACP,GAAG,IAAI;EACP,GAAG;EACH,MAAM;EACN,OAAO;EACP,OAAO;EACR,EACD,EACD,CACF"}
|
|
1
|
+
{"version":3,"file":"components.js","names":[],"sources":["../../src/components/index.tsx"],"sourcesContent":["/**\n * components/index.tsx — React component wrappers for Pebble primitives\n *\n * These provide a friendly JSX API that maps to the underlying\n * pbl-* element types the reconciler handles.\n */\n\nimport { h } from 'preact';\nimport type { ComponentChildren } from 'preact';\nimport type { PebbleButtonHandler } from '../hooks/index.js';\n\n// Use Preact's `h` as a stand-in for `React.createElement` so we can keep\n// the factory-based element construction that was in the react version.\nconst React = { createElement: h } as const;\n\n// Unify ReactNode with Preact's ComponentChildren so we don't have to\n// rename every prop type.\ntype ReactNode = ComponentChildren;\n\n// ---------------------------------------------------------------------------\n// Shared prop types\n// ---------------------------------------------------------------------------\n\nexport interface PositionProps {\n x?: number;\n y?: number;\n}\n\nexport interface SizeProps {\n w?: number;\n h?: number;\n /** Alias for `w`. */\n width?: number;\n /** Alias for `h`. */\n height?: number;\n}\n\nexport interface ButtonHandlerProps {\n onUp?: PebbleButtonHandler;\n onDown?: PebbleButtonHandler;\n onSelect?: PebbleButtonHandler;\n onBack?: PebbleButtonHandler;\n onLongUp?: PebbleButtonHandler;\n onLongDown?: PebbleButtonHandler;\n onLongSelect?: PebbleButtonHandler;\n}\n\nexport type ColorName =\n | 'black' | 'white' | 'red' | 'green' | 'blue' | 'yellow' | 'orange'\n | 'cyan' | 'magenta' | 'clear' | 'lightGray' | 'darkGray'\n // Pass-through for raw GColor / hex values\n | (string & {});\n\nexport type FontName =\n | 'gothic14' | 'gothic14Bold' | 'gothic18' | 'gothic18Bold'\n | 'gothic24' | 'gothic24Bold' | 'gothic28' | 'gothic28Bold'\n | 'bitham30Black' | 'bitham42Bold' | 'bitham42Light'\n | 'bitham34MediumNumbers' | 'bitham42MediumNumbers'\n | 'robotoCondensed21' | 'roboto21' | 'droid28'\n | 'leco20' | 'leco26' | 'leco28' | 'leco32' | 'leco36' | 'leco38' | 'leco42'\n | (string & {});\n\nexport type Alignment = 'left' | 'center' | 'right';\n\n// ---------------------------------------------------------------------------\n// JSX intrinsic element declarations for the pbl-* tags\n// ---------------------------------------------------------------------------\n\ndeclare module 'preact' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'pbl-root': IntrinsicPblProps;\n 'pbl-rect': IntrinsicPblProps;\n 'pbl-circle': IntrinsicPblProps;\n 'pbl-text': IntrinsicPblProps;\n 'pbl-line': IntrinsicPblProps;\n 'pbl-image': IntrinsicPblProps;\n 'pbl-group': IntrinsicPblProps;\n 'pbl-statusbar': IntrinsicPblProps;\n 'pbl-actionbar': IntrinsicPblProps;\n }\n }\n}\n\ninterface IntrinsicPblProps {\n children?: ReactNode;\n // Loose escape hatch — wrappers below provide the typed surface.\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Layout primitives\n// ---------------------------------------------------------------------------\n\nexport interface WindowProps extends ButtonHandlerProps {\n backgroundColor?: ColorName;\n fullscreen?: boolean;\n children?: ReactNode;\n}\n\nexport function Window({ children, ...props }: WindowProps) {\n return React.createElement('pbl-group', props, children);\n}\n\nexport interface RectProps extends PositionProps, SizeProps {\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n borderRadius?: number;\n children?: ReactNode;\n}\n\nexport function Rect({ children, ...props }: RectProps) {\n return React.createElement('pbl-rect', props, children);\n}\n\nexport interface CircleProps extends PositionProps {\n r?: number;\n /** Alias for `r`. */\n radius?: number;\n fill?: ColorName;\n stroke?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Circle(props: CircleProps) {\n return React.createElement('pbl-circle', props);\n}\n\nexport interface TextProps extends PositionProps, SizeProps {\n font?: FontName;\n color?: ColorName;\n align?: Alignment;\n children?: ReactNode;\n}\n\nexport function Text({ children, ...props }: TextProps) {\n return React.createElement('pbl-text', props, children);\n}\n\nexport interface LineProps extends PositionProps {\n x2?: number;\n y2?: number;\n color?: ColorName;\n strokeWidth?: number;\n}\n\nexport function Line(props: LineProps) {\n return React.createElement('pbl-line', props);\n}\n\nexport interface ImageProps extends PositionProps {\n bitmap: unknown;\n /** Rotation in radians. */\n rotation?: number;\n /** Scale factor (1 = original size). */\n scale?: number;\n}\n\nexport function Image(props: ImageProps) {\n return React.createElement('pbl-image', props);\n}\n\nexport interface GroupProps extends PositionProps, SizeProps {\n children?: ReactNode;\n}\n\nexport function Group({ children, ...props }: GroupProps) {\n return React.createElement('pbl-group', props, children);\n}\n\n// ---------------------------------------------------------------------------\n// Flow layout containers\n// ---------------------------------------------------------------------------\n\nexport interface ColumnProps extends PositionProps, SizeProps {\n /** Gap between children in pixels (default 0). */\n gap?: number;\n children?: ReactNode;\n}\n\n/**\n * Stacks children vertically, auto-computing each child's `y` offset.\n * Children should specify `h` (or `height`) for correct stacking;\n * children without a height are given a default of 20px.\n */\nexport function Column({ x = 0, y = 0, gap = 0, children, ...props }: ColumnProps) {\n let offsetY = 0;\n const mapped = toArray(children).map((child, i) => {\n if (!child || typeof child !== 'object') return child;\n const vnode = child as { type: unknown; props: Record<string, unknown> };\n if (!vnode.type || !vnode.props) return child;\n const childH = (vnode.props.h ?? vnode.props.height ?? 20) as number;\n const el = React.createElement(\n vnode.type as string,\n { ...(vnode.props as object), y: offsetY, key: i },\n );\n offsetY += childH + gap;\n return el;\n });\n\n return React.createElement('pbl-group', { x, y, ...props }, ...(mapped as ReactNode[]));\n}\n\nexport interface RowProps extends PositionProps, SizeProps {\n /** Gap between children in pixels (default 0). */\n gap?: number;\n children?: ReactNode;\n}\n\n/**\n * Stacks children horizontally, auto-computing each child's `x` offset.\n * Children should specify `w` (or `width`) for correct stacking;\n * children without a width are given a default of 40px.\n */\nexport function Row({ x = 0, y = 0, gap = 0, children, ...props }: RowProps) {\n let offsetX = 0;\n const mapped = toArray(children).map((child, i) => {\n if (!child || typeof child !== 'object') return child;\n const vnode = child as { type: unknown; props: Record<string, unknown> };\n if (!vnode.type || !vnode.props) return child;\n const childW = (vnode.props.w ?? vnode.props.width ?? 40) as number;\n const el = React.createElement(\n vnode.type as string,\n { ...(vnode.props as object), x: offsetX, key: i },\n );\n offsetX += childW + gap;\n return el;\n });\n\n return React.createElement('pbl-group', { x, y, ...props }, ...(mapped as ReactNode[]));\n}\n\n/** Flatten ComponentChildren into an array, filtering nulls. */\nfunction toArray(children: ReactNode): unknown[] {\n if (children == null) return [];\n if (Array.isArray(children)) return children.filter(Boolean);\n return [children];\n}\n\nexport interface StatusBarProps {\n color?: ColorName;\n backgroundColor?: ColorName;\n separator?: 'dotted' | 'line' | 'none';\n}\n\nexport function StatusBar(props: StatusBarProps) {\n return React.createElement('pbl-statusbar', props);\n}\n\nexport interface ActionBarProps {\n upIcon?: unknown;\n selectIcon?: unknown;\n downIcon?: unknown;\n backgroundColor?: ColorName;\n}\n\nexport function ActionBar(props: ActionBarProps) {\n return React.createElement('pbl-actionbar', props);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience composites\n// ---------------------------------------------------------------------------\n\nexport interface CardProps extends PositionProps {\n title: ReactNode;\n body?: ReactNode;\n titleFont?: FontName;\n bodyFont?: FontName;\n w?: number;\n}\n\nexport function Card({\n title,\n body,\n titleFont,\n bodyFont,\n x = 0,\n y = 0,\n w = 144,\n ...props\n}: CardProps) {\n const titleH = 28;\n const bodyY = titleH + 4;\n\n return React.createElement(\n 'pbl-group',\n { x, y, ...props },\n React.createElement('pbl-rect', { x: 0, y: 0, w, h: titleH, fill: 'white' }),\n React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: 2,\n w: w - 8,\n h: titleH,\n font: titleFont ?? 'gothic18Bold',\n color: 'black',\n },\n title,\n ),\n body\n ? React.createElement(\n 'pbl-text',\n {\n x: 4,\n y: bodyY,\n w: w - 8,\n h: 120,\n font: bodyFont ?? 'gothic14',\n color: 'white',\n },\n body,\n )\n : null,\n );\n}\n\nexport interface BadgeProps extends PositionProps {\n r?: number;\n color?: ColorName;\n textColor?: ColorName;\n children?: ReactNode;\n}\n\nexport function Badge({\n x = 0,\n y = 0,\n r = 12,\n color = 'red',\n textColor = 'white',\n children,\n}: BadgeProps) {\n return React.createElement(\n 'pbl-group',\n { x, y },\n React.createElement('pbl-circle', { x: 0, y: 0, r, fill: color }),\n React.createElement(\n 'pbl-text',\n {\n x: 0,\n y: r - 8,\n w: r * 2,\n h: 16,\n font: 'gothic14Bold',\n color: textColor,\n align: 'center',\n },\n children,\n ),\n );\n}\n"],"mappings":"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"}
|
package/dist/lib/hooks.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`preact`),t=require(`preact/hooks`);var n=t.useState;function r(e){return n(e)}function i(e){n=e}function a(){n=t.useState}var o=(0,e.createContext)(null);function s(){let e=(0,t.useContext)(o);if(!e)throw Error(`useApp must be used inside a react-pebble render tree`);return e}var c={_listeners:new Map,subscribe(e,t){let n=this._listeners.get(e);n||(n=new Set,this._listeners.set(e,n)),n.add(t)},unsubscribe(e,t){let n=this._listeners.get(e);n&&(n.delete(t),n.size===0&&this._listeners.delete(e))},emit(e){let t=this._listeners.get(e);if(t)for(let e of t)e()}};function l(e,n){let r=(0,t.useRef)(n);r.current=n,(0,t.useEffect)(()=>{let t=()=>r.current();return c.subscribe(e,t),()=>{c.unsubscribe(e,t)}},[e])}function u(e,n){let r=(0,t.useRef)(n);r.current=n,(0,t.useEffect)(()=>{let t=()=>r.current(),n=`long_${e}`;return c.subscribe(n,t),()=>c.unsubscribe(n,t)},[e])}function d(e=1e3){let[n,i]=r(()=>new Date);return(0,t.useEffect)(()=>{let t=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`preact`),t=require(`preact/hooks`);var n=t.useState;function r(e){return n(e)}function i(e){n=e}function a(){n=t.useState}var o=(0,e.createContext)(null);function s(){let e=(0,t.useContext)(o);if(!e)throw Error(`useApp must be used inside a react-pebble render tree`);return e}var c={_listeners:new Map,subscribe(e,t){let n=this._listeners.get(e);n||(n=new Set,this._listeners.set(e,n)),n.add(t)},unsubscribe(e,t){let n=this._listeners.get(e);n&&(n.delete(t),n.size===0&&this._listeners.delete(e))},emit(e){let t=this._listeners.get(e);if(t)for(let e of t)e()}};function l(e,n){let r=(0,t.useRef)(n);r.current=n,(0,t.useEffect)(()=>{let t=()=>r.current();return c.subscribe(e,t),()=>{c.unsubscribe(e,t)}},[e])}function u(e,n){let r=(0,t.useRef)(n);r.current=n,(0,t.useEffect)(()=>{let t=()=>r.current(),n=`long_${e}`;return c.subscribe(n,t),()=>c.unsubscribe(n,t)},[e])}function d(e=1e3){let[n,i]=r(()=>new Date);return(0,t.useEffect)(()=>{let t=()=>i(new Date);if(typeof watch<`u`&&watch){let n=e<=1e3?`secondchange`:`minutechange`;return watch.addEventListener(n,t),()=>watch.removeEventListener(n,t)}let n=setInterval(t,e);return()=>clearInterval(n)},[e]),n}function f(e=`HH:mm`){let t=d(e.includes(`ss`)?1e3:6e4),n=t.getHours(),r=n%12||12,i=t.getMinutes().toString().padStart(2,`0`),a=t.getSeconds().toString().padStart(2,`0`),o=n<12?`AM`:`PM`,s=e;return s=s.replace(`HH`,n.toString().padStart(2,`0`)),s=s.replace(`hh`,r.toString().padStart(2,`0`)),s=s.replace(`mm`,i),s=s.replace(`ss`,a),s=s.replace(`a`,o),s}function p(e,n){let r=(0,t.useRef)(e);r.current=e,(0,t.useEffect)(()=>{if(n===null)return;let e=setInterval(()=>r.current(),n);return()=>clearInterval(e)},[n])}function m(e,n={}){let{wrap:i=!1}=n,[a,o]=r(0),s=(0,t.useCallback)(()=>{o(t=>t>=e.length-1?i?0:t:t+1)},[e.length,i]),c=(0,t.useCallback)(()=>{o(t=>t<=0?i?e.length-1:t:t-1)},[e.length,i]);return l(`down`,s),l(`up`,c),{index:a,item:e[a],next:s,prev:c,setIndex:o}}function h(e){let[n,i]=r(null),[a,o]=r(!0);return(0,t.useEffect)(()=>{let t=setTimeout(()=>{i(e.mockData),o(!1)},e.mockDelay??100);return()=>clearTimeout(t)},[]),{data:n,loading:a}}function g(){return typeof Battery<`u`&&Battery?{percent:Battery.percent,charging:Battery.charging,plugged:Battery.plugged}:{percent:100,charging:!1,plugged:!1}}function _(){return typeof watch<`u`&&watch?.connected?{app:watch.connected.app,pebblekit:watch.connected.pebblekit}:{app:!0,pebblekit:!0}}function v(e,n){let[i,a]=r(()=>{if(typeof localStorage>`u`)return n;try{let t=localStorage.getItem(e);return t===null?n:JSON.parse(t)}catch{return n}});return[i,(0,t.useCallback)(t=>{a(n=>{let r=typeof t==`function`?t(n):t;if(typeof localStorage<`u`)try{localStorage.setItem(e,JSON.stringify(r))}catch{}return r})},[e])]}function y(e,n={}){let[i,a]=r(null),[o,s]=r(!0),[c,l]=r(null);return(0,t.useEffect)(()=>{if(typeof globalThis.fetch==`function`){globalThis.fetch(e,n.init).then(e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);return e.json()}).then(e=>{a(e),s(!1)}).catch(e=>{l(String(e)),s(!1)});return}if(n.mockData!==void 0){let e=setTimeout(()=>{a(n.mockData),s(!1)},n.mockDelay??100);return()=>clearTimeout(e)}l(`fetch() not available`),s(!1)},[e]),{data:i,loading:o,error:c}}var b={linear:e=>e,quadEaseIn:e=>e*e,quadEaseOut:e=>e*(2-e),quadEaseInOut:e=>e<.5?2*e*e:-1+(4-2*e)*e,cubicEaseIn:e=>e*e*e,cubicEaseOut:e=>--e*e*e+1,cubicEaseInOut:e=>e<.5?4*e*e*e:(e-1)*(2*e-2)*(2*e-2)+1,sinEaseIn:e=>1-Math.cos(e*Math.PI/2),sinEaseOut:e=>Math.sin(e*Math.PI/2),sinEaseInOut:e=>-(Math.cos(Math.PI*e)-1)/2,expoEaseIn:e=>e===0?0:2**(10*(e-1)),expoEaseOut:e=>e===1?1:1-2**(-10*e),circEaseIn:e=>1-Math.sqrt(1-e*e),circEaseOut:e=>Math.sqrt(1- --e*e),bounceEaseOut:e=>e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375,bounceEaseIn:e=>1-b.bounceEaseOut(1-e),elasticEaseOut:e=>e===0||e===1?e:2**(-10*e)*Math.sin((e-.075)*(2*Math.PI)/.3)+1,backEaseOut:e=>{let t=1.70158;return--e*e*((t+1)*e+t)+1}};function x(e){let{duration:n,easing:r=b.linear,loop:i=!1}=e,a=d(1e3),o=a.getMinutes()*60+a.getSeconds(),s=n/1e3;return{progress:r(i?o%s/s:Math.min(o/s,1)),running:!0,start:(0,t.useCallback)(()=>{},[]),stop:(0,t.useCallback)(()=>{},[])}}function S(e,t,n){return e+(t-e)*n}function C(e={}){let{sampleRate:n=100,onTap:i,onDoubleTap:a}=e,[o,s]=r({x:0,y:0,z:-1e3}),c=(0,t.useRef)(i),l=(0,t.useRef)(a);return c.current=i,l.current=a,(0,t.useEffect)(()=>{if(typeof globalThis<`u`&&globalThis.__pbl_accel){let e=globalThis.__pbl_accel;return e.onSample=(e,t,n)=>s({x:e,y:t,z:n}),c.current&&(e.onTap=()=>c.current?.()),l.current&&(e.onDoubleTap=()=>l.current?.()),e.start?.(),()=>e.stop?.()}let e=setInterval(()=>{s({x:Math.round(Math.sin(Date.now()/1e3)*50),y:Math.round(Math.cos(Date.now()/1200)*30),z:-1e3+Math.round(Math.sin(Date.now()/800)*20)})},n);return()=>clearInterval(e)},[n]),o}function w(){let[e,n]=r(0);return(0,t.useEffect)(()=>{if(typeof globalThis<`u`&&globalThis.__pbl_compass){let e=globalThis.__pbl_compass;return e.onSample=e=>n(e),e.start?.(),()=>e.stop?.()}let e=setInterval(()=>{n(e=>(e+1)%360)},100);return()=>clearInterval(e)},[]),{heading:e}}function T(e){let[n,i]=r(null),[a,o]=r(!1),s=(0,t.useRef)(null);return(0,t.useEffect)(()=>{if(typeof WebSocket<`u`){let t=new WebSocket(e);return t.onopen=()=>o(!0),t.onmessage=e=>i(String(e.data)),t.onclose=()=>o(!1),t.onerror=()=>o(!1),s.current={send:e=>t.send(e),close:()=>t.close()},()=>t.close()}return o(!0),s.current={send:e=>{setTimeout(()=>i(`echo: ${e}`),50)},close:()=>o(!1)},()=>o(!1)},[e]),{lastMessage:n,connected:a,send:(0,t.useCallback)(e=>{s.current?.send(e)},[]),close:(0,t.useCallback)(()=>{s.current?.close()},[])}}function E(e){let n=(0,t.useRef)(null);if(!n.current){let t=globalThis.device;if(t?.keyValue)n.current=t.keyValue.open(e);else{let e=new Map;n.current={get:t=>e.get(t)??null,set:(t,n)=>e.set(t,n),delete:t=>{e.delete(t)}}}}let r=n.current;return{get:(0,t.useCallback)(e=>r.get(e),[]),set:(0,t.useCallback)((e,t)=>r.set(e,t),[]),remove:(0,t.useCallback)(e=>r.delete(e),[])}}exports.ButtonRegistry=c,exports.Easing=b,exports.PebbleAppContext=o,exports._restoreUseState=a,exports._setUseStateImpl=i,exports.lerp=S,exports.useAccelerometer=C,exports.useAnimation=x,exports.useApp=s,exports.useBattery=g,exports.useButton=l,exports.useCompass=w,exports.useConnection=_,exports.useFetch=y,exports.useFormattedTime=f,exports.useInterval=p,exports.useKVStorage=E,exports.useListNavigation=m,exports.useLocalStorage=v,exports.useLongButton=u,exports.useMessage=h,exports.useState=r,exports.useTime=d,exports.useWebSocket=T;
|
|
2
2
|
//# sourceMappingURL=hooks.cjs.map
|
package/dist/lib/hooks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.cjs","names":[],"sources":["../../src/hooks/index.ts"],"sourcesContent":["/**\n * hooks/index.ts — React hooks for react-pebble.\n *\n * This set intentionally covers only what the Alloy runtime can support\n * today. Sensor / connectivity hooks (battery, BT connection, accelerometer,\n * phone↔watch messaging) used to live here based on a fictional `Pebble`\n * global; they've been removed until we've identified the real Moddable\n * module shape for each one.\n *\n * See `pebble-render.ts` for the runtime wiring that lets `useTime` and\n * `useButton` actually fire on-device — hooks publish to registries that the\n * renderer connects to Moddable's `watch` event source.\n */\n\nimport { createContext } from 'preact';\nimport {\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState as _preactUseState,\n} from 'preact/hooks';\n\n// Wrap useState so the compiler can swap the implementation at compile time.\n// Direct ESM re-exports are sealed (getter-only), so we use a mutable\n// internal reference that the compiler patches via _setUseStateImpl().\ntype UseStateFn = <T>(init: T | (() => T)) => [T, (v: T | ((p: T) => T)) => void];\nlet _useStateImpl: UseStateFn = _preactUseState;\n\nexport function useState<T>(init: T | (() => T)): [T, (v: T | ((p: T) => T)) => void] {\n return _useStateImpl(init);\n}\n\n/** @internal — called by the compiler to intercept useState calls. */\nexport function _setUseStateImpl(impl: UseStateFn): void {\n _useStateImpl = impl;\n}\n\n/** @internal — restore original useState after compilation. */\nexport function _restoreUseState(): void {\n _useStateImpl = _preactUseState;\n}\nimport type { PebbleApp } from '../pebble-render.js';\n\n// ---------------------------------------------------------------------------\n// Button types\n// ---------------------------------------------------------------------------\n\nexport type PebbleButton = 'up' | 'down' | 'select' | 'back';\nexport type PebbleButtonHandler = () => void;\n\n// ---------------------------------------------------------------------------\n// App context — provides access to the render app from nested components.\n// ---------------------------------------------------------------------------\n\nexport const PebbleAppContext = createContext<PebbleApp | null>(null);\n\nexport function useApp(): PebbleApp {\n const app = useContext(PebbleAppContext);\n if (!app) {\n throw new Error('useApp must be used inside a react-pebble render tree');\n }\n return app;\n}\n\n// ---------------------------------------------------------------------------\n// Button registry\n//\n// Buttons are delivered to react-pebble two ways:\n// (a) via props on elements (onUp/onDown/onSelect/onBack), collected and\n// subscribed to Moddable's `watch` in pebble-render.ts; or\n// (b) via this hook registry, which the renderer pumps from `watch` events.\n//\n// The registry is the substrate for both `useButton` and `useLongButton`.\n// The exact `watch` event names for buttons are not yet confirmed — the\n// renderer normalizes them into our four logical buttons before emitting.\n// ---------------------------------------------------------------------------\n\ntype ButtonRegistryKey = PebbleButton | `long_${PebbleButton}`;\n\ninterface ButtonRegistryShape {\n _listeners: Map<ButtonRegistryKey, Set<PebbleButtonHandler>>;\n subscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;\n unsubscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;\n emit(button: ButtonRegistryKey): void;\n}\n\nexport const ButtonRegistry: ButtonRegistryShape = {\n _listeners: new Map<ButtonRegistryKey, Set<PebbleButtonHandler>>(),\n\n subscribe(button, fn) {\n let set = this._listeners.get(button);\n if (!set) {\n set = new Set();\n this._listeners.set(button, set);\n }\n set.add(fn);\n },\n\n unsubscribe(button, fn) {\n const set = this._listeners.get(button);\n if (set) {\n set.delete(fn);\n if (set.size === 0) this._listeners.delete(button);\n }\n },\n\n emit(button) {\n const set = this._listeners.get(button);\n if (set) {\n for (const fn of set) fn();\n }\n },\n};\n\nexport function useButton(button: PebbleButton, handler: PebbleButtonHandler): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const listener: PebbleButtonHandler = () => handlerRef.current();\n ButtonRegistry.subscribe(button, listener);\n return () => {\n ButtonRegistry.unsubscribe(button, listener);\n };\n }, [button]);\n}\n\nexport function useLongButton(button: PebbleButton, handler: PebbleButtonHandler): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const listener: PebbleButtonHandler = () => handlerRef.current();\n const key: ButtonRegistryKey = `long_${button}`;\n ButtonRegistry.subscribe(key, listener);\n return () => ButtonRegistry.unsubscribe(key, listener);\n }, [button]);\n}\n\n// ---------------------------------------------------------------------------\n// Time hooks\n//\n// On Alloy, `watch.addEventListener('secondchange'|'minutechange', fn)` is\n// the canonical tick source. In Node mock mode we fall back to setInterval.\n// Both paths are abstracted away by the renderer, which pumps the tick into\n// the hook via React state — here we just use setInterval directly, which\n// works in both XS and Node. (`watch` is used for redraws, not hook state.)\n// ---------------------------------------------------------------------------\n\nexport function useTime(intervalMs = 1000): Date {\n const [time, setTime] = useState<Date>(() => new Date());\n\n useEffect(() => {\n const tick = () => setTime(new Date());\n const id = setInterval(tick, intervalMs);\n return () => clearInterval(id);\n }, [intervalMs]);\n\n return time;\n}\n\nexport function useFormattedTime(format = 'HH:mm'): string {\n const time = useTime(format.includes('ss') ? 1000 : 60000);\n\n const hours24 = time.getHours();\n const hours12 = hours24 % 12 || 12;\n const minutes = time.getMinutes().toString().padStart(2, '0');\n const seconds = time.getSeconds().toString().padStart(2, '0');\n const ampm = hours24 < 12 ? 'AM' : 'PM';\n\n let result = format;\n result = result.replace('HH', hours24.toString().padStart(2, '0'));\n result = result.replace('hh', hours12.toString().padStart(2, '0'));\n result = result.replace('mm', minutes);\n result = result.replace('ss', seconds);\n result = result.replace('a', ampm);\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// useInterval\n// ---------------------------------------------------------------------------\n\nexport function useInterval(callback: () => void, delay: number | null): void {\n const savedCallback = useRef(callback);\n savedCallback.current = callback;\n\n useEffect(() => {\n if (delay === null) return;\n\n const id = setInterval(() => savedCallback.current(), delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n\n// ---------------------------------------------------------------------------\n// List navigation\n// ---------------------------------------------------------------------------\n\nexport interface ListNavigationOptions {\n wrap?: boolean;\n}\n\nexport interface ListNavigationResult<T> {\n index: number;\n item: T | undefined;\n next: () => void;\n prev: () => void;\n setIndex: (index: number) => void;\n}\n\nexport function useListNavigation<T>(\n items: readonly T[],\n options: ListNavigationOptions = {},\n): ListNavigationResult<T> {\n const { wrap = false } = options;\n const [index, setIndex] = useState(0);\n\n const next = useCallback(() => {\n setIndex((i) => {\n if (i >= items.length - 1) return wrap ? 0 : i;\n return i + 1;\n });\n }, [items.length, wrap]);\n\n const prev = useCallback(() => {\n setIndex((i) => {\n if (i <= 0) return wrap ? items.length - 1 : i;\n return i - 1;\n });\n }, [items.length, wrap]);\n\n useButton('down', next);\n useButton('up', prev);\n\n return {\n index,\n item: items[index],\n next,\n prev,\n setIndex,\n };\n}\n\n// ---------------------------------------------------------------------------\n// useMessage — runtime data loading via phone→watch messaging\n// ---------------------------------------------------------------------------\n\nexport interface UseMessageOptions<T> {\n /** Message key name (must match PebbleKit JS sendAppMessage key) */\n key: string;\n /** Mock data returned at compile time so the compiler can render the loaded state */\n mockData: T;\n /** Delay in ms before mock data appears (for SETTLE_MS) */\n mockDelay?: number;\n}\n\nexport interface UseMessageResult<T> {\n data: T | null;\n loading: boolean;\n}\n\n/**\n * Load data from the phone at runtime via Pebble's Message API.\n *\n * At compile time (Node mock mode): returns mockData after mockDelay ms.\n * At runtime (Alloy): the compiler emits a Message subscription that\n * populates data when the phone sends it.\n *\n * Usage:\n * const { data, loading } = useMessage({\n * key: 'items',\n * mockData: [{ title: 'Fix bug', status: 'Open' }],\n * });\n */\nexport function useMessage<T>(options: UseMessageOptions<T>): UseMessageResult<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n // In mock mode (compile time), simulate async data arrival\n const timer = setTimeout(() => {\n setData(options.mockData);\n setLoading(false);\n }, options.mockDelay ?? 100);\n return () => clearTimeout(timer);\n }, []);\n\n return { data, loading };\n}\n\n// ---------------------------------------------------------------------------\n// REMOVED HOOKS — reference for future reimplementation\n//\n// - useBattery — previously read Pebble.battery (fictional global).\n// Alloy equivalent: probably a Moddable power\n// module; not yet explored.\n// - useConnection — previously Pebble.connection.isConnected().\n// Alloy equivalent: `watch.connected` property was\n// observed on the watch prototype but its shape is\n// unknown.\n// - useAccelerometer — previously Pebble.accel. Alloy has Moddable\n// sensor modules; specific import path unknown.\n// - useAppMessage — previously Pebble.sendAppMessage / addEventListener\n// for 'appmessage'. Alloy has no direct equivalent;\n// phone↔watch messaging goes through PebbleKit JS on\n// the phone side (`src/pkjs/index.js`) and is a\n// separate concern.\n// ---------------------------------------------------------------------------\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.cjs","names":[],"sources":["../../src/hooks/index.ts"],"sourcesContent":["/**\n * hooks/index.ts — React hooks for react-pebble.\n *\n * This set intentionally covers only what the Alloy runtime can support\n * today. Sensor / connectivity hooks (battery, BT connection, accelerometer,\n * phone↔watch messaging) used to live here based on a fictional `Pebble`\n * global; they've been removed until we've identified the real Moddable\n * module shape for each one.\n *\n * See `pebble-render.ts` for the runtime wiring that lets `useTime` and\n * `useButton` actually fire on-device — hooks publish to registries that the\n * renderer connects to Moddable's `watch` event source.\n */\n\nimport { createContext } from 'preact';\nimport {\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState as _preactUseState,\n} from 'preact/hooks';\n\n// Wrap useState so the compiler can swap the implementation at compile time.\n// Direct ESM re-exports are sealed (getter-only), so we use a mutable\n// internal reference that the compiler patches via _setUseStateImpl().\ntype UseStateFn = <T>(init: T | (() => T)) => [T, (v: T | ((p: T) => T)) => void];\nlet _useStateImpl: UseStateFn = _preactUseState;\n\nexport function useState<T>(init: T | (() => T)): [T, (v: T | ((p: T) => T)) => void] {\n return _useStateImpl(init);\n}\n\n/** @internal — called by the compiler to intercept useState calls. */\nexport function _setUseStateImpl(impl: UseStateFn): void {\n _useStateImpl = impl;\n}\n\n/** @internal — restore original useState after compilation. */\nexport function _restoreUseState(): void {\n _useStateImpl = _preactUseState;\n}\nimport type { PebbleApp } from '../pebble-render.js';\n\n// ---------------------------------------------------------------------------\n// Button types\n// ---------------------------------------------------------------------------\n\nexport type PebbleButton = 'up' | 'down' | 'select' | 'back';\nexport type PebbleButtonHandler = () => void;\n\n// ---------------------------------------------------------------------------\n// App context — provides access to the render app from nested components.\n// ---------------------------------------------------------------------------\n\nexport const PebbleAppContext = createContext<PebbleApp | null>(null);\n\nexport function useApp(): PebbleApp {\n const app = useContext(PebbleAppContext);\n if (!app) {\n throw new Error('useApp must be used inside a react-pebble render tree');\n }\n return app;\n}\n\n// ---------------------------------------------------------------------------\n// Button registry\n//\n// Buttons are delivered to react-pebble two ways:\n// (a) via props on elements (onUp/onDown/onSelect/onBack), collected and\n// subscribed to Moddable's `watch` in pebble-render.ts; or\n// (b) via this hook registry, which the renderer pumps from `watch` events.\n//\n// The registry is the substrate for both `useButton` and `useLongButton`.\n// The exact `watch` event names for buttons are not yet confirmed — the\n// renderer normalizes them into our four logical buttons before emitting.\n// ---------------------------------------------------------------------------\n\ntype ButtonRegistryKey = PebbleButton | `long_${PebbleButton}`;\n\ninterface ButtonRegistryShape {\n _listeners: Map<ButtonRegistryKey, Set<PebbleButtonHandler>>;\n subscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;\n unsubscribe(button: ButtonRegistryKey, fn: PebbleButtonHandler): void;\n emit(button: ButtonRegistryKey): void;\n}\n\nexport const ButtonRegistry: ButtonRegistryShape = {\n _listeners: new Map<ButtonRegistryKey, Set<PebbleButtonHandler>>(),\n\n subscribe(button, fn) {\n let set = this._listeners.get(button);\n if (!set) {\n set = new Set();\n this._listeners.set(button, set);\n }\n set.add(fn);\n },\n\n unsubscribe(button, fn) {\n const set = this._listeners.get(button);\n if (set) {\n set.delete(fn);\n if (set.size === 0) this._listeners.delete(button);\n }\n },\n\n emit(button) {\n const set = this._listeners.get(button);\n if (set) {\n for (const fn of set) fn();\n }\n },\n};\n\nexport function useButton(button: PebbleButton, handler: PebbleButtonHandler): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const listener: PebbleButtonHandler = () => handlerRef.current();\n ButtonRegistry.subscribe(button, listener);\n return () => {\n ButtonRegistry.unsubscribe(button, listener);\n };\n }, [button]);\n}\n\nexport function useLongButton(button: PebbleButton, handler: PebbleButtonHandler): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const listener: PebbleButtonHandler = () => handlerRef.current();\n const key: ButtonRegistryKey = `long_${button}`;\n ButtonRegistry.subscribe(key, listener);\n return () => ButtonRegistry.unsubscribe(key, listener);\n }, [button]);\n}\n\n// ---------------------------------------------------------------------------\n// Time hooks\n//\n// On Alloy, `watch.addEventListener('secondchange'|'minutechange', fn)` is\n// the canonical tick source — it fires exactly on boundaries and is far more\n// battery-efficient than setInterval. In Node mock mode we fall back to\n// setInterval.\n// ---------------------------------------------------------------------------\n\nexport function useTime(intervalMs = 1000): Date {\n const [time, setTime] = useState<Date>(() => new Date());\n\n useEffect(() => {\n const tick = () => setTime(new Date());\n\n // On Alloy: use watch tick events for battery efficiency\n if (typeof watch !== 'undefined' && watch) {\n const event = intervalMs <= 1000 ? 'secondchange' : 'minutechange';\n watch.addEventListener(event, tick);\n return () => watch!.removeEventListener(event, tick);\n }\n\n // Mock mode: fall back to setInterval\n const id = setInterval(tick, intervalMs);\n return () => clearInterval(id);\n }, [intervalMs]);\n\n return time;\n}\n\nexport function useFormattedTime(format = 'HH:mm'): string {\n const time = useTime(format.includes('ss') ? 1000 : 60000);\n\n const hours24 = time.getHours();\n const hours12 = hours24 % 12 || 12;\n const minutes = time.getMinutes().toString().padStart(2, '0');\n const seconds = time.getSeconds().toString().padStart(2, '0');\n const ampm = hours24 < 12 ? 'AM' : 'PM';\n\n let result = format;\n result = result.replace('HH', hours24.toString().padStart(2, '0'));\n result = result.replace('hh', hours12.toString().padStart(2, '0'));\n result = result.replace('mm', minutes);\n result = result.replace('ss', seconds);\n result = result.replace('a', ampm);\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// useInterval\n// ---------------------------------------------------------------------------\n\nexport function useInterval(callback: () => void, delay: number | null): void {\n const savedCallback = useRef(callback);\n savedCallback.current = callback;\n\n useEffect(() => {\n if (delay === null) return;\n\n const id = setInterval(() => savedCallback.current(), delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n\n// ---------------------------------------------------------------------------\n// List navigation\n// ---------------------------------------------------------------------------\n\nexport interface ListNavigationOptions {\n wrap?: boolean;\n}\n\nexport interface ListNavigationResult<T> {\n index: number;\n item: T | undefined;\n next: () => void;\n prev: () => void;\n setIndex: (index: number) => void;\n}\n\nexport function useListNavigation<T>(\n items: readonly T[],\n options: ListNavigationOptions = {},\n): ListNavigationResult<T> {\n const { wrap = false } = options;\n const [index, setIndex] = useState(0);\n\n const next = useCallback(() => {\n setIndex((i) => {\n if (i >= items.length - 1) return wrap ? 0 : i;\n return i + 1;\n });\n }, [items.length, wrap]);\n\n const prev = useCallback(() => {\n setIndex((i) => {\n if (i <= 0) return wrap ? items.length - 1 : i;\n return i - 1;\n });\n }, [items.length, wrap]);\n\n useButton('down', next);\n useButton('up', prev);\n\n return {\n index,\n item: items[index],\n next,\n prev,\n setIndex,\n };\n}\n\n// ---------------------------------------------------------------------------\n// useMessage — runtime data loading via phone→watch messaging\n// ---------------------------------------------------------------------------\n\nexport interface UseMessageOptions<T> {\n /** Message key name (must match PebbleKit JS sendAppMessage key) */\n key: string;\n /** Mock data returned at compile time so the compiler can render the loaded state */\n mockData: T;\n /** Delay in ms before mock data appears (for SETTLE_MS) */\n mockDelay?: number;\n}\n\nexport interface UseMessageResult<T> {\n data: T | null;\n loading: boolean;\n}\n\n/**\n * Load data from the phone at runtime via Pebble's Message API.\n *\n * At compile time (Node mock mode): returns mockData after mockDelay ms.\n * At runtime (Alloy): the compiler emits a Message subscription that\n * populates data when the phone sends it.\n *\n * Usage:\n * const { data, loading } = useMessage({\n * key: 'items',\n * mockData: [{ title: 'Fix bug', status: 'Open' }],\n * });\n */\nexport function useMessage<T>(options: UseMessageOptions<T>): UseMessageResult<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n // In mock mode (compile time), simulate async data arrival\n const timer = setTimeout(() => {\n setData(options.mockData);\n setLoading(false);\n }, options.mockDelay ?? 100);\n return () => clearTimeout(timer);\n }, []);\n\n return { data, loading };\n}\n\n// ---------------------------------------------------------------------------\n// Battery hook — reads Alloy's Battery sensor (percent, charging, plugged)\n// ---------------------------------------------------------------------------\n\nexport interface BatteryState {\n percent: number;\n charging: boolean;\n plugged: boolean;\n}\n\n/**\n * Returns the current battery state. On Alloy, reads the `Battery` global.\n * In mock mode (Node), returns a static default (100%, not charging).\n *\n * Re-reads on each render; battery updates arrive via watch tick events\n * which trigger redraws, so the value stays fresh.\n */\nexport function useBattery(): BatteryState {\n if (typeof Battery !== 'undefined' && Battery) {\n return {\n percent: Battery.percent,\n charging: Battery.charging,\n plugged: Battery.plugged,\n };\n }\n // Mock mode — return a sensible default\n return { percent: 100, charging: false, plugged: false };\n}\n\n// ---------------------------------------------------------------------------\n// Connection hook — reads watch.connected (app + pebblekit)\n// ---------------------------------------------------------------------------\n\nexport interface ConnectionState {\n app: boolean;\n pebblekit: boolean;\n}\n\n/**\n * Returns the current phone connection state. On Alloy, reads\n * `watch.connected`. In mock mode, returns connected for both.\n */\nexport function useConnection(): ConnectionState {\n if (typeof watch !== 'undefined' && watch?.connected) {\n return {\n app: watch.connected.app,\n pebblekit: watch.connected.pebblekit,\n };\n }\n // Mock mode\n return { app: true, pebblekit: true };\n}\n\n// ---------------------------------------------------------------------------\n// localStorage hook — persists state across app restarts and reboots\n// ---------------------------------------------------------------------------\n\n/**\n * Like useState, but backed by localStorage so the value persists across\n * app restarts and watch reboots.\n *\n * On Alloy, `localStorage` is a standard Web API global.\n * In mock mode (Node), falls back to a plain in-memory useState.\n *\n * Values are JSON-serialized. Only use with JSON-safe types.\n */\nexport function useLocalStorage<T>(key: string, defaultValue: T): [T, (v: T | ((prev: T) => T)) => void] {\n const [value, setValue] = useState<T>(() => {\n if (typeof localStorage === 'undefined') return defaultValue;\n try {\n const stored = localStorage.getItem(key);\n return stored !== null ? JSON.parse(stored) as T : defaultValue;\n } catch {\n return defaultValue;\n }\n });\n\n const setAndPersist = useCallback((v: T | ((prev: T) => T)) => {\n setValue((prev) => {\n const next = typeof v === 'function' ? (v as (p: T) => T)(prev) : v;\n if (typeof localStorage !== 'undefined') {\n try {\n localStorage.setItem(key, JSON.stringify(next));\n } catch {\n // Storage full or unavailable — silently ignore\n }\n }\n return next;\n });\n }, [key]);\n\n return [value, setAndPersist];\n}\n\n// ---------------------------------------------------------------------------\n// useFetch — HTTP data loading via pebbleproxy\n// ---------------------------------------------------------------------------\n\nexport interface UseFetchOptions<T> {\n /** Mock data returned in Node mock mode so the compiler can render. */\n mockData?: T;\n /** Delay in ms before mock data appears (default 100). */\n mockDelay?: number;\n /** fetch() RequestInit options (method, headers, body). */\n init?: RequestInit;\n}\n\nexport interface UseFetchResult<T> {\n data: T | null;\n loading: boolean;\n error: string | null;\n}\n\n/**\n * Fetch JSON data from a URL.\n *\n * On Alloy: uses the standard `fetch()` API (proxied via @moddable/pebbleproxy).\n * In mock mode (Node): returns `mockData` after `mockDelay` ms.\n *\n * Usage:\n * const { data, loading, error } = useFetch<Weather>(\n * 'https://api.example.com/weather',\n * { mockData: { temp: 72, condition: 'Sunny' } }\n * );\n */\nexport function useFetch<T>(url: string, options: UseFetchOptions<T> = {}): UseFetchResult<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n // On Alloy (real device): use fetch()\n if (typeof globalThis.fetch === 'function') {\n globalThis.fetch(url, options.init)\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.json() as Promise<T>;\n })\n .then((json) => {\n setData(json);\n setLoading(false);\n })\n .catch((err) => {\n setError(String(err));\n setLoading(false);\n });\n return;\n }\n\n // Mock mode: return mockData after delay\n if (options.mockData !== undefined) {\n const timer = setTimeout(() => {\n setData(options.mockData!);\n setLoading(false);\n }, options.mockDelay ?? 100);\n return () => clearTimeout(timer);\n }\n\n // No fetch and no mock data\n setError('fetch() not available');\n setLoading(false);\n }, [url]);\n\n return { data, loading, error };\n}\n\n// ---------------------------------------------------------------------------\n// useAnimation — property interpolation with easing\n// ---------------------------------------------------------------------------\n\n/** Standard easing functions matching Alloy's Timeline API. */\nexport const Easing = {\n linear: (t: number) => t,\n quadEaseIn: (t: number) => t * t,\n quadEaseOut: (t: number) => t * (2 - t),\n quadEaseInOut: (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,\n cubicEaseIn: (t: number) => t * t * t,\n cubicEaseOut: (t: number) => (--t) * t * t + 1,\n cubicEaseInOut: (t: number) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,\n sinEaseIn: (t: number) => 1 - Math.cos(t * Math.PI / 2),\n sinEaseOut: (t: number) => Math.sin(t * Math.PI / 2),\n sinEaseInOut: (t: number) => -(Math.cos(Math.PI * t) - 1) / 2,\n expoEaseIn: (t: number) => t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),\n expoEaseOut: (t: number) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),\n circEaseIn: (t: number) => 1 - Math.sqrt(1 - t * t),\n circEaseOut: (t: number) => Math.sqrt(1 - (--t) * t),\n bounceEaseOut: (t: number) => {\n if (t < 1 / 2.75) return 7.5625 * t * t;\n if (t < 2 / 2.75) return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;\n if (t < 2.5 / 2.75) return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;\n return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;\n },\n bounceEaseIn: (t: number) => 1 - Easing.bounceEaseOut(1 - t),\n elasticEaseOut: (t: number) => {\n if (t === 0 || t === 1) return t;\n return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;\n },\n backEaseOut: (t: number) => {\n const s = 1.70158;\n return (--t) * t * ((s + 1) * t + s) + 1;\n },\n} as const;\n\nexport type EasingFn = (t: number) => number;\n\nexport interface UseAnimationOptions {\n /** Duration in ms. */\n duration: number;\n /** Easing function (default: linear). */\n easing?: EasingFn;\n /** Delay before start in ms (default: 0). */\n delay?: number;\n /** Loop the animation (default: false). */\n loop?: boolean;\n /** Auto-start on mount (default: true). */\n autoStart?: boolean;\n}\n\nexport interface UseAnimationResult {\n /** Current progress value (0 to 1), eased. */\n progress: number;\n /** Whether the animation is currently running. */\n running: boolean;\n /** Start or restart the animation. */\n start: () => void;\n /** Stop the animation. */\n stop: () => void;\n}\n\n/**\n * Animate a progress value from 0 to 1 over a duration with easing.\n *\n * Uses `useTime` internally so that animation progress is derived from\n * the wall clock. This ensures compatibility with the piu compiler,\n * which detects time-dependent values via T1/T2 render diffs.\n *\n * The animation cycles based on `duration` (in ms). If `loop` is true,\n * it repeats indefinitely.\n *\n * Usage:\n * const { progress } = useAnimation({ duration: 10000, easing: Easing.bounceEaseOut, loop: true });\n * const x = lerp(0, 200, progress);\n */\nexport function useAnimation(options: UseAnimationOptions): UseAnimationResult {\n const { duration, easing = Easing.linear, loop = false } = options;\n // Use useTime for clock ticks — this makes the compiler detect time deps.\n // Progress is derived purely from the current time (no stored start time),\n // so the compiler can diff T1 vs T2 and detect changing values.\n const time = useTime(1000);\n\n // Derive progress from current time modulo duration.\n // For a 60s duration, this cycles every 60s based on wall clock.\n const totalSeconds = time.getMinutes() * 60 + time.getSeconds();\n const durationSec = duration / 1000;\n const raw = loop\n ? (totalSeconds % durationSec) / durationSec\n : Math.min(totalSeconds / durationSec, 1);\n const progress = easing(raw);\n\n const start = useCallback(() => { /* no-op: auto-driven by time */ }, []);\n const stop = useCallback(() => { /* no-op: auto-driven by time */ }, []);\n\n return { progress, running: true, start, stop };\n}\n\n/**\n * Interpolate between two values using an animation progress (0-1).\n */\nexport function lerp(from: number, to: number, progress: number): number {\n return from + (to - from) * progress;\n}\n\n// ---------------------------------------------------------------------------\n// useAccelerometer — motion sensing via Moddable sensor API\n// ---------------------------------------------------------------------------\n\nexport interface AccelerometerData {\n x: number;\n y: number;\n z: number;\n}\n\nexport interface UseAccelerometerOptions {\n /** Sample rate in ms (default: 100). */\n sampleRate?: number;\n /** Called on tap gesture. */\n onTap?: () => void;\n /** Called on double-tap gesture. */\n onDoubleTap?: () => void;\n}\n\n/**\n * Read accelerometer data.\n *\n * On Alloy: reads from the Moddable Accelerometer sensor.\n * In mock mode: returns { x: 0, y: 0, z: -1000 } (gravity pointing down).\n */\nexport function useAccelerometer(options: UseAccelerometerOptions = {}): AccelerometerData {\n const { sampleRate = 100, onTap, onDoubleTap } = options;\n const [data, setData] = useState<AccelerometerData>({ x: 0, y: 0, z: -1000 });\n const tapRef = useRef(onTap);\n const doubleTapRef = useRef(onDoubleTap);\n tapRef.current = onTap;\n doubleTapRef.current = onDoubleTap;\n\n useEffect(() => {\n // Try to access the Alloy accelerometer\n if (typeof globalThis !== 'undefined' && (globalThis as Record<string, unknown>).__pbl_accel) {\n const accel = (globalThis as Record<string, unknown>).__pbl_accel as {\n onSample?: (x: number, y: number, z: number) => void;\n onTap?: () => void;\n onDoubleTap?: () => void;\n start?: () => void;\n stop?: () => void;\n };\n accel.onSample = (x: number, y: number, z: number) => setData({ x, y, z });\n if (tapRef.current) accel.onTap = () => tapRef.current?.();\n if (doubleTapRef.current) accel.onDoubleTap = () => doubleTapRef.current?.();\n accel.start?.();\n return () => accel.stop?.();\n }\n\n // Mock mode: simulate gentle wobble\n const id = setInterval(() => {\n setData({\n x: Math.round(Math.sin(Date.now() / 1000) * 50),\n y: Math.round(Math.cos(Date.now() / 1200) * 30),\n z: -1000 + Math.round(Math.sin(Date.now() / 800) * 20),\n });\n }, sampleRate);\n return () => clearInterval(id);\n }, [sampleRate]);\n\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// useCompass — magnetic heading via Moddable sensor API\n// ---------------------------------------------------------------------------\n\nexport interface CompassData {\n /** Heading in degrees (0-360, 0 = north). */\n heading: number;\n}\n\n/**\n * Read compass heading.\n *\n * On Alloy: reads from the Moddable Compass sensor.\n * In mock mode: returns a slowly rotating heading.\n */\nexport function useCompass(): CompassData {\n const [heading, setHeading] = useState(0);\n\n useEffect(() => {\n // Try to access the Alloy compass\n if (typeof globalThis !== 'undefined' && (globalThis as Record<string, unknown>).__pbl_compass) {\n const compass = (globalThis as Record<string, unknown>).__pbl_compass as {\n onSample?: (heading: number) => void;\n start?: () => void;\n stop?: () => void;\n };\n compass.onSample = (h: number) => setHeading(h);\n compass.start?.();\n return () => compass.stop?.();\n }\n\n // Mock mode: slowly rotate\n const id = setInterval(() => {\n setHeading((h) => (h + 1) % 360);\n }, 100);\n return () => clearInterval(id);\n }, []);\n\n return { heading };\n}\n\n// ---------------------------------------------------------------------------\n// useWebSocket — bidirectional communication via pebbleproxy\n// ---------------------------------------------------------------------------\n\nexport interface UseWebSocketResult {\n /** Last received message (null until first message). */\n lastMessage: string | null;\n /** Whether the connection is open. */\n connected: boolean;\n /** Send a message. */\n send: (data: string) => void;\n /** Close the connection. */\n close: () => void;\n}\n\n/**\n * Connect to a WebSocket server.\n *\n * On Alloy: uses the WebSocket API (proxied via @moddable/pebbleproxy).\n * In mock mode: simulates a connection that echoes messages back.\n */\nexport function useWebSocket(url: string): UseWebSocketResult {\n const [lastMessage, setLastMessage] = useState<string | null>(null);\n const [connected, setConnected] = useState(false);\n const wsRef = useRef<{ send: (d: string) => void; close: () => void } | null>(null);\n\n useEffect(() => {\n // On Alloy: use real WebSocket\n if (typeof WebSocket !== 'undefined') {\n const ws = new WebSocket(url);\n ws.onopen = () => setConnected(true);\n ws.onmessage = (e) => setLastMessage(String(e.data));\n ws.onclose = () => setConnected(false);\n ws.onerror = () => setConnected(false);\n wsRef.current = { send: (d) => ws.send(d), close: () => ws.close() };\n return () => ws.close();\n }\n\n // Mock mode: echo server\n setConnected(true);\n wsRef.current = {\n send: (d: string) => {\n setTimeout(() => setLastMessage(`echo: ${d}`), 50);\n },\n close: () => setConnected(false),\n };\n return () => setConnected(false);\n }, [url]);\n\n const send = useCallback((data: string) => {\n wsRef.current?.send(data);\n }, []);\n\n const close = useCallback(() => {\n wsRef.current?.close();\n }, []);\n\n return { lastMessage, connected, send, close };\n}\n\n// ---------------------------------------------------------------------------\n// useKVStorage — ECMA-419 binary key-value storage\n// ---------------------------------------------------------------------------\n\n/**\n * Key-value storage for binary and structured data using the ECMA-419 API.\n *\n * On Alloy: uses `device.keyValue.open(storeName)` for persistent binary storage.\n * In mock mode: uses an in-memory Map.\n *\n * For simple string storage, prefer `useLocalStorage`.\n */\nexport function useKVStorage(storeName: string): {\n get: (key: string) => string | null;\n set: (key: string, value: string) => void;\n remove: (key: string) => void;\n} {\n const storeRef = useRef<{\n get: (key: string) => string | null;\n set: (key: string, value: string) => void;\n delete: (key: string) => void;\n } | null>(null);\n\n if (!storeRef.current) {\n // Try ECMA-419 device.keyValue API\n const device = (globalThis as Record<string, unknown>).device as {\n keyValue?: { open: (path: string) => {\n get: (key: string) => string | null;\n set: (key: string, value: string) => void;\n delete: (key: string) => void;\n }};\n } | undefined;\n\n if (device?.keyValue) {\n storeRef.current = device.keyValue.open(storeName);\n } else {\n // Mock mode: in-memory map\n const map = new Map<string, string>();\n storeRef.current = {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => map.set(k, v),\n delete: (k) => { map.delete(k); },\n };\n }\n }\n\n const store = storeRef.current!;\n return {\n get: useCallback((key: string) => store.get(key), []),\n set: useCallback((key: string, value: string) => store.set(key, value), []),\n remove: useCallback((key: string) => store.delete(key), []),\n };\n}\n\n// ---------------------------------------------------------------------------\n// FUTURE HOOKS\n//\n// - useAppMessage — phone↔watch messaging goes through PebbleKit JS\n// on the phone side (`src/pkjs/index.js`).\n// - useLocation — GPS via phone proxy (one-shot).\n// ---------------------------------------------------------------------------\n"],"mappings":"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"}
|