Skip to content

Commit 42b942b

Browse files
committed
Enable collapsing the logs panel
1 parent 407ff50 commit 42b942b

File tree

1 file changed

+110
-68
lines changed
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam

1 file changed

+110
-68
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx

Lines changed: 110 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
33
import { typedjson, useTypedLoaderData } from "remix-typedjson";
44
import { useEffect, useState, useRef, useCallback } from "react";
55
import { S2, S2Error } from "@s2-dev/streamstore";
6-
import { Clipboard, ClipboardCheck } from "lucide-react";
6+
import { Clipboard, ClipboardCheck, ChevronDown, ChevronUp } from "lucide-react";
77
import { ExitIcon } from "~/assets/icons/ExitIcon";
88
import { GitMetadata } from "~/components/GitMetadata";
99
import { RuntimeIcon } from "~/components/RuntimeIcon";
@@ -410,15 +410,20 @@ export default function Page() {
410410
);
411411
}
412412

413-
type LogsDisplayProps = {
413+
function LogsDisplay({
414+
logs,
415+
isStreaming,
416+
streamError,
417+
initialCollapsed = false,
418+
}: {
414419
logs: LogEntry[];
415420
isStreaming: boolean;
416421
streamError: string | null;
417-
};
418-
419-
function LogsDisplay({ logs, isStreaming, streamError }: LogsDisplayProps) {
422+
initialCollapsed?: boolean;
423+
}) {
420424
const [copied, setCopied] = useState(false);
421425
const [mouseOver, setMouseOver] = useState(false);
426+
const [collapsed, setCollapsed] = useState(initialCollapsed);
422427
const logsContainerRef = useRef<HTMLDivElement>(null);
423428

424429
// auto-scroll log container to bottom when new logs arrive
@@ -473,78 +478,115 @@ function LogsDisplay({ logs, isStreaming, streamError }: LogsDisplayProps) {
473478
</div>
474479
</div>
475480
{logs.length > 0 && (
476-
<TooltipProvider>
477-
<Tooltip open={copied || mouseOver} disableHoverableContent>
478-
<TooltipTrigger
479-
onClick={onCopyLogs}
480-
onMouseEnter={() => setMouseOver(true)}
481-
onMouseLeave={() => setMouseOver(false)}
482-
className={cn(
483-
"transition-colors duration-100 focus-custom hover:cursor-pointer",
484-
copied ? "text-success" : "text-text-dimmed hover:text-text-bright"
485-
)}
486-
>
487-
{copied ? <ClipboardCheck className="size-4" /> : <Clipboard className="size-4" />}
488-
</TooltipTrigger>
489-
<TooltipContent side="left" className="text-xs">
490-
{copied ? "Copied" : "Copy"}
491-
</TooltipContent>
492-
</Tooltip>
493-
</TooltipProvider>
481+
<div className="flex items-center gap-3">
482+
<TooltipProvider>
483+
<Tooltip open={copied || mouseOver} disableHoverableContent>
484+
<TooltipTrigger
485+
onClick={onCopyLogs}
486+
onMouseEnter={() => setMouseOver(true)}
487+
onMouseLeave={() => setMouseOver(false)}
488+
className={cn(
489+
"transition-colors duration-100 focus-custom hover:cursor-pointer",
490+
copied ? "text-success" : "text-text-dimmed hover:text-text-bright"
491+
)}
492+
>
493+
<div className="size-4 shrink-0">
494+
{copied ? (
495+
<ClipboardCheck className="size-full" />
496+
) : (
497+
<Clipboard className="size-full" />
498+
)}
499+
</div>
500+
</TooltipTrigger>
501+
<TooltipContent side="left" className="text-xs">
502+
{copied ? "Copied" : "Copy"}
503+
</TooltipContent>
504+
</Tooltip>
505+
</TooltipProvider>
506+
507+
<TooltipProvider>
508+
<Tooltip disableHoverableContent>
509+
<TooltipTrigger
510+
onClick={() => setCollapsed(!collapsed)}
511+
className={cn(
512+
"transition-colors duration-100 focus-custom hover:cursor-pointer",
513+
"text-text-dimmed hover:text-text-bright"
514+
)}
515+
>
516+
{collapsed ? (
517+
<ChevronDown className="size-4" />
518+
) : (
519+
<ChevronUp className="size-4" />
520+
)}
521+
</TooltipTrigger>
522+
<TooltipContent side="left" className="text-xs">
523+
{collapsed ? "Expand" : "Collapse"}
524+
</TooltipContent>
525+
</Tooltip>
526+
</TooltipProvider>
527+
</div>
494528
)}
495529
</div>
496530

497-
<div
498-
ref={logsContainerRef}
499-
className="h-64 grow overflow-x-auto overflow-y-scroll font-mono text-xs scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
500-
>
501-
<div className="flex w-fit min-w-full flex-col">
502-
{logs.length === 0 && (
503-
<div className="flex gap-x-2.5 border-l-2 border-transparent px-2.5 py-1">
504-
{streamError ? (
505-
<span className="text-error">Failed fetching logs</span>
506-
) : (
507-
<span className="text-text-dimmed">
508-
{isStreaming ? "Waiting for logs..." : "No logs yet"}
509-
</span>
510-
)}
511-
</div>
531+
<div className="relative">
532+
<div
533+
ref={logsContainerRef}
534+
className={cn(
535+
"grow overflow-x-auto overflow-y-scroll font-mono text-xs transition-all duration-200 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
536+
collapsed ? "h-16" : "h-64"
512537
)}
513-
{logs.map((log, index) => {
514-
return (
515-
<div
516-
key={index}
517-
className={cn(
518-
"flex w-full gap-x-2.5 border-l-2 px-2.5 py-1",
519-
log.level === "error" && "border-error/60 bg-error/15 hover:bg-error/25",
520-
log.level === "warn" && "border-warning/60 bg-warning/20 hover:bg-warning/30",
521-
log.level === "info" && "border-transparent hover:bg-charcoal-750"
538+
>
539+
<div className="flex w-fit min-w-full flex-col">
540+
{logs.length === 0 && (
541+
<div className="flex gap-x-2.5 border-l-2 border-transparent px-2.5 py-1">
542+
{streamError ? (
543+
<span className="text-error">Failed fetching logs</span>
544+
) : (
545+
<span className="text-text-dimmed">
546+
{isStreaming ? "Waiting for logs..." : "No logs yet"}
547+
</span>
522548
)}
523-
>
524-
<span
525-
className={cn(
526-
"select-none whitespace-nowrap py-px",
527-
log.level === "error" && "text-error/80",
528-
log.level === "warn" && "text-warning/70",
529-
log.level === "info" && "text-text-dimmed"
530-
)}
531-
>
532-
<DateTimeAccurate date={log.timestamp} hideDate />
533-
</span>
534-
<span
549+
</div>
550+
)}
551+
{logs.map((log, index) => {
552+
return (
553+
<div
554+
key={index}
535555
className={cn(
536-
"whitespace-nowrap",
537-
log.level === "error" && "text-error",
538-
log.level === "warn" && "text-warning",
539-
log.level === "info" && "text-text-bright"
556+
"flex w-full gap-x-2.5 border-l-2 px-2.5 py-1",
557+
log.level === "error" && "border-error/60 bg-error/15 hover:bg-error/25",
558+
log.level === "warn" && "border-warning/60 bg-warning/20 hover:bg-warning/30",
559+
log.level === "info" && "border-transparent hover:bg-charcoal-750"
540560
)}
541561
>
542-
{log.message}
543-
</span>
544-
</div>
545-
);
546-
})}
562+
<span
563+
className={cn(
564+
"select-none whitespace-nowrap py-px",
565+
log.level === "error" && "text-error/80",
566+
log.level === "warn" && "text-warning/70",
567+
log.level === "info" && "text-text-dimmed"
568+
)}
569+
>
570+
<DateTimeAccurate date={log.timestamp} hideDate />
571+
</span>
572+
<span
573+
className={cn(
574+
"whitespace-nowrap",
575+
log.level === "error" && "text-error",
576+
log.level === "warn" && "text-warning",
577+
log.level === "info" && "text-text-bright"
578+
)}
579+
>
580+
{log.message}
581+
</span>
582+
</div>
583+
);
584+
})}
585+
</div>
547586
</div>
587+
{collapsed && (
588+
<div className="pointer-events-none absolute bottom-0 left-0 right-0 h-8 bg-gradient-to-t from-charcoal-800/90 to-transparent" />
589+
)}
548590
</div>
549591
</div>
550592
);

0 commit comments

Comments
 (0)