mirror of
https://github.com/JohnRiceML/clawport-ui.git
synced 2026-08-14 00:47:50 +00:00
- Break CostsPage (1500 lines) into 8 sub-components under components/costs/ - Add sidebar usage widget showing Claude Code 5-hour and weekly utilization - Fix usage API: never cache zeros, retry on 429 with backoff, hide when unavailable - Remove system theme option (dark/glass/color/light only) - Compact sidebar nav (36px items, scrollable nav area, pinned footer) - LiveStreamWidget: always visible as collapsed pill, play/pause on pill - Live logs: add search bar and level filter pills (INF/WRN/ERR/DBG) - Rename "Live Stream" to "Live Logs" throughout - Weekly cap shows reset day instead of countdown - Add PipelineDetailPanel and pipelines API route - Add cron-utils with parseCronExpression and tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback } from 'react';
|
|
import { NavLinks } from '@/components/NavLinks';
|
|
import { ThemeToggle } from '@/components/ThemeToggle';
|
|
import { MobileSidebar } from '@/components/MobileSidebar';
|
|
import { SidebarUsageWidget } from '@/components/sidebar/SidebarUsageWidget';
|
|
import { GlobalSearch, SearchTrigger } from '@/components/GlobalSearch';
|
|
import { useSettings } from '@/app/settings-provider';
|
|
|
|
/**
|
|
* Sidebar -- client wrapper that coordinates desktop sidebar, mobile sidebar,
|
|
* and the Cmd+K search palette. Rendered inside layout.tsx.
|
|
*/
|
|
export function Sidebar() {
|
|
const { settings } = useSettings();
|
|
const openSearch = useCallback(() => {
|
|
// We trigger the search modal by simulating Cmd+K.
|
|
// Instead, we expose a controlled open state via a custom event.
|
|
// The GlobalSearch component listens for this.
|
|
window.dispatchEvent(new CustomEvent('clawport:open-search'));
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
{/* Desktop sidebar — hidden on mobile */}
|
|
<aside
|
|
className="hidden md:flex md:flex-col"
|
|
style={{
|
|
width: '220px',
|
|
flexShrink: 0,
|
|
background: 'var(--sidebar-bg)',
|
|
backdropFilter: 'blur(40px) saturate(180%)',
|
|
WebkitBackdropFilter: 'blur(40px) saturate(180%)',
|
|
borderRight: '1px solid var(--separator)',
|
|
}}
|
|
>
|
|
{/* App icon + title */}
|
|
<div className="px-4 pt-5 pb-3">
|
|
<div className="flex items-center gap-3">
|
|
{settings.portalIcon ? (
|
|
<img
|
|
src={settings.portalIcon}
|
|
alt=""
|
|
style={{
|
|
width: '36px',
|
|
height: '36px',
|
|
borderRadius: '10px',
|
|
objectFit: 'cover',
|
|
boxShadow: 'var(--shadow-card)',
|
|
flexShrink: 0,
|
|
}}
|
|
/>
|
|
) : (
|
|
<img
|
|
src="/clawport-logo.png"
|
|
alt=""
|
|
style={{
|
|
width: '72px',
|
|
height: '72px',
|
|
objectFit: 'contain',
|
|
flexShrink: 0,
|
|
}}
|
|
/>
|
|
)}
|
|
<div>
|
|
<div
|
|
style={{
|
|
fontSize: '17px',
|
|
fontWeight: 600,
|
|
letterSpacing: '-0.3px',
|
|
color: 'var(--text-primary)',
|
|
}}
|
|
>
|
|
{(!settings.portalName || settings.portalName === 'ClawPort')
|
|
? <>Claw<span style={{ color: 'var(--accent)' }}>Port</span></>
|
|
: settings.portalName}
|
|
</div>
|
|
<div
|
|
style={{
|
|
fontSize: '12px',
|
|
color: 'var(--text-secondary)',
|
|
letterSpacing: '0.01em',
|
|
}}
|
|
>
|
|
{settings.portalSubtitle ?? 'Command Centre'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Search trigger */}
|
|
<div className="px-3 pb-2">
|
|
<SearchTrigger onClick={openSearch} />
|
|
</div>
|
|
|
|
<NavLinks bottomSlot={<SidebarUsageWidget />} />
|
|
<ThemeToggle />
|
|
</aside>
|
|
|
|
{/* Mobile sidebar */}
|
|
<MobileSidebar onOpenSearch={openSearch} />
|
|
|
|
{/* Global search modal (Cmd+K) */}
|
|
<GlobalSearch />
|
|
</>
|
|
);
|
|
}
|