KQ-1347 · TEAM/CHAT

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.

KQ-1347 · TEAM/CHAT

Fieldline

Noor Halvorsen · active

field-ops

Survey crews, gauges and the plan for the week

  1. Yesterday

    Jun Takeda16:05

    Gauge firmware 2.4 is on every N-series logger. Readings now arrive every 5 minutes instead of 15.

  2. Today

    Lina Moreau09:12

    Overnight rain pushed N4 to 1.8 m³/s. I'd like a second reading before we trust it.

  3. Rae Okafor09:14

    I can take it on the way to the weir. The ford is passable this morning.

  4. Bringing the spare flow meter in case the first one drifts again.

  5. Pinned

    Noor Halvorsen09:31

    Plan for today: Rae on N4 and the weir, Lina on the slump survey, Tomas checks the logger sync. Shout if anything moves.

  6. Lina Moreau09:58

    @Rae the left bank below the weir has slumped about a metre since July. Can you get a GPS fix on the new edge?

  7. Tomas Ilunga10:19

    Logger sync is clean. Two stations were 40 seconds behind; fixed.

  8. Rae Okafor10:22

    Flow meter reads 1.76 at N4 against the staff gauge. Calling it confirmed.

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

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/team-chat

Props

thread
"dock" | "overlay" | "full"
default "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"
default "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"
default "cozy"
Avatar size, type and spacing in the stream; compact sets each run's name inline with its first line.
people
ChatPerson[]
default defaultChatPeople
Everyone in the workspace, with their presence and an optional status line.
me
string
default "noor"
The viewer's person id: the author of what they send and whose reactions show as theirs.
channels / defaultChannels
ChatChannel[]
default defaultChatChannels
Channels and direct messages in sidebar order, with unread and mention counts and muted state; controlled or uncontrolled.
onChannelsChange
(channels: ChatChannel[]) => void
default —
Fires from the visit, mute or read that changed a channel, with all of them.
messages / defaultMessages
ChatMessage[]
default defaultChatMessages
Every channel's messages and thread replies as one flat list; a reply carries its parent's id.
onMessagesChange
(messages: ChatMessage[]) => void
default —
Fires from the send, reaction or pin that changed the list, with all of it.
channel / defaultChannel
string
default "field-ops", else the first
The open channel's id.
onChannelChange
(id: string) => void
default —
Fires from the row, Alt+arrow or phone push that opened a channel.
openThread / defaultOpenThread
string | null
default null
The open thread: its first message's id, or null.
onOpenThreadChange
(id: string | null) => void
default —
Fires from Reply in thread, a thread summary, Enter on a message, Close, Back, Escape or the back-swipe.
following / defaultFollowing
string[]
default []
The threads the viewer follows, by their first messages' ids.
onFollowingChange
(ids: string[]) => void
default —
Fires from the thread's Follow button, or from a reply that followed the thread.
typing
string[]
default —
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
default 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
default 0
Minutes east of UTC that times are shown in, so server and browser agree.
workspace
string
default "Fieldline"
The workspace's name over the channel list.
onSend
(message: ChatMessage) => void
default —
A message or thread reply was sent; it is already in the list.
onReact
(messageId: string, kind: ChatReactionKind, added: boolean) => void
default —
The viewer added or took back a reaction.
onPinChange
(messageId: string, pinned: boolean) => void
default —
A message was pinned or unpinned from its action bar.
onMuteChange
(channelId: string, muted: boolean) => void
default —
The channel header's mute toggle changed.
status
"ready" | "loading" | "error"
default "ready"
Loading draws placeholder rows; error offers Try again.
onRetry
() => void
default —
Try again was pressed in the error state.
label
string
default "Team chat"
The screen's accessible name.
sound
boolean
default 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
default false
Read only: nothing can be sent, reacted to, pinned or muted.
className
string
default —
Classes for the root. It is 560px tall by default; pass a height class to change it.