diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 86a5f6e..b3b95c4 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -10,6 +10,7 @@ import { ThemeProvider } from '@/lib/theme'; import { IdentityProvider, useIdentityQuery } from '@/lib/identity'; import { LayoutProvider } from '@/lib/layout'; import { PiggyContextProvider } from '@/lib/piggy-context'; +import { PlatformAudioProvider } from '@/lib/audio'; import { Shell } from '@/components/Shell'; import { SignIn } from '@/pages/SignIn'; import { CreateProfile } from '@/pages/CreateProfile'; @@ -85,9 +86,19 @@ export function App() { void patch('/api/me/preferences', prefs).catch(() => {}); }} > - - - + {/* + Above the router, so the music survives navigation AND covers the + anonymous Learn page — a share-code visitor gets the same platform + character as a member. It never plays unbidden: the browser refuses + audio until the page has had a real gesture, so it begins when + someone actually starts using the page, and the header control mutes + it for good on that device. + */} + + + + + {/* Inside ThemeProvider: the host reads the resolved light/dark value. */} diff --git a/apps/web/src/components/Shell.tsx b/apps/web/src/components/Shell.tsx index 60c60b2..e6a1a1b 100644 --- a/apps/web/src/components/Shell.tsx +++ b/apps/web/src/components/Shell.tsx @@ -29,7 +29,6 @@ import { NavLink } from 'react-router-dom'; import { useIdentity } from '@/lib/identity'; import { useLayout } from '@/lib/layout'; import { visibleNav, type NavItem } from '@/lib/nav'; -import { PlatformAudioProvider } from '@/lib/audio'; import { AppHeader } from './AppHeader'; import { AppSidebar } from './AppSidebar'; import { PiggyDock } from './PiggyDock'; @@ -45,14 +44,6 @@ export function Shell() { const items = visibleNav(identity); return ( - /* - * Music is mounted HERE rather than in App.tsx, so it wraps only the - * signed-in application. The anonymous Learn page renders outside Shell, - * and a share-code visitor opening a link someone sent them should not get - * unexpected audio — that is the one context where it reads as a fault - * rather than as character. - */ - item.primary)} /> - ); } diff --git a/apps/web/src/pages/Learn.tsx b/apps/web/src/pages/Learn.tsx index 8f18f61..db803c5 100644 --- a/apps/web/src/pages/Learn.tsx +++ b/apps/web/src/pages/Learn.tsx @@ -55,6 +55,7 @@ import { usePageTitle } from '@/lib/title'; import { usePiggyContext } from '@/lib/piggy-context'; import { toPiggyPageRoute } from '@pig/core'; import { Badge, Button, Card, EmptyState, Skeleton } from '@/components/ui'; +import { AudioControl } from '@/components/AudioControl'; import { PiggyMark } from '@/components/PiggyMark'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { AddResourceDialog } from '@/components/learn/AddResourceDialog'; @@ -418,9 +419,15 @@ function AnonFrame({ children }: { children: ReactNode }) { pig - + + {/* The anonymous page renders outside Shell, so it does not get the + app header — but it DOES get the music, and music with no way to + stop it is the worst version of this feature. */} + + +
{children}