/** * dsh-widgets — the `timer` rich block (秒表 / 倒计时's live reading and picker). * * THREE BODIES, ONE CONTRACT (`TimerRich.layout`). * * - `plain` — the reading, an optional caption above it and an optional note below. * 74px at scale 1, sized for a card that KEEPS ITS TITLE **and** carries an * `actions` row: `12(pad) + 16(title) + [74] + 6(gap) + 26(buttons) + 12(pad) = 146 ≤ 150`. * - `dial` — the reference stopwatch: a TOP-ALIGNED `HH:MM:SS.cc` reading over two * round controls sitting on the card's floor, filling a titleless tile: * `12(pad) + [120] + 12(pad) = 144 ≤ 150`. This is the stopwatch, and the * countdown's run view (same shape, `MM:SS`, under its own title row). * - `picker` — a 2×2 grid of duration capsules, the countdown's entry view: * `12(pad) + 16(title) + [106] + 12(pad) = 146 ≤ 150`. * * EVERY SIZE IS FITTED TO THE BOX IT IS HANDED, never assumed. The first cut of the * dial hard-coded the reference's disc width and the reading's size against a 126px * content column — exactly the column, zero slack — and the owner's screenshot at * the market's 1.9× preview scale showed the discs and the reading CLIPPED by the * card's padding (reported 2026-10-14). Every dimension below is therefore derived * from the real `width` the card hands over (`CardBody` → `RichBlock` → here): * the reading is capped by `width ÷ 0.55em per character` and the two discs by * `width ÷ 2.6` (two discs plus a 0.4-disc gap and ~15% slack), so no scale can * overflow. `scripts/probe-geometry.cjs` now also asserts it (RICH-OVERFLOW). * * WHY IT TICKS ITSELF. A widget render is a pure data description and the rail * re-renders a card only when its data changes (idle: once every 30s — measured, * see data/collector.tsx). A stopwatch wired to that tick would freeze for half a * minute at a time and a countdown would jump in 30-second steps. So the live * reading is owned HERE, exactly like the clock's second hand: a 1s interval while a * segment RUNS, a 100ms one when the card prints hundredths, and NO interval at all * when the card is paused or a countdown has run out. * * The interval belongs to THIS component, rendered by `CardBody` as a real element * (`createElement`) — never called as a plain function. Called inline its effect * would land on `CardBody`'s fibre, and the skeleton → loaded swap (which returns * before the rich block on the skeleton pass) would then render a different number * of hooks per pass, which React rejects with #310 (the same trap the chart * registry and `clock-face.tsx` document). */ import * as React from 'react'; import type { TimerRich } from '../../lib/contract/types'; /** * The timer rich block. * * @param timer - the widget's payload (see `TimerRich`); `at` absent = live. * @param scale - `unit / BASE_SIDE`, the card's own scale factor. * @param width - the card's body width in px; every fitted size derives from it. * @param onAction - the rail's action dispatcher, for capsules and controls. */ export declare function TimerFace({ timer, scale, width, onAction }: { timer: TimerRich; scale: number; width: number; onAction?: (id: string) => void; }): React.ReactElement;