diff --git a/.bumpversion.toml b/.bumpversion.toml index 2ed8293..f6ee76a 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.2.0" +current_version = "0.2.1" commit = true tag = true tag_name = "v{new_version}" diff --git a/VERSION b/VERSION index 0ea3a94..0c62199 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.2.1 diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index 7d165c6..e60a508 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -58,6 +58,7 @@ services: LANGFLOW__BASE_URL: http://localhost:7860/api/v1 LANGFLOW__API_KEY: ${LANGFLOW_API_KEY:-dev-langflow-api-key} SECRET_KEY: dev-secret-key-change-in-production + CORS_ORIGINS: '["http://localhost:5173", "http://localhost:8000"]' volumes: - ../backend:/app depends_on: @@ -79,6 +80,8 @@ services: volumes: - ../frontend:/app - /app/node_modules + # Comment the following line out if you don't want to mount the local UX package - this requires cloning the UX repository. + - ../../ux:/app/node_modules/@tidemark-security/ux depends_on: - backend command: ["npm", "run", "dev", "--", "--host", "0.0.0.0"] @@ -125,7 +128,7 @@ services: LANGFLOW_AUTO_LOGIN: "false" LANGFLOW_SUPERUSER: ${LANGFLOW_SUPERUSER:-admin} LANGFLOW_SUPERUSER_PASSWORD: ${LANGFLOW_SUPERUSER_PASSWORD:-admin} - LANGFLOW_SECRET_KEY: ${LANGFLOW_SECRET_KEY:-dev-langflow-secret-change-in-production} + LANGFLOW_SECRET_KEY: ${LANGFLOW_SECRET_KEY:-3R1HFctPJZ_MDJg-GQe2Z_TaEyZyXQZtbcCR5l8S0E4=} LANGFLOW_NEW_USER_IS_ACTIVE: "false" LANGFLOW_ENABLE_SUPERUSER_CLI: "false" LANGFLOW_API_KEY_SOURCE: env diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9f26813..c56d2e0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "intercept", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "intercept", - "version": "0.2.0", + "version": "0.2.1", "dependencies": { "@lexical/rich-text": "^0.35.0", "@mdxeditor/editor": "^3.53.1", diff --git a/frontend/package.json b/frontend/package.json index fcadb68..b688026 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "intercept", "private": true, - "version": "0.2.0", + "version": "0.2.1", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/overlays/ModalShell.tsx b/frontend/src/components/overlays/ModalShell.tsx index ba91c96..204f3bd 100644 --- a/frontend/src/components/overlays/ModalShell.tsx +++ b/frontend/src/components/overlays/ModalShell.tsx @@ -1,26 +1,49 @@ import React from "react"; +import { Dialog } from "@tidemark-security/ux"; +import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; + import { cn } from "@/utils/cn"; interface ModalShellProps { children: React.ReactNode; + title: string; + description?: string; panelClassName?: string; contentClassName?: string; + onClose?: () => void; } export function ModalShell({ children, + title, + description, panelClassName, contentClassName, + onClose, }: ModalShellProps) { return ( -
-
{ + if (!open) onClose?.(); + }} + className="p-4" + > + + + {title} + + {description && ( + + {description} + + )}
{children}
-
-
+ + ); } diff --git a/frontend/src/contexts/SessionProvider.tsx b/frontend/src/contexts/SessionProvider.tsx index e66f51f..2210688 100644 --- a/frontend/src/contexts/SessionProvider.tsx +++ b/frontend/src/contexts/SessionProvider.tsx @@ -163,9 +163,8 @@ export const SessionProvider = ({ children }: SessionProviderProps) => { dispatch({ type: "START_AUTH" }); try { if (!browserSupportsPasskeys()) { - dispatch({ type: "SET_ERROR", payload: "Passkey sign-in is unavailable on this browser." }); dispatch({ type: "SET_UNAUTHENTICATED" }); - return "failed"; + return "password_required"; } const begin = await AuthenticationService.beginPasskeyAuthenticationApiV1AuthPasskeysAuthenticateOptionsPost({ diff --git a/frontend/src/index.css b/frontend/src/index.css index 564e0a2..ebcb136 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -3,148 +3,17 @@ @tailwind utilities; /** - * Z-Index Layering System - * - * Defines a consistent z-index hierarchy for the application: - * - Dropdowns and popovers inside modals appear above modal content - * - Modals obscure page-level dropdowns - * - Tooltips always appear on top + * App-specific styles for Tidemark Intercept. + * Shared tokens (CSS variables, animations, bevels) come from + * @tidemark-security/ux/tokens.css — imported in main.tsx before index.css. */ -:root { - --z-dropdown: 50; - --z-sticky: 100; - --z-modal-backdrop: 1000; - --z-modal: 1001; - --z-popover: 1002; - --z-tooltip: 2000; - - --color-default-font: 200 200 200; - --color-subtext-color: 165 165 165; - --color-neutral-border: 65 65 65; - --color-focus-border: 208 255 0; - --color-default-shadow: 208 255 0; - --color-scrollbar-thumb: 208 255 0; - --color-default-background: 10 10 10; - --color-page-background: 25 25 25; - --color-brand-secondary: 10 15 13; - --color-brand-secondary-blush: 5 12 9; - --color-brand-primary-blush: 208 255 0; - --color-accent-3-primary-blush: 42 30 92; - --color-accent-2-primary-blush: 255 0 85; - --color-accent-1-primary-blush: 0 255 217; - - --color-neutral-0: 10 10 10; - --color-neutral-50: 23 23 23; - --color-neutral-100: 38 38 38; - --color-neutral-200: 64 64 64; - --color-neutral-300: 82 82 82; - --color-neutral-400: 115 115 115; - --color-neutral-500: 163 163 163; - --color-neutral-600: 212 212 212; - --color-neutral-700: 229 229 229; - --color-neutral-800: 245 245 245; - --color-neutral-900: 250 250 250; - --color-neutral-1000: 255 255 255; - color-scheme: dark; -} - -:root[data-theme="light"] { - --color-default-font: 23 23 23; - --color-subtext-color: 82 82 82; - --color-neutral-border: 212 212 212; - --color-focus-border: 10 10 10; - --color-default-shadow: 10 10 10; - --color-scrollbar-thumb: 10 10 10; - --color-default-background: 245 245 245; - --color-page-background: 30 30 30; - --color-brand-secondary: 250 250 250; - --color-brand-secondary-blush: 250 250 250; - - --color-neutral-0: 255 255 255; - --color-neutral-50: 250 250 250; - --color-neutral-100: 245 245 245; - --color-neutral-200: 229 229 229; - --color-neutral-300: 212 212 212; - --color-neutral-400: 163 163 163; - --color-neutral-500: 115 115 115; - --color-neutral-600: 82 82 82; - --color-neutral-700: 64 64 64; - --color-neutral-800: 38 38 38; - --color-neutral-900: 23 23 23; - --color-neutral-1000: 10 10 10; - color-scheme: light; -} - -@media (prefers-color-scheme: light) { - :root:not([data-theme]) { - --color-default-font: 23 23 23; - --color-subtext-color: 82 82 82; - --color-neutral-border: 212 212 212; - --color-focus-border: 10 10 10; - --color-default-shadow: 10 10 10; - --color-scrollbar-thumb: 10 10 10; - --color-default-background: 255 255 255; - --color-page-background: 245 245 245; - --color-brand-secondary: 250 250 250; - --color-brand-secondary-blush: 250 250 250; - - --color-neutral-0: 255 255 255; - --color-neutral-50: 250 250 250; - --color-neutral-100: 245 245 245; - --color-neutral-200: 229 229 229; - --color-neutral-300: 212 212 212; - --color-neutral-400: 163 163 163; - --color-neutral-500: 115 115 115; - --color-neutral-600: 82 82 82; - --color-neutral-700: 64 64 64; - --color-neutral-800: 38 38 38; - --color-neutral-900: 23 23 23; - --color-neutral-1000: 10 10 10; - color-scheme: light; - } -} - -/** - * Lucide Icon Sizing Fix - * - * Lucide React icons default to 24x24px via inline attributes, but we need them - * to inherit font-size from parent elements. - * - * This makes all Lucide icons use 1em sizing, so they respect text-* classes: - * - text-body (14px) → 14px icon - * - text-heading-3 (16px) → 16px icon - * - text-heading-2 (20px) → 20px icon - * - * For icons that need explicit pixel sizes, use the size prop on the Lucide component: - * - for 24px icon - * - for 16px icon - * - * Note: Tailwind h-* w-* classes do NOT work on Lucide icons because Lucide sets - * inline width/height attributes. Use the size prop instead. - */ -svg.lucide { - width: 1em !important; - height: 1em !important; -} html { - font-family: "Saira", sans-serif; - font-size: 16px; height: 100vh; display: flex; flex-direction: column; } -/* Page transition animation */ -@keyframes pageEnter { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - /* View transitions API for modern browsers */ @view-transition { navigation: auto; @@ -168,11 +37,6 @@ html { to { opacity: 1; } } -/* Content transition class - apply to dynamic content that appears/disappears */ -.content-fade-in { - animation: pageEnter 0.15s ease-out; -} - .prose { font-size: 14px; max-width: 1024px; @@ -182,8 +46,6 @@ body { display: flex; flex-direction: column; flex-grow: 1; - background-color: rgb(var(--color-default-background)); - color: rgb(var(--color-default-font)); } #root { @@ -191,65 +53,6 @@ body { animation: pageEnter 0.2s ease-out; } -@keyframes aiCaretBlink { - 0%, 45% { - opacity: 1; - } - 50%, 100% { - opacity: 0; - } -} - -@keyframes aiScanlineSweep { - 0% { - transform: translateX(-115%); - opacity: 0.35; - } - 12% { - opacity: 1; - } - 100% { - transform: translateX(265%); - opacity: 0.35; - } -} - -.ai-caret { - animation: aiCaretBlink 0.9s steps(1, end) infinite; -} - -.ai-scanline-track { - position: relative; - width: 100%; - height: 10px; - overflow: hidden; - background-color: rgb(var(--color-neutral-300)); -} - -.ai-scanline { - position: absolute; - top: 0; - left: 0; - width: 45%; - height: 100%; - mask-image: repeating-linear-gradient( - 135deg, - transparent, - transparent 8px, - black 8px, - black 16px - ); - -webkit-mask-image: repeating-linear-gradient( - 135deg, - transparent, - transparent 8px, - black 8px, - black 16px - ); - animation: aiScanlineSweep 4s linear infinite; -} - - * { scrollbar-color: rgb(var(--color-scrollbar-thumb)) transparent !important; } @@ -260,153 +63,4 @@ body { ::-webkit-scrollbar-thumb { background-color: rgb(var(--color-scrollbar-thumb)); -} - -/* Beveled corner effect using CSS corner-shape */ -.bevel-tr-none { - corner-top-right-shape: bevel; - border-top-right-radius: 0px; -} -.bevel-tr-sm { - corner-top-right-shape: bevel; - border-top-right-radius: 0.125rem; /* 2px */ -} -.bevel-tr { - corner-top-right-shape: bevel; - border-top-right-radius: 0.25rem; /* 4px */ -} -.bevel-tr-md { - corner-top-right-shape: bevel; - border-top-right-radius: 0.375rem; /* 6px */ -} -.bevel-tr-lg { - corner-top-right-shape: bevel; - border-top-right-radius: 0.5rem; /* 8px */ -} -.bevel-tr-xl { - corner-top-right-shape: bevel; - border-top-right-radius: 0.75rem; /* 12px */ -} -.bevel-tr-2xl { - corner-top-right-shape: bevel; - border-top-right-radius: 1rem; /* 16px */ -} -.bevel-tr-3xl { - corner-top-right-shape: bevel; - border-top-right-radius: 1.5rem; /* 24px */ -} -.bevel-tr-full { - corner-top-right-shape: bevel; - border-top-right-radius: 9999px; -} - -.bevel-tl-none { - corner-top-left-shape: bevel; - border-top-left-radius: 0px; -} -.bevel-tl-sm { - corner-top-left-shape: bevel; - border-top-left-radius: 0.125rem; /* 2px */ -} -.bevel-tl { - corner-top-left-shape: bevel; - border-top-left-radius: 0.25rem; /* 4px */ -} -.bevel-tl-md { - corner-top-left-shape: bevel; - border-top-left-radius: 0.375rem; /* 6px */ -} -.bevel-tl-lg { - corner-top-left-shape: bevel; - border-top-left-radius: 0.5rem; /* 8px */ -} -.bevel-tl-xl { - corner-top-left-shape: bevel; - border-top-left-radius: 0.75rem; /* 12px */ -} -.bevel-tl-2xl { - corner-top-left-shape: bevel; - border-top-left-radius: 1rem; /* 16px */ -} -.bevel-tl-3xl { - corner-top-left-shape: bevel; - border-top-left-radius: 1.5rem; /* 24px */ -} -.bevel-tl-full { - corner-top-left-shape: bevel; - border-top-left-radius: 9999px; -} - -.bevel-br-none { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0px; -} -.bevel-br-sm { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0.125rem; /* 2px */ -} -.bevel-br { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0.25rem; /* 4px */ -} -.bevel-br-md { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0.375rem; /* 6px */ -} -.bevel-br-lg { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0.5rem; /* 8px */ -} -.bevel-br-xl { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 0.75rem; /* 12px */ -} -.bevel-br-2xl { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 1rem; /* 16px */ -} -.bevel-br-3xl { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 1.5rem; /* 24px */ -} -.bevel-br-full { - corner-bottom-right-shape: bevel; - border-bottom-right-radius: 9999px; -} - -.bevel-bl-none { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0px; -} -.bevel-bl-sm { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0.125rem; /* 2px */ -} -.bevel-bl { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0.25rem; /* 4px */ -} -.bevel-bl-md { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0.375rem; /* 6px */ -} -.bevel-bl-lg { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0.5rem; /* 8px */ -} -.bevel-bl-xl { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 0.75rem; /* 12px */ -} -.bevel-bl-2xl { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 1rem; /* 16px */ -} -.bevel-bl-3xl { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 1.5rem; /* 24px */ -} -.bevel-bl-full { - corner-bottom-left-shape: bevel; - border-bottom-left-radius: 9999px; } \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 6f3c34a..cc4a05f 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import App from "./App.tsx"; import "./index.css"; +import "@tidemark-security/ux/tokens.css"; import { ThemeProvider } from "./contexts/ThemeContext"; import { TimezoneProvider } from "./contexts/TimezoneContext"; import { VisualFilterProvider } from "./contexts/VisualFilterContext"; diff --git a/frontend/src/pages/AdminLinkTemplates.tsx b/frontend/src/pages/AdminLinkTemplates.tsx index fa4b072..da9e0a9 100644 --- a/frontend/src/pages/AdminLinkTemplates.tsx +++ b/frontend/src/pages/AdminLinkTemplates.tsx @@ -3,6 +3,7 @@ import { Badge } from "@/components/data-display/Badge"; import { Button } from "@/components/buttons/Button"; import { DropdownMenu } from "@/components/overlays/DropdownMenu"; import { IconButton } from "@/components/buttons/IconButton"; +import { ModalShell } from "@/components/overlays"; import { Table } from "@/components/data-display/Table"; import { TextField } from "@/components/forms/TextField"; import { Toast } from "@/components/feedback/Toast"; @@ -388,9 +389,12 @@ function AdminLinkTemplates() { {/* Create/Edit Modal */} {showModal && ( -
-
-
+ {/* Modal Header */}
@@ -597,9 +601,7 @@ function AdminLinkTemplates() { {isEditing ? "Save Changes" : "Create Template"}
-
-
-
+ )} {/* Toast Notifications */} diff --git a/frontend/src/pages/AdminSettings.tsx b/frontend/src/pages/AdminSettings.tsx index 522ffd6..b6c6a59 100644 --- a/frontend/src/pages/AdminSettings.tsx +++ b/frontend/src/pages/AdminSettings.tsx @@ -1110,7 +1110,7 @@ function AdminSettings() {
) : null} -
+
General -
+

@@ -1158,7 +1158,7 @@ function AdminSettings() {

System

-
+

@@ -1229,7 +1229,7 @@ function AdminSettings() {

Identity Providers

-
+

@@ -1435,7 +1435,7 @@ function AdminSettings() { {/* LangFlow Settings Section */}
{/* LangFlow Setup Wizard */}
@@ -1446,7 +1446,7 @@ function AdminSettings() {
-
+
Setup Langflow for Intercept @@ -1457,6 +1457,7 @@ function AdminSettings() {