Skip to content

feat(apollo-wind): close the chat primitive gaps found by the Wind chat - #1402

Draft
BenGSchulz wants to merge 1 commit into
feat/apollo-wind-math-fencesfrom
feat/apollo-wind-chat-primitive-gaps
Draft

BenGSchulz wants to merge 1 commit into
feat/apollo-wind-math-fencesfrom
feat/apollo-wind-chat-primitive-gaps

Conversation

@BenGSchulz

@BenGSchulz BenGSchulz commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

MST-16880, part of the Wind chat epic MST-16172. Stacked on #1400 (math fences); the next PR adds compact density.

The Wind chat renderers (R-3 to R-8) worked around gaps in apollo-wind primitives. This adds the smallest options that let them drop those workarounds, plus the overlay animations that generated no CSS.

Ticket correction: KaTeX strict

The ticket says input Material rejects renders in Wind because Material uses strict: true. It does not: Material also renders math through rehype-katex, and when a formula fails strict mode rehype-katex renders it again with strict: 'ignore'. strict: true and 'ignore' produce identical HTML (checked). Material's direct katex.renderToString path in code.tsx never runs for math fences, because rehype-katex replaces them first. katexOptions.strict is still exposed, documented as reporting only.

Changes per item

Item Change Decision
CodeBlock body replaces the highlighted source; header, actions and copy stay. false, null, undefined keep the source.
Markdown renderCodeBlock(block, props) renders each fenced block with the props Markdown would pass CodeBlock, so a host component can keep per-block state (a preview toggle). Needed because actions and body share state per block. renderCodeActions stays.
Markdown katexOptions: { strict, macros }. Output stays MathML and trust stays off, applied after the options. Each render gets its own copy of macros. KaTeX writes \gdef into the object it is given: a shared one would let one message redefine a host macro for later messages, and a frozen one would throw.
Markdown Display math gets overflow-y-hidden next to its horizontal scroll. Fixes a pre-existing axe scrollable-region-focusable (1 to 2px of vertical overflow) on main's Math story and #1400's MathFences story.
Dialog DialogStrings / DEFAULT_DIALOG_STRINGS: close, closeTakeover, expand, collapse. Defaults unchanged.
Sheet SheetStrings / DEFAULT_SHEET_STRINGS: close.
DisclosureTree New read-only ARIA tree: no toolbar, defaultExpanded or controlled expanded / onExpandedChange, lazy children (built once per item object), leading icon, trailing info, description as tooltip and accessible description, roving tabindex with Up/Down, Home/End, Right/Left, Enter/Space. Each treeitem contains its group and is named by its label. Rows share ToolCall's row style through an internal module. A sibling rather than options on TreeView: TreeView is a file manager (search, drag selection, context menus, a HoverCard per row, inline English copy, every row tabbable), and switching that off would still not give it tree keyboard navigation.
DropzoneOverlay typesHint, sizeHint, countHint strings, shown under the hint when not empty. The host formats them, since they carry values.
DropzoneOverlay A paste with files is kept from the field unless nothing was accepted and it carries text. Text pasted along with a refused file now lands.
AttachmentTitle Tooltip with the full name while the title is cut off, measured on every open. Opt out with truncationTooltip={false}. On by default, so composer chips get it too. Radix has no way to detect a provider above, so each title has its own TooltipProvider (a context, no DOM).
Search SearchStrings / DEFAULT_SEARCH_STRINGS with clear; SearchWithSuggestions passes strings through.
EmptyState size="compact" for panels: smaller type and spacing, sm buttons, no heading element. A heading inside a panel would add a stray entry to the page outline.
Animations animate-in / animate-out with fade-*, zoom-* and slide-* modifiers (numbers, fractions, bare, arbitrary values) in tailwind.utilities.css, plus an unlayered reduced-motion rule. Own CSS instead of tw-animate-css (MIT, CSS only): it redefines delay-* for every consumer, its collapsible keyframes clash with ours, and it has no reduced-motion handling. The unlayered rule also wins over tw-animate-css if a host loads it.

Host-visible changes and release notes

  • Overlays now animate. Popover, Dialog, AlertDialog, Tooltip, HoverCard, Sheet, DropdownMenu, ContextMenu, Select and the DropzoneOverlay used animation classes that generated no CSS. They now fade, zoom and slide (150ms by default, Dialog 200ms, Sheet 300 to 500ms), and not at all under prefers-reduced-motion. Radix Presence keeps a closing overlay mounted until its exit animation ends, so downstream e2e tests that assert immediate removal may need to wait. Consumers of dist/styles.css and of @uipath/apollo-wind/tailwind.css (as apollo-react's tailwind.chat.css and tailwind.canvas.css import it) both get the utilities; checked by compiling a sheet of that shape.
  • Strings types. DropzoneOverlayStrings gains required typesHint, sizeHint and countHint. A host that builds the full object (not a Partial) gets a type error until it adds them.
  • Paste. DropzoneOverlay with enablePaste no longer blocks text pasted along with refused files.
  • DisclosureTree markup. New component, no change to TreeView.
  • AttachmentTitle. The title span carries a Radix tooltip trigger (data-state) by default.

What the Wind chat can drop in its follow-up

  • CodeBlockActions opening the HTML preview in a Dialog: an inline code and preview toggle through renderCodeBlock and body, as Material does.
  • The custom Dialog close button (showCloseButton={false} plus its own close): DialogContent strings.
  • Its own DisclosureTree and openState: apollo-wind DisclosureTree with expanded / onExpandedChange for the tree message and the Traces section.
  • The single-string drop hint: typesHint, sizeHint, countHint.
  • titleTruncatedChip (native title on pointer over): the built-in tooltip.
  • showClearButton={false} on the history Search: strings.clear.
  • EmptyMessage in the history panel: EmptyState size="compact".
  • Its own paste handler in WindChatDropzone, if enablePaste with the remaining count covers it.
  • Nothing for strict: Wind already matches Material.

Out of scope, noted

  • A disabled DropzoneOverlay still lets a dropped file open in the tab; the chat keeps its panel-scoped guard.
  • MarkdownCodeBlock has no per-block streaming flag; the host passes its message-level flag.
  • An AttachmentGroup that overflows with chips that have no buttons (the chat's message attachments) is a scroll region keyboard users cannot reach.
  • Prism token colours fail axe colour contrast on a few tokens in light mode (existing, e.g. CodeBlock Languages).

Tests

  • Unit tests for every option: CodeBlock body (including false and null), renderCodeBlock props, KaTeX macros (passed through, host object unchanged after \gdef, frozen object still renders) and trust staying off, display math overflow classes, Dialog, Sheet and Search strings, SearchWithSuggestions strings, DropzoneOverlay limit lines and paste (accepted with text, refused with text, image only), AttachmentTitle tooltip (cut off, fits, re-measured, axe with the tooltip open), EmptyState compact (with axe), DisclosureTree (structure, ownership, naming, click, controlled, lazy, keyboard including Right on an open row with no children, zero-valued slots, description, axe).
  • tailwind.animations.test.ts compiles the utilities with Tailwind: keyframes, the unlayered reduced-motion rule, and ten value forms.
  • Storybook: every new story checked with axe in the browser (clean apart from the Prism contrast note above). Popover and Dialog checked opening with apollo-enter and closing with apollo-exit. The HTML preview stories use sandbox="allow-scripts" with a no-referrer and CSP prefix, shared from .storybook/html-preview.ts.
  • Biome on touched files, tsc --noEmit, the full apollo-wind suite and pnpm --filter @uipath/apollo-wind build.

🤖 Generated with Claude Code

The Wind chat renderers worked around several apollo-wind gaps. This adds
the smallest options that let them drop those workarounds:

- CodeBlock: a body slot that replaces the highlighted source (an inline
  HTML preview toggled from the header actions); false, null and
  undefined keep the source. Markdown gains renderCodeBlock, which gets
  each block and the CodeBlock props it would use, so a host can keep
  per-block state such as that toggle.
- Markdown: katexOptions for strict and macros. Output stays MathML and
  trust stays off. Each render gets its own copy of macros, since KaTeX
  writes \gdef into the object it is given. rehype-katex re-renders a
  formula that fails strict mode leniently, so strict only reports
  (Material behaves the same way). Display math no longer overflows
  vertically by a pixel, which made it an unfocusable scroll region.
- Dialog and Sheet: strings for their built-in button labels.
- DisclosureTree: a new read-only ARIA tree for traces and nested
  results (no toolbar, defaultExpanded or controlled open rows, lazy
  children, leading icon, trailing info, description as tooltip and
  accessible description, roving tabindex with arrow keys, Home/End,
  Enter/Space). Each treeitem contains its group and is named by its
  label. TreeView stays the file manager; switching its search,
  selection and menus off would not give it tree keyboard navigation.
  Its rows share ToolCall's row style.
- DropzoneOverlay: typesHint, sizeHint and countHint lines under the
  hint, empty by default. A paste with files is kept from the field
  unless nothing was accepted and it carries text, so that text lands.
- AttachmentTitle: a tooltip with the full name while it is cut off.
- Search and SearchWithSuggestions: strings for the clear button label.
- EmptyState: a compact size for panels, with no heading element.
- Animations: the animate-in/out, fade, zoom and slide utilities that the
  shadcn overlays use generated no CSS. They are now defined in the
  package's utilities (not tw-animate-css, which would redefine delay-*
  for consumers and clash with the collapsible keyframes), and an
  unlayered rule turns them off for prefers-reduced-motion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Dependency License Review

  • ✅ 1947 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1713
ISC 91
Apache-2.0 56
BSD-3-Clause 28
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.3.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

This branch was successfully deployed

4 active deployments
preview/apollo-landing — 524325af Deployed Oct 17, 2026 by github-actions[bot]
preview/apollo-design — 524325af Deployed Oct 17, 2026 by github-actions[bot]
preview/apollo-vertex — 524325af Deployed Oct 17, 2026 by github-actions[bot]
preview/apollo-docs — 524325af Deployed Oct 17, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant