diff --git a/canvas/src/components/MemoryInspectorPanel.tsx b/canvas/src/components/MemoryInspectorPanel.tsx index 6c0e0c3b..eac67c65 100644 --- a/canvas/src/components/MemoryInspectorPanel.tsx +++ b/canvas/src/components/MemoryInspectorPanel.tsx @@ -33,6 +33,19 @@ interface Props { // ── Helpers ─────────────────────────────────────────────────────────────────── +/** + * Sanitise a memory key for use in an HTML id attribute. + * HTML IDs must not contain whitespace; many non-alphanumeric characters also + * cause selector or ARIA failures. Replace every non-alphanumeric character + * with a hyphen, collapse consecutive hyphens, then strip leading/trailing ones. + */ +function sanitizeId(key: string): string { + return key + .replace(/[^a-zA-Z0-9]/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); +} + function formatRelativeTime(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); if (diff < 60_000) return `${Math.floor(diff / 1000)}s`; @@ -414,7 +427,7 @@ function MemoryEntryRow({ onCancelEdit, onDelete, }: MemoryEntryRowProps) { - const bodyId = `memory-body-${entry.key.replace(/\s+/g, "-")}`; + const bodyId = `mem-body-${sanitizeId(entry.key)}`; return (
{/* Header row — click to expand/collapse */} diff --git a/canvas/src/components/__tests__/AuthGate.test.tsx b/canvas/src/components/__tests__/AuthGate.test.tsx index 7f581769..75f2d4d0 100644 --- a/canvas/src/components/__tests__/AuthGate.test.tsx +++ b/canvas/src/components/__tests__/AuthGate.test.tsx @@ -19,12 +19,14 @@ beforeEach(() => { }); vi.mock("@/lib/auth", () => ({ - fetchSession: (...args: unknown[]) => mockFetchSession(...args), - redirectToLogin: (...args: unknown[]) => mockRedirectToLogin(...args), + // Cast required: vi.fn() returns Mock which TypeScript + // won't call directly inside a factory closure (TS2348). Cast to Function resolves it. + fetchSession: (...args: unknown[]) => (mockFetchSession as unknown as (...a: unknown[]) => unknown)(...args), + redirectToLogin: (...args: unknown[]) => (mockRedirectToLogin as unknown as (...a: unknown[]) => unknown)(...args), })); vi.mock("@/lib/tenant", () => ({ - getTenantSlug: (...args: unknown[]) => mockGetTenantSlug(...args), + getTenantSlug: (...args: unknown[]) => (mockGetTenantSlug as unknown as (...a: unknown[]) => unknown)(...args), })); // Import after mocks are set up diff --git a/canvas/src/components/__tests__/ProvisioningTimeout.test.tsx b/canvas/src/components/__tests__/ProvisioningTimeout.test.tsx index 432954aa..f1c5b150 100644 --- a/canvas/src/components/__tests__/ProvisioningTimeout.test.tsx +++ b/canvas/src/components/__tests__/ProvisioningTimeout.test.tsx @@ -28,6 +28,7 @@ function makeWS(overrides: Partial & { id: string }): WorkspaceDa y: 0, collapsed: false, runtime: "", + budget_limit: null, ...overrides, }; } diff --git a/canvas/src/components/tabs/ActivityTab.tsx b/canvas/src/components/tabs/ActivityTab.tsx index 68d942a6..74f0d781 100644 --- a/canvas/src/components/tabs/ActivityTab.tsx +++ b/canvas/src/components/tabs/ActivityTab.tsx @@ -80,6 +80,7 @@ export function ActivityTab({ workspaceId }: Props) {
)} {confirmDelete ? ( -
- - +
+

+ Confirm deletion +

+
+ + +
) : (