@olegkoval/agent-skills 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +7 -2
- package/.cursor-plugin/index.json +26 -1
- package/.grok-plugin/index.json +26 -1
- package/.kiro/steering/garmin-watchface.md +83 -31
- package/.windsurf/rules/garmin-watchface.md +83 -31
- package/README.md +8 -3
- package/catalog/skills.json +117 -1
- package/collections/software-development.json +2 -1
- package/package.json +1 -1
- package/packages/software-development/branch-cleanup/SKILL.md +204 -0
- package/packages/software-development/branch-cleanup/adapters/claude/plugin.json +5 -0
- package/packages/software-development/branch-cleanup/adapters/claude/skills/branch-cleanup/SKILL.md +205 -0
- package/packages/software-development/branch-cleanup/adapters/codex/README.md +19 -0
- package/packages/software-development/branch-cleanup/adapters/cursor/plugin.json +6 -0
- package/packages/software-development/branch-cleanup/adapters/cursor/skills/branch-cleanup/SKILL.md +205 -0
- package/packages/software-development/branch-cleanup/adapters/grok/plugin.json +6 -0
- package/packages/software-development/branch-cleanup/adapters/grok/skills/branch-cleanup/SKILL.md +205 -0
- package/packages/software-development/morning-routine/SKILL.md +149 -0
- package/packages/software-development/morning-routine/adapters/claude/plugin.json +5 -0
- package/packages/software-development/morning-routine/adapters/claude/skills/morning-routine/SKILL.md +150 -0
- package/packages/software-development/morning-routine/adapters/codex/README.md +19 -0
- package/packages/software-development/morning-routine/adapters/cursor/plugin.json +6 -0
- package/packages/software-development/morning-routine/adapters/cursor/skills/morning-routine/SKILL.md +150 -0
- package/packages/software-development/morning-routine/adapters/grok/plugin.json +6 -0
- package/packages/software-development/morning-routine/adapters/grok/skills/morning-routine/SKILL.md +150 -0
- package/packages/software-development/pr-finalize-complete/adapters/claude/plugin.json +1 -1
- package/packages/software-development/pr-finalize-complete/adapters/codex/README.md +7 -11
- package/packages/software-development/pr-finalize-complete/adapters/cursor/plugin.json +1 -1
- package/packages/software-development/pr-finalize-complete/adapters/grok/plugin.json +1 -1
- package/packages/software-development/release-day/SKILL.md +286 -0
- package/packages/software-development/release-day/adapters/claude/plugin.json +5 -0
- package/packages/software-development/release-day/adapters/claude/skills/release-day/SKILL.md +287 -0
- package/packages/software-development/release-day/adapters/codex/README.md +19 -0
- package/packages/software-development/release-day/adapters/cursor/plugin.json +6 -0
- package/packages/software-development/release-day/adapters/cursor/skills/release-day/SKILL.md +287 -0
- package/packages/software-development/release-day/adapters/grok/plugin.json +6 -0
- package/packages/software-development/release-day/adapters/grok/skills/release-day/SKILL.md +287 -0
- package/packages/software-development/vinted-listing/SKILL.md +58 -0
- package/packages/software-development/vinted-listing/adapters/claude/plugin.json +5 -0
- package/packages/software-development/vinted-listing/adapters/claude/skills/vinted-listing/SKILL.md +59 -0
- package/packages/software-development/vinted-listing/adapters/claude/skills/vinted-listing/references/photo-and-duplicate-policy.md +20 -0
- package/packages/software-development/vinted-listing/adapters/codex/README.md +3 -0
- package/packages/software-development/vinted-listing/adapters/cursor/plugin.json +6 -0
- package/packages/software-development/vinted-listing/adapters/cursor/skills/vinted-listing/SKILL.md +59 -0
- package/packages/software-development/vinted-listing/adapters/cursor/skills/vinted-listing/references/photo-and-duplicate-policy.md +20 -0
- package/packages/software-development/vinted-listing/adapters/grok/plugin.json +6 -0
- package/packages/software-development/vinted-listing/adapters/grok/skills/vinted-listing/SKILL.md +59 -0
- package/packages/software-development/vinted-listing/adapters/grok/skills/vinted-listing/references/photo-and-duplicate-policy.md +20 -0
- package/packages/software-development/vinted-listing/references/photo-and-duplicate-policy.md +20 -0
- package/packages/wearables/garmin-watchface/SKILL.md +82 -35
- package/packages/wearables/garmin-watchface/adapters/claude/plugin.json +1 -1
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/SKILL.md +82 -35
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/design-proposals.md +99 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/devices.md +112 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/display.md +78 -3
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/inspiration-wizard.md +140 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/layout.md +28 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/publishing.md +134 -5
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/simulator.md +86 -2
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-inspire +329 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-mock +502 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-release +12 -6
- package/packages/wearables/garmin-watchface/adapters/cursor/plugin.json +1 -1
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/SKILL.md +82 -35
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/design-proposals.md +99 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/devices.md +112 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/display.md +78 -3
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/inspiration-wizard.md +140 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/layout.md +28 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/publishing.md +134 -5
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/simulator.md +86 -2
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-inspire +329 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-mock +502 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-release +12 -6
- package/packages/wearables/garmin-watchface/adapters/grok/plugin.json +1 -1
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/SKILL.md +82 -35
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/design-proposals.md +99 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/devices.md +112 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/display.md +78 -3
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/inspiration-wizard.md +140 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/layout.md +28 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/publishing.md +134 -5
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/simulator.md +86 -2
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-inspire +329 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-mock +502 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-release +12 -6
- package/packages/wearables/garmin-watchface/adapters/kiro/steering/garmin-watchface.md +83 -31
- package/packages/wearables/garmin-watchface/adapters/windsurf/rules/garmin-watchface.md +83 -31
- package/packages/wearables/garmin-watchface/references/design-proposals.md +99 -0
- package/packages/wearables/garmin-watchface/references/devices.md +112 -0
- package/packages/wearables/garmin-watchface/references/display.md +78 -3
- package/packages/wearables/garmin-watchface/references/inspiration-wizard.md +140 -0
- package/packages/wearables/garmin-watchface/references/layout.md +28 -0
- package/packages/wearables/garmin-watchface/references/publishing.md +134 -5
- package/packages/wearables/garmin-watchface/references/simulator.md +86 -2
- package/packages/wearables/garmin-watchface/scripts/ciq-inspire +329 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-mock +502 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-release +12 -6
package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/SKILL.md
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: garmin-watchface
|
|
3
3
|
description: Build, test, screenshot and publish Garmin Connect IQ watch faces in Monkey C. Use when working on a Connect IQ watch face or app - creating one, fixing layout that clips or overlaps, capturing simulator screenshots, adding app settings, widening device support, or preparing a Connect IQ Store submission. Encodes the traps that silently produce a passing build and a broken face.
|
|
4
|
-
license: MIT
|
|
5
|
-
allowed-tools: Bash, Read, Write, Edit, Grep, Glob
|
|
6
|
-
compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires macOS, the Connect IQ SDK, a JDK, and Python 3 (Pillow for screenshot cropping).
|
|
7
|
-
metadata:
|
|
8
|
-
targets: [_source-only]
|
|
9
4
|
---
|
|
10
5
|
<!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
|
|
11
6
|
|
|
@@ -16,43 +11,43 @@ that draws off the bottom of the screen, a test suite that contains no tests, an
|
|
|
16
11
|
a colour that is not the colour you get. Everything below is a failure mode that
|
|
17
12
|
looked like success first.
|
|
18
13
|
|
|
19
|
-
**Read `
|
|
14
|
+
**Read `reference/` files as needed — do not read them all up front.**
|
|
20
15
|
|
|
21
16
|
| File | When |
|
|
22
17
|
| --- | --- |
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
18
|
+
| `reference/display.md` | Choosing colours, brightness, legibility; **AMOLED always-on and burn-in** |
|
|
19
|
+
| `reference/layout.md` | Positioning anything; content clipped, overlapping, or off-screen |
|
|
20
|
+
| `reference/testing.md` | Writing tests; a suspiciously clean test run |
|
|
21
|
+
| `reference/simulator.md` | Screenshots, settings not applying, monkeydo hanging |
|
|
22
|
+
| `reference/devices.md` | Adding device support, launcher icons, API levels |
|
|
23
|
+
| `reference/store.md` | Publishing, listing copy, IP questions |
|
|
24
|
+
| `reference/publishing.md` | Driving the store portal in a browser; upload/update flow, validator rejections |
|
|
30
25
|
|
|
31
26
|
## Tools
|
|
32
27
|
|
|
33
28
|
Run these rather than reinventing them. All are standalone.
|
|
34
29
|
|
|
35
30
|
```bash
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
bin/ciq-doctor # toolchain check: SDK, JDK, key, devices
|
|
32
|
+
bin/ciq-devices # survey installed devices by resolution
|
|
33
|
+
bin/ciq-devices --same-as fenix6pro # products you can add with no code change
|
|
34
|
+
bin/ciq-capture out.png # calibrated simulator screenshot
|
|
35
|
+
bin/ciq-capture out.png --face --size 260 # cropped + masked to the round display
|
|
36
|
+
bin/ciq-calibrate # re-derive the display rect if capture looks wrong
|
|
37
|
+
bin/ciq-release # pre-submission check: package, screenshots, icon, keys, copy
|
|
43
38
|
```
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
`bin/ciq-release` is what you run before opening the store portal. Every check
|
|
41
|
+
in it is something otherwise discovered halfway through the submission form --
|
|
42
|
+
a stale screenshot set, an icon still copied from the last project, or copy
|
|
43
|
+
containing a character the description validator rejects.
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
`bin/ciq-capture` exists because `screencapture -R` grabs a screen *region*, not
|
|
51
46
|
a window: without a frontmost check it silently photographs whatever is on top,
|
|
52
47
|
and the window moves between simulator restarts. Both failure modes produce a
|
|
53
48
|
plausible PNG of the wrong thing.
|
|
54
49
|
|
|
55
|
-
## The
|
|
50
|
+
## The seven things that will bite you
|
|
56
51
|
|
|
57
52
|
### 1. `make test` can compile zero tests and report success
|
|
58
53
|
|
|
@@ -62,7 +57,7 @@ get `BUILD SUCCESSFUL` and a green run with nothing compiled — even when the t
|
|
|
62
57
|
files reference symbols that no longer exist.
|
|
63
58
|
|
|
64
59
|
Fix: a second jungle passed as an additional `-f`. See
|
|
65
|
-
|
|
60
|
+
`templates/monkey.test.jungle` and the `test` target in `templates/Makefile`.
|
|
66
61
|
|
|
67
62
|
**Prove it before you trust it.** Put a deliberately unresolvable symbol in one
|
|
68
63
|
test and confirm the build fails:
|
|
@@ -83,6 +78,13 @@ they look. Measured on fenix 6 Pro:
|
|
|
83
78
|
| `FONT_SMALL` | 32 |
|
|
84
79
|
| `FONT_NUMBER_MEDIUM` | 74 |
|
|
85
80
|
|
|
81
|
+
**Font tiers already scale with the device.** `FONT_XTINY` is a tier, not a
|
|
82
|
+
pixel height: a 454px watch supplies a proportionately taller glyph than a
|
|
83
|
+
260px one, unasked. So when text looks wrong at a new resolution, the bug is a
|
|
84
|
+
LENGTH that failed to scale, never the font. Do not add screen-size branching
|
|
85
|
+
to pick a bigger tier -- doing so double-scales, and labels end up wider than
|
|
86
|
+
the containers naming them.
|
|
87
|
+
|
|
86
88
|
Content drawn past the screen height is simply invisible — no error, no warning,
|
|
87
89
|
no clipping indicator. A two-line `FONT_NUMBER_MEDIUM` block is 148px of a 260px
|
|
88
90
|
face.
|
|
@@ -97,7 +99,7 @@ function clampRuleY(derived as Number, footerH as Number) as Number {
|
|
|
97
99
|
}
|
|
98
100
|
```
|
|
99
101
|
|
|
100
|
-
See `
|
|
102
|
+
See `reference/layout.md`.
|
|
101
103
|
|
|
102
104
|
### 3. The screen is round; your layout is not
|
|
103
105
|
|
|
@@ -126,7 +128,7 @@ simulator's backlit LCD.
|
|
|
126
128
|
Corollary: a saturated colour on a glyph reflects only its own channel, making
|
|
127
129
|
the thing you want to read the *dimmest* thing on screen.
|
|
128
130
|
|
|
129
|
-
See `
|
|
131
|
+
See `reference/display.md`.
|
|
130
132
|
|
|
131
133
|
### 5. The simulator lies about settings
|
|
132
134
|
|
|
@@ -137,14 +139,59 @@ settings code.
|
|
|
137
139
|
|
|
138
140
|
Reset also drops the loaded device, so relaunch and re-push afterwards.
|
|
139
141
|
|
|
140
|
-
See `
|
|
142
|
+
See `reference/simulator.md`.
|
|
143
|
+
|
|
144
|
+
### 6. On AMOLED, the face you designed is the one that fails review
|
|
145
|
+
|
|
146
|
+
`requiresBurnInProtection` devices must show a restricted always-on frame
|
|
147
|
+
between wrist raises. The trap is that the *stronger* your face's identity —
|
|
148
|
+
a bright panel, a filled dial — the worse a dimmed version of it performs,
|
|
149
|
+
because it still lights most of the screen. The always-on frame has to be a
|
|
150
|
+
different drawing: outlines where there were fills, and shifted a few pixels on
|
|
151
|
+
a cycle so no pixel is driven continuously.
|
|
152
|
+
|
|
153
|
+
Also note the flag can be **null** on older products, and a null propagating
|
|
154
|
+
into a `Boolean` field throws at the first wrist drop.
|
|
155
|
+
|
|
156
|
+
See the AMOLED section of `reference/display.md`.
|
|
157
|
+
|
|
158
|
+
### 7. A watch face CAN have settings on the watch
|
|
159
|
+
|
|
160
|
+
`AppBase.getSettingsView()` has existed since **API 3.2.0** and the SDK
|
|
161
|
+
documents it as "only applicable to watch faces and data fields". Plenty of
|
|
162
|
+
store copy — including, at one point, this author's own — claims settings are
|
|
163
|
+
reachable only from the phone. They are not.
|
|
164
|
+
|
|
165
|
+
The override signature must include `or Null` or the compiler rejects it as
|
|
166
|
+
narrowing:
|
|
167
|
+
|
|
168
|
+
```monkeyc
|
|
169
|
+
function getSettingsView() as
|
|
170
|
+
[WatchUi.Views] or [WatchUi.Views, WatchUi.InputDelegates] or Null {
|
|
171
|
+
return [new SettingsMenu(), new SettingsMenuDelegate()];
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Two things to get right in the menu itself:
|
|
176
|
+
|
|
177
|
+
- `ToggleMenuItem` has **already flipped its own state** by the time `onSelect`
|
|
178
|
+
runs. Read `isEnabled()`; negating the stored value inverts the setting.
|
|
179
|
+
- A picker should `setFocus()` the current choice, not open at the top of a long
|
|
180
|
+
list. If the list filters out unavailable options, item position is *not* the
|
|
181
|
+
option id — count the focus row as you build the list.
|
|
182
|
+
|
|
183
|
+
Keep one module that owns every property read, each wrapped with a default, and
|
|
184
|
+
have both the phone path and the on-watch menu go through it. Two readers with
|
|
185
|
+
two sets of defaults is exactly how the watch and the phone come to disagree
|
|
186
|
+
about what "off" means — and check the fallbacks actually match
|
|
187
|
+
`properties.xml`, because nothing enforces that.
|
|
141
188
|
|
|
142
189
|
## Workflow
|
|
143
190
|
|
|
144
191
|
### Starting a face
|
|
145
192
|
|
|
146
|
-
1.
|
|
147
|
-
2. Copy
|
|
193
|
+
1. `bin/ciq-doctor` — confirm SDK, JDK, developer key, target device installed.
|
|
194
|
+
2. Copy `templates/Makefile`, `templates/monkey.jungle`, `templates/monkey.test.jungle`.
|
|
148
195
|
3. Generate a fresh app id: `python3 -c "import uuid;print(uuid.uuid4().hex)"`.
|
|
149
196
|
4. Split source by responsibility. This structure has held up well:
|
|
150
197
|
|
|
@@ -173,7 +220,7 @@ Capture and *look at it*. Layout bugs are invisible in a passing test run:
|
|
|
173
220
|
|
|
174
221
|
```bash
|
|
175
222
|
make build && make sim
|
|
176
|
-
|
|
223
|
+
bin/ciq-capture /tmp/face.png --face --size 260
|
|
177
224
|
```
|
|
178
225
|
|
|
179
226
|
Then Read the PNG. Every layout bug in this skill's history was found by looking,
|
|
@@ -206,7 +253,7 @@ are what makes it recognisable:
|
|
|
206
253
|
- **Where the legends sit** — maker's name on the bezel, not the glass.
|
|
207
254
|
|
|
208
255
|
No system font is a segment display. If you need one, draw it: see
|
|
209
|
-
`
|
|
256
|
+
`reference/layout.md` for a working seven-segment renderer, including the ghost
|
|
210
257
|
(unlit) segments that are most of what sells the effect.
|
|
211
258
|
|
|
212
|
-
**Do not print a real brand on the face.** See `
|
|
259
|
+
**Do not print a real brand on the face.** See `reference/store.md`.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Proposing a face in SVG before writing Monkey C
|
|
2
|
+
|
|
3
|
+
A Monkey C iteration is a build, a simulator launch, a screenshot and a look:
|
|
4
|
+
minutes. An SVG iteration is a file write: seconds. Every visual decision that
|
|
5
|
+
can be made in SVG should be made there, because the ones that reach Monkey C
|
|
6
|
+
get made once and then defended.
|
|
7
|
+
|
|
8
|
+
The failures this prevents are the expensive ones -- a subdial layout that turns
|
|
9
|
+
out to be unreadable, a status row that collides with the index band, a digit
|
|
10
|
+
cell too small for the stroke inside it. All of those survive a green test suite
|
|
11
|
+
and only show up in a capture.
|
|
12
|
+
|
|
13
|
+
## The rule that makes a mock worth anything
|
|
14
|
+
|
|
15
|
+
**An SVG mock must be constrained to what the panel can draw, or it is a lie.**
|
|
16
|
+
|
|
17
|
+
A browser gives you 16.7 million colours, alpha, gradients, blur and free
|
|
18
|
+
anti-aliasing. A fenix MIP panel gives you 64 colours on a 4x4x4 lattice, no
|
|
19
|
+
alpha blending at all, and no anti-aliasing. A mock that uses the browser's
|
|
20
|
+
extras gets approved and then cannot be implemented, and the gap is discovered
|
|
21
|
+
after the geometry is already committed.
|
|
22
|
+
|
|
23
|
+
`scripts/ciq-mock` exists to close that gap: it renders each proposal with every
|
|
24
|
+
colour quantised to the lattice, inside the round bezel, and lists what will not
|
|
25
|
+
survive.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
<skill-dir>/scripts/ciq-mock /tmp/proposals.html a.svg b.svg c.svg --device fenix6pro
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then **open it and look at it**, the same way you would a simulator capture.
|
|
32
|
+
|
|
33
|
+
## What to write, and what not to
|
|
34
|
+
|
|
35
|
+
| Use | Not |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `<rect>` `<circle>` `<line>` `<polygon>` `<path>` | `<image>`, `<pattern>` |
|
|
38
|
+
| flat `fill` on the lattice | `<linearGradient>`, `<radialGradient>` |
|
|
39
|
+
| `stroke-width` 2 or more | hairlines; a 1px diagonal is a dotted line |
|
|
40
|
+
| solid colour to separate shapes | `opacity` / `fill-opacity` of any kind |
|
|
41
|
+
| `<g transform="rotate(...)">` for hands | anything you cannot express as sin/cos |
|
|
42
|
+
| `<text>` as a *placeholder* | `<text>` as a measurement |
|
|
43
|
+
|
|
44
|
+
`<path>` is fine to draw with, but nothing in Monkey C consumes one: it has to
|
|
45
|
+
become `fillPolygon`, `drawArc` or `drawLine`. If a shape cannot be decomposed
|
|
46
|
+
into those, it is not a shape you can ship.
|
|
47
|
+
|
|
48
|
+
`<text>` deserves its own warning. Browser font metrics are not device font
|
|
49
|
+
metrics and never will be. Use text to reserve *approximate* space, then derive
|
|
50
|
+
the real box from `dc.getFontHeight()` and `dc.getTextWidthInPixels()` at draw
|
|
51
|
+
time. See `layout.md`.
|
|
52
|
+
|
|
53
|
+
## The lattice
|
|
54
|
+
|
|
55
|
+
`0x00 / 0x55 / 0xAA / 0xFF` per channel. Write colours on it directly and the
|
|
56
|
+
mock and the watch agree. Write anything else and `ciq-mock` will tell you what
|
|
57
|
+
it becomes -- usually a bigger jump than expected, and two carefully separated
|
|
58
|
+
greys frequently collapse onto one.
|
|
59
|
+
|
|
60
|
+
Rank by luminance, not by hue: on a transflective panel brightness *is*
|
|
61
|
+
reflectance. See `display.md`.
|
|
62
|
+
|
|
63
|
+
## The bezel
|
|
64
|
+
|
|
65
|
+
`ciq-mock` checks the farthest point of every `rect`, `circle`, `ellipse`,
|
|
66
|
+
`line`, `polygon` and `polyline` against the glass radius. It does **not** check
|
|
67
|
+
`<path>` shapes -- that needs a real path renderer and remains unchecked -- and
|
|
68
|
+
it does not validate text extents (font metrics come from the device, not the
|
|
69
|
+
browser).
|
|
70
|
+
|
|
71
|
+
The check that matters most is the rectangle one: a rectangle inscribed in a
|
|
72
|
+
circle fails at its **corners** long before its sides, and the amount by which
|
|
73
|
+
it fails is `sqrt(w^2 + h^2)/2`, not `w/2`. Radial markers have the same
|
|
74
|
+
problem: a marker of width `w` ending at radius `r` actually reaches
|
|
75
|
+
`sqrt(r^2 + (w/2)^2)`.
|
|
76
|
+
|
|
77
|
+
## Proposing to a human
|
|
78
|
+
|
|
79
|
+
Exactly three variants, not one and not seven. One is an ultimatum; seven is a survey.
|
|
80
|
+
|
|
81
|
+
Make the three differ on **one axis you actually want an answer about** --
|
|
82
|
+
information density, or dial polarity, or analog versus digital -- and hold
|
|
83
|
+
everything else constant. Variants that differ in five ways at once produce
|
|
84
|
+
"I like bits of each", which is not a decision.
|
|
85
|
+
|
|
86
|
+
State what is fixed by the device (resolution, colour count, no alpha) so the
|
|
87
|
+
feedback lands on choices rather than constraints, and say plainly that colours
|
|
88
|
+
shown are post-quantisation.
|
|
89
|
+
|
|
90
|
+
## After approval
|
|
91
|
+
|
|
92
|
+
Freeze the chosen variant into constants before writing drawing code: every
|
|
93
|
+
radius, every centre, every width, as named values in `Layout.mc`. The SVG is a
|
|
94
|
+
picture; `Layout.mc` is the specification, and keeping it free of `Dc` calls is
|
|
95
|
+
what makes the geometry unit-testable.
|
|
96
|
+
|
|
97
|
+
Then re-derive the mock from those constants if you change them. A stale mock
|
|
98
|
+
that no longer matches the code is worse than none, because it is what the human
|
|
99
|
+
remembers approving.
|
|
@@ -90,9 +90,121 @@ does not "helpfully" add products:
|
|
|
90
90
|
bezel. Another size or shape will compile and look wrong, not fail. -->
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
## Launcher icons differ within a resolution family
|
|
94
|
+
|
|
95
|
+
`ciq-devices --same-as <device>` prints this, and it is easy to skip past. Sizes
|
|
96
|
+
seen in practice: 35×35 (vivoactive 4, the legacy hero editions), 40×40 (fenix
|
|
97
|
+
6/7 at 260×260), 60×60 (descent mk3 51mm, epix 2 pro 51mm), 65×65 (the 454×454
|
|
98
|
+
AMOLED group).
|
|
99
|
+
|
|
100
|
+
Give each size its own `resourcePath` in the jungle, listed *after* the base so
|
|
101
|
+
its drawables win:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
fr970.resourcePath = $(base.resourcePath);resources-65
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Do not upsample.** A 40×40 icon scaled to 65×65 turns 2px segment bars into
|
|
108
|
+
grey smears. Draw it at size — render at 8× and downsample once with a good
|
|
109
|
+
filter, which is what gives rounded corners and stroke ends clean edges at these
|
|
110
|
+
sizes. Keep the generator in `tools/` so the next size is one command.
|
|
111
|
+
|
|
112
|
+
## Making `Layout` resolution-aware
|
|
113
|
+
|
|
114
|
+
Worth doing once a face has traction, and much less risky than it sounds if you
|
|
115
|
+
treat the original numbers as a **design grid** rather than replacing them:
|
|
116
|
+
|
|
117
|
+
```monkeyc
|
|
118
|
+
const DESIGN = 260; // a unit, not a screen size
|
|
119
|
+
var FRAME_W as Number = 204; // var, not const -- init() rewrites these
|
|
120
|
+
|
|
121
|
+
// Round, don't truncate: truncation biases every constant down by up to a
|
|
122
|
+
// pixel, and across a gasket and two insets that is a visible seam.
|
|
123
|
+
function grid(value as Number) as Number {
|
|
124
|
+
return ((value * SCREEN) + (DESIGN / 2)) / DESIGN;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function init(width as Number) as Void {
|
|
128
|
+
SCREEN = width;
|
|
129
|
+
CENTER = width / 2;
|
|
130
|
+
FRAME_W = grid(204);
|
|
131
|
+
// ...
|
|
132
|
+
if (STROKE < 1) { STROKE = 1; } // a stroke rounded to 0 draws nothing
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Call it from `onLayout(dc)` with `dc.getWidth()` — never from a device name, so
|
|
137
|
+
a new product needs no code at all.
|
|
138
|
+
|
|
139
|
+
**Constants outside `Layout.mc` are the trap.** Widget modules accumulate their
|
|
140
|
+
own pixel values -- an arc radius, a digit cell, a gap -- and a design-grid
|
|
141
|
+
conversion that only edits `Layout.mc` leaves every one of them at the old size.
|
|
142
|
+
The result compiles, passes every test, and renders as a small face marooned in
|
|
143
|
+
the middle of a larger screen. Grep the whole `source/` tree for numeric
|
|
144
|
+
constants, not just the geometry module, and give each widget its own `build()`
|
|
145
|
+
called from `onLayout` after `Layout.init`. Exclude angles and bit masks: a
|
|
146
|
+
sweep is 270 degrees at every size.
|
|
147
|
+
|
|
148
|
+
Three things this gets wrong if you are not careful:
|
|
149
|
+
|
|
150
|
+
1. **Relational tests keep passing at the new size but prove nothing new.** Most
|
|
151
|
+
layout assertions are written as comparisons, so they hold at any scale
|
|
152
|
+
*given whatever Layout currently holds*. Add a test that sweeps every shipped
|
|
153
|
+
resolution, calling `init()` for each and restoring the design grid on exit.
|
|
154
|
+
2. **A scale function that ignored its argument would pass all of those.** Pin
|
|
155
|
+
growth explicitly: 204 on the grid must land near 356 at 454.
|
|
156
|
+
3. **Rounding direction is untested by default.** Assert `grid(1) == 2` at 454,
|
|
157
|
+
not just `grid(1) >= 1`.
|
|
158
|
+
|
|
159
|
+
### Derived constants are baked at compile time
|
|
160
|
+
|
|
161
|
+
This is the one that will cost you an afternoon. A constant defined in terms of
|
|
162
|
+
another constant is evaluated ONCE, at build time, from the design-grid values:
|
|
163
|
+
|
|
164
|
+
```monkeyc
|
|
165
|
+
const SUB_N_X = CENTER; // baked as 130, forever
|
|
166
|
+
const SUB_N_Y = CENTER - SUB_OFFSET; // baked as 78
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`init()` rewriting `CENTER` does not move them. On a 454 screen every one of
|
|
170
|
+
those subdials stayed clustered in the top-left corner, on top of each other and
|
|
171
|
+
the crest, while the chapter ring and hands scaled perfectly. It compiled, and
|
|
172
|
+
every test passed.
|
|
173
|
+
|
|
174
|
+
Convert them to `var` and recompute them at the END of `init()`, after
|
|
175
|
+
everything they depend on is set.
|
|
176
|
+
|
|
177
|
+
**Give them real design-grid defaults, not 0.** Unit tests never call `init()`,
|
|
178
|
+
so a derived var initialised to 0 reads as `band 0` in assertions that were
|
|
179
|
+
previously passing for the right reason. Four layout tests failed that way, and
|
|
180
|
+
the failure looks like a geometry regression rather than an initialisation
|
|
181
|
+
order problem.
|
|
182
|
+
|
|
183
|
+
### A checklist for the whole conversion
|
|
184
|
+
|
|
185
|
+
Grep the entire `source/` tree, not just `Layout.mc`:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
grep -rn "const [A-Z_][A-Z_0-9]* =" source/
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Then sort what you find into three buckets:
|
|
192
|
+
|
|
193
|
+
| Bucket | Examples | Scale? |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| Lengths | radii, widths, offsets, gaps, Y positions | **yes** |
|
|
196
|
+
| Counts | station count, rows, bars, ticks | never |
|
|
197
|
+
| Angles and masks | sweep degrees, start angle, `SEG_A = 0x01` | never |
|
|
198
|
+
|
|
199
|
+
Scaling a count deforms the dial rather than resizing it, and scaling a bitmask
|
|
200
|
+
produces garbage. Both compile.
|
|
201
|
+
|
|
93
202
|
## Recommended order
|
|
94
203
|
|
|
95
204
|
1. Ship one resolution, verified on your own wrist.
|
|
96
205
|
2. Add the same-resolution family — biggest reach per unit of risk.
|
|
97
206
|
3. Make `Layout` resolution-aware only if it gets traction. It is real work and
|
|
98
207
|
it risks the pixel-exact look you tuned.
|
|
208
|
+
4. Crossing to AMOLED is a *separate* step from crossing resolution, even though
|
|
209
|
+
the 454×454 group makes them arrive together. See the always-on section in
|
|
210
|
+
`reference/display.md` — that is the part that fails review, not the layout.
|
|
@@ -115,6 +115,81 @@ function panelIsLighterThanItsSegments(logger as Logger) as Boolean {
|
|
|
115
115
|
|
|
116
116
|
## AMOLED devices
|
|
117
117
|
|
|
118
|
-
Venu
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
Venu, Forerunner 265/965/970, fenix 8 (non-solar) and epix are **emissive**.
|
|
119
|
+
Almost every rule above inverts, and the one that matters most is not about
|
|
120
|
+
colour at all.
|
|
121
|
+
|
|
122
|
+
Read the device rather than guessing — `compiler.json` carries `displayType`,
|
|
123
|
+
`bitsPerPixel` and `alphaBlendingSupport`:
|
|
124
|
+
|
|
125
|
+
| | fenix 6 Pro | Forerunner 970 |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| `displayType` | `mip` | `amoled` |
|
|
128
|
+
| Resolution | 260×260 | 454×454 |
|
|
129
|
+
| `bitsPerPixel` | 8 | 16 |
|
|
130
|
+
| `alphaBlendingSupport` | false | **true** |
|
|
131
|
+
| Launcher icon | 40×40 | **65×65** |
|
|
132
|
+
|
|
133
|
+
So on AMOLED you get anti-aliasing and real colour, and the 4×4×4 lattice stops
|
|
134
|
+
being a constraint. Lattice colours still render correctly, so a palette tuned
|
|
135
|
+
for MIP is safe to ship to both — it is merely conservative.
|
|
136
|
+
|
|
137
|
+
### The always-on frame is the whole problem
|
|
138
|
+
|
|
139
|
+
`System.getDeviceSettings().requiresBurnInProtection` is true on these devices.
|
|
140
|
+
Between wrist raises the face must show a restricted frame: a small fraction of
|
|
141
|
+
pixels lit, and not always the *same* pixels. Garmin rejects faces that ignore
|
|
142
|
+
this.
|
|
143
|
+
|
|
144
|
+
The property arrived after some supported products shipped and **can be null**,
|
|
145
|
+
so check both the symbol and the value — a null flowing into a `Boolean` field
|
|
146
|
+
throws at the first wrist drop:
|
|
147
|
+
|
|
148
|
+
```monkeyc
|
|
149
|
+
private function needsAlwaysOn() as Boolean {
|
|
150
|
+
var s = System.getDeviceSettings();
|
|
151
|
+
if (!(s has :requiresBurnInProtection)) { return false; }
|
|
152
|
+
var flag = s.requiresBurnInProtection;
|
|
153
|
+
return flag != null && flag;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**The always-on frame is a different drawing, not a dimmed one.** This is the
|
|
158
|
+
part that bites: if the face's identity is a large bright area — a lit LCD
|
|
159
|
+
panel, a white dial, a filled gauge — then dimming it still lights most of the
|
|
160
|
+
screen. Reach instead for what the real object looks like with the power off.
|
|
161
|
+
An outline where there was a fill; strokes where there was a panel.
|
|
162
|
+
|
|
163
|
+
Budget check: on 454×454 (206k pixels), seven-segment strokes for four digits
|
|
164
|
+
plus a 1px frame outline is roughly 2% lit. A filled rounded rectangle the size
|
|
165
|
+
of that frame alone is 40k pixels — 19%.
|
|
166
|
+
|
|
167
|
+
### Move the lit pixels
|
|
168
|
+
|
|
169
|
+
Static content burns the pixel even at low duty. Shift the whole block by a few
|
|
170
|
+
pixels on a cycle, and make the horizontal and vertical periods **coprime** so
|
|
171
|
+
the pair does not return to the same offset every few minutes:
|
|
172
|
+
|
|
173
|
+
```monkeyc
|
|
174
|
+
const SHIFT_PERIOD = 7; // x
|
|
175
|
+
const SHIFT_PERIOD_Y = 5; // y -> 35 distinct positions, not 7
|
|
176
|
+
|
|
177
|
+
function shiftX(minute as Number) as Number {
|
|
178
|
+
return (minute % SHIFT_PERIOD) - (SHIFT_PERIOD / 2);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Both periods dividing into 60 is the trap: with 6 and 3 you get 6 positions and
|
|
183
|
+
park a segment edge on the same pixels ten times an hour.
|
|
184
|
+
|
|
185
|
+
Test the arithmetic even though you cannot test the drawing — that the offsets
|
|
186
|
+
stay small enough to keep content on screen, that **every** offset in range is
|
|
187
|
+
actually visited (a `shiftX` stuck at 0 passes "stays small" and protects
|
|
188
|
+
nothing), and that the (x, y) pair yields the full period.
|
|
189
|
+
|
|
190
|
+
### Helpers that pick their own colour
|
|
191
|
+
|
|
192
|
+
Segment and hand renderers commonly call `dc.setColor(Palette.SEGMENT, ...)`
|
|
193
|
+
internally. Reusing them for the always-on frame silently repaints it in the
|
|
194
|
+
lit-face colour. Either pass the colour in, or draw the always-on frame against
|
|
195
|
+
the mask primitives directly.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Wizard mode: from "make me a watch face" to a frozen spec
|
|
2
|
+
|
|
3
|
+
Use this when the brief is a mood rather than a design -- "something modern",
|
|
4
|
+
"like a chronograph", "match my brand". Do not use it when the user already
|
|
5
|
+
knows what they want; skip to `design-proposals.md`.
|
|
6
|
+
|
|
7
|
+
The wizard is five stages and **two** questions to the human. Stopping at every
|
|
8
|
+
stage for approval is how a design session becomes a survey; stopping at none is
|
|
9
|
+
how you build the wrong face beautifully.
|
|
10
|
+
|
|
11
|
+
The order below is the point. Constraints come before ideas, because the device
|
|
12
|
+
facts invalidate whole categories of idea and it is cheaper to lose them now.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Stage 0 -- Constraints, before a single idea
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
<skill-dir>/scripts/ciq-doctor
|
|
20
|
+
<skill-dir>/scripts/ciq-devices --same-as <target>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then read the target's `compiler.json` yourself and write down four numbers:
|
|
24
|
+
|
|
25
|
+
| Fact | Why it kills ideas |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| resolution + shape | the round bezel eliminates most rectangular layouts |
|
|
28
|
+
| `bitsPerPixel` | 8bpp is 64 colours; photographic anything is out |
|
|
29
|
+
| `alphaBlendingSupport` | almost always false: no translucency, no soft edges |
|
|
30
|
+
| `watchFace` memoryLimit | typically ~112 KB: vector only, no bitmap dial |
|
|
31
|
+
|
|
32
|
+
A face conceived without these gets redesigned after it is built.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Stage 1 -- Gather
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
<skill-dir>/scripts/ciq-inspire --brand acme.com --json /tmp/tokens.json
|
|
40
|
+
<skill-dir>/scripts/ciq-inspire --image ~/crest.png
|
|
41
|
+
<skill-dir>/scripts/ciq-inspire --prior-art "chronograph"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Three axes worth gathering along:
|
|
45
|
+
|
|
46
|
+
- **Object** -- if there is a real watch or instrument behind the brief, its
|
|
47
|
+
proportions and polarity are what make it recognisable. Get those first; see
|
|
48
|
+
the homage section in `SKILL.md`.
|
|
49
|
+
- **Palette** -- from a brand domain, or from an image the user already owns.
|
|
50
|
+
Everything comes back quantised, so what you show is what ships.
|
|
51
|
+
- **Prior art** -- read other people's Monkey C for *technique*, not for code.
|
|
52
|
+
Most Connect IQ repositories carry no licence, which means all rights
|
|
53
|
+
reserved.
|
|
54
|
+
|
|
55
|
+
**IP boundary.** Use brand research and design references as inspiration only:
|
|
56
|
+
implementation must be original, and distinctive creative elements or
|
|
57
|
+
compositions must not be copied verbatim. Names, wordmarks and logos are not
|
|
58
|
+
permissible -- not in the face, the launcher icon, the screenshots or the copy.
|
|
59
|
+
`ciq-inspire --brand` will return another company's logo without complaint; that
|
|
60
|
+
is a research artefact, not an asset. See `store.md`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Stage 2 -- Question one: the axis
|
|
65
|
+
|
|
66
|
+
Ask the human **one** question, with three or four concrete options, about the
|
|
67
|
+
axis you cannot infer. In practice it is nearly always one of:
|
|
68
|
+
|
|
69
|
+
| Axis | Options that actually differ |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| Form | analog hands / digital numerals / hybrid |
|
|
72
|
+
| Density | core metrics only / moderate / dense |
|
|
73
|
+
| Polarity | dark dial, bright ink / light dial, dark ink |
|
|
74
|
+
| Character | instrument, tactical, minimal, retro homage |
|
|
75
|
+
|
|
76
|
+
Alongside it, settle the boring-but-load-bearing ones in the same breath,
|
|
77
|
+
because each changes the code shape rather than the look:
|
|
78
|
+
|
|
79
|
+
- **Which devices.** One device is a manifest line; a second resolution is a
|
|
80
|
+
second layout. Widening later is cheap only if you stayed vector-only.
|
|
81
|
+
- **Sweeping seconds.** This is a battery decision, not an aesthetic one. It
|
|
82
|
+
forces `onPartialUpdate`, a 20 ms budget, clip-box arithmetic and an
|
|
83
|
+
`onPowerBudgetExceeded` fallback ladder. Costs roughly a third of the build.
|
|
84
|
+
- **Settings.** Themes and toggles mean `properties.xml`, `settings.xml`, and
|
|
85
|
+
the simulator's habit of ignoring changed defaults. Also: watch face settings
|
|
86
|
+
are unreachable from the watch, only from the phone app -- if you add them,
|
|
87
|
+
the listing has to say so or every user reports them missing.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Stage 3 -- Diverge: exactly three variants as SVG
|
|
92
|
+
|
|
93
|
+
Write exactly three SVG files that differ on **the one axis you asked about**,
|
|
94
|
+
holding everything else constant. Then:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
<skill-dir>/scripts/ciq-mock /tmp/proposals.html a.svg b.svg c.svg --device <target>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Read `design-proposals.md` before writing the SVG -- the constraints there are
|
|
101
|
+
what stop a mock promising something the panel cannot render. Open the HTML and
|
|
102
|
+
look at it yourself before showing anyone. Fix anything the audit flags; an
|
|
103
|
+
approved design with a bezel violation in it is a design that will be quietly
|
|
104
|
+
changed later.
|
|
105
|
+
|
|
106
|
+
Density is where these get decided in practice. The most-cited reason people
|
|
107
|
+
uninstall a fenix face is data crammed in too small to read -- so if the
|
|
108
|
+
variants differ in density, the dense one must be honestly dense, at real font
|
|
109
|
+
sizes, or the comparison is rigged.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Stage 4 -- Question two: converge
|
|
114
|
+
|
|
115
|
+
Show the sheet. Ask which variant, and what to change about it. Expect the
|
|
116
|
+
answer to be "B, but with A's date placement" -- that is a good answer and it is
|
|
117
|
+
why the variants held everything else constant.
|
|
118
|
+
|
|
119
|
+
Do **one** revision round, re-run `ciq-mock`, and stop. A third round means the
|
|
120
|
+
axis in Stage 2 was the wrong one; go back and ask a better question rather than
|
|
121
|
+
iterating on pixels.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Stage 5 -- Freeze
|
|
126
|
+
|
|
127
|
+
Turn the approved picture into a specification before writing drawing code:
|
|
128
|
+
|
|
129
|
+
- every radius, centre, width and offset as a named constant in `Layout.mc`
|
|
130
|
+
- the palette as **roles** (`DIAL`, `RULE`, `INDEX`, `HAND`, `ACCENT`), not as
|
|
131
|
+
colour names, so a second theme is a table row rather than a rewrite
|
|
132
|
+
- the draw order, written down -- a later fill erases an earlier ring
|
|
133
|
+
- the two or three places where the layout is nearly out of room, with the
|
|
134
|
+
remedy already chosen, so the fix later is not an ad-hoc nudge
|
|
135
|
+
|
|
136
|
+
Keep `Layout.mc` free of `Dc` calls. That is what lets the geometry be
|
|
137
|
+
unit-tested against the bezel across the full sweep, which is the only automated
|
|
138
|
+
check that catches a clipped layout.
|
|
139
|
+
|
|
140
|
+
Then, and only then, start Phase 0 of the build.
|