chore: cleanup dead code and unused files

Deleted 7 unused frontend components (ChatBubble, PetZone, Live2DCat,
BackgroundScene, Clock, Toolbar, types/index.ts).
Deleted unused backend: whisper_stream.py, empty models/ and routers/ dirs.
Removed dead code: Message interface, messages/micError/sendText/addMessage,
conversation_text handler, 3 unused memory.py methods.
Fixed task persistence bug: added 'tasks' to DEFAULT_PREFERENCES.
Stale comment in Live2DStage updated.
This commit is contained in:
2026-06-06 12:05:28 -04:00
parent 1eaef8b6ab
commit b06f20f9d8
17 changed files with 4 additions and 537 deletions
-1
View File
@@ -16,7 +16,6 @@ import { useConversation } from './hooks/useConversation';
export default function App() {
const [isLoggedIn, setIsLoggedIn] = useState(() => sessionStorage.getItem('kira-auth') === '1');
const {
messages,
isConnected,
isKiraSpeaking,
isRecording,
@@ -1,35 +0,0 @@
import { SCENES } from './scenes';
interface Props {
currentScene: string;
onSelect: (id: string) => void;
}
export default function BackgroundScene({ currentScene, onSelect }: Props) {
return (
<div className="p-4">
<h3 className="text-sm font-bold text-kira-plum mb-3 flex items-center gap-2">
<span>🎨</span> Scene
</h3>
<div className="flex flex-wrap gap-2">
{SCENES.map((s) => (
<button
key={s.id}
onClick={() => onSelect(s.id)}
className={`
flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-sm font-medium transition-all
${currentScene === s.id
? 'bg-kira-pink text-white shadow-md scale-105'
: 'bg-white/50 text-kira-plum/70 hover:bg-kira-glow hover:scale-102'
}
`}
title={s.name}
>
<span>{s.icon}</span>
<span className="hidden sm:inline">{s.name}</span>
</button>
))}
</div>
</div>
);
}
-85
View File
@@ -1,85 +0,0 @@
import { useRef, useEffect } from 'react';
interface Message {
id: string;
role: 'user' | 'kira';
text: string;
timestamp: number;
}
interface Props {
messages: Message[];
isKiraSpeaking: boolean;
userName?: string;
}
export default function ChatBubble({ messages, isKiraSpeaking }: Props) {
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="p-3 flex flex-col" style={{ maxHeight: 160 }}>
<h3 className="text-sm font-bold text-kira-plum mb-3 flex items-center gap-2">
<span>💬</span> Conversation
<span className={`w-2 h-2 rounded-full ${isKiraSpeaking ? 'bg-kira-pink animate-pulse' : 'bg-kira-mint'}`} />
</h3>
<div className="flex-1 overflow-y-auto space-y-2 scrollbar-thin pr-1">
{messages.length === 0 && (
<div className="text-xs text-kira-plum/30 text-center py-6">
click the mic or type to talk to Kira
</div>
)}
{messages.map((msg, i) => {
const isLastKira = msg.role === 'kira' && i === messages.length - 1 && isKiraSpeaking;
return (
<div
key={msg.id}
className={`flex gap-2 ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
{msg.role === 'kira' && (
<span className="text-sm mt-1">🌸</span>
)}
<div
className={`
max-w-[80%] px-3 py-2 rounded-2xl text-sm leading-relaxed
${msg.role === 'user'
? 'bg-kira-lav/30 text-kira-plum rounded-br-md'
: 'bg-white/60 text-kira-plum rounded-bl-md'
}
${isLastKira ? 'animate-fade-in' : ''}
`}
>
{msg.text}
{isLastKira && (
<span className="inline-block w-1.5 h-4 bg-kira-pink/60 ml-0.5 animate-blink" />
)}
</div>
{msg.role === 'user' && (
<span className="text-sm mt-1">👤</span>
)}
</div>
);
})}
<div ref={bottomRef} />
</div>
<style>{`
@keyframes fade-in {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.animate-fade-in { animation: fade-in 0.3s ease-out; }
.animate-blink { animation: blink 0.8s infinite; }
`}</style>
</div>
);
}
-21
View File
@@ -1,21 +0,0 @@
import { useEffect, useState } from 'react';
export default function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id);
}, []);
return (
<div className="px-5 py-3 text-center">
<div className="text-3xl font-bold tracking-tight text-kira-plum">
{time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</div>
<div className="text-xs text-kira-violet/60 font-medium">
{time.toLocaleDateString([], { weekday: 'long', month: 'long', day: 'numeric' })}
</div>
</div>
);
}
-76
View File
@@ -1,76 +0,0 @@
import { useEffect, useRef } from 'react';
interface Props {
className?: string;
}
export default function Live2DCat({ className }: Props) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let mounted = true;
const canvas = canvasRef.current;
if (!canvas) return;
let app: any = null;
const init = async () => {
try {
const pixi = await import('pixi.js');
const { Live2DModel } = await import('pixi-live2d-display/cubism4');
(Live2DModel as any).registerTicker(pixi.Ticker as any);
const w = canvas.clientWidth || 120;
const h = canvas.clientHeight || 120;
// Use explicit CanvasRenderer to avoid WebGL context conflict with KiraAvatar
app = new pixi.Application({
view: canvas,
width: w,
height: h,
antialias: true,
resolution: Math.min(window.devicePixelRatio || 1, 2),
backgroundAlpha: 0,
autoDensity: true,
// Use WebGL1 context (more compatible with separate contexts)
preferWebGLVersion: 1,
} as any);
if (!mounted) { app.destroy(true); return; }
const model = await Live2DModel.from('/live2d/models/little-cat/LittleCat.model3.json', {
autoInteract: false,
});
if (!mounted) { app.destroy(true); return; }
const sw = app.screen.width;
const sh = app.screen.height;
const s = Math.min((sw * 0.85) / model.width, (sh * 0.85) / model.height);
model.scale.set(s);
model.anchor.set(0.5, 0.5);
model.position.set(sw / 2, sh / 2);
app.stage.addChild(model as any);
(model as any).isInteractive = () => false;
try { model.motion('Idle'); } catch { /* */ }
} catch (e) {
console.warn('[Live2DCat]', e);
}
};
init();
return () => {
mounted = false;
if (app) { app.destroy(true, { children: true }); }
};
}, []);
return (
<canvas
ref={canvasRef}
className={className}
style={{ display: 'block' }}
/>
);
}
+1 -1
View File
@@ -11,7 +11,7 @@ interface Props {
/**
* Single full-viewport Live2D stage. One WebGL context, two models:
* - Kira (center panel)
* - Mochi the cat (PetZone, bottom of right sidebar)
// Mochi the cat (next to Kira in center panel)
*
* Canvas sits above UI panels (z-50, pointer-events: none) so Live2D
* models render on top of the frosted-glass sidebars.
-12
View File
@@ -1,12 +0,0 @@
export default function PetZone() {
return (
<div data-petzone className="p-4 relative overflow-hidden" style={{ minHeight: 140 }}>
<h3 className="text-sm font-bold text-kira-plum mb-2 flex items-center gap-2">
<span>🐱</span> Pet Zone
</h3>
<div className="flex flex-col items-center pt-2">
<span className="text-[10px] text-kira-plum/60 font-medium">Mochi</span>
</div>
</div>
);
}
-34
View File
@@ -1,34 +0,0 @@
import { SCENES } from './scenes';
interface Props {
currentScene: string;
onSceneChange: (id: string) => void;
}
export default function Toolbar({ currentScene, onSceneChange }: Props) {
return (
<div className="px-4 py-3 flex items-center justify-between gap-2">
<div className="flex items-center gap-3">
<span className="text-lg font-bold text-kira-plum">Kira</span>
<span className="text-kira-plum/20">|</span>
<div className="flex gap-1.5">
{SCENES.filter((_, i) => i < 4).map((s) => (
<button
key={s.id}
onClick={() => onSceneChange(s.id)}
className={`text-sm ${currentScene === s.id ? 'opacity-100' : 'opacity-40 hover:opacity-70'} transition-opacity`}
title={s.name}
>
{s.icon}
</button>
))}
</div>
</div>
<div className="flex items-center gap-3 text-sm text-kira-plum/50">
<span>🐱 2</span>
<span className="hidden sm:inline">focus bestie</span>
</div>
</div>
);
}
+1 -22
View File
@@ -13,12 +13,6 @@ export interface Task {
completed: boolean;
}
interface Message {
id: string;
role: 'user' | 'kira';
text: string;
timestamp: number;
}
const WS_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/api/ws`;
const USER_ID_KEY = 'kira-user-id';
@@ -56,7 +50,6 @@ function float32ToPcm16Base64(float32: Float32Array, srcRate: number): string {
}
export function useConversation() {
const [messages, setMessages] = useState<Message[]>([]);
const [isConnected, setIsConnected] = useState(false);
const [isKiraSpeaking, setIsKiraSpeaking] = useState(false);
const [isRecording, setIsRecording] = useState(false);
@@ -68,7 +61,6 @@ export function useConversation() {
accessory: '',
});
const [loadingPrefs, setLoadingPrefs] = useState(true);
const [micError, setMicError] = useState<string | null>(null);
const [tasks, setTasks] = useState<Task[]>([]);
const [celebrateTrigger, setCelebrateTrigger] = useState(0);
@@ -130,7 +122,6 @@ export function useConversation() {
break;
case 'transcript':
addMessage(msg.role === 'user' ? 'user' : 'kira', msg.text);
break;
case 'audio': {
@@ -220,12 +211,6 @@ export function useConversation() {
return playbackCtxRef.current;
}
const addMessage = useCallback((role: 'user' | 'kira', text: string) => {
setMessages((prev) => [
...prev,
{ id: crypto.randomUUID(), role, text, timestamp: Date.now() },
]);
}, []);
// ── Identity ──
const identify = useCallback((name: string) => {
@@ -248,12 +233,10 @@ export function useConversation() {
// ── Audio capture via ScriptProcessorNode (PCM16 16kHz stream) ──
const startRecording = useCallback(async () => {
if (!navigator.mediaDevices?.getUserMedia) {
addMessage('kira', 'Mic requires HTTPS. Try accessing via HTTPS!');
return;
}
try {
setMicError(null);
const stream = await navigator.mediaDevices.getUserMedia({
audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 48000 },
});
@@ -261,7 +244,6 @@ export function useConversation() {
const ws = wsRef.current;
if (!ws || ws.readyState !== WebSocket.OPEN) {
addMessage('kira', 'Not connected to server yet...');
stream.getTracks().forEach((t) => t.stop());
return;
}
@@ -295,10 +277,9 @@ export function useConversation() {
setIsRecording(true);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
setMicError(msg);
console.error('[Kira Mic]', msg);
}
}, [addMessage]);
}, []);
const stopRecording = useCallback(() => {
processorRef.current?.disconnect();
@@ -328,14 +309,12 @@ export function useConversation() {
}, [connect, stopRecording]);
return {
messages,
isConnected,
isKiraSpeaking,
isRecording,
identified,
preferences,
loadingPrefs,
micError,
identify,
setPreference,
sendText,
-9
View File
@@ -7,7 +7,6 @@
--color-kira-bg: #FFF5F5;
--color-kira-plum: #4A1942;
--color-kira-violet: #7C3AED;
--color-kira-card: #FFFFFF;
--color-kira-glow: #FDF2F8;
--font-nunito: 'Nunito', sans-serif;
}
@@ -27,14 +26,6 @@ body {
width: 100vw;
}
.glass-card {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 182, 193, 0.3);
border-radius: 16px;
box-shadow: 0 4px 24px rgba(74, 25, 66, 0.08);
}
.btn-kira {
background: linear-gradient(135deg, #FFB6C1, #D8B4FE);
color: white;
-13
View File
@@ -1,13 +0,0 @@
import { SCENES, type Scene } from '../components/scenes';
export interface KiraState {
currentScene: Scene;
isListening: boolean;
isSpeaking: boolean;
currentOutfit: string;
currentAccessory: string | null;
sessionNotes: string[];
}
export type { Scene };
export { SCENES };
+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/LoginScreen.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"}
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AnimatedAvatar.tsx","./src/components/KiraAvatar.tsx","./src/components/Live2DStage.tsx","./src/components/LoginScreen.tsx","./src/components/MusicPlayer.tsx","./src/components/Notes.tsx","./src/components/Particles.tsx","./src/components/TaskList.tsx","./src/components/Timer.tsx","./src/components/Wardrobe.tsx","./src/components/WelcomeScreen.tsx","./src/components/WhiteNoise.tsx","./src/components/scenes.ts","./src/hooks/useConversation.ts"],"version":"6.0.3"}