mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +08:00
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)
This commit is contained in:
32
app/page.tsx
32
app/page.tsx
@@ -15,6 +15,13 @@ export default function Home() {
|
|||||||
const { drawioRef, handleDiagramExport } = useDiagram();
|
const { drawioRef, handleDiagramExport } = useDiagram();
|
||||||
const [isMobile, setIsMobile] = useState(false);
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [isChatVisible, setIsChatVisible] = useState(true);
|
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<ImperativePanelHandle>(null);
|
const chatPanelRef = useRef<ImperativePanelHandle>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -42,14 +49,14 @@ export default function Home() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
if ((event.ctrlKey || event.metaKey) && event.key === 'b') {
|
if ((event.ctrlKey || event.metaKey) && event.key === "b") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
toggleChatPanel();
|
toggleChatPanel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Show confirmation dialog when user tries to leave the page
|
// Show confirmation dialog when user tries to leave the page
|
||||||
@@ -57,11 +64,12 @@ export default function Home() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return '';
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||||
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
return () =>
|
||||||
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -77,7 +85,9 @@ export default function Home() {
|
|||||||
Desktop Required
|
Desktop Required
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
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.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,9 +99,11 @@ export default function Home() {
|
|||||||
<div className="h-full relative p-2">
|
<div className="h-full relative p-2">
|
||||||
<div className="h-full rounded-xl overflow-hidden shadow-soft-lg border border-border/30 bg-white">
|
<div className="h-full rounded-xl overflow-hidden shadow-soft-lg border border-border/30 bg-white">
|
||||||
<DrawIoEmbed
|
<DrawIoEmbed
|
||||||
|
key={drawioUi}
|
||||||
ref={drawioRef}
|
ref={drawioRef}
|
||||||
onExport={handleDiagramExport}
|
onExport={handleDiagramExport}
|
||||||
urlParameters={{
|
urlParameters={{
|
||||||
|
ui: drawioUi,
|
||||||
spin: true,
|
spin: true,
|
||||||
libraries: false,
|
libraries: false,
|
||||||
saveAndExit: false,
|
saveAndExit: false,
|
||||||
@@ -119,6 +131,12 @@ export default function Home() {
|
|||||||
<ChatPanel
|
<ChatPanel
|
||||||
isVisible={isChatVisible}
|
isVisible={isChatVisible}
|
||||||
onToggleVisibility={toggleChatPanel}
|
onToggleVisibility={toggleChatPanel}
|
||||||
|
drawioUi={drawioUi}
|
||||||
|
onToggleDrawioUi={() => {
|
||||||
|
const newTheme = drawioUi === "min" ? "sketch" : "min";
|
||||||
|
localStorage.setItem("drawio-theme", newTheme);
|
||||||
|
setDrawioUi(newTheme);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { ResetWarningModal } from "@/components/reset-warning-modal";
|
import { ResetWarningModal } from "@/components/reset-warning-modal";
|
||||||
import { SaveDialog } from "@/components/save-dialog";
|
import { SaveDialog } from "@/components/save-dialog";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import {
|
import {
|
||||||
Loader2,
|
Loader2,
|
||||||
Send,
|
Send,
|
||||||
@@ -12,6 +20,8 @@ import {
|
|||||||
Image as ImageIcon,
|
Image as ImageIcon,
|
||||||
History,
|
History,
|
||||||
Download,
|
Download,
|
||||||
|
PenTool,
|
||||||
|
LayoutGrid,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { ButtonWithTooltip } from "@/components/button-with-tooltip";
|
import { ButtonWithTooltip } from "@/components/button-with-tooltip";
|
||||||
@@ -97,6 +107,8 @@ interface ChatInputProps {
|
|||||||
onToggleHistory?: (show: boolean) => void;
|
onToggleHistory?: (show: boolean) => void;
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
error?: Error | null;
|
error?: Error | null;
|
||||||
|
drawioUi?: "min" | "sketch";
|
||||||
|
onToggleDrawioUi?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatInput({
|
export function ChatInput({
|
||||||
@@ -111,6 +123,8 @@ export function ChatInput({
|
|||||||
onToggleHistory = () => {},
|
onToggleHistory = () => {},
|
||||||
sessionId,
|
sessionId,
|
||||||
error = null,
|
error = null,
|
||||||
|
drawioUi = "min",
|
||||||
|
onToggleDrawioUi = () => {},
|
||||||
}: ChatInputProps) {
|
}: ChatInputProps) {
|
||||||
const { diagramHistory, saveDiagramToFile } = useDiagram();
|
const { diagramHistory, saveDiagramToFile } = useDiagram();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@@ -118,6 +132,7 @@ export function ChatInput({
|
|||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [showClearDialog, setShowClearDialog] = useState(false);
|
const [showClearDialog, setShowClearDialog] = useState(false);
|
||||||
const [showSaveDialog, setShowSaveDialog] = 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")
|
// Allow retry when there's an error (even if status is still "streaming" or "submitted")
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
@@ -295,6 +310,50 @@ export function ChatInput({
|
|||||||
showHistory={showHistory}
|
showHistory={showHistory}
|
||||||
onToggleHistory={onToggleHistory}
|
onToggleHistory={onToggleHistory}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ButtonWithTooltip
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => 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" ? (
|
||||||
|
<PenTool className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<LayoutGrid className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</ButtonWithTooltip>
|
||||||
|
|
||||||
|
<Dialog open={showThemeWarning} onOpenChange={setShowThemeWarning}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Switch Theme?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Switching themes will reload the diagram editor and clear any unsaved changes.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setShowThemeWarning(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => {
|
||||||
|
onClearChat();
|
||||||
|
onToggleDrawioUi();
|
||||||
|
setShowThemeWarning(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Switch Theme
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right actions */}
|
{/* Right actions */}
|
||||||
|
|||||||
@@ -29,11 +29,15 @@ import {
|
|||||||
interface ChatPanelProps {
|
interface ChatPanelProps {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
onToggleVisibility: () => void;
|
onToggleVisibility: () => void;
|
||||||
|
drawioUi: "min" | "sketch";
|
||||||
|
onToggleDrawioUi: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatPanel({
|
export default function ChatPanel({
|
||||||
isVisible,
|
isVisible,
|
||||||
onToggleVisibility,
|
onToggleVisibility,
|
||||||
|
drawioUi,
|
||||||
|
onToggleDrawioUi,
|
||||||
}: ChatPanelProps) {
|
}: ChatPanelProps) {
|
||||||
const {
|
const {
|
||||||
loadDiagram: onDisplayChart,
|
loadDiagram: onDisplayChart,
|
||||||
@@ -531,6 +535,8 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
onToggleHistory={setShowHistory}
|
onToggleHistory={setShowHistory}
|
||||||
sessionId={sessionId}
|
sessionId={sessionId}
|
||||||
error={error}
|
error={error}
|
||||||
|
drawioUi={drawioUi}
|
||||||
|
onToggleDrawioUi={onToggleDrawioUi}
|
||||||
/>
|
/>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user