mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-09 19:19:50 +08:00
The canvas now fills the window and the chat floats on its right. draw.io is served from our own origin (public/drawio, downloaded at build time and checked against the release's SHA-256), so the app can drive the editor directly: - every AI change becomes a version card with a thumbnail; compare, restore or undo it, and Ctrl+Z on the canvas takes an AI change back in one step - shapes the AI just changed are highlighted on the canvas - selected shapes appear as a chip and are sent to the model - the app's own canvas toolbar, shape library and page tabs replace draw.io's chrome - new start screen, session rename and switcher, open .drawio files - settings split into Models & keys, Appearance, Advanced and About - phones switch between canvas and chat - dark mode keeps the diagram on a white sheet and no longer flashes With an external draw.io (NEXT_PUBLIC_DRAWIO_BASE_URL) the app falls back to draw.io's own toolbar and the features above that need same origin are off. The chat logic moved from the 1500-line chat panel into a chat engine plus small UI components; zustand holds canvas, versions, settings and UI state.
63 lines
2.3 KiB
TypeScript
63 lines
2.3 KiB
TypeScript
import { create } from "zustand"
|
|
|
|
export type SettingsTab = "models" | "appearance" | "advanced" | "about"
|
|
export type MobileView = "canvas" | "chat"
|
|
|
|
interface UiState {
|
|
/** Desktop: the floating chat panel is open */
|
|
panelOpen: boolean
|
|
/** The start screen was put away (drawing by hand, an opened file) */
|
|
heroDismissed: boolean
|
|
/** Phones: which half of the app is visible */
|
|
mobileView: MobileView
|
|
settingsOpen: boolean
|
|
settingsTab: SettingsTab
|
|
/** Version shown in the compare dialog, or null when closed */
|
|
compareVersionId: string | null
|
|
saveDialogOpen: boolean
|
|
/** Bumped to ask the composer to take focus */
|
|
focusComposerToken: number
|
|
setPanelOpen: (open: boolean) => void
|
|
setHeroDismissed: (dismissed: boolean) => void
|
|
togglePanel: () => void
|
|
setMobileView: (view: MobileView) => void
|
|
openSettings: (tab?: SettingsTab) => void
|
|
setSettingsOpen: (open: boolean) => void
|
|
setSettingsTab: (tab: SettingsTab) => void
|
|
openCompare: (versionId: string) => void
|
|
closeCompare: () => void
|
|
setSaveDialogOpen: (open: boolean) => void
|
|
focusComposer: () => void
|
|
}
|
|
|
|
export const useUiStore = create<UiState>((set) => ({
|
|
panelOpen: true,
|
|
heroDismissed: false,
|
|
mobileView: "chat",
|
|
settingsOpen: false,
|
|
settingsTab: "models",
|
|
compareVersionId: null,
|
|
saveDialogOpen: false,
|
|
focusComposerToken: 0,
|
|
setPanelOpen: (open) => set({ panelOpen: open }),
|
|
setHeroDismissed: (dismissed) => set({ heroDismissed: dismissed }),
|
|
togglePanel: () => set((state) => ({ panelOpen: !state.panelOpen })),
|
|
setMobileView: (view) => set({ mobileView: view }),
|
|
openSettings: (tab) =>
|
|
set((state) => ({
|
|
settingsOpen: true,
|
|
settingsTab: tab ?? state.settingsTab,
|
|
})),
|
|
setSettingsOpen: (open) => set({ settingsOpen: open }),
|
|
setSettingsTab: (tab) => set({ settingsTab: tab }),
|
|
openCompare: (versionId) => set({ compareVersionId: versionId }),
|
|
closeCompare: () => set({ compareVersionId: null }),
|
|
setSaveDialogOpen: (open) => set({ saveDialogOpen: open }),
|
|
focusComposer: () =>
|
|
set((state) => ({
|
|
panelOpen: true,
|
|
mobileView: "chat",
|
|
focusComposerToken: state.focusComposerToken + 1,
|
|
})),
|
|
}))
|