Skip to content

Composer context lane in the frontend library (ERMAIN-1022) - #1409

Draft
bdart wants to merge 11 commits into
feature/ermain-1021-composer-context-fixes-before-the-lane-phase-0from
feature/ermain-1022-composer-context-lane-in-the-frontend-library-phase-1
Draft

bdart wants to merge 11 commits into
feature/ermain-1021-composer-context-fixes-before-the-lane-phase-0from
feature/ermain-1022-composer-context-lane-in-the-frontend-library-phase-1

Conversation

@bdart

@bdart bdart commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Phase 1 of the composer context chips (ERMAIN-1022). Adds the context lane to the frontend library: one family of chips inside the composer, before staged files, whose state comes from the host's send resolver. No host passes items yet, so the web app and the add-ins render as before. Stacked on #1406.

  • ChatInput takes contextItems and composerNotice and draws the lane itself (not through the replaceable files slot): one height cap shared with files, "+N more", token estimate from included items, claimed files hidden only while their chip is sent.
  • Chip primitive with six states, always-visible (x)/(+), roving focus, Delete/+ keys, one polite live region that announces outcomes, and a detail that is a sheet below 480 px and a popover on the wide app.
  • New kit point ComposerContextChipFace (faces only), conformance suite, example kit face, shared surface 1.19.
  • Stories for every host state of the prototype, a Storybook Playwright spec, strings in all five locales.

Decision defaults: (x) on ambient context turns it suggested and never removes it, and the suggested meta states how long it stays off (decisions 1-2); superseded uses the muted look (decision 4).

Plan deviations:

  • Files stay in the kit slot, so "+N more" counts chips only; the file tile the cap cuts fades into the pinned "Remove all".
  • After Delete on an ambient chip, focus stays on it (now suggested) so the announced + acts on it. Needs design sign-off.
  • The lane is labelled "Message context" (files are their own list).
  • The contract adds the prototype's lockReason, removeLabel, removeActionLabel, restoreLabel, spokenLabel, announcements and detail.actions.

Next: phase 2 moves the Word selection and document into the lane (ERMAIN-1023). Native Word PC check pending.

bdart added 11 commits October 19, 2026 22:06
…1022)

The ComposerContextItem contract and the lane that renders it: one tab
stop with arrows, Home/End, Enter for the detail, Delete/Backspace and +;
always-visible (x)/(+) beside the open button; six states drawn from the
state the host hands back, with the outcome announced only once it does;
"+N more" against a budget measured in one pass; the detail as a bottom
sheet below 480px and an anchored popover above it; a composer notice
whose live region is in place before it speaks.

ComposerContextChipFace is a new composed-node registry point (surface
1.19) with a host conformance suite; the lane and the default face are
pinned on the kit surface. Staged files can leave the cap to a shared
area (sharedHeightCap, pinned Remove all), ModalBase docks as a sheet,
and the chip geometry, skin and layout tokens are themable.
contextItems and composerNotice are new optional props; omitted, the
composer renders exactly as before. With items, the lane and the files
slot share one capped area outside the replaceable slot, so a kit
replacing the slot cannot hide what is sent. Included items' estimates
join virtualFiles as one stable array, claimed files leave the file row
but still count and go with their user chip, Remove all only takes the
files shown, and the turn-end refocus leaves a keyboard user on a chip.
…MAIN-1022)

The face is registered against surface 1.19 and runs the host's
conformance suite. The attachment preview no longer removes a file on a
click on its name: the name previews it and a named x removes it, and
the bare x for Remove all is now worded, in all five locales. The kit
Storybook shows the face in the host lane.
Stories play the host resolver at the task-pane width and on the wide
app. The harness measures the lane's semantics, geometry, state looks
and contrast in light and dark, the keyboard model and announcements,
the sheet and popover detail, the shared cap with "+N more", German
strings, a kit face, notices, and that the web composer is unchanged.
…N-1022)

Checked against the prototype at 340 and 480 px:

- The shared cap gave the first file row priority, so Word's three ambient
  chips beside five files showed one chip and "+2 more". Files now keep the
  top half of their first tile and the pinned "Remove all" row, and all
  three chips stay in view, as in the prototype. The budget is a pure
  function with unit tests; the Storybook spec checks the half tile.
- Notices drew the themed error mark, an (x) beside the dismiss (x), and a
  warning mark the chips use for errors. They now draw the chips' warning
  and error marks, and the busy spinner sits on the first line of text.
Every host state of the target prototype is now a story under
Chat/ComposerContextHosts, fed to the real ChatInput as data: Word, Outlook
reading and composing, and Teams. The story plays the host resolver, so (x),
+ and their announcements behave as each host will.

The lane harness checks each one against the prototype: kind, state, tone,
the open button's name and the (x)/(+) name, every chip in view at 340 and
480 px, and contrast in light and dark. It also walks the flows the
prototype settles: an error dismissed to suggested with + to retry,
host-worded removals with the lane stating the lifetime, locked chips, and
the last chip handing focus to the message box. State styles are now
measured in dark too, and chips and files are checked to sit inside the
composer box with the chips first.
…les (ERMAIN-1022)

A click on the example chip face opens the host's detail and never removes;
only the host's x does. The example attachment preview previews a file from
its name, removes it only from its named x, and words Remove all.
…MAIN-1022)

- A + or (x) that lands anywhere but the expected state (a retry that
  fails again, a selection that wins) is announced; late host changes are
  no longer taken for the answer to a click, and a + still loading waits.
- Claimed files hide only while their chip is included or loading, leave
  the row only once the host drops a user chip, and cannot be removed while
  the composer is locked. The lane handler keeps its identity across
  keystrokes.
- Focus after a removal follows the lane's order, folded chips included;
  the lane folds again once everything fits, it empties or every item is
  new; error and choice chips are never folded; "+N more" keeps the tab
  stop through a re-measure.
- spokenLabel for announcements and default control names,
  removeActionLabel for the detail footer, pinned sheet footer, two
  persistent notice regions, glyph swap by state only.
- Focus ring without :has(), quieter superseded chips in dark, a fade on
  the file tile the shared cap cuts, conformance checks the meta, "item"
  lifetime copy names mail items, German choice copy.
…-1022)

- Dismissing the Word or Outlook selection gives the document or draft
  back in the stories, as one resolver would, with spoken names and
  footer labels from the prototype.
- New stories for a retry that fails again (Word, Outlook) and a detail
  taller than the sheet; the specs assert the failed-retry announcements,
  the document coming back, the pinned footer, the fade and both notice
  regions.
- The example kit's Storybook loads the host stylesheet and catalog, so
  the context-chip panel renders as in the app.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant