wff-web 0.1.0 → 0.1.1

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 (2) hide show
  1. package/README.md +142 -0
  2. package/package.json +16 -13
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # wff-web
2
+
3
+ Render [WearOS Watch Face Format (WFF) v4](https://developer.android.com/training/wearables/wff) XML in the browser using HTML Canvas.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install wff-web
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { renderWatchFace } from "wff-web";
15
+
16
+ const canvas = document.createElement("canvas");
17
+ document.body.appendChild(canvas);
18
+
19
+ const xml = `<WatchFace width="450" height="450" clipShape="CIRCLE">
20
+ <Scene backgroundColor="#1a1a2e">
21
+ <AnalogClock centerX="225" centerY="225">
22
+ <HourHand resource="hour.png" x="0" y="0" width="450" height="450"
23
+ pivotX="0.5" pivotY="0.5" />
24
+ <MinuteHand resource="minute.png" x="0" y="0" width="450" height="450"
25
+ pivotX="0.5" pivotY="0.5" />
26
+ </AnalogClock>
27
+ </Scene>
28
+ </WatchFace>`;
29
+
30
+ const { metadata } = await renderWatchFace(canvas, { xml });
31
+ ```
32
+
33
+ ### With assets
34
+
35
+ Pass image assets as a `Map<string, ArrayBuffer>`. Keys match the `resource` attribute paths in the XML.
36
+
37
+ ```js
38
+ const assets = new Map();
39
+ assets.set("hour.png", await fetch("/hands/hour.png").then(r => r.arrayBuffer()));
40
+ assets.set("minute.png", await fetch("/hands/minute.png").then(r => r.arrayBuffer()));
41
+
42
+ await renderWatchFace(canvas, { xml, assets });
43
+ ```
44
+
45
+ ### Setting the time
46
+
47
+ By default the renderer uses the current time. Pass a `Date` to render a specific moment:
48
+
49
+ ```js
50
+ await renderWatchFace(canvas, {
51
+ xml,
52
+ time: new Date("2024-06-15T10:10:30"),
53
+ });
54
+ ```
55
+
56
+ ### Ambient mode
57
+
58
+ Render the always-on display variant:
59
+
60
+ ```js
61
+ await renderWatchFace(canvas, { xml, ambient: true });
62
+ ```
63
+
64
+ ### Animation
65
+
66
+ Start a live animation loop that updates every frame:
67
+
68
+ ```js
69
+ const { stop } = await renderWatchFace(canvas, {
70
+ xml,
71
+ assets,
72
+ animate: true,
73
+ });
74
+
75
+ // Later, stop the loop:
76
+ stop();
77
+ ```
78
+
79
+ ### Custom dimensions
80
+
81
+ Override the dimensions declared in the XML:
82
+
83
+ ```js
84
+ await renderWatchFace(canvas, { xml, width: 300, height: 300 });
85
+ ```
86
+
87
+ ### User configuration
88
+
89
+ Watch faces can declare user-customizable options (colors, lists, booleans). Override their defaults:
90
+
91
+ ```js
92
+ await renderWatchFace(canvas, {
93
+ xml,
94
+ configuration: {
95
+ theme_color: "1", // ColorConfiguration option id
96
+ show_seconds: "TRUE", // BooleanConfiguration
97
+ dial_style: "2", // ListConfiguration option id
98
+ },
99
+ });
100
+ ```
101
+
102
+ ## API
103
+
104
+ ### `renderWatchFace(canvas, options): Promise<RenderResult>`
105
+
106
+ Renders a WFF XML watch face onto the provided `HTMLCanvasElement`.
107
+
108
+ #### `RenderOptions`
109
+
110
+ | Option | Type | Default | Description |
111
+ |---|---|---|---|
112
+ | `xml` | `string` | *required* | WFF v4 XML document |
113
+ | `assets` | `Map<string, ArrayBuffer>` | `new Map()` | Image assets keyed by resource path |
114
+ | `width` | `number` | from XML | Canvas width in pixels |
115
+ | `height` | `number` | from XML | Canvas height in pixels |
116
+ | `time` | `Date` | `new Date()` | Time to render |
117
+ | `ambient` | `boolean` | `false` | Render in ambient (always-on) mode |
118
+ | `configuration` | `Record<string, string \| number \| boolean>` | `{}` | User configuration overrides |
119
+ | `animate` | `boolean` | `false` | Start a `requestAnimationFrame` loop |
120
+
121
+ #### `RenderResult`
122
+
123
+ | Field | Type | Description |
124
+ |---|---|---|
125
+ | `metadata` | `Map<string, string>` | Metadata from the XML (e.g. `CLOCK_TYPE`, `PREVIEW_TIME`) |
126
+ | `stop` | `() => void \| undefined` | Stops the animation loop (only present when `animate: true`) |
127
+
128
+ ## Supported elements
129
+
130
+ Shapes: `Arc`, `Ellipse`, `Line`, `Rectangle`, `RoundRectangle`
131
+ Layout: `Group`, `Part`, `PartDraw`
132
+ Text: `PartText`, `TimeText`, `Font`
133
+ Clock: `AnalogClock`, `HourHand`, `MinuteHand`, `SecondHand`, `DigitalClock`
134
+ Images: `PartImage`
135
+ Conditions: `Condition`, `Compare`, `Default`
136
+ Styling: `Fill`, `Stroke`, `LinearGradient`, `RadialGradient`, `SweepGradient`
137
+ Animation: `Transform`, `Gyro`, `Variant`
138
+ Masking: `Mask` with blend modes
139
+
140
+ ## License
141
+
142
+ ISC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wff-web",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Web preview renderer for WearOS Watch Face Format (WFF) v4 XML",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,6 +16,15 @@
16
16
  "dist"
17
17
  ],
18
18
  "sideEffects": false,
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm --dts",
21
+ "prepublishOnly": "pnpm build",
22
+ "test": "vitest run",
23
+ "test:visual": "vitest run --config vitest.visual.config.ts",
24
+ "test:visual:update-baselines": "tsx scripts/update-baselines.ts",
25
+ "report": "tsx scripts/generate-report.ts",
26
+ "examples": "pnpm build && npx serve . -l 3000 --no-clipboard -C"
27
+ },
19
28
  "keywords": [
20
29
  "wearos",
21
30
  "watch-face",
@@ -38,23 +47,17 @@
38
47
  "engines": {
39
48
  "node": ">=18"
40
49
  },
50
+ "packageManager": "pnpm@10.31.0",
41
51
  "devDependencies": {
42
- "@playwright/test": "^1.58.2",
43
- "@types/node": "^25.5.0",
52
+ "@playwright/test": "^1.59.1",
53
+ "@types/node": "^25.5.2",
44
54
  "@types/pngjs": "^6.0.5",
45
55
  "pixelmatch": "^7.1.0",
46
- "playwright": "^1.58.2",
56
+ "playwright": "^1.59.1",
47
57
  "pngjs": "^7.0.0",
48
58
  "tsup": "^8.5.1",
49
59
  "tsx": "^4.21.0",
50
60
  "typescript": "^6.0.2",
51
- "vitest": "^4.1.1"
52
- },
53
- "scripts": {
54
- "build": "tsup src/index.ts --format esm --dts",
55
- "test": "vitest run",
56
- "test:visual": "vitest run --config vitest.visual.config.ts",
57
- "test:visual:update-baselines": "tsx scripts/update-baselines.ts",
58
- "report": "tsx scripts/generate-report.ts"
61
+ "vitest": "^4.1.3"
59
62
  }
60
- }
63
+ }