fix(ui): 浮窗内容区改为列方向 flex 契约,修复插件 tab 内容 0 宽白窗

.floatContent 原为行方向 flex——弹性子项主尺寸(宽)按内容收缩,内在宽度为
0 的插件宿主(如深迹 DeepTrace)塌成 0px 宽:DOM 在、内容画不出(可见白窗
但 DevTools 有内容)。对齐 pane 的内容契约:.floatContent 复制 .paneContent
的列方向 + overflow hidden,浮窗内再包一层 .paneTab(pane 的 per-tab 包装,
flex:1),子项宽度由 align-stretch 撑满——浮窗里的 tab 获得与 pane 内完全
一致的尺寸契约。e2e 增加内容绘制断言(浮动后内容子项实际宽度 > 200px),
真实浏览器回归。
This commit is contained in:
Menghuan1918
2026-08-24 00:35:58 +08:00
parent e20b95024b
commit f648b0c006
3 changed files with 26 additions and 1 deletions
+5 -1
View File
@@ -284,8 +284,12 @@ export function FreeWindow(props: {
anchor={<span />}
/>
</div>
{/* The tab cell reuses .paneTab (the pane's per-tab wrapper): the
column-flex chain (window → .floatContent → .paneTab) is what
gives the component a definite width via align-stretch — the exact
sizing contract a tab gets inside a pane. */}
<div className={css.floatContent}>
{renderTab(float.tab, true, float.id)}
<div className={css.paneTab}>{renderTab(float.tab, true, float.id)}</div>
</div>
<div
className={css.floatResize}
+7
View File
@@ -361,11 +361,18 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
color: var(--dsw-alias-label-primary);
}
/* Mirrors .paneContent's column-flex contract: the tab cell must hand its
child a definite WIDTH via align-stretch. A row flex (the default) sizes
the main axis (width) from content — intrinsic-zero hosts (e.g. a plugin
tab UI) collapse to a 0px-wide sliver and paint nothing (the blank
whale-report window). */
.floatContent {
flex: 1;
min-height: 0;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* The SE resize corner (mirror of .cornerHandle). */
+14
View File
@@ -144,6 +144,18 @@ async function conversationBox(page: Page): Promise<{ x: number; y: number; widt
})
}
/** The float's content cell must hand its child a real width (the
* column-flex contract): a collapsed 0px cell paints nothing — the tab is
* "in" the window but invisible. */
async function assertContentPainted(page: Page, label: string): Promise<void> {
const width = await page.evaluate(() => {
const cell = document.querySelector('[data-dsh-float-window] [class*="floatContent"]')
const child = cell?.firstElementChild
return child === null || child === undefined ? -1 : Math.round(child.getBoundingClientRect().width)
})
expect(width, `${label}: the floated tab's content must have real width`).toBeGreaterThan(200)
}
test('float a tab, move the window, reload restores it, dock it back', async ({ page }) => {
const sidebar = await bootExpanded(page)
@@ -158,6 +170,7 @@ test('float a tab, move the window, reload restores it, dock it back', async ({
await expect(floatWindow).toHaveCount(1, { timeout: 10_000 })
// The tab left the strip with the window born over the conversation area
// (center), well clear of the panel's right edge.
await assertContentPainted(page, 'context-menu float')
const leftBefore = await floatWindow.evaluate((node) => Number.parseFloat((node as HTMLElement).style.left))
expect(Number.isFinite(leftBefore), `style.left must be numeric, got ${leftBefore}`).toBe(true)
expect(leftBefore).toBeGreaterThan(0)
@@ -240,5 +253,6 @@ test('dragging a tab onto the conversation area floats it (drag-out gesture)', a
// its title.
await expect(stripTabs).toHaveCount(1)
await expect(floatWindow.locator('[class*="floatTitle"]')).toHaveText(terminalTitle)
await assertContentPainted(page, 'drag-out float')
await assertNoCrash(page)
})