Mischief

119 / Agent UI

Message Queue

Keeps follow-ups typed while the agent is busy. They stack above the composer, can be edited, reordered or removed, and go out one at a time when the agent is free.

  1. Add a nullable email column to the users table.

The agent is working.

Queued messages 2Sends when the agent finishes

Use the up and down arrow keys to change the order.

Enter adds to the queue while the agent works. Stopping it sends the next one.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/message-queue
import { MessageQueue } from "mischief-ui/message-queue"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/message-queue/message-queue.tsx
"use client" import * as React from "react"import { GripVertical, Pencil, X } from "lucide-react" import { cn } from "@/lib/utils" export type QueuedMessage = {  id: string  text: string} export type MessageQueueProps = Omit<  React.HTMLAttributes<HTMLDivElement>,

Usage

export function Composer() {
  const { busy, send } = useAgent()
  const [queue, setQueue] = useState<QueuedMessage[]>([])

  return (
    <>
      <MessageQueue
        busy={busy}
        items={queue}
        onItemsChange={setQueue}
        onRelease={(item) => send(item.text)}
      />
      <PromptInput
        status={busy ? "streaming" : "ready"}
        onSubmit={(text) =>
          busy
            ? setQueue((q) => [...q, { id: crypto.randomUUID(), text }])
            : send(text)
        }
      />
    </>
  )
}

When an item goes

The queue is yours: items in, onItemsChange out. While busy is true nothing moves. When it turns false the first item slides down toward the composer, leaves items, and is handed to onRelease. One item goes per idle spell, so if your send does not turn busy back on, the rest still wait for the next one.

An item being edited is never sent. If the agent finishes while someone is rewording the first follow-up, the queue waits for them to finish and sends the new wording.

Editing and reordering

Press an item to edit it in place. Enter saves, Shift+Enter starts a new line, Escape throws the edit away, and saving it empty removes it. Drag the handle to reorder, or focus the handle and use the up and down arrow keys. Rows glide to their new places in 160ms, and focus stays on the item that moved.

API

itemsreadonly QueuedMessage[]The queued follow-ups, first to go first.
onItemsChange(items: QueuedMessage[]) => voidReceives the queue after an edit, move, removal or release.
busybooleanWhile true the queue waits. When it turns false the first item goes.
onRelease(item: QueuedMessage) => voidReceives the item being sent. It has already left items.
labelstringNames the list. Defaults to "Queued messages".
id, text (QueuedMessage)stringA stable key and the message text.
classNamestringClasses for the queue container.
...divPropsHTMLAttributesNative div attributes.

Accessibility

The queue is a named ordered list. Each item has a move handle, an edit button and a remove button, all named after the item's text. The move handle is described by its arrow key instruction, and every move, removal and release is announced in a polite live region. Focus stays on a moved item, goes to the neighbouring item after a removal, and returns to the item after an edit. Reduced motion removes the entrance, the release slide and the reordering glide, and releases without the short exit delay.