# Construct ring

A signet emitter ring: a lit bezel over a knurled band, a teardown in fitting order, and a forge that turns a stroke you draw into a construct of solid light.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/construct-ring.json
```

Registry item: `construct-ring` · [`https://robocn.dev/r/construct-ring.json`](https://robocn.dev/r/construct-ring.json)

## Notes

- Solved: the stroke frame (centroid, principal axis, spans, closure, area, circularity, corners), the classifier that reads an archetype out of it, the outline's fit to that frame, the scanline lattice that fills it, the draw it costs, and the explode schedule. All of it is `construct-geometry`, tested on its own.
- Illustrated: the archetype outlines themselves — a glove is a glove because it is drawn as one — plus the glow, the emission column, the knurl and the crystal. There is no physics on a construct: nothing swings, nothing collides, nothing has mass.
- Turning is a camera move, not a second drawing: `azimuth` and `elevation` go straight to `robotCameraAt`, so every angle on the sphere is the same geometry the four named views are. The camera has no roll axis, so a drag that carries over the pole comes down the far side upright.
- The ring is projected; the construct is not. The ring is modelled once in world units and pushed through `robotCamera`, so all four cameras are the same geometry. A construct lives in the picture plane and does not rotate with the camera, because the stroke that made it was drawn there.
- An original archetype: a generic signet emitter. The face is an abstract iris — a bore, four radial inlays and a spiral gauge — and carries no insignia from anywhere.

## Usage

```tsx
import { ConstructRing } from "@/components/ui/construct-ring"

// Turn it with a drag or the arrow keys, and draw on it: a round loop is a
// bubble, a long stroke a hammer, a stubby one a glove.
<ConstructRing drawable onConstructChange={(c) => console.log(c.archetype)} />

// Or command the camera, anywhere on the sphere.
<ConstructRing azimuth={70} elevation={-25} onOrbitChange={setOrbit} />

// Or drive it, which stops the cycle.
<ConstructRing construct="glove" reserve={0.8} exploded={0.4} view="iso" />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `reserve` | `number` | — | Controlled reserve, 0 spent to 1 charged. Supplying it stops the loop. |
| `onReserveChange` | `(reserve: number) => void` | — | Fires while it is dragged or keyed, so interaction works in controlled mode too. |
| `construct` | `"bubble" | "shield" | "cage" | "glove" | "hammer" | "bridge" | "claw" | null` | — | Controlled construct; null holds none. Supplying it stops the cycle. A drawn stroke still wins over it. |
| `onConstructChange` | `(report: { archetype, cost, frame }) => void` | — | Fires whenever a stroke is forged, with what it was read as, what it costs and the frame it was measured into. |
| `behavior` | `"conjure" | "charge" | "flare" | "idle" | "static"` | `"conjure"` | Conjure cycles the archetypes; charge fills the reserve and forges nothing; flare holds one construct and pulses; idle breathes. |
| `exploded` | `number` | `0` | Take the ring apart, 0 seated to 1 clear. At 0 every part is exactly where it was fitted. |
| `drawable` | `boolean` | `false` | Hand the field to a person: drag to draw a stroke and it is forged on release. Enter forges the next archetype, Escape clears — the keyboard path to the same feature. |
| `interactive` | `boolean` | `false` | Makes the ring a slider over its own reserve: drag it, or focus it and use the arrow keys. With `drawable` on, a gesture starting on the ring adjusts the reserve and one starting in the field forges. |
| `rotatable` | `boolean` | `true` | Turn the ring with a drag, a finger or the arrow keys: right round, over the pole and down the far side. Home returns to the named view. While it is on, the slider role reports the camera rather than the reserve. |
| `azimuth / elevation` | `number` | — | Controlled camera in degrees. Either one turns the ring to that angle and `view` only names the drawing plane. Azimuth wraps at 360; elevation past 90 carries over the pole instead of stopping. |
| `onOrbitChange` | `({ azimuth, elevation }) => void` | — | Fires on every turn, in controlled mode too, with the angles already wrapped back onto the sphere. |
| `showField` | `boolean` | `true` | The frame and corner ticks of the field constructs are forged in. |
| `showGround` | `boolean` | `true` | Draw the contact shadow and the ground line beneath it. |
| `label` | `string` | — | Optional technical caption under the drawing. |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"iso"` | Where the camera stands. One ring, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `speed` | `number` | `0.5` | Cycles per second. |
| `animate` | `boolean` | `true` | Off parks the machine at phase and stops rendering. A reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze where it stands. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `variant` | `"solid" | "outline" | "blueprint" | "wire"` | `"solid"` | How the machine is painted. Geometry never changes between variants. |
| `size` | `"xs" | "sm" | "md" | "lg" | "xl" | number` | `"md"` | Rendered width in pixels, or a step on the scale. |
| `color` | `string` | `var(--robot-shell)` | Body panels — the colour the machine reads as. |
| `accent` | `string` | `var(--robot-accent)` | Status colour: tip light, live tool, readouts. |
| `metal` | `string` | `var(--robot-metal)` | Bare machined parts: collars, bolts, tool bodies. |
| `dark` | `string` | `var(--robot-dark)` | Cast joints, base, shadow side. |
| `palette` | `Partial<RobotPalette>` | — | Override any subset of roles at once, including glow and grid. |

## Source

- `src/components/ui/construct-ring.tsx`
