Initial commit of Hearbit AI App

This commit is contained in:
michael.borak
2026-01-19 17:27:30 +01:00
commit a4e82acdfa
60 changed files with 12443 additions and 0 deletions

121
src/App.tsx Normal file
View File

@@ -0,0 +1,121 @@
import { useState } from "react";
import Settings from "./components/Settings";
import Recorder from "./components/Recorder";
interface PromptTemplate {
id: string;
name: string;
content: string;
}
function App() {
const [view, setView] = useState<'recorder' | 'settings'>('recorder');
const [apiKey, setApiKey] = useState(localStorage.getItem('infomaniak_api_key') || '');
const [productId, setProductId] = useState(localStorage.getItem('infomaniak_product_id') || '');
// Default prompts if none exist
const defaultPrompts: PromptTemplate[] = [
{ id: '1', name: 'General Summary', content: 'Summarize the following text into clear bullet points.' },
{ id: '2', name: 'Action Items', content: 'Extract all action items and tasks from this text.' },
{ id: '3', name: 'Email Draft', content: 'Draft a follow-up email based on this conversation.' }
];
const [prompts, setPrompts] = useState<PromptTemplate[]>(() => {
const saved = localStorage.getItem('infomaniak_prompts');
return saved ? JSON.parse(saved) : defaultPrompts;
});
const handleSaveSettings = (newApiKey: string, newProductId: string, newPrompts: PromptTemplate[]) => {
setApiKey(newApiKey);
setProductId(newProductId);
setPrompts(newPrompts);
localStorage.setItem('infomaniak_api_key', newApiKey);
localStorage.setItem('infomaniak_product_id', newProductId);
localStorage.setItem('infomaniak_prompts', JSON.stringify(newPrompts));
setView('recorder');
};
// State for Recorder (lifted to persist across view changes)
const [transcription, setTranscription] = useState('');
const [summary, setSummary] = useState('');
interface HistoryItem {
id: string;
date: string;
transcription: string;
summary: string;
}
const [history, setHistory] = useState<HistoryItem[]>(() => {
const saved = localStorage.getItem('infomaniak_history');
return saved ? JSON.parse(saved) : [];
});
const handleSaveToHistory = (t?: string, s?: string) => {
const transToSave = t !== undefined ? t : transcription;
const sumToSave = s !== undefined ? s : summary;
if (!transToSave && !sumToSave) return;
const newItem: HistoryItem = {
id: Date.now().toString(),
date: new Date().toLocaleString(),
transcription: transToSave,
summary: sumToSave
};
const newHistory = [newItem, ...history];
setHistory(newHistory);
localStorage.setItem('infomaniak_history', JSON.stringify(newHistory));
};
const handleDeleteHistory = (id: string) => {
const newHistory = history.filter(item => item.id !== id);
setHistory(newHistory);
localStorage.setItem('infomaniak_history', JSON.stringify(newHistory));
};
const handleLoadHistory = (item: HistoryItem) => {
setTranscription(item.transcription);
setSummary(item.summary);
};
return (
<div className="min-h-screen bg-background text-foreground flex flex-col select-none">
<div className="flex-1 flex flex-col justify-center h-full">
{view === 'recorder' ? (
<Recorder
apiKey={apiKey}
productId={productId}
prompts={prompts}
onOpenSettings={() => setView('settings')}
transcription={transcription}
setTranscription={setTranscription}
summary={summary}
setSummary={setSummary}
history={history}
onSaveToHistory={handleSaveToHistory}
onDeleteHistory={handleDeleteHistory}
onLoadHistory={handleLoadHistory}
/>
) : (
<Settings
onSave={handleSaveSettings}
onBack={() => setView('recorder')}
initialApiKey={apiKey}
initialProductId={productId}
initialPrompts={prompts}
/>
)}
</div>
{view === 'settings' && (
<div className="fixed top-4 right-4 z-50">
{/* Close button handled inside Settings typically */}
</div>
)}
</div>
);
}
export default App;