(props: PluginUIProps)
| 85 | }; |
| 86 | |
| 87 | export const PluginUI = (props: PluginUIProps) => { |
| 88 | const [showAbout, setShowAbout] = useState(false); |
| 89 | const [showLoading, setShowLoading] = useState(false); |
| 90 | const [hasHandledInitialLoad, setHasHandledInitialLoad] = useState(false); |
| 91 | |
| 92 | const [previewExpanded, setPreviewExpanded] = useState(false); |
| 93 | const [previewViewMode, setPreviewViewMode] = useState< |
| 94 | "desktop" | "mobile" | "precision" |
| 95 | >("precision"); |
| 96 | const [previewBgColor, setPreviewBgColor] = useState<"white" | "black">( |
| 97 | "white", |
| 98 | ); |
| 99 | |
| 100 | useEffect(() => { |
| 101 | if (!props.isLoading) { |
| 102 | setShowLoading(false); |
| 103 | setHasHandledInitialLoad(true); |
| 104 | return; |
| 105 | } |
| 106 | |
| 107 | if (hasHandledInitialLoad) { |
| 108 | setShowLoading(true); |
| 109 | return; |
| 110 | } |
| 111 | |
| 112 | // On plugin startup, the UI waits for a ready handshake before the first conversion. |
| 113 | // Delay the loader only for that initial pass to avoid a one-frame loading flash. |
| 114 | const timer = window.setTimeout(() => { |
| 115 | setShowLoading(true); |
| 116 | }, LOADING_INDICATOR_DELAY_MS); |
| 117 | |
| 118 | return () => window.clearTimeout(timer); |
| 119 | }, [props.isLoading]); |
| 120 | |
| 121 | if (props.isLoading) return showLoading ? <Loading /> : null; |
| 122 | |
| 123 | const isEmpty = props.code === ""; |
| 124 | const warnings = props.warnings ?? []; |
| 125 | |
| 126 | return ( |
| 127 | <TooltipProvider> |
| 128 | <div className="flex flex-col h-full overflow-hidden bg-background text-foreground"> |
| 129 | <div className="px-2 py-1.5 dark:bg-card"> |
| 130 | <div className="flex gap-1 bg-muted dark:bg-card rounded-lg p-0.5"> |
| 131 | <FrameworkTabs |
| 132 | frameworks={frameworks} |
| 133 | selectedFramework={props.selectedFramework} |
| 134 | setSelectedFramework={props.setSelectedFramework} |
| 135 | showAbout={showAbout} |
| 136 | setShowAbout={setShowAbout} |
| 137 | /> |
| 138 | <Button |
| 139 | variant="ghost" |
| 140 | size="icon" |
| 141 | className={`h-8 w-8 rounded-md ${ |
| 142 | showAbout |
| 143 | ? "bg-primary text-primary-foreground shadow-xs hover:bg-primary hover:text-primary-foreground dark:hover:bg-primary" |
| 144 | : "bg-muted text-foreground hover:bg-primary/90 hover:text-primary-foreground dark:hover:bg-primary/90" |
nothing calls this directly
no outgoing calls
no test coverage detected