react-pebble 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/lib/compiler.cjs +3 -0
  2. package/dist/lib/compiler.cjs.map +1 -0
  3. package/dist/lib/compiler.js +54 -0
  4. package/dist/lib/compiler.js.map +1 -0
  5. package/dist/lib/components.cjs +2 -0
  6. package/dist/lib/components.cjs.map +1 -0
  7. package/dist/lib/components.js +80 -0
  8. package/dist/lib/components.js.map +1 -0
  9. package/dist/lib/hooks.cjs +2 -0
  10. package/dist/lib/hooks.cjs.map +1 -0
  11. package/dist/lib/hooks.js +99 -0
  12. package/dist/lib/hooks.js.map +1 -0
  13. package/dist/lib/index.cjs +2 -0
  14. package/dist/lib/index.cjs.map +1 -0
  15. package/dist/lib/index.js +585 -0
  16. package/dist/lib/index.js.map +1 -0
  17. package/dist/lib/platform.cjs +2 -0
  18. package/dist/lib/platform.cjs.map +1 -0
  19. package/dist/lib/platform.js +52 -0
  20. package/dist/lib/platform.js.map +1 -0
  21. package/dist/lib/plugin.cjs +60 -0
  22. package/dist/lib/plugin.cjs.map +1 -0
  23. package/dist/lib/plugin.js +102 -0
  24. package/dist/lib/plugin.js.map +1 -0
  25. package/dist/lib/src/compiler/index.d.ts +40 -0
  26. package/dist/lib/src/components/index.d.ts +129 -0
  27. package/dist/lib/src/hooks/index.d.ts +75 -0
  28. package/dist/lib/src/index.d.ts +36 -0
  29. package/dist/lib/src/pebble-dom-shim.d.ts +45 -0
  30. package/dist/lib/src/pebble-dom.d.ts +59 -0
  31. package/dist/lib/src/pebble-output.d.ts +44 -0
  32. package/dist/lib/src/pebble-reconciler.d.ts +16 -0
  33. package/dist/lib/src/pebble-render.d.ts +31 -0
  34. package/dist/lib/src/platform.d.ts +30 -0
  35. package/dist/lib/src/plugin/index.d.ts +20 -0
  36. package/package.json +90 -0
  37. package/scripts/compile-to-piu.ts +1794 -0
  38. package/scripts/deploy.sh +46 -0
  39. package/src/compiler/index.ts +114 -0
  40. package/src/components/index.tsx +280 -0
  41. package/src/hooks/index.ts +311 -0
  42. package/src/index.ts +126 -0
  43. package/src/pebble-dom-shim.ts +266 -0
  44. package/src/pebble-dom.ts +190 -0
  45. package/src/pebble-output.ts +310 -0
  46. package/src/pebble-reconciler.ts +54 -0
  47. package/src/pebble-render.ts +311 -0
  48. package/src/platform.ts +50 -0
  49. package/src/plugin/index.ts +274 -0
  50. package/src/types/moddable.d.ts +156 -0
@@ -0,0 +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.basename)((0,t.resolve)(o,n.entry)).replace(/\.[jt]sx?$/,``),c=(0,t.resolve)(i,`../../scripts/compile-to-piu.ts`);if(!(0,r.existsSync)(c))throw Error(`Compiler script not found at ${c}`);a(`Compiling ${s}...`);let l={...process.env,EXAMPLE:s};n.settleMs&&(l.SETTLE_MS=String(n.settleMs)),n.platform&&(l.PEBBLE_PLATFORM=n.platform);let u,d;try{u=(0,e.execSync)(`npx tsx "${c}"`,{cwd:o,env:l,encoding:`utf-8`,timeout:3e4,stdio:[`pipe`,`pipe`,`pipe`]});try{d=(0,e.execSync)(`npx tsx "${c}" 2>&1 1>/dev/null`,{cwd:o,env:l,encoding:`utf-8`,timeout:3e4})}catch{d=``}}catch(e){let t=e;throw Error(`Compilation failed for ${s}: ${t.stderr??t.message}`)}let f=d.includes(`Button bindings discovered:`)&&!d.includes(`Button bindings discovered: 0`),p=[],m=d.match(/useMessage detected: key="([^"]+)"/);return m?.[1]&&p.push(m[1]),a(`Compiled ${s}: ${u.split(`
2
+ `).length} lines, buttons=${f}, messageKeys=[${p.join(`,`)}]`),{code:u,hasButtons:f,messageKeys:p,diagnostics:d}}exports.compileToPiu=a;
3
+ //# sourceMappingURL=compiler.cjs.map
@@ -0,0 +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 to an example name (for backwards compat with the script)\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: exampleName,\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 log(`Compiled ${exampleName}: ${code.split('\\n').length} lines, buttons=${hasButtons}, messageKeys=[${messageKeys.join(',')}]`);\n\n return { code, hasButtons, messageKeys, diagnostics };\n}\n"],"mappings":"yKAkBA,IAAM,GAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,CAA8C,IAAI,CAAC,CA+BzD,eAAsB,EAAa,EAAiD,CAClF,IAAM,EAAM,EAAQ,aAAiB,IAC/B,EAAc,EAAQ,aAAe,QAAQ,KAAK,CAIlD,GAAA,EAAA,EAAA,WAAA,EAAA,EAAA,SADoB,EAAa,EAAQ,MAAM,CACd,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,CAKxE,OAJI,IAAW,IAAI,EAAY,KAAK,EAAS,GAAG,CAEhD,EAAI,YAAY,EAAY,IAAI,EAAK,MAAM;EAAK,CAAC,OAAO,kBAAkB,EAAW,iBAAiB,EAAY,KAAK,IAAI,CAAC,GAAG,CAExH,CAAE,OAAM,aAAY,cAAa,cAAa"}
@@ -0,0 +1,54 @@
1
+ import { execSync as e } from "node:child_process";
2
+ import { basename as t, dirname as n, resolve as r } from "node:path";
3
+ import { fileURLToPath as i } from "node:url";
4
+ import { existsSync as a } from "node:fs";
5
+ //#region src/compiler/index.ts
6
+ var o = n(i(import.meta.url));
7
+ async function s(n) {
8
+ let i = n.logger ?? (() => {}), s = n.projectRoot ?? process.cwd(), c = t(r(s, n.entry)).replace(/\.[jt]sx?$/, ""), l = r(o, "../../scripts/compile-to-piu.ts");
9
+ if (!a(l)) throw Error(`Compiler script not found at ${l}`);
10
+ i(`Compiling ${c}...`);
11
+ let u = {
12
+ ...process.env,
13
+ EXAMPLE: c
14
+ };
15
+ n.settleMs && (u.SETTLE_MS = String(n.settleMs)), n.platform && (u.PEBBLE_PLATFORM = n.platform);
16
+ let d, f;
17
+ try {
18
+ d = e(`npx tsx "${l}"`, {
19
+ cwd: s,
20
+ env: u,
21
+ encoding: "utf-8",
22
+ timeout: 3e4,
23
+ stdio: [
24
+ "pipe",
25
+ "pipe",
26
+ "pipe"
27
+ ]
28
+ });
29
+ try {
30
+ f = e(`npx tsx "${l}" 2>&1 1>/dev/null`, {
31
+ cwd: s,
32
+ env: u,
33
+ encoding: "utf-8",
34
+ timeout: 3e4
35
+ });
36
+ } catch {
37
+ f = "";
38
+ }
39
+ } catch (e) {
40
+ let t = e;
41
+ throw Error(`Compilation failed for ${c}: ${t.stderr ?? t.message}`);
42
+ }
43
+ let p = f.includes("Button bindings discovered:") && !f.includes("Button bindings discovered: 0"), m = [], h = f.match(/useMessage detected: key="([^"]+)"/);
44
+ return h?.[1] && m.push(h[1]), i(`Compiled ${c}: ${d.split("\n").length} lines, buttons=${p}, messageKeys=[${m.join(",")}]`), {
45
+ code: d,
46
+ hasButtons: p,
47
+ messageKeys: m,
48
+ diagnostics: f
49
+ };
50
+ }
51
+ //#endregion
52
+ export { s as compileToPiu };
53
+
54
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +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 to an example name (for backwards compat with the script)\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: exampleName,\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 log(`Compiled ${exampleName}: ${code.split('\\n').length} lines, buttons=${hasButtons}, messageKeys=[${messageKeys.join(',')}]`);\n\n return { code, hasButtons, messageKeys, diagnostics };\n}\n"],"mappings":";;;;;AAkBA,IAAM,IAAY,EAAQ,EAAc,OAAO,KAAK,IAAI,CAAC;AA+BzD,eAAsB,EAAa,GAAiD;CAClF,IAAM,IAAM,EAAQ,iBAAiB,KAC/B,IAAc,EAAQ,eAAe,QAAQ,KAAK,EAIlD,IAAc,EADF,EAAQ,GAAa,EAAQ,MAAM,CACd,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;AAKxE,QAJI,IAAW,MAAI,EAAY,KAAK,EAAS,GAAG,EAEhD,EAAI,YAAY,EAAY,IAAI,EAAK,MAAM,KAAK,CAAC,OAAO,kBAAkB,EAAW,iBAAiB,EAAY,KAAK,IAAI,CAAC,GAAG,EAExH;EAAE;EAAM;EAAY;EAAa;EAAa"}
@@ -0,0 +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-statusbar`,t)}function l(t){return e.createElement(`pbl-actionbar`,t)}function u({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 d({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=l,exports.Badge=d,exports.Card=u,exports.Circle=r,exports.Group=s,exports.Image=o,exports.Line=a,exports.Rect=n,exports.StatusBar=c,exports.Text=i,exports.Window=t;
2
+ //# sourceMappingURL=components.cjs.map
@@ -0,0 +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"}
@@ -0,0 +1,80 @@
1
+ import { h as e } from "preact";
2
+ //#region src/components/index.tsx
3
+ var t = { createElement: e };
4
+ function n({ children: e, ...n }) {
5
+ return t.createElement("pbl-group", n, e);
6
+ }
7
+ function r({ children: e, ...n }) {
8
+ return t.createElement("pbl-rect", n, e);
9
+ }
10
+ function i(e) {
11
+ return t.createElement("pbl-circle", e);
12
+ }
13
+ function a({ children: e, ...n }) {
14
+ return t.createElement("pbl-text", n, e);
15
+ }
16
+ function o(e) {
17
+ return t.createElement("pbl-line", e);
18
+ }
19
+ function s(e) {
20
+ return t.createElement("pbl-image", e);
21
+ }
22
+ function c({ children: e, ...n }) {
23
+ return t.createElement("pbl-group", n, e);
24
+ }
25
+ function l(e) {
26
+ return t.createElement("pbl-statusbar", e);
27
+ }
28
+ function u(e) {
29
+ return t.createElement("pbl-actionbar", e);
30
+ }
31
+ function d({ title: e, body: n, titleFont: r, bodyFont: i, x: a = 0, y: o = 0, w: s = 144, ...c }) {
32
+ return t.createElement("pbl-group", {
33
+ x: a,
34
+ y: o,
35
+ ...c
36
+ }, t.createElement("pbl-rect", {
37
+ x: 0,
38
+ y: 0,
39
+ w: s,
40
+ h: 28,
41
+ fill: "white"
42
+ }), t.createElement("pbl-text", {
43
+ x: 4,
44
+ y: 2,
45
+ w: s - 8,
46
+ h: 28,
47
+ font: r ?? "gothic18Bold",
48
+ color: "black"
49
+ }, e), n ? t.createElement("pbl-text", {
50
+ x: 4,
51
+ y: 32,
52
+ w: s - 8,
53
+ h: 120,
54
+ font: i ?? "gothic14",
55
+ color: "white"
56
+ }, n) : null);
57
+ }
58
+ function f({ x: e = 0, y: n = 0, r = 12, color: i = "red", textColor: a = "white", children: o }) {
59
+ return t.createElement("pbl-group", {
60
+ x: e,
61
+ y: n
62
+ }, t.createElement("pbl-circle", {
63
+ x: 0,
64
+ y: 0,
65
+ r,
66
+ fill: i
67
+ }), t.createElement("pbl-text", {
68
+ x: 0,
69
+ y: r - 8,
70
+ w: r * 2,
71
+ h: 16,
72
+ font: "gothic14Bold",
73
+ color: a,
74
+ align: "center"
75
+ }, o));
76
+ }
77
+ //#endregion
78
+ export { u as ActionBar, f as Badge, d as Card, i as Circle, c as Group, s as Image, o as Line, r as Rect, l as StatusBar, a as Text, n as Window };
79
+
80
+ //# sourceMappingURL=components.js.map
@@ -0,0 +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"}
@@ -0,0 +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=setInterval(()=>i(new Date),e);return()=>clearInterval(t)},[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}}exports.ButtonRegistry=c,exports.PebbleAppContext=o,exports._restoreUseState=a,exports._setUseStateImpl=i,exports.useApp=s,exports.useButton=l,exports.useFormattedTime=f,exports.useInterval=p,exports.useListNavigation=m,exports.useLongButton=u,exports.useMessage=h,exports.useState=r,exports.useTime=d;
2
+ //# sourceMappingURL=hooks.cjs.map
@@ -0,0 +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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"}
@@ -0,0 +1,99 @@
1
+ import { createContext as e } from "preact";
2
+ import { useCallback as t, useContext as n, useEffect as r, useRef as i, useState as a } from "preact/hooks";
3
+ //#region src/hooks/index.ts
4
+ var o = a;
5
+ function s(e) {
6
+ return o(e);
7
+ }
8
+ function c(e) {
9
+ o = e;
10
+ }
11
+ function l() {
12
+ o = a;
13
+ }
14
+ var u = e(null);
15
+ function d() {
16
+ let e = n(u);
17
+ if (!e) throw Error("useApp must be used inside a react-pebble render tree");
18
+ return e;
19
+ }
20
+ var f = {
21
+ _listeners: /* @__PURE__ */ new Map(),
22
+ subscribe(e, t) {
23
+ let n = this._listeners.get(e);
24
+ n || (n = /* @__PURE__ */ new Set(), this._listeners.set(e, n)), n.add(t);
25
+ },
26
+ unsubscribe(e, t) {
27
+ let n = this._listeners.get(e);
28
+ n && (n.delete(t), n.size === 0 && this._listeners.delete(e));
29
+ },
30
+ emit(e) {
31
+ let t = this._listeners.get(e);
32
+ if (t) for (let e of t) e();
33
+ }
34
+ };
35
+ function p(e, t) {
36
+ let n = i(t);
37
+ n.current = t, r(() => {
38
+ let t = () => n.current();
39
+ return f.subscribe(e, t), () => {
40
+ f.unsubscribe(e, t);
41
+ };
42
+ }, [e]);
43
+ }
44
+ function m(e, t) {
45
+ let n = i(t);
46
+ n.current = t, r(() => {
47
+ let t = () => n.current(), r = `long_${e}`;
48
+ return f.subscribe(r, t), () => f.unsubscribe(r, t);
49
+ }, [e]);
50
+ }
51
+ function h(e = 1e3) {
52
+ let [t, n] = s(() => /* @__PURE__ */ new Date());
53
+ return r(() => {
54
+ let t = setInterval(() => n(/* @__PURE__ */ new Date()), e);
55
+ return () => clearInterval(t);
56
+ }, [e]), t;
57
+ }
58
+ function g(e = "HH:mm") {
59
+ let t = h(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;
60
+ 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;
61
+ }
62
+ function _(e, t) {
63
+ let n = i(e);
64
+ n.current = e, r(() => {
65
+ if (t === null) return;
66
+ let e = setInterval(() => n.current(), t);
67
+ return () => clearInterval(e);
68
+ }, [t]);
69
+ }
70
+ function v(e, n = {}) {
71
+ let { wrap: r = !1 } = n, [i, a] = s(0), o = t(() => {
72
+ a((t) => t >= e.length - 1 ? r ? 0 : t : t + 1);
73
+ }, [e.length, r]), c = t(() => {
74
+ a((t) => t <= 0 ? r ? e.length - 1 : t : t - 1);
75
+ }, [e.length, r]);
76
+ return p("down", o), p("up", c), {
77
+ index: i,
78
+ item: e[i],
79
+ next: o,
80
+ prev: c,
81
+ setIndex: a
82
+ };
83
+ }
84
+ function y(e) {
85
+ let [t, n] = s(null), [i, a] = s(!0);
86
+ return r(() => {
87
+ let t = setTimeout(() => {
88
+ n(e.mockData), a(!1);
89
+ }, e.mockDelay ?? 100);
90
+ return () => clearTimeout(t);
91
+ }, []), {
92
+ data: t,
93
+ loading: i
94
+ };
95
+ }
96
+ //#endregion
97
+ export { f as ButtonRegistry, u as PebbleAppContext, l as _restoreUseState, c as _setUseStateImpl, d as useApp, p as useButton, g as useFormattedTime, _ as useInterval, v as useListNavigation, m as useLongButton, y as useMessage, s as useState, h as useTime };
98
+
99
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","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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"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./platform.cjs`),t=require(`./hooks.cjs`),n=require(`./components.cjs`);let r=require(`preact`);var i=new Set([`pbl-root`,`pbl-rect`,`pbl-circle`,`pbl-text`,`pbl-line`,`pbl-image`,`pbl-group`,`pbl-statusbar`,`pbl-actionbar`]),a=1;function o(e){return{id:a++,type:e,props:{},children:[],parent:null,onRender:null,onComputeLayout:null,_dirty:!0}}function s(e){return{id:a++,type:`#text`,value:e,parent:null}}function c(e,t){t.parent&&u(t.parent,t),t.parent=e,e.children.push(t)}function l(e,t,n){t.parent&&u(t.parent,t),t.parent=e;let r=e.children.indexOf(n);r>=0?e.children.splice(r,0,t):e.children.push(t)}function u(e,t){e.children=e.children.filter(e=>e!==t),t.parent=null}function d(e,t,n){n===void 0?delete e.props[t]:e.props[t]=n}function f(e,t){e.value=t}function p(e){return e.type===`#text`?e.value:e.children.map(p).join(``)}function m(e,t,n=0){if(t(e,n),e.type!==`#text`)for(let r of e.children)m(r,t,n+1)}function h(e){let t=e;for(;t.parent;)t=t.parent;return t}var g={black:{r:0,g:0,b:0},white:{r:255,g:255,b:255},red:{r:255,g:0,b:0},green:{r:0,g:255,b:0},blue:{r:0,g:0,b:255},yellow:{r:255,g:255,b:0},orange:{r:255,g:128,b:0},cyan:{r:0,g:255,b:255},magenta:{r:255,g:0,b:255},clear:{r:0,g:0,b:0},lightGray:{r:192,g:192,b:192},darkGray:{r:64,g:64,b:64}},_={gothic14:{family:`Bitham-Black`,size:14},gothic14Bold:{family:`Bitham-Black`,size:14},gothic18:{family:`Bitham-Black`,size:18},gothic18Bold:{family:`Bitham-Black`,size:18},gothic24:{family:`Bitham-Black`,size:24},gothic24Bold:{family:`Bitham-Black`,size:24},gothic28:{family:`Bitham-Black`,size:28},gothic28Bold:{family:`Bitham-Black`,size:28},bitham30Black:{family:`Bitham-Black`,size:30},bitham42Bold:{family:`Bitham-Black`,size:42},bitham42Light:{family:`Bitham-Black`,size:42},bitham34MediumNumbers:{family:`Bitham-Black`,size:34},bitham42MediumNumbers:{family:`Bitham-Black`,size:42}},v=`gothic18`;function y(e,t){let n=e[t];return typeof n==`number`?n:0}function b(e,t){let n=e[t];return typeof n==`string`?n:void 0}var x=class{poco;colorCache=new Map;fontCache=new Map;constructor(e){this.poco=e}render(e,t={}){let{poco:n}=this,r=t.dirty;r?n.begin(r.x,r.y,r.w,r.h):n.begin();let i=this.getColor(t.backgroundColor??`black`);n.fillRectangle(i,0,0,n.width,n.height),this.renderChildren(e,0,0),n.end()}getColor(e){let t=e??`black`,n=this.colorCache.get(t);if(n!==void 0)return n;let r=g[t]??g.white,i=this.poco.makeColor(r.r,r.g,r.b);return this.colorCache.set(t,i),i}getFont(e){let t=e??v,n=this.fontCache.get(t);if(n!==void 0)return n;let r=_[t]??_[v],i=this.poco.Font,a=new i(r.family,r.size);return this.fontCache.set(t,a),a}renderChildren(e,t,n){for(let r of e.children)r.type!==`#text`&&this.renderNode(r,t,n)}renderNode(e,t,n){let r=e.props;if(r._hidden)return;let i=y(r,`x`)+t,a=y(r,`y`)+n;switch(e.type){case`pbl-rect`:{let t=y(r,`w`)||y(r,`width`),n=y(r,`h`)||y(r,`height`),o=b(r,`fill`),s=b(r,`stroke`);if(o&&this.poco.fillRectangle(this.getColor(o),i,a,t,n),s){let e=y(r,`strokeWidth`)||1,o=this.getColor(s);this.poco.fillRectangle(o,i,a,t,e),this.poco.fillRectangle(o,i,a+n-e,t,e),this.poco.fillRectangle(o,i,a,e,n),this.poco.fillRectangle(o,i+t-e,a,e,n)}this.renderChildren(e,i,a);break}case`pbl-text`:{let t=p(e);if(!t)break;let n=y(r,`w`)||y(r,`width`)||this.poco.width-i,o=this.getFont(b(r,`font`)),s=this.getColor(b(r,`color`)??`white`),c=b(r,`align`)??`left`,l=i;if(c===`center`||c===`right`){let e=this.poco.getTextWidth(t,o);l=c===`center`?i+Math.floor((n-e)/2):i+n-e}this.poco.drawText(t,o,s,l,a);break}case`pbl-line`:{let e=y(r,`x2`)+t,o=y(r,`y2`)+n,s=this.getColor(b(r,`color`)??b(r,`stroke`)??`white`),c=y(r,`strokeWidth`)||1;if(i===e){let e=Math.min(a,o),t=Math.abs(o-a)||1;this.poco.fillRectangle(s,i,e,c,t)}else if(a===o){let t=Math.min(i,e),n=Math.abs(e-i)||1;this.poco.fillRectangle(s,t,a,n,c)}break}case`pbl-circle`:break;case`pbl-image`:{let e=r.bitmap;e&&this.poco.drawBitmap(e,i,a);break}case`pbl-group`:this.renderChildren(e,i,a);break;case`pbl-statusbar`:case`pbl-actionbar`:break;case`pbl-root`:this.renderChildren(e,t,n);break}}};function S(e){return e&&e in g?e:`black`}function C(e){return e&&e in _?e:v}function w(e){let t=e.childNodes;e.firstChild=t[0]??null;for(let n=0;n<t.length;n++){let r=t[n];r.parentNode=e,r.nextSibling=t[n+1]??null}}function T(e){let t=e;if(!i.has(t))throw Error(`react-pebble: unknown element tag "${e}"`);let n=o(t);return{nodeType:1,nodeName:e.toUpperCase(),localName:e,tagName:e.toUpperCase(),_pbl:n,attributes:{},parentNode:null,childNodes:[],firstChild:null,nextSibling:null,appendChild(e){return e.parentNode&&e.parentNode.removeChild(e),this.childNodes.push(e),c(n,e._pbl),w(this),e},insertBefore(e,t){if(e.parentNode&&e.parentNode.removeChild(e),t===null)return this.appendChild(e);let r=this.childNodes.indexOf(t);return r<0?this.appendChild(e):(this.childNodes.splice(r,0,e),l(n,e._pbl,t._pbl),w(this),e)},removeChild(e){let t=this.childNodes.indexOf(e);return t>=0&&this.childNodes.splice(t,1),u(n,e._pbl),e.parentNode=null,e.nextSibling=null,w(this),e},remove(){this.parentNode&&this.parentNode.removeChild(this)},setAttribute(e,t){this.attributes[e]=t,d(n,e,t)},removeAttribute(e){delete this.attributes[e],d(n,e,void 0)},getAttribute(e){return this.attributes[e]},addEventListener(e,t){let r=`on${e[0]?.toUpperCase()}${e.slice(1)}`;this.attributes[r]=t,d(n,r,t)},removeEventListener(e,t){let r=`on${e[0]?.toUpperCase()}${e.slice(1)}`;delete this.attributes[r],d(n,r,void 0)}}}function E(e){let t=s(e),n={nodeType:3,nodeName:`#text`,_pbl:t,data:e,nodeValue:e,textContent:e,parentNode:null,childNodes:[],firstChild:null,nextSibling:null};return Object.defineProperty(n,`data`,{get(){return t.value},set(e){f(t,e)}}),Object.defineProperty(n,`nodeValue`,{get(){return t.value},set(e){f(t,e)}}),Object.defineProperty(n,`textContent`,{get(){return t.value},set(e){f(t,e)}}),n}var D={createElement:T,createElementNS:(e,t)=>T(t),createTextNode:E};function O(){let e=T(`pbl-root`);return e.ownerDocument=D,e}typeof document>`u`&&(globalThis.document=D);function k(){let e=O();return{shimRoot:e,pblRoot:e._pbl}}function A(e,t){(0,r.render)(e,t.shimRoot)}function j(e){(0,r.render)(null,e.shimRoot)}var M={createContainer:k,updateContainer:A,unmountContainer:j},N=class{width;height;Font;constructor(e,t,n){this.log=n,this.width=e,this.height=t,this.Font=class{name;size;height;constructor(e,t){this.name=e,this.size=t,this.height=t}}}begin(e,t,n,r){this.log.push({op:`begin`,x:e,y:t,width:n,height:r})}end(){this.log.push({op:`end`})}continue(e,t,n,r){this.log.push({op:`continue`,x:e,y:t,width:n,height:r})}clip(e,t,n,r){this.log.push({op:`clip`,x:e,y:t,width:n,height:r})}origin(e,t){this.log.push({op:`origin`,x:e,y:t})}makeColor(e,t,n){return(e&255)<<16|(t&255)<<8|n&255}fillRectangle(e,t,n,r,i){this.log.push({op:`fillRectangle`,color:e,x:t,y:n,width:r,height:i})}blendRectangle(e,t,n,r,i,a){this.log.push({op:`blendRectangle`,color:e,blend:t,x:n,y:r,width:i,height:a})}drawPixel(e,t,n){this.log.push({op:`drawPixel`,color:e,x:t,y:n})}drawBitmap(e,t,n){this.log.push({op:`drawBitmap`,x:t,y:n})}drawMonochrome(e,t,n,r,i){this.log.push({op:`drawMonochrome`,fore:t,back:n,x:r,y:i})}drawText(e,t,n,r,i){this.log.push({op:`drawText`,text:e,font:t,color:n,x:r,y:i})}getTextWidth(e,t){let n=t.size??14;return Math.round(e.length*n*.6)}};function P(e,t){return typeof screen<`u`&&screen&&t?{poco:new t(screen),info:{isReal:!0,platform:`alloy`,screenWidth:screen.width,screenHeight:screen.height}}:{poco:new N(200,228,e),info:{isReal:!1,platform:`mock`,screenWidth:200,screenHeight:228}}}function F(){if(typeof watch>`u`||!watch)return()=>void 0;let e=e=>{if(typeof e!=`string`)return;let t=e.toLowerCase();if(t===`up`||t===`down`||t===`select`||t===`back`)return t},n=n=>{let r=e(n?.button);r&&t.ButtonRegistry.emit(r)},r=n=>{let r=e(n?.button);r&&t.ButtonRegistry.emit(`long_${r}`)};return watch.addEventListener(`button`,n),watch.addEventListener(`buttonClick`,n),watch.addEventListener(`longClick`,r),()=>{typeof watch>`u`||!watch||(watch.removeEventListener(`button`,n),watch.removeEventListener(`buttonClick`,n),watch.removeEventListener(`longClick`,r))}}function I(e){typeof Promise<`u`?Promise.resolve().then(e):setTimeout(e,0)}function L(e,t={}){let n=[],r=k(),{poco:i,info:a}=P(n,t.poco),o=new x(i),s=!1,c=()=>{s=!1,n.length=0,o.render(r.pblRoot,{backgroundColor:t.backgroundColor})},l=()=>{s||(s=!0,I(c))},u=t,d=u._commit??u.__c,f=(e,t)=>{d&&d(e,t),l()};u._commit=f,u.__c=f,A(e,r),c();let p=F();return{update(e){A(e,r),l()},unmount(){j(r),u._commit=d,u.__c=d,p()},get platform(){return a},get drawLog(){return n},get _root(){return r.pblRoot}}}exports.ActionBar=n.ActionBar,exports.Badge=n.Badge,exports.ButtonRegistry=t.ButtonRegistry,exports.COLOR_PALETTE=g,exports.Card=n.Card,exports.Circle=n.Circle,exports.ELEMENT_TYPES=i,exports.FONT_PALETTE=_,exports.Group=n.Group,exports.Image=n.Image,exports.Line=n.Line,exports.PLATFORMS=e.PLATFORMS,exports.PebbleAppContext=t.PebbleAppContext,exports.PocoRenderer=x,exports.Rect=n.Rect,exports.SCREEN=e.SCREEN,exports.StatusBar=n.StatusBar,exports.Text=n.Text,exports.Window=n.Window,exports._setPlatform=e._setPlatform,exports.appendChildNode=c,exports.createNode=o,exports.createTextNode=s,exports.findRoot=h,exports.getTextContent=p,exports.insertBeforeNode=l,exports.reconciler=M,exports.removeChildNode=u,exports.render=L,exports.resolveColorName=S,exports.resolveFontName=C,exports.setAttribute=d,exports.setTextNodeValue=f,exports.useApp=t.useApp,exports.useButton=t.useButton,exports.useFormattedTime=t.useFormattedTime,exports.useInterval=t.useInterval,exports.useListNavigation=t.useListNavigation,exports.useLongButton=t.useLongButton,exports.useTime=t.useTime,exports.walkTree=m;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/pebble-dom.ts","../../src/pebble-output.ts","../../src/pebble-dom-shim.ts","../../src/pebble-reconciler.ts","../../src/pebble-render.ts"],"sourcesContent":["/**\n * pebble-dom.ts — Virtual DOM layer for React Pebble\n *\n * Modeled after Ink's dom.ts but stripped of Yoga layout.\n * Pebble uses absolute positioning (x, y, w, h) rather than flexbox,\n * so nodes are just plain JS objects with type, props, and children.\n *\n * Each node type maps to a Pebble drawing primitive:\n * pbl-root → Container root (the Window)\n * pbl-rect → fillRect / drawRect\n * pbl-circle → fillCircle / drawCircle\n * pbl-text → drawText\n * pbl-line → drawLine\n * pbl-image → drawImage (bitmap)\n * pbl-group → Logical grouping with offset (no draw call)\n * #text → Raw text content (only valid inside pbl-text)\n */\n\n// ---------------------------------------------------------------------------\n// Element types\n// ---------------------------------------------------------------------------\n\nexport type ElementType =\n | 'pbl-root'\n | 'pbl-rect'\n | 'pbl-circle'\n | 'pbl-text'\n | 'pbl-line'\n | 'pbl-image'\n | 'pbl-group'\n | 'pbl-statusbar'\n | 'pbl-actionbar';\n\nexport const ELEMENT_TYPES: ReadonlySet<ElementType> = new Set<ElementType>([\n 'pbl-root',\n 'pbl-rect',\n 'pbl-circle',\n 'pbl-text',\n 'pbl-line',\n 'pbl-image',\n 'pbl-group',\n 'pbl-statusbar',\n 'pbl-actionbar',\n]);\n\n// ---------------------------------------------------------------------------\n// Node shapes\n// ---------------------------------------------------------------------------\n\n/**\n * Loose prop bag — element-specific shapes are documented on each component\n * wrapper, but the reconciler treats them uniformly.\n */\nexport type NodeProps = Record<string, unknown> & {\n _hidden?: boolean;\n};\n\nexport interface DOMElement {\n id: number;\n type: ElementType;\n props: NodeProps;\n children: Array<DOMElement | TextNode>;\n parent: DOMElement | null;\n /** Called by the reconciler after each commit; wired up by `render()`. */\n onRender: (() => void) | null;\n /** Optional layout pass; we currently don't use this. */\n onComputeLayout: (() => void) | null;\n /** Internal dirty flag (currently informational only). */\n _dirty: boolean;\n}\n\nexport interface TextNode {\n id: number;\n type: '#text';\n value: string;\n parent: DOMElement | null;\n /** Saved value while the node is hidden by Suspense. */\n _hiddenValue?: string;\n}\n\nexport type AnyNode = DOMElement | TextNode;\n\n// ---------------------------------------------------------------------------\n// Node creation\n// ---------------------------------------------------------------------------\n\nlet nextNodeId = 1;\n\nexport function createNode(type: ElementType): DOMElement {\n return {\n id: nextNodeId++,\n type,\n props: {},\n children: [],\n parent: null,\n onRender: null,\n onComputeLayout: null,\n _dirty: true,\n };\n}\n\nexport function createTextNode(text: string): TextNode {\n return {\n id: nextNodeId++,\n type: '#text',\n value: text,\n parent: null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tree manipulation\n// ---------------------------------------------------------------------------\n\nexport function appendChildNode(parent: DOMElement, child: AnyNode): void {\n if (child.parent) {\n removeChildNode(child.parent, child);\n }\n child.parent = parent;\n parent.children.push(child);\n}\n\nexport function insertBeforeNode(\n parent: DOMElement,\n child: AnyNode,\n beforeChild: AnyNode,\n): void {\n if (child.parent) {\n removeChildNode(child.parent, child);\n }\n child.parent = parent;\n const idx = parent.children.indexOf(beforeChild);\n if (idx >= 0) {\n parent.children.splice(idx, 0, child);\n } else {\n parent.children.push(child);\n }\n}\n\nexport function removeChildNode(parent: DOMElement, child: AnyNode): void {\n parent.children = parent.children.filter((c) => c !== child);\n child.parent = null;\n}\n\n// ---------------------------------------------------------------------------\n// Property / attribute helpers\n// ---------------------------------------------------------------------------\n\nexport function setAttribute(node: DOMElement, key: string, value: unknown): void {\n if (value === undefined) {\n delete node.props[key];\n } else {\n node.props[key] = value;\n }\n}\n\nexport function setTextNodeValue(node: TextNode, text: string): void {\n node.value = text;\n}\n\n// ---------------------------------------------------------------------------\n// Tree traversal helpers\n// ---------------------------------------------------------------------------\n\nexport function getTextContent(node: AnyNode): string {\n if (node.type === '#text') {\n return node.value;\n }\n return node.children.map(getTextContent).join('');\n}\n\nexport type Visitor = (node: AnyNode, depth: number) => void;\n\nexport function walkTree(root: AnyNode, visitor: Visitor, depth = 0): void {\n visitor(root, depth);\n if (root.type !== '#text') {\n for (const child of root.children) {\n walkTree(child, visitor, depth + 1);\n }\n }\n}\n\nexport function findRoot(node: AnyNode): DOMElement {\n let current: AnyNode = node;\n while (current.parent) {\n current = current.parent;\n }\n // The root is always a DOMElement (created via createNode).\n return current as DOMElement;\n}\n","/**\n * pebble-output.ts — Poco output layer for react-pebble on Pebble Alloy.\n *\n * Walks the virtual DOM tree (pebble-dom) and issues draw calls against\n * a `commodetto/Poco` renderer, which writes into the watch framebuffer.\n *\n * Key differences from a canvas-style API:\n *\n * - **No stateful color or font.** Every draw call takes the color (an int\n * produced by `poco.makeColor(r, g, b)`) and font (a `new poco.Font(...)`\n * object) as arguments. We maintain per-Poco caches so we resolve each\n * named color / font exactly once.\n * - **No native line, circle, or stroked rectangle.** Poco only has\n * `fillRectangle`, `drawText`, and bitmap draws. Outlines (stroke) are\n * emulated as four thin fillRectangles. Axis-aligned lines likewise.\n * Circles and diagonal lines would need the `commodetto/outline` extension\n * — they're currently stubbed (TODO for a later pass).\n * - **Text alignment is manual.** `drawText(text, font, color, x, y)` only\n * draws at a point. For center/right alignment we measure with\n * `getTextWidth` and compute the origin ourselves.\n */\n\nimport type Poco from 'commodetto/Poco';\nimport type { PocoColor, PocoFont } from 'commodetto/Poco';\nimport type { DOMElement, NodeProps } from './pebble-dom.js';\nimport { getTextContent } from './pebble-dom.js';\n\n// ---------------------------------------------------------------------------\n// Named palette — mapped to RGB, then resolved to PocoColor via a cache.\n// ---------------------------------------------------------------------------\n\nexport interface RGB {\n r: number;\n g: number;\n b: number;\n}\n\nexport const COLOR_PALETTE: Readonly<Record<string, RGB>> = {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n green: { r: 0, g: 255, b: 0 },\n blue: { r: 0, g: 0, b: 255 },\n yellow: { r: 255, g: 255, b: 0 },\n orange: { r: 255, g: 128, b: 0 },\n cyan: { r: 0, g: 255, b: 255 },\n magenta: { r: 255, g: 0, b: 255 },\n clear: { r: 0, g: 0, b: 0 },\n lightGray: { r: 192, g: 192, b: 192 },\n darkGray: { r: 64, g: 64, b: 64 },\n};\n\n// ---------------------------------------------------------------------------\n// Named font shortcuts — mapped to (family, size) pairs.\n//\n// The family names here need to match fonts available in the Moddable\n// manifest. The Alloy scaffold ships \"Bitham-Black\" as a default — we use\n// it for every logical font for now. Revisit once we add custom font\n// resources to the library manifest.\n// ---------------------------------------------------------------------------\n\nexport interface FontSpec {\n family: string;\n size: number;\n}\n\nexport const FONT_PALETTE: Readonly<Record<string, FontSpec>> = {\n gothic14: { family: 'Bitham-Black', size: 14 },\n gothic14Bold: { family: 'Bitham-Black', size: 14 },\n gothic18: { family: 'Bitham-Black', size: 18 },\n gothic18Bold: { family: 'Bitham-Black', size: 18 },\n gothic24: { family: 'Bitham-Black', size: 24 },\n gothic24Bold: { family: 'Bitham-Black', size: 24 },\n gothic28: { family: 'Bitham-Black', size: 28 },\n gothic28Bold: { family: 'Bitham-Black', size: 28 },\n bitham30Black: { family: 'Bitham-Black', size: 30 },\n bitham42Bold: { family: 'Bitham-Black', size: 42 },\n bitham42Light: { family: 'Bitham-Black', size: 42 },\n bitham34MediumNumbers: { family: 'Bitham-Black', size: 34 },\n bitham42MediumNumbers: { family: 'Bitham-Black', size: 42 },\n};\n\nconst DEFAULT_FONT_KEY = 'gothic18';\n\n// ---------------------------------------------------------------------------\n// Prop accessors — the DOM is loosely typed so we coerce here.\n// ---------------------------------------------------------------------------\n\nfunction num(p: NodeProps, key: string): number {\n const v = p[key];\n return typeof v === 'number' ? v : 0;\n}\n\nfunction str(p: NodeProps, key: string): string | undefined {\n const v = p[key];\n return typeof v === 'string' ? v : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Renderer options\n// ---------------------------------------------------------------------------\n\nexport interface RenderOptions {\n backgroundColor?: string;\n /**\n * Incremental update region. If provided, `poco.begin(x, y, w, h)` is used\n * to clip drawing to just that region. Otherwise the full frame is redrawn.\n */\n dirty?: { x: number; y: number; w: number; h: number };\n}\n\n// ---------------------------------------------------------------------------\n// PocoRenderer — owns a Poco instance plus color/font caches\n// ---------------------------------------------------------------------------\n\nexport class PocoRenderer {\n readonly poco: Poco;\n private readonly colorCache = new Map<string, PocoColor>();\n private readonly fontCache = new Map<string, PocoFont>();\n\n constructor(poco: Poco) {\n this.poco = poco;\n }\n\n /**\n * Render the full tree into a fresh frame.\n */\n render(rootNode: DOMElement, options: RenderOptions = {}): void {\n const { poco } = this;\n const dirty = options.dirty;\n\n if (dirty) {\n poco.begin(dirty.x, dirty.y, dirty.w, dirty.h);\n } else {\n poco.begin();\n }\n\n // Clear to background\n const bg = this.getColor(options.backgroundColor ?? 'black');\n poco.fillRectangle(bg, 0, 0, poco.width, poco.height);\n\n // Walk the tree\n this.renderChildren(rootNode, 0, 0);\n\n poco.end();\n }\n\n /** Resolve a color name (or pass-through int) to a PocoColor. */\n getColor(name: string | undefined): PocoColor {\n const key = name ?? 'black';\n const cached = this.colorCache.get(key);\n if (cached !== undefined) return cached;\n\n const rgb = COLOR_PALETTE[key] ?? COLOR_PALETTE.white!;\n const color = this.poco.makeColor(rgb.r, rgb.g, rgb.b);\n this.colorCache.set(key, color);\n return color;\n }\n\n /** Resolve a font name to a PocoFont (cached). */\n getFont(name: string | undefined): PocoFont {\n const key = name ?? DEFAULT_FONT_KEY;\n const cached = this.fontCache.get(key);\n if (cached !== undefined) return cached;\n\n const spec = FONT_PALETTE[key] ?? FONT_PALETTE[DEFAULT_FONT_KEY]!;\n // `poco.Font` is a constructor hanging off the Poco instance.\n const FontCtor = this.poco.Font;\n const font = new FontCtor(spec.family, spec.size);\n this.fontCache.set(key, font);\n return font;\n }\n\n // -------------------------------------------------------------------------\n // Private: node renderers\n // -------------------------------------------------------------------------\n\n private renderChildren(node: DOMElement, ox: number, oy: number): void {\n for (const child of node.children) {\n if (child.type === '#text') continue;\n this.renderNode(child, ox, oy);\n }\n }\n\n private renderNode(node: DOMElement, ox: number, oy: number): void {\n const p = node.props;\n if (p._hidden) return;\n\n const x = num(p, 'x') + ox;\n const y = num(p, 'y') + oy;\n\n switch (node.type) {\n case 'pbl-rect': {\n const w = num(p, 'w') || num(p, 'width');\n const h = num(p, 'h') || num(p, 'height');\n const fill = str(p, 'fill');\n const stroke = str(p, 'stroke');\n\n if (fill) {\n this.poco.fillRectangle(this.getColor(fill), x, y, w, h);\n }\n if (stroke) {\n // Emulate outline with four thin fill rects.\n const sw = num(p, 'strokeWidth') || 1;\n const c = this.getColor(stroke);\n this.poco.fillRectangle(c, x, y, w, sw); // top\n this.poco.fillRectangle(c, x, y + h - sw, w, sw); // bottom\n this.poco.fillRectangle(c, x, y, sw, h); // left\n this.poco.fillRectangle(c, x + w - sw, y, sw, h); // right\n }\n\n this.renderChildren(node, x, y);\n break;\n }\n\n case 'pbl-text': {\n const text = getTextContent(node);\n if (!text) break;\n\n const boxW = num(p, 'w') || num(p, 'width') || this.poco.width - x;\n const font = this.getFont(str(p, 'font'));\n const color = this.getColor(str(p, 'color') ?? 'white');\n const align = str(p, 'align') ?? 'left';\n\n let tx = x;\n if (align === 'center' || align === 'right') {\n const tw = this.poco.getTextWidth(text, font);\n if (align === 'center') {\n tx = x + Math.floor((boxW - tw) / 2);\n } else {\n tx = x + boxW - tw;\n }\n }\n\n this.poco.drawText(text, font, color, tx, y);\n break;\n }\n\n case 'pbl-line': {\n // Only axis-aligned lines are supported natively. Diagonals would\n // need the commodetto/outline extension — TODO.\n const x2 = num(p, 'x2') + ox;\n const y2 = num(p, 'y2') + oy;\n const c = this.getColor(str(p, 'color') ?? str(p, 'stroke') ?? 'white');\n const sw = num(p, 'strokeWidth') || 1;\n\n if (x === x2) {\n // Vertical\n const top = Math.min(y, y2);\n const h = Math.abs(y2 - y) || 1;\n this.poco.fillRectangle(c, x, top, sw, h);\n } else if (y === y2) {\n // Horizontal\n const left = Math.min(x, x2);\n const w = Math.abs(x2 - x) || 1;\n this.poco.fillRectangle(c, left, y, w, sw);\n }\n // else: diagonal line — silently skipped for now\n break;\n }\n\n case 'pbl-circle': {\n // TODO: implement via commodetto/outline extension (blendOutline)\n // or a Bresenham-style approximation. Stubbed for now.\n break;\n }\n\n case 'pbl-image': {\n const bitmap = p.bitmap;\n if (bitmap) {\n this.poco.drawBitmap(bitmap as never, x, y);\n }\n break;\n }\n\n case 'pbl-group': {\n this.renderChildren(node, x, y);\n break;\n }\n\n case 'pbl-statusbar':\n case 'pbl-actionbar': {\n // Alloy has no built-in status/action bar UI; these are no-ops for now.\n // An app that wants a status bar should draw its own.\n break;\n }\n\n case 'pbl-root': {\n this.renderChildren(node, ox, oy);\n break;\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Compatibility shims — still useful for mock-mode tests that want to\n// resolve a color name without constructing a Poco. These return *names*\n// rather than native handles.\n// ---------------------------------------------------------------------------\n\nexport function resolveColorName(color: string | undefined): string {\n if (!color) return 'black';\n return color in COLOR_PALETTE ? color : 'black';\n}\n\nexport function resolveFontName(font: string | undefined): string {\n if (!font) return DEFAULT_FONT_KEY;\n return font in FONT_PALETTE ? font : DEFAULT_FONT_KEY;\n}\n","/**\n * pebble-dom-shim.ts — A minimal DOM-like adapter over `pebble-dom` so\n * Preact's `render(vnode, parentDom)` can drive it.\n *\n * Preact's diff loop mutates a tree of DOM-shaped nodes by calling:\n * - document.createElement(tag)\n * - document.createTextNode(text)\n * - parent.appendChild(child), .insertBefore(child, ref), .removeChild(child)\n * - element.setAttribute(name, value), .removeAttribute(name)\n * - element.addEventListener(name, handler), .removeEventListener(...)\n * - element.nodeType, .nodeName, .parentNode, .childNodes, .firstChild,\n * .nextSibling\n *\n * This file implements the minimum surface Preact actually touches when\n * rendering into a headless tree. Each \"element\" carries a reference to the\n * underlying pebble-dom node so the renderer can walk both views\n * interchangeably.\n *\n * The shim is deliberately NOT a full undom — it only does what Preact 10\n * needs. If we trip on a missing method, add it here rather than including\n * undom (which would push the bundle back up).\n */\n\nimport type { AnyNode, DOMElement, ElementType, TextNode } from './pebble-dom.js';\nimport {\n ELEMENT_TYPES,\n appendChildNode,\n createNode,\n createTextNode,\n insertBeforeNode,\n removeChildNode,\n setAttribute,\n setTextNodeValue,\n} from './pebble-dom.js';\n\n// ---------------------------------------------------------------------------\n// DOM node interfaces the shim exposes to Preact\n// ---------------------------------------------------------------------------\n\ninterface ShimNodeBase {\n readonly nodeType: number;\n nodeName: string;\n parentNode: ShimElement | null;\n childNodes: ShimNode[];\n firstChild: ShimNode | null;\n nextSibling: ShimNode | null;\n /** The backing pebble-dom node. */\n readonly _pbl: AnyNode;\n}\n\nexport interface ShimElement extends ShimNodeBase {\n readonly nodeType: 1;\n readonly localName: string;\n readonly tagName: string;\n readonly _pbl: DOMElement;\n attributes: Record<string, unknown>;\n\n appendChild<T extends ShimNode>(child: T): T;\n insertBefore<T extends ShimNode>(child: T, ref: ShimNode | null): T;\n removeChild<T extends ShimNode>(child: T): T;\n remove(): void;\n\n setAttribute(name: string, value: unknown): void;\n removeAttribute(name: string): void;\n getAttribute(name: string): unknown;\n\n addEventListener(name: string, handler: (...args: unknown[]) => unknown): void;\n removeEventListener(name: string, handler: (...args: unknown[]) => unknown): void;\n}\n\nexport interface ShimText extends ShimNodeBase {\n readonly nodeType: 3;\n readonly _pbl: TextNode;\n data: string;\n nodeValue: string;\n textContent: string;\n}\n\nexport type ShimNode = ShimElement | ShimText;\n\n// ---------------------------------------------------------------------------\n// Shim factories\n// ---------------------------------------------------------------------------\n\nfunction linkSiblings(parent: ShimElement): void {\n const kids = parent.childNodes;\n parent.firstChild = kids[0] ?? null;\n for (let i = 0; i < kids.length; i++) {\n const node = kids[i]!;\n node.parentNode = parent;\n node.nextSibling = kids[i + 1] ?? null;\n }\n}\n\nexport function createShimElement(tag: string): ShimElement {\n // Accept both 'pbl-rect' and friendly aliases from components. Unknown\n // tags are rejected loudly so typos surface at render time.\n const pblType = tag as ElementType;\n if (!ELEMENT_TYPES.has(pblType)) {\n throw new Error(`react-pebble: unknown element tag \"${tag}\"`);\n }\n\n const pbl = createNode(pblType);\n\n const el: ShimElement = {\n nodeType: 1,\n nodeName: tag.toUpperCase(),\n localName: tag,\n tagName: tag.toUpperCase(),\n _pbl: pbl,\n attributes: {},\n parentNode: null,\n childNodes: [],\n firstChild: null,\n nextSibling: null,\n\n appendChild(child) {\n // Detach from any prior parent in the shim view\n if (child.parentNode) {\n child.parentNode.removeChild(child);\n }\n this.childNodes.push(child);\n appendChildNode(pbl, child._pbl);\n linkSiblings(this);\n return child;\n },\n\n insertBefore(child, ref) {\n if (child.parentNode) {\n child.parentNode.removeChild(child);\n }\n if (ref === null) {\n return this.appendChild(child);\n }\n const idx = this.childNodes.indexOf(ref);\n if (idx < 0) {\n return this.appendChild(child);\n }\n this.childNodes.splice(idx, 0, child);\n insertBeforeNode(pbl, child._pbl, ref._pbl);\n linkSiblings(this);\n return child;\n },\n\n removeChild(child) {\n const idx = this.childNodes.indexOf(child);\n if (idx >= 0) {\n this.childNodes.splice(idx, 1);\n }\n removeChildNode(pbl, child._pbl);\n child.parentNode = null;\n child.nextSibling = null;\n linkSiblings(this);\n return child;\n },\n\n remove() {\n if (this.parentNode) {\n this.parentNode.removeChild(this);\n }\n },\n\n setAttribute(name, value) {\n this.attributes[name] = value;\n setAttribute(pbl, name, value);\n },\n\n removeAttribute(name) {\n delete this.attributes[name];\n setAttribute(pbl, name, undefined);\n },\n\n getAttribute(name) {\n return this.attributes[name];\n },\n\n addEventListener(name, handler) {\n // Event handlers are stored in props (on* style) so the button wiring\n // in pebble-render.ts can find them the same way React props do.\n const key = `on${name[0]?.toUpperCase()}${name.slice(1)}`;\n this.attributes[key] = handler;\n setAttribute(pbl, key, handler);\n },\n\n removeEventListener(name, _handler) {\n const key = `on${name[0]?.toUpperCase()}${name.slice(1)}`;\n delete this.attributes[key];\n setAttribute(pbl, key, undefined);\n },\n };\n\n return el;\n}\n\nexport function createShimText(data: string): ShimText {\n const pbl = createTextNode(data);\n\n const node: ShimText = {\n nodeType: 3,\n nodeName: '#text',\n _pbl: pbl,\n data,\n nodeValue: data,\n textContent: data,\n parentNode: null,\n childNodes: [],\n firstChild: null,\n nextSibling: null,\n };\n\n // Keep pebble-dom in sync whenever data/nodeValue/textContent is assigned.\n // Preact mutates `.data` directly on text updates.\n Object.defineProperty(node, 'data', {\n get() {\n return pbl.value;\n },\n set(next: string) {\n setTextNodeValue(pbl, next);\n },\n });\n Object.defineProperty(node, 'nodeValue', {\n get() {\n return pbl.value;\n },\n set(next: string) {\n setTextNodeValue(pbl, next);\n },\n });\n Object.defineProperty(node, 'textContent', {\n get() {\n return pbl.value;\n },\n set(next: string) {\n setTextNodeValue(pbl, next);\n },\n });\n\n return node;\n}\n\n// ---------------------------------------------------------------------------\n// Shim \"document\" — what Preact reaches via `parentDom.ownerDocument` etc.\n// ---------------------------------------------------------------------------\n\nexport interface ShimDocument {\n createElement(tag: string): ShimElement;\n createElementNS(ns: string | null, tag: string): ShimElement;\n createTextNode(data: string): ShimText;\n}\n\nexport const shimDocument: ShimDocument = {\n createElement: createShimElement,\n createElementNS: (_ns, tag) => createShimElement(tag),\n createTextNode: createShimText,\n};\n\n// ---------------------------------------------------------------------------\n// Root container — the \"parent DOM\" handed to preact.render().\n// ---------------------------------------------------------------------------\n\nexport function createShimRoot(): ShimElement {\n const root = createShimElement('pbl-root');\n // Preact reads `ownerDocument` off the parent.\n (root as unknown as { ownerDocument: ShimDocument }).ownerDocument = shimDocument;\n return root;\n}\n","/**\n * pebble-reconciler.ts — Preact-backed reconciler for react-pebble.\n *\n * Replaces the old react-reconciler host config. We don't need a custom\n * reconciler at all with Preact — Preact's `render(vnode, parentDom)` does\n * the diffing, and we provide a DOM-shaped container via `pebble-dom-shim`\n * so Preact writes into our pebble-dom tree instead of a real DOM.\n *\n * The public surface (for pebble-render.ts) is:\n * - createReconcilerContainer() → a pair of root shim + pebble-dom root\n * - updateContainer(vnode, container) → runs preact.render()\n * - unmountContainer(container) → runs preact.render(null, ...)\n */\n\nimport { render as preactRender } from 'preact';\nimport type { ComponentChild } from 'preact';\nimport type { DOMElement } from './pebble-dom.js';\nimport { createShimRoot, shimDocument } from './pebble-dom-shim.js';\nimport type { ShimElement } from './pebble-dom-shim.js';\n\n// Preact's render() references `document` internally. In non-browser\n// environments (Node mock mode, Alloy XS) we shim it with our pebble-dom\n// adapter so Preact doesn't crash.\nif (typeof document === 'undefined') {\n (globalThis as unknown as { document: unknown }).document = shimDocument;\n}\n\nexport interface PebbleContainer {\n shimRoot: ShimElement;\n pblRoot: DOMElement;\n}\n\nexport function createContainer(): PebbleContainer {\n const shimRoot = createShimRoot();\n return {\n shimRoot,\n pblRoot: shimRoot._pbl,\n };\n}\n\nexport function updateContainer(vnode: ComponentChild, container: PebbleContainer): void {\n preactRender(vnode, container.shimRoot as unknown as Element);\n}\n\nexport function unmountContainer(container: PebbleContainer): void {\n preactRender(null, container.shimRoot as unknown as Element);\n}\n\n// For backwards-compat with the old default export pattern.\nexport default {\n createContainer,\n updateContainer,\n unmountContainer,\n};\n","/**\n * pebble-render.ts — Entry point for react-pebble on Pebble Alloy.\n *\n * Bridges Preact's output (via a DOM-shim over pebble-dom) to Moddable's\n * Poco renderer, which draws into the watch framebuffer. Also hosts the\n * Node mock path used for unit tests and local development.\n *\n * Platform detection is via `typeof screen`:\n * - screen exists → Alloy/XS runtime → real Poco draws\n * - screen undefined → Node → mock Poco records calls to an in-memory log\n */\n\nimport { options } from 'preact';\nimport type { ComponentChild } from 'preact';\nimport type Poco from 'commodetto/Poco';\nimport type { PocoBitmap, PocoColor, PocoFont } from 'commodetto/Poco';\nimport type { DOMElement } from './pebble-dom.js';\nimport { PocoRenderer } from './pebble-output.js';\nimport type { PebbleButton, PebbleButtonHandler } from './hooks/index.js';\nimport { ButtonRegistry } from './hooks/index.js';\nimport type { PebbleContainer } from './pebble-reconciler.js';\nimport {\n createContainer,\n updateContainer,\n unmountContainer,\n} from './pebble-reconciler.js';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface PebblePlatformInfo {\n isReal: boolean;\n platform: 'alloy' | 'mock';\n screenWidth: number;\n screenHeight: number;\n}\n\nexport interface DrawCall {\n op: string;\n [key: string]: unknown;\n}\n\nexport interface RenderOptions {\n backgroundColor?: string;\n}\n\nexport interface PebbleApp {\n update(newElement: ComponentChild): void;\n unmount(): void;\n readonly platform: PebblePlatformInfo;\n readonly drawLog: readonly DrawCall[];\n readonly _root: DOMElement;\n}\n\n// ---------------------------------------------------------------------------\n// Mock Poco — records every draw call into a shared log so tests can assert.\n// ---------------------------------------------------------------------------\n\nclass MockPoco {\n readonly width: number;\n readonly height: number;\n readonly Font: new (name: string, size: number) => PocoFont;\n\n constructor(width: number, height: number, private readonly log: DrawCall[]) {\n this.width = width;\n this.height = height;\n const FontImpl = class {\n readonly name: string;\n readonly size: number;\n readonly height: number;\n constructor(name: string, size: number) {\n this.name = name;\n this.size = size;\n this.height = size;\n }\n };\n this.Font = FontImpl as unknown as new (name: string, size: number) => PocoFont;\n }\n\n begin(x?: number, y?: number, width?: number, height?: number): void {\n this.log.push({ op: 'begin', x, y, width, height });\n }\n end(): void {\n this.log.push({ op: 'end' });\n }\n continue(x: number, y: number, width: number, height: number): void {\n this.log.push({ op: 'continue', x, y, width, height });\n }\n clip(x?: number, y?: number, width?: number, height?: number): void {\n this.log.push({ op: 'clip', x, y, width, height });\n }\n origin(x?: number, y?: number): void {\n this.log.push({ op: 'origin', x, y });\n }\n\n makeColor(r: number, g: number, b: number): PocoColor {\n return ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);\n }\n\n fillRectangle(color: PocoColor, x: number, y: number, width: number, height: number): void {\n this.log.push({ op: 'fillRectangle', color, x, y, width, height });\n }\n blendRectangle(\n color: PocoColor,\n blend: number,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void {\n this.log.push({ op: 'blendRectangle', color, blend, x, y, width, height });\n }\n drawPixel(color: PocoColor, x: number, y: number): void {\n this.log.push({ op: 'drawPixel', color, x, y });\n }\n drawBitmap(_bits: PocoBitmap, x: number, y: number): void {\n this.log.push({ op: 'drawBitmap', x, y });\n }\n drawMonochrome(\n _monochrome: PocoBitmap,\n fore: PocoColor,\n back: PocoColor | undefined,\n x: number,\n y: number,\n ): void {\n this.log.push({ op: 'drawMonochrome', fore, back, x, y });\n }\n\n drawText(text: string, font: PocoFont, color: PocoColor, x: number, y: number): void {\n this.log.push({ op: 'drawText', text, font, color, x, y });\n }\n getTextWidth(text: string, font: PocoFont): number {\n const size = (font as unknown as { size?: number }).size ?? 14;\n return Math.round(text.length * size * 0.6);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Poco construction — real on Alloy, mock in Node\n// ---------------------------------------------------------------------------\n\nfunction createPoco(\n log: DrawCall[],\n pocoCtor: typeof Poco | undefined,\n): { poco: Poco; info: PebblePlatformInfo } {\n if (typeof screen !== 'undefined' && screen && pocoCtor) {\n const poco = new pocoCtor(screen);\n return {\n poco,\n info: {\n isReal: true,\n platform: 'alloy',\n screenWidth: screen.width,\n screenHeight: screen.height,\n },\n };\n }\n\n const width = 200;\n const height = 228;\n const mock = new MockPoco(width, height, log);\n return {\n poco: mock as unknown as Poco,\n info: {\n isReal: false,\n platform: 'mock',\n screenWidth: width,\n screenHeight: height,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Button wiring — see moddable.d.ts for the known/unknown event names.\n// ---------------------------------------------------------------------------\n\nfunction wireWatchButtons(): () => void {\n if (typeof watch === 'undefined' || !watch) return () => undefined;\n\n const normalize = (raw: unknown): PebbleButton | undefined => {\n if (typeof raw !== 'string') return undefined;\n const low = raw.toLowerCase();\n if (low === 'up' || low === 'down' || low === 'select' || low === 'back') {\n return low;\n }\n return undefined;\n };\n\n const onShort = (payload?: { button?: unknown }) => {\n const b = normalize(payload?.button);\n if (b) ButtonRegistry.emit(b);\n };\n const onLong = (payload?: { button?: unknown }) => {\n const b = normalize(payload?.button);\n if (b) ButtonRegistry.emit(`long_${b}`);\n };\n\n watch.addEventListener('button', onShort);\n watch.addEventListener('buttonClick', onShort);\n watch.addEventListener('longClick', onLong);\n\n return () => {\n if (typeof watch === 'undefined' || !watch) return;\n watch.removeEventListener('button', onShort);\n watch.removeEventListener('buttonClick', onShort);\n watch.removeEventListener('longClick', onLong);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Redraw scheduling\n//\n// Preact renders are synchronous; when any component calls setState, Preact\n// re-runs the diff and mutates the shim tree in place. We hook into that\n// by scheduling a Poco redraw on the next tick.\n// ---------------------------------------------------------------------------\n\nfunction scheduleMicrotask(fn: () => void): void {\n // Prefer Promise.resolve().then for microtask batching; fall back to\n // setTimeout(0) if Promise isn't wired up in some host.\n if (typeof Promise !== 'undefined') {\n Promise.resolve().then(fn);\n } else {\n setTimeout(fn, 0);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public render API\n// ---------------------------------------------------------------------------\n\nexport interface RenderOptionsExt extends RenderOptions {\n /**\n * Pre-imported Poco constructor. Alloy entry files must import Poco at\n * the top and pass it here so the Moddable bundler resolves it correctly.\n */\n poco?: typeof Poco;\n}\n\nexport function render(element: ComponentChild, options: RenderOptionsExt = {}): PebbleApp {\n const drawLog: DrawCall[] = [];\n const container: PebbleContainer = createContainer();\n const { poco, info } = createPoco(drawLog, options.poco);\n const renderer = new PocoRenderer(poco);\n\n let pending = false;\n const redraw = () => {\n pending = false;\n drawLog.length = 0;\n renderer.render(container.pblRoot, { backgroundColor: options.backgroundColor });\n };\n\n const schedule = () => {\n if (pending) return;\n pending = true;\n scheduleMicrotask(redraw);\n };\n\n // Monkey-patch the shim root to redraw on any mutation.\n // Preact calls appendChild/insertBefore/removeChild/setAttribute on the\n // tree during diff; we only need to schedule a redraw when the diff\n // settles, which is right after the top-level render() call returns.\n // For that we just trigger a redraw synchronously after updateContainer.\n\n // Hook Preact's commit phase so hook-driven state updates trigger a redraw\n // without polling. options._commit is an undocumented-but-stable hook that\n // fires once per root-level diff settle.\n type PreactOptionsWithCommit = typeof options & {\n _commit?: (root: unknown, queue: unknown[]) => void;\n __c?: (root: unknown, queue: unknown[]) => void;\n };\n const opts = options as PreactOptionsWithCommit;\n const prevCommit = opts._commit ?? opts.__c;\n const commitHook = (root: unknown, queue: unknown[]) => {\n if (prevCommit) prevCommit(root, queue);\n schedule();\n };\n opts._commit = commitHook;\n opts.__c = commitHook;\n\n updateContainer(element, container);\n // Always paint once on mount.\n redraw();\n\n // Subscribe to watch events on-device.\n const unwireButtons = wireWatchButtons();\n\n return {\n update(newElement) {\n updateContainer(newElement, container);\n schedule();\n },\n unmount() {\n unmountContainer(container);\n // Restore prior commit hook (in case another app was rendered).\n opts._commit = prevCommit;\n opts.__c = prevCommit;\n unwireButtons();\n },\n get platform() {\n return info;\n },\n get drawLog() {\n return drawLog as readonly DrawCall[];\n },\n get _root() {\n return container.pblRoot;\n },\n };\n}\n"],"mappings":"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"}