fix(ui): keep the saved sidebar width when a narrower sidebar clamps it

The workspace context panels and Favorites share one stored
`sidebar-width` but clamp it to different limits. Loading wrote the
clamped value back, so visiting Settings (max 400) cut a categories
panel widened to 520 down to 400; the saved width could only shrink.

Load now clamps for rendering only. Legacy alias keys migrate their raw
value, an unparseable value falls back to the default without a write,
and only a drag that changes the width persists — a click on the handle
no longer saves the clamped width either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-08 18:34:46 +02:00
1 parent 6c8f5028c7
commit 6edec0fc76
5 files changed
+324 -30

No files matched your search

@@ -0,0 +1,86 @@
import { Locator, Page } from '@playwright/test';
import {
addXtreamPortal,
closeElectronApp,
expect,
launchElectronApp,
openSettings,
openWorkspaceSection,
resetMockServers,
test,
waitForXtreamWorkspaceReady,
} from './electron-test-fixtures';
/**
* The workspace context panels share one stored width but clamp it to their
* own limits. Opening a narrower panel used to save its clamped width, so a
* categories panel widened past Settings' 400px maximum came back at 400.
*/
const SHARED_WIDTH_KEY = 'sidebar-width';
/** The CSS width the directive sets; the panels are content-box. */
function renderedWidth(panel: Locator): Promise<number> {
return panel.evaluate((el) => parseFloat(getComputedStyle(el).width));
}
function storedWidth(page: Page): Promise<string | null> {
return page.evaluate((key) => localStorage.getItem(key), SHARED_WIDTH_KEY);
}
async function dragPanelTo(
page: Page,
panel: Locator,
width: number
): Promise<void> {
const handle = panel.locator(':scope > .resize-handle');
const box = await handle.boundingBox();
if (!box) throw new Error('The panel has no resize handle');
const startX = Math.round(box.x + box.width / 2);
const y = Math.round(box.y + box.height / 2);
// The directive starts a drag from offsetWidth (border box included).
const delta = width - (await panel.evaluate((el) => el.offsetWidth));
await page.mouse.move(startX, y);
await page.mouse.down();
await page.mouse.move(startX + delta, y, { steps: 10 });
await page.mouse.up();
}
test.describe('Electron shared sidebar width', () => {
test('@persistence @electron keeps a widened categories panel after Settings clamps it', async ({
dataDir,
request,
}) => {
await resetMockServers(request, ['xtream']);
const app = await launchElectronApp(dataDir);
try {
const page = app.mainWindow;
await page.setViewportSize({ width: 1440, height: 900 });
await addXtreamPortal(page);
await openWorkspaceSection(page, 'Movies');
await waitForXtreamWorkspaceReady(page);
const categories = page.locator('aside.context-panel--route');
await expect(categories).toBeVisible();
const categoriesUrl = page.url();
await dragPanelTo(page, categories, 520);
await expect.poll(() => renderedWidth(categories)).toBe(520);
await expect.poll(() => storedWidth(page)).toBe('520');
await openSettings(page);
const settings = page.locator('aside.context-panel--settings');
await expect(settings).toBeVisible();
await expect.poll(() => renderedWidth(settings)).toBe(400);
expect(await storedWidth(page)).toBe('520');
await page.goBack();
await page.waitForURL(categoriesUrl);
await expect(categories).toBeVisible();
await expect.poll(() => renderedWidth(categories)).toBe(520);
expect(await storedWidth(page)).toBe('520');
} finally {
await closeElectronApp(app);
}
});
});