Expand AITURK Turkish settings and desktop surfaces for beta 5
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
|
||||
import { I18nProvider, useI18n } from '@/i18n'
|
||||
import { EN_SURFACES } from '@/i18n/en-surfaces'
|
||||
import { TR_SURFACES } from '@/i18n/tr-surfaces'
|
||||
import { $petActivity, $petState } from '@/store/pet'
|
||||
|
||||
import { PetBubble } from './pet-bubble'
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
$petActivity.set({})
|
||||
})
|
||||
|
||||
it('changes the visible status language while preserving the actual failure state', () => {
|
||||
$petActivity.set({ error: true })
|
||||
|
||||
function Surface() {
|
||||
const { setLocale } = useI18n()
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => void setLocale('tr')}>Türkçe</button>
|
||||
<PetBubble />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const view = render(
|
||||
<I18nProvider configClient={null} initialLocale="en">
|
||||
<Surface />
|
||||
</I18nProvider>
|
||||
)
|
||||
|
||||
expect(EN_SURFACES.pet.failed.some(line => view.container.textContent?.includes(line))).toBe(true)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Türkçe' }))
|
||||
expect(TR_SURFACES.pet.failed.some(line => view.container.textContent?.includes(line))).toBe(true)
|
||||
expect(EN_SURFACES.pet.failed.some(line => view.container.textContent?.includes(line))).toBe(false)
|
||||
expect($petState.get()).toBe('failed')
|
||||
expect($petActivity.get()).toEqual({ error: true })
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { useI18n } from '@/i18n'
|
||||
import { AlertCircle, Clock, type IconComponent } from '@/lib/icons'
|
||||
import { $petActivity, $petState, type PetState } from '@/store/pet'
|
||||
|
||||
@@ -27,46 +28,6 @@ interface Spec {
|
||||
|
||||
// Phrasings per mood, picked at random (no immediate repeat) for a bit of life.
|
||||
// Keep them short — the bubble is tiny and never wraps.
|
||||
const SPECS: Partial<Record<PetState, Spec>> = {
|
||||
run: {
|
||||
lines: [
|
||||
'working…',
|
||||
'on it…',
|
||||
'crunching…',
|
||||
'tinkering…',
|
||||
'cooking…',
|
||||
'in the weeds…',
|
||||
'wiring it up…',
|
||||
'making moves…',
|
||||
'heads down…',
|
||||
'hammering away…'
|
||||
]
|
||||
},
|
||||
review: {
|
||||
lines: [
|
||||
'thinking…',
|
||||
'reading…',
|
||||
'reviewing…',
|
||||
'pondering…',
|
||||
'connecting dots…',
|
||||
'sizing it up…',
|
||||
'tracing it…',
|
||||
'mulling…',
|
||||
'scheming…',
|
||||
'hmm…'
|
||||
]
|
||||
},
|
||||
failed: {
|
||||
glyph: AlertCircle,
|
||||
lines: ['hit a snag', 'welp', 'that broke', 'oof', 'snagged'],
|
||||
tone: 'error'
|
||||
},
|
||||
waiting: {
|
||||
glyph: Clock,
|
||||
lines: ['your turn', 'all yours', 'over to you', 'ball’s in your court', 'awaiting orders'],
|
||||
tone: 'wait'
|
||||
}
|
||||
}
|
||||
|
||||
const TONE_COLOR: Record<Tone, string> = {
|
||||
error: 'var(--ui-red)',
|
||||
@@ -89,6 +50,18 @@ function pick(lines: string[], prev: string): string {
|
||||
}
|
||||
|
||||
export function PetBubble() {
|
||||
const { t } = useI18n()
|
||||
|
||||
const specs = useMemo<Partial<Record<PetState, Spec>>>(
|
||||
() => ({
|
||||
run: { lines: t.surfaces.pet.run },
|
||||
review: { lines: t.surfaces.pet.review },
|
||||
failed: { glyph: AlertCircle, lines: t.surfaces.pet.failed, tone: 'error' },
|
||||
waiting: { glyph: Clock, lines: t.surfaces.pet.waiting, tone: 'wait' }
|
||||
}),
|
||||
[t]
|
||||
)
|
||||
|
||||
const state = useStore($petState)
|
||||
const activity = useStore($petActivity)
|
||||
const [line, setLine] = useState('')
|
||||
@@ -96,14 +69,14 @@ export function PetBubble() {
|
||||
// Finish beats are carried by the sprite/mail icon; idle only speaks up when
|
||||
// it's actually the user's turn. Everything else maps to a mood spec.
|
||||
const specKey: null | PetState =
|
||||
state in SPECS ? state : state === 'idle' && activity.awaitingInput ? 'waiting' : null
|
||||
state in specs ? state : state === 'idle' && activity.awaitingInput ? 'waiting' : null
|
||||
|
||||
const rotating = specKey === 'run' || specKey === 'review'
|
||||
|
||||
// Pick a fresh line on every mood change, then keep rotating (random, no
|
||||
// repeat) only while the agent is actively working/thinking.
|
||||
useEffect(() => {
|
||||
const spec = specKey ? SPECS[specKey] : null
|
||||
const spec = specKey ? specs[specKey] : null
|
||||
|
||||
if (!spec) {
|
||||
setLine('')
|
||||
@@ -120,9 +93,9 @@ export function PetBubble() {
|
||||
const id = window.setInterval(() => setLine(prev => pick(spec.lines, prev)), 2600)
|
||||
|
||||
return () => window.clearInterval(id)
|
||||
}, [specKey, rotating])
|
||||
}, [specKey, rotating, specs])
|
||||
|
||||
const spec = specKey ? SPECS[specKey] : null
|
||||
const spec = specKey ? specs[specKey] : null
|
||||
|
||||
if (!spec) {
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user