From 7e67e480bbd17702410444117ce3202eced08ba1 Mon Sep 17 00:00:00 2001 From: mawkone Date: Sun, 14 Jun 2026 12:43:09 -0700 Subject: [PATCH] fix(logs): fix terminal colors and payload rendering --- .../project/[projectId]/(home)/logs/page.tsx | 11 +- vibn-frontend/components/ui/terminal.tsx | 200 +++++++++--------- 2 files changed, 107 insertions(+), 104 deletions(-) diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx index 97cdb35..33dc38b 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx @@ -44,11 +44,12 @@ export default function LogsPage() { }), }); const d = await r.json(); - setLogs( - typeof d.result === "string" - ? d.result - : JSON.stringify(d.result ?? d.error, null, 2), - ); + let parsed = ""; + try { + parsed = JSON.parse(d.result).logs; + } catch {} + + setLogs(parsed || d.result || "No logs returned."); } catch { setLogs("Failed to load logs. Is the container running?"); } finally { diff --git a/vibn-frontend/components/ui/terminal.tsx b/vibn-frontend/components/ui/terminal.tsx index 0370980..2e5d0f2 100644 --- a/vibn-frontend/components/ui/terminal.tsx +++ b/vibn-frontend/components/ui/terminal.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Children, @@ -10,29 +10,29 @@ import { useState, type ComponentType, type RefAttributes, -} from "react" +} from "react"; import { motion, useInView, type DOMMotionComponents, type HTMLMotionProps, type MotionProps, -} from "motion/react" +} from "motion/react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; interface SequenceContextValue { - completeItem: (index: number) => void - activeIndex: number - sequenceStarted: boolean + completeItem: (index: number) => void; + activeIndex: number; + sequenceStarted: boolean; } -const SequenceContext = createContext(null) +const SequenceContext = createContext(null); -const useSequence = () => useContext(SequenceContext) +const useSequence = () => useContext(SequenceContext); -const ItemIndexContext = createContext(null) -const useItemIndex = () => useContext(ItemIndexContext) +const ItemIndexContext = createContext(null); +const useItemIndex = () => useContext(ItemIndexContext); const motionElements = { article: motion.article, @@ -47,21 +47,21 @@ const motionElements = { p: motion.p, section: motion.section, span: motion.span, -} as const +} as const; type MotionElementType = Extract< keyof DOMMotionComponents, keyof typeof motionElements -> +>; type TerminalTypingMotionComponent = ComponentType< Omit, "ref"> & RefAttributes -> +>; interface AnimatedSpanProps extends MotionProps { - children: React.ReactNode - delay?: number - className?: string - startOnView?: boolean + children: React.ReactNode; + delay?: number; + className?: string; + startOnView?: boolean; } export const AnimatedSpan = ({ @@ -71,25 +71,25 @@ export const AnimatedSpan = ({ startOnView = false, ...props }: AnimatedSpanProps) => { - const elementRef = useRef(null) + const elementRef = useRef(null); const isInView = useInView(elementRef as React.RefObject, { amount: 0.3, once: true, - }) + }); - const sequence = useSequence() - const itemIndex = useItemIndex() - const [hasStarted, setHasStarted] = useState(false) + const sequence = useSequence(); + const itemIndex = useItemIndex(); + const [hasStarted, setHasStarted] = useState(false); useEffect(() => { - if (!sequence || itemIndex === null) return - if (!sequence.sequenceStarted) return - if (hasStarted) return + if (!sequence || itemIndex === null) return; + if (!sequence.sequenceStarted) return; + if (hasStarted) return; if (sequence.activeIndex === itemIndex) { - setHasStarted(true) + setHasStarted(true); } - }, [sequence, hasStarted, itemIndex]) + }, [sequence, hasStarted, itemIndex]); - const shouldAnimate = sequence ? hasStarted : startOnView ? isInView : true + const shouldAnimate = sequence ? hasStarted : startOnView ? isInView : true; return ( { - if (!sequence) return - if (itemIndex === null) return - sequence.completeItem(itemIndex) + if (!sequence) return; + if (itemIndex === null) return; + sequence.completeItem(itemIndex); }} {...props} > {children} - ) -} + ); +}; interface TypingAnimationProps extends Omit { - children: string - className?: string - duration?: number - delay?: number - as?: MotionElementType - startOnView?: boolean + children: string; + className?: string; + duration?: number; + delay?: number; + as?: MotionElementType; + startOnView?: boolean; } export const TypingAnimation = ({ @@ -129,52 +129,52 @@ export const TypingAnimation = ({ ...props }: TypingAnimationProps) => { if (typeof children !== "string") { - throw new Error("TypingAnimation: children must be a string. Received:") + throw new Error("TypingAnimation: children must be a string. Received:"); } const MotionComponent = motionElements[ Component - ] as TerminalTypingMotionComponent + ] as TerminalTypingMotionComponent; - const [displayedText, setDisplayedText] = useState("") - const [started, setStarted] = useState(false) - const elementRef = useRef(null) + const [displayedText, setDisplayedText] = useState(""); + const [started, setStarted] = useState(false); + const elementRef = useRef(null); const isInView = useInView(elementRef as React.RefObject, { amount: 0.3, once: true, - }) + }); - const sequence = useSequence() - const itemIndex = useItemIndex() - const hasSequence = sequence !== null - const sequenceStarted = sequence?.sequenceStarted ?? false - const sequenceActiveIndex = sequence?.activeIndex ?? null + const sequence = useSequence(); + const itemIndex = useItemIndex(); + const hasSequence = sequence !== null; + const sequenceStarted = sequence?.sequenceStarted ?? false; + const sequenceActiveIndex = sequence?.activeIndex ?? null; const sequenceCompleteItemRef = useRef< SequenceContextValue["completeItem"] | null - >(null) - const sequenceItemIndexRef = useRef(null) + >(null); + const sequenceItemIndexRef = useRef(null); useEffect(() => { - sequenceCompleteItemRef.current = sequence?.completeItem ?? null - sequenceItemIndexRef.current = itemIndex - }, [sequence?.completeItem, itemIndex]) + sequenceCompleteItemRef.current = sequence?.completeItem ?? null; + sequenceItemIndexRef.current = itemIndex; + }, [sequence?.completeItem, itemIndex]); useEffect(() => { - let startTimeout: ReturnType | null = null + let startTimeout: ReturnType | null = null; if (hasSequence && itemIndex !== null) { if (sequenceStarted && !started && sequenceActiveIndex === itemIndex) { - setStarted(true) + setStarted(true); } } else if (!startOnView || isInView) { - startTimeout = setTimeout(() => setStarted(true), delay) + startTimeout = setTimeout(() => setStarted(true), delay); } return () => { if (startTimeout !== null) { - clearTimeout(startTimeout) + clearTimeout(startTimeout); } - } + }; }, [ delay, startOnView, @@ -184,36 +184,36 @@ export const TypingAnimation = ({ sequenceActiveIndex, sequenceStarted, itemIndex, - ]) + ]); useEffect(() => { - let typingEffect: ReturnType | null = null + let typingEffect: ReturnType | null = null; if (started) { - let i = 0 + let i = 0; typingEffect = setInterval(() => { if (i < children.length) { - setDisplayedText(children.substring(0, i + 1)) - i++ + setDisplayedText(children.substring(0, i + 1)); + i++; } else { if (typingEffect !== null) { - clearInterval(typingEffect) + clearInterval(typingEffect); } - const completeItem = sequenceCompleteItemRef.current - const currentItemIndex = sequenceItemIndexRef.current + const completeItem = sequenceCompleteItemRef.current; + const currentItemIndex = sequenceItemIndexRef.current; if (completeItem && currentItemIndex !== null) { - completeItem(currentItemIndex) + completeItem(currentItemIndex); } } - }, duration) + }, duration); } return () => { if (typingEffect !== null) { - clearInterval(typingEffect) + clearInterval(typingEffect); } - } - }, [children, duration, started]) + }; + }, [children, duration, started]); return ( {displayedText} - ) -} + ); +}; interface TerminalProps { - children: React.ReactNode - className?: string - sequence?: boolean - startOnView?: boolean + children: React.ReactNode; + className?: string; + sequence?: boolean; + startOnView?: boolean; } export const Terminal = ({ @@ -239,45 +239,47 @@ export const Terminal = ({ sequence = true, startOnView = true, }: TerminalProps) => { - const containerRef = useRef(null) + const containerRef = useRef(null); const isInView = useInView(containerRef as React.RefObject, { amount: 0.3, once: true, - }) + }); - const [activeIndex, setActiveIndex] = useState(0) - const sequenceHasStarted = sequence ? !startOnView || isInView : false + const [activeIndex, setActiveIndex] = useState(0); + const sequenceHasStarted = sequence ? !startOnView || isInView : false; const contextValue = useMemo(() => { - if (!sequence) return null + if (!sequence) return null; return { completeItem: (index: number) => { - setActiveIndex((current) => (index === current ? current + 1 : current)) + setActiveIndex((current) => + index === current ? current + 1 : current, + ); }, activeIndex, sequenceStarted: sequenceHasStarted, - } - }, [sequence, activeIndex, sequenceHasStarted]) + }; + }, [sequence, activeIndex, sequenceHasStarted]); const wrappedChildren = useMemo(() => { - if (!sequence) return children - const array = Children.toArray(children) + if (!sequence) return children; + const array = Children.toArray(children); return array.map((child, index) => ( {child as React.ReactNode} - )) - }, [children, sequence]) + )); + }, [children, sequence]); const content = (
-
+
@@ -288,13 +290,13 @@ export const Terminal = ({ {wrappedChildren}
- ) + ); - if (!sequence) return content + if (!sequence) return content; return ( {content} - ) -} + ); +};