88 lines · 3.1 KB · TypeScript
RawDownload
import { type ReactNode, useEffect, useState } from "react"; import { isElectron } from "~/env";import { useResizableWidth } from "~/hooks/useResizableWidth";import { cn } from "~/lib/utils"; import { RightPanelResizeHandle } from "./RightPanelResizeHandle"; export type PreviewPanelMode = "inline" | "sheet" | "sidebar" | "embedded"; const PREVIEW_PANEL_WIDTH_STORAGE_KEY = "t3code:preview-panel-width";const PREVIEW_PANEL_MIN_WIDTH = 360;/** Fraction of the viewport allowed, preserving the remaining space for chat. */const PREVIEW_PANEL_MAX_WIDTH_FRACTION = 0.7;const PREVIEW_PANEL_DEFAULT_WIDTH = 540; export function getPreviewPanelMaxWidth(viewportWidth: number): number {  return Math.floor(viewportWidth * PREVIEW_PANEL_MAX_WIDTH_FRACTION);} /** * Shell for the preview panel. In inline mode the panel is user-resizable * via a drag handle on the left edge; width persists per browser. In * sheet/sidebar modes the parent owns the size. */export function PreviewPanelShell(props: {  mode: PreviewPanelMode;  maximized?: boolean;  children: ReactNode;}) {  const useDragRegion = isElectron && props.mode !== "sheet" && props.mode !== "embedded";  const isInline = props.mode === "inline";  const maxWidth = useViewportClampedMaxWidth();  const { width, handlers } = useResizableWidth({    storageKey: PREVIEW_PANEL_WIDTH_STORAGE_KEY,    defaultWidth: PREVIEW_PANEL_DEFAULT_WIDTH,    minWidth: PREVIEW_PANEL_MIN_WIDTH,    maxWidth,    edge: "left",  });   return (    <div      className={cn(        "relative flex h-full min-h-0 min-w-0 flex-col self-stretch bg-background",        isInline          ? props.maximized            ? "flex-1 border-l border-border"            : "shrink-0 border-l border-border"          : "w-full",      )}      style={isInline && !props.maximized ? { width: `${width}px` } : undefined}      data-preview-panel-mode={props.mode}      data-preview-panel-maximized={props.maximized ? "true" : "false"}    >      {isInline && !props.maximized ? <RightPanelResizeHandle handlers={handlers} /> : null}      {useDragRegion ? <div className="electron-drag-region h-0 w-full" aria-hidden /> : null}      {props.children}    </div>  );} /** * Track viewport width to derive a sensible upper bound for the panel. * Resize-aware so dragging the OS window narrower re-clamps the stored * width on the next render (the hook's clamp picks this up automatically). */function useViewportClampedMaxWidth(): number {  const [vw, setVw] = useState(() => (typeof window === "undefined" ? 1280 : window.innerWidth));  useEffect(() => {    if (typeof window === "undefined") return;    let frame = 0;    const onResize = () => {      // Coalesce rapid resize events into one rAF tick.      if (frame !== 0) return;      frame = window.requestAnimationFrame(() => {        frame = 0;        setVw(window.innerWidth);      });    };    window.addEventListener("resize", onResize);    return () => {      window.removeEventListener("resize", onResize);      if (frame !== 0) window.cancelAnimationFrame(frame);    };  }, []);  return getPreviewPanelMaxWidth(vw);}