feat(know-your-world): enhance hint audio and region name display

Audio hints now include the region name:
- Speak "France. Located in Western Europe" instead of just "Located in Western Europe"
- Applies to all hint speech triggers (manual, auto-speak, region change)

Region name display is now more prominent:
- Larger font size (2xl)
- Pop-in animation when region changes
- Subtle glow pulse animation on the prompt box
- Target emoji (🎯) added to "Find" label
- Improved styling with rounded corners and thicker border

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-26 21:19:48 -06:00
parent 81301ab148
commit e6f58bfd93
2 changed files with 53 additions and 18 deletions

View File

@ -170,31 +170,48 @@ export function GameInfoPanel({
className={css({ className={css({
flex: 1, flex: 1,
textAlign: 'center', textAlign: 'center',
padding: '2', padding: '3',
bg: isDark ? 'blue.900' : 'blue.50', bg: isDark ? 'blue.900' : 'blue.50',
rounded: 'md', rounded: 'xl',
border: '2px solid', border: '3px solid',
borderColor: 'blue.500', borderColor: 'blue.500',
minWidth: 0, // Allow shrinking minWidth: 0, // Allow shrinking
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: '1', gap: '1',
})} })}
style={{
animation: 'glowPulse 2s ease-in-out infinite',
}}
> >
<style>{`
@keyframes glowPulse {
0%, 100% { box-shadow: 0 0 10px rgba(59, 130, 246, 0.3); }
50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.6), 0 0 30px rgba(59, 130, 246, 0.3); }
}
@keyframes popIn {
0% { transform: scale(0.8); opacity: 0; }
50% { transform: scale(1.1); }
100% { transform: scale(1); opacity: 1; }
}
`}</style>
<div <div
className={css({ className={css({
fontSize: '2xs', fontSize: 'xs',
color: isDark ? 'blue.300' : 'blue.700', color: isDark ? 'blue.300' : 'blue.700',
fontWeight: 'semibold', fontWeight: 'bold',
textTransform: 'uppercase',
letterSpacing: 'wide',
})} })}
> >
Find: 🎯 Find
</div> </div>
<div <div
key={currentRegionId || 'empty'} // Re-trigger animation on change
className={css({ className={css({
fontSize: 'lg', fontSize: '2xl',
fontWeight: 'bold', fontWeight: 'bold',
color: isDark ? 'blue.100' : 'blue.900', color: isDark ? 'white' : 'blue.900',
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
@ -203,8 +220,12 @@ export function GameInfoPanel({
justifyContent: 'center', justifyContent: 'center',
gap: '2', gap: '2',
})} })}
style={{
animation: 'popIn 0.4s ease-out',
textShadow: isDark ? '0 2px 4px rgba(0,0,0,0.3)' : 'none',
}}
> >
{flagEmoji && <span className={css({ fontSize: 'xl' })}>{flagEmoji}</span>} {flagEmoji && <span className={css({ fontSize: '2xl' })}>{flagEmoji}</span>}
<span>{currentRegionName || '...'}</span> <span>{currentRegionName || '...'}</span>
</div> </div>
</div> </div>

View File

@ -494,6 +494,20 @@ export function MapRenderer({
const hintText = useRegionHint(hintMapKey, currentPrompt) const hintText = useRegionHint(hintMapKey, currentPrompt)
const hasHint = useHasRegionHint(hintMapKey, currentPrompt) const hasHint = useHasRegionHint(hintMapKey, currentPrompt)
// Get the current region name for audio hints
const currentRegionName = useMemo(() => {
if (!currentPrompt) return null
const region = mapData.regions.find((r) => r.id === currentPrompt)
return region?.name ?? null
}, [currentPrompt, mapData.regions])
// Create full hint text with region name prefix for speech
const fullHintText = useMemo(() => {
if (!hintText) return null
if (!currentRegionName) return hintText
return `${currentRegionName}. ${hintText}`
}, [currentRegionName, hintText])
// Speech synthesis for reading hints aloud // Speech synthesis for reading hints aloud
const { const {
speak: speakHint, speak: speakHint,
@ -585,10 +599,10 @@ export function MapRenderer({
const handleSpeakClick = useCallback(() => { const handleSpeakClick = useCallback(() => {
if (isSpeaking) { if (isSpeaking) {
stopSpeaking() stopSpeaking()
} else if (hintText) { } else if (fullHintText) {
speakHint(hintText, withAccent) speakHint(fullHintText, withAccent)
} }
}, [isSpeaking, stopSpeaking, hintText, speakHint, withAccent]) }, [isSpeaking, stopSpeaking, fullHintText, speakHint, withAccent])
const speakButton = usePointerLockButton({ const speakButton = usePointerLockButton({
id: 'speak-hint', id: 'speak-hint',
@ -712,10 +726,10 @@ export function MapRenderer({
const justOpened = showHintBubble && !prevShowHintBubbleRef.current const justOpened = showHintBubble && !prevShowHintBubbleRef.current
prevShowHintBubbleRef.current = showHintBubble prevShowHintBubbleRef.current = showHintBubble
if (justOpened && autoSpeak && hintText && isSpeechSupported) { if (justOpened && autoSpeak && fullHintText && isSpeechSupported) {
speakHint(hintText, withAccent) speakHint(fullHintText, withAccent)
} }
}, [showHintBubble, autoSpeak, hintText, isSpeechSupported, speakHint, withAccent]) }, [showHintBubble, autoSpeak, fullHintText, isSpeechSupported, speakHint, withAccent])
// Track previous prompt to detect region changes // Track previous prompt to detect region changes
const prevPromptRef = useRef<string | null>(null) const prevPromptRef = useRef<string | null>(null)
@ -741,13 +755,13 @@ export function MapRenderer({
setShowHintBubble(true) setShowHintBubble(true)
// If region changed and both auto-hint and auto-speak are enabled, speak immediately // If region changed and both auto-hint and auto-speak are enabled, speak immediately
// This handles the case where the bubble was already open // This handles the case where the bubble was already open
if (isNewRegion && autoSpeakRef.current && hintText && isSpeechSupported) { if (isNewRegion && autoSpeakRef.current && fullHintText && isSpeechSupported) {
speakHint(hintText, withAccentRef.current) speakHint(fullHintText, withAccentRef.current)
} }
} else { } else {
setShowHintBubble(false) setShowHintBubble(false)
} }
}, [currentPrompt, hasHint, hintText, isSpeechSupported, speakHint]) }, [currentPrompt, hasHint, fullHintText, isSpeechSupported, speakHint])
// Hot/cold audio feedback hook // Hot/cold audio feedback hook
// Only enabled if: 1) assistance level allows it, 2) user toggle is on, 3) not touch device // Only enabled if: 1) assistance level allows it, 2) user toggle is on, 3) not touch device