The library is built on Atomic Design principles, which ensure:
- Clear component hierarchy
- Element reusability
- Easy maintenance
- Flexible customization
Minimal indivisible UI elements without business logic.
Characteristics:
- Do not contain business logic
- Stateless
- Unaware of usage context
- Accept data only through props
Examples: Loader, Button, Icon
Simple groups of atoms forming basic UI blocks.
Characteristics:
- Combine multiple atoms
- Minimal internal logic
- Reusable blocks
Examples: ButtonGroup, Tabs, Suggestions
Complex self-sufficient components with internal logic.
Characteristics:
- Contain business logic
- Have internal state
- Provide ready component + hook
- Can be used independently
Examples: Header, MessageList, PromptBox
Complete layouts combining organisms.
Characteristics:
- Define page structure
- Coordinate organism interactions
- Work with abstract data
Examples: History, EmptyContainer, ChatContent
Full integrations with specific data.
Characteristics:
- Highest level of abstraction
- Manage data and state
- Connect all components together
Examples: ChatContainer
Code that runs exclusively on the server side and is responsible for interacting with external APIs, primarily neural network services.
Characteristics:
- Runs only on the server
- Contains no UI and is not involved in component rendering
- Serves as a middleware layer between the application and external services (such as neural network APIs)
Examples: OpenAIService
Each organism provides two ways of usage:
Fully assembled component with UI and logic:
import { PromptBox } from 'aikit';
<PromptBox
onSend={handleSend}
placeholder="Enter message..."
/>Hook for creating custom view:
import { usePromptBox } from 'aikit';
function CustomPromptBox() {
const {
value,
setValue,
handleSubmit,
canSubmit
} = usePromptBox({
onSend: handleSend
});
return (
<div className="custom-prompt">
{/* Your custom implementation */}
</div>
);
}- Library doesn't manage data directly
- All data is passed through props
- State is managed on the client side
- Library provides callback interfaces
// Client code manages data
const [messages, setMessages] = useState<MessageType[]>([]);
const [isLoading, setIsLoading] = useState(false);
// Library receives data through props
<ChatContainer
messages={messages}
isLoading={isLoading}
onSendMessage={async (content) => {
// Client manages requests
setIsLoading(true);
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ content })
});
const data = await response.json();
setMessages(prev => [...prev, data]);
setIsLoading(false);
}}
/>export type BaseMessage<TData = any> = {
id: string; // Unique identifier
type: string; // Message type
author: string; // Author
timestamp: string; // Timestamp
state?: MessageState; // State
data: TData; // Display data
metadata?: Record<string, any>; // Metadata
};- Common metadata at the top level
- Specific data in
datafield - Type safety through generics
- Easy validation and serialization
const customTypes: MessageTypeRegistry = {
chart: {
component: ChartMessageView,
validator: (msg) => msg.type === 'chart',
metadata: {
name: 'Chart Message',
description: 'Interactive chart',
}
}
};
<ChatContainer
messages={messages}
messageTypeRegistry={customTypes}
/>All styles are controlled through CSS variables:
.g-root {
--g-aikit-color-bg-primary: #ffffff;
/* ... */
}
[data-theme='dark'] {
--g-aikit-bg-primary: #1a1a1a;
/* ... */
}<ChatContainer
theme="dark" // 'light' | 'dark' | 'auto'
// ...
/>React.memofor all components- Virtualization for large lists
- Lazy loading for custom types
- Memoization for heavy computations
// Automatic code splitting for custom types
const ChartMessage = lazy(() => import('./ChartMessage'));All interactive elements have:
aria-labelfor screen readersrolefor semanticsaria-livefor dynamic changes
Enter— send messageEscape— cancelTab— navigation↑/↓— prompt history
- Tests for each atom and molecule
- Mock data for isolation
- Component interaction tests
- User scenario verification