fix: Gemini model -> gemini-2.5-flash, stable DOM player, favicon
Deploy to Netlify / build-and-deploy (push) Successful in 1m2s
Deploy to Netlify / build-and-deploy (push) Successful in 1m2s
- Update Gemini API model from deprecated gemini-2.0-flash to gemini-2.5-flash - Remove SidebarPlayer sub-component from inside App() - caused DOM remounts every 250ms (currentTime tick) making Pause/Skip buttons unclickable - Inline player JSX directly in render tree for stable DOM nodes - Add favicon.svg (dark bg + emerald trend line) to fix 404 console errors
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Falah Coach Portal</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#0f172a"/><path d="M7 22 L16 8 L25 22" stroke="#10b981" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
After Width: | Height: | Size: 243 B |
+28
-46
@@ -27,7 +27,7 @@ import {
|
||||
========================================================================== */
|
||||
|
||||
const GEMINI_API_KEY = 'AIzaSyDZn7tv1D3n2zuns8uaXIqp_z1FZTeVyKI';
|
||||
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}`;
|
||||
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`;
|
||||
|
||||
const AI_SYSTEM_PROMPT = `You are a hostile, senior executive playing a roleplay simulation for management consultants at Falah Consulting.
|
||||
Your persona: a hard-nosed C-suite executive (CIO, CFO, or CEO) who is deeply sceptical of consultants and demands hard financial evidence.
|
||||
@@ -606,52 +606,8 @@ export default function App() {
|
||||
setSidebarOpen(false);
|
||||
};
|
||||
|
||||
// ── Audio Player UI (reused in sidebar + mobile strip) ───────────────────
|
||||
|
||||
const progressPct = duration > 0 ? (currentTime / duration) * 100 : 0;
|
||||
|
||||
const SidebarPlayer = () => (
|
||||
activeTrack ? (
|
||||
<div className="animate-fade-in-up">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-[10px] font-bold text-emerald-400 uppercase tracking-wider flex items-center gap-1">
|
||||
<Volume2 size={10} /> Now Playing
|
||||
</span>
|
||||
<span className="text-xs text-slate-500 font-mono">{formatTime(currentTime)} / {activeTrack.duration}</span>
|
||||
</div>
|
||||
<p className="font-semibold text-sm text-white truncate mb-0.5" title={activeTrack.title}>{activeTrack.title}</p>
|
||||
<p className="text-xs text-slate-400 mb-3 truncate">{activeTrack.author}</p>
|
||||
{/* Seekable progress bar */}
|
||||
<div
|
||||
className="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden cursor-pointer mb-3"
|
||||
onClick={seek}
|
||||
title="Click to seek"
|
||||
>
|
||||
<div
|
||||
className="bg-emerald-500 h-full rounded-full transition-all duration-300"
|
||||
style={{ width: `${progressPct}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<button aria-label="Previous track" onClick={skipPrev} className="text-slate-400 hover:text-white transition-colors"><SkipBack size={18} /></button>
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
aria-label={isPlaying ? 'Pause' : 'Play'}
|
||||
className="text-white bg-emerald-500 hover:bg-emerald-400 rounded-full w-10 h-10 flex items-center justify-center transition-all shadow-lg shadow-emerald-500/20"
|
||||
>
|
||||
{isPlaying ? <PauseCircle size={24} className="text-slate-950" /> : <PlayCircle size={24} className="text-slate-950" />}
|
||||
</button>
|
||||
<button aria-label="Next track" onClick={skipNext} className="text-slate-400 hover:text-white transition-colors"><SkipForward size={18} /></button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4 text-center">
|
||||
<Headphones className="mx-auto text-slate-700 mb-2" size={24} />
|
||||
<p className="text-xs text-slate-500 font-medium">Select an audio brief to begin</p>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
// ── Render ────────────────────────────────────────────────────────────────
|
||||
|
||||
return (
|
||||
@@ -705,7 +661,33 @@ export default function App() {
|
||||
</nav>
|
||||
|
||||
<div className="mt-auto border-t border-slate-800 p-4 bg-slate-950/50 backdrop-blur-md">
|
||||
<SidebarPlayer />
|
||||
{activeTrack ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-[10px] font-bold text-emerald-400 uppercase tracking-wider flex items-center gap-1">
|
||||
<Volume2 size={10} /> Now Playing
|
||||
</span>
|
||||
<span className="text-xs text-slate-500 font-mono">{formatTime(currentTime)} / {activeTrack.duration}</span>
|
||||
</div>
|
||||
<p className="font-semibold text-sm text-white truncate mb-0.5" title={activeTrack.title}>{activeTrack.title}</p>
|
||||
<p className="text-xs text-slate-400 mb-3 truncate">{activeTrack.author}</p>
|
||||
<div className="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden cursor-pointer mb-3" onClick={seek} title="Click to seek">
|
||||
<div className="bg-emerald-500 h-full rounded-full transition-all duration-300" style={{ width: `${progressPct}%` }} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<button aria-label="Previous track" onClick={skipPrev} className="text-slate-400 hover:text-white transition-colors"><SkipBack size={18} /></button>
|
||||
<button onClick={togglePlay} aria-label={isPlaying ? 'Pause' : 'Play'} className="text-white bg-emerald-500 hover:bg-emerald-400 rounded-full w-10 h-10 flex items-center justify-center transition-all shadow-lg shadow-emerald-500/20">
|
||||
{isPlaying ? <PauseCircle size={24} className="text-slate-950" /> : <PlayCircle size={24} className="text-slate-950" />}
|
||||
</button>
|
||||
<button aria-label="Next track" onClick={skipNext} className="text-slate-400 hover:text-white transition-colors"><SkipForward size={18} /></button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4 text-center">
|
||||
<Headphones className="mx-auto text-slate-700 mb-2" size={24} />
|
||||
<p className="text-xs text-slate-500 font-medium">Select an audio brief to begin</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user