mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-08 09:24:41 +00:00
fix(app): preserve normalized image URLs in the lightbox
Use the decoder URL spelling when tracking image dimensions so mixed-case hosts still initialize the viewport. Keep the source check against queued loads and wait for refreshed image fitting before asserting geometry.
This commit is contained in:
@@ -146,6 +146,36 @@ test("opens a timeline image in a zoomable lightbox", async ({
|
||||
await expectAgentReadyToInterrupt(page);
|
||||
});
|
||||
|
||||
test("opens a remote Markdown image whose URL needs normalization", async ({
|
||||
imageWorkspace: workspace,
|
||||
page,
|
||||
}) => {
|
||||
const image = await createSmallAssistantPng(workspace, {
|
||||
alt: "Remote image with a mixed-case host",
|
||||
fileName: "remote.png",
|
||||
width: 240,
|
||||
height: 160,
|
||||
});
|
||||
const delivery = await serveDelayedImage(path.join(workspace.repoPath, image.relativePath));
|
||||
const url = delivery.url.replace("127.0.0.1", "LOCALHOST");
|
||||
try {
|
||||
delivery.release();
|
||||
const agent = await createSettledMockAgent(workspace, "Remote image lightbox");
|
||||
await emitSettledAssistantImage(workspace.client, agent, image, url);
|
||||
await openAssistantImageTimeline(page, agent);
|
||||
const rendered = page.getByRole("button", { name: image.alt }).first();
|
||||
await expect(rendered).toHaveJSProperty("naturalWidth", image.width);
|
||||
await rendered.click();
|
||||
await expect(page.getByTestId("attachment-lightbox-canvas")).toBeVisible();
|
||||
await page.getByTestId("zoomable-viewport-toolbar").hover();
|
||||
await page.getByRole("button", { name: "Zoom in", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Close image", exact: true }).click();
|
||||
await expect(page.getByTestId("attachment-lightbox-image")).toHaveCount(0);
|
||||
} finally {
|
||||
await delivery.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("reloading a timeline reserves image height without a load-time scroll jump", async ({
|
||||
imageWorkspace: workspace,
|
||||
page,
|
||||
|
||||
@@ -38,6 +38,15 @@ function editor(page: Page) {
|
||||
return page.getByTestId("file-source-editor").filter({ visible: true }).locator(".cm-content");
|
||||
}
|
||||
|
||||
async function expectTallImageFrame(frame: Locator): Promise<void> {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const box = await frame.boundingBox();
|
||||
return box ? box.height - box.width : 0;
|
||||
})
|
||||
.toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
function hasHorizontalOverflow(element: HTMLElement): boolean {
|
||||
return element.scrollWidth > element.clientWidth;
|
||||
}
|
||||
@@ -806,8 +815,7 @@ test.describe("CodeMirror workspace file editing", () => {
|
||||
await pendingLoad.release();
|
||||
}
|
||||
await expect(imageCanvas).toBeVisible();
|
||||
const tallFrame = await transformedContent.boundingBox();
|
||||
expect(tallFrame!.height).toBeGreaterThan(tallFrame!.width);
|
||||
await expectTallImageFrame(transformedContent);
|
||||
});
|
||||
|
||||
test("renders local relative images inside Markdown preview", async ({ page, withWorkspace }) => {
|
||||
|
||||
@@ -37,14 +37,16 @@ export function ZoomableImage({
|
||||
testID = "zoomable-image",
|
||||
wheelActivation = "always",
|
||||
}: ZoomableImageProps) {
|
||||
const source = useMemo(() => ({ uri: normalizeImageUri(uri) }), [uri]);
|
||||
const [loadedImage, setLoadedImage] = useState<{ uri: string; size: ViewportSize } | null>(null);
|
||||
const resolvedSize = contentSize ?? (loadedImage?.uri === uri ? loadedImage.size : null);
|
||||
const source = useMemo(() => ({ uri }), [uri]);
|
||||
const resolvedSize = contentSize ?? (loadedImage?.uri === source.uri ? loadedImage.size : null);
|
||||
const handleLoad = useCallback(
|
||||
({ source: { url, width, height } }: ImageLoadEventData) => {
|
||||
if (url === uri && width > 0 && height > 0) setLoadedImage({ uri, size: { width, height } });
|
||||
if (url === source.uri && width > 0 && height > 0) {
|
||||
setLoadedImage({ uri: source.uri, size: { width, height } });
|
||||
}
|
||||
},
|
||||
[uri],
|
||||
[source.uri],
|
||||
);
|
||||
|
||||
if (!resolvedSize) {
|
||||
@@ -88,6 +90,15 @@ export function ZoomableImage({
|
||||
);
|
||||
}
|
||||
|
||||
// Use the same URL spelling as the decoder while preserving opaque native URIs.
|
||||
function normalizeImageUri(uri: string): string {
|
||||
try {
|
||||
return new URL(uri).href;
|
||||
} catch {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create(() => ({
|
||||
root: { flex: 1, minHeight: 0 },
|
||||
image: { width: "100%", height: "100%" },
|
||||
|
||||
Reference in New Issue
Block a user