Skip to content
Open
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
16 changes: 7 additions & 9 deletions apps/desktop/src/app/chat/perf-probe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,13 @@ if (typeof window !== 'undefined' && !window.__PERF_DRIVE__) {
let baseline: ReturnType<typeof $messages.get> | null = null
let activeHandle: SyntheticDriverHandle | null = null

let rightPaneBaseline:
| null
| {
activeTerminalId: null | string
cwd: string
repoStatusByCwd: ReturnType<typeof $repoStatusByCwd.get>
takeover: boolean
terminals: readonly TerminalEntry[]
} = null
let rightPaneBaseline: null | {
activeTerminalId: null | string
cwd: string
repoStatusByCwd: ReturnType<typeof $repoStatusByCwd.get>
takeover: boolean
terminals: readonly TerminalEntry[]
} = null

const stop = () => {
activeHandle = null
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/app/chat/session-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,12 @@ function TileChat({
(url: string) => addContextRefAttachment(`@url:${formatRefValue(url)}`, url),
[addContextRefAttachment]
)

const onPasteClipboardImage = useCallback(
(opts?: { silent?: boolean }) => pasteClipboardImage(opts),
[pasteClipboardImage]
)

const onPickFiles = useCallback(() => void pickContextPaths('file'), [pickContextPaths])
const onPickFolders = useCallback(() => void pickContextPaths('folder'), [pickContextPaths])
const onPickImages = useCallback(() => void pickImages(), [pickImages])
Expand Down
4 changes: 3 additions & 1 deletion apps/desktop/src/app/chat/sidebar/sessions-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,9 @@ export function SidebarSessionsSection({
(items: SessionInfo[]) => {
const entries = flattenSessionsWithBranches(items)

return (dateGrouped ? groupEntriesByRecency(entries) : toSessionRows(entries)).map(row => renderListRow(row, false))
return (dateGrouped ? groupEntriesByRecency(entries) : toSessionRows(entries)).map(row =>
renderListRow(row, false)
)
},
[dateGrouped, renderListRow]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export function useMessageStream({
// stays as the fallback.
const writeCost = performance.now() - startedAt
lastFlushCostRef.current = writeCost

// At most one measurement rAF may be pending: only the newest flush's
// measurement matters (the guard below discards stale frames), and a
// hidden renderer parks rAF callbacks — without cancellation a long
Expand All @@ -295,8 +296,10 @@ export function useMessageStream({
if (measureRafRef.current !== null) {
window.cancelAnimationFrame(measureRafRef.current)
}

measureRafRef.current = window.requestAnimationFrame(frameStart => {
measureRafRef.current = null

// A newer flush already started; its own measurement wins.
if (lastFlushAtRef.current !== startedAt) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ describe('reconcileResumeMessages — structural parts on a mid-turn switch', ()

expect(assistant.parts.some(part => part.type === 'reasoning')).toBe(true)
expect(assistant.parts.some(part => part.type === 'tool-call')).toBe(true)
expect(assistant.parts.filter(part => part.type === 'text').map(part => ('text' in part ? part.text : ''))).toEqual([
'partial'
])
expect(assistant.parts.filter(part => part.type === 'text').map(part => ('text' in part ? part.text : ''))).toEqual(
['partial']
)
})

it('does not graft historical structure onto a live text-only row after compression rewrote ordinals', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ describe('preserveLocalPendingTurnMessages', () => {
'does not duplicate the optimistic %s turn when the persisted turn carries its directive',
kind => {
const ref = `@${kind}:X`

const previous = [
msg('1-user', 'user', 'first'),
msg('2-assistant', 'assistant', 'first answer'),
Expand Down Expand Up @@ -771,6 +772,7 @@ describe('preserveLocalPendingTurnMessages', () => {

it('does not duplicate a turn with multiple CRLF directives and Unicode payloads', () => {
const refs = ['@file:`資料/über notes.md`', '@url:`https://example.com/café?q=✓`']

const previous = [
msg('1-user', 'user', 'first'),
msg('2-assistant', 'assistant', 'first answer'),
Expand Down Expand Up @@ -1153,9 +1155,9 @@ describe('appendLiveSessionProjection', () => {
expect(assistants[0].parts.some(part => part.type === 'reasoning')).toBe(true)
expect(assistants[0].parts.some(part => part.type === 'tool-call')).toBe(true)
// Answer text stays the structured row's text, not the dump.
expect(assistants[0].parts.filter(part => part.type === 'text').map(part => ('text' in part ? part.text : ''))).toEqual([
'partial'
])
expect(
assistants[0].parts.filter(part => part.type === 'text').map(part => ('text' in part ? part.text : ''))
).toEqual(['partial'])
})

it('still projects inflight when only a completed historical tool reply has structure', () => {
Expand Down
14 changes: 9 additions & 5 deletions apps/desktop/src/components/pet/floating-pet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export function FloatingPet() {

if (hasPetSpriteForMeta(current, meta)) {
const merged = mergePetInfoMeta(current, meta)

if (merged !== current) {
setPetInfo(merged)
}
Expand Down Expand Up @@ -214,11 +215,14 @@ export function FloatingPet() {
// so no timer. Legacy backend: the historical poll.
const timer = changeEventsAvailable
? null
: window.setInterval(() => {
if (document.visibilityState === 'visible') {
void pull()
}
}, active ? PET_ACTIVE_REFRESH_MS : PET_POLL_MS)
: window.setInterval(
() => {
if (document.visibilityState === 'visible') {
void pull()
}
},
active ? PET_ACTIVE_REFRESH_MS : PET_POLL_MS
)

return () => {
cancelled = true
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/components/ui/status-pulse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const beat = () => {
for (const subscriber of pulseSubscribers) {
subscriber.play()
}

sharedTimer = window.setTimeout(beat, PULSE_PERIOD_MS)
}

Expand All @@ -46,6 +47,7 @@ const handleSharedPauseChange = () => {
for (const subscriber of pulseSubscribers) {
subscriber.cancel()
}

return
}

Expand Down
6 changes: 1 addition & 5 deletions apps/desktop/src/debug/right-pane-events.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
export type RightPanePerfEvent =
| 'project-tree-render'
| 'project-tree-row-render'
| 'terminal-fit-active'
| 'terminal-fit-hidden'
| 'terminal-measure'
'project-tree-render' | 'project-tree-row-render' | 'terminal-fit-active' | 'terminal-fit-hidden' | 'terminal-measure'

export interface RightPanePerfSnapshot {
counts: Record<RightPanePerfEvent, number>
Expand Down
4 changes: 1 addition & 3 deletions apps/desktop/src/debug/right-pane-probe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ if (typeof window !== 'undefined' && !window.__RIGHT_PANE_PERF__) {
},
snapshot: (): RightPanePerfSnapshot => ({
counts: { ...counts },
details: Object.fromEntries(
Object.entries(details).map(([event, eventDetails]) => [event, { ...eventDetails }])
),
details: Object.fromEntries(Object.entries(details).map(([event, eventDetails]) => [event, { ...eventDetails }])),
rows: { ...rows }
}),
start: () => {
Expand Down
5 changes: 1 addition & 4 deletions apps/desktop/src/lib/inflight-turn-journal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,7 @@ describe('recoverInFlightTurnJournal', () => {
})

it('keeps journal answer text when a longer flat dump is not a strict extension (#76444)', () => {
journalEntry([
user('u1', 'do the thing'),
assistantWithTool('assistant-stream-old', 'partial', { pending: true })
])
journalEntry([user('u1', 'do the thing'), assistantWithTool('assistant-stream-old', 'partial', { pending: true })])

const base = [
user('db-u1', 'do the thing'),
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/store/pet-gallery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('pet gallery pet.info sync', () => {

throw new Error(`unexpected method: ${method}`)
})

const request = requestMock as unknown as GatewayRequest

await loadPetGallery(request)
Expand Down Expand Up @@ -113,6 +114,7 @@ describe('pet gallery pet.info sync', () => {

throw new Error(`unexpected method: ${method}`)
})

const request = requestMock as unknown as GatewayRequest

await loadPetGallery(request)
Expand Down Expand Up @@ -147,6 +149,7 @@ describe('pet gallery pet.info sync', () => {

throw new Error(`unexpected method: ${method}`)
})

const request = requestMock as unknown as GatewayRequest

await loadPetGallery(request)
Expand Down Expand Up @@ -195,6 +198,7 @@ describe('pet gallery pet.info sync', () => {

throw new Error(`unexpected method: ${method}`)
})

const request = requestMock as unknown as GatewayRequest

await expect(adoptPet(request, 'boba', 'Could not adopt pet.')).resolves.toBe(true)
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/store/pet-gallery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ export function loadPetGallery(request: GatewayRequest, options: { force?: boole
$petGalleryError.set(null)
localOk = true
}

} catch (e) {
if (isMissingMethod(e)) {
$petGalleryStatus.set('stale')
Expand Down Expand Up @@ -216,6 +215,7 @@ async function syncInfo(request: GatewayRequest): Promise<void> {

if (hasPetSpriteForMeta(current, meta)) {
const merged = mergePetInfoMeta(current, meta)

if (merged !== current) {
setPetInfo(merged)
}
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/store/pet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('pet info metadata cache helpers', () => {
spritesheetBase64: 'large-sprite-payload',
spritesheetRevision: '100:2048'
}

const meta = {
enabled: true,
slug: 'boba',
Expand Down Expand Up @@ -116,6 +117,7 @@ describe('pet info metadata cache helpers', () => {
spritesheetBase64: 'large-sprite-payload',
spritesheetRevision: '100:2048'
}

const meta = {
enabled: true,
slug: 'boba',
Expand Down
3 changes: 1 addition & 2 deletions ui-tui/src/__tests__/scrollBoxRendererBounds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,7 @@ describe('ScrollBox renderer bounds', () => {
expect(nestedTextWrapper?.yogaNode).toBeDefined()

const nestedText = nestedTextWrapper!.childNodes.find(child => child.nodeName === 'ink-text') as
| DOMElement
| undefined
DOMElement | undefined

const nestedTextChild = nestedText?.childNodes[0]

Expand Down
10 changes: 5 additions & 5 deletions ui-tui/src/app/overlayStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ export const $isBlocked = computed(
export const hasFloatingPanel = (overlay: OverlayState): boolean =>
Boolean(
overlay.modelPicker ||
overlay.pager ||
overlay.petPicker ||
overlay.pluginsHub ||
overlay.sessions ||
overlay.skillsHub
overlay.pager ||
overlay.petPicker ||
overlay.pluginsHub ||
overlay.sessions ||
overlay.skillsHub
)

export const $isStatusRuleOccluded = computed([$overlayState, $uiState], (overlay, ui) =>
Expand Down