react-pebble 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,8 @@ export interface CompileResult {
29
29
  hasButtons: boolean;
30
30
  /** Message keys used by useMessage hooks */
31
31
  messageKeys: string[];
32
+ /** Mock data source from useMessage (for generating phone-side JS) */
33
+ mockDataSource: string | null;
32
34
  /** Diagnostic messages from the compiler */
33
35
  diagnostics: string;
34
36
  }
@@ -64,6 +64,7 @@ export interface RectProps extends PositionProps, SizeProps {
64
64
  fill?: ColorName;
65
65
  stroke?: ColorName;
66
66
  strokeWidth?: number;
67
+ borderRadius?: number;
67
68
  children?: ReactNode;
68
69
  }
69
70
  export declare function Rect({ children, ...props }: RectProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
@@ -92,12 +93,38 @@ export interface LineProps extends PositionProps {
92
93
  export declare function Line(props: LineProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
93
94
  export interface ImageProps extends PositionProps {
94
95
  bitmap: unknown;
96
+ /** Rotation in radians. */
97
+ rotation?: number;
98
+ /** Scale factor (1 = original size). */
99
+ scale?: number;
95
100
  }
96
101
  export declare function Image(props: ImageProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
97
- export interface GroupProps extends PositionProps {
102
+ export interface GroupProps extends PositionProps, SizeProps {
98
103
  children?: ReactNode;
99
104
  }
100
105
  export declare function Group({ children, ...props }: GroupProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
106
+ export interface ColumnProps extends PositionProps, SizeProps {
107
+ /** Gap between children in pixels (default 0). */
108
+ gap?: number;
109
+ children?: ReactNode;
110
+ }
111
+ /**
112
+ * Stacks children vertically, auto-computing each child's `y` offset.
113
+ * Children should specify `h` (or `height`) for correct stacking;
114
+ * children without a height are given a default of 20px.
115
+ */
116
+ export declare function Column({ x, y, gap, children, ...props }: ColumnProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
117
+ export interface RowProps extends PositionProps, SizeProps {
118
+ /** Gap between children in pixels (default 0). */
119
+ gap?: number;
120
+ children?: ReactNode;
121
+ }
122
+ /**
123
+ * Stacks children horizontally, auto-computing each child's `x` offset.
124
+ * Children should specify `w` (or `width`) for correct stacking;
125
+ * children without a width are given a default of 40px.
126
+ */
127
+ export declare function Row({ x, y, gap, children, ...props }: RowProps): import('preact').VNode<(import('preact').ClassAttributes<HTMLElement> & import('preact').HTMLAttributes<EventTarget> & import('preact').SVGAttributes<SVGElement>) | null>;
101
128
  export interface StatusBarProps {
102
129
  color?: ColorName;
103
130
  backgroundColor?: ColorName;
@@ -72,4 +72,186 @@ export interface UseMessageResult<T> {
72
72
  * });
73
73
  */
74
74
  export declare function useMessage<T>(options: UseMessageOptions<T>): UseMessageResult<T>;
75
+ export interface BatteryState {
76
+ percent: number;
77
+ charging: boolean;
78
+ plugged: boolean;
79
+ }
80
+ /**
81
+ * Returns the current battery state. On Alloy, reads the `Battery` global.
82
+ * In mock mode (Node), returns a static default (100%, not charging).
83
+ *
84
+ * Re-reads on each render; battery updates arrive via watch tick events
85
+ * which trigger redraws, so the value stays fresh.
86
+ */
87
+ export declare function useBattery(): BatteryState;
88
+ export interface ConnectionState {
89
+ app: boolean;
90
+ pebblekit: boolean;
91
+ }
92
+ /**
93
+ * Returns the current phone connection state. On Alloy, reads
94
+ * `watch.connected`. In mock mode, returns connected for both.
95
+ */
96
+ export declare function useConnection(): ConnectionState;
97
+ /**
98
+ * Like useState, but backed by localStorage so the value persists across
99
+ * app restarts and watch reboots.
100
+ *
101
+ * On Alloy, `localStorage` is a standard Web API global.
102
+ * In mock mode (Node), falls back to a plain in-memory useState.
103
+ *
104
+ * Values are JSON-serialized. Only use with JSON-safe types.
105
+ */
106
+ export declare function useLocalStorage<T>(key: string, defaultValue: T): [T, (v: T | ((prev: T) => T)) => void];
107
+ export interface UseFetchOptions<T> {
108
+ /** Mock data returned in Node mock mode so the compiler can render. */
109
+ mockData?: T;
110
+ /** Delay in ms before mock data appears (default 100). */
111
+ mockDelay?: number;
112
+ /** fetch() RequestInit options (method, headers, body). */
113
+ init?: RequestInit;
114
+ }
115
+ export interface UseFetchResult<T> {
116
+ data: T | null;
117
+ loading: boolean;
118
+ error: string | null;
119
+ }
120
+ /**
121
+ * Fetch JSON data from a URL.
122
+ *
123
+ * On Alloy: uses the standard `fetch()` API (proxied via @moddable/pebbleproxy).
124
+ * In mock mode (Node): returns `mockData` after `mockDelay` ms.
125
+ *
126
+ * Usage:
127
+ * const { data, loading, error } = useFetch<Weather>(
128
+ * 'https://api.example.com/weather',
129
+ * { mockData: { temp: 72, condition: 'Sunny' } }
130
+ * );
131
+ */
132
+ export declare function useFetch<T>(url: string, options?: UseFetchOptions<T>): UseFetchResult<T>;
133
+ /** Standard easing functions matching Alloy's Timeline API. */
134
+ export declare const Easing: {
135
+ readonly linear: (t: number) => number;
136
+ readonly quadEaseIn: (t: number) => number;
137
+ readonly quadEaseOut: (t: number) => number;
138
+ readonly quadEaseInOut: (t: number) => number;
139
+ readonly cubicEaseIn: (t: number) => number;
140
+ readonly cubicEaseOut: (t: number) => number;
141
+ readonly cubicEaseInOut: (t: number) => number;
142
+ readonly sinEaseIn: (t: number) => number;
143
+ readonly sinEaseOut: (t: number) => number;
144
+ readonly sinEaseInOut: (t: number) => number;
145
+ readonly expoEaseIn: (t: number) => number;
146
+ readonly expoEaseOut: (t: number) => number;
147
+ readonly circEaseIn: (t: number) => number;
148
+ readonly circEaseOut: (t: number) => number;
149
+ readonly bounceEaseOut: (t: number) => number;
150
+ readonly bounceEaseIn: (t: number) => number;
151
+ readonly elasticEaseOut: (t: number) => number;
152
+ readonly backEaseOut: (t: number) => number;
153
+ };
154
+ export type EasingFn = (t: number) => number;
155
+ export interface UseAnimationOptions {
156
+ /** Duration in ms. */
157
+ duration: number;
158
+ /** Easing function (default: linear). */
159
+ easing?: EasingFn;
160
+ /** Delay before start in ms (default: 0). */
161
+ delay?: number;
162
+ /** Loop the animation (default: false). */
163
+ loop?: boolean;
164
+ /** Auto-start on mount (default: true). */
165
+ autoStart?: boolean;
166
+ }
167
+ export interface UseAnimationResult {
168
+ /** Current progress value (0 to 1), eased. */
169
+ progress: number;
170
+ /** Whether the animation is currently running. */
171
+ running: boolean;
172
+ /** Start or restart the animation. */
173
+ start: () => void;
174
+ /** Stop the animation. */
175
+ stop: () => void;
176
+ }
177
+ /**
178
+ * Animate a progress value from 0 to 1 over a duration with easing.
179
+ *
180
+ * Uses `useTime` internally so that animation progress is derived from
181
+ * the wall clock. This ensures compatibility with the piu compiler,
182
+ * which detects time-dependent values via T1/T2 render diffs.
183
+ *
184
+ * The animation cycles based on `duration` (in ms). If `loop` is true,
185
+ * it repeats indefinitely.
186
+ *
187
+ * Usage:
188
+ * const { progress } = useAnimation({ duration: 10000, easing: Easing.bounceEaseOut, loop: true });
189
+ * const x = lerp(0, 200, progress);
190
+ */
191
+ export declare function useAnimation(options: UseAnimationOptions): UseAnimationResult;
192
+ /**
193
+ * Interpolate between two values using an animation progress (0-1).
194
+ */
195
+ export declare function lerp(from: number, to: number, progress: number): number;
196
+ export interface AccelerometerData {
197
+ x: number;
198
+ y: number;
199
+ z: number;
200
+ }
201
+ export interface UseAccelerometerOptions {
202
+ /** Sample rate in ms (default: 100). */
203
+ sampleRate?: number;
204
+ /** Called on tap gesture. */
205
+ onTap?: () => void;
206
+ /** Called on double-tap gesture. */
207
+ onDoubleTap?: () => void;
208
+ }
209
+ /**
210
+ * Read accelerometer data.
211
+ *
212
+ * On Alloy: reads from the Moddable Accelerometer sensor.
213
+ * In mock mode: returns { x: 0, y: 0, z: -1000 } (gravity pointing down).
214
+ */
215
+ export declare function useAccelerometer(options?: UseAccelerometerOptions): AccelerometerData;
216
+ export interface CompassData {
217
+ /** Heading in degrees (0-360, 0 = north). */
218
+ heading: number;
219
+ }
220
+ /**
221
+ * Read compass heading.
222
+ *
223
+ * On Alloy: reads from the Moddable Compass sensor.
224
+ * In mock mode: returns a slowly rotating heading.
225
+ */
226
+ export declare function useCompass(): CompassData;
227
+ export interface UseWebSocketResult {
228
+ /** Last received message (null until first message). */
229
+ lastMessage: string | null;
230
+ /** Whether the connection is open. */
231
+ connected: boolean;
232
+ /** Send a message. */
233
+ send: (data: string) => void;
234
+ /** Close the connection. */
235
+ close: () => void;
236
+ }
237
+ /**
238
+ * Connect to a WebSocket server.
239
+ *
240
+ * On Alloy: uses the WebSocket API (proxied via @moddable/pebbleproxy).
241
+ * In mock mode: simulates a connection that echoes messages back.
242
+ */
243
+ export declare function useWebSocket(url: string): UseWebSocketResult;
244
+ /**
245
+ * Key-value storage for binary and structured data using the ECMA-419 API.
246
+ *
247
+ * On Alloy: uses `device.keyValue.open(storeName)` for persistent binary storage.
248
+ * In mock mode: uses an in-memory Map.
249
+ *
250
+ * For simple string storage, prefer `useLocalStorage`.
251
+ */
252
+ export declare function useKVStorage(storeName: string): {
253
+ get: (key: string) => string | null;
254
+ set: (key: string, value: string) => void;
255
+ remove: (key: string) => void;
256
+ };
75
257
  export {};
@@ -25,10 +25,10 @@ export { SCREEN, PLATFORMS, _setPlatform } from './platform.js';
25
25
  export type { PebblePlatform } from './platform.js';
26
26
  export { render } from './pebble-render.js';
27
27
  export type { PebbleApp, PebblePlatformInfo, RenderOptions, RenderOptionsExt, DrawCall, } from './pebble-render.js';
28
- export { Window, Rect, Circle, Text, Line, Image, Group, StatusBar, ActionBar, Card, Badge, } from './components/index.js';
29
- export type { WindowProps, RectProps, CircleProps, TextProps, LineProps, ImageProps, GroupProps, StatusBarProps, ActionBarProps, CardProps, BadgeProps, PositionProps, SizeProps, ButtonHandlerProps, ColorName, FontName, Alignment, } from './components/index.js';
30
- export { useApp, useButton, useLongButton, useTime, useFormattedTime, useInterval, useListNavigation, ButtonRegistry, PebbleAppContext, } from './hooks/index.js';
31
- export type { PebbleButton, PebbleButtonHandler, ListNavigationOptions, ListNavigationResult, } from './hooks/index.js';
28
+ export { Window, Rect, Circle, Text, Line, Image, Group, Column, Row, StatusBar, ActionBar, Card, Badge, } from './components/index.js';
29
+ export type { WindowProps, RectProps, CircleProps, TextProps, LineProps, ImageProps, GroupProps, ColumnProps, RowProps, StatusBarProps, ActionBarProps, CardProps, BadgeProps, PositionProps, SizeProps, ButtonHandlerProps, ColorName, FontName, Alignment, } from './components/index.js';
30
+ export { useApp, useButton, useLongButton, useTime, useFormattedTime, useInterval, useListNavigation, useBattery, useConnection, useLocalStorage, useFetch, useAnimation, useAccelerometer, useCompass, useWebSocket, useKVStorage, Easing, lerp, ButtonRegistry, PebbleAppContext, } from './hooks/index.js';
31
+ export type { PebbleButton, PebbleButtonHandler, ListNavigationOptions, ListNavigationResult, BatteryState, ConnectionState, UseFetchOptions, UseFetchResult, UseAnimationOptions, UseAnimationResult, EasingFn, AccelerometerData, UseAccelerometerOptions, CompassData, UseWebSocketResult, } from './hooks/index.js';
32
32
  export { default as reconciler } from './pebble-reconciler.js';
33
33
  export { ELEMENT_TYPES, appendChildNode, createNode, createTextNode, findRoot, getTextContent, insertBeforeNode, removeChildNode, setAttribute, setTextNodeValue, walkTree, } from './pebble-dom.js';
34
34
  export type { AnyNode, DOMElement, ElementType, NodeProps, TextNode, Visitor, } from './pebble-dom.js';
@@ -39,6 +39,21 @@ export declare class PocoRenderer {
39
39
  getFont(name: string | undefined): PocoFont;
40
40
  private renderChildren;
41
41
  private renderNode;
42
+ /** Fill a circle by drawing horizontal spans for each row. */
43
+ private fillCircle;
44
+ /** Stroke a circle outline by drawing small rects at each perimeter point. */
45
+ private strokeCircle;
46
+ private drawDiagonalLine;
47
+ /** Fill a rounded rectangle by combining rects and quarter-circle corners. */
48
+ private fillRoundRect;
49
+ /** Stroke a rounded rectangle outline. */
50
+ private strokeRoundRect;
51
+ /** Fill four quarter-circles at the corners of a rounded rect. */
52
+ private fillQuarterCircles;
53
+ /** Stroke four quarter-circle arcs at the corners of a rounded rect. */
54
+ private strokeQuarterCircles;
55
+ /** Break text into lines that fit within maxWidth. */
56
+ private wrapText;
42
57
  }
43
58
  export declare function resolveColorName(color: string | undefined): string;
44
59
  export declare function resolveFontName(font: string | undefined): string;
@@ -6,6 +6,12 @@ export interface PebblePiuOptions {
6
6
  settleMs?: number;
7
7
  /** Target platform — sets screen dimensions (default: 'emery') */
8
8
  platform?: string;
9
+ /**
10
+ * Target platforms for multi-platform builds (e.g. ['emery', 'gabbro']).
11
+ * When set, builds once per platform into `{buildDir}-{platform}/`.
12
+ * Overrides the `platform` option.
13
+ */
14
+ targetPlatforms?: string[];
9
15
  /** Directory for the generated Pebble project (default: '.pebble-build') */
10
16
  buildDir?: string;
11
17
  /** Auto-run pebble build + install after compilation */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-pebble",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Write Pebble watchfaces and apps in JSX — compiles to piu for Pebble Alloy at build time",
5
5
  "type": "module",
6
6
  "main": "dist/lib/index.cjs",
@@ -36,15 +36,6 @@
36
36
  "src",
37
37
  "scripts"
38
38
  ],
39
- "scripts": {
40
- "typecheck": "tsc --noEmit",
41
- "test": "npm run typecheck && npx tsx test/snapshot-test.ts",
42
- "test:snapshots": "npx tsx test/snapshot-test.ts",
43
- "test:update": "npx tsx test/snapshot-test.ts --update",
44
- "build": "npm run build:lib",
45
- "build:lib": "vite build --config vite.config.lib.js",
46
- "prepublishOnly": "npm run test && npm run build"
47
- },
48
39
  "keywords": [
49
40
  "pebble",
50
41
  "smartwatch",
@@ -86,5 +77,13 @@
86
77
  },
87
78
  "dependencies": {
88
79
  "preact": "^10.29.1"
80
+ },
81
+ "scripts": {
82
+ "typecheck": "tsc --noEmit",
83
+ "test": "npm run typecheck && npx tsx test/snapshot-test.ts",
84
+ "test:snapshots": "npx tsx test/snapshot-test.ts",
85
+ "test:update": "npx tsx test/snapshot-test.ts --update",
86
+ "build": "npm run build:lib",
87
+ "build:lib": "vite build --config vite.config.lib.js"
89
88
  }
90
- }
89
+ }