Skip to content

Commit 3cd019a

Browse files
hi-ogawaOpenCode
andauthored
fix(ui): persist trace step-iframe split pane sizes (#11027)
Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode <noreply@opencode.ai>
1 parent a6d5ea2 commit 3cd019a

3 files changed

Lines changed: 90 additions & 2 deletions

File tree

‎packages/ui/client/components/trace/TraceView.vue‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script setup lang="ts">
2+
import type { SplitpanesResizedPayload } from 'splitpanes'
23
import type { NormalizedBrowserTraceData, NormalizedBrowserTraceEntry, TraceSelection } from '~/composables/trace-view'
34
import { createCache, createMirror, rebuild } from 'rrweb-snapshot'
45
import { Pane, Splitpanes } from 'splitpanes'
56
import { computed, ref, watch } from 'vue'
67
import { openLocation } from '~/composables/location'
8+
import { traceViewSplitSizes } from '~/composables/navigation'
79
import { getTraceEntryClass, selectActiveTraceStep, showTraceSelectorHighlight } from '~/composables/trace-view'
810
911
const props = defineProps<{
@@ -181,13 +183,20 @@ function formatStepName(step: NormalizedBrowserTraceEntry) {
181183
function isTraceStepInProgress(step: NormalizedBrowserTraceEntry) {
182184
return step.range?.phase === 'start'
183185
}
186+
187+
function onSplitpanesResized({ panes }: SplitpanesResizedPayload) {
188+
if (panes.length === 2) {
189+
traceViewSplitSizes.value = [panes[0].size, panes[1].size]
190+
}
191+
}
184192
</script>
185193

186194
<template>
187195
<Splitpanes
188196
class="h-full min-h-0"
197+
@resized="onSplitpanesResized"
189198
>
190-
<Pane :size="30" min-size="20">
199+
<Pane :size="traceViewSplitSizes[0]" min-size="20">
191200
<div
192201
class="h-full min-h-0 p-4"
193202
flex="~ col gap-1"
@@ -246,7 +255,7 @@ function isTraceStepInProgress(step: NormalizedBrowserTraceEntry) {
246255
</button>
247256
</div>
248257
</Pane>
249-
<Pane :size="70" min-size="20">
258+
<Pane :size="traceViewSplitSizes[1]" min-size="20">
250259
<div class="h-full min-h-0" flex="~ col" overflow-auto>
251260
<iframe
252261
v-if="selectedStep"

‎packages/ui/client/composables/navigation.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const detailSizes = useLocalStorage<[left: number, right: number]>(
3434
67,
3535
],
3636
)
37+
export const traceViewSplitSizes = useLocalStorage<[steps: number, iframe: number]>(
38+
'vitest-ui_splitpanes-traceViewSplitSizes',
39+
[30, 70],
40+
)
3741

3842
export const detailsPanelVisible = useLocalStorage<boolean>(
3943
'vitest-ui_details-panel-visible',

‎test/ui/test/trace.spec.ts‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ test.describe('ui', () => {
7272
test('persists attempt in URL', async ({ page }) => {
7373
await testPersistsAttemptInURL(page)
7474
})
75+
76+
test('persists resized trace panes across reloads', async ({ page }) => {
77+
await testPersistsResizedTracePanes(page)
78+
})
7579
})
7680

7781
test.describe('html reporter', () => {
@@ -154,6 +158,10 @@ test.describe('html reporter', () => {
154158
test('persists attempt in URL', async ({ page }) => {
155159
await testPersistsAttemptInURL(page)
156160
})
161+
162+
test('persists resized trace panes across reloads', async ({ page }) => {
163+
await testPersistsResizedTracePanes(page)
164+
})
157165
})
158166

159167
async function testBasic(page: Page) {
@@ -498,3 +506,70 @@ function getHashParams(page: Page) {
498506
const hash = new URL(page.url()).hash
499507
return Object.fromEntries(new URLSearchParams(hash.split('?')[1]))
500508
}
509+
510+
async function testPersistsResizedTracePanes(page: Page) {
511+
// Opening a trace renders resizable step list and iframe panes.
512+
await openExplorerItem(page, 'simple')
513+
514+
const traceView = page.getByTestId('trace-view')
515+
const traceSteps = traceView.getByRole('listbox', { name: 'Trace steps' })
516+
const splitpanes = traceView.locator('.splitpanes').first()
517+
const splitter = splitpanes.locator('.splitpanes__splitter').first()
518+
await expect(traceView).toBeVisible()
519+
await expect(splitter).toBeVisible()
520+
521+
const initialTraceStepsBox = await traceSteps.boundingBox()
522+
const traceViewBox = await traceView.boundingBox()
523+
const splitterBox = await splitter.boundingBox()
524+
if (!initialTraceStepsBox || !traceViewBox || !splitterBox) {
525+
throw new Error('Trace split panes are not visible')
526+
}
527+
528+
// Resize the step list from its 30% default to roughly 60% and persist both pane sizes.
529+
expect(initialTraceStepsBox.width).toBeLessThan(traceViewBox.width / 2)
530+
await page.mouse.move(
531+
splitterBox.x + splitterBox.width / 2,
532+
splitterBox.y + splitterBox.height / 2,
533+
)
534+
await page.mouse.down()
535+
await page.mouse.move(
536+
traceViewBox.x + traceViewBox.width * 0.6,
537+
splitterBox.y + splitterBox.height / 2,
538+
{ steps: 5 },
539+
)
540+
await page.mouse.up()
541+
542+
// wait for storage update
543+
await expect.poll(async () => (await getStoredTracePaneSizes(page))?.[0]).toBeGreaterThan(55)
544+
const expectedTraceStepsBox = await traceSteps.boundingBox()
545+
if (!expectedTraceStepsBox) {
546+
throw new Error('Trace steps are not visible')
547+
}
548+
// The resize visibly expands the step list without moving it.
549+
expect(expectedTraceStepsBox).toEqual({
550+
x: expect.closeTo(initialTraceStepsBox.x, 1),
551+
y: expect.closeTo(initialTraceStepsBox.y, 1),
552+
width: expect.any(Number),
553+
height: expect.closeTo(initialTraceStepsBox.height, 1),
554+
})
555+
expect(expectedTraceStepsBox.width).toBeGreaterThan(traceViewBox.width / 2)
556+
557+
// Reloading repeatedly preserves the resized trace step geometry.
558+
for (let i = 0; i < 2; i++) {
559+
await page.reload()
560+
await expect(traceView).toBeVisible()
561+
await expect.poll(() => traceSteps.boundingBox()).toEqual({
562+
x: expect.closeTo(expectedTraceStepsBox.x, 1),
563+
y: expect.closeTo(expectedTraceStepsBox.y, 1),
564+
width: expect.closeTo(expectedTraceStepsBox.width, 1),
565+
height: expect.closeTo(expectedTraceStepsBox.height, 1),
566+
})
567+
}
568+
}
569+
570+
function getStoredTracePaneSizes(page: Page): Promise<number[] | null> {
571+
return page.evaluate(() => {
572+
const value = localStorage.getItem('vitest-ui_splitpanes-traceViewSplitSizes')
573+
return value ? JSON.parse(value) : null
574+
})
575+
}

0 commit comments

Comments
 (0)