Your co-worker can show what it is talking about. When it says a customer’s name, that row lights up. It can circle the answer, bring a chart onto the page when it says “here’s the revenue chart”, put it away when it moves on, and make a roadmap’s milestones appear one by one as it names them. Every move is timed to the spoken word.
None of this needs permission. A move only changes what is shown; it never clicks, types, or changes anything in your app. Anything with consequences stays an action.
Showing is in early access and off until we switch it on for your workspace, so ask us to enable it. While it is off the co-worker makes no moves, and nothing is sent; your page’s own useStage controls still work.
Label the things
Put data-coworkkit-item on each thing the co-worker might talk about, set to the name it would say. That is the whole change for lighting and marking.
<table data-coworkkit-element="orders">
{orders.map((o) => (
<tr key={o.id} data-coworkkit-item={o.customer}>
…
</tr>
))}
</table>- The label is a spoken name, not an ID. Use what people say:
"Acme Europe", not"cust_8f2". We match it to the co-worker’s words, ignoring case and accents, and the longest name wins ( “Acme Europe” beats “Acme”). data-coworkkit-elementis optional. Put it once on the component’s container, with the same name you gaveuseElement. It tells us which table a row belongs to, so when the conversation is about invoices, the Acme in the invoices table lights up and not the Acme in orders.- Two things with the same name both light. They really are both Acme’s. In development the console warns you once.
The four moves
| Move | What it does | What you declare |
|---|---|---|
light | Points at the thing being talked about right now. It moves with every mention, one thing at a time. | Nothing: a label is enough. |
mark | Circles a thing so it stays visible after the voice moves on: the answer, a warning, a recommendation. | Nothing: a label is enough. |
show | Brings a panel onto the page when the co-worker says it is showing it. On a list's items: they appear one by one as they are named. | moves: ["show"] on the element, or itemMoves: ["show"] for its items. |
hide | Puts a panel away when the co-worker moves on. | moves: ["hide"] on the element. |
One effect at a time: a thing never shows the glow and the circle together. A circle on the lit thing is drawn when the light moves on.
Bring panels up and put them away
Declare the bigger moves on the element. The co-worker brings the panel up when it says it is showing it (“here’s the revenue chart”), never on a passing mention or a question, and puts it away when it moves on (“back to your orders”).
useElement({
name: "chart",
state: { quarters },
moves: ["show", "hide"],
});
// Render it by the stage state...
const chart = useStage("chart");
return chart.shown ? <RevenueChart /> : null;- Where it starts. An element that can be shown starts hidden; one that can only be hidden starts shown. Set
shownonuseElementto choose. The start applies with or without a session. - Your own buttons.
useStage("chart").show()and.hide()drive the same state, so a “Show chart” button and the co-worker never disagree. The co-worker never hides a panel your page showed. - No code at all. If the panel’s container carries
data-coworkkit-element, we setdata-coworkkit-shownon it and hide it while it is"false". Our rule has zero specificity, so your own CSS always wins.
Walkthroughs
Add "show" to itemMoves and the element’s items appear one at a time as the co-worker names them. A declared itemMoves list replaces the default (["light", "mark"]), so list every move you want. When it moves on, or goes quiet for two seconds, the rest appear.
useElement({
name: "roadmap",
state: { milestones },
moves: ["show", "hide"],
itemMoves: ["show", "light"], // replaces the default (light and mark)
});Custom looks
The glow and the circle are drawn on our own layer, on top of your page, so your components don’t change. For a look of your own, such as a bar on a canvas chart, read the stage:
const { shown, lit, marked, waiting } = useStage("chart");
// lit: the name being talked about, or null
// marked: the circled names · waiting: walkthrough items not reached yetCSS-only pages can style the attributes we set on labelled nodes: data-coworkkit-lit, data-coworkkit-marked and data-coworkkit-shown. The intensity prop on CoworkkitProvider applies too: "off" draws no glow or circle, and the stage state still updates.
When the AI writes back
When typing and written replies are on for your workspace (also in early access), a written reply has no voice to time the moves to. So they land with the reply, already in their final state: panels shown or put away, the answer circled, and the first thing it named lit for a moment. In the reply itself, every word that names a thing becomes pointable: point at it and that thing lights up. In voice, the moves follow the voice; in text, they follow the reader.
What we read
Only your labels and where the labelled things sit on the screen. We never read text, input values, or anything you didn’t label. The names go to our servers so the co-worker can match them; descriptions come from the state you already declare with useElement.
Troubleshooting
- Nothing lights. Showing may not be on for your workspace yet. Otherwise, check that the label is the name the co-worker says, and that the thing is on screen: we never scroll your page for a light.
- A panel never appears. An element with
moves: ["show"]starts hidden. The co-worker shows it when it says it is showing it; your own controls can calluseStage(name).show(). - The wrong Acme lights. Add
data-coworkkit-elementto each table so we know which one a row belongs to.