spine-rigc 1.0.2 → 1.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.
- package/README.md +37 -5
- package/cli.ts +562 -48
- package/docs/AUTHORING.md +156 -7
- package/docs/MOTION.md +1 -0
- package/package.json +1 -1
- package/skills/rigc/SKILL.md +42 -12
- package/skills/{face → rigc-face}/SKILL.md +11 -7
- package/skills/{ingest → rigc-ingest}/SKILL.md +10 -6
- package/skills/{motion → rigc-motion}/SKILL.md +11 -7
- package/skills/{rigging → rigc-rigging}/SKILL.md +11 -7
- package/src/ballot.ts +9 -12
- package/src/check.ts +181 -5
- package/src/checkpics.ts +293 -0
- package/src/preview.ts +223 -32
- package/src/render.ts +140 -2
package/docs/AUTHORING.md
CHANGED
|
@@ -123,6 +123,8 @@ bun cli.ts check \
|
|
|
123
123
|
# …and if nobody gave you frames, LOOK at it instead — neither needs a reference:
|
|
124
124
|
bun cli.ts render --candidate path/to/spine # PNG frames + a contact sheet grid
|
|
125
125
|
bun cli.ts preview --candidate path/to/spine # one .html that plays it in Spine's own player
|
|
126
|
+
# ↳ a green build ends by naming this command for its own --out; repeat
|
|
127
|
+
# --candidate for a pane per candidate on one page
|
|
126
128
|
|
|
127
129
|
# …and where you have several green candidates and no instrument that separates
|
|
128
130
|
# them, ask a human — the one loop step this toolchain cannot run for you:
|
|
@@ -141,6 +143,27 @@ the animation is the one in the frames, and there is no assertion that could —
|
|
|
141
143
|
§9. The two run in that order because `check` needs artifacts on disk and `build`
|
|
142
144
|
only writes them when the gate is green.
|
|
143
145
|
|
|
146
|
+
🎞️ **When the source is a foreign player — a Live2D model, a Unity scene, a video —
|
|
147
|
+
make the reference frames first, from the source.** rigc reads none of those
|
|
148
|
+
formats and will not ([FACE.md](FACE.md#11-non-goals--stated-so-nobody-proposes-them-as-gaps)
|
|
149
|
+
§11, the paragraph that opens *No Live2D file is read or written*); what it reads
|
|
150
|
+
is the pictures they produce. Render the source at the rate you will check at into
|
|
151
|
+
one directory of `f0000.png`, `f0001.png`… named after the candidate animation it
|
|
152
|
+
shows (or pair the two with `--as`): `check` accepts a set with no `frames.json`
|
|
153
|
+
and takes its rate from `--fps` (§9). What that set cannot carry is its
|
|
154
|
+
**background**, which the sidecar would have recorded — so render it onto an
|
|
155
|
+
**opaque** background of the colour `check`'s no-`frames.json` note names
|
|
156
|
+
(`232, 232, 232, 255` in this release). The content box is found against that
|
|
157
|
+
colour and alpha is not read: measured on the 24 frames of `gallery/look`'s `turn`,
|
|
158
|
+
the same drawn pixels read MAE mean **2.25** over that grey, **5.19** over white and
|
|
159
|
+
**20.37** over a transparent background, where the fit took the whole 234×256
|
|
160
|
+
frame for the figure — all three at exit 0 with the same notes. A port with no
|
|
161
|
+
reference frames is **unmeasured, not finished**: green from `build` says the file
|
|
162
|
+
is valid and nothing about whether it is the source's picture. And the parts are
|
|
163
|
+
the source's own texture cut along its drawables, **never a screenshot of it** — a
|
|
164
|
+
screenshot is the composed result, so a part cut from it carries every part under
|
|
165
|
+
it, which is what the first of the three questions under *LOOK* below finds.
|
|
166
|
+
|
|
144
167
|
🚨 **Read `check`'s per-frame column before its MAE.** The table's headline figures
|
|
145
168
|
are the MAE and the slot drift, and a reader who came for those will skip the
|
|
146
169
|
`per-frame` line printed under them — but that line is the only thing in this
|
|
@@ -159,7 +182,7 @@ What the flags mean:
|
|
|
159
182
|
| --- | --- |
|
|
160
183
|
| `--rig` | the rig spec — skeleton structure |
|
|
161
184
|
| `--motion` | the motion spec — time |
|
|
162
|
-
| `--out` | directory for `skeleton.json` + `skeleton.atlas`; atlas page paths and `skeleton.images` are written relative to it |
|
|
185
|
+
| `--out` | directory for `skeleton.json` + `skeleton.atlas`; atlas page paths and `skeleton.images` are written relative to it. On `check` it is a directory of **pictures** instead: one per frame the table lists (every compared frame under `--all-frames`), reference · candidate · difference · overlay at the comparison grid's native size, as `<dir>/<set>/f####.png` beside a `frames.json` that says what they are pictures of. Each `<dir>/<set>/` is cleared first; a file at `<dir>`, or a directory that is or holds `--frames`, is refused by name — **§9.2.1** |
|
|
163
186
|
| `--copy-images` | `build` only: also copies every page **the emitted atlas names** into `--out` and rewrites the atlas to the copies, so the directory is self-contained enough to zip or commit on its own, and points `skeleton.images` at `--out` itself so the editor's import finds the parts beside the skeleton (§3.1 says why it is spelled `../<out>/` and not `./`). Under `--atlas-in` those pages are the pack's, not one per part (**§0.2**). Without it, page paths point at the source art |
|
|
164
187
|
| `--pack` | `build` only: arrange every part onto **shared** atlas page(s), written into `--out` as real PNGs, instead of one page per part. Lossless — nothing is resampled, trimmed or rotated. The default is one page per part — **§0.1** |
|
|
165
188
|
| `--page-size` | `build --pack` only: the largest page edge (default `2048`). A ceiling, not the size: page edges are powers of two and the one written is the smallest that holds the pack — **§0.1** |
|
|
@@ -170,8 +193,8 @@ What the flags mean:
|
|
|
170
193
|
| `--cut` | `build`, `explain` and `validate`: look up a named cut in `--cuts <cuts.json>`, **instead of** `--rig`/`--motion`/`--out` — the two spellings are one build stated two ways and are refused together. A `cuts.json` is `{ "<name>": { "rig": …, "motion": …, "out": …, "manifest"?: … } }`, every path in it relative to the table's own file, so the table lives with the project that owns the art |
|
|
171
194
|
| `--cuts` | the `cuts.json` `--cut` names. Required beside it — `--cut` alone is refused, with no guess at where the table lives |
|
|
172
195
|
| `--profile` | `spine` = the 34 validity rules (**the default**) · `spine-html` = all 49, opt-in |
|
|
173
|
-
| `--candidate` | `check`, `bench`, `render`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. `--atlas <path>` names the atlas when it does not sit beside the skeleton. **`vote`
|
|
174
|
-
| `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview
|
|
196
|
+
| `--candidate` | `check`, `bench`, `render`, `preview`, `chainfit` and `vote` only: a **compiled** artifact — the directory `build --out` wrote, or a `skeleton.json` path. `--atlas <path>` names the atlas when it does not sit beside the skeleton. **`vote` and `preview` take it more than once**: `vote` 2–4 times, one per pane, labelled A, B, C, D in the order given; `preview` any number of times, one pane per candidate in the order given, each headed by its path and its gate line, and the same skeleton twice (a directory and its `skeleton.json` are one) is refused by name. `--atlas` goes with one candidate only. Everywhere else a repeat is a typo and is refused |
|
|
197
|
+
| `--animation` | `render`, `preview` and `vote` only: which animation to show. The default is **every** one for `render`, the **first** for `preview` (each candidate's own first, with several), and for `vote` the first of candidate A. A name the skeleton does not have is refused, with the ones it does have listed — and for `vote` and a several-candidate `preview`, so is a name that only *some* candidates have, naming the one that lacks it |
|
|
175
198
|
| `--record` | `vote` only: a saved vote to check against its ballot and append to the ledger, instead of writing a ballot. This is the command's second mode; it takes no `--candidate` |
|
|
176
199
|
| `--ballot` | `vote --record` only: the ballot the vote answers (default `ballot.html`). Its embedded manifest is what the vote is checked against, so the ballot file is the record of the question |
|
|
177
200
|
| `--ledger` | `vote --record` only: the append-only JSONL the vote lands in (default `votes.jsonl`), one vote per line |
|
|
@@ -186,6 +209,8 @@ What the flags mean:
|
|
|
186
209
|
| `--min-visible` | `chainfit` only: below this share of a part surviving the parts drawn over it, the placement is refused `occluded` instead of reported flat (default `0.25`) — §12.4 |
|
|
187
210
|
| `--passes` | `chainfit` only: how many times the occluder masks are rebuilt from the answers and the fit rerun (default `2`) — §12.4 |
|
|
188
211
|
| `--anchor-residual` | `chainfit` only: the residual a `pose` placement must be within to anchor a chain (default `0.16`) — §12.2 |
|
|
212
|
+
| `--dir` | `skills install` only: the directory to install the agent skills into, resolved against your working directory (default `.agents/skills`, which Codex, Gemini CLI and Antigravity read). The skills are the `skills/` directory of the package the command runs from, never the working directory's |
|
|
213
|
+
| `--copy` | `skills install` only: copy each skill folder instead of writing a relative symlink to it. A copy is not reached by an upgrade of the package; one that is no longer the package's bytes is refused on the next run, naming the first file that differs |
|
|
189
214
|
|
|
190
215
|
`render` also takes `--fps <n>` (the rate it samples at, default 12 — the same
|
|
191
216
|
protocol rate the reference frames use) and `--max <px>` (the long side of a
|
|
@@ -193,6 +218,26 @@ frame, default 256). Three commands take `--out`: a directory for `render`
|
|
|
193
218
|
(default `render/`), the `.html` file for `preview` (default `preview.html`) and
|
|
194
219
|
for `vote` (default `ballot.html`).
|
|
195
220
|
|
|
221
|
+
`skills install` is the one command that is not about a rig: it puts the agent
|
|
222
|
+
skills the package ships where an agent host looks for them. Run it through the
|
|
223
|
+
project's own install — `bun rigc skills install` — so the links point into that
|
|
224
|
+
project's `node_modules/spine-rigc/skills/`. A second run has nothing to do and
|
|
225
|
+
exits 0. An entry already at `<dir>/<name>` that is not a link to the same folder
|
|
226
|
+
(or, under `--copy`, not the same bytes) is refused, exit 1, and **nothing is
|
|
227
|
+
written** — every such entry is named with what is there and what was required:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
rigc skills install: <n> of the <m> skill(s) cannot be installed into <dir>, and nothing was written:
|
|
231
|
+
<dir>/rigc-motion is a plain file; a symlink to ../../node_modules/spine-rigc/skills/rigc-motion was required
|
|
232
|
+
Remove the entries named above, or pass --dir to install somewhere else.
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
What is found is one of `a plain file`, `a directory`, `a symlink to <text>, which
|
|
236
|
+
resolves to <path>` (or `to nothing`), and under `--copy` `a directory that is not
|
|
237
|
+
the package's copy (<file> differs)`. A link to the package's own folder is not in
|
|
238
|
+
the way however it is spelled, so an absolute link somebody else made is kept and
|
|
239
|
+
reported `already linked`.
|
|
240
|
+
|
|
196
241
|
Pick the profile deliberately, and know which one you got by saying nothing. The
|
|
197
242
|
default is `spine`: "is this valid Spine 4.3 that any runtime plays correctly",
|
|
198
243
|
which is the question a rig authored anywhere is asking. `--profile spine-html`
|
|
@@ -727,7 +772,8 @@ bun cli.ts diff candidate.json reference.json [--as <candidate>=<reference>]
|
|
|
727
772
|
bun cli.ts check --candidate path/to/spine --frames path/to/frames [--skin …]
|
|
728
773
|
bun cli.ts bench 3 --candidate path/to/spine [--frames path/to/frames]
|
|
729
774
|
bun cli.ts render --candidate path/to/spine [--animation …] [--skin …] [--fps 12] [--max 256]
|
|
730
|
-
bun cli.ts
|
|
775
|
+
bun cli.ts render --candidate path/to/spine --slot <name,…> | --hide <name,…> # part of the rig, same grid
|
|
776
|
+
bun cli.ts preview --candidate path/to/spine [--candidate path/to/another …] [--animation …] [--out preview.html]
|
|
731
777
|
bun cli.ts vote --candidate path/to/a --candidate path/to/b [--out ballot.html]
|
|
732
778
|
bun cli.ts vote --record vote-<id>.json [--ballot ballot.html] [--ledger votes.jsonl]
|
|
733
779
|
bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
|
|
@@ -802,12 +848,39 @@ bun cli.ts pose --images path/to/parts --frame poseA.png [--out pose.json]
|
|
|
802
848
|
gate can know you did not mean them. `render` writes
|
|
803
849
|
`render/<animation>/f0000.png…` with a `contact.png` grid of **every** frame
|
|
804
850
|
beside them (open that one first — spacing is a comparison across frames) and a
|
|
805
|
-
`frames.json` sidecar naming the world box they are pictures of. `
|
|
851
|
+
`frames.json` sidecar naming the world box they are pictures of. `contact.png`
|
|
852
|
+
is for spacing across frames; a defect is read on **one frame at full size**,
|
|
853
|
+
because a tile is too small to say which part a pixel belongs to. When a frame
|
|
854
|
+
looks wrong, render it again with `--hide <slot>` — the **largest attachment**
|
|
855
|
+
first, since it covers the most and is the likeliest to hide what is under it —
|
|
856
|
+
and compare the two frames: `--hide` and `--slot <name,…>` draw a subset of the
|
|
857
|
+
slots on the **same grid** as the whole rig (the viewport is still fitted to
|
|
858
|
+
every slot), so the frames overlay pixel for pixel and the difference is the
|
|
859
|
+
part. Three questions to ask of that full-size frame, each answered that way.
|
|
860
|
+
**Is any picture drawn twice?** Hide the largest attachment and look for a second
|
|
861
|
+
copy of what it covered, since a ghost under an opaque part is invisible until the
|
|
862
|
+
part is gone. **Is there a straight edge where the art has none?** A part cut as a
|
|
863
|
+
rectangle carries a border its drawing never had, and `--slot <that slot>` shows it
|
|
864
|
+
alone. **Does a part cover a feature the art shows?** An eye slot drawn over the
|
|
865
|
+
eye is found by hiding the slot and watching the feature come back. A name the skeleton does not declare is refused with every slot it does,
|
|
866
|
+
in draw order; a slot whose art lives only under another skin is refused naming
|
|
867
|
+
that skin (pass `--skin`); the two flags together are refused. `frames.json`
|
|
868
|
+
records the subset as `slots` or `hidden`, and **`check` refuses such a set as
|
|
869
|
+
a reference** (§9). `preview`
|
|
806
870
|
writes one self-contained `.html`: your skeleton, atlas and page PNGs are
|
|
807
871
|
embedded in it as data URIs and played by the official **Spine Web Player**, so
|
|
808
872
|
double-clicking it is also the interop proof — what plays there was played by
|
|
809
873
|
Esoteric Software's runtime, not by rigc's. The player is loaded from a CDN
|
|
810
|
-
rather than copied into the file, so the first open needs a network.
|
|
874
|
+
rather than copied into the file, so the first open needs a network. Its
|
|
875
|
+
header carries the gate's line for the candidate — the `N assertions: …`
|
|
876
|
+
summary `rigc validate <dir>` prints for the same files, and its first `FAIL`
|
|
877
|
+
line when there is one — with the rigc version, measured when the page is
|
|
878
|
+
written; a refused candidate is still previewed, because looking at a red
|
|
879
|
+
build is what the page is for. Note that it is the **bare-directory** reading:
|
|
880
|
+
with no rig spec and no second compile beside the files, `A09` and `A18` report
|
|
881
|
+
SKIP there, so its counts are not the ones `build` printed. It takes
|
|
882
|
+
neither `--slot` nor `--hide` — the player draws what the skeleton draws — and
|
|
883
|
+
refuses both by name rather than playing the whole rig as if it had obeyed.
|
|
811
884
|
- 📐 **`pose` is the only command here that reads an INPUT rather than a result.**
|
|
812
885
|
Everything else takes a spec or a build and tells you something about it; `pose`
|
|
813
886
|
takes a picture the user already has — a key pose — and reports where each loose
|
|
@@ -5996,7 +6069,8 @@ rather than the rig (**§9.2**'s atlas floor — and note that it is *not* `--at
|
|
|
5996
6069
|
which re-seats your geometry on that atlas's packing), `--skin <name>` to pose
|
|
5997
6070
|
your candidate under one of its skins (below), `--all-frames` to list
|
|
5998
6071
|
every frame instead of the worst by MAE, `--json <out>` for the whole per-frame,
|
|
5999
|
-
per-slot report
|
|
6072
|
+
per-slot report, `--out <dir>` for the picture each listed frame's figures came from
|
|
6073
|
+
(**§9.2.1**).
|
|
6000
6074
|
|
|
6001
6075
|
🚨 **What `check` certifies is the DEFAULT skin, unless you pass `--skin`.** With no `--skin` no skin is set at all, which is `spine-core`'s own
|
|
6002
6076
|
initial state: every slot resolves through `SkeletonData.defaultSkin` alone, and a
|
|
@@ -6034,6 +6108,21 @@ remember:
|
|
|
6034
6108
|
A skin name the candidate does not declare is refused with the ones it does —
|
|
6035
6109
|
`the candidate declares no skin "path"; it declares [default, patch, torn]`.
|
|
6036
6110
|
|
|
6111
|
+
🚫 **A frame set of PART of a rig is not a reference.** `render --slot`/`--hide`
|
|
6112
|
+
(§0) records the subset in `frames.json`, and `check` refuses any set that carries
|
|
6113
|
+
`slots` or `hidden`, before posing anything:
|
|
6114
|
+
|
|
6115
|
+
```
|
|
6116
|
+
rigc check error: --frames frames/ records a slot subset (hidden: head) in frames.json; a partial render is not a reference set — render the reference without --slot/--hide
|
|
6117
|
+
```
|
|
6118
|
+
|
|
6119
|
+
It is the skin clause one step stronger: two skins are two pictures of one rig,
|
|
6120
|
+
while a reference with a part left out is not a picture of the whole rig at all,
|
|
6121
|
+
and scoring a whole candidate against it would print a real figure about art the
|
|
6122
|
+
reference never drew. There is no flag that makes it comparable. A set with
|
|
6123
|
+
neither key — every set rendered without the flags, and every set written before
|
|
6124
|
+
they existed — is compared exactly as before.
|
|
6125
|
+
|
|
6037
6126
|
📌 **Deform measurement needs no such flag, because the timeline carries the
|
|
6038
6127
|
name.** `A39` and the `DEFORM` block pose each key with the skin that key is keyed
|
|
6039
6128
|
on — a deform timeline's address is a `skin / slot / attachment` triple — so a
|
|
@@ -6979,6 +7068,66 @@ slots column is a parenthesis rather than a fraction, the deformation it carries
|
|
|
6979
7068
|
already being scored inside the chain that owns the slot, and the row is telling you
|
|
6980
7069
|
about your bone tree rather than about a hole in your figure.
|
|
6981
7070
|
|
|
7071
|
+
#### 9.2.1 Reading the pictures — `--out`
|
|
7072
|
+
|
|
7073
|
+
```bash
|
|
7074
|
+
bun cli.ts check --candidate path/to/spine --frames path/to/reference/frames --out check-pictures
|
|
7075
|
+
```
|
|
7076
|
+
|
|
7077
|
+
The table says **how much** a frame differs; `--out <dir>` writes **where**. For
|
|
7078
|
+
every frame the table lists — the frames worth reading, or every compared frame
|
|
7079
|
+
under `--all-frames` — it writes `<dir>/<set>/f####.png`, and beside them a
|
|
7080
|
+
`frames.json` recording what they are pictures of: the candidate, the frames
|
|
7081
|
+
directory, both skins, the framing scope, the grid, and per set the box it was
|
|
7082
|
+
framed in and which frames were written. Each picture is four panes, left to right:
|
|
7083
|
+
|
|
7084
|
+
| Pane | What it is | The figure in its label |
|
|
7085
|
+
| --- | --- | --- |
|
|
7086
|
+
| `REFERENCE` | the frame as read from `--frames` | `REF D` — the table's `ref Δ`: pixels the reference moved since its own previous frame (`-` when that frame was not compared) |
|
|
7087
|
+
| `CANDIDATE` | your candidate exactly as `check` rendered it onto that grid — same framing, same skin, same texture | `DPX` — the table's `Δpx`, the same count on your side |
|
|
7088
|
+
| `DIFFERENCE` | per pixel, the largest of the three channel differences between the two, on a **fixed** grey ramp: 0 is black, 255 is white. Drawn over exactly the pixels the MAE averages over — what your geometry covers or the reference drew — and transparent everywhere else | `MAE` — the frame's MAE, as the table prints it |
|
|
7089
|
+
| `OVERLAY` | the two at 50 % each, so a displaced edge reads as a double line and a part drawn twice reads as a ghost | `UNION` — the table's `union px` |
|
|
7090
|
+
|
|
7091
|
+
Under the panes, one row per slot of that frame: its drift at the table's
|
|
7092
|
+
precision and how it was matched, or why it has none (`not drawn`, `no attributable
|
|
7093
|
+
drift`). The frame's worst slot — the one its table row names — is marked `>`. The
|
|
7094
|
+
text is the tree's 5x7 bitmap font, so it prints in capitals, and a character the
|
|
7095
|
+
font does not carry draws as a solid block rather than disappearing.
|
|
7096
|
+
|
|
7097
|
+
- **The ramp is fixed, and deliberately not stretched to the frame's own maximum.**
|
|
7098
|
+
Two runs' pictures are then comparable by eye, and a quiet frame looks quiet. The
|
|
7099
|
+
cost is the other side of the same choice: a frame whose MAE is a texture floor
|
|
7100
|
+
of a few points reads as a **near-black silhouette** of the union, because 7 of
|
|
7101
|
+
255 is dark. That silhouette is not nothing — it is where the two sides drew —
|
|
7102
|
+
and the grey you have to find is the difference you have to fix.
|
|
7103
|
+
- **Every pane is at the grid's native size, and there is no upscaling option.** An
|
|
7104
|
+
upscaled difference is a difference the instrument invented: a resampling filter
|
|
7105
|
+
spreads ink into pixels the comparison never measured. Open the file and zoom in
|
|
7106
|
+
your viewer instead, where the magnification is yours and visibly not the data.
|
|
7107
|
+
- **Nothing in a picture is a new number.** Every figure in it is one the report
|
|
7108
|
+
already holds. The rasters are the ones the figures were computed on, kept at the
|
|
7109
|
+
moment they were compared rather than rendered again.
|
|
7110
|
+
|
|
7111
|
+
What a picture cannot tell you, and does not try to:
|
|
7112
|
+
|
|
7113
|
+
- **Which side is right.** The difference pane is symmetric. A pixel lit there is
|
|
7114
|
+
a pixel the two disagree about; whether yours or the reference's is the one to
|
|
7115
|
+
move is yours to read off the other panes.
|
|
7116
|
+
- **Anything about a frame it does not show.** The set is the table's listing, so
|
|
7117
|
+
a frame outside it was not pictured — not pictured clean. `--all-frames` widens
|
|
7118
|
+
the set to every compared frame when the listing is not enough.
|
|
7119
|
+
|
|
7120
|
+
`<dir>/<set>/` is cleared and rewritten, as `render` clears its own, and nothing
|
|
7121
|
+
else under `<dir>` is touched. Two things are refused before anything is compared:
|
|
7122
|
+
a **file** at `<dir>` (`check: --out <path> is a file; it names a directory`), and a
|
|
7123
|
+
directory that **is `--frames` or holds it** (`check: --out <path> is --frames …` /
|
|
7124
|
+
`… holds --frames …`), because clearing a set directory there deletes the frames the
|
|
7125
|
+
pictures are of. And a `check --out` directory is not a frame set: pointed at by
|
|
7126
|
+
`--frames`, it is refused by the field that says so —
|
|
7127
|
+
`` frames.json carries "comparison": it was written by `rigc check --out` `` and the
|
|
7128
|
+
rest of the sentence names the frames it was made against. The reference pane inside each
|
|
7129
|
+
picture is a frame; the file is four panes and a table.
|
|
7130
|
+
|
|
6982
7131
|
### 9.3 What it still cannot see
|
|
6983
7132
|
|
|
6984
7133
|
- **Anything a frame does not contain.** Bone `length`, the setup `inherit` mode,
|
package/docs/MOTION.md
CHANGED
|
@@ -1007,6 +1007,7 @@ rigc build --rig semaphore.rig.json --motion semaphore.motion.json --images part
|
|
|
1007
1007
|
.. pages=3 regions=3 bones=4 slots=3 animations=1 version=4.3.13 regionAttachments=3 meshAttachments=0 physicsConstraints=0 rig=semaphore profile=spine
|
|
1008
1008
|
rigc: wrote …/semaphore/spine/skeleton.json
|
|
1009
1009
|
rigc: wrote …/semaphore/spine/skeleton.atlas
|
|
1010
|
+
rigc: look at it: rigc preview --candidate …/semaphore/spine
|
|
1010
1011
|
```
|
|
1011
1012
|
|
|
1012
1013
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Rig compiler for Spine — declarative rig specs in, Spine 4.3 skeleton data out, verified by a spine-core round-trip. Built so AI agents can author rigs and check their own work; the output imports into the Spine editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/skills/rigc/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rigc
|
|
3
|
-
description: Author, build and validate Spine 4.3 skeleton data (skeleton.json plus its .atlas) from loose part PNGs with rigc, the rig compiler that verifies its own output through a spine-core round-trip before writing it. Use for any request to make a Spine rig or Spine animation from PNG parts, to run or read rigc build, validate, render, preview, check or vote, or to write or fix a *.rig.json or *.motion.json spec; it says which shipped guide to open for the need at hand. Not for Live2D conversion, cutting an illustration into parts, or real-time face tracking.
|
|
3
|
+
description: Author, build and validate Spine 4.3 skeleton data (skeleton.json plus its .atlas) from loose part PNGs with rigc, the rig compiler that verifies its own output through a spine-core round-trip before writing it. Use for any request to make a Spine rig or Spine animation from PNG parts, or where the source is a Live2D, Unity or video model whose pictures you can render, to run or read rigc build, validate, render, preview, check or vote, or to write or fix a *.rig.json or *.motion.json spec; it says which shipped guide to open for the need at hand. Not for Live2D conversion, cutting an illustration into parts, or real-time face tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later. The tool is the npm package spine-rigc (bunx spine-rigc, or bun add -d spine-rigc); the command it installs is rigc.
|
|
6
6
|
---
|
|
@@ -34,8 +34,13 @@ whole interface, and this skill only says which of them to open.
|
|
|
34
34
|
```shell
|
|
35
35
|
bunx spine-rigc --help # run it without installing
|
|
36
36
|
bun add -d spine-rigc # or pin it in the project; the command is `rigc`
|
|
37
|
+
bun rigc skills install # then link these skills into .agents/skills
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
Codex, Gemini CLI and Antigravity read skills from `.agents/skills/` in the
|
|
41
|
+
workspace and none of them reads `node_modules`; `rigc skills install` puts every
|
|
42
|
+
skill the package ships there, and `rigc skills --help` says what it refuses.
|
|
43
|
+
|
|
39
44
|
## The loop
|
|
40
45
|
|
|
41
46
|
1. `rigc build --rig <spec> --motion <spec> --images <dir> --out <dir>` compiles,
|
|
@@ -50,28 +55,53 @@ bun add -d spine-rigc # or pin it in the project; the command is `rigc`
|
|
|
50
55
|
§4.11.2.
|
|
51
56
|
4. `rigc render --candidate <out>` or `rigc preview --candidate <out>` — look at
|
|
52
57
|
it. A rig with its head off its torso passes the gate; looking is what catches it.
|
|
53
|
-
|
|
58
|
+
Open one frame at full size, not only `contact.png`: the sheet is for spacing
|
|
59
|
+
across frames, and a defect is read on a frame. Ask it three things — is any
|
|
60
|
+
picture drawn twice, is there a straight edge where the art has none, does a part
|
|
61
|
+
cover a feature the art shows — and answer each with `render --hide <slot>` (or
|
|
62
|
+
`--slot <slot,…>`), which draws the frame again without that part on the same
|
|
63
|
+
grid, so the two frames say which part a pixel is. AUTHORING §0 holds the three.
|
|
64
|
+
5. `rigc check --candidate <out> --frames <dir>` when you have reference pictures
|
|
65
|
+
(`--out <dir>` writes the picture each of its numbers came from — open the worst);
|
|
54
66
|
`rigc vote --candidate <a> --candidate <b>` when several candidates are green and
|
|
55
67
|
only a person can choose between them.
|
|
56
68
|
6. `rigc validate <out>` re-gates artifacts already on disk, and
|
|
57
69
|
`rigc <command> --help` is each command's own flag table.
|
|
70
|
+
7. Every finished unit ends with `rigc preview --candidate <out>`, and the report
|
|
71
|
+
names the `.html` it wrote. The hand-off to a person is part of the work.
|
|
58
72
|
|
|
59
73
|
The loop in full, with `pose` before it and `chainfit` after the first build:
|
|
60
74
|
AUTHORING §0.
|
|
61
75
|
|
|
76
|
+
## When the source is not loose PNGs
|
|
77
|
+
|
|
78
|
+
A Live2D model, a Unity scene or a video is a player, not a set of parts. Make the
|
|
79
|
+
reference frames first: render the source at the rate you will check at, into one
|
|
80
|
+
directory of `f0000.png`, `f0001.png`…, and `rigc check --frames <that dir> --fps <rate>`
|
|
81
|
+
reads them with no `frames.json`. A port with no reference frames is unmeasured,
|
|
82
|
+
not finished. The parts are the source's own texture cut along its drawables, never
|
|
83
|
+
a screenshot: a screenshot is the composed result, and a part cut from it carries
|
|
84
|
+
every part under it. rigc reads none of those formats — FACE §11, the paragraph
|
|
85
|
+
that opens *No Live2D file is read or written*
|
|
86
|
+
([FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md#11-non-goals--stated-so-nobody-proposes-them-as-gaps)) —
|
|
87
|
+
only the pictures they produce. The rule, the background those frames need and
|
|
88
|
+
what a wrong one costs: AUTHORING §0, *When the source is a foreign player*.
|
|
89
|
+
|
|
62
90
|
## Which guide, for which need
|
|
63
91
|
|
|
64
|
-
Read [AUTHORING.md](
|
|
92
|
+
Read [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) first, whatever the need: the two
|
|
65
93
|
spec files field by field, the emission rules, the loop, and the failure map. Then:
|
|
66
94
|
|
|
67
95
|
| The request is… | Open | Skill |
|
|
68
96
|
| --- | --- | --- |
|
|
69
|
-
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](
|
|
70
|
-
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](
|
|
71
|
-
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](
|
|
72
|
-
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](
|
|
73
|
-
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) | `rigc-rigging` |
|
|
98
|
+
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) | `rigc-motion` |
|
|
99
|
+
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) | `rigc-face` |
|
|
100
|
+
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) | `rigc-ingest` |
|
|
101
|
+
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/firejune/rigc/blob/main/docs/PROMPTING.md) | — |
|
|
102
|
+
|
|
103
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
104
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
105
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
106
|
+
Formats, the CLI reference and the licence chain:
|
|
107
|
+
[README.md](https://github.com/firejune/rigc/blob/main/README.md), installed at `node_modules/spine-rigc/README.md`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: face
|
|
2
|
+
name: rigc-face
|
|
3
3
|
description: Author a face on plain Spine data with rigc — a blink, a gaze shift, a breathing portrait and a head turn a few degrees off axis, built from deform timelines and per-part parallax. Use when the request is a talking or living portrait, a standing character, an expression or a head turn, such as "rig this face", "make the portrait blink and look around" or "turn the head". Not for Live2D file conversion, cutting a face illustration into parts, or VTuber-style real-time face tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **head rather than a body**: a drawn face that
|
|
11
11
|
breathes, blinks, moves its eyes and turns a few degrees off axis. Every rule below
|
|
12
|
-
is owned by [FACE.md](
|
|
12
|
+
is owned by [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md); this file says when to open it and what
|
|
13
13
|
it will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -40,17 +40,21 @@ differential audit and the three limits it does not lift.
|
|
|
40
40
|
|
|
41
41
|
## Read, in this order
|
|
42
42
|
|
|
43
|
-
1. [AUTHORING.md](
|
|
43
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the `deform` timeline field by field
|
|
44
44
|
and what rigc refuses in it (§4), the failure map (§5–§6), the editor's
|
|
45
45
|
conventions (§10).
|
|
46
|
-
2. [MOTION.md](
|
|
46
|
+
2. [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) — timing, easing and the offset table (§3),
|
|
47
47
|
candidates and the ballot (§4–§5). A blink and a gaze are ordinary motion work.
|
|
48
|
-
3. [FACE.md](
|
|
48
|
+
3. [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) — the face's own geometry: the closed form every
|
|
49
49
|
number in a turn comes from (§1), the hierarchy underneath (§3), what
|
|
50
50
|
foreshortens (§5), and the deform audit gap (§9).
|
|
51
|
-
4. Then [RIGGING.md](
|
|
52
|
-
rather than this closed form, and [INGEST.md](
|
|
51
|
+
4. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) for the hierarchy as a general rule
|
|
52
|
+
rather than this closed form, and [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the head
|
|
53
53
|
arrived as a compiled skeleton.
|
|
54
54
|
|
|
55
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
56
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
57
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
58
|
+
|
|
55
59
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
56
60
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: ingest
|
|
2
|
+
name: rigc-ingest
|
|
3
3
|
description: Work with a Spine skeleton.json somebody else authored — exported from the Spine editor or another tool — using rigc. Read and validate it, understand a complaint rigc raised about it, decompile it into rigc specs with `rigc ingest`, normalise, re-pivot or rename it, and extend it with an animation it does not have. Use when the input is an existing skeleton.json with its .atlas and page images rather than loose part PNGs. Not for Live2D file conversion or runtime tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
10
10
|
Load this when what you were handed is **already a skeleton**: a `skeleton.json`
|
|
11
11
|
with its `.atlas` and page images, and a request to understand it, answer a
|
|
12
12
|
complaint about it, re-express it, or extend it. Every rule below is owned by
|
|
13
|
-
[INGEST.md](
|
|
13
|
+
[INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -61,14 +61,18 @@ thing to reach for; transcription by hand is what you fall back on for a constru
|
|
|
61
61
|
|
|
62
62
|
## Read, in this order
|
|
63
63
|
|
|
64
|
-
1. [INGEST.md](
|
|
64
|
+
1. [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) — what every command will and will not do with
|
|
65
65
|
a foreign file (§0), `ingest` and transcription (§2), what each validator
|
|
66
66
|
complaint means on an export (§3), and the re-pivot, rename and extend
|
|
67
67
|
recipes (§4).
|
|
68
|
-
2. [AUTHORING.md](
|
|
68
|
+
2. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the two spec files the transcription
|
|
69
69
|
targets (§3–§4), the failure map (§5–§6), and the coordinate contract (§11.2).
|
|
70
|
-
3. Then [RIGGING.md](
|
|
71
|
-
it has, and [MOTION.md](
|
|
70
|
+
3. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) for why the re-pivot edit has the shape
|
|
71
|
+
it has, and [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) for the animation you are adding.
|
|
72
|
+
|
|
73
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
74
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
75
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
72
76
|
|
|
73
77
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
74
78
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: motion
|
|
2
|
+
name: rigc-motion
|
|
3
3
|
description: Author a Spine animation with rigc from key poses — an idle, a loop, a move from one picture to another — with timing and spacing, ease in and out, anticipation, arcs, overlap, follow-through, squash and stretch, and candidate variants a person can choose between. Use when the request is a movement on an existing or planned rig, in the animator's words too: "animate this rig", "make it breathe", "go from pose A to pose B", "make it feel heavier", "the cape should follow through". Not for Live2D, separating an image into parts, or real-time tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
10
10
|
Load this when the request is a **movement rather than a skeleton**: a sentence of
|
|
11
11
|
intent, between zero and N pictures of what the movement passes through, and a
|
|
12
12
|
Spine animation somebody would choose coming back. Every rule below is owned by
|
|
13
|
-
[MOTION.md](
|
|
13
|
+
[MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -31,17 +31,21 @@ thing that judges a movement is a person's eye through `rigc vote` — MOTION §
|
|
|
31
31
|
|
|
32
32
|
## Read, in this order
|
|
33
33
|
|
|
34
|
-
1. [AUTHORING.md](
|
|
34
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the motion spec field by field (§4),
|
|
35
35
|
the failure map (§5–§6), reading reference frames (§8) and checking against
|
|
36
36
|
them (§9), what the editor does when nobody tells it otherwise (§10), and
|
|
37
37
|
reading a pose out of a picture (§11–§12).
|
|
38
|
-
2. [MOTION.md](
|
|
38
|
+
2. [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) — the normal form every motion request
|
|
39
39
|
reduces to (§0), timing, easing and the per-bone offset table (§3), and how to
|
|
40
40
|
spread candidates so a ballot informs (§4–§5).
|
|
41
|
-
3. Then [RIGGING.md](
|
|
42
|
-
to decide; [FACE.md](
|
|
43
|
-
head turn; [INGEST.md](
|
|
41
|
+
3. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) if the skeleton itself is what you have
|
|
42
|
+
to decide; [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) if the movement is a blink, a gaze or a
|
|
43
|
+
head turn; [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the rig arrived as a compiled
|
|
44
44
|
skeleton rather than loose parts.
|
|
45
45
|
|
|
46
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
47
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
48
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
49
|
+
|
|
46
50
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
47
51
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: rigging
|
|
2
|
+
name: rigc-rigging
|
|
3
3
|
description: Decide a Spine rig's hierarchy with rigc — how many bones, where each pivot sits, what hangs off what, offsets, chains and what a chain can reach, siblings versus chains, constraints as structure — and which of those decisions the reference frames can check. Use when the request is a skeleton from loose part PNGs, such as "rig these parts", "make a Spine skeleton" or "where do the joints go", before any motion is authored. Not for Live2D, cutting an illustration into parts, or VTuber-style tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **skeleton rather than a movement**: loose part
|
|
11
11
|
PNGs in, a bone hierarchy out. Every rule below is owned by
|
|
12
|
-
[RIGGING.md](
|
|
12
|
+
[RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md); this file says when to open it and what it
|
|
13
13
|
will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -30,16 +30,20 @@ are named in RIGGING §11 — neither as a pass bar.
|
|
|
30
30
|
|
|
31
31
|
## Read, in this order
|
|
32
32
|
|
|
33
|
-
1. [AUTHORING.md](
|
|
33
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the rig spec field by field (§3),
|
|
34
34
|
the failure map (§5–§6), reading a pose out of a picture (§8.1, §11, §12),
|
|
35
35
|
and the editor's own conventions (§10).
|
|
36
|
-
2. [RIGGING.md](
|
|
36
|
+
2. [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) — the structure itself: what identifies a
|
|
37
37
|
pivot and what moving one costs (§2–§3), gauges (§4), what a chain can reach
|
|
38
38
|
(§6), and the instruments that see structure (§11).
|
|
39
|
-
3. Then [MOTION.md](
|
|
40
|
-
[FACE.md](
|
|
41
|
-
[INGEST.md](
|
|
39
|
+
3. Then [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) once the skeleton exists;
|
|
40
|
+
[FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) if the figure is a head;
|
|
41
|
+
[INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the skeleton was handed to you already
|
|
42
42
|
compiled.
|
|
43
43
|
|
|
44
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
45
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
46
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
47
|
+
|
|
44
48
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
45
49
|
skill.
|
package/src/ballot.ts
CHANGED
|
@@ -63,6 +63,9 @@ import {
|
|
|
63
63
|
dataUri,
|
|
64
64
|
embeddedJson,
|
|
65
65
|
escapeHtml,
|
|
66
|
+
PANE_STAGE_CSS,
|
|
67
|
+
paneGridCss,
|
|
68
|
+
paneSection,
|
|
66
69
|
PLAYER_LINE,
|
|
67
70
|
PLAYER_SCRIPT_URL,
|
|
68
71
|
PLAYER_STYLE_URL,
|
|
@@ -592,14 +595,10 @@ export function buildBallot(input: BallotInput): { html: string; manifest: Ballo
|
|
|
592
595
|
},
|
|
593
596
|
};
|
|
594
597
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
<div class="stage" id="rigc-player-${c.label}"></div>
|
|
600
|
-
</section>`,
|
|
601
|
-
)
|
|
602
|
-
.join('\n');
|
|
598
|
+
// The pane markup and its grid are `preview.ts`'s since issue #837, which
|
|
599
|
+
// borrowed them for a page of panes that asks nothing; the bytes here are
|
|
600
|
+
// the ones this page always wrote.
|
|
601
|
+
const panes = manifest.candidates.map((c) => paneSection(`rigc-player-${c.label}`, `<h2>${c.label}</h2>`)).join('\n');
|
|
603
602
|
|
|
604
603
|
const choices = [...labels, TIE]
|
|
605
604
|
.map(
|
|
@@ -649,11 +648,9 @@ export function buildBallot(input: BallotInput): { html: string; manifest: Ballo
|
|
|
649
648
|
button { font: inherit; padding: 4px 12px; border: 1px solid rgba(0, 0, 0, 0.35); border-radius: 4px; background: rgba(255, 255, 255, 0.6); cursor: pointer; }
|
|
650
649
|
button:hover { background: rgba(255, 255, 255, 0.95); }
|
|
651
650
|
button[aria-pressed="true"] { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
|
|
652
|
-
|
|
653
|
-
@media (max-width: 720px) { #panes { grid-template-columns: minmax(0, 1fr); } }
|
|
654
|
-
.pane { background: ${backgroundHex()}; display: flex; flex-direction: column; min-width: 0; }
|
|
651
|
+
${paneGridCss(manifest.candidates.length)}
|
|
655
652
|
.pane h2 { margin: 0; padding: 6px 14px; font-size: 15px; letter-spacing: 0.12em; }
|
|
656
|
-
|
|
653
|
+
${PANE_STAGE_CSS}
|
|
657
654
|
#vote { border-top: 1px solid rgba(0, 0, 0, 0.15); display: flex; flex-direction: column; gap: 10px; }
|
|
658
655
|
.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
659
656
|
.row > label, .caption { opacity: 0.65; }
|