Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ further prompts from the GUI.

## Conversation selection

The converter panel's resize handle supports Left/Right to adjust its width and
Home/End to move to its minimum/maximum. Keyboard and mouse resizing use the
available workspace width, and assistive technology reports the visible width.
Resizing the window or sidebar updates those bounds automatically.

Opening a saved attack with related conversations automatically opens the
desktop conversation drawer. This default applies once per attack; later send
refreshes do not override your open/closed choice. Creating, copying, or repeating
Expand Down
36 changes: 36 additions & 0 deletions frontend/e2e/converters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,42 @@ test.describe("Shared per-piece converter pipelines @seeded", () => {
await expect(page.getByTestId("chat-input")).toBeEnabled();
});

test("should keep keyboard resize bounds aligned with the visible panel after viewport changes", async ({ page }) => {
await page.getByTestId("toggle-converter-panel-btn").click();
const handle = page.getByRole("separator", { name: "Resize converters panel" });
const dimensions = () => handle.evaluate((element: HTMLElement) => {
const panel = element.parentElement;
if (!panel) throw new Error("Converter panel is missing");
return {
width: panel.getBoundingClientRect().width,
current: Number(element.getAttribute("aria-valuenow")),
minimum: Number(element.getAttribute("aria-valuemin")),
maximum: Number(element.getAttribute("aria-valuemax")),
};
});

for (const width of [1440, 800, 390, 1440]) {
await page.setViewportSize({ width, height: 900 });
await expect.poll(async () => {
const size = await dimensions();
return Math.abs(size.current - size.width);
}).toBeLessThan(1);
await handle.press("End");
const maximum = await dimensions();
expect(maximum.current).toBe(maximum.maximum);
expect(maximum.width).toBeCloseTo(maximum.current, 0);
await handle.press("ArrowLeft");
const smaller = await dimensions();
if (maximum.minimum < maximum.maximum) {
expect(smaller.width).toBeLessThan(maximum.width);
} else {
expect(smaller.width).toBeCloseTo(maximum.width, 0);
}
expect(smaller.current).toBeCloseTo(smaller.width, 0);
await expect(handle).toBeFocused();
}
});

test("should align highlighted text through wrapping, scrolling and resizing", async ({ page }) => {
await selectConverter(page, base64Id);
const editor = page.getByRole("textbox", { name: "Working input - Text", exact: true });
Expand Down
21 changes: 21 additions & 0 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import js from "@eslint/js";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import jsxA11y from "eslint-plugin-jsx-a11y";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
Expand Down Expand Up @@ -39,6 +40,26 @@ export default [
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
},
},
// Accessibility checks for production components. Exceptions are disabled
// inline with a reason, so new findings fail lint.
{
files: ["src/**/*.tsx"],
ignores: ["src/**/*.test.tsx"],
plugins: {
"jsx-a11y": jsxA11y,
},
rules: {
...jsxA11y.flatConfigs.recommended.rules,
// Labelled scroll regions and window splitters are meant to take focus.
"jsx-a11y/no-noninteractive-tabindex": [
"error",
{ tags: [], roles: ["tabpanel", "region", "separator"], allowExpressionValues: true },
],
// Media here is user-supplied attack content or target output, so there
// are no caption tracks to provide.
"jsx-a11y/media-has-caption": "off",
},
},
// Test files configuration
{
files: ["**/*.test.{ts,tsx}", "**/setupTests.ts"],
Expand Down
Loading
Loading