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:
parent
81301ab148
commit
e6f58bfd93
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue