From 6b4b5002bb7a974fe6614de05cc44c0dc58de972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Mart=C3=ADnez?= Date: Fri, 7 Aug 2026 20:19:23 +0200 Subject: [PATCH 01/12] fix(ui): stabilize locale routing and keyboard card navigation --- src/__tests__/middleware.test.ts | 34 +++++++++++++ src/app/globals.css | 50 ++++++++++++++++++- .../ConfirmDialog/ConfirmDialog.tsx | 4 +- src/components/ContextMenu/ContextMenu.tsx | 4 +- src/components/CopyToast/CopyToast.tsx | 2 +- .../CreateSnippetModal/CreateSnippetModal.tsx | 4 +- .../CreatedSnippetToast.tsx | 2 +- .../FormatErrorToast/FormatErrorToast.tsx | 2 +- src/components/MarkdownPreview/LinkDialog.tsx | 6 +-- .../MarkdownPreview/MarkdownEditorInner.tsx | 4 +- src/components/MobileHome/MobileBottomBar.tsx | 2 +- .../PreferencesDialog/PreferencesDialog.tsx | 4 +- .../SearchPalette/SearchPalette.tsx | 4 +- .../ShortcutsDialog/ShortcutsDialog.tsx | 4 +- src/components/SnippetCards/SnippetCard.tsx | 2 +- src/components/SnippetCards/SnippetCards.tsx | 9 ++-- src/middleware.ts | 14 +++++- src/ui/Tooltip.tsx | 2 +- 18 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 src/__tests__/middleware.test.ts diff --git a/src/__tests__/middleware.test.ts b/src/__tests__/middleware.test.ts new file mode 100644 index 0000000..35f5833 --- /dev/null +++ b/src/__tests__/middleware.test.ts @@ -0,0 +1,34 @@ +import { NextRequest } from "next/server"; +import { describe, expect, it } from "vitest"; + +import { middleware } from "@/middleware"; + +function request(pathname: string, headers?: HeadersInit) { + return new NextRequest(`http://example.test${pathname}`, { headers }); +} + +describe("locale middleware", () => { + it("rewrites the clean English app URL without redirecting", () => { + const response = middleware(request("/app")); + + expect(response.headers.get("x-middleware-rewrite")).toBe("http://example.test/en/app"); + expect(response.headers.get("location")).toBeNull(); + expect(response.headers.get("x-middleware-request-x-klipcode-internal-locale")).toBe("en"); + }); + + it("does not redirect the internal English rewrite back to the clean URL", () => { + const response = middleware( + request("/en/app", { "x-klipcode-internal-locale": "en" }), + ); + + expect(response.headers.get("x-middleware-next")).toBe("1"); + expect(response.headers.get("location")).toBeNull(); + }); + + it("keeps explicit English URLs redirecting to their clean equivalent", () => { + const response = middleware(request("/en/app")); + + expect(response.status).toBe(308); + expect(response.headers.get("location")).toBe("http://example.test/app"); + }); +}); diff --git a/src/app/globals.css b/src/app/globals.css index 0549582..2dea3d0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3,8 +3,7 @@ /* ─── Z-index scale ────────────────────────────────────────────────────────── Single source of truth for stacking order. Tippy.js poppers default to z-index 9999 — keep every framework-managed floating layer *below* the - dialog layer so Tippy never renders above a modal. Consumers use - `z-[var(--z-*)]` instead of hardcoding literal values. */ + dialog layer so Tippy never renders above a modal. */ :root { --z-base: 0; --z-tooltip: 100; @@ -99,6 +98,53 @@ --scrollbar-thumb-hover: #a1a1a6; } +/* Semantic utilities for values that Tailwind cannot safely infer from + arbitrary-value syntax. Keeping these in CSS also prevents the production + scanner from treating CSS variable names as class candidates. */ +.klipcode-z-tooltip { + z-index: var(--z-tooltip); +} + +.klipcode-z-menu { + z-index: var(--z-menu); +} + +.klipcode-z-popover { + z-index: var(--z-popover); +} + +.klipcode-z-toast { + z-index: var(--z-toast); +} + +.klipcode-z-dialog { + z-index: var(--z-dialog); +} + +.klipcode-z-dialog-sticky { + z-index: var(--z-dialog-sticky); +} + +.klipcode-z-dialog-menu { + z-index: var(--z-dialog-menu); +} + +.klipcode-scrim { + background-color: var(--scrim); +} + +.klipcode-code-surface { + background-color: var(--code-surface); +} + +.klipcode-popover-shadow { + box-shadow: var(--popover-shadow); +} + +.klipcode-safe-area-bottom { + padding-bottom: calc(0.625rem + env(safe-area-inset-bottom)); +} + @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); diff --git a/src/components/ConfirmDialog/ConfirmDialog.tsx b/src/components/ConfirmDialog/ConfirmDialog.tsx index e93314b..708cbb4 100644 --- a/src/components/ConfirmDialog/ConfirmDialog.tsx +++ b/src/components/ConfirmDialog/ConfirmDialog.tsx @@ -47,12 +47,12 @@ export function ConfirmDialog({ {/* Backdrop */}