Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/__tests__/editor-store/undo-redo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,43 @@ describe('Undo / Redo — basic lifecycle', () => {
expect(nodesAfterRedo).toBe(nodesBeforeUndo)
})

it('undo prunes selection that points at the reverted insertion', () => {
const site = getStore().createSite('Test SiteDocument')
const rootId = site.pages[0].rootNodeId
const insertedId = useEditorStore.getState().insertNode('base.text', {}, rootId)

useEditorStore.getState().selectNode(insertedId)
expect(useEditorStore.getState().selectedNodeIds).toEqual([insertedId])

useEditorStore.getState().undo()

const afterUndo = useEditorStore.getState()
expect(afterUndo.site!.pages[0].nodes[insertedId]).toBeUndefined()
expect(afterUndo.selectedNodeIds).toEqual([])
expect(afterUndo.selectedNodeId).toBeNull()

const nextId = afterUndo.insertNode('base.text', {}, rootId)
expect(nextId).not.toBe('')
expect(useEditorStore.getState().site!.pages[0].nodes[nextId]).toBeDefined()
})

it('redo prunes selection when replaying a deletion', () => {
const site = getStore().createSite('Test SiteDocument')
const rootId = site.pages[0].rootNodeId
const insertedId = useEditorStore.getState().insertNode('base.text', {}, rootId)

useEditorStore.getState().deleteNode(insertedId)
useEditorStore.getState().undo()
useEditorStore.getState().selectNode(insertedId)

useEditorStore.getState().redo()

const afterRedo = useEditorStore.getState()
expect(afterRedo.site!.pages[0].nodes[insertedId]).toBeUndefined()
expect(afterRedo.selectedNodeIds).toEqual([])
expect(afterRedo.selectedNodeId).toBeNull()
})

it('canRedo is true after undo', () => {
const s = getStore()
const site = s.createSite('Test SiteDocument')
Expand Down
29 changes: 29 additions & 0 deletions src/__tests__/panels/agentPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,35 @@ describe('AgentPanel', () => {
expect(screen.getByTestId('agent-new-chat-header-button')).toBeTruthy()
})

it('autofocuses the composer when the open panel has no focused control', async () => {
installModelFetch(true)
renderAgentPanel({
agentActiveCredentialId: TEST_CREDENTIAL.id,
agentActiveModelId: 'model-1',
})

const textarea = await screen.findByLabelText('Message to AI assistant')
await waitFor(() => expect(document.activeElement).toBe(textarea))
})

it('does not steal focus from New chat when its deferred autofocus runs', async () => {
installModelFetch(true)
renderAgentPanel({
agentActiveCredentialId: TEST_CREDENTIAL.id,
agentActiveModelId: 'model-1',
})

const newChat = screen.getByRole('button', { name: 'New chat' })
newChat.focus()
fireEvent.click(newChat)
expect(document.activeElement).toBe(newChat)

await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 75))
})
expect(document.activeElement).toBe(newChat)
})

it('shows compact context, token, and cost detail beside the image action', async () => {
const credential = { ...TEST_CREDENTIAL, id: 'cred_context_meter' }
installModelFetch(true, true, 128_000, credential)
Expand Down
10 changes: 9 additions & 1 deletion src/admin/pages/site/panels/AgentPanel/AgentComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ export function AgentComposer({

useEffect(() => {
if (!isOpen) return
const id = setTimeout(() => inputRef.current?.focus(), 50)
const id = setTimeout(() => {
const input = inputRef.current
if (!input) return
const panel = input.closest('[data-panel]')
// A header or composer control may have received an explicit click
// while this deferred autofocus was waiting.
if (panel?.contains(document.activeElement)) return
input.focus()
}, 50)
return () => clearTimeout(id)
}, [isOpen])

Expand Down
3 changes: 3 additions & 0 deletions src/admin/pages/site/store/slices/site/undoRedoActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { apply } from 'mutative'
import { clonePackageJson } from '@core/site-dependencies/manifest'
import { cloneSiteRuntimeConfig } from '@core/site-runtime'
import { pruneCanvasSelectionDraft } from '../selectionSlice'
import { collectDirtyFromSitePatches, mergeDirtyMarks } from './dirtyTracking'
import type { SiteSlice, SiteSliceHelpers } from './types'

Expand Down Expand Up @@ -47,6 +48,7 @@ export function createUndoRedoActions({ get, set }: SiteSliceHelpers): UndoRedoA
if (!state.site.pages.find((p) => p.id === state.activePageId)) {
state.activePageId = state.site.pages[0]?.id ?? null
}
pruneCanvasSelectionDraft(state)
})
},

Expand Down Expand Up @@ -75,6 +77,7 @@ export function createUndoRedoActions({ get, set }: SiteSliceHelpers): UndoRedoA
if (!state.site.pages.find((p) => p.id === state.activePageId)) {
state.activePageId = state.site.pages[0]?.id ?? null
}
pruneCanvasSelectionDraft(state)
})
},
}
Expand Down
Loading