KQ-2088 · GOAL/BLOOM
Goal Bloom
A radial plan with the goal in a disc at the centre, sub-goals as pills on the first ring and their steps on the second, each sub-goal owning a sector and a petal. As the agent plans, every new node blooms out of its parent along its own angle on springs.glide while its siblings re-space round the ring on the same spring, each node's angle and radius animated in polar coordinates so a re-spacing sweeps round the circle; when a branch finishes, its steps fold back into the sub-goal, the sub-goal moves in toward the centre and its petal fills from the centre on springs.recoil, so progress reads as the flower closing while the goal's ring fills on glide. It is a tree: Left and Right move round a ring, Enter steps out a ring, Escape steps back in, and under 32rem the bloom becomes an indented list with the same states and keys.
Coldbrook · Larch Street
Store opening plan
Left and Right move round a ring, Enter steps out to the next ring, Escape steps back in. In the list, Up and Down move, Right steps out, Left steps in.
Goal · Planning
Open Coldbrook on Larch Street
Doors open Saturday 1 November at 7:00.
planning · 11 steps · 1 of 5 branches done
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/goal-bloom
Props
- petalShape
- "leaf" | "round" | "wedge"
- default "leaf"
- Every petal, outline and fill: a pointed leaf, a blunt round petal, or an annular wedge.
- ringGap
- number
- default 64
- Units between rings, 40 to 76, in a bloom 400 units across — how open the flower is. Deeper rings scale to fit.
- accent
- string
- default "var(--accent-bright)"
- Petals, active nodes, the goal's ring and Approve — any CSS colour. Filled petals take its hue at a fixed lightness so they read alike in both themes.
- goal
- GoalBloomGoal
- default defaultGoalBloomGoal
- The goal at the centre: title, a short form for the disc, and a note for the panel.
- nodes
- GoalBloomNode[]
- default defaultGoalBloomNodes
- Sub-goals and steps as one flat list linked by parent (null for a sub-goal of the goal): id, title, short, status (todo, active, done, blocked, planning), owner, note. A node with children takes its status from them.
- depth
- number
- default 2
- How many rings to draw, 1 to 3; deeper nodes are left out.
- status
- "ready" | "planning" | "blocked" | "loading"
- default "ready"
- The plan as a whole: planning holds Approve and draws new nodes dashed, blocked flags the plan, loading draws the bud alone. An empty nodes list shows the empty state.
- selected / defaultSelected
- string | null
- default — / null
- The selected node id, controlled or initial; null is the goal.
- onSelectNode
- (id: string | null) => void
- default —
- Fires from the click or key that moved the selection.
- onAddSubgoal
- (parentId: string | null) => void
- default —
- Add a step (or Add a sub-goal, on the goal) was pressed in the detail panel.
- onApprove
- () => void
- default —
- Approve plan was pressed.
- intro
- boolean
- default true
- Bloom out of the centre the first time the plan is seen.
- title
- string
- default "Store opening plan"
- The plan's name.
- product
- string
- default "Coldbrook · Larch Street"
- Who it is for, shown above the title.
- label
- string
- default the title
- The tree's accessible name.
- disabled
- boolean
- default false
- Read only: nothing can be added or approved.
- className
- string
- default —
- Classes for the outer surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| petalShape | "leaf" | "round" | "wedge" | "leaf" | Every petal, outline and fill: a pointed leaf, a blunt round petal, or an annular wedge. |
| ringGap | number | 64 | Units between rings, 40 to 76, in a bloom 400 units across — how open the flower is. Deeper rings scale to fit. |
| accent | string | "var(--accent-bright)" | Petals, active nodes, the goal's ring and Approve — any CSS colour. Filled petals take its hue at a fixed lightness so they read alike in both themes. |
| goal | GoalBloomGoal | defaultGoalBloomGoal | The goal at the centre: title, a short form for the disc, and a note for the panel. |
| nodes | GoalBloomNode[] | defaultGoalBloomNodes | Sub-goals and steps as one flat list linked by parent (null for a sub-goal of the goal): id, title, short, status (todo, active, done, blocked, planning), owner, note. A node with children takes its status from them. |
| depth | number | 2 | How many rings to draw, 1 to 3; deeper nodes are left out. |
| status | "ready" | "planning" | "blocked" | "loading" | "ready" | The plan as a whole: planning holds Approve and draws new nodes dashed, blocked flags the plan, loading draws the bud alone. An empty nodes list shows the empty state. |
| selected / defaultSelected | string | null | — / null | The selected node id, controlled or initial; null is the goal. |
| onSelectNode | (id: string | null) => void | — | Fires from the click or key that moved the selection. |
| onAddSubgoal | (parentId: string | null) => void | — | Add a step (or Add a sub-goal, on the goal) was pressed in the detail panel. |
| onApprove | () => void | — | Approve plan was pressed. |
| intro | boolean | true | Bloom out of the centre the first time the plan is seen. |
| title | string | "Store opening plan" | The plan's name. |
| product | string | "Coldbrook · Larch Street" | Who it is for, shown above the title. |
| label | string | the title | The tree's accessible name. |
| disabled | boolean | false | Read only: nothing can be added or approved. |
| className | string | — | Classes for the outer surface. |