Documentation Index
Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
Use this file to discover all available pages before exploring further.
Implementation
ArtifactPanel API
Common Examples
ArtifactPanel reads its responsive mode (inline vs. overlay) from the surrounding ArtifactPanelLayout. Wrap chat content and any artifact panels in ArtifactPanelLayout so the panel can switch modes when the available width changes. Outside a layout the panel falls back to inline mode.
Basic Usage
Wrap chat content and the panel in ArtifactPanelLayout. Open and close the panel from consumer state:import { useState } from "react";
import {
ArtifactPanel,
ArtifactPanelLayout,
} from "@servicetitan/anvil2-ext-atlas";
function ArtifactDetails({ children }) {
const [open, setOpen] = useState(false);
return (
<ArtifactPanelLayout>
{children}
<ArtifactPanel
isOpen={open}
onOpenChange={setOpen}
title="Supplemental info"
>
{/* artifact content */}
</ArtifactPanel>
</ArtifactPanelLayout>
);
}
Swapping Artifacts While Open
Pass triggerKey so the panel treats a value change as a new open event — focus shifts into the panel for the new artifact, and close-restore returns focus to the most recent trigger:import { useState } from "react";
import { ArtifactCard, ArtifactPanel } from "@servicetitan/anvil2-ext-atlas";
function ChatWithArtifacts() {
const [activeId, setActiveId] = useState<string | null>(null);
return (
<>
<ArtifactCard
title="Insight 1"
description="..."
artifactId="insight-1"
active={activeId === "insight-1"}
onClick={() => setActiveId("insight-1")}
/>
<ArtifactCard
title="Insight 2"
description="..."
artifactId="insight-2"
active={activeId === "insight-2"}
onClick={() => setActiveId("insight-2")}
/>
<ArtifactPanel
isOpen={activeId !== null}
onOpenChange={(open) => {
if (!open) setActiveId(null);
}}
title={`Insight ${activeId ?? ""}`}
triggerKey={activeId ?? undefined}
>
{/* artifact content */}
</ArtifactPanel>
</>
);
}
Agent-Initiated Open
For panels opened by the agent rather than a user gesture, set focusOnOpen to false so focus is not pulled away from the chat composer:<ArtifactPanel
isOpen={agentOpened}
onOpenChange={setAgentOpened}
title="Atlas opened this for you"
focusOnOpen={false}
>
{/* artifact content */}
</ArtifactPanel>
React Accessibility
- The panel renders an
<aside> landmark with aria-labelledby tied to the title and aria-describedby tied to the description when present.
- Focus moves into the panel on open (default behavior) and returns to the previously focused element on close.
- Slide and fade animations respect
prefers-reduced-motion.
- The panel is not modal — chat content remains interactive while the panel is open. The panel does not handle Escape or click-outside; those belong to the parent chat.
- When
ArtifactPanelLayout switches the panel from inline to overlay mode at narrow widths, the chat content beneath remains in the document flow and reachable by assistive technology.
<ArtifactPanel
isOpen={open}
onOpenChange={setOpen}
title="Supplemental info"
description="Variance details"
position="right"
triggerKey={artifactId}
>
{content}
</ArtifactPanel>
ArtifactPanel Props
Content rendered in the panel body. Accepts arbitrary content including markdown, GenUI components, and other interactive elements.
Whether the panel is currently rendered.
onOpenChange
(open: boolean) => void
required
Called when the panel requests an open-state change (e.g. when the close button is clicked).
Panel heading rendered in the header and referenced by aria-labelledby.
Optional sub-header text rendered beneath the title.
Move focus into the panel when it opens, and return focus to the previously focused element when it closes. Set false for agent-initiated opens so focus is not pulled away from the chat composer.
initialFocusResolver
(focusables: FocusableElement[]) => FocusableElement
Pick the initial focus target when focusOnOpen is true. Defaults to the first non-close focusable.
position
"left" | "right"
default:"right"
Side the panel is anchored to. Drives border placement.
Identifier for the current artifact. When this value changes while the panel is already open, focus management treats it as a new open event: it captures the newly-focused trigger and re-shifts focus into the panel, so close-restore returns to the right element.