proposals/ChatInput/decisions.md53 lines · View on GitHub
Decisions
New component, not a Textarea prop. The published Textarea puts the border on the <textarea> itself and has no slot, so a toolbar can't sit inside the box. ChatInput moves the border, background, focus ring and resize handle to a wrapper (the "field") that holds the textarea and the toolbar.
Slots, not built-in buttons:actionsStart and actionsEnd take any nodes. The component hardcodes no buttons or text, and the screen decides which controls to show. The story composes IconButton (also as Send, variant='primary'), MenuButton and (since 2026-10-19) the bare Select from these proposals; ModelSelector was retired.
Resize:resize: vertical is on the field, so the drag handle is in the bottom corner (below the toolbar, as in the design) and the textarea grows with it.
Same look as Textarea: label, helper message, focus ring, size (s/m padding) and isResizable mirror Textarea. The wrapper reuses the package's pds-input, pds-input-label and pds-input-message classes, since InputLabel and InputMessage are not exported.
Left out of this first version (in Textarea): required marker, label tooltip, character counter, read-only, inputWidth, translation strings, and field validation (error and success borders and messages). Chat prompts have nothing to validate; send failures belong in the conversation, not on the input. Open: does chat need a counter or a max length?
Open: model selector shows a chevron (the Select's trigger icon). The design shows text only. Confirm.
Voice button: PDS has no microphone icon, so microphoneLines (FA Pro regular microphone-lines) is staged in the Icon proposal and the story uses it. The port adds it to the icon registry.
Open: agentic. Should the field get the AI gradient border (for example while the agent is working), like Button? Not added.
Open: Enter to send. The component only renders the field; Enter / Shift+Enter handling is left to the screen. Confirm it shouldn't be built in.
Marked new-pattern (record only; the sidebar pill was removed 2026-10-19).
Storybook: Component Proposals/AI Chat/Chat Input. Depends on the IconButton (including its primary variant, the Send button), MenuButton and Select (bare variant) proposals (and Spinner through IconButton).
The plus button is a MenuButton (icon-only), from the PDS base. Sample menu: "Add photos & files" (paperclip icon and description) followed by placeholders (Menu item 2, 3 and 4), since the real tools depend on the product. Keyboard, focus and open/close behavior come from MenuButton, so no new menu component.
Not in v2's MenuButton, seen in the references: a trailing status or shortcut on an item ("Connected", a key hint), selected toggles (check marks on Web search / Memory), submenus with a chevron, and a count badge on an item. Not built; each would be an extension to MenuButton's item type. Open: which of these does chat need? Toggles (on/off tools) look the most likely.
Menu item text and icons are sample data in the story; the component holds none.
Model selector is the Select proposal with variant='bare' (no border or fill, check on the selected model). It replaced the ModelSelector proposal on 2026-10-19. Flat list: Zappy first (preselected), then other models. No groups, headings or icons. Picking a model updates the button label. Model names are sample data.
Selected model marker: the Select's own check (showSelectedIcon). It used to come from isSelected on MenuButton items for the retired ModelSelector.
MenuButton renders its icons with the local Icon wrapper (so new icons like sparklesSolid work in menu items), though no menu uses one right now.
variant: default | compact (a look, picked once per screen). Compact is a single-line bar with the controls on the same row, using the standard input radius (not a pill; PDS fields aren't fully round): add at the start, then the text, then model selector, voice and Send. It is not resizable. In compact the DOM follows the visual order (start controls, text, end controls) so keyboard order matches.
Compact story shows only the add button at the start (no separate attach) and no helper message, as in the reference.
Compact keeps the rounded-square Send button (not the reference's circle). The reference's waveform (voice mode) state when the field is empty is not included. Open: wanted?
Open (compact): growth. It is one row now; should it grow taller (to a max) as the user types a long prompt?
The label is visually hidden by default (showLabel defaults to false, in both variants). It is still required and rendered for screen readers; the placeholder carries the meaning on screen. Set showLabel to show it.
The Usage page example shows the slots as placeholders (actionsStart, actionsEnd, the dashed boxes the PDS stories use), so what the component owns (the field and the toolbar) is clear from what the screen fills in. The Default story keeps the full toolbar.
Drag and drop files: opt-in via onFilesDrop. The component only reports the dropped File[]; the screen decides what to do (upload, attach chips, validate type or size). No handler, no drop behavior. Ignored when disabled.
Only file drags are intercepted (dataTransfer.types includes Files), so dragging selected text into the textarea still works natively.
Drag-over look: dashed border in the focus color plus the hover tint, on the field (so the toolbar is inside it). Optional dropLabel text overlays the field (aria-hidden, since a drag is a pointer interaction; keyboard users keep the attach button). Text is a prop, not hardcoded. Open: which wording, and whether the overlay should hide the textarea, as in the references.
Open: attachments. Dropped files aren't rendered inside the field yet (no chips or previews). The story lists the names in the helper message as a stand-in. Do we want an attachment row above the textarea?
Two new slots, named by position (2026-10-16).aboveField sits on the top edge, outside the border: a grey bar inset by spacing-xs with only its top corners rounded, for context such as the branch the prompt applies to. fieldHeader sits inside the border above the textarea, for attachment chips and image thumbnails; it wraps. (It was first not rendered in compact; the same day it was added there, above the row. See below.) Both take any nodes; the component holds no content. The bar uses surface-default-secondary and border-default-secondary. Open: should the bar show in compact, and should fieldHeader get a max height with scroll when many files are attached?
Default story fills the new slots with samples (2026-10-16).aboveField shows a branch icon, text and time; fieldHeader shows two sample file chips, and dropped files are added there (images get a thumbnail from an object URL, each with a remove button). The chip is now the ChatAttachment proposal (it replaced a story-only helper). The helper text no longer lists dropped names.
Compact has its own story (2026-10-16),Compact, next to Default. variant is still a control on both.
Compact takes the slots too (2026-10-16). From the designer's compact mock: fieldHeader (the files) sits above the single row inside the border, and aboveField works as in default. To allow it, the compact controls and textarea moved into a .pds-chat-input__row inside the field (the field is a column in both variants), and the helper message shows in compact. The Compact story shows the plain bar and the bar with both slots. The attach button is still only in default.
Slot stories (2026-10-16):Default and Compact show the plain field; With Above Field and With Field Header show each slot filled, with the default and compact variants one above the other. The earlier "Story samples" toggles were removed in favor of these stories.
With All Properties story (2026-10-16): every slot filled (above field, field header, both toolbars) plus the helper message, in both variants.
With Attachment Error story (2026-10-16): one failed ChatAttachment plus a critical helper message under the field (icon, file name and reason, role='alert'). The message is written by the screen, not the component, and goes away when the failed file is removed. See ChatAttachment/decisions.md.
Several attachment errors, one helper message (2026-10-16). The screen writes it, since the component holds no copy. One failure: name the file ("report-final.pdf is too large (max 10 MB)"). Several with the same reason: name the reason ("2 files are too large (max 10 MB)"). Different reasons: count them and point to the chips ("2 files could not be attached. Hover a file to see why."). One role='alert' for the whole message (the chips do not each announce), recomputed when a file is removed, gone when the last failure is. Shown in the With Attachment Errors story. Open: an errorType that drives retry versus remove-only.
One error story (2026-10-16):With Attachment Error was folded into With Attachment Errors. Removing failed files there shows the one-file message.
Drag-over uses the focus blue, not grey (2026-10-19). The drop state was the neutral hover grey (--pds-color-interactive-background-hover) with a dashed focus border, which the designer disliked. The field and the dropLabel overlay now fill with --pds-color-status-info-background (a light blue in the focus hue), the border stays --pds-color-interactive-focus, and the label text is --pds-color-status-info-foreground. Not checked in dark mode. Port impact: none beyond the CSS.
No separate attach (paperclip) button in the samples (2026-10-19). The add menu already has "Add photos & files", so the paperclip IconButton in actionsStart was redundant and was removed from the Chat Input and Chat Panel stories. actionsStart still takes any nodes. Keyboard users add files through the menu; drag and drop stays a pointer shortcut.
Removed: attachments in the above-field container (2026-10-21)
Decision: the idea of putting the attached files and images in the aboveField container (shown in the With All Properties story on 2026-10-20) was dropped at the designer's request. Attachments go in fieldHeader, inside the border. The story-only wiring was removed; the component was never changed.
Why: the chips sat on the grey context bar and read as part of the context, not the message.
Port impact: none.
Send is a primary IconButton (2026-10-21)
Decision: the separate SendButton proposal was removed. Send is an IconButton with variant='primary' and, while the request is in flight, isWorking; stop is iconName='stop'. The stories (Chat Input and the Chat with an agent recipe) use it that way.
Why: the designer decided Send should be a variant of the icon button, filled with the primary color, not its own component.
Port impact: one component fewer to port; Chat Input now needs IconButton's primary variant and isWorking (and the stop icon).