From f29658fee8c6a06df3ed3d9a67362e074ac571ac Mon Sep 17 00:00:00 2001 From: AI Christianson Date: Fri, 14 Mar 2025 08:54:24 -0400 Subject: [PATCH] ui styling --- frontend/common/dist/styles/global.css | 137 ++++++----- .../common/src/components/SessionDrawer.tsx | 26 +- .../common/src/components/SessionSidebar.tsx | 4 +- .../common/src/components/TimelineFeed.tsx | 134 +++-------- .../common/src/components/TimelineStep.tsx | 39 ++- frontend/common/src/components/ui/sheet.tsx | 4 +- frontend/web/src/components/Layout.tsx | 99 ++++++++ frontend/web/src/index.tsx | 224 +++++++++--------- 8 files changed, 366 insertions(+), 301 deletions(-) create mode 100644 frontend/web/src/components/Layout.tsx diff --git a/frontend/common/dist/styles/global.css b/frontend/common/dist/styles/global.css index 13af638..7e46c9e 100644 --- a/frontend/common/dist/styles/global.css +++ b/frontend/common/dist/styles/global.css @@ -623,17 +623,24 @@ video { .top-4 { top: 1rem; } +.z-40 { + z-index: 40; +} .z-50 { z-index: 50; } +.mx-auto { + margin-left: auto; + margin-right: auto; +} .mb-2 { margin-bottom: 0.5rem; } -.mb-4 { - margin-bottom: 1rem; +.mb-5 { + margin-bottom: 1.25rem; } -.mr-2 { - margin-right: 0.5rem; +.mr-1 { + margin-right: 0.25rem; } .mr-3 { margin-right: 0.75rem; @@ -644,8 +651,8 @@ video { .mt-1\.5 { margin-top: 0.375rem; } -.mt-3 { - margin-top: 0.75rem; +.mt-4 { + margin-top: 1rem; } .mt-6 { margin-top: 1.5rem; @@ -659,9 +666,6 @@ video { .inline-flex { display: inline-flex; } -.hidden { - display: none; -} .h-10 { height: 2.5rem; } @@ -671,6 +675,9 @@ video { .h-3 { height: 0.75rem; } +.h-3\.5 { + height: 0.875rem; +} .h-4 { height: 1rem; } @@ -689,30 +696,27 @@ video { .h-full { height: 100%; } -.h-screen { - height: 100vh; -} .w-2\.5 { width: 0.625rem; } .w-3 { width: 0.75rem; } +.w-3\.5 { + width: 0.875rem; +} .w-3\/4 { width: 75%; } .w-4 { width: 1rem; } -.w-5 { - width: 1.25rem; +.w-8 { + width: 2rem; } .w-9 { width: 2.25rem; } -.w-\[250px\] { - width: 250px; -} .w-\[85\%\] { width: 85%; } @@ -722,8 +726,8 @@ video { .min-w-0 { min-width: 0px; } -.max-w-xs { - max-width: 20rem; +.max-w-md { + max-width: 28rem; } .flex-1 { flex: 1 1 0%; @@ -766,12 +770,14 @@ video { .justify-between { justify-content: space-between; } -.gap-2 { - gap: 0.5rem; -} .gap-4 { gap: 1rem; } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} .space-y-1\.5 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(0.375rem * calc(1 - var(--tw-space-y-reverse))); @@ -787,12 +793,17 @@ video { margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} +.overflow-auto { + overflow: auto; +} .overflow-hidden { overflow: hidden; } -.overflow-x-auto { - overflow-x: auto; -} .truncate { overflow: hidden; text-overflow: ellipsis; @@ -804,9 +815,6 @@ video { .whitespace-pre-wrap { white-space: pre-wrap; } -.rounded { - border-radius: 0.25rem; -} .rounded-\[inherit\] { border-radius: inherit; } @@ -840,9 +848,15 @@ video { .border-t { border-top-width: 1px; } +.border-dashed { + border-style: dashed; +} .border-border { border-color: hsl(var(--border)); } +.border-border\/50 { + border-color: hsl(var(--border) / 0.5); +} .border-input { border-color: hsl(var(--input)); } @@ -874,6 +888,9 @@ video { .bg-card { background-color: hsl(var(--card)); } +.bg-card\/50 { + background-color: hsl(var(--card) / 0.5); +} .bg-destructive { background-color: hsl(var(--destructive)); } @@ -908,6 +925,9 @@ video { .p-4 { padding: 1rem; } +.p-5 { + padding: 1.25rem; +} .p-6 { padding: 1.5rem; } @@ -930,21 +950,22 @@ video { padding-left: 2rem; padding-right: 2rem; } +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; } +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; } -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} -.pb-2 { - padding-bottom: 0.5rem; -} .pt-0 { padding-top: 0px; } @@ -981,6 +1002,9 @@ video { .leading-none { line-height: 1; } +.leading-relaxed { + line-height: 1.625; +} .tracking-tight { letter-spacing: -0.025em; } @@ -996,6 +1020,9 @@ video { .text-muted-foreground { color: hsl(var(--muted-foreground)); } +.text-muted-foreground\/50 { + color: hsl(var(--muted-foreground) / 0.5); +} .text-primary { color: hsl(var(--primary)); } @@ -1034,12 +1061,17 @@ video { --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} +.ring-ring\/20 { + --tw-ring-color: hsl(var(--ring) / 0.2); +} .ring-offset-background { --tw-ring-offset-color: hsl(var(--background)); } -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} .backdrop-blur-sm { --tw-backdrop-blur: blur(4px); -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); @@ -1120,6 +1152,9 @@ video { .hover\:bg-accent:hover { background-color: hsl(var(--accent)); } +.hover\:bg-accent\/30:hover { + background-color: hsl(var(--accent) / 0.3); +} .hover\:bg-accent\/50:hover { background-color: hsl(var(--accent) / 0.5); } @@ -1141,6 +1176,11 @@ video { .hover\:opacity-100:hover { opacity: 1; } +.hover\:shadow-md:hover { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} .focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; @@ -1188,6 +1228,11 @@ video { .disabled\:opacity-50:disabled { opacity: 0.5; } +.group:hover .group-hover\:scale-110 { + --tw-scale-x: 1.1; + --tw-scale-y: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} .data-\[state\=checked\]\:translate-x-4[data-state="checked"] { --tw-translate-x: 1rem; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -1318,20 +1363,4 @@ video { .sm\:text-left { text-align: left; } -} -@media (min-width: 768px) { - - .md\:block { - display: block; - } - - .md\:hidden { - display: none; - } -} -@media (min-width: 1024px) { - - .lg\:w-\[300px\] { - width: 300px; - } } \ No newline at end of file diff --git a/frontend/common/src/components/SessionDrawer.tsx b/frontend/common/src/components/SessionDrawer.tsx index 67fbf65..ad3596e 100644 --- a/frontend/common/src/components/SessionDrawer.tsx +++ b/frontend/common/src/components/SessionDrawer.tsx @@ -1,14 +1,11 @@ import React from 'react'; -import { Menu } from 'lucide-react'; import { Sheet, - SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetClose } from './ui/sheet'; -import { Button } from './ui/button'; import { ScrollArea } from './ui/scroll-area'; import { AgentSession, getSampleAgentSessions } from '../utils/sample-data'; @@ -16,12 +13,16 @@ interface SessionDrawerProps { onSelectSession?: (sessionId: string) => void; currentSessionId?: string; sessions?: AgentSession[]; + isOpen?: boolean; + onClose?: () => void; } export const SessionDrawer: React.FC = ({ onSelectSession, currentSessionId, - sessions = getSampleAgentSessions() + sessions = getSampleAgentSessions(), + isOpen = false, + onClose }) => { // Get status color const getStatusColor = (status: string) => { @@ -48,19 +49,16 @@ export const SessionDrawer: React.FC = ({ }; return ( - - - - - + + Sessions - -
+ +
{sessions.map((session) => ( -
- -
- {filterTypes.map(type => ( - - ))} -
+
+
+ {sortedSteps.length > 0 ? ( + sortedSteps.map((step) => ( + + )) + ) : ( +
+ + + +

No steps to display

+
+ )}
- - -
- {sortedSteps.length > 0 ? ( - sortedSteps.map((step) => ( - - )) - ) : ( -
- No steps to display -
- )} -
-
); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/common/src/components/TimelineStep.tsx b/frontend/common/src/components/TimelineStep.tsx index 52522fa..45df5d8 100644 --- a/frontend/common/src/components/TimelineStep.tsx +++ b/frontend/common/src/components/TimelineStep.tsx @@ -47,34 +47,47 @@ export const TimelineStep: React.FC = ({ step }) => { }; return ( - - -
-
-
{getTypeIcon(step.type)}
+ + +
+
+
{getTypeIcon(step.type)}
-
{step.title}
-
+
{step.title}
+
{step.type === 'tool-execution' ? 'Run tool' : step.content.substring(0, 60)} {step.content.length > 60 ? '...' : ''}
- {formatTime(step.timestamp)} + {formatTime(step.timestamp)} {step.duration && ( - {(step.duration / 1000).toFixed(1)}s + + {(step.duration / 1000).toFixed(1)}s + )}
-
-
+
+
{step.content}
{step.duration && ( -
-
+
+
+ + + + Duration: {(step.duration / 1000).toFixed(1)} seconds
diff --git a/frontend/common/src/components/ui/sheet.tsx b/frontend/common/src/components/ui/sheet.tsx index e8b5a68..41ef9a9 100644 --- a/frontend/common/src/components/ui/sheet.tsx +++ b/frontend/common/src/components/ui/sheet.tsx @@ -19,7 +19,7 @@ const SheetOverlay = React.forwardRef< >(({ className, ...props }, ref) => ( = ({ header, sidebar, drawer, children }) => { + return ( + <> + + +
+
+ {header} +
+ + {sidebar && ( + + )} + + {/* Mobile drawer - rendered outside grid */} + {drawer} + +
+ {children} +
+
+ + ); +}; \ No newline at end of file diff --git a/frontend/web/src/index.tsx b/frontend/web/src/index.tsx index f60a342..e482dc7 100644 --- a/frontend/web/src/index.tsx +++ b/frontend/web/src/index.tsx @@ -8,6 +8,7 @@ import { getSampleAgentSessions, getSampleAgentSteps } from '@ra-aid/common'; +import { Layout } from './components/Layout'; // The CSS import happens through the common package's index.ts // Theme management helper function @@ -85,122 +86,125 @@ const App = () => { localStorage.setItem('theme', newIsDark ? 'dark' : 'light'); }; - return ( -
- {/* Header */} -
-
-

- RA-Aid -

- -
- {/* Theme toggle button */} - - - {/* Mobile drawer toggle - show only on small screens */} -
- -
-
-
-
- - {/* Main content */} -
- {/* Desktop sidebar - hidden on mobile */} - - - {/* Mobile drawer */} - - - {/* Main content area */} -
- {selectedSessionId ? ( - <> -

- Session: {sessions.find(s => s.id === selectedSessionId)?.name || 'Unknown'} -

- - + + + + + + + + + + ) : ( -
-

Select a session to view details

-
+ // Moon icon for dark mode toggle + + + )} -
+ + + {/* Mobile drawer toggle - show only on small screens */} +
+ +
- -
-

Built with shadcn/ui components from the RA-Aid common package

-
); + + // Render sidebar content + const sidebarContent = ( + + ); + + // Render drawer + const drawerContent = ( + setIsDrawerOpen(false)} + /> + ); + + // Render main content + const mainContent = ( + selectedSessionId ? ( + <> +

+ Session: {sessions.find(s => s.id === selectedSessionId)?.name || 'Unknown'} +

+ + + ) : ( +
+

Select a session to view details

+
+ ) + ); + + return ( + + {mainContent} + + ); }; // Initialize theme before rendering the app