diff --git a/echo/frontend/docs/button_design_system.md b/echo/frontend/docs/button_design_system.md new file mode 100644 index 00000000..18d3559b --- /dev/null +++ b/echo/frontend/docs/button_design_system.md @@ -0,0 +1,197 @@ +# Button Design System + +This document describes the button design system implemented in the ECHO frontend. + +## Quick Reference + +| Button Type | Mantine Variant | Example Usage | +|-------------|-----------------|---------------| +| **Primary** | `filled` (default) | `` | +| **Secondary** | `outline` | `` | +| **Tertiary** | `subtle` | `` | +| **Disabled** | Any + `disabled` | `` | + +## Design Spec + +### Primary Button (filled) +- **Default**: Institution Blue (`#4169E1`) background, white text, **pill-shaped (fully rounded)** +- **Hover**: Graphite (`#2D2D2C`) background +- **Click/Active**: Graphite (`#2D2D2C`) background +- **Loading**: Graphite (`#2D2D2C`) background with white spinner + +### Secondary Button (outline) +- **Default**: Institution Blue border, Institution Blue text, transparent background, **standard corners** +- **Hover**: 10% Institution Blue background +- **Click/Active**: 20% Institution Blue background + +### Tertiary Button (subtle) +- **Default**: No border, Institution Blue text, transparent background, **standard corners** +- **Hover**: 10% Institution Blue background +- **Click/Active**: 20% Institution Blue background + +### Disabled Button +- **Default**: Gray background, Graphite text +- **Hover**: Gray background + 1px Peach (`#FFD166`) border +- **Click/Active**: Gray background + 1px Salmon (`#FF9AA2`) border +- **Note**: Loading buttons are technically disabled but use their own styling (see Primary/Loading above) + +## Usage Examples + +```tsx +// Primary button (default) + + +// Secondary button + + +// Tertiary button + + +// Disabled button (shows interactive borders) + + +// Loading button (graphite background with spinner) + + +// Custom color (overrides primary) + +``` + +## Brand Colors + +All brand colors are defined in [`src/colors.ts`](../src/colors.ts) as the single source of truth. + +### Available Colors + +| Name | Base Color | Mantine Usage | Tailwind Usage | Purpose | +|------|------------|---------------|----------------|---------| +| **Primary** | `#4169E1` | `color="primary.6"` | `bg-primary-500` | Buttons, links, accents | +| **Cyan** | `#00FFFF` | `color="cyan.6"` | `bg-cyan-500` | Deep Dive mode accent | +| **Graphite** | `#2D2D2C` | `color="graphite.6"` | `bg-graphite` | Text (DM Sans theme) | +| **Lime Yellow** | `#F4FF81` | `color="limeYellow.6"` | `bg-limeYellow-500` | Highlights | +| **Mauve** | `#FFC2FF` | `color="mauve.6"` | `bg-mauve-500` | Accent color | +| **Parchment** | `#F6F4F1` | `color="parchment.6"` | `bg-parchment` | Background (DM Sans theme) | +| **Peach** | `#FFD166` | `color="peach.6"` | `bg-peach-500` | Warnings, alerts | +| **Salmon** | `#FF9AA2` | `color="salmon.6"` | `bg-salmon-500` | Error states | +| **Spring Green** | `#1EFFA1` | `color="springGreen.6"` | `bg-springGreen-500` | Success, Overview mode | + +### Using Brand Colors + +**In Mantine Components:** +```tsx + +Error +Success! +``` + +**In Tailwind Classes:** +```tsx +
+ Content +
+``` + +**In Inline Styles:** +```tsx +import { baseColors } from "@/colors"; + +
+ Content +
+``` + +## Implementation Details + +### File Structure + +- **[`src/colors.ts`](../src/colors.ts)**: Single source of truth for all brand colors +- **[`src/theme.tsx`](../src/theme.tsx)**: Mantine theme configuration with button defaults +- **[`src/styles/button.module.css`](../src/styles/button.module.css)**: Custom button variant styles +- **[`tailwind.config.js`](../../tailwind.config.js)**: Tailwind configuration with brand colors + +### How It Works + +1. **Colors are defined once** in `colors.ts` with 10 shades per color +2. **Mantine imports** `mantineColors` (array format, 0-9 indices) +3. **Tailwind imports** `tailwindColors` (object format, 50-900 keys) +4. **Button styles** are applied via CSS modules attached to the Button component in the theme + +### Default Button Behavior + +All buttons automatically get: +- `color="primary"` (Institution Blue) +- `variant="filled"` (Primary style) + +**Only primary (filled) buttons are pill-shaped.** Secondary and tertiary buttons use standard rounded corners. + +Override these by passing props: +```tsx + + + +``` + +## Exceptions + +The following buttons are exempt from the design system and use custom CSS: +- **Refine button** (custom animated loading state) +- Other buttons with explicit custom `className` or `classNames` props + +## Migration Guide + +### Updating from Old Button Styles + +**Before:** +```tsx + +``` + +**After:** +```tsx + // Uses primary (Institution Blue) by default +``` + +### Reviewing `variant="default"` Usage + +The `default` variant is not part of the design system. Consider replacing with: +- `variant="outline"` for secondary actions +- `variant="subtle"` for low-emphasis actions + +### Adding New Colors + +To add a new brand color: + +1. Add the 10-shade array to `brandColors` in `src/colors.ts` +2. Add to `toTailwindPalette()` conversion in `tailwindColors` +3. Add base color to `baseColors` export +4. Colors will automatically be available in both Mantine and Tailwind + +## Testing + +Test button states in development: +- Hover over buttons to see hover states +- Click buttons to see active states +- Try disabled buttons to see interactive border feedback (Peach on hover, Salmon on click) + +## Questions? + +For questions about the design system, refer to: +- [Frontend Style Guides](../../docs/style-guides/) +- [AGENTS.md](../AGENTS.md) for general frontend patterns +- [COPY_GUIDE.md](../COPY_GUIDE.md) for button text guidelines diff --git a/echo/frontend/package.json b/echo/frontend/package.json index 3b150d25..d85eaae5 100644 --- a/echo/frontend/package.json +++ b/echo/frontend/package.json @@ -23,7 +23,6 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@fontsource-variable/dm-sans": "^5.2.8", "@fontsource-variable/space-grotesk": "^5.2.8", "@formkit/auto-animate": "^0.8.2", "@hookform/resolvers": "^3.10.0", @@ -39,6 +38,7 @@ "@mantine/modals": "^7.17.8", "@mantine/notifications": "^7.17.8", "@mdxeditor/editor": "^3.40.0", + "@phosphor-icons/react": "^2.1.10", "@react-pdf/renderer": "^4.3.0", "@sentry/react": "^8.55.0", "@tabler/icons-react": "^3.34.1", diff --git a/echo/frontend/pnpm-lock.yaml b/echo/frontend/pnpm-lock.yaml index 2118ee4e..479b6cc2 100644 --- a/echo/frontend/pnpm-lock.yaml +++ b/echo/frontend/pnpm-lock.yaml @@ -26,9 +26,6 @@ importers: '@dnd-kit/utilities': specifier: ^3.2.2 version: 3.2.2(react@19.0.0) - '@fontsource-variable/dm-sans': - specifier: ^5.2.8 - version: 5.2.8 '@fontsource-variable/space-grotesk': specifier: ^5.2.8 version: 5.2.8 @@ -74,6 +71,9 @@ importers: '@mdxeditor/editor': specifier: ^3.40.0 version: 3.40.0(@codemirror/language@6.11.2)(@lezer/highlight@1.2.1)(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.22) + '@phosphor-icons/react': + specifier: ^2.1.10 + version: 2.1.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-pdf/renderer': specifier: ^4.3.0 version: 4.3.0(react@19.0.0) @@ -1169,9 +1169,6 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@fontsource-variable/dm-sans@5.2.8': - resolution: {integrity: sha512-AxkvMTvNWgfrmlyjiV05vlHYJa+nRQCf1EfvIrQAPBpFJW0O9VTz7oAFr9S3lvbWdmnFoBk7yFqQL86u64nl2g==} - '@fontsource-variable/space-grotesk@5.2.8': resolution: {integrity: sha512-ei9jNXzZVgBGEBVfHZqPe6F9ZxpPUG8kJYrtlLsivlWJZLCfrfSxcayjnMYAmslEGvvfjth7qybl7PNNqE8ZHw==} @@ -1577,6 +1574,13 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -5610,8 +5614,6 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@fontsource-variable/dm-sans@5.2.8': {} - '@fontsource-variable/space-grotesk@5.2.8': {} '@formkit/auto-animate@0.8.2': {} @@ -6273,6 +6275,11 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@phosphor-icons/react@2.1.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + '@pkgjs/parseargs@0.11.0': optional: true diff --git a/echo/frontend/src/App.tsx b/echo/frontend/src/App.tsx index e6f30bb8..df726c24 100644 --- a/echo/frontend/src/App.tsx +++ b/echo/frontend/src/App.tsx @@ -1,4 +1,3 @@ -import "@fontsource-variable/dm-sans"; import "@fontsource-variable/space-grotesk"; import "@mantine/core/styles.css"; import "@mantine/dropzone/styles.css"; diff --git a/echo/frontend/src/assets/dembrane-logo-new.svg b/echo/frontend/src/assets/dembrane-logo-new.svg new file mode 100644 index 00000000..4881f9b2 --- /dev/null +++ b/echo/frontend/src/assets/dembrane-logo-new.svg @@ -0,0 +1,99 @@ + + + + + + + + + diff --git a/echo/frontend/src/assets/logomark-no-bg.svg b/echo/frontend/src/assets/logomark-no-bg.svg new file mode 100644 index 00000000..d6a7fa6d --- /dev/null +++ b/echo/frontend/src/assets/logomark-no-bg.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/echo/frontend/src/assets/wordmark-no-padding.svg b/echo/frontend/src/assets/wordmark-no-padding.svg new file mode 100644 index 00000000..a18da5cc --- /dev/null +++ b/echo/frontend/src/assets/wordmark-no-padding.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/echo/frontend/src/colors.ts b/echo/frontend/src/colors.ts new file mode 100644 index 00000000..31b40dcb --- /dev/null +++ b/echo/frontend/src/colors.ts @@ -0,0 +1,208 @@ +/** + * Brand Color Palettes + * Single source of truth for colors used in both Mantine and Tailwind + * + * Mantine uses 10-shade arrays (index 0-9, base at index 6) + * Tailwind uses object with keys 50-900 (base at 500) + */ + +// Mantine-style color arrays (10 shades, base at index 6) +export const brandColors = { + // Cyan (base: #00FFFF) + cyan: [ + "#f0ffff", + "#e5ffff", + "#ccffff", + "#99ffff", + "#66ffff", + "#33ffff", + "#00FFFF", // base at position 6 + "#00e6e6", + "#00cccc", + "#00b3b3", + ], + // Graphite (solid - same across all shades) + graphite: [ + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + "#2D2D2C", + ], + // Institution Blue (alias for primary) + institutionBlue: [ + "#f0f5ff", + "#e9f1ff", + "#d4dffe", + "#a8bbf4", + "#7996eb", + "#5176e4", + "#4169e1", // base at position 6 + "#2957df", + "#1a48c6", + "#1040b2", + ], + // Lime Yellow (base: #F4FF81) + limeYellow: [ + "#fefff5", + "#fdfff0", + "#fbffe1", + "#f8ffc3", + "#f6ffa5", + "#f5ff93", + "#F4FF81", // base at position 6 + "#dce674", + "#c4cc67", + "#acb35a", + ], + // Mauve (base: #FFC2FF) + mauve: [ + "#fffaff", + "#fff5ff", + "#ffe8ff", + "#ffd6ff", + "#ffc8ff", + "#ffc5ff", + "#FFC2FF", // base at position 6 + "#e6aee6", + "#cc9acc", + "#b386b3", + ], + // Parchment (solid - same across all shades) + parchment: [ + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + "#F6F4F1", + ], + // Peach (base: #FFD166) + peach: [ + "#fffcf5", + "#fff8ec", + "#fff1d9", + "#ffe3b3", + "#ffd68c", + "#ffc866", + "#FFD166", // base at position 6 + "#e6bc5c", + "#cca752", + "#b39248", + ], + // Primary / Institution Blue (base: #4169E1) + primary: [ + "#f0f5ff", + "#e9f1ff", + "#d4dffe", + "#a8bbf4", + "#7996eb", + "#5176e4", + "#4169e1", // base at position 6 + "#2957df", + "#1a48c6", + "#1040b2", + ], + // Salmon (base: #FF9AA2) + salmon: [ + "#fffafc", + "#fff5f6", + "#ffebec", + "#ffd7da", + "#ffc3c7", + "#ffafb5", + "#FF9AA2", // base at position 6 + "#e68b92", + "#cc7c82", + "#b36d72", + ], + // Spring Green (base: #1EFFA1) + springGreen: [ + "#f0fffb", + "#e8fff5", + "#d1ffeb", + "#a3ffd7", + "#75ffc3", + "#47ffaf", + "#1EFFA1", // base at position 6 + "#1be691", + "#18cc81", + "#15b371", + ], +} as const; + +// Type for Mantine color tuple (10 shades) +export type MantineColorTuple = readonly [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, +]; + +// Mantine-compatible colors export +export const mantineColors: Record = + brandColors as Record; + +/** + * Helper to convert Mantine array (10 shades) to Tailwind object (50-900 keys) + */ +function toTailwindPalette( + colors: readonly string[], +): Record { + const tailwindKeys = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; + const palette: Record = {}; + + colors.forEach((color, index) => { + if (index < tailwindKeys.length) { + palette[tailwindKeys[index]] = color; + } + }); + + // Add DEFAULT as the base color (index 6 = key 500 in Tailwind) + palette.DEFAULT = colors[6]; + + return palette; +} + +// Tailwind-compatible colors export +export const tailwindColors = { + cyan: toTailwindPalette(brandColors.cyan), + graphite: toTailwindPalette(brandColors.graphite), + institutionBlue: toTailwindPalette(brandColors.institutionBlue), + limeYellow: toTailwindPalette(brandColors.limeYellow), + mauve: toTailwindPalette(brandColors.mauve), + parchment: toTailwindPalette(brandColors.parchment), + peach: toTailwindPalette(brandColors.peach), + primary: toTailwindPalette(brandColors.primary), + salmon: toTailwindPalette(brandColors.salmon), + springGreen: toTailwindPalette(brandColors.springGreen), +}; + +// Base color values for quick access (e.g., in CSS-in-JS or inline styles) +export const baseColors = { + cyan: "#00FFFF", + graphite: "#2D2D2C", + institutionBlue: "#4169E1", + limeYellow: "#F4FF81", + mauve: "#FFC2FF", + parchment: "#F6F4F1", + peach: "#FFD166", + primary: "#4169E1", + salmon: "#FF9AA2", + springGreen: "#1EFFA1", +} as const; diff --git a/echo/frontend/src/components/announcement/AnnouncementIcon.tsx b/echo/frontend/src/components/announcement/AnnouncementIcon.tsx index ee7885fa..dc0a9844 100644 --- a/echo/frontend/src/components/announcement/AnnouncementIcon.tsx +++ b/echo/frontend/src/components/announcement/AnnouncementIcon.tsx @@ -1,5 +1,5 @@ import { ActionIcon, Box, Group, Indicator, Loader } from "@mantine/core"; -import { IconSpeakerphone } from "@tabler/icons-react"; +import { FlagBannerIcon } from "@phosphor-icons/react"; import { useAnnouncementDrawer } from "@/components/announcement/hooks"; import { getTranslatedContent } from "@/components/announcement/hooks/useProcessedAnnouncements"; import { Markdown } from "@/components/common/Markdown"; @@ -54,12 +54,14 @@ export const AnnouncementIcon = () => { disabled={(unreadCount || 0) === 0} withBorder > - + {isLoading ? ( ) : ( - )} diff --git a/echo/frontend/src/components/announcement/TopAnnouncementBar.tsx b/echo/frontend/src/components/announcement/TopAnnouncementBar.tsx index 8496bb69..d7ab3b27 100644 --- a/echo/frontend/src/components/announcement/TopAnnouncementBar.tsx +++ b/echo/frontend/src/components/announcement/TopAnnouncementBar.tsx @@ -85,7 +85,7 @@ export function TopAnnouncementBar() { return ( diff --git a/echo/frontend/src/components/chat/ChatAccordion.tsx b/echo/frontend/src/components/chat/ChatAccordion.tsx index 26ac8563..0953c47b 100644 --- a/echo/frontend/src/components/chat/ChatAccordion.tsx +++ b/echo/frontend/src/components/chat/ChatAccordion.tsx @@ -48,8 +48,6 @@ export const ChatModeIndicator = ({ const isOverview = effectiveMode === "overview"; const colors = MODE_COLORS[effectiveMode]; - const iconSize = size === "xs" ? 14 : 16; - return ( {isOverview ? ( - + + + ) : ( - + > + + )} @@ -254,17 +268,12 @@ export const ChatAccordionMain = ({ projectId }: { projectId: string }) => { const chatMode = (item as ProjectChat & { chat_mode?: string }) .chat_mode as "overview" | "deep_dive" | null | undefined; const isActive = item.id === activeChatId; - const effectiveMode = chatMode ?? "deep_dive"; - const activeBorderColor = isActive - ? MODE_COLORS[effectiveMode].border - : undefined; return ( diff --git a/echo/frontend/src/components/chat/ChatModeSelector.tsx b/echo/frontend/src/components/chat/ChatModeSelector.tsx index 3d33807e..cdf69396 100644 --- a/echo/frontend/src/components/chat/ChatModeSelector.tsx +++ b/echo/frontend/src/components/chat/ChatModeSelector.tsx @@ -122,15 +122,14 @@ const ModeCard = ({ {isThisLoading ? ( - + ) : ( - + )} @@ -176,7 +175,7 @@ const ModeCard = ({ diff --git a/echo/frontend/src/components/chat/ChatTemplatesMenu.tsx b/echo/frontend/src/components/chat/ChatTemplatesMenu.tsx index 9a7d0f31..aefbb5d7 100644 --- a/echo/frontend/src/components/chat/ChatTemplatesMenu.tsx +++ b/echo/frontend/src/components/chat/ChatTemplatesMenu.tsx @@ -80,11 +80,7 @@ const SuggestionPill = ({ )} > - + {suggestion.label} diff --git a/echo/frontend/src/components/common/Breadcrumbs.tsx b/echo/frontend/src/components/common/Breadcrumbs.tsx index 22538ab0..beac25e8 100644 --- a/echo/frontend/src/components/common/Breadcrumbs.tsx +++ b/echo/frontend/src/components/common/Breadcrumbs.tsx @@ -30,7 +30,7 @@ export const Breadcrumbs = ({ items }: BreadcrumbsProps) => { } return ( - + {item.label} ); diff --git a/echo/frontend/src/components/common/DembraneLoadingSpinner/index.tsx b/echo/frontend/src/components/common/DembraneLoadingSpinner/index.tsx index dc31c35d..9a187bff 100644 --- a/echo/frontend/src/components/common/DembraneLoadingSpinner/index.tsx +++ b/echo/frontend/src/components/common/DembraneLoadingSpinner/index.tsx @@ -3,7 +3,7 @@ import type React from "react"; import { useEffect, useState } from "react"; import "./DembraneLoading.css"; import { cn } from "@/lib/utils"; -import dembraneLogoHQ from "../../../assets/dembrane-logo-hq.png"; +import { DembraneLogomark } from "../Logo"; interface DembraneLoadingSpinnerProps { isLoading: boolean; @@ -62,7 +62,7 @@ const DembraneLoadingSpinner: React.FC = ({ )} > Spinning Dembrane Logo to indicate loading diff --git a/echo/frontend/src/components/common/Logo.tsx b/echo/frontend/src/components/common/Logo.tsx index 1b343890..473d1486 100644 --- a/echo/frontend/src/components/common/Logo.tsx +++ b/echo/frontend/src/components/common/Logo.tsx @@ -1,37 +1,24 @@ -import { Group, type GroupProps, Title } from "@mantine/core"; +import { Group, type GroupProps } from "@mantine/core"; import aiconlLogo from "@/assets/aiconl-logo.png"; import aiconlLogoHQ from "@/assets/aiconl-logo-hq.png"; -import dembranelogo from "@/assets/dembrane-logo-hq.png"; -import { cn } from "@/lib/utils"; + +import dembraneLogoFull from "@/assets/dembrane-logo-new.svg"; +import dembraneLogomark from "@/assets/logomark-no-bg.svg"; type LogoProps = { hideLogo?: boolean; hideTitle?: boolean; - textAfterLogo?: string | React.ReactNode; } & GroupProps; -export const LogoDembrane = ({ - hideLogo, - hideTitle, - textAfterLogo, - ...props -}: LogoProps) => ( - +export const LogoDembrane = ({ hideLogo, hideTitle, ...props }: LogoProps) => ( + {!hideLogo && ( Dembrane Logo )} - {!hideTitle && ( - - <span className={cn("font-medium", textAfterLogo && "mr-1")}> - Dembrane - </span> - {textAfterLogo && <span>{textAfterLogo}</span>} - - )} ); @@ -61,3 +48,6 @@ export const Logo = (props: LogoProps) => { ); }; + +// Export logomark for use in spinners and loading indicators +export const DembraneLogomark = dembraneLogomark; diff --git a/echo/frontend/src/components/conversation/AutoSelectConversations.tsx b/echo/frontend/src/components/conversation/AutoSelectConversations.tsx index f1f16593..1795771b 100644 --- a/echo/frontend/src/components/conversation/AutoSelectConversations.tsx +++ b/echo/frontend/src/components/conversation/AutoSelectConversations.tsx @@ -163,7 +163,7 @@ export const AutoSelectConversations = () => { diff --git a/echo/frontend/src/components/conversation/ConversationAccordion.tsx b/echo/frontend/src/components/conversation/ConversationAccordion.tsx index e23e3a52..ad3d2bff 100644 --- a/echo/frontend/src/components/conversation/ConversationAccordion.tsx +++ b/echo/frontend/src/components/conversation/ConversationAccordion.tsx @@ -39,7 +39,7 @@ import { IconArrowsUpDown, IconChevronDown, IconChevronUp, - IconRosetteDiscountCheckFilled, + IconRosetteDiscountCheck, IconSearch, IconSelectAll, IconTags, @@ -516,11 +516,6 @@ const ConversationAccordionItem = ({ return null; } - const isLocked = chatContextQuery.data?.conversations?.find( - (c) => c.conversation_id === conversation.id && c.locked, - ); - - const isAutoSelectEnabled = chatContextQuery.data?.auto_select_bool ?? false; const chatMode = chatContextQuery.data?.chat_mode; // Hide checkboxes when: @@ -538,26 +533,10 @@ const ConversationAccordionItem = ({ (artefact) => (artefact as ConversationArtifact).approved_at, ); - // In overview mode, show a subtle "included" indicator - const isOverviewMode = chatMode === "overview"; - - // Mode-based styling - const isDeepDiveWithSelection = - inChatMode && !isNewChatRoute && chatMode === "deep_dive" && isLocked; - return ( - + )} @@ -1091,7 +1070,7 @@ export const ConversationAccordion = ({ }} {...testId("conversation-search-clear-button")} > - + ) } @@ -1106,8 +1085,6 @@ export const ConversationAccordion = ({ setShowFilterActions((prev) => !prev)} aria-label={t`Options`} {...testId("conversation-filter-options-toggle")} @@ -1146,9 +1123,8 @@ export const ConversationAccordion = ({ > - - + )} @@ -1391,7 +1364,7 @@ export const ConversationAccordion = ({ disabled={remainingCount > 0} > - - + {!isStopping && chunks?.data && chunks.data.length > 0 && ( - - + {text.trim() === "" && chunks.data && chunks.data.length > 0 && ( - )} + + <Trans id="participant.ready.to.begin">Ready to Begin?</Trans> + + + + ) : ( + <>
- {React.createElement(currentCard.icon, { - className: "text-blue-500", - size: 64, - })} -
+ {currentCard?.type === "microphone" && ( + + )} + {currentCard.icon && ( +
+ {React.createElement(currentCard.icon, { + className: "text-blue-500", + size: 64, + })} +
+ )} -

- {currentCard.title} -

+ + {currentCard.title} + - {currentCard.content && ( -

{currentCard.content}

- )} + {currentCard.content && ( + + {currentCard.content} + + )} - {currentCard.extraHelp && ( -

{currentCard.extraHelp}

- )} + {currentCard.extraHelp && ( + + {currentCard.extraHelp} + + )} - {currentCard.component && ( -
- -
- )} - - {currentCard.link && ( - - )} - - {currentCard.checkbox && ( -
- + +
+ )} + + {currentCard.link && ( + + {currentCard.link.label} + + )} + + {currentCard.checkbox && ( + - - - )} - + )} + -
- {currentCard?.type === "microphone" ? ( - <> - - - - ) : ( - <> - - {!isLastSlide && ( +
+ {currentCard?.type === "microphone" ? ( + <> + - )} - - )} -
- -
-
- {allSlides.map((slide, index) => ( -
- ))} + + ) : ( + <> + + {!isLastSlide && ( + + )} + + )} +
+ +
+
+ {allSlides.map((slide, index) => ( +
+ ))} +
-
- - )} + + )} +
); }; diff --git a/echo/frontend/src/components/participant/StopRecordingConfirmationModal.tsx b/echo/frontend/src/components/participant/StopRecordingConfirmationModal.tsx index 0dc808f4..0d09bf4a 100644 --- a/echo/frontend/src/components/participant/StopRecordingConfirmationModal.tsx +++ b/echo/frontend/src/components/participant/StopRecordingConfirmationModal.tsx @@ -69,19 +69,17 @@ export const StopRecordingConfirmationModal = ({ onClick={handleClose} disabled={isStopping} miw={100} - radius="md" size="md" {...testId("portal-audio-stop-resume-button")} > Resume diff --git a/echo/frontend/src/components/project/ProjectQRCode.tsx b/echo/frontend/src/components/project/ProjectQRCode.tsx index 8ab5b894..8bc89350 100644 --- a/echo/frontend/src/components/project/ProjectQRCode.tsx +++ b/echo/frontend/src/components/project/ProjectQRCode.tsx @@ -13,6 +13,7 @@ import { import { IconCheck, IconCopy, IconShare } from "@tabler/icons-react"; import { useMemo } from "react"; import { PARTICIPANT_BASE_URL } from "@/config"; +import { useAppPreferences } from "@/hooks/useAppPreferences"; import { testId } from "@/lib/testUtils"; import { QRCode } from "../common/QRCode"; @@ -22,6 +23,8 @@ interface ProjectQRCodeProps { // eslint-disable-next-line react-refresh/only-export-components export const useProjectSharingLink = (project?: Project) => { + const { preferences } = useAppPreferences(); + // biome-ignore lint/correctness/useExhaustiveDependencies: not an issue return useMemo(() => { if (!project) { @@ -58,9 +61,12 @@ export const useProjectSharingLink = (project?: Project) => { | "it-IT" ]; - const link = `${PARTICIPANT_BASE_URL}/${languageCode}/${project.id}/start`; + // Include theme in URL so participant portal uses the same theme + const baseLink = `${PARTICIPANT_BASE_URL}/${languageCode}/${project.id}/start`; + const theme = preferences.fontFamily; + const link = `${baseLink}?theme=${theme}`; return link; - }, [project?.language, project?.id]); + }, [project?.language, project?.id, preferences.fontFamily]); }; export const ProjectQRCode = ({ project }: ProjectQRCodeProps) => { diff --git a/echo/frontend/src/components/project/ProjectSidebar.tsx b/echo/frontend/src/components/project/ProjectSidebar.tsx index 44715c10..6a530e94 100644 --- a/echo/frontend/src/components/project/ProjectSidebar.tsx +++ b/echo/frontend/src/components/project/ProjectSidebar.tsx @@ -9,12 +9,13 @@ import { Title, Tooltip, } from "@mantine/core"; +import { GraphIcon, HouseIcon, QuestionIcon } from "@phosphor-icons/react"; import { useRef } from "react"; import { useLocation, useParams } from "react-router"; import { useInitializeChatModeMutation } from "@/components/chat/hooks"; import { useProjectById } from "@/components/project/hooks"; import { useI18nNavigate } from "@/hooks/useI18nNavigate"; -import { Icons } from "@/icons"; + import { testId } from "@/lib/testUtils"; import { Breadcrumbs } from "../common/Breadcrumbs"; import { I18nLink } from "../common/i18nLink"; @@ -94,7 +95,7 @@ export const ProjectSidebar = () => { variant="transparent" {...testId("project-breadcrumb-home")} > - + ), @@ -140,7 +141,7 @@ export const ProjectSidebar = () => { } + rightIcon={} active={pathname.includes("chat")} {...testId("sidebar-ask-button")} > @@ -150,7 +151,7 @@ export const ProjectSidebar = () => { } + rightIcon={} active={pathname.includes("library")} {...testId("sidebar-library-button")} > diff --git a/echo/frontend/src/components/project/webhooks/WebhookSettingsCard.tsx b/echo/frontend/src/components/project/webhooks/WebhookSettingsCard.tsx index 31a85375..1cb4a33f 100644 --- a/echo/frontend/src/components/project/webhooks/WebhookSettingsCard.tsx +++ b/echo/frontend/src/components/project/webhooks/WebhookSettingsCard.tsx @@ -797,7 +797,7 @@ export const WebhookSection = ({ projectId }: WebhookSectionProps) => { - + + Update Report + + } + > {isPending ? ( @@ -118,6 +127,8 @@ export const UpdateReportModalButton = ({ }} loading={isPending} disabled={isPending} + size="md" + mt="xs" > Update Report diff --git a/echo/frontend/src/components/settings/AuditLogsCard.tsx b/echo/frontend/src/components/settings/AuditLogsCard.tsx index 947e80b4..a11b3bd3 100644 --- a/echo/frontend/src/components/settings/AuditLogsCard.tsx +++ b/echo/frontend/src/components/settings/AuditLogsCard.tsx @@ -395,7 +395,7 @@ export const AuditLogsCard = () => { radius="md" p="lg" className="shadow-sm dark:bg-dark-6" - style={{ backgroundColor: "var(--app-background)" }} + style={{ backgroundColor: "var(--app-background)" }} > @@ -430,7 +430,7 @@ export const AuditLogsCard = () => { )} diff --git a/echo/frontend/src/routes/project/chat/ProjectChatRoute.tsx b/echo/frontend/src/routes/project/chat/ProjectChatRoute.tsx index 069fd667..71d475b5 100644 --- a/echo/frontend/src/routes/project/chat/ProjectChatRoute.tsx +++ b/echo/frontend/src/routes/project/chat/ProjectChatRoute.tsx @@ -600,7 +600,6 @@ export const ProjectChatRoute = () => { )} @@ -166,7 +166,7 @@ export const ProjectLibraryRoute = () => { } >