From afce0dda28dbef2e7521a0bc5d120b678adf776d Mon Sep 17 00:00:00 2001 From: Kartios Date: Thu, 13 Aug 2026 19:04:31 -0700 Subject: [PATCH] Play the platform music on the shared Learn page too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- apps/web/src/App.tsx | 17 ++++++++++++++--- apps/web/src/components/Shell.tsx | 10 ---------- apps/web/src/pages/Learn.tsx | 13 ++++++++++--- 3 files changed, 24 insertions(+), 16 deletions(-) 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}