Jun Takeda16:05
Gauge firmware 2.4 is on every N-series logger. Readings now arrive every 5 minutes instead of 15.
KQ-1347 · TEAM/CHAT
A complete team chat screen built around three Studio toggles: channels whose current pill travels on snap, a message stream whose action bar rises 4px on snap with pin-press in it, drawn reactions that hop and land on recoil while a ring flares and the count rolls, and a thread panel that arrives on glide — docked, over the stream or pushed — with its replies cascading in and follow-knot in its header, while mute-cone mutes the channel. Below 640px the panes become a stack whose pushes slide on glide and whose left edge takes a 1:1 back-swipe that commits by projection. Up and Down walk the messages, Enter opens a thread, Alt+Up and Alt+Down switch channel, and Escape closes the thread or goes back.
Fieldline
Noor Halvorsen · active
Survey crews, gauges and the plan for the week
Up and Down move between messages, Enter opens a message's thread, Tab reaches its actions. Alt+Up and Alt+Down switch channel.
thread open · 4 replies
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/team-chat
| Prop | Type | Default | Description |
|---|---|---|---|
| thread | "dock" | "overlay" | "full" | "dock" | How the thread panel arrives on wide screens: docked beside the stream (which narrows, and on a tablet the channel list folds to a rail), over it above a scrim as a modal dialog, or pushed like a phone screen. A phone always pushes. |
| presence | "dot" | "ring" | "off" | "dot" | How presence is drawn on avatars: a corner dot, a ring, or not drawn. It is always spoken, and the header still counts who is active. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Avatar size, type and spacing in the stream; compact sets each run's name inline with its first line. |
| people | ChatPerson[] | defaultChatPeople | Everyone in the workspace, with their presence and an optional status line. |
| me | string | "noor" | The viewer's person id: the author of what they send and whose reactions show as theirs. |
| channels / defaultChannels | ChatChannel[] | defaultChatChannels | Channels and direct messages in sidebar order, with unread and mention counts and muted state; controlled or uncontrolled. |
| onChannelsChange | (channels: ChatChannel[]) => void | — | Fires from the visit, mute or read that changed a channel, with all of them. |
| messages / defaultMessages | ChatMessage[] | defaultChatMessages | Every channel's messages and thread replies as one flat list; a reply carries its parent's id. |
| onMessagesChange | (messages: ChatMessage[]) => void | — | Fires from the send, reaction or pin that changed the list, with all of it. |
| channel / defaultChannel | string | "field-ops", else the first | The open channel's id. |
| onChannelChange | (id: string) => void | — | Fires from the row, Alt+arrow or phone push that opened a channel. |
| openThread / defaultOpenThread | string | null | null | The open thread: its first message's id, or null. |
| onOpenThreadChange | (id: string | null) => void | — | Fires from Reply in thread, a thread summary, Enter on a message, Close, Back, Escape or the back-swipe. |
| following / defaultFollowing | string[] | [] | The threads the viewer follows, by their first messages' ids. |
| onFollowingChange | (ids: string[]) => void | — | Fires from the thread's Follow button, or from a reply that followed the thread. |
| typing | string[] | — | Person ids writing in the open channel right now; shown over the composer with three dots that rest while the page is hidden. |
| now | Date | number | defaultTeamChatNow | The moment day dividers are read against, and what a sent message is stamped with. Never read from the clock during render. |
| zoneOffset | number | 0 | Minutes east of UTC that times are shown in, so server and browser agree. |
| workspace | string | "Fieldline" | The workspace's name over the channel list. |
| onSend | (message: ChatMessage) => void | — | A message or thread reply was sent; it is already in the list. |
| onReact | (messageId: string, kind: ChatReactionKind, added: boolean) => void | — | The viewer added or took back a reaction. |
| onPinChange | (messageId: string, pinned: boolean) => void | — | A message was pinned or unpinned from its action bar. |
| onMuteChange | (channelId: string, muted: boolean) => void | — | The channel header's mute toggle changed. |
| status | "ready" | "loading" | "error" | "ready" | Loading draws placeholder rows; error offers Try again. |
| onRetry | () => void | — | Try again was pressed in the error state. |
| label | string | "Team chat" | The screen's accessible name. |
| sound | boolean | false | Play a pop for reactions, sends and channel picks and a swish for threads and stack moves; the composed toggles play their own. Off unless asked for. |
| disabled | boolean | false | Read only: nothing can be sent, reacted to, pinned or muted. |
| className | string | — | Classes for the root. It is 560px tall by default; pass a height class to change it. |