Play the platform music on the shared Learn page too
CI / verify (push) Successful in 3m41s
CI / publish (push) Has been skipped

Reversing yesterday's call at Karti's direction. The provider moves from Shell
up above the router in App, so a share-code visitor gets the same character as
a member rather than a silent page.

The reason that is safe is the same reason "autoplay" was never really
autoplay: the browser refuses audio until the page has had a real gesture, so
nothing plays the instant a link opens — it starts once someone is actually
using the page.

The anonymous page renders OUTSIDE Shell and therefore has no app header, so
the mute control is added to its own chrome. Music with no way to stop it is
the worst version of this feature, and a visitor who cannot find the switch
does not conclude the site has taste.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 19:04:31 -07:00
parent 0108a70131
commit afce0dda28
3 changed files with 24 additions and 16 deletions
+11
View File
@@ -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.
*/}
<PlatformAudioProvider>
<BrowserRouter>
<AuthGate config={config} />
</BrowserRouter>
</PlatformAudioProvider>
{/* Inside ThemeProvider: the host reads the resolved light/dark value. */}
<Toaster />
</ThemeProvider>
-10
View File
@@ -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.
*/
<PlatformAudioProvider>
<SidebarProvider
open={sidebarOpen}
onOpenChange={setSidebarOpen}
@@ -98,7 +89,6 @@ export function Shell() {
<MobileTabBar items={items.filter((item) => item.primary)} />
</SidebarProvider>
</PlatformAudioProvider>
);
}
+7
View File
@@ -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 }) {
<PiggyMark className="size-7 shrink-0 text-fg" aria-hidden />
<span className="min-w-0 truncate font-semibold tracking-tight">pig</span>
</span>
<span className="flex shrink-0 items-center gap-1">
{/* 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. */}
<AudioControl />
<Button variant="ghost" size="sm" className="shrink-0" asChild>
<a href="/">Sign in</a>
</Button>
</span>
</header>
<main className="mx-auto flex w-full min-w-0 max-w-5xl flex-1 flex-col gap-10 px-4 pb-14 pt-2 sm:px-6 sm:pb-20">
{children}