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.
- package/README.md +142 -0
- 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.
|
|
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.
|
|
43
|
-
"@types/node": "^25.5.
|
|
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.
|
|
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.
|
|
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
|
+
}
|