feat(tasks): Gemini tool calling for task list management

5 tools: add_task, remove_task, complete_task, get_tasks, clear_completed_tasks
Backend stores tasks in-memory per session. Frontend TaskList component syncs via WS.
Kira can manage tasks via voice or text conversation.
This commit is contained in:
2026-06-06 00:01:52 -04:00
parent 3dd3032ffa
commit cbeec65637
5 changed files with 227 additions and 29 deletions
+3 -1
View File
@@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import MusicPlayer from './components/MusicPlayer';
import TaskList from './components/TaskList';
import Timer from './components/Timer';
import Notes from './components/Notes';
import WhiteNoise from './components/WhiteNoise';
@@ -27,7 +28,7 @@ export default function App() {
sendText,
startRecording,
stopRecording,
tasks,
} = useConversation();
const [currentSceneId, setCurrentSceneId] = useState('cozy-room');
@@ -143,6 +144,7 @@ export default function App() {
</div>
<div className="shrink-0">
<Notes />
<TaskList tasks={tasks} />
</div>
<div className="shrink-0">
<ChatBubble messages={messages} isKiraSpeaking={isKiraSpeaking} userName={userName} />
+53
View File
@@ -0,0 +1,53 @@
import { Task } from '../hooks/useConversation';
interface Props {
tasks: Task[];
}
export default function TaskList({ tasks }: Props) {
const pending = tasks.filter((t) => !t.completed);
const done = tasks.filter((t) => t.completed);
return (
<div className="p-3">
<h3 className="text-sm font-bold text-kira-plum mb-2 flex items-center gap-2">
<span></span> Tasks
{pending.length > 0 && (
<span className="text-xs font-normal bg-kira-pink/30 text-kira-plum px-1.5 py-0.5 rounded-full">
{pending.length}
</span>
)}
</h3>
{tasks.length === 0 && (
<div className="text-xs text-kira-plum/30 text-center py-4">
tell Kira what you need to do!
</div>
)}
{pending.length > 0 && (
<ul className="space-y-1.5 mb-2">
{pending.map((t) => (
<li key={t.id} className="flex items-start gap-2 text-sm text-kira-plum">
<span className="mt-0.5 shrink-0 w-4 h-4 rounded-full border-2 border-kira-pink/40" />
<span className="leading-snug">{t.text}</span>
</li>
))}
</ul>
)}
{done.length > 0 && (
<ul className="space-y-1">
{done.map((t) => (
<li key={t.id} className="flex items-start gap-2 text-sm text-kira-plum/40 line-through">
<span className="mt-0.5 shrink-0 w-4 h-4 rounded-full bg-kira-mint flex items-center justify-center">
<span className="text-[8px] text-white"></span>
</span>
<span className="leading-snug">{t.text}</span>
</li>
))}
</ul>
)}
</div>
);
}
+12
View File
@@ -7,6 +7,12 @@ export interface UserPreferences {
accessory: string;
}
export interface Task {
id: string;
text: string;
completed: boolean;
}
interface Message {
id: string;
role: 'user' | 'kira';
@@ -63,6 +69,7 @@ export function useConversation() {
});
const [loadingPrefs, setLoadingPrefs] = useState(true);
const [micError, setMicError] = useState<string | null>(null);
const [tasks, setTasks] = useState<Task[]>([]);
const wsRef = useRef<WebSocket | null>(null);
const streamRef = useRef<MediaStream | null>(null);
@@ -159,6 +166,10 @@ export function useConversation() {
case 'error':
console.error('[Kira]', msg.message);
break;
case 'tasks':
if (msg.tasks) setTasks(msg.tasks);
break;
}
}, []);
@@ -319,5 +330,6 @@ export function useConversation() {
sendText,
startRecording,
stopRecording,
tasks,
};
}
+1 -1
View File
@@ -1 +1 @@
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AnimatedAvatar.tsx","./src/components/BackgroundScene.tsx","./src/components/ChatBubble.tsx","./src/components/Clock.tsx","./src/components/KiraAvatar.tsx","./src/components/Live2DCat.tsx","./src/components/Live2DStage.tsx","./src/components/MusicPlayer.tsx","./src/components/Notes.tsx","./src/components/Particles.tsx","./src/components/PetZone.tsx","./src/components/Timer.tsx","./src/components/Toolbar.tsx","./src/components/Wardrobe.tsx","./src/components/WelcomeScreen.tsx","./src/components/WhiteNoise.tsx","./src/components/scenes.ts","./src/hooks/useConversation.ts","./src/types/index.ts"],"version":"6.0.3"}
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AnimatedAvatar.tsx","./src/components/BackgroundScene.tsx","./src/components/ChatBubble.tsx","./src/components/Clock.tsx","./src/components/KiraAvatar.tsx","./src/components/Live2DCat.tsx","./src/components/Live2DStage.tsx","./src/components/MusicPlayer.tsx","./src/components/Notes.tsx","./src/components/Particles.tsx","./src/components/PetZone.tsx","./src/components/TaskList.tsx","./src/components/Timer.tsx","./src/components/Toolbar.tsx","./src/components/Wardrobe.tsx","./src/components/WelcomeScreen.tsx","./src/components/WhiteNoise.tsx","./src/components/scenes.ts","./src/hooks/useConversation.ts","./src/types/index.ts"],"version":"6.0.3"}