‹ the toolbox

The setting sheet — one skeleton, every tool

The tool setup sheet, for muscle memory: someone who has set up a poll once should manage a sealed deal without reading anything. All four sheets below are live and generated from ONE template — if they ever diverge, the config diverged, not the layout.

The idea underneath. The three sections are the lifecycle itself: 设计 is open, 截止触发 is close, 揭晓触发 is reveal. Every instrument already rides those phases(task A), so the sheet wears the same skeleton every time, in the same order, in the same colour. Only 设计 changes between tools — and a phase that a tool does not have simply is not drawn.

1 · Four tools, side by side

Switch the top row on any sheet and watch the sections below it react. Scroll sideways to compare. 掷骰 · 投票 · 密存 · 发牌.

2 · What disappears, and why

A choice that removes the meaning of a later choice must remove the choice itself. Greying it out would still ask the question; hiding it says there was never a question.

WhereWhat goesWhy
明掷 / 明转
dice · wheel
揭晓触发 collapses to a fixed 「截止即揭晓」An open roll is public the instant it lands. There is no held content, so there is nothing a reveal trigger could govern.
明发牌
deal
揭晓触发 gone entirelyA face-up deal posts every hand at the shuffle. Nothing is ever hidden, so no phase exists to open.
发牌 — always截止触发 gone entirelyRuling ⑥: a deal has no collecting phase. Every hand is committed the moment the deck is shuffled — the card is born closed. There is no door to shut, so the whole section is meaningless and is not drawn. This is the owner's case, and it is the sharpest one.
投票 — always揭晓触发 collapses to a fixed 「截止即揭晓」A ballot's standings open to you the moment you vote and never before — the pay-to-see gate is built into the tool(ruling ⑦). A deferred reveal is a different instrument: use 密存.
密存nothingThe deposit is the one tool where every phase is genuinely yours to set. It is the reference sheet — the others are it, minus what they cannot mean.
the payoffBecause sections are removed rather than disabled, every sheet you meet is shorter than the reference and never longer. 发牌 is the shortest — two sections — and a person who learned 密存 first reads it instantly, because it is the same sheet with the parts that cannot apply taken away.

3 · Notes on the three new sheets

shipped · v646标题 is optional only on the FIRST card of its kind. The uniqueness invariant(DoD §4): a second open card of a kind must carry a title, and a distinct one — so 骰子 · 转盘 · 发牌 keep 标题 optional while the table has no other card of that tool, and it flips to required-with-a-reason the moment one appears(a .pk-hint under the box, Start greyed with the reason in the commit row; a name already on the table says so instead). Compared per canon kind, so a 转盘 never blocks a 骰子 — and a Coin counts as a 转盘, which is why changing the dice preset re-asks. ⚠ The sheet asks; the SERVER decides(one predicate, both doors, name needed / name taken back to the sheet)— a card opened by somebody else while this sheet was up must land on the same refusal. 封存笔记 is the one carve-out: it has no 标题 field and only its writer opens it.
投票 · the poll标题 is mandatory here, not optional — it is the question, and a ballot that arrives untitled is unreadable in scrollback a day later(the sheet refuses to start without it). 选项 replaces the dice row; 可多选 is the one extra dial.
密存 · the depositCarries no separate 「my answer」 field: when you are a player in your own round you answer in the deposit bubble like everybody else — one channel, used by whoever is playing, which is the parity law again. It is also the sheet where 参与者 = 只有我 turns the tool into a private note — one instrument, the participants chosen, exactly as the human dialog already does it.
发牌 · the deal牌堆 offers the three standard decks by name(poker52 · poker54 · tarot)plus a typed list; 每人几张 is the field whose absence once dealt one card apiece when three were asked for. dealer 看牌 — whether the dealer is shown the map — sits here rather than in 高级, because in a werewolf game it is the most consequential choice on the sheet.
binding on the buildEvery field on these sheets wears house CSS — no raw system text box, anywhere. A default <input> brings the OS's own border, radius, font and focus ring, and beside these surfaces it reads as a foreign object. The mockup uses contenteditable for the short in-line values(the deck name, the option rows, the stepper numbers, the deck code); a restyled <input> or <textarea> is equally acceptable provided it sets appearance:none, font:inherit, its own focus treatment and a ::placeholder colour — which is exactly the route 白板内容 and 记事内容 take, because a standalone multi-line field is the case contenteditable is worst at. The contract is the look, not the element. Full rule + the traps of each route: lib/PRESENTATION.md § Text fields.
binding on the buildA participant pill is(avatar)名字, never a bare name. The mockup draws text only because it has no roster to draw from, but in the room every seat already has a face — and a face is what a person recognises in a list of ten. The same holds in the picker sub-sheet, where the avatar replaces the tick column’s dead space. Sizing follows the existing seat chips in the chat header rather than inventing a second scale.
shipped · v634Built, and the mock-up above is now a specification the app satisfies rather than a proposal. The five setup dialogs collapsed into ONE sheet(#sheetScrim)whose body is generated from SHEETS[key] in lib/room-ui.html; the phase hues read --plum from theme.css, not from a page. Every control arms a real instrument through the existing doors — user_roll_open · user_vote · user_seal_open · user_deal — and three dials the human path did not previously reach were added to those doors rather than beside them: 截止触发条件close_trigger, all four rungs)· 副数(a named deck's ×N shoe)· 各自点开 on a sealed roll, which is genuinely new machinery(_roll_row_reveal + a roll_reveal record — dice had no per-row reveal until now, so「我先开」opens YOUR face while every other stays down). Persona parity is linted: smoketest's form↔grammar section now covers the ballot's and the collect's attributes too, and every dial on all five sheets round-trips form → tag → parser or the build fails.
owner correction · the close row截止触发条件 is NOT a choice between doors — it is CHECK MARKS. The first cut read the four rungs as alternatives, which produced a poll that never closed by itself; the owner's model is the right one and is what ships: a card ALWAYS closes when the last listed person is in(等大家都完成 rides the row checked and locked, because it is the law rather than a setting), and 发起人点击 · 任何人点击 · 定时 are extra doors that LAYER — whichever comes first shuts it. So the two hand rungs are a permission, not a trigger: they say whose「Close now」button exists. 任何人点击 subsumes 发起人点击, so checking the wider one checks and locks the narrower(the visible-coercion pattern the presence combos already use). Default: 任何人点击 — the standing stall valve every card has always had. Server-side it is a new axis, close_hands ∈ none · creator · any, orthogonal to close_trigger and to the clock; the card never draws a「Close now」the server would refuse, and the smoketest carries the regression test that a hand permission does not move the all-in close.
amendments made in the buildThree more places where the mock-up under-specified and the build had to decide.The mode DEFAULT stays per-tool(明 for dice and slots — v623; 暗 for the deal — v625)rather than following the template's index 0, because both were deliberate and recorded. ② Leaving 明 restores the tool's own sealed default(发起人点击), never rung 0: on a 发牌, 发牌即揭晓 means「open every hand at the shuffle」, which is 明发牌 again — so a forced value is never written into the draft. ③ 全部 ships greyed and struck(owner's call)rather than hidden, applying the sheet's own real-but-unavailable rule to the shortcut row.
shipped · v642 · 白板The board's sheet is now as drawn above — and where the earlier draft and the shipped app disagreed, the DRAFT was the one that changed. This page used to draw 白板内容 as a contenteditable under a B / I / S / • / 1. / ▦ toolbar. That is superseded: the field is a plain <textarea> holding markdown source, no toolbar and no WYSIWYG. Three reasons, in the order they matter. ① The expected author of a board is often a persona, and a persona writes markdown natively into <board>…</board> — a room that wants a whiteboard kept will seat a secretary to keep it. A toolbar would be a second way to express what that channel already expresses, and two ways to say one thing is how they drift. ② Liberal in what it accepts, one renderer: type **bold** and you get bold; type plain lines and they render exactly as typed, so nobody has to know markdown to use the board. ③ A formatting bar over a five-line panel is more chrome than content. What DID land as drawn: 谁能改(仅创建者 default · 所有人)and 传笔(关 default, and only drawn under 仅创建者 — with 所有人 there is no marker to lend, so per ruling ⑥'s treatment the row is not drawn rather than greyed), plus 撤下 in the foot. A non-creator opening this sheet sees THE WHOLE BODY GREYED with a banner that says why(「Ben has the pen — you cannot write on this board.」), no 撤下 at all, and a dead 更新 — v643, the owner's read: a dialog whose commit is dead but whose fields look normal makes a person type a paragraph before finding out. ⚠ 传笔 GREYS RATHER THAN HIDING under 所有人(v643, superseding this page's own ruling-⑥ reading)— and that fixed a real bug: [hidden]'s display:none is a UA rule and .pk-seg{display:flex} outranks it, so the buttons had been staying while their LABEL vanished, taking its gap with it and leaving an impossible 所有人+开 pair on screen. Setting hidden on a node your own CSS gives a display to does nothing. And 白板内容 is EIGHT line-boxes(v643), derived from one --lh as the pad's is.
sheet = policy · card = state谁能改 and 传笔 live HERE; who is holding the pen right now lives on the CARD. The split is not tidiness — it is the difference between a setting and a fact. A dial is chosen once by the person who owns the board; the pen moves many times, in front of everyone, and the verbs that move it(要笔 · 给笔 · 收回 · 交回)belong beside the thing they change. Putting a pen control in the sheet would have made two places that set one value, and one of them would have gone stale.

4 · The sixth sheet — and it is mostly absences

The pad(toolbox §13, shipped v636)gets a sheet too, and it is the shortest in the set. It is worth recording here precisely because what it lacks is the specification: five of the skeleton's six regions do not apply, and the reason is the same each time — the pad has no audience, so it has nothing to configure about an audience.

the skeleton's regionon the padwhy
the mode row(明 · 暗 · 盲)gonethere is no second way to be private. A visibility ladder needs somebody to be above or below you on it.
参与者 — who takes partgoneone owner, always, and it is whoever opened the sheet. A picker here could only ever pick you.
设计 — the payload editorthis IS the sheetONE field: the text.(It was two until v641; the title went the way of everything else here — a label exists so somebody looking for the thing can find it, and nobody else will ever look for this.) The dictate mic is not a second field, it is a second way to fill the one. If a build ever adds a real second, something has been misunderstood about the tool.
收集 / 截止gonenothing is collected, so there is no input door to shut. Ruling ⑥'s treatment applies: a phase a tool cannot have is not drawn, never greyed.
揭晓gonenothing opens. A reveal is a promise to an audience and there is none.
高级(blocking · place · unique)gonethe presence properties describe how a card behaves on the room's screen. The pad is on no screen but yours.
what it adds insteadA dictate mic, one live count, and a destructive door that asks. The notes field is a two-deck — the situation box's shape(.curate-wrap)— so the mic sits on its own row at the bottom right rather than over the text: this is the second place in the app you speak a paragraph into, and it should not need a second visual idea. It is the same micTap, the same body.stt-on gate, and the same button rule as the situation box's. The count(n/2000, amber from 1 600)exists because a write over the cap is refused rather than trimmed — a cap you only discover by being rejected is a cap that eats a page of writing, so it has to be visible on the way there. And「撕掉」asks for confirmation where the board's「撤下」does not: a board can be pinned again from memory, a torn-up pad cannot.
shipped · v641The two lifecycle-less sheets now match this page, and where they diverged the PROTOTYPE won. 计时's three quick-pick chips + its <input type="number"> became the rung ladder + ± stepper above — the same control the 定时 triggers use, extracted so there is one implementation(durLabel/durNudge/durPaint/durWire)rather than two that agree by luck. Rungs are 30s · 1 · 5 · 10 · 30 · 1时 · 3时; ± moves one unit of what is shown(10 min → 11 min · 1小时 → 2小时). ⚠ The top rung forced a SERVER change: CLOCK_MAX_SEC was 2 hours, so 「3时」 would have quietly set a two-hour clock — the cap moved to meet the control, because a rung that clamps to a different number is a rung that lies. Its take-off moved to the foot(it opened the body)and its verb is 停掉. The pad lost its 标题 and its field is confirmed plain text, no toolbar — formatting is something you do for readers. Neither sheet has 高级, and neither ever did. Pads that had already stored a title are not orphaned: _pads_drop_human_titles() folds it into the first line at load.
the commit verb写下 when the pad is empty, 保存 when it is not — the board's Pin it / Update flip, same mirror(mirrorCommit("pkCommit","pkSend")). ⚠ One trap paid for in the build: the English 「Write it」 was already the Seen writer's key and translates as「开写」. Two dialogs sharing one English string is how a verb ends up wrong in one of them; the pad carries its own「Write it down」. The i18n audit does not catch a duplicate key — it json-parses the dict, so the later entry silently wins.
The setting sheet · owner's layout 2026-07-27, five tools generated from one template · the three sections are the lifecycle(task A)· the sub-sheets hold the seat picker and the deck editor · 发牌 has no close phase per ruling ⑥, 投票 no reveal choice per ruling ⑦. 转盘's ladder reads 明转 · 暗转 · 盲转(the 摇 forms — owner 2026-07-27 — followed 老虎机 and retired with it when the object went back to 转盘, 2026-07-28; English canon is now spinner). Built v634; the component record is in the Style Guide. The pad joined as a sixth sheet 2026-07-28(§4) — it does not come off SHEETS[key], because a template whose whole job is the three lifecycle sections has nothing to generate for a tool with none; it is the board's dialog with everything audience-shaped removed.