Enable @ mentions for contextual references in chat
import { useState } from 'react';
import { useRegisterState, useStateBasedMentionProvider } from 'cedar-os';
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Buy groceries', category: 'shopping' },
{ id: 2, text: 'Call dentist', category: 'health' },
]);
// Register the state
useRegisterState({
key: 'todos',
value: todos,
setValue: setTodos,
description: 'Todo items',
});
// Enable mentions for todos
useStateBasedMentionProvider({
stateKey: 'todos',
trigger: '@',
labelField: 'text',
searchFields: ['text', 'category'],
description: 'Todo items',
icon: 'π',
color: '#3b82f6',
});
return <ChatInput />;
}
import { useMentionProvider } from 'cedar-os';
function UserMentions() {
const users = [
{
id: 1,
name: 'Alice Johnson',
email: 'alice@company.com',
role: 'Designer',
},
{ id: 2, name: 'Bob Smith', email: 'bob@company.com', role: 'Developer' },
];
useMentionProvider({
id: 'users',
trigger: '@',
label: 'Users',
description: 'Team members',
icon: 'π€',
getItems: (query) => {
const filtered = query
? users.filter(
(user) =>
user.name.toLowerCase().includes(query.toLowerCase()) ||
user.email.toLowerCase().includes(query.toLowerCase()) ||
user.role.toLowerCase().includes(query.toLowerCase())
)
: users;
return filtered.map((user) => ({
id: user.id.toString(),
label: `${user.name} (${user.role})`,
data: user,
metadata: {
icon: 'π€',
color: '#10b981',
},
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: {
label: item.label,
icon: 'π€',
color: '#10b981',
},
}),
});
return <ChatInput />;
}
import { useMentionProvider } from 'cedar-os';
function CustomMentions() {
// Static mention provider for commands
useMentionProvider({
id: 'commands',
trigger: '/',
label: 'Commands',
description: 'Available commands',
icon: 'β‘',
color: '#f59e0b',
getItems: (query) => {
const commands = [
{ id: 'help', name: 'help', description: 'Show available commands' },
{ id: 'reset', name: 'reset', description: 'Reset the conversation' },
{ id: 'export', name: 'export', description: 'Export chat history' },
];
const filtered = query
? commands.filter(
(cmd) =>
cmd.name.toLowerCase().includes(query.toLowerCase()) ||
cmd.description.toLowerCase().includes(query.toLowerCase())
)
: commands;
return filtered.map((cmd) => ({
id: cmd.id,
label: cmd.name,
data: cmd,
metadata: {
icon: 'β‘',
color: '#f59e0b',
},
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: {
label: item.label,
icon: 'β‘',
color: '#f59e0b',
},
}),
});
// Dynamic mention provider for files
useMentionProvider({
id: 'files',
trigger: '#',
label: 'Files',
description: 'Project files',
icon: 'π',
color: '#8b5cf6',
getItems: async (query) => {
// Simulate async file search
const files = await searchFiles(query);
return files.map((file) => ({
id: file.id,
label: file.name,
data: file,
metadata: {
icon: 'π',
color: '#8b5cf6',
},
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: {
label: item.label,
icon: 'π',
color: '#8b5cf6',
},
}),
});
return <ChatInput />;
}
// Mock function for demonstration
async function searchFiles(query: string) {
// Simulate API call
return [
{ id: '1', name: 'README.md', path: '/README.md', type: 'markdown' },
{ id: '2', name: 'package.json', path: '/package.json', type: 'json' },
].filter(
(file) => !query || file.name.toLowerCase().includes(query.toLowerCase())
);
}
import { useMentionProvider } from 'cedar-os';
function CustomRendering() {
useMentionProvider({
id: 'users',
trigger: '@',
label: 'Users',
icon: 'π€',
getItems: (query) => {
// ... get items logic
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'π€' },
}),
// Custom rendering in the mention menu
renderMenuItem: (item) => (
<div className='flex items-center gap-2 p-2'>
<span className='text-lg'>π€</span>
<div>
<div className='font-medium'>{item.label}</div>
<div className='text-sm text-gray-500'>{item.data.role}</div>
</div>
</div>
),
// Custom rendering in the editor
renderEditorItem: (item, attrs) => (
<span className='bg-blue-100 text-blue-800 px-1 rounded'>
π€ {item.label}
</span>
),
// Custom rendering in context badges
renderContextBadge: (entry) => (
<div className='bg-gray-100 px-2 py-1 rounded text-sm'>
π€ {entry.metadata?.label}
</div>
),
});
return <ChatInput />;
}
import { useMentionProvider } from 'cedar-os';
function MultiMentionChat() {
const users = [
{ id: '1', name: 'Alice', role: 'Designer' },
{ id: '2', name: 'Bob', role: 'Developer' },
];
const channels = [
{ id: '1', name: 'general', topic: 'General discussion' },
{ id: '2', name: 'dev', topic: 'Development updates' },
];
const commands = [
{ id: '1', name: 'help', description: 'Show help' },
{ id: '2', name: 'reset', description: 'Reset chat' },
];
// @ for users
useMentionProvider({
id: 'users',
trigger: '@',
label: 'Users',
icon: 'π€',
getItems: (query) => {
const filtered = query
? users.filter((u) =>
u.name.toLowerCase().includes(query.toLowerCase())
)
: users;
return filtered.map((user) => ({
id: user.id,
label: user.name,
data: user,
metadata: { icon: 'π€' },
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'π€' },
}),
});
// # for channels/topics
useMentionProvider({
id: 'channels',
trigger: '#',
label: 'Channels',
icon: 'π’',
getItems: (query) => {
const filtered = query
? channels.filter((c) =>
c.name.toLowerCase().includes(query.toLowerCase())
)
: channels;
return filtered.map((channel) => ({
id: channel.id,
label: channel.name,
data: channel,
metadata: { icon: 'π’' },
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'π’' },
}),
});
// / for commands
useMentionProvider({
id: 'commands',
trigger: '/',
label: 'Commands',
icon: 'β‘',
getItems: (query) => {
const filtered = query
? commands.filter((c) =>
c.name.toLowerCase().includes(query.toLowerCase())
)
: commands;
return filtered.map((command) => ({
id: command.id,
label: command.name,
data: command,
metadata: { icon: 'β‘' },
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'β‘' },
}),
});
return <ChatInput />;
}
import { useEffect } from 'react';
import { useMentionProvider } from 'cedar-os';
function ContextualMentions() {
const currentPage = useCurrentPage();
// Conditional mention providers
useEffect(() => {
if (currentPage === 'projects') {
const projects = getProjects();
useMentionProvider({
id: 'projects',
trigger: '@',
label: 'Projects',
icon: 'π',
getItems: (query) => {
const filtered = query
? projects.filter((p) =>
p.name.toLowerCase().includes(query.toLowerCase())
)
: projects;
return filtered.map((project) => ({
id: project.id,
label: project.name,
data: project,
metadata: { icon: 'π' },
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'π' },
}),
});
} else if (currentPage === 'tasks') {
const tasks = getTasks();
useMentionProvider({
id: 'tasks',
trigger: '@',
label: 'Tasks',
icon: 'β
',
getItems: (query) => {
const filtered = query
? tasks.filter((t) =>
t.title.toLowerCase().includes(query.toLowerCase())
)
: tasks;
return filtered.map((task) => ({
id: task.id,
label: task.title,
data: task,
metadata: { icon: 'β
' },
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: { label: item.label, icon: 'β
' },
}),
});
}
}, [currentPage]);
return <ChatInput />;
}
// Mock functions for demonstration
function useCurrentPage() {
return 'projects'; // or 'tasks'
}
function getProjects() {
return [
{ id: '1', name: 'Website Redesign' },
{ id: '2', name: 'Mobile App' },
];
}
function getTasks() {
return [
{ id: '1', title: 'Fix login bug' },
{ id: '2', title: 'Add dark mode' },
];
}
// When user types: "Update the status of @task-123 to completed"
// The agent receives:
{
message: "Update the status of @task-123 to completed",
mentions: [
{
id: "task-123",
type: "tasks",
data: {
id: "task-123",
title: "Implement user authentication",
status: "in-progress",
assignee: "Alice"
},
position: { start: 23, end: 32 }
}
]
}
import { useMentionProvider } from 'cedar-os';
function ValidatedMentions() {
const currentUser = getCurrentUser();
useMentionProvider({
id: 'team-users',
trigger: '@',
label: 'Team Members',
icon: 'π€',
getItems: (query) => {
const allUsers = getAllUsers();
// Filter by permissions - only show users in same team
const allowedUsers = allUsers.filter(
(user) => user.teamId === currentUser.teamId && user.active
);
// Filter by search query
const filtered = query
? allowedUsers.filter((user) =>
user.name.toLowerCase().includes(query.toLowerCase())
)
: allowedUsers;
return filtered.map((user) => ({
id: user.id,
label: user.name,
data: user,
metadata: {
icon: 'π€',
color: user.active ? '#10b981' : '#6b7280',
},
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: {
label: item.label,
icon: 'π€',
// Validate data is still current
valid: item.data.active,
},
}),
});
return <ChatInput />;
}
// Mock functions
function getCurrentUser() {
return { id: '1', teamId: 'team-a' };
}
function getAllUsers() {
return [
{ id: '1', name: 'Alice', teamId: 'team-a', active: true },
{ id: '2', name: 'Bob', teamId: 'team-a', active: false },
{ id: '3', name: 'Charlie', teamId: 'team-b', active: true },
];
}
import { useMentionProvider } from 'cedar-os';
function RichContextMentions() {
useMentionProvider({
id: 'project-tasks',
trigger: '#',
label: 'Tasks',
icon: 'β
',
getItems: async (query) => {
const tasks = await fetchTasks(query);
return tasks.map((task) => ({
id: task.id,
label: `${task.title} (${task.status})`,
data: {
...task,
// Include rich context data
assignee: task.assignee,
dueDate: task.dueDate,
priority: task.priority,
comments: task.comments,
},
metadata: {
icon: getTaskIcon(task.status),
color: getTaskColor(task.priority),
},
}));
},
toContextEntry: (item) => ({
id: item.id,
source: 'mention',
data: item.data,
metadata: {
label: item.label,
icon: item.metadata?.icon,
color: item.metadata?.color,
// Additional metadata for agent context
type: 'task',
status: item.data.status,
priority: item.data.priority,
},
}),
// Rich preview in mention menu
renderMenuItem: (item) => (
<div
className='p-2 border-l-2'
style={{ borderColor: item.metadata?.color }}>
<div className='flex items-center justify-between'>
<span className='font-medium'>{item.data.title}</span>
<span className='text-xs bg-gray-100 px-2 py-1 rounded'>
{item.data.status}
</span>
</div>
<div className='text-sm text-gray-500 mt-1'>
Due: {item.data.dueDate} β’ Assigned to: {item.data.assignee?.name}
</div>
</div>
),
});
return <ChatInput />;
}
// Helper functions
function getTaskIcon(status: string) {
const icons = {
todo: 'β³',
'in-progress': 'π',
done: 'β
',
blocked: 'π«',
};
return icons[status] || 'π';
}
function getTaskColor(priority: string) {
const colors = {
high: '#ef4444',
medium: '#f59e0b',
low: '#10b981',
};
return colors[priority] || '#6b7280';
}
async function fetchTasks(query: string) {
// Mock async task fetching
return [
{
id: '1',
title: 'Fix login bug',
status: 'in-progress',
priority: 'high',
dueDate: '2024-01-15',
assignee: { name: 'Alice' },
},
];
}