From 3f35c52527fb79e2e6a3bf8d93cdce671d13ab89 Mon Sep 17 00:00:00 2001 From: Dayuan Jiang <34411969+DayuanJiang@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:10:48 +0900 Subject: [PATCH] feat: add draw.io theme toggle between minimal and sketch (#106) - Add toggle button in chat input area to switch between min and sketch themes - Show warning dialog before switching (clears messages and diagram) - Persist theme selection in localStorage - Default theme is minimal (hides shapes sidebar) --- app/page.tsx | 32 ++++++++++++++++----- components/chat-input.tsx | 59 +++++++++++++++++++++++++++++++++++++++ components/chat-panel.tsx | 6 ++++ 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 2702d3e..16ae4a2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -15,6 +15,13 @@ export default function Home() { const { drawioRef, handleDiagramExport } = useDiagram(); const [isMobile, setIsMobile] = useState(false); const [isChatVisible, setIsChatVisible] = useState(true); + const [drawioUi, setDrawioUi] = useState<"min" | "sketch">(() => { + if (typeof window !== "undefined") { + const saved = localStorage.getItem("drawio-theme"); + if (saved === "min" || saved === "sketch") return saved; + } + return "min"; + }); const chatPanelRef = useRef(null); useEffect(() => { @@ -42,14 +49,14 @@ export default function Home() { useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if ((event.ctrlKey || event.metaKey) && event.key === 'b') { + if ((event.ctrlKey || event.metaKey) && event.key === "b") { event.preventDefault(); toggleChatPanel(); } }; - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); }, []); // Show confirmation dialog when user tries to leave the page @@ -57,11 +64,12 @@ export default function Home() { useEffect(() => { const handleBeforeUnload = (event: BeforeUnloadEvent) => { event.preventDefault(); - return ''; + return ""; }; - window.addEventListener('beforeunload', handleBeforeUnload); - return () => window.removeEventListener('beforeunload', handleBeforeUnload); + window.addEventListener("beforeunload", handleBeforeUnload); + return () => + window.removeEventListener("beforeunload", handleBeforeUnload); }, []); return ( @@ -77,7 +85,9 @@ export default function Home() { Desktop Required

- This application works best on desktop or laptop devices. Please open it on a larger screen for the full experience. + This application works best on desktop or laptop + devices. Please open it on a larger screen for the + full experience.

@@ -89,9 +99,11 @@ export default function Home() {
{ + const newTheme = drawioUi === "min" ? "sketch" : "min"; + localStorage.setItem("drawio-theme", newTheme); + setDrawioUi(newTheme); + }} />
diff --git a/components/chat-input.tsx b/components/chat-input.tsx index 0316271..05b1bb8 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -5,6 +5,14 @@ import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { ResetWarningModal } from "@/components/reset-warning-modal"; import { SaveDialog } from "@/components/save-dialog"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { Loader2, Send, @@ -12,6 +20,8 @@ import { Image as ImageIcon, History, Download, + PenTool, + LayoutGrid, } from "lucide-react"; import { toast } from "sonner"; import { ButtonWithTooltip } from "@/components/button-with-tooltip"; @@ -97,6 +107,8 @@ interface ChatInputProps { onToggleHistory?: (show: boolean) => void; sessionId?: string; error?: Error | null; + drawioUi?: "min" | "sketch"; + onToggleDrawioUi?: () => void; } export function ChatInput({ @@ -111,6 +123,8 @@ export function ChatInput({ onToggleHistory = () => {}, sessionId, error = null, + drawioUi = "min", + onToggleDrawioUi = () => {}, }: ChatInputProps) { const { diagramHistory, saveDiagramToFile } = useDiagram(); const textareaRef = useRef(null); @@ -118,6 +132,7 @@ export function ChatInput({ const [isDragging, setIsDragging] = useState(false); const [showClearDialog, setShowClearDialog] = useState(false); const [showSaveDialog, setShowSaveDialog] = useState(false); + const [showThemeWarning, setShowThemeWarning] = useState(false); // Allow retry when there's an error (even if status is still "streaming" or "submitted") const isDisabled = @@ -295,6 +310,50 @@ export function ChatInput({ showHistory={showHistory} onToggleHistory={onToggleHistory} /> + + setShowThemeWarning(true)} + tooltipContent={drawioUi === "min" ? "Switch to Sketch theme" : "Switch to Minimal theme"} + className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground" + > + {drawioUi === "min" ? ( + + ) : ( + + )} + + + + + + Switch Theme? + + Switching themes will reload the diagram editor and clear any unsaved changes. + + + + + + + +
{/* Right actions */} diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index f4158e6..441c14b 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -29,11 +29,15 @@ import { interface ChatPanelProps { isVisible: boolean; onToggleVisibility: () => void; + drawioUi: "min" | "sketch"; + onToggleDrawioUi: () => void; } export default function ChatPanel({ isVisible, onToggleVisibility, + drawioUi, + onToggleDrawioUi, }: ChatPanelProps) { const { loadDiagram: onDisplayChart, @@ -531,6 +535,8 @@ Please retry with an adjusted search pattern or use display_diagram if retries a onToggleHistory={setShowHistory} sessionId={sessionId} error={error} + drawioUi={drawioUi} + onToggleDrawioUi={onToggleDrawioUi} />