diff --git a/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx b/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx index dd570c47..6fcbda95 100644 --- a/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx +++ b/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx @@ -139,7 +139,7 @@ export const ChatHeader = ({ panelId }: { panelId?: string }) => { if (s.id === sessionId) setSessionId(null); void deleteSession(s.id); }} - className="p-0.5 opacity-50 hover:opacity-100 text-red-500 cursor-pointer" + className="p-0.5 opacity-50 hover:opacity-100 text-destructive cursor-pointer" > diff --git a/src/workspaces/officerdev/src/apps/Chat/components/CodeSurface.tsx b/src/workspaces/officerdev/src/apps/Chat/components/CodeSurface.tsx index 323d1132..38d63ee2 100644 --- a/src/workspaces/officerdev/src/apps/Chat/components/CodeSurface.tsx +++ b/src/workspaces/officerdev/src/apps/Chat/components/CodeSurface.tsx @@ -131,7 +131,7 @@ export const CodeSurface = ({ code, lang = '', startLine = 1, maxLines, numbered aria-label="Copy the code" className={CONTROL} > - {copied === 'raw' ? : } + {copied === 'raw' ? : } ); }; diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/AgentRunnerModal.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/AgentRunnerModal.tsx index a5b71754..cd34af44 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/AgentRunnerModal.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/AgentRunnerModal.tsx @@ -160,7 +160,7 @@ export const AgentRunnerModal = ({ open, onOpenChange, agent, entryName, entryFu )} {error && ( -
+
{error}
diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/DictateDialog.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/DictateDialog.tsx index a8edfa39..11edb806 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/DictateDialog.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/DictateDialog.tsx @@ -187,7 +187,7 @@ export const DictateDialog = ({ fileBrowserManager }: DictateDialogProps) => {
- {recording && } + {recording && } {recording ? 'Recording...' : 'Starting...'}
diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx index 3611c39d..80572b80 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx @@ -413,7 +413,7 @@ const MenuItems = ({ )} - onDelete(entry)} className="text-red-600 cursor-pointer"> + onDelete(entry)} className="text-destructive cursor-pointer"> Delete diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/NewEntryDialog.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/NewEntryDialog.tsx index b38dc25b..f6b0b14f 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/NewEntryDialog.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/NewEntryDialog.tsx @@ -138,7 +138,7 @@ export const NewEntryDialog = ({ kind, onClose, onSubmit, existingNames }: NewEn /> {/* Reserves its own line whether or not there is a problem, so the dialog does not jump as you type past a colliding name. */} -

{problem ?? ''}

+

{problem ?? ''}

diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/TaskRunnerModal.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/TaskRunnerModal.tsx index 39040675..0f3ce561 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/TaskRunnerModal.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/TaskRunnerModal.tsx @@ -240,18 +240,18 @@ const AgenticTaskRunner = ({ {phase === 'running' ? ( ) : accRef.current.some((m) => m.role === 'error') ? ( - + Task failed ) : ( - + Task complete @@ -544,9 +544,7 @@ const AudioMetaPanel = ({ meta, fileName }: { meta: AudioMeta; fileName: string {dur ? {dur} : null} Lyrics: {meta.hasLyrics ? 'Yes' : 'No'} @@ -1268,7 +1266,7 @@ const ScriptRunner = ({ -
+          
             
               {runner.output.map((line, i) => (
                 
             
             Stop
           
         ) : runner.exitCode !== 0 ? (
-          
+          
             
             Task failed (exit {runner.exitCode})
           
         ) : (
-          
+          
             
             Task complete
           
@@ -1528,7 +1526,7 @@ const PipelineRunner = ({ taskDirName, context, cwd, onClose }: PipelineRunnerPr
             {pipeline.currentStep.status === 'running' && (
               
             )}
-            {pipeline.currentStep.status === 'complete' && done}
+            {pipeline.currentStep.status === 'complete' && done}
           
)} @@ -1541,12 +1539,12 @@ const PipelineRunner = ({ taskDirName, context, cwd, onClose }: PipelineRunnerPr {pDone}/{pTotal} done {pRunning > 0 && · {pRunning} running} - {pError > 0 && · {pError} failed} + {pError > 0 && · {pError} failed} {pDone + pError < pTotal && ( )} - {pDone + pError === pTotal && pTotal > 0 && done} + {pDone + pError === pTotal && pTotal > 0 && done} )} @@ -1577,10 +1575,10 @@ const PipelineRunner = ({ taskDirName, context, cwd, onClose }: PipelineRunnerPr )} {it.status === 'running' && } - {it.status === 'complete' && } - {it.status === 'error' && } + {it.status === 'complete' && } + {it.status === 'error' && } {it.label} @@ -1590,7 +1588,7 @@ const PipelineRunner = ({ taskDirName, context, cwd, onClose }: PipelineRunnerPr )} {it.error && ( - + {it.error} )} @@ -1618,18 +1616,18 @@ const PipelineRunner = ({ taskDirName, context, cwd, onClose }: PipelineRunnerPr {pipeline.phase === 'running' ? ( ) : pipeline.hasError ? ( - + Pipeline failed ) : ( - + Pipeline complete diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/Toolbar/SelectionActions.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/Toolbar/SelectionActions.tsx index b6308148..0c8647c0 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/Toolbar/SelectionActions.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/Toolbar/SelectionActions.tsx @@ -56,7 +56,7 @@ export const SelectionActions = ({ fileBrowserManager }: SelectionActionsProps) diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/VideoDownloadDialog.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/VideoDownloadDialog.tsx index 5539661d..50d11480 100644 --- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/VideoDownloadDialog.tsx +++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/VideoDownloadDialog.tsx @@ -249,7 +249,7 @@ export const VideoDownloadDialog = ({ fileBrowserManager }: VideoDownloadDialogP {e.status === 'loading' ? ( ) : e.status === 'error' ? ( - + ) : e.thumbnail && !audioOnly ? ( ) : ( @@ -265,7 +265,7 @@ export const VideoDownloadDialog = ({ fileBrowserManager }: VideoDownloadDialogP ) : e.status === 'error' ? ( <> -

Could not fetch

+

Could not fetch

{e.error || e.url}

) : ( @@ -276,7 +276,7 @@ export const VideoDownloadDialog = ({ fileBrowserManager }: VideoDownloadDialogP

{[e.uploader, fmtDuration(e.duration)].filter(Boolean).join(' · ')}

- {e.dl === 'error' &&

{e.dlError}

} + {e.dl === 'error' &&

{e.dlError}

} )} diff --git a/src/workspaces/officerdev/src/apps/FileViewer/FileViewerBody.tsx b/src/workspaces/officerdev/src/apps/FileViewer/FileViewerBody.tsx index e0eedfbd..4aa9816a 100644 --- a/src/workspaces/officerdev/src/apps/FileViewer/FileViewerBody.tsx +++ b/src/workspaces/officerdev/src/apps/FileViewer/FileViewerBody.tsx @@ -43,7 +43,7 @@ export const FileViewerBody = () => { // action that still works, and it is offered rather than left to be guessed at.
- {error} + {error}
- { if (error) { return (
- Failed to load image + Failed to load image
); } diff --git a/src/workspaces/officerdev/src/apps/FileViewer/renderers/VideoRenderer.tsx b/src/workspaces/officerdev/src/apps/FileViewer/renderers/VideoRenderer.tsx index a6015275..8005bf27 100644 --- a/src/workspaces/officerdev/src/apps/FileViewer/renderers/VideoRenderer.tsx +++ b/src/workspaces/officerdev/src/apps/FileViewer/renderers/VideoRenderer.tsx @@ -213,7 +213,7 @@ export const VideoRenderer = ({ src, fileName, filePath, root }: VideoRendererPr if (error) { return (
- Failed to load video + Failed to load video
); } @@ -227,9 +227,22 @@ export const VideoRenderer = ({ src, fileName, filePath, root }: VideoRendererPr if (playing) setShowControls(false); }} > -
{/* Raw output is dimmer and indented under the step that produced it — present when you want it, @@ -165,7 +165,7 @@ const ActionLog = ({ ))} - {result?.error ?
{result.error}
: null} + {result?.error ?
{result.error}
: null}
); }; @@ -309,7 +309,7 @@ const Purge = ({ plugin, busy, onPurge }: { plugin: PluginItem; busy: boolean; o type="button" disabled={busy} onClick={() => setConfirming(true)} - className="cursor-pointer text-xs text-duck-dark/40 underline underline-offset-2 transition-colors hover:text-red-600 disabled:opacity-40" + className="cursor-pointer text-xs text-duck-dark/40 underline underline-offset-2 transition-colors hover:text-destructive disabled:opacity-40" > Delete {plugin.label} and all its data @@ -318,7 +318,7 @@ const Purge = ({ plugin, busy, onPurge }: { plugin: PluginItem; busy: boolean; o } return ( -
+

Delete {plugin.label} completely?

  • diff --git a/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx b/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx index 9da7ce61..9cbeebc4 100644 --- a/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx +++ b/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx @@ -13,7 +13,7 @@ const Status = ({ plugin }: { plugin: PluginItem }) => { if (!plugin.installed) return not installed; if (!plugin.enabled) return disabled; if (plugin.outdated) return update available; - return enabled; + return enabled; }; export const PluginsList = () => { @@ -97,10 +97,10 @@ export const PluginsList = () => { manifest looks exactly like a plugin nobody wrote. */} {broken.map((b) => (
    - +
    {b.appName}
    -
    {b.error}
    +
    {b.error}
    ))} diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/BtopView.tsx b/src/workspaces/officerdev/src/apps/SystemMonitor/BtopView.tsx index 8f1f5094..fbef980b 100644 --- a/src/workspaces/officerdev/src/apps/SystemMonitor/BtopView.tsx +++ b/src/workspaces/officerdev/src/apps/SystemMonitor/BtopView.tsx @@ -10,29 +10,53 @@ type Stats = { uptimeSec: number; loadavg: number[]; cpu: { model: string; cores: number; usagePct: number; perCore: number[] }; - mem: { totalBytes: number; usedBytes: number; freeBytes: number; usedPct: number; swapTotalBytes: number; swapUsedBytes: number } | null; + mem: { + totalBytes: number; + usedBytes: number; + freeBytes: number; + usedPct: number; + swapTotalBytes: number; + swapUsedBytes: number; + } | null; disks: { mount: string; fsType: string; totalBytes: number; usedBytes: number; usedPct: number }[]; processes: { pid: number; user: string; cpuPct: number; memPct: number; command: string }[]; temp: { cpuC: number | null; cpuLabel: string | null; sensors: Sensor[] } | null; gpu: { busyPct: number; vramUsedBytes: number; vramTotalBytes: number } | null; - net: { rxBytesPerSec: number; txBytesPerSec: number; interfaces: { name: string; rxBytesPerSec: number; txBytesPerSec: number }[] } | null; + net: { + rxBytesPerSec: number; + txBytesPerSec: number; + interfaces: { name: string; rxBytesPerSec: number; txBytesPerSec: number }[]; + } | null; power: { cpuWatts: number | null; gpuWatts: number | null } | null; timestamp: number; }; const POLL_MS = 2000; -const barColor = (pct: number) => (pct >= 85 ? 'bg-red-500' : pct >= 60 ? 'bg-amber-500' : 'bg-emerald-500'); -const tempColor = (c: number) => (c >= 80 ? 'text-red-500' : c >= 65 ? 'text-amber-500' : 'text-emerald-500'); +const barColor = (pct: number) => (pct >= 85 ? 'bg-destructive' : pct >= 60 ? 'bg-amber-500' : 'bg-success'); +const tempColor = (c: number) => (c >= 80 ? 'text-destructive' : c >= 65 ? 'text-amber-500' : 'text-success'); const fmtRate = (bps: number) => `${fmtBytes(bps)}/s`; const Bar = ({ pct }: { pct: number }) => (
    -
    +
    ); -const Card = ({ title, icon: Icon, children, className }: { title: string; icon: typeof Cpu; children: React.ReactNode; className?: string }) => ( +const Card = ({ + title, + icon: Icon, + children, + className, +}: { + title: string; + icon: typeof Cpu; + children: React.ReactNode; + className?: string; +}) => (
    @@ -77,12 +101,16 @@ export const BtopView = () => {
    up {fmtDuration(stats.uptimeSec * 1000)} load {stats.loadavg.map((l) => l.toFixed(2)).join(' ')} - +
    )}
    - {error &&
    {error}
    } + {error && ( +
    + {error} +
    + )} {stats && ( <> @@ -93,7 +121,9 @@ export const BtopView = () => { {stats.cpu.cores} cores
    -

    {stats.cpu.model}

    +

    + {stats.cpu.model} +

    {stats.cpu.perCore.map((c, i) => (
    @@ -130,8 +160,12 @@ export const BtopView = () => { {stats.disks.map((d) => (
    - {d.mount} - {fmtBytes(d.usedBytes)} / {fmtBytes(d.totalBytes)} + + {d.mount} + + + {fmtBytes(d.usedBytes)} / {fmtBytes(d.totalBytes)} +
    @@ -147,10 +181,14 @@ export const BtopView = () => { <>
    {stats.gpu.busyPct}% - VRAM {fmtBytes(stats.gpu.vramUsedBytes)} / {fmtBytes(stats.gpu.vramTotalBytes)} + + VRAM {fmtBytes(stats.gpu.vramUsedBytes)} / {fmtBytes(stats.gpu.vramTotalBytes)} +
    - {stats.gpu.vramTotalBytes > 0 && } + {stats.gpu.vramTotalBytes > 0 && ( + + )} ) : (

    no gpu

    @@ -161,7 +199,9 @@ export const BtopView = () => { {stats.temp?.cpuC != null ? ( <>
    - {stats.temp.cpuC.toFixed(0)}°C + + {stats.temp.cpuC.toFixed(0)}°C + {stats.temp.cpuLabel}
    @@ -170,7 +210,9 @@ export const BtopView = () => { .slice(0, 6) .map((s, i) => (
    - {s.name} · {s.label} + + {s.name} · {s.label} + {s.celsius.toFixed(0)}°C
    ))} @@ -192,7 +234,9 @@ export const BtopView = () => { {stats.net.interfaces.slice(0, 4).map((i) => (
    {i.name} - ↓{fmtRate(i.rxBytesPerSec)} ↑{fmtRate(i.txBytesPerSec)} + + ↓{fmtRate(i.rxBytesPerSec)} ↑{fmtRate(i.txBytesPerSec)} +
    ))} {stats.net.interfaces.length === 0 && idle} @@ -208,11 +252,15 @@ export const BtopView = () => {
    CPU package - {stats.power.cpuWatts != null ? `${stats.power.cpuWatts} W` : '—'} + + {stats.power.cpuWatts != null ? `${stats.power.cpuWatts} W` : '—'} +
    GPU - {stats.power.gpuWatts != null ? `${stats.power.gpuWatts} W` : '—'} + + {stats.power.gpuWatts != null ? `${stats.power.gpuWatts} W` : '—'} +
    ) : ( @@ -239,8 +287,12 @@ export const BtopView = () => { {p.pid} {p.user} {p.cpuPct.toFixed(1)} - {p.memPct.toFixed(1)} - {p.command} + + {p.memPct.toFixed(1)} + + + {p.command} + ))} diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/DockerView.tsx b/src/workspaces/officerdev/src/apps/SystemMonitor/DockerView.tsx index 48bc7f49..4c93b485 100644 --- a/src/workspaces/officerdev/src/apps/SystemMonitor/DockerView.tsx +++ b/src/workspaces/officerdev/src/apps/SystemMonitor/DockerView.tsx @@ -5,7 +5,7 @@ import { LogStream } from './LogStream'; type Container = { id: string; name: string; image: string; state: string; status: string; ports: string }; const POLL_MS = 3000; -const stateColor = (s: string) => (s === 'running' ? 'bg-emerald-500' : s === 'exited' ? 'bg-red-500' : 'bg-amber-500'); +const stateColor = (s: string) => (s === 'running' ? 'bg-success' : s === 'exited' ? 'bg-destructive' : 'bg-amber-500'); export const DockerView = () => { const { get } = useClient(); @@ -45,7 +45,9 @@ export const DockerView = () => { ); const q = filter.trim().toLowerCase(); - const filtered = (containers ?? []).filter((c) => !q || c.name.toLowerCase().includes(q) || c.image.toLowerCase().includes(q)); + const filtered = (containers ?? []).filter( + (c) => !q || c.name.toLowerCase().includes(q) || c.image.toLowerCase().includes(q), + ); return (
    @@ -61,7 +63,11 @@ export const DockerView = () => { className="w-56 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary" />
    - {error &&
    {error}
    } + {error && ( +
    + {error} +
    + )}
    {filtered.map((c) => (
    -
    {c.image}
    -
    {c.status}
    - {c.ports &&
    {c.ports}
    } +
    + {c.image} +
    +
    + {c.status} +
    + {c.ports && ( +
    + {c.ports} +
    + )} ))}
    diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/LogStream.tsx b/src/workspaces/officerdev/src/apps/SystemMonitor/LogStream.tsx index 9ff2487e..84281448 100644 --- a/src/workspaces/officerdev/src/apps/SystemMonitor/LogStream.tsx +++ b/src/workspaces/officerdev/src/apps/SystemMonitor/LogStream.tsx @@ -6,7 +6,17 @@ const MAX_LINES = 1200; // Generic live log viewer: SSE-tails `${streamUrl}&token=…` (server sends one `data:` frame per line), // on a solid-black terminal-style pane. Used for both pm2 and docker logs. -export const LogStream = ({ streamUrl, title, backLabel, onBack }: { streamUrl: string; title: string; backLabel: string; onBack: () => void }) => { +export const LogStream = ({ + streamUrl, + title, + backLabel, + onBack, +}: { + streamUrl: string; + title: string; + backLabel: string; + onBack: () => void; +}) => { const { token } = useClient(); const [lines, setLines] = useState([]); const scrollRef = useRef(null); @@ -25,14 +35,21 @@ export const LogStream = ({ streamUrl, title, backLabel, onBack }: { streamUrl: return (
    - {title} - +
    -
    -        {lines.length === 0 ? connecting… : lines.join('\n')}
    +      
    +        {lines.length === 0 ? connecting… : lines.join('\n')}
           
    ); diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx b/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx index db854b34..771c1713 100644 --- a/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx +++ b/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx @@ -3,12 +3,27 @@ import { useClient } from 'hooks/useClient'; import { fmtBytes, fmtDuration } from './shared'; import { LogStream } from './LogStream'; -type Proc = { id: number; name: string; status: string; pid: number | null; cpuPct: number; memBytes: number; restarts: number; uptimeMs: number }; +type Proc = { + id: number; + name: string; + status: string; + pid: number | null; + cpuPct: number; + memBytes: number; + restarts: number; + uptimeMs: number; +}; type SortKey = 'id' | 'name' | 'status' | 'cpuPct' | 'memBytes' | 'restarts' | 'uptimeMs' | 'pid'; const POLL_MS = 2000; const statusColor = (s: string) => - s === 'online' ? 'bg-emerald-500' : s === 'stopped' ? 'bg-muted-foreground/40' : s === 'errored' ? 'bg-red-500' : 'bg-amber-500'; + s === 'online' + ? 'bg-success' + : s === 'stopped' + ? 'bg-muted-foreground/40' + : s === 'errored' + ? 'bg-destructive' + : 'bg-amber-500'; const COLS: { key: SortKey; label: string; align: 'left' | 'right' }[] = [ { key: 'id', label: '#', align: 'right' }, @@ -62,7 +77,8 @@ export const Pm2View = () => { arr.sort((a, b) => { const av = a[sortKey] ?? 0; const bv = b[sortKey] ?? 0; - const cmp = typeof av === 'string' && typeof bv === 'string' ? av.localeCompare(bv) : (av as number) - (bv as number); + const cmp = + typeof av === 'string' && typeof bv === 'string' ? av.localeCompare(bv) : (av as number) - (bv as number); return sortDir === 'asc' ? cmp : -cmp; }); return arr; @@ -81,8 +97,14 @@ export const Pm2View = () => { return (
    -
    pm2 · {procs?.length ?? 0} processes · click a name for live logs
    - {error &&
    {error}
    } +
    + pm2 · {procs?.length ?? 0} processes · click a name for live logs +
    + {error && ( +
    + {error} +
    + )}
    @@ -95,7 +117,9 @@ export const Pm2View = () => { className={`inline-flex items-center gap-1 hover:text-foreground ${c.align === 'right' ? 'flex-row-reverse' : ''} ${sortKey === c.key ? 'text-foreground' : ''}`} > {c.label} - {sortKey === c.key ? (sortDir === 'asc' ? '▲' : '▼') : ''} + + {sortKey === c.key ? (sortDir === 'asc' ? '▲' : '▼') : ''} + ))} @@ -129,7 +153,9 @@ export const Pm2View = () => { ))} {procs && procs.length === 0 && ( - + )}
    no processes + no processes +