npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@automattic/agenttic-ui

v0.1.52

Published

UI components for the Agenttic framework

Readme

@automattic/agenttic-ui

React UI components for AI agent chat interfaces. A pure UI layer designed to work seamlessly with @automattic/agenttic-client hooks or any agent communication system.

Installation

npm install @automattic/agenttic-ui

Key Features

  • Pure UI components with no agent communication logic
  • Composable architecture for complete layout flexibility
  • Floating and embedded chat variants
  • Controlled input support for external value management
  • Smooth animations and drag-and-drop positioning
  • Message actions and markdown rendering
  • Request cancellation UI with stop button functionality
  • TypeScript support with comprehensive types
  • Storybook component documentation

Quick Start

Complete Chat Interface (Convenience API)

import { useAgentChat } from '@automattic/agenttic-client';
import { AgentUI } from '@automattic/agenttic-ui';

function ChatApplication() {
	const {
		messages,
		isProcessing,
		error,
		onSubmit,
		abortCurrentRequest,
		suggestions,
		clearSuggestions,
		messageRenderer,
	} = useAgentChat( {
		agentId: 'big-sky',
		sessionId: 'my-session',
	} );

	return (
		<AgentUI
			messages={ messages }
			isProcessing={ isProcessing }
			error={ error }
			onSubmit={ onSubmit }
			onStop={ abortCurrentRequest }
			suggestions={ suggestions }
			clearSuggestions={ clearSuggestions }
			messageRenderer={ messageRenderer }
			variant="floating"
			placeholder="Ask me anything..."
		/>
	);
}

Composable Architecture

For complete control over layout and component ordering:

import { AgentUI } from '@automattic/agenttic-ui';

function CustomChatLayout() {
	return (
		<AgentUI.Container
			messages={ messages }
			isProcessing={ isProcessing }
			error={ error }
			onSubmit={ onSubmit }
			onStop={ abortCurrentRequest }
			variant="embedded"
		>
			<AgentUI.ConversationView>
				<AgentUI.Header />
				<AgentUI.Messages />
				<AgentUI.Footer>
					<AgentUI.Notice />
					<AgentUI.Input />
				</AgentUI.Footer>
				<AgentUI.Suggestions />
			</AgentUI.ConversationView>
		</AgentUI.Container>
	);
}

Controlled Input

External control of input value and changes:

function ExternallyControlledChat() {
	const [ inputValue, setInputValue ] = useState( '' );

	return (
		<AgentUI
			messages={ messages }
			isProcessing={ isProcessing }
			onSubmit={ onSubmit }
			inputValue={ inputValue }
			onInputChange={ setInputValue }
			variant="embedded"
		/>
	);
}

Typing Status Tracking

Track when the user is actively typing in the input field:

function ChatWithTypingStatus() {
	const [ isTyping, setIsTyping ] = useState( false );

	return (
		<AgentUI
			messages={ messages }
			isProcessing={ isProcessing }
			onSubmit={ onSubmit }
			onTypingStatusChange={ setIsTyping }
			variant="embedded"
		/>
	);
}

The onTypingStatusChange callback is triggered when the typing status changes. The user is considered "typing" when:

  • The input field is focused
  • The browser window is focused
  • The input contains text (length > 0)

Architecture

AgentUI Components

AgentUI - Convenience wrapper with default layout AgentUI.Container - Root container with state management and context AgentUI.ConversationView - Conversation layout wrapper AgentUI.Header - Chat header with close/expand buttons AgentUI.Messages - Message history display AgentUI.Footer - Footer wrapper for input and notice AgentUI.Input - Text input with auto-resize AgentUI.Notice - Error and notification display AgentUI.Suggestions - Quick action suggestions

AgentUIProps Interface

interface AgentUIProps {
	// Core chat data
	messages: Message[];
	isProcessing: boolean;
	error?: string | null;
	onSubmit: ( message: string ) => void;
	onStop?: () => void;

	// UI configuration
	variant?: 'floating' | 'embedded';
	placeholder?: string | string[];
	triggerIcon?: React.ReactNode;
	notice?: NoticeConfig;
	emptyView?: React.ReactNode;

	// Chat state management (floating variant)
	floatingChatState?: ChatState;
	onOpen?: () => void;
	onExpand?: () => void;
	onClose?: () => void;

	// Suggestions
	suggestions?: Suggestion[];
	clearSuggestions?: () => void;

	// Message rendering
	messageRenderer?: ComponentType< { children: string } >;

	// Controlled input (optional)
	inputValue?: string;
	onInputChange?: ( value: string ) => void;

	// Typing status tracking
	onTypingStatusChange?: ( isTyping: boolean ) => void;

	// Styling
	className?: string;
	style?: React.CSSProperties;
}

Usage Patterns

Flexible Component Ordering

Place suggestions anywhere in your layout:

<AgentUI.Container {...props}>
  <AgentUI.ConversationView>
    <AgentUI.Messages />
    <AgentUI.Suggestions /> {/* Above input */}
    <AgentUI.Footer>
      <AgentUI.Input />
    </AgentUI.Footer>
  </AgentUI.ConversationView>
</AgentUI.Container>

// Or below input:
<AgentUI.Container {...props}>
  <AgentUI.ConversationView>
    <AgentUI.Messages />
    <AgentUI.Footer>
      <AgentUI.Input />
    </AgentUI.Footer>
    <AgentUI.Suggestions /> {/* Below input */}
  </AgentUI.ConversationView>
</AgentUI.Container>

Individual Components

Use individual components for complete customization:

import {
	Messages,
	Message,
	ChatInput,
	Suggestions,
} from '@automattic/agenttic-ui';

function FullyCustomChat() {
	return (
		<div className="my-chat-container">
			<Messages
				messages={ messages }
				messageRenderer={ messageRenderer }
			/>
			<ChatInput
				value={ inputValue }
				onChange={ setInputValue }
				onSubmit={ onSubmit }
				placeholder="Type a message..."
				isProcessing={ isProcessing }
			/>
			<Suggestions
				suggestions={ suggestions }
				onSuggestionClick={ onSubmit }
				onClear={ clearSuggestions }
			/>
		</div>
	);
}

Request Cancellation

Stop button appears automatically during processing:

<AgentUI
	isProcessing={ isProcessing }
	onStop={ abortCurrentRequest }
	// Submit button becomes stop button when processing
/>

Content Types

Content items can have different types that determine how they're displayed:

  • type: 'text' - Normal text content (visible)
  • type: 'image_url' - Image content (visible)
  • type: 'component' - React component (visible)
  • type: 'context' - Context information sent as text to the agent but hidden from UI
// Example: Mixing visible and context content
const messages = [
	{
		id: '1',
		role: 'user',
		content: [ { type: 'text', text: 'Take me to the dashboard' } ],
		timestamp: Date.now(),
		archived: false,
		showIcon: true,
	},
	{
		id: '2',
		role: 'user',
		content: [
			{
				type: 'context', // Hidden from UI, sent to agent for context
				text: 'Navigation completed. Dashboard loaded successfully.',
			},
		],
		timestamp: Date.now(),
		archived: false,
		showIcon: true,
	},
	{
		id: '3',
		role: 'agent',
		content: [
			{ type: 'text', text: "I've taken you to the dashboard." },
		],
		timestamp: Date.now(),
		archived: false,
		showIcon: true,
	},
];

<AgentUI messages={ messages } />;
// Only messages 1 and 3 will be visible (message 2 has only context content)

// Example: Message with both visible and context content
const mixedMessage = {
	id: '4',
	role: 'user',
	content: [
		{ type: 'text', text: 'Here are your analytics' },
		{ type: 'context', text: 'page: /analytics, loaded: true' },
	],
	timestamp: Date.now(),
	archived: false,
	showIcon: true,
};
// The context content will be filtered out, only "Here are your analytics" is visible

Custom Message Renderer

import { ReactMarkdown } from 'react-markdown';

const customRenderer = ( { children }: { children: string } ) => (
	<ReactMarkdown remarkPlugins={ [ remarkGfm ] }>{ children }</ReactMarkdown>
);

<AgentUI messageRenderer={ customRenderer } />;

Chat State Control

For floating variant, control state externally:

const [ chatState, setChatState ] = useState< ChatState >( 'collapsed' );

<AgentUI
	variant="floating"
	floatingChatState={ chatState }
	onOpen={ () => setChatState( 'compact' ) }
	onExpand={ () => setChatState( 'expanded' ) }
	onClose={ () => setChatState( 'collapsed' ) }
/>;

Hooks

useChat

Manages floating chat state:

const {
	state, // 'collapsed' | 'compact' | 'expanded'
	setState,
	isOpen, // boolean
	open, // () => void
	close, // () => void
	toggle, // () => void
} = useChat( initialState );

useInput

Manages input state with auto-resize:

const { value, setValue, clear, textareaRef, handleKeyDown, adjustHeight } =
	useInput( {
		value: inputValue,
		setValue: setInputValue,
		onSubmit: handleSubmit,
		isProcessing: false,
	} );

Type Definitions

interface Message {
	id: string;
	role: 'user' | 'agent';
	content: Array< {
		type: 'text' | 'image_url' | 'component' | 'context';
		text?: string;
		image_url?: string;
		component?: React.ComponentType;
		componentProps?: any;
	} >;
	timestamp: number;
	archived: boolean;
	showIcon: boolean;
	icon?: string;
	actions?: MessageAction[];
	disabled?: boolean;
}

interface MessageAction {
	id: string;
	icon?: React.ReactNode;
	label: string;
	onClick: ( message: Message ) => void | Promise< void >;
	tooltip?: string;
	disabled?: boolean;
	pressed?: boolean;
	showLabel?: boolean;
}

interface Suggestion {
	id: string;
	label: string;
	prompt: string;
}

interface NoticeConfig {
	icon?: React.ReactNode | null | false;
	message: string;
	action?: {
		label: string;
		onClick: () => void;
	};
	dismissible?: boolean;
	onDismiss?: () => void;
}

type ChatState = 'collapsed' | 'compact' | 'expanded';

Styling

CSS Import

import '@automattic/agenttic-ui/index.css';

CSS Scoping

All styles are scoped to .agenttic class to prevent conflicts.

Customization

Override CSS custom properties:

.agenttic {
	--color-primary: #your-brand-color;
	--color-background: #ffffff;
	--color-foreground: #000000;
}

.agenttic [data-slot='chat-footer'] {
	--color-background: oklch( 1 0 0 );
	--color-primary: #your-brand-color;
}

Icons

Pre-built icon components:

import {
	ThumbsUpIcon,
	ThumbsDownIcon,
	CopyIcon,
	StopIcon,
	ArrowUpIcon,
	XIcon,
	BigSkyIcon,
	StylesIcon,
} from '@automattic/agenttic-ui';

Development

# Build the package
pnpm build

# Run in development mode
pnpm dev

# Run tests
pnpm test

# Type checking
pnpm type-check

# Start Storybook
pnpm storybook

Integration with agenttic-client

import { useAgentChat } from '@automattic/agenttic-client';
import { AgentUI } from '@automattic/agenttic-ui';

function App() {
	const agentProps = useAgentChat( {
		agentId: 'big-sky',
	} );

	return <AgentUI { ...agentProps } variant="floating" />;
}

The useAgentChat hook returns props that match the AgentUI interface.