mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
Polish file viewer UX review punchlist (PAP-1962)
Addresses UX review feedback on PAP-1963: - Fix denial-body leak for too_large/size codes (describeDenial no longer echoes fallback for known codes; the call site passes empty fallback so unknown codes show a generic message). - Sticky line-number gutter with solid code-bg fill; highlight border moves from absolute-positioned sibling onto the sticky gutter so the amber marker stays pinned when users scroll long lines horizontally. - Swap FileSearch copy-link icon for Link2. - WorkspaceFileLink now sets a real deep-link href so modifier-click opens the file viewer in a new tab instead of scrolling to the top. - Collapse duplicate title/description in the sheet header when the basename equals the full path (e.g. .env.production). - Drop the duplicate title/helper block from OpenFilePrompt (sheet header already carries the title + description). - Replace ArrowLeft with Cloud for the remote-workspace state. - Retire arbitrary text-[12.5px]/[11px]/[0.78em] sizes in favor of text-xs. - Unify 404 copy on "active workspace" everywhere. - Raise chip-row contrast by using bg-muted instead of bg-background. Also commits previously untracked visual-regression infra: - Storybook stories for 12 file viewer states - screenshot-file-viewer.mjs / screenshot-one.mjs - describeDenial unit tests Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
4bad0cf17f
commit
9b58daba6e
8 files changed
+710
-39
No files matched your search
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
// Screenshots every FileViewerSheet Storybook state at desktop + mobile viewports.
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { chromium } from "@playwright/test";
|
||||
|
||||
const BASE = "http://localhost:6006/iframe.html";
|
||||
const OUT_DIR = path.resolve(process.argv[2] || "/tmp/pap-1963");
|
||||
|
||||
const SHOTS = [
|
||||
{ id: "components-file-viewer-sheet--text-content-with-highlighted-line", label: "content-highlighted-line" },
|
||||
{ id: "components-file-viewer-sheet--text-content-long-path-truncation", label: "content-long-path" },
|
||||
{ id: "components-file-viewer-sheet--open-file-prompt", label: "open-file-prompt" },
|
||||
{ id: "components-file-viewer-sheet--loading-spinner", label: "loading-spinner" },
|
||||
{ id: "components-file-viewer-sheet--error-not-found-with-fallback", label: "error-not-found" },
|
||||
{ id: "components-file-viewer-sheet--error-no-workspace", label: "error-no-workspace" },
|
||||
{ id: "components-file-viewer-sheet--error-outside-workspace", label: "error-outside-workspace" },
|
||||
{ id: "components-file-viewer-sheet--error-denied-sensitive", label: "error-denied" },
|
||||
{ id: "components-file-viewer-sheet--remote-workspace", label: "remote-workspace" },
|
||||
{ id: "components-file-viewer-sheet--workspace-archived", label: "workspace-archived" },
|
||||
{ id: "components-file-viewer-sheet--binary-unsupported", label: "binary-unsupported" },
|
||||
{ id: "components-file-viewer-sheet--too-large-to-preview", label: "too-large" },
|
||||
];
|
||||
|
||||
const VIEWPORTS = [
|
||||
{ name: "desktop", width: 1440, height: 900 },
|
||||
{ name: "mobile", width: 390, height: 844 },
|
||||
];
|
||||
|
||||
async function run() {
|
||||
await fs.mkdir(OUT_DIR, { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
try {
|
||||
for (const vp of VIEWPORTS) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: vp.width, height: vp.height },
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
const page = await context.newPage();
|
||||
for (const shot of SHOTS) {
|
||||
const url = `${BASE}?id=${shot.id}&viewMode=story&args=`;
|
||||
console.log(`[${vp.name}] → ${shot.label}`);
|
||||
await page.goto(url, { waitUntil: "domcontentloaded" });
|
||||
// Give the sheet animation + potential network stubs ~1.5s to settle.
|
||||
await page.waitForTimeout(shot.label === "loading-spinner" ? 900 : 1600);
|
||||
const out = path.join(OUT_DIR, `${vp.name}-${shot.label}.png`);
|
||||
await page.screenshot({ path: out, fullPage: false });
|
||||
}
|
||||
// Extra capture: mobile view story (its own viewport story) as a cross-check.
|
||||
if (vp.name === "mobile") {
|
||||
await page.goto(
|
||||
`${BASE}?id=components-file-viewer-sheet--mobile-view&viewMode=story&args=`,
|
||||
{ waitUntil: "domcontentloaded" },
|
||||
);
|
||||
await page.waitForTimeout(1600);
|
||||
await page.screenshot({
|
||||
path: path.join(OUT_DIR, `mobile-story-variant.png`),
|
||||
fullPage: false,
|
||||
});
|
||||
}
|
||||
await context.close();
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
console.log(`\nScreenshots written to ${OUT_DIR}`);
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env node
|
||||
// Screenshot a single story id at desktop 1440x900.
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { chromium } from "@playwright/test";
|
||||
|
||||
const [, , storyId, out, widthArg, heightArg, waitArg] = process.argv;
|
||||
const width = Number(widthArg) || 1440;
|
||||
const height = Number(heightArg) || 900;
|
||||
const waitMs = Number(waitArg) || 2500;
|
||||
const url = `http://localhost:6006/iframe.html?id=${storyId}&viewMode=story`;
|
||||
|
||||
await fs.mkdir(path.dirname(path.resolve(out)), { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
try {
|
||||
const ctx = await browser.newContext({
|
||||
viewport: { width, height },
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
const page = await ctx.newPage();
|
||||
await page.goto(url, { waitUntil: "networkidle" });
|
||||
await page.waitForTimeout(waitMs);
|
||||
await page.screenshot({ path: out, fullPage: false });
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
console.log(`Wrote ${out}`);
|
||||
Reference in new issue
Block a user