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:
+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