Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ui/client/src/components/LogEntryLine.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@
.expandable { cursor: pointer; }
.expandable:hover { color: var(--text-primary); }
.expandHint { color: var(--text-muted); font-size: 10px; }

@media (max-width: 768px) {
.line { gap: 6px; font-size: 12px; }
.event { min-width: 60px; font-size: 11px; }
.ts { font-size: 11px; }
}
45 changes: 45 additions & 0 deletions ui/client/src/components/NodeDetail.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,48 @@ details[open] > summary.sectionLabel { margin-bottom: 6px; }
.runStatus.error { background: rgba(203,36,49,0.12); color: var(--red); }
.runMeta { color: var(--text-muted); font-size: 10px; margin-top: 2px; font-variant-numeric: tabular-nums; }
.runHint { color: var(--text-secondary); font-size: 10px; font-family: var(--font-mono); margin-top: 2px; word-break: break-all; }

@media (max-width: 768px) {
/* Keep the node id + close button visible while the user scrolls through
long Lean source / NL bodies on a small screen. */
.header {
position: sticky;
top: 0;
z-index: 5;
padding: 12px 12px 8px;
}
.id { font-size: 14px; }

.section { padding: 10px 12px; }

/* Two-column flag grid keeps each cell wide enough to read on a 375px
viewport. */
.flagsGrid { grid-template-columns: repeat(2, 1fr); }

/* Stack the "Reviewing as" label + input vertically so the input gets the
full row width. */
.reviewerLabel {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
/* font-size ≥ 16px on the actual <input>/<textarea> stops iOS Safari from
auto-zooming when it gains focus. The visual scale is virtually
identical to 14px on Android. */
.reviewerInput,
.reviewCommentBox { font-size: 16px; padding: 8px 10px; }

.reviewBtn {
width: 100%;
padding: 12px;
font-size: 14px;
min-height: 44px;
}

/* Cap the embedded scroll regions to roughly half the viewport so the
review form / comments stay reachable without scrolling past a frozen
content block. */
.rendered { max-height: 50vh; }
.leanSource { max-height: 50vh; font-size: 12px; }
.excerpt { max-height: 40vh; }
}
18 changes: 18 additions & 0 deletions ui/client/src/styles/global.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
:root {
--header-height: 40px;
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #f0f1f3;
Expand Down Expand Up @@ -72,3 +73,20 @@ select {
font-size: 12px;
font-family: var(--font-sans);
}

@media (max-width: 768px) {
body { font-size: 14px; }
.header {
gap: 8px;
padding: 8px 12px;
flex-wrap: wrap;
}
.header h1 { font-size: 13px; }
.header-nav { gap: 0; margin-left: auto; }
.nav-link { padding: 6px 8px; }
.main-content { padding: 12px; }
}

@media (max-width: 480px) {
.project-badge { display: none; }
}
61 changes: 61 additions & 0 deletions ui/client/src/views/LogViewer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,64 @@
.summaryText pre { background: var(--bg-tertiary); padding: 8px; border-radius: 4px; overflow-x: auto; font-size: 12px; margin: 4px 0; }

.emptyContent { padding: 32px; text-align: center; color: var(--text-muted); font-size: 12px; }

/* Mobile-only sidebar toggle. Hidden on desktop. */
.mobileSidebarToggle { display: none; }
.mobileBackdrop { display: none; border: none; padding: 0; }

@media (max-width: 768px) {
.root {
height: 100%;
flex-direction: column;
}

.sidebar {
position: fixed;
top: var(--header-height);
left: 0;
bottom: 0;
width: 280px;
max-width: 80vw;
transform: translateX(-100%);
transition: transform 0.2s ease;
z-index: 20;
background: var(--bg-primary);
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.12);
min-width: 0;
}
.sidebar.mobileSidebarOpen { transform: translateX(0); }

.mobileBackdrop {
display: block;
position: fixed;
top: var(--header-height);
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.32);
z-index: 19;
}

.mobileSidebarToggle {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px 10px;
font-size: 14px;
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary);
cursor: pointer;
}

.toolbar { padding: 6px 8px; gap: 6px; }
.selectedLabel {
max-width: 50vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.filterChip { padding: 4px 10px; font-size: 11px; }
.count { font-size: 10px; }
}
30 changes: 28 additions & 2 deletions ui/client/src/views/LogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ function RunSummaryBar({ entries }: { entries: LogEntry[] }) {
export default function LogViewer() {
const [selectedFile, setSelectedFile] = useState('');
const [selectedFilters, setSelectedFilters] = useState<FilterEvent[]>(DEFAULT_FILTERS);
// Mobile-only: log-list sidebar collapses into a slide-in drawer behind a
// toggle button. Desktop CSS hides the toggle and keeps the sidebar fixed.
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const highlightRef = useRef<HTMLDivElement>(null);

const { data: logsData } = useLogs();
Expand Down Expand Up @@ -152,14 +155,28 @@ export default function LogViewer() {
const showSessionSummary = !!sessionEnd && selectedFilterSet.has('session_end');
const selectedLabel = selectedFile.replace(/\.jsonl$/, '').replace(/\//g, ' / ');

// Wrap setSelectedFile so picking a log on mobile auto-closes the drawer.
const handleSelectFile = (path: string) => {
setSelectedFile(path);
setMobileSidebarOpen(false);
};

return (
<div className={styles.root}>
<div className={styles.sidebar}>
{mobileSidebarOpen && (
<button
type="button"
className={styles.mobileBackdrop}
onClick={() => setMobileSidebarOpen(false)}
aria-label="Close log list"
/>
)}
<div className={`${styles.sidebar} ${mobileSidebarOpen ? styles.mobileSidebarOpen : ''}`}>
{Array.from(agentGroups.entries()).map(([agentName, groups]) => (
<div key={agentName} className={styles.agentSection}>
<div className={styles.agentHeader}>{agentName}</div>
{groups.map(g => (
<RunGroup key={g.id} group={g} selectedFile={selectedFile} onSelect={setSelectedFile} />
<RunGroup key={g.id} group={g} selectedFile={selectedFile} onSelect={handleSelectFile} />
))}
</div>
))}
Expand All @@ -171,6 +188,15 @@ export default function LogViewer() {

<div className={styles.main}>
<div className={styles.toolbar}>
<button
type="button"
className={styles.mobileSidebarToggle}
onClick={() => setMobileSidebarOpen(v => !v)}
aria-expanded={mobileSidebarOpen}
aria-label="Toggle log list"
>
</button>
<span className={styles.selectedLabel}>{selectedLabel || 'Select a log'}</span>
<div className={styles.filterBar} aria-label="Event type filters">
<span className={styles.filterLabel}>Show</span>
Expand Down
80 changes: 80 additions & 0 deletions ui/client/src/views/Nodes.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,83 @@
.drawerResize:active {
background: rgba(3, 102, 214, 0.25);
}

/* Mobile-only filter toggle. Hidden on desktop where the sidebar is
permanently docked. */
.mobileFilterToggle { display: none; }
.mobileBackdrop { display: none; border: none; padding: 0; }

@media (max-width: 768px) {
.root { grid-template-columns: 1fr; }

/* Sidebar transforms into an off-canvas drawer. Anchored below the app
header via --header-height. */
.sidebar {
position: fixed;
top: var(--header-height);
left: 0;
bottom: 0;
width: 280px;
max-width: 80vw;
transform: translateX(-100%);
transition: transform 0.2s ease;
z-index: 20;
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.12);
}
.sidebar.mobileSidebarOpen { transform: translateX(0); }

.mobileBackdrop {
display: block;
position: fixed;
top: var(--header-height);
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.32);
z-index: 19;
}

.mobileFilterToggle {
display: inline-flex;
align-items: center;
gap: 4px;
position: absolute;
top: 8px;
left: 8px;
z-index: 12;
padding: 6px 12px;
font-size: 12px;
font-weight: 500;
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-primary);
cursor: pointer;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

/* Larger touch targets for filter chips. */
.chip { padding: 6px 12px; font-size: 12px; gap: 6px; }
.search { padding: 8px 10px; font-size: 14px; }

/* Detail drawer becomes a fixed fullscreen sheet — position:fixed is
relative to the visual viewport so it renders at 1x regardless of any
pinch-zoom the user applied to the graph canvas. */
.drawer {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100% !important;
z-index: 30;
}
.drawerResize { display: none; }

.canvasOverlay {
font-size: 10px;
padding: 4px 8px;
bottom: 8px;
left: 8px;
}
}
Loading
Loading