Skip to main content
Current version: 0.11.11. Source: packages/anvil2-ai-kit/CHANGELOG.md.

0.11.11

Patch Changes

  • #3027 60f130a Thanks @ericag1985! - [CartoTheme] Render bold body text at the semibold weight
  • #3027 60f130a Thanks @ericag1985! - [GuidanceCard] Update the reason label to use the semibold weight token
  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.10

0.11.10

Patch Changes

  • Updated dependencies [60a7a12]:
    • @servicetitan/anvil2-icons@0.1.9

0.11.9

Patch Changes

  • #2932 5a21d98 Thanks @pbuckingham-st! - [DiffValue, Text, TruncatedText] Change the suggested-change highlight fill to a blue-to-cyan gradient
  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.8

0.11.8

Minor Changes

Patch Changes

  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.7

0.11.7

Patch Changes

  • Updated dependencies [6e236a2]:
    • @servicetitan/anvil2-icons@0.1.6

0.11.6

Minor Changes

  • #2827 165ba0d Thanks @ericag1985! - [AppShell] Add drag resizing for the rail with responsive width restoration and drawer sizing

Patch Changes

  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.5

0.11.5

Patch Changes

  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.4

0.11.4

Minor Changes

0.11.3

Patch Changes

  • #2757 74b671f Thanks @AdamLantz! - [ChatLayout] Fix the “scroll to latest” button letting transcript content show through its translucent secondary fill

0.11.2

Patch Changes

  • #2729 84ff49a Thanks @jpex! - [Suggestion] Fix bordered suggestion chips to keep a consistent 40px tap target
  • #2729 84ff49a Thanks @jpex! - [Suggestion] Restyle the chip surface to match Card — standard fill, 1px standard border, and hover-only ambient elevation
  • #2755 018477e Thanks @arnold-dad-st! - [TabbedLayout] Fix panel content losing its state and scroll position when switching tabs or changing layout mode
  • #2741 51b4408 Thanks @ericag1985! - [ButtonToggle] Fix toggles rendering 4px larger than the equivalent Button
  • Updated dependencies []:
    • @servicetitan/anvil2-icons@0.1.3

0.11.1

Patch Changes

  • Updated dependencies [2ef0653]:
    • @servicetitan/anvil2-icons@0.1.2

0.11.0

Major Changes

  • #2691 4ff7ce9 Thanks @ericag1985! - [Exports] Rename public DOM data attributes from data-carto* to data-ai-kit* (including theme, scroll-lock, and handle attributes). Update any selectors, tests, or overrides that targeted the old attribute names.
  • #2711 4b85324 Thanks @ericag1985! - [Icons] Remove @servicetitan/anvil2-ai-kit/icons. Import from @servicetitan/anvil2-icons instead.
  • #2691 4ff7ce9 Thanks @ericag1985! - [AiKitIntlProvider] Rename i18n public surface from Carto to AI Kit: CartoIntlProvider → AiKitIntlProvider, CartoIntlProviderProps → AiKitIntlProviderProps, and message ids carto.* → ai-kit.*. Update imports and any messages overrides keyed by the old ids.

Patch Changes

  • Updated dependencies [4b85324, 4b85324]:
    • @servicetitan/anvil2-icons@0.1.1

0.10.0

Major Changes

  • #2669 21efe99 Thanks @w-a-t-s-o-n! - Publish Carto’s web AI kit from the Hammer monorepo as @servicetitan/anvil2-ai-kit. Formerly @servicetitan/carto-react-kit.

0.9.0

Major Changes

  • #288 aa78524 Thanks @w-a-t-s-o-n! - Do not use the previous version in this package’s changelog. For packages versioned in #287, that entry was merged to main but never successfully published (@servicetitan/anvil2-charts-kit@0.0.2, @servicetitan/anvil2-rn-charts-kit@0.1.0, @servicetitan/anvil2-ai-kit@0.8.4, @servicetitan/anvil2-rn@0.0.10). Several of those numbers (and other unpublished builds) are permanently reserved after a security-related npm unpublish and cannot be republished. Bump package versions past the reserved numbers. Upgrade to the versions from this release; do not pin to the removed or unpublished ones.

Minor Changes

  • #283 debb29f Thanks @ericag1985! - [TooltipSurface] Add the tooltip pill and caret as a standalone visual, for cases where the caller already knows what to explain and where — a chart overlay labelling the point under the cursor, say. Also exports the TooltipCaretDirection and TooltipSurfaceProps types

Patch Changes

  • #254 81c808a Thanks @AdamLantz! - [Window] Freeze chat scroll while popover/dialog overlays are open (excluding tooltips and details) so top-layer UI stays within the Window chrome
  • #284 f8fae3c Thanks @jessparis! - [PromptBar] Update the default disclaimer copy to “Atlas is AI and can make mistakes.”
  • #283 debb29f Thanks @ericag1985! - [Tooltip] Restyle to the new Carto design — a blue surface with an 8px radius, and a caret that is now a rounded-tip vector rather than a sharp triangle. The caret’s hook is now data-carto="tooltip-caret"

0.8.4

Minor Changes

  • #274 fc7eefd Thanks @ericag1985! - [CartoTheme, useCartoTheme] Add useCartoTheme for reading the resolved "light"/"dark" appearance from the nearest CartoTheme, plus the CartoThemeContextValue type

0.8.3

Patch Changes

  • #262 9cea437 Thanks @w-a-t-s-o-n! - [PromptBar] Fix auto-grow when the composer is nested in Window so wrapped multi-line drafts expand the bar instead of clipping to one row

0.8.2

Patch Changes

  • #231 faa01a6 Thanks @AdamLantz! - [ChatLayout] Fix short conversations not filling the available conversation region height

0.8.1

Minor Changes

  • #239 dfa0b94 Thanks @AdamLantz! - [AgentMediaCard, UserMediaMessage] Fix video previews resizing when playback starts — the poster no longer renders at a different size than the video it represents. The frame is now pinned to the video’s real aspect ratio (read from metadata) with the poster filling it. Adds an optional VideoMediaAttachment.aspectRatio (a CSS aspect-ratio value, e.g. "16 / 9") to reserve the correct frame size before the video loads for a fully shift-free preview.

Patch Changes

  • #239 dfa0b94 Thanks @AdamLantz! - [AgentMessage, UserMediaMessage, UserMessage] Pull the action toolbar up by 4px so it sits closer to the message content. The toolbar’s ghost buttons reserve hit-area padding above their glyphs, which made the gap read taller than intended; a negative top offset trims that reserved space back to the intended rhythm.

0.8.0

Major Changes

  • #246 ced2173 Thanks @ericag1985! - [Card] Remove highlighted prop; the gradient-glow highlight treatment is retired with no replacement. All cards now render with a 1px solid border instead of a resting-state box-shadow (hover lift is unchanged).

Minor Changes

Patch Changes

  • #246 ced2173 Thanks @ericag1985! - [ActionLog] Render each step title in body/regular (Inter) as plain text instead of a headline (Sofia Pro) heading
  • #246 ced2173 Thanks @ericag1985! - [AgentThinking] Render the label in body/regular (Inter) instead of headline (Sofia Pro)
  • #246 ced2173 Thanks @ericag1985! - [ButtonToggle] Compose Button’s secondary recipe class for the unselected state instead of a parallel one, and remove its lingering box-shadow/border excess
  • #246 ced2173 Thanks @ericag1985! - [DecorativeIcon] Update ai origin’s gradient background to a blue.700 → blue.600 diagonal gradient with white icon color for improved contrast
  • #246 ced2173 Thanks @ericag1985! - [Text] Update highlighted treatment to a flat blue-50 background using the new component.text.highlight.background token
  • #246 ced2173 Thanks @ericag1985! - [ArtifactCard] Fix trailing icon to render a rotated arrow instead of a chevron
  • #246 ced2173 Thanks @ericag1985! - [AgentMediaCard] Compose Card for its surface instead of re-deriving it from raw tokens, picking up the same shadow-removed/bordered visual treatment as Card. The file name now renders as an accessible heading (matching ArtifactCard), with a new titleLevel prop (default 4) to set its level
  • #246 ced2173 Thanks @ericag1985! - [Button] Update secondary variant to a borderless, no-elevation treatment; remove the resting/hover/pressed box-shadow from every variant (primary, danger included) and from the shared disabled state
  • #246 ced2173 Thanks @ericag1985! - [Text] Replace the Fontshare-hosted Satoshi typeface with a self-hosted Sofia Pro Bold @font-face (bundled from @servicetitan/anvil-fonts); the headline variant, and anything using typography.fontFamily.expressive, now renders in Sofia Pro Bold
  • #246 ced2173 Thanks @ericag1985! - [ChatHistory] Restyle the rail to a white surface with a trailing border, add an inset “Recents” label above the list, switch the “New Chat” button to a left-aligned ghost treatment with IconNewChat, and give the selected chat item an informative-subdued background with a rounded accent bar
  • #246 ced2173 Thanks @ericag1985! - [Suggestion] Update background to the same subtle wash as Button’s secondary variant
  • #246 ced2173 Thanks @ericag1985! - [ChatLayout] Update background to blue-grey.10 and reduce accent borders to 1px for visual consistency. [TabbedLayout] Reduce accent borders to 1px; the inactive tab background now uses the same subtle wash as Button’s secondary variant instead of blue-grey.10

0.7.0

Major Changes

  • #244 eb1344b Thanks @ericag1985! - [DecorativeIcon] Rename IconAvatar to DecorativeIcon and replace variant (gradient/plain) with origin (user/system/ai)

Minor Changes

  • #236 15a46a4 Thanks @ericag1985! - [ButtonToggle] Add new component to toggle a button between active and inactive state
  • #245 c5715db Thanks @AdamLantz! - [PromptBar] Add isSending and onStop props for stop-while-generating response cancellation

Patch Changes

0.6.0

Major Changes

Minor Changes

  • #237 3e60ca5 Thanks @ericag1985! - [Icons] Add IconList, IconGrid, IconLock, IconKey, IconClockFading, IconCalendarCheck, IconTruck, IconFileText, IconRocket, IconLandmark, IconHandCoins, IconGitFork, IconTarget, and IconSquare to @servicetitan/anvil2-ai-kit/icons

0.5.1

Minor Changes

  • #225 026560f Thanks @AdamLantz! - [AppShell, Window] Add defaultRailCollapsed and defaultArtifactCollapsed props so regions can start hidden on first paint without a post-mount close animation

0.5.0

Major Changes

  • #216 fd1ad53 Thanks @ericag1985! - [Citation] Replace openInNew prop with target/rel passthrough, matching Link’s convention. Set target="_blank" to open in a new tab (the external-link icon and rel="noopener noreferrer" follow automatically unless a rel is supplied); omit target for same-page citations.

Minor Changes

  • #217 cab0394 Thanks @AdamLantz! - [AgentMediaCard, IconAvatar, UserMediaMessage, Icons] Add AgentMediaCard and UserMediaMessage for chat media attachments with showPreview, expanded image/video previews, inline audio playback, and IconAvatar plain variant plus IconVideo

0.4.1

Minor Changes

  • #211 aa1a67b Thanks @AdamLantz! - [AgentMessage] Add agent chat message component with assistant toolbar actions
  • #211 aa1a67b Thanks @AdamLantz! - [AgentThinking] Export inline agent thinking indicator component

Patch Changes

  • #204 9c07804 Thanks @jpex! - [Button] Scale small/xsmall label type to 14px/12px, shrink labelled prefix/suffix icons to 16px, and lock a 24px content row so heights stay 48/40/32
  • #215 be1fb46 Thanks @ericag1985! - [AppHeader] Start-align the title instead of centering it when docked in a non-fullscreen Window

0.4.0

Major Changes

  • #194 38a13c0 Thanks @ericag1985! - [ChatHistory] ChatHistoryItem now takes a required subtitle (a secondary line, e.g. a timestamp) and an optional unread mark — subtitle is a breaking addition for existing items.
  • #198 96b404f Thanks @ericag1985! - [Icons] Add IconAboveGoal, IconBelowGoal, IconCircleCheck, IconDownload, IconOnTrack, IconPause, and IconPlay, and remove IconArrowRight, IconCheckedSelection, and IconCompletedStep from @servicetitan/anvil2-ai-kit/icons — replace IconArrowRight with IconArrowUp (rotated), IconCheckedSelection with IconCircleCheck, and IconCompletedStep with IconCircleCheck.

Minor Changes

  • #174 78d6ae5 Thanks @w-a-t-s-o-n! - [Checkbox] Add optional isIndeterminate prop with a minus indicator for mixed select-all states
  • #174 78d6ae5 Thanks @w-a-t-s-o-n! - [GuidanceCard] Add composite component for Atlas suggested-change visuals with four lifecycle states, selectable change rows, diff display, line-item slots, and responsive action footer
  • #174 78d6ae5 Thanks @w-a-t-s-o-n! - [Icons] Add IconMinus to @servicetitan/anvil2-ai-kit/icons

Patch Changes

0.3.5

Minor Changes

  • #180 4ef1ee7 Thanks @w-a-t-s-o-n! - [Checkbox] Add Checkbox component.
  • #182 bcaeffe Thanks @AdamLantz! - [ChatLayout] Add component for an application shell’s chat region — a centered, measure-capped column that stacks an optional conversation transcript above an optional bottom-pinned prompt composer. The transcript sticks to the bottom as messages arrive, stops auto-scrolling when the user scrolls up to read history, and shows a “scroll to latest” button while scrolled up; force a scroll to the latest after the user sends via controlRef’s scrollToBottom(). The transcript is a role="log" live region for screen-reader announcements.
  • #181 49af511 Thanks @w-a-t-s-o-n! - [DiffValue] Add before/after diff component for suggested-change UIs.
  • #193 34449a0 Thanks @AdamLantz! - [Suggestion, SuggestionList] Add Suggestion and SuggestionList components — tappable AI/agent suggestion chips and a wrapping group container that reports the selected suggestion via onSelect.
  • #179 6a85b82 Thanks @w-a-t-s-o-n! - [Text] Add highlighted surface flag for suggested-change styling.
  • #179 6a85b82 Thanks @w-a-t-s-o-n! - [TruncatedText] Add plain-string truncation with Show more / Show less.
  • #173 a665dda Thanks @ericag1985! - [ActionLog] Add component for rendering a collapsible vertical timeline of agent steps from a configuration-first steps array

Patch Changes

  • #195 e7beb96 Thanks @AdamLantz! - [ChatLayout, ChatHistory, TabbedLayout, AppShell] Background color corrections across chat and layout surfaces. Tab chips gain a continuous 3px left border from the first tab through the panel content. Resize handles across AppShell and TabbedLayout now show a dynamic blue pill indicator that follows the cursor along the cross axis instead of a static background tint. TabbedLayout split panes meet at a flush 0px seam with an out-of-flow handle.
  • #178 b1a54ff Thanks @w-a-t-s-o-n! - [Icons] Fix custom Icon SVG sizing via CSS instead of width/height attributes.
  • #170 106b7d3 Thanks @AdamLantz! - [TabbedLayout] Fix the horizontal-split resize divider. It now tracks the cursor as you drag instead of drifting out of sync, and it responds reliably when the split opens inside a Window. Split-pane headings no longer show the active-tab underline, which only conveys meaning in the stacked tab strip.
  • #191 6fee6ce Thanks @ericag1985! - [Menu] Fix the scrolling option list overflowing the popover’s rounded corners
  • #145 4fc2468 Thanks @tounsoo! - [AppShell, BottomSheet, Button, Card, Citation, Dialog, Link, Menu, PromptBar, TabbedLayout, UserMessage, Window] Drive motion from semantic motion tokens instead of inlined curves. Hover/press/focus acknowledgments now use the faster feedback duration (100ms, down from 200ms), panel and overlay transitions use the emphasis duration with directional enter/exit easing, and easing curves shift to the semantic motion values. Animation timings and curves change as a result. [Tokens] Retarget the bottom-sheet and dialog focus-ring transition.duration from motion.duration.transition to motion.duration.feedback (200ms to 100ms).
  • #190 ab27e8c Thanks @ericag1985! - [Button] Button label text now renders in medium weight (Inter Medium).
  • #188 520d9dc Thanks @AdamLantz! - [Window] Smooth out the docked↔fullscreen morph. Maximizing then restoring a docked panel that has been dragged now animates back to where the panel actually sits instead of snapping to its default corner at the end.

0.3.4

Minor Changes

  • #160 8d837f1 Thanks @tounsoo! - Citation now renders a 1px solid white hairline border. Adds a new citation.border.color component token that aliases primitive neutral 0 (white) directly, so the border stays white in both light and dark appearances.

Patch Changes

  • #168 95f9c55 Thanks @w-a-t-s-o-n! - [Card] Fix highlighted gradient pulse glow extending unevenly on non-square cards
  • #162 d6044e5 Thanks @tounsoo! - Tokenize PromptInput and the AgentPrompt header foreground.
    • carto-tokens — adds agentPrompt.header.foreground (aliases foreground.color.standard) and a new prompt-input.tokens.json with promptInput.font (composite typography), promptInput.foreground, promptInput.placeholder.foreground, and promptInput.disabled.foreground.
    • carto-react-kit — wires the AgentPrompt question heading to the new header token, and routes PromptInput’s text, placeholder, disabled, and typography through the new component tokens (previously raw primitive.font.* and direct semantic.* references). All values are unchanged, so there is no visual change.
  • #167 ece80f9 Thanks @tounsoo! - [PromptBar] Fix inline padding layout and use semantic.opacity.disabled token

0.3.3

Minor Changes

  • #149 48247a2 Thanks @w-a-t-s-o-n! - [Exports] Add @servicetitan/anvil2-ai-kit/tokens subpath re-exporting the Carto token proxies (primitive/semantic/component/cartoThemeClass), so consumers reference the kit’s single compiled theme instead of recompiling @servicetitan/carto-tokens.
  • #132 f8ef3da Thanks @w-a-t-s-o-n! - [Dialog] Make primaryAction optional to support footerless dialogs

Patch Changes

  • #153 35243f8 Thanks @tounsoo! - [BottomSheet] Fix scrollable body padding so the scrollbar sits at the sheet edge and prevent the header close button from squishing
  • #154 2c4029f Thanks @tounsoo! - [Dialog] Fix scroll shadows spanning inset content width by splitting block and inline padding
  • #155 fd93c63 Thanks @tounsoo! - [TabbedLayout] Unify panel shell and body internal layout classes

0.3.2

Minor Changes

  • #146 175ac52 Thanks @AdamLantz! - [Window] Add drag-to-move for the docked panel. The control surface now exposes dragHandleProps (via controlRef / useAppSurface()); spread it onto a leading AppHeader action to turn that action into a grip that drags the panel anywhere in the viewport, with arrow keys nudging it for keyboard users (clamped inside the viewport). The dragged position survives a maximize→restore round-trip (restore returns the panel to where it was dragged) and resets to the default corner only on close. [AppHeader] AppHeaderAction gains an optional dragHandleProps field. When set, that action renders as a drag handle (carrying the move handlers and grab cursor) instead of a press button — for use with the Window drag handle above. Inert when there’s no docked window to move, so it’s safe to pass unconditionally. [Window] Open and size state are now controllable. Pass open / state (with onOpenChange / onStateChange) to let an external owner be the single source of truth; omit them to keep the existing uncontrolled behavior seeded by defaultOpen / defaultState. The change callbacks fire on every transition — including ones the window triggers itself (a header close/maximize button, or a region intent that auto-opens) — so a controlling store hears user-driven changes and can run its own side effects. Fully backward-compatible: all new props are optional.

Patch Changes

  • #146 175ac52 Thanks @AdamLantz! - Fix broken type declarations on the main entry. Several public declarations re-exported types from src/internal/, which the build excludes from .d.ts emit — so the shipped declarations referenced ../../internal/… modules that aren’t in the package, and consumers hit “Cannot find module”. The fix moves those types to public locations rather than shipping internal/:
    • The appSurface module (useAppSurface, AppSurfaceHandle, ShowArtifactOptions, WindowDragHandleProps, ArtifactPresentation) moved from internal/appSurface/ to components/AppShell/appSurface/. Consumed by AppShell, Window, AppHeader, and TabbedLayout; the public re-export path (@servicetitan/anvil2-ai-kit) is unchanged.
    • The public Waveform types (WaveformPreset, WaveformSource) now live in PromptBar’s public types.ts (where they were already re-exported); WaveformProps stays internal.
    No public import paths change — these were only ever exported from the package barrel. Known follow-up (pre-existing, untouched here): the ./icons subpath entry has the same defect (dist/icons/*.d.ts reference ../internal/components/Icon/*); tracked for a separate fix.

0.3.1

Minor Changes

  • #115 fbc44d8 Thanks @pbuckingham-st! - [AgentPrompt] Add component for the agent’s inline clarifying-question prompt — single or multi select with an optional free-text “Something else…” row (shown when onOtherChange is wired), an optional multi-step Back/Next/Send stepper, an overridable question headingLevel, an always-present close button, and a resolved answer passed to onSend/onNext
  • #104 11a4c84 Thanks @AdamLantz! - AppShell: the artifact is now presented as a modal drawer only when the shell is narrow. At/above a measured shell width of 720px the artifact is always inline — drag-detach, the imperative showArtifact / toggleArtifact, and fit-fallout are all gated, and an artifact drawer opened below 720px re-attaches inline when the shell widens past it. To keep the artifact inline when it can’t fit alongside the rail (720–960px), the rail yields to a drawer (it’s the lowest-priority region and collapses first); the rail may still be a drawer at ≥ 720px. Below 720px, the artifact behaves exactly as before. This also affects the read-only artifactPresentation on useAppSurface() — it never reports "drawer" for the artifact at ≥ 720px.
  • #104 11a4c84 Thanks @AdamLantz! - AppShell: when the shell widens, extra space now goes to the artifact first — up to the width it was opened at (its regular / large preset, or the user’s last dragged width) — and only then to chat. Previously all extra space went to chat and the artifact stayed at its shrunken width. The artifact now tracks a desired width (set by an imperative open or a user drag) that’s clamped at render rather than overwritten on resize, so shrinking the shell clamps the artifact down without losing its target and re-widening restores it. Chat still absorbs any space beyond the artifact’s desired width.
  • #119 04e0174 Thanks @ericag1985! - [Icons] Add IconAtlasLogo — the Atlas brand mark, a fixed full-color gradient icon (it ignores the color override, like the Load spinner).
  • #121 acb1ed5 Thanks @ericag1985! - [ChatHistory] Add component for the conversation navigation rail — a “New Chat” button (onNewChat) above a single-select list of past chats (items, selectedKeys / onSelectionChange), with a history-less state when items is empty
  • #117 629bf5d Thanks @pbuckingham-st! - [Citation] Add component for rendering inline source citations as a pill-shaped link. Takes a required href and text; openInNew (default true) sets target="_blank" + rel="noopener noreferrer" and renders a trailing external-link icon, or set false for on-page anchors. The recipe consumes the new citation component-token tier (aliasing the semantic tier for light/dark).
  • #110 71b7210 Thanks @tounsoo! - [Tokens] Add a global focus semantic category — focus.width ({size.75}, the new 3px primitive rung), focus.offset ({size.50}, 2px), and focus.color ({border.color.accent}) — so every focus ring shares one system-wide treatment. button.focus.ring.{color,width,offset} now alias focus.* (no visual change: still #0265dc, 3px, 2px offset), and the new size.75 = 3px primitive backs the ring width. [React Kit] Dialog (body scroll region) and PromptBar (disclaimer link) now consume the global semantic.focus.* tokens instead of inline outline literals. The Dialog ring is unchanged (3px / 2px / accent); the PromptBar disclaimer-link ring grows from 2px to the global 3px width so it matches every other focus ring.
  • #118 e88586e Thanks @ericag1985! - [ArtifactCard] Add a button-surface card for AI artifacts, with a title, optional description, configurable heading level, and an artifactType that selects its decorative gradient-avatar icon.
  • #118 e88586e Thanks @ericag1985! - [IconAvatar] Add a gradient-avatar component that frames a Carto icon at a fixed size and color, for use in cards and other surfaces.
  • #116 0399b0a Thanks @ericag1985! - [Menu] Add component for action and selection menus, with a configured items list, trigger button, placement, and selectionMode
  • #114 59ddeb9 Thanks @ericag1985! - [Card] Add component for static, link, and button surfaces with padding scale and highlighted treatment
  • #103 53f4704 Thanks @ericag1985! - [BottomSheet] Add bottom-anchored, full-width modal overlay with a drag- and keyboard-resizable handle (3 detents), a title/subheader/close-button header, and a scrollable body.
  • #118 e88586e Thanks @ericag1985! - [Icons] Add IconFile, IconInvoice, and IconMessageSquareText to @servicetitan/anvil2-ai-kit/icons
  • #104 11a4c84 Thanks @AdamLantz! - Add AppShell — a fully-fluid rail / chat / artifact application layout (no breakpoints). A side region that can’t fit inline becomes a modal drawer rather than disappearing; the artifact is one persistent panel that morphs between its inline track and the drawer (it also drag-resizes and drag-detaches). Region state is internal, driven imperatively via the AppSurfaceHandle (show/hide/toggle per region; showArtifact({ width }) opens "regular" 560px or "large" 720px-floor) from the controlRef prop or the useAppSurface() hook. The same handle works in a bare shell or inside a Window, so a control anywhere — including a card in the chat — drives the layout the same way. Exports AppShell, useAppSurface, and the AppShellProps / AppSurfaceHandle / ArtifactWidth / ShowArtifactOptions types.
  • #104 11a4c84 Thanks @AdamLantz! - Add AppHeader — a horizontal header bar: leadingActions / trailingActions (generic { icon, label, onPress } descriptors rendered as ghost icon-only Carto Buttons) around a centered title (substantial-headline scale, <h1> semantics, ellipsis-truncated). Renders a semantic <header>; drops into AppShell’s header slot (wiring its toggles via useAppSurface) but works standalone. Exports AppHeader and the AppHeaderProps / AppHeaderAction types.
  • #120 3e3ccc1 Thanks @AdamLantz! - Add TabbedLayout — a multi-pane layout for the artifact region. It shows one or more tabs as a tab strip (stacked) or two side-by-side / stacked panes (vertical-split / horizontal-split, drag-resizable); a single tab renders just its label heading and content. The mode picker appears at ≥ 720px and the layout falls back to stacked below that. In an artifact drawer it surfaces a close button. Exports TabbedLayout and the TabbedLayoutProps / TabbedLayoutMode / TabbedLayoutTab types.
  • #104 11a4c84 Thanks @AdamLantz! - Add Window — chrome that hosts an AppShell as a fixed-position chat window (docked, bottom-right) that grows into a fullscreen modal and shrinks back (shared-element animation). The header and chat nodes are preserved across the transition (relocated, not remounted), so conversation state survives expand/restore. Window + shell share one control surface — the AppSurfaceHandle via controlRef or useAppSurface(): open/close/toggle, maximize/restore/toggleSize, plus the shell’s region intents. Region intents auto-compose with the window, so showArtifact() from a docked chat maximizes and opens the artifact in one call — no docked/fullscreen branching for consumers. Esc / scrim-click restores to docked. Exports Window and the WindowProps / WindowState types.

Patch Changes

  • #123 c8b8b28 Thanks @tounsoo! - [Tokens] Add agentPrompt.* component tokens for the AgentPrompt surface — the card (background, border.color, radius, elevation, padding, gap), region spacing (header.gap, options.gap, actions.gap), the option rows (option.background.{resting,hover,pressed}, option.foreground, option.padding, option.radius, option.selected.border.color), the multi-select checkbox indicator (checkbox.border.color, checkbox.foreground, checkbox.selected.background), the step counter (stepCount.foreground), and the empty-answer error (error.foreground, error.gap), each aliasing the semantic tier. Also adds a semantic opacity.disabled (0.6) token. AgentPrompt now consumes these via the component.agentPrompt.* proxy instead of reaching into semantic.* directly. As part of this:
    • Option rows are now proper interactive surfaces: their background uses background.color.subtle.{resting,hover,pressed} (replacing the prior translucent rgba(4,4,4,…) fills) with the paired foreground.color.on.subtle text color.
    • The disabled-dim opacity is now the shared opacity.disabled token; the option rows shift from 0.5 to the system 0.6.
    • The focus ring adopts the semantic focus tier (focus.width / focus.color / focus.offset), shifting the ring thickness from a hard-coded 2px to the system 3px.
  • #139 ca506f0 Thanks @tounsoo! - [Tokens] Add artifactCard.* component tokens for the ArtifactCard surface — foreground (the title/description/arrow color, aliasing foreground.color.standard), gap (the horizontal rhythm between the lead cluster and the trailing arrow, and between the icon and text, aliasing spacing.base), and text.gap (the title↔description lockup, aliasing spacing.micro). ArtifactCard now consumes these via the component.artifactCard.* proxy instead of reaching into semantic.*/primitive.* directly. No visual change: the title↔description gap was already 4px (primitive.size["100"]), now expressed as the matching spacing.micro intent.
  • #126 d5c6c81 Thanks @tounsoo! - [Tokens] Add the bottomSheet component token set, aliasing the semantic tier: surface.{background,foreground,radius,elevation}, padding.{inline,block.end}, gap, header.{gap,padding.inline}, subheader.foreground, handle.padding.block.{start,end}, handle.grip.{width,height,radius,background}, focus.ring.{color,width,offset}, and focus.ring.transition.{duration,easing} (aliasing motion.duration.transition / motion.easing.transition — the everyday color-shift motion intent; excludePlatforms: figma). handle.grip.background is a provisional gap (primitive {color.neutral.400} — no foreground/handle intent matches it yet); handle.grip.{width,height} are intentional primitives ({size.900} / {size.100}). [React Kit] BottomSheet now consumes its component.bottomSheet.* tokens instead of inline semantic/primitive literals. Visual changes adopted from the token mappings: top corners use the large-container radius.generous (24px, up from 12px); the surface elevation and text pair with the standard surface; the drag-handle focus-ring offset normalizes to the system 2px (was 4px); and the now-shipped focus.* tokens back both the grip and body focus rings. The vestigial 1px top hairline (not in the Figma spec) is removed — the drop shadow alone separates the sheet from the backdrop. The shared modal backdrop now consumes semantic.scrim.color (no visual change). The body focus-ring fade now consumes focus.ring.transition.{duration,easing} (200ms / cubic-bezier(0.4, 0, 0.2, 1)); its easing adopts the motion.easing.transition curve, shifting from the kit’s inline [0,0,0.4,1] — making BottomSheet the first consumer of the motion tokens.
  • #108 011c6ea Thanks @tounsoo! - [Tokens] Add per-variant button.<variant>.border.width tokens (secondary → size.25 / 1px, primary / ghost / danger → size.0) so the borderless variants gain border capability without changing their current footprint. Button’s secondary variant now consumes its width token directly — replacing the prior hard-coded 1px literal — and subtracts it from the padding so the bordered variant keeps the same outer size as the borderless ones (which stay at border-width: 0).
  • #135 c00c7d5 Thanks @tounsoo! - [Tokens] Add the card component token set, aliasing the semantic tier: card.background.{resting,hover} ({background.color.standard.*}), card.radius ({radius.moderate}), card.elevation.{resting,hover} ({shadow.ambient.resting} / {shadow.ambient.hover}), card.padding.{snug,base,relaxed} ({spacing.*}), and card.focus.ring.{color,width,offset} ({focus.*}). No visual change — these formalize the values Card already uses (resting + hover elevation now route through the shadow.ambient family). [React Kit] Card.css.ts now consumes component.card.* instead of reaching into semantic.* directly. The disabled state consumes semantic.opacity.disabled, and the in-place transition consumes semantic.motion.duration.transition + semantic.motion.easing.transition — adopting the semantic ease-in-out curve (cubic-bezier(0.4, 0, 0.2, 1)) in place of the prior inlined cubic-bezier(0, 0, 0.4, 1) (duration unchanged at 200ms). Only the highlight gradient remains inline as a flagged gap (gradients have no DTCG representation).
  • #137 ec0c239 Thanks @tounsoo! - [Tokens] Add the dialog component token set, aliasing the semantic tier: overlay.padding, surface.{background,foreground,border.color,border.width,radius,elevation}, padding, gap, header.gap, footer.gap, focus.ring.{color,width,offset}, and focus.ring.transition.{duration,easing} (aliasing motion.duration.transition / motion.easing.transition — the everyday color-shift motion intent; excludePlatforms: figma). Values cross-checked against Figma (Carto Web Components, node 787:3308): width 360px, padding/gap 24px, header gap 8px, footer gap 12px, background white all match. Adopted value shifts (semantic intent over the Figma literal): surface.border.color aliases {border.color.subtle} (neutral.30, #f6f6f6, from Figma’s #e6e6e6); surface.radius aliases {radius.generous} (24px, from Figma’s 12px) to match BottomSheet’s panel radius. surface.elevation aliases {shadow.floating.resting} (the shared transient-surface intent; blur follows the semantic token). surface.border.width is {size.25} (1px). [React Kit] Dialog now consumes its component.dialog.* tokens instead of inline semantic/primitive literals. The surface foreground pairs with the standard surface (foreground.color.on.standard, same neutral.800 value — no visual change). The body focus-ring fade now consumes focus.ring.transition.{duration,easing} (200ms / cubic-bezier(0.4, 0, 0.2, 1)); its easing adopts the motion.easing.transition curve, shifting from the prior inline [0,0,0.4,1] to match BottomSheet + Button. The fixed 360px surface width stays an intentional raw literal (no semantic intent, no matching size primitive); zIndex: 1000 is kept raw on purpose (Carto has no layering token scale yet); the shared modal backdrop continues to consume semantic.scrim.color.
  • #133 0454f19 Thanks @w-a-t-s-o-n! - [Dependencies] Move React Aria (react-aria, react-aria-components, react-stately) from peerDependencies to dependencies and externalize all runtime deps in the build (only react/react-dom stay peers); carto-tokens stays a devDependency so consumers don’t need vanilla-extract.
  • #116 0399b0a Thanks @ericag1985! - Ghost buttons now raise to the shared elevation on hover, active, and focus-visible (matching Figma), instead of staying flush.
  • #129 b1a78a4 Thanks @tounsoo! - [Tokens] Add link component tokens that alias the semantic tier: link.foreground.resting ({foreground.color.standard}) and link.foreground.hover ({foreground.color.informative}) for the link’s text color, link.radius ({radius.trim}) for the focus-ring corner, and link.icon.gap ({spacing.micro}) for the trailing external-link icon’s lockup. Link’s recipe now consumes these tokens in place of the raw semantic.* / primitive.* references it used before — all value-preserving, no rendered change. The shared focus-ring (component.button.focus.ring.*) and the inline transition curve are unchanged: the ring is still the kit’s single button-owned definition consumed directly, and motion adoption stays deferred pending the curve-shift decision (matching Button).
  • #134 c083c3c Thanks @tounsoo! - [Tokens] Add menu.* component tokens for the Menu surface and options — the popover (surface.background, surface.radius, surface.elevation), the option list (list.gap, list.padding), the option rows (option.background.{resting,hover,pressed}, option.foreground.resting, option.gap, option.radius, option.padding.{inline,block}, option.opacity.disabled), the selected option (option.selected.background.{resting,hover,pressed}, option.selected.foreground.{resting,hover,pressed}), and the focus ring (focus.ring.color, focus.ring.width), each aliasing the semantic tier. [Tokens] Extend the semantic tier so the Menu’s selected states alias real intents instead of raw primitives:
    • background.color.informative-subdued is promoted from a scalar to a stateful group — resting (blue.50), hover (blue.100), pressed (blue.200).
    • foreground.color.on.informative-subdued is added — resting (blue.600), hover (blue.700), pressed (blue.800) — the on-color paired with the informative-subdued surface.
    Citation’s hover surface moves to the .resting leaf with no value change (background.color.informative-subdued → .resting). Menu now consumes these via the component.menu.* proxy instead of reaching into semantic.* / primitive.* directly. As part of this:
    • The popover surface uses radius.moderate (16px) and the option rows use radius.trim (12px), matching the Figma bindings (semantic/radius/moderate on the container, semantic/radius/trim on the options).
    • The disabled-dim opacity is now the shared opacity.disabled token (replacing a hard-coded 0.6).
    • The selected option surfaces/text alias background.color.informative-subdued.{resting,hover,pressed} and foreground.color.on.informative-subdued.{resting,hover,pressed} (no more color.blue.* primitive interims).
    • The unselected option uses the ghost surface intent — background.color.ghost.{resting,hover,pressed} — paired with foreground.color.on.ghost text (neutral.800, a deliberate shift from Figma’s foreground/color/default #040404, which has no carto equivalent).
    Cross-checked against Figma (node 43:290): popover background/elevation/radius, option spacing/typography, the selected-state surfaces/text, and the 180/360px width mechanics all match. The new informative-subdued / on.informative-subdued hover values (blue.100 #cce3ff, blue.700 #1d4ca3) equal Figma’s primary-subdued-hover / primary-hover. The unselected option’s hover/press tints intentionally use the translucent ghost intent (neutral.400 @ 0.08/0.16) rather than Figma’s opaque standard.hover (#f9f9f9). The option pressed/active states are kept from the RAC/anvil2 interaction model but are beyond the Figma spec, which defines only default/hover/disabled. Remaining gap: the popover min/max width (180/360px) stay raw literals in the recipe as no width scale exists at any tier.
  • #136 5bf6962 Thanks @tounsoo! - [Tokens] Add shadow.ambient — a new low rung on the elevation scale for a subtle interactive affordance. Unlike the directional raised/floating shadows, ambient is a soft, diffuse shadow that sits flush to the surface with no offset (neutral.800 @ 4% resting / 8% hover, blur {size.400}), signalling an element is interactive without pronounced elevation. New ladder: flush (0) → ambient (4%) → raised (12%) → floating (24%). flush, raised, and floating are unchanged (same values and Figma variable IDs), so existing consumers are unaffected. [React Kit] Card now consumes semantic.shadow.ambient.{resting,hover} for its resting and hover elevation, replacing the inline custom ambient shadow (the resting value is identical, resolving the long-standing TODO(carto-tokens)) and the prior raised.resting hover (12% → ambient 8%, a softer hover lift).
  • #104 11a4c84 Thanks @AdamLantz! - AppShell / Window accessibility and RTL fixes from review:
    • AppShell: dismissing the artifact drawer with Escape now stops propagation, so it no longer also collapses a surrounding fullscreen Window; on close it moves focus to the chat region (a programmatic focus target) instead of letting focus fall to <body>. The artifact scrim is marked aria-hidden. The resize separator no longer emits an undefined aria-valuemax before the shell is measured (it falls back to the artifact min). Positional styles use logical properties (inset-inline-*) for RTL.
    • Window: the docked panel is now a named region (using the same accessible name as the fullscreen dialog, overridable via aria-label) so assistive tech can locate it.

0.3.0

Major Changes

  • #90 7a412a2 Thanks @ericag1985! - [Icons] Remove IconCircle from @servicetitan/anvil2-ai-kit/icons. It is no longer part of the design. For the Status Old state, use IconCircleSmall.

Minor Changes

  • #90 7a412a2 Thanks @ericag1985! - [Icons] Add IconArrowRight to @servicetitan/anvil2-ai-kit/icons.
  • #80 085fa1f Thanks @AdamLantz! - [CartoIntlProvider] Add component for localizing Carto’s built-in strings. It renders the kit’s own scoped react-intl context so Carto strings never collide with a consuming app’s setup. The provider is optional — components render English with no provider mounted. Pass locale to switch language and keep React Aria date/time formatting in sync, or messages to override individual built-in strings by Carto message ID.
  • #75 c7bf5c0 Thanks @ericag1985! - [Button] Add a danger variant for destructive actions — a filled, raised button on the critical/red surface, composing with all existing sizes and icon configurations.
  • #100 7c5244b Thanks @ericag1985! - [Link] Add component for rendering accessible inline and standalone links.
  • #86 a451448 Thanks @ericag1985! - [Dialog] Add component for modal confirmation dialogs, with trigger, controlled open state, and footer primaryAction / secondaryAction Buttons (including a danger confirm variant)
  • #90 7a412a2 Thanks @ericag1985! - [Icons] Add IconMenu to @servicetitan/anvil2-ai-kit/icons.
  • #80 085fa1f Thanks @AdamLantz! - [PromptBar] Localize the component’s built-in strings through the kit’s i18n layer — the placeholder, attachment add/remove, voice, send/done/dismiss/cancel actions, and the disclaimer now resolve via useCartoIntl. They render in English by default (unchanged copy) and respond to CartoIntlProvider’s locale / messages. The placeholder prop still overrides as before; the attachment remove label interpolates the attachment name via ICU. The disclaimer prop now takes a single message string with a <link>…</link> placeholder marking the linked text, replacing the separate text + linkLabel fields. This keeps the wording and the link’s position in the sentence as one translatable unit — a translator (or consumer) can place the link mid-sentence — rather than assuming text-then-link concatenation. linkHref is unchanged.
  • #69 75fc760 Thanks @AdamLantz! - [PromptBar] Add component for composing AI prompts with text, attachments, and voice transcription.

Patch Changes

  • #100 7c5244b Thanks @ericag1985! - [Markdown] Restyle links to reuse the Link treatment: underlined near-black at rest, informative blue on hover, and a focus ring (replacing the flat blue style).
  • #59 2b25a96 Thanks @tounsoo! - [Tokens] Add button component tokens (primary / secondary / ghost backgrounds, foregrounds, elevation; font, gap, padding, radius, and focus) and wire Button to consume them via the component proxy instead of hard-coded primitives. The button’s icon size consumes the existing icon.size.large token directly (no redundant button.icon alias)
  • #85 69c049c Thanks @tounsoo! - [Tokens] Add button.danger component tokens (background, foreground, elevation) aliasing the critical semantic surface, and wire the Button danger variant to consume them via the component proxy instead of reading semantic.* critical tokens directly
  • #77 987b206 Thanks @tounsoo! - [Tokens] Add the border.color.accent semantic intent (brand-interactive outline for focus rings, active navigation, and selected controls) and restructure the button’s focus tokens into a button.focus.ring group (color → border.color.accent, plus width and offset). Button now consumes button.focus.ring.{color,width,offset} for its focus ring instead of a primitive color alias and an inline outline width. No visual change — the ring stays #0265dc, 3px thick, 2px offset.
  • #83 fdcc420 Thanks @tounsoo! - [PromptBar] Add promptBar component tokens and wire the recipe to consume them New semantic-aliasing component tokens (promptBar.*) cover the bar surface (background, border, radius, elevation, gap, padding), the attachment tile, the disclaimer, and the error foreground. The PromptBar recipe now consumes component.promptBar.* instead of referencing semantic/primitive directly. Values map 1:1 from the prior references except the attachment-tile background, which adopts background.color.subtle.resting (neutral.30 → neutral.50) and now follows light/dark.
  • #90 7a412a2 Thanks @ericag1985! - [Icons] Update IconGrip to render Lucide GripVertical, the six-dot vertical drag-handle glyph.
  • #90 7a412a2 Thanks @ericag1985! - [Icons] Update IconStatusNew to render in currentColor instead of a hardcoded blue, so it inherits text color like other icons.
  • #65 1dd0874 Thanks @tounsoo! - [Tokens] Add tooltip.radius, tooltip.padding.inline, tooltip.padding.block, and a tooltip.font typography composite, and repoint tooltip.foreground / tooltip.background to the prominent semantic intents (foreground.color.on.prominent and background.color.prominent.resting) in place of raw color.neutral primitives. Tooltip now sources its surface (color, radius, padding) and typography from these tokens via the component proxy instead of hard-coded primitives, and renders its content in a plain <span> carrying tooltip.font rather than a nested <Text>.
  • #74 3935c98 Thanks @tounsoo! - [UserMessage] Style against userMessage and messageToolbar component tokens instead of raw primitives
  • Updated dependencies [2b25a96, 69c049c, 987b206, fdcc420, 3bcff52, 8cad2e0, 9c85f60, 7d8d6b3, 57c6646, 9b3dfa8, 36c2afe, 03a0e99, 1dd0874, 3935c98, 2b25a96]:
    • @servicetitan/carto-tokens@0.1.2

0.2.0

Major Changes

  • #37 ed3dec3 Thanks @ericag1985! - [VisuallyHidden] Remove component; use aria-label on the interactive parent with decorative Icon instead
  • #49 45c4c42 Thanks @ericag1985! - [UserMessage] Add an action toolbar and new props:
    • timestamp (required) renders the copy action and formatted send time
    • onRetry adds a retry action when error is set
    • revealToolbarOnHover hides the toolbar until the message is hovered or focused

Minor Changes

  • #37 ed3dec3 Thanks @ericag1985! - [Button] Add component (primary / secondary / ghost; medium / small / xsmall) with required label, optional icon, and touch promotion for xsmall
  • #37 ed3dec3 Thanks @ericag1985! - [Icons, Types] Export IconElement type alias for factory-created Carto icons
  • #31 69ef424 Thanks @tounsoo! - [Text, Tokens] Convert text tokens to DTCG typography composites; fix broken font-family in Swift build; Text now spreads composite values for font-family, weight, and line-height

Patch Changes

0.1.0

Minor Changes

  • a3b08e5: [Icons] Add per-icon components under the ./icons subpath (25 Lucide-backed, 13 custom ServiceTitan icons)
  • a8863c6: [Markdown] Add component for rendering GFM markdown via a required source prop
  • 16bc697: [Text] Add component (headline / body / eyebrow variants) and components.text.* tokens
  • a3b08e5: [CartoTheme] Add container component with appearance="auto" | "light" | "dark"
  • b6292cc: [Tooltip] Add internal component with tooltip color tokens and MFE shadow-DOM support
  • 17b11d2: [UserMessage] Add chat bubble component for end-user-authored messages

Patch Changes

  • b6292cc: [CartoTheme] Swap font loading to direct rel="stylesheet" with font-display: optional
  • 1c2ea19: [Tokens] Singularize vanilla-extract tier names (primitives/components → primitive/component; deprecated aliases remain for one release)
  • Updated dependencies [2578aea]
  • Updated dependencies [a3b08e5]
  • Updated dependencies [16bc697]
  • Updated dependencies [b6292cc]
  • Updated dependencies [5aa7870]
  • Updated dependencies [04c22a3]
  • Updated dependencies [d0ec6be]
  • Updated dependencies [0638cef]
  • Updated dependencies [c676116]
  • Updated dependencies [b6292cc]
  • Updated dependencies [2e9073b]
  • Updated dependencies [48de98e]
  • Updated dependencies [8178a7d]
  • Updated dependencies [1c2ea19]
  • Updated dependencies [128f12e]
  • Updated dependencies [974616f]
    • @servicetitan/carto-tokens@0.1.0
Last modified on November 2, 2026