Ambient WebGL petals + cute notification sounds
Add a fixed, GPU-driven petal layer that drifts soft blossoms over the page (sparse, translucent, paused when hidden, skipped under prefers-reduced-motion). Add a synthesized cute sound palette — bubble pop, water droplet, soft bell, sparkle, cheer — generated as embedded WAV assets (scripts/gen_sounds.py) and played through a quiet Web Audio bus with soft-clipping and a persisted mute toggle in the status bar. Sounds fire per suggestion type when fresh advice arrives (staggered, old pending advice stays silent) and on companion bubbles by tone. Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
This commit is contained in:
@@ -13,6 +13,8 @@ import { StatusBar } from './components/StatusBar/StatusBar'
|
||||
import { PetalCompanion } from './components/Companion/PetalCompanion'
|
||||
import { UpdateBanner } from './components/UpdateBanner/UpdateBanner'
|
||||
import { useVersionWatch } from './hooks/useVersionWatch'
|
||||
import { PetalFall } from './effects/PetalFall'
|
||||
import { playSuggestionSound } from './audio/sounds'
|
||||
|
||||
export default function App() {
|
||||
const updateAvailable = useVersionWatch()
|
||||
@@ -309,8 +311,34 @@ export default function App() {
|
||||
[removeSuggestion],
|
||||
)
|
||||
|
||||
// Play a soft sound when freshly-checked suggestions arrive — one per distinct
|
||||
// new type, lightly staggered so a batch reads as a little melody rather than
|
||||
// a pile-up. We track which ids we've already chimed for, and only chime for
|
||||
// recently-created suggestions so opening a doc with old pending advice stays
|
||||
// silent (the existing set was created in a past session).
|
||||
const chimedRef = useRef<Set<string>>(new Set())
|
||||
useEffect(() => {
|
||||
const fresh = suggestions.filter((s) => !chimedRef.current.has(s.id))
|
||||
fresh.forEach((s) => chimedRef.current.add(s.id))
|
||||
const justMade = fresh.filter(
|
||||
(s) => Date.now() - new Date(s.created_at).getTime() < 12_000,
|
||||
)
|
||||
if (justMade.length === 0) return
|
||||
const types = [...new Set(justMade.map((s) => s.type))].slice(0, 3)
|
||||
const timers = types.map((t, i) =>
|
||||
window.setTimeout(() => playSuggestionSound(t), i * 150),
|
||||
)
|
||||
return () => timers.forEach((id) => window.clearTimeout(id))
|
||||
}, [suggestions])
|
||||
|
||||
// Forget chimed ids when switching documents so the set can't grow unbounded.
|
||||
useEffect(() => {
|
||||
chimedRef.current = new Set()
|
||||
}, [currentDoc?.id])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<PetalFall />
|
||||
<header
|
||||
onMouseDown={handleChromeDown}
|
||||
className="petal-no-print flex h-12 shrink-0 items-center gap-2 px-5"
|
||||
|
||||
Reference in New Issue
Block a user