feat: canvas options
This commit is contained in:
@@ -34,8 +34,6 @@ export type CanvasMenubarProps = {
|
|||||||
canDuplicate?: boolean
|
canDuplicate?: boolean
|
||||||
canCopy?: boolean
|
canCopy?: boolean
|
||||||
onFitView?: () => void
|
onFitView?: () => void
|
||||||
showMinimap?: boolean
|
|
||||||
onToggleMinimap?: (visible: boolean) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UNDO_KEYS = { key: 'z', shiftKey: false }
|
const UNDO_KEYS = { key: 'z', shiftKey: false }
|
||||||
@@ -59,8 +57,6 @@ export function CanvasMenubar({
|
|||||||
canDuplicate = false,
|
canDuplicate = false,
|
||||||
canCopy = false,
|
canCopy = false,
|
||||||
onFitView,
|
onFitView,
|
||||||
showMinimap = false,
|
|
||||||
onToggleMinimap,
|
|
||||||
}: CanvasMenubarProps) {
|
}: CanvasMenubarProps) {
|
||||||
const { projectId } = useParams<{ projectId: string }>()
|
const { projectId } = useParams<{ projectId: string }>()
|
||||||
const { projects, renameProject } = usePlatform()
|
const { projects, renameProject } = usePlatform()
|
||||||
@@ -233,18 +229,6 @@ export function CanvasMenubar({
|
|||||||
</span>
|
</span>
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
)}
|
)}
|
||||||
{onToggleMinimap != null && (
|
|
||||||
<>
|
|
||||||
<MenubarSeparator />
|
|
||||||
<MenubarCheckboxItem
|
|
||||||
checked={showMinimap}
|
|
||||||
onCheckedChange={(checked) => onToggleMinimap(checked)}
|
|
||||||
className="gap-2"
|
|
||||||
>
|
|
||||||
Minimap
|
|
||||||
</MenubarCheckboxItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</MenubarContent>
|
</MenubarContent>
|
||||||
</MenubarMenu>
|
</MenubarMenu>
|
||||||
</Menubar>
|
</Menubar>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import { AnimatedEdge } from '@/components/base/AnimatedEdge'
|
import { AnimatedEdge } from '@/components/base/AnimatedEdge'
|
||||||
import FlowContext from '@/lib/flowContext'
|
import FlowContext from '@/lib/flowContext'
|
||||||
import { useTheme } from '@/lib/themeContext'
|
import { useTheme } from '@/lib/themeContext'
|
||||||
|
import { usePlatform } from '@/app/platform/platformContext'
|
||||||
import { useGraphStateWithHistory } from '@/hooks/useGraphStateWithHistory'
|
import { useGraphStateWithHistory } from '@/hooks/useGraphStateWithHistory'
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
@@ -148,6 +149,7 @@ function getInitialGraph(projectId: string | undefined): { nodes: AppNode[]; edg
|
|||||||
|
|
||||||
export function CanvasPage({ projectId }: CanvasPageProps) {
|
export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
const { showMinimap } = usePlatform()
|
||||||
const initialGraph = useMemo(() => getInitialGraph(projectId), [projectId])
|
const initialGraph = useMemo(() => getInitialGraph(projectId), [projectId])
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
@@ -190,7 +192,6 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
|||||||
const [lastCreatedNodeId, setLastCreatedNodeId] = React.useState<string | null>(null)
|
const [lastCreatedNodeId, setLastCreatedNodeId] = React.useState<string | null>(null)
|
||||||
const [isPanning, setIsPanning] = React.useState(false)
|
const [isPanning, setIsPanning] = React.useState(false)
|
||||||
const [isSelecting, setIsSelecting] = React.useState(false)
|
const [isSelecting, setIsSelecting] = React.useState(false)
|
||||||
const [showMinimap, setShowMinimap] = React.useState(false)
|
|
||||||
const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null)
|
const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null)
|
||||||
const nodesRef = useRef(nodes)
|
const nodesRef = useRef(nodes)
|
||||||
nodesRef.current = nodes
|
nodesRef.current = nodes
|
||||||
@@ -570,8 +571,6 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
|||||||
canDuplicate={selectedNodes.length > 0}
|
canDuplicate={selectedNodes.length > 0}
|
||||||
canCopy={selectedNodes.length === 1}
|
canCopy={selectedNodes.length === 1}
|
||||||
onFitView={() => flowActionsRef.current?.fitView?.()}
|
onFitView={() => flowActionsRef.current?.fitView?.()}
|
||||||
showMinimap={showMinimap}
|
|
||||||
onToggleMinimap={setShowMinimap}
|
|
||||||
/>
|
/>
|
||||||
{apiTodosCount !== null && (
|
{apiTodosCount !== null && (
|
||||||
<div className="shrink-0 px-3 py-1 text-xs text-muted-foreground border-b border-border/50">
|
<div className="shrink-0 px-3 py-1 text-xs text-muted-foreground border-b border-border/50">
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { useTheme } from '@/lib/themeContext'
|
import { useTheme } from '@/lib/themeContext'
|
||||||
import type { Theme } from '@/lib/themeContext'
|
import type { Theme } from '@/lib/themeContext'
|
||||||
|
import { usePlatform } from './platformContext'
|
||||||
import { Sun, Sparkles } from 'lucide-react'
|
import { Sun, Sparkles } from 'lucide-react'
|
||||||
|
|
||||||
type SettingsSection = 'appearance' | 'ai'
|
type SettingsSection = 'appearance' | 'ai'
|
||||||
@@ -32,6 +34,7 @@ export function SettingsDialog({
|
|||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [section, setSection] = useState<SettingsSection>('appearance')
|
const [section, setSection] = useState<SettingsSection>('appearance')
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
|
const { showMinimap, setShowMinimap } = usePlatform()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
@@ -88,6 +91,21 @@ export function SettingsDialog({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-3 pt-2 border-t">
|
||||||
|
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||||
|
Canvas
|
||||||
|
</h4>
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center">
|
||||||
|
<label htmlFor="settings-minimap" className="text-sm font-medium">
|
||||||
|
Minimap
|
||||||
|
</label>
|
||||||
|
<Switch
|
||||||
|
id="settings-minimap"
|
||||||
|
checked={showMinimap}
|
||||||
|
onCheckedChange={setShowMinimap}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{section === 'ai' && (
|
{section === 'ai' && (
|
||||||
|
|||||||
@@ -11,8 +11,24 @@ import { PROJECT_VERSION } from './projectGraphStorage'
|
|||||||
const STORAGE_KEY = 'zui_platform_projects'
|
const STORAGE_KEY = 'zui_platform_projects'
|
||||||
const ORDER_STORAGE_KEY = 'zui_platform_project_order'
|
const ORDER_STORAGE_KEY = 'zui_platform_project_order'
|
||||||
const RECENT_STORAGE_KEY = 'zui_platform_recent_project_ids'
|
const RECENT_STORAGE_KEY = 'zui_platform_recent_project_ids'
|
||||||
|
const CANVAS_MINIMAP_KEY = 'zui_canvas_show_minimap'
|
||||||
const RECENT_MAX = 5
|
const RECENT_MAX = 5
|
||||||
|
|
||||||
|
function loadShowMinimap(): boolean {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CANVAS_MINIMAP_KEY)
|
||||||
|
if (raw === 'true') return true
|
||||||
|
if (raw === 'false') return false
|
||||||
|
} catch {}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveShowMinimap(value: boolean) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(CANVAS_MINIMAP_KEY, JSON.stringify(value))
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
function loadOrder(): string[] {
|
function loadOrder(): string[] {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(ORDER_STORAGE_KEY)
|
const raw = localStorage.getItem(ORDER_STORAGE_KEY)
|
||||||
@@ -107,6 +123,9 @@ export type PlatformContextValue = {
|
|||||||
updateLastEdited: (id: string) => void
|
updateLastEdited: (id: string) => void
|
||||||
reorderProjects: (orderedIds: string[]) => void
|
reorderProjects: (orderedIds: string[]) => void
|
||||||
restoreProject: (project: Project, graphSnapshot: GraphSnapshot | null) => void
|
restoreProject: (project: Project, graphSnapshot: GraphSnapshot | null) => void
|
||||||
|
/** Canvas: show React Flow minimap (persisted) */
|
||||||
|
showMinimap: boolean
|
||||||
|
setShowMinimap: (value: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlatformContext = createContext<PlatformContextValue | null>(null)
|
const PlatformContext = createContext<PlatformContextValue | null>(null)
|
||||||
@@ -115,6 +134,12 @@ export function PlatformProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const [projects, setProjects] = useState<Project[]>(loadProjects)
|
const [projects, setProjects] = useState<Project[]>(loadProjects)
|
||||||
const [projectOrder, setProjectOrder] = useState<string[]>(loadOrder)
|
const [projectOrder, setProjectOrder] = useState<string[]>(loadOrder)
|
||||||
const [recentProjectIds, setRecentProjectIds] = useState<string[]>(loadRecentIds)
|
const [recentProjectIds, setRecentProjectIds] = useState<string[]>(loadRecentIds)
|
||||||
|
const [showMinimap, setShowMinimapState] = useState<boolean>(loadShowMinimap)
|
||||||
|
|
||||||
|
const setShowMinimap = useCallback((value: boolean) => {
|
||||||
|
setShowMinimapState(value)
|
||||||
|
saveShowMinimap(value)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const persist = useCallback((next: Project[]) => {
|
const persist = useCallback((next: Project[]) => {
|
||||||
setProjects(next)
|
setProjects(next)
|
||||||
@@ -220,6 +245,8 @@ export function PlatformProvider({ children }: { children: React.ReactNode }) {
|
|||||||
updateLastEdited,
|
updateLastEdited,
|
||||||
reorderProjects,
|
reorderProjects,
|
||||||
restoreProject,
|
restoreProject,
|
||||||
|
showMinimap,
|
||||||
|
setShowMinimap,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
projects,
|
projects,
|
||||||
@@ -234,6 +261,8 @@ export function PlatformProvider({ children }: { children: React.ReactNode }) {
|
|||||||
updateLastEdited,
|
updateLastEdited,
|
||||||
reorderProjects,
|
reorderProjects,
|
||||||
restoreProject,
|
restoreProject,
|
||||||
|
showMinimap,
|
||||||
|
setShowMinimap,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user