Release 1.1: Pre-meeting models, Calendar improvements, Logs, Compact Recorder
This commit is contained in:
140
src/App.tsx
140
src/App.tsx
@@ -1,9 +1,8 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { useState } from 'react';
|
||||
import { Settings as SettingsIcon } from "lucide-react";
|
||||
import Settings from "./components/Settings";
|
||||
import Settings, { SmtpConfig, AzureConfig } from "./components/Settings";
|
||||
import Recorder from "./components/Recorder";
|
||||
import LogViewer, { LogEntry } from "./components/LogViewer";
|
||||
|
||||
import TranscriptionView from "./components/TranscriptionView";
|
||||
import Tabs from "./components/Tabs";
|
||||
import MeetingsView from "./components/MeetingsView";
|
||||
@@ -17,9 +16,16 @@ export interface PromptTemplate {
|
||||
keywords?: string[];
|
||||
}
|
||||
|
||||
export interface EmailTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [view, setView] = useState<'recorder' | 'logs' | 'settings' | 'transcription' | 'meetings' | 'history'>('recorder');
|
||||
const [lastTab, setLastTab] = useState<'recorder' | 'logs' | 'transcription' | 'meetings' | 'history'>('recorder');
|
||||
const [view, setView] = useState<'recorder' | 'settings' | 'transcription' | 'meetings' | 'history'>('recorder');
|
||||
const [lastTab, setLastTab] = useState<'recorder' | 'transcription' | 'meetings' | 'history'>('recorder');
|
||||
|
||||
|
||||
// Auto-start recording state to handle "Join & Record" transition
|
||||
@@ -40,6 +46,23 @@ function App() {
|
||||
const [apiKey, setApiKey] = useState(localStorage.getItem('infomaniak_api_key') || '');
|
||||
const [productId, setProductId] = useState(localStorage.getItem('infomaniak_product_id') || '');
|
||||
const [savePath, setSavePath] = useState(localStorage.getItem('infomaniak_save_path') || '');
|
||||
const [smtpConfig, setSmtpConfig] = useState<SmtpConfig>(() => {
|
||||
const saved = localStorage.getItem('hearbit_smtp_config');
|
||||
return saved ? JSON.parse(saved) : { host: '', port: '587', user: '', pass: '', sender: '', senderName: '' };
|
||||
});
|
||||
const [azureConfig, setAzureConfig] = useState<AzureConfig>(() => {
|
||||
const saved = localStorage.getItem('hearbit_azure_config');
|
||||
return saved ? JSON.parse(saved) : { clientId: '', tenantId: '' };
|
||||
});
|
||||
|
||||
const [selectedModel, setSelectedModel] = useState<string>(() => {
|
||||
return localStorage.getItem('hearbit_selected_model') || 'mixtral';
|
||||
});
|
||||
|
||||
const handleModelChange = (model: string) => {
|
||||
setSelectedModel(model);
|
||||
localStorage.setItem('hearbit_selected_model', model);
|
||||
};
|
||||
|
||||
// Default prompts if none exist
|
||||
/* eslint-disable no-useless-escape */ // Escape quotes in prompts
|
||||
@@ -164,20 +187,70 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
}
|
||||
];
|
||||
|
||||
// Default Email Templates
|
||||
const defaultEmailTemplates: EmailTemplate[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Meeting Summary (Standard)',
|
||||
subject: 'Meeting Summary: {{subject}}',
|
||||
body: `Hi everyone,
|
||||
|
||||
Here is the summary of our meeting "{{subject}}" from {{date}}.
|
||||
|
||||
{{summary}}
|
||||
|
||||
Best regards,
|
||||
Hearbit Assistant`
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Action Items Only',
|
||||
subject: 'Action Items: {{subject}}',
|
||||
body: `Hi Team,
|
||||
|
||||
Please find below the action items from our call on {{date}}:
|
||||
|
||||
{{summary}}
|
||||
|
||||
Thanks!`
|
||||
}
|
||||
];
|
||||
|
||||
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[], newSavePath: string) => {
|
||||
const [emailTemplates, setEmailTemplates] = useState<EmailTemplate[]>(() => {
|
||||
const saved = localStorage.getItem('hearbit_email_templates');
|
||||
return saved ? JSON.parse(saved) : defaultEmailTemplates;
|
||||
});
|
||||
|
||||
const handleSaveSettings = (
|
||||
newApiKey: string,
|
||||
newProductId: string,
|
||||
newPrompts: PromptTemplate[],
|
||||
newSavePath: string,
|
||||
newSmtp: SmtpConfig,
|
||||
newAzure: AzureConfig,
|
||||
newEmailTemplates: EmailTemplate[]
|
||||
) => {
|
||||
setApiKey(newApiKey);
|
||||
setProductId(newProductId);
|
||||
setPrompts(newPrompts);
|
||||
setSavePath(newSavePath);
|
||||
setSmtpConfig(newSmtp);
|
||||
setAzureConfig(newAzure);
|
||||
setEmailTemplates(newEmailTemplates);
|
||||
|
||||
localStorage.setItem('infomaniak_api_key', newApiKey);
|
||||
localStorage.setItem('infomaniak_product_id', newProductId);
|
||||
localStorage.setItem('infomaniak_prompts', JSON.stringify(newPrompts));
|
||||
localStorage.setItem('infomaniak_save_path', newSavePath);
|
||||
localStorage.setItem('hearbit_smtp_config', JSON.stringify(newSmtp));
|
||||
localStorage.setItem('hearbit_azure_config', JSON.stringify(newAzure));
|
||||
localStorage.setItem('hearbit_email_templates', JSON.stringify(newEmailTemplates));
|
||||
|
||||
setView(lastTab);
|
||||
};
|
||||
|
||||
@@ -250,18 +323,7 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
setView('transcription'); // Switch to Transcription view to see content
|
||||
};
|
||||
|
||||
// Logs State
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = listen<LogEntry>('log-event', (event) => {
|
||||
setLogs((prevLogs) => [...prevLogs, event.payload]);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unlisten.then(f => f());
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground flex flex-col select-none overflow-hidden">
|
||||
@@ -271,7 +333,7 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
<div className="absolute right-4 top-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
setLastTab(view === 'logs' || view === 'history' ? view : 'recorder');
|
||||
setLastTab(view === 'history' ? view : 'recorder');
|
||||
setView('settings');
|
||||
}}
|
||||
className="p-2 text-muted-foreground hover:text-foreground hover:bg-secondary rounded-full transition-colors"
|
||||
@@ -281,7 +343,7 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
</div>
|
||||
|
||||
<Tabs
|
||||
currentTab={view as 'recorder' | 'logs' | 'transcription' | 'meetings' | 'history'}
|
||||
currentTab={view as 'recorder' | 'transcription' | 'meetings' | 'history'}
|
||||
onTabChange={(t) => setView(t)}
|
||||
/>
|
||||
</div>
|
||||
@@ -313,11 +375,34 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
recordingSubject={recordingSubject}
|
||||
onAutoStartHandled={() => setAutoStartRecording(false)}
|
||||
addToast={addToast}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={handleModelChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{view === 'transcription' && (
|
||||
<TranscriptionView transcription={transcription} summary={summary} />
|
||||
<TranscriptionView
|
||||
transcription={transcription}
|
||||
summary={summary}
|
||||
smtpConfig={smtpConfig}
|
||||
apiKey={apiKey}
|
||||
productId={productId}
|
||||
prompts={prompts}
|
||||
emailTemplates={emailTemplates}
|
||||
onUpdateSummary={(newSummary) => {
|
||||
setSummary(newSummary); // Update view
|
||||
// Also update history item if it exists
|
||||
// We identify by transcription content match (simple heuristic) or we should track currentId
|
||||
const histIdx = history.findIndex(h => h.transcription === transcription);
|
||||
if (histIdx >= 0) {
|
||||
const newHist = [...history];
|
||||
newHist[histIdx] = { ...newHist[histIdx], summary: newSummary };
|
||||
setHistory(newHist);
|
||||
localStorage.setItem('infomaniak_history', JSON.stringify(newHist));
|
||||
}
|
||||
}}
|
||||
addToast={addToast}
|
||||
/>
|
||||
)}
|
||||
|
||||
{view === 'history' && (
|
||||
@@ -329,18 +414,22 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
)}
|
||||
|
||||
{view === 'meetings' && (
|
||||
|
||||
<MeetingsView
|
||||
azureClientId={azureConfig.clientId}
|
||||
onStartRecording={(subject) => {
|
||||
setView('recorder');
|
||||
setRecordingSubject(subject || '');
|
||||
setAutoStartRecording(true);
|
||||
}}
|
||||
apiKey={apiKey}
|
||||
productId={productId}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={handleModelChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{view === 'logs' && (
|
||||
<LogViewer logs={logs} />
|
||||
)}
|
||||
|
||||
|
||||
{view === 'settings' && (
|
||||
<Settings
|
||||
@@ -350,6 +439,9 @@ Wann findet das nächste Meeting statt oder was ist der nächste Meilenstein?`,
|
||||
productId={productId}
|
||||
prompts={prompts}
|
||||
savePath={savePath}
|
||||
smtpConfig={smtpConfig}
|
||||
azureConfig={azureConfig}
|
||||
emailTemplates={emailTemplates}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user