fix(web): move the new-session chip when the default preset changes
The General row and the preset page already re-read on `settings/changed`, but
the hero chip did not — so changing the default and starting a session without
reloading composed the previous default. That is the one session the setting
claims to govern ("对此后新建的会话生效"), and the chip is what decides it.
A staged pick still wins: `load()` prefers the stage over the refreshed
fallback, so a refresh never overwrites a choice the user just made.
This commit is contained in:
@@ -110,6 +110,15 @@ export function apply(ctx: ClientContext): void {
|
||||
// and either way the chip's pick predates it — so the stage is applied
|
||||
// when the session arrives, not when it was made.
|
||||
const stop = scope.sessions.list.subscribe(() => { void seat.apply() })
|
||||
// The chip opens on the deployment default, so a default changed from
|
||||
// the settings surface moves it too — otherwise the screen that starts
|
||||
// the next session keeps offering the previous default until a reload,
|
||||
// which is exactly the session the setting claims to govern. A staged
|
||||
// pick survives: `load()` prefers it over the refreshed fallback.
|
||||
const settingsMoved = scope.on('settings/changed', (ns?: string) => {
|
||||
if (ns !== undefined && ns !== AGENT_PRESET_SETTINGS_NS) return
|
||||
void seat.load()
|
||||
})
|
||||
const chip = scope.slots.register({
|
||||
name: 'conversation.hero.agentPreset',
|
||||
locale: 'settings.agentPreset',
|
||||
@@ -124,6 +133,7 @@ export function apply(ctx: ClientContext): void {
|
||||
}, AgentPresetLabel)
|
||||
return () => {
|
||||
stop()
|
||||
settingsMoved()
|
||||
chip()
|
||||
label()
|
||||
}
|
||||
|
||||
@@ -25,13 +25,32 @@ import type { AgentPresetSeatInjected } from '../src/client/AgentPresetSeat.tsx'
|
||||
// the shipped Chinese copy, so they state the browser they assume.
|
||||
usePinnedBrowserLanguages('zh-CN')
|
||||
|
||||
const ROSTER = {
|
||||
const ROSTER_ONE = {
|
||||
rpcId: 'r',
|
||||
result: { ok: true as const, value: { presets: [{ id: 'standard', trust: 'system', isDefault: true }], authorable: true } },
|
||||
}
|
||||
|
||||
/** The same roster with a second preset carrying the default. */
|
||||
const ROSTER_MOVED = {
|
||||
rpcId: 'r',
|
||||
result: {
|
||||
ok: true as const,
|
||||
value: {
|
||||
presets: [
|
||||
{ id: 'standard', trust: 'system', isDefault: false },
|
||||
{ id: 'minimal', trust: 'system', isDefault: true },
|
||||
],
|
||||
authorable: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
async function bench() {
|
||||
const ctx = new Context()
|
||||
// The host's answer, mutable so a spec can move the default the way the
|
||||
// settings surface does and watch who re-reads it.
|
||||
let ROSTER: typeof ROSTER_ONE | typeof ROSTER_MOVED = ROSTER_ONE
|
||||
const moveDefault = (): void => { ROSTER = ROSTER_MOVED }
|
||||
await ctx.plugin(SlotsService).await()
|
||||
const locale = new LocaleService(ctx)
|
||||
ctx.provide('locale', locale)
|
||||
@@ -56,7 +75,7 @@ async function bench() {
|
||||
},
|
||||
},
|
||||
} as never)
|
||||
return { ctx, slots: ctx.get('slots') as SlotsService, calls }
|
||||
return { ctx, slots: ctx.get('slots') as SlotsService, calls, moveDefault }
|
||||
}
|
||||
|
||||
function declareRoot(slots: SlotsService): () => void {
|
||||
@@ -238,6 +257,31 @@ describe('ui-agent-preset apply', () => {
|
||||
conversation()
|
||||
})
|
||||
|
||||
it('moves the chip when the default changes on the settings surface', async () => {
|
||||
const { ctx, slots, moveDefault } = await bench()
|
||||
declareRoot(slots)
|
||||
const conversation = declareConversation(slots)
|
||||
ctx.provide('conversation', {} as never)
|
||||
ctx.provide('sessions', sessionsDouble({ byId: {} }) as never)
|
||||
await ctx.plugin({ inject: [...inject, 'conversation', 'sessions'], apply }).await()
|
||||
|
||||
const chip = slots.entries('conversation.hero.agentPreset')[0]!
|
||||
const seat = (chip.inject as unknown as () => AgentPresetSeatInjected)()
|
||||
await seat.load()
|
||||
expect(seat.hooks.agentPresetSeat.getSnapshot().current).toBe('standard')
|
||||
|
||||
// The chip opens on the deployment default, and the setting it comes from
|
||||
// lives on another screen: without this the next session — the very one
|
||||
// the setting governs — would be composed from the previous default until
|
||||
// a reload.
|
||||
moveDefault()
|
||||
ctx.emit('settings/changed', 'agent-presets')
|
||||
await vi.waitFor(() => {
|
||||
expect(seat.hooks.agentPresetSeat.getSnapshot().current).toBe('minimal')
|
||||
})
|
||||
conversation()
|
||||
})
|
||||
|
||||
it('applies the staged choice to the blank session the flow lands on', async () => {
|
||||
const { ctx, slots, calls } = await bench()
|
||||
declareRoot(slots)
|
||||
|
||||
Reference in New Issue
Block a user