Pattern · Prompt Input

New

Command Composer SwiftUI Block

A message composer with slash commands and at mentions: a filtered suggestion panel, chips for picks, and structured tokens on send.

A composer pinned above the keyboard that opens a suggestion panel when a word starts with / or @. Commands filter as you type, mentions search through your closure with Searching, no matches and Retry, and a pick becomes a chip above the text that onSend receives as a structured token.

  • SwiftUI
  • iOS 17.0+
  • composer-mentions
Get SwiftPieces Pro

$199 once, every piece included. See how installing works · Sign in

01

What's included

SwiftUI source you own, built on Apple frameworks only.

  • / opens commands with a symbol, name and line, filtered as you type
  • @ searches people and files through your closure
  • Searching, no matches and a failure with Retry in the panel
  • Picks become chips that remove with x or Delete
  • onSend gets the text and the tokens, and Send turns into Stop

02

Implementation notes

How it behaves, what it handles, and the decisions behind it.

The composer is one card pinned above the keyboard, with a field that grows from one line to six and Send. Typing / at the start of a word opens a Commands panel above the card, each row a symbol, the command's name, a line about what it does and the command as typed, the first row highlighted; typing on filters it, so "sum" leaves Summarize. Typing @ opens People and files and searches through your closure a moment after typing pauses, showing Searching, the results, "No people or files match" or a failure with Retry. Tap a row or press Return to take the highlighted one: the typed word leaves the field and the pick becomes a chip on a row above the text, with its symbol and an x. Delete at the start of the field removes the last chip. With a hardware keyboard the arrow keys move the highlight and Escape closes the panel. Send stays off until there is text or a chip; while your onSend runs, Stop takes its place, and a failed send hands the text and chips back with the error under the card.

Notes

  • CommandComposer(commands:placeholder:onSearchMentions:onSend:). Only onSend is required. It is a section: no navigation stack, background, padding or scroll view of its own. Pin it with .safeAreaInset(edge: .bottom) and .padding(.horizontal, 20) on a theme.color.bg background; the panel grows upward from the card.
  • CommandComposer.Command is an id (what is typed after the slash, lowercased), a title, a one-line detail and an SF Symbol. .summarize, .rewrite, .translate, .actionItems and .explain are ready, and Command.samples lists them. Commands already used are left out of the panel. An empty list turns / into plain text.
  • CommandComposer.Mention is an id, a kind (.person or .file), a name, a detail (a role, or where the file lives) and an optional symbol, person.crop.circle or doc by default. Mention.samples holds two people and three files.
  • onSearchMentions receives what was typed after the @, empty right after it, about 180 milliseconds after typing pauses. A newer query cancels the one running. Return up to six matches. Throw to show the error's description, or "Couldn't search. Try again.", with Retry. Nil turns @ into plain text.
  • onSend receives CommandComposer.Message: the text, trimmed and without the typed triggers, and tokens, an array of Token (.command or .mention) in the order they were picked, with commands and mentions for convenience. Token.label is "/summarize" or "@Q3 Report.pdf".
  • Stop cancels the task and frees the composer at once. Any other thrown error shows its errorDescription, or "Couldn't send. Try again.", and hands back the text and chips.
  • The panel follows the last word typed. Returns typed while it is open take the highlighted row instead of starting a new line.
  • Haptics: a selection tick when the highlight moves or a chip is added, a light impact on Send, an error when a send fails.
  • VoiceOver reads the panel's title as a header, each row as its name and line with the highlighted one selected, and each chip with a Remove button; adding and removing chips is announced. Every control has a 44 point target, except the chips' x, which sits in a 32 point chip and is also reached as its own button.
  • Reduce Motion turns the panel and chips into short fades.
  • CommandComposerExample at the end of CommandComposer.swift is the Workspace thread from the preview: five commands, a search over two people and three files that takes a moment, and a reply held for a couple of seconds so Stop can be tried. Pass wrapsInNavigationStack: false to push it in your own stack.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • The card and the panel are color.surface with radius.lg and 1 point separator hairlines; the card's turns to textPrimary while the field is focused. The field is type.body in textPrimary, its prompt textTertiary.
  • Panel rows are at least 52 points: a 24 point symbol column in textSecondary, names in type.body, lines in type.footnote textSecondary, the typed command in monospaced type.footnote textTertiary. The highlighted row sits on fill at radius.md. The panel title is type.footnote in textSecondary.
  • Chips are fill capsules 32 points tall with type.subhead medium labels in textPrimary and symbols in textSecondary.
  • Send is the only accent: an accent circle with an accentOn arrow, fill with textTertiary while off. Errors are type.footnote in danger.
  • State changes use motion.smooth and motion.snappy, and become short fades under Reduce Motion.