feat(*): 画布创作体验 - 完善 Agent 引导、生成防重、搜索快捷键、素材多选与分辨率兼容
This commit is contained in:
1 parent
913cf4b304
commit
ab89c05362
52 files changed
+2344
-500
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { AnimatePresence, useReducedMotion } from "motion/react";
|
||||
import { AnimatePresence } from "motion/react";
|
||||
import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { ArrowLeft, Check, ChevronRight, Clipboard, CloudUpload, Copy, FolderOpen, FolderPlus, Image as ImageIcon, Layers3, Link2, Maximize2, PanelTop, Pencil, Plus, Redo2, Tags, Trash2, Undo2, Upload, UserRound } from "lucide-react";
|
||||
|
||||
@@ -93,7 +93,6 @@ export function CanvasNodeContextMenu({
|
||||
onToggleFrame,
|
||||
}: CanvasNodeContextMenuProps) {
|
||||
const theme = canvasThemes[useThemeStore((state) => state.theme)];
|
||||
const reducedMotion = useReducedMotion();
|
||||
const [addOpen, setAddOpen] = useState(false);
|
||||
const [categoryOpen, setCategoryOpen] = useState(false);
|
||||
|
||||
@@ -148,8 +147,8 @@ export function CanvasNodeContextMenu({
|
||||
<SpotlightSurface
|
||||
spotlightColor={theme.toolbar.itemHover}
|
||||
data-canvas-context-menu
|
||||
initial={reducedMotion ? { opacity: 0 } : { opacity: 0, scale: 0.97, x: -3, y: -3 }}
|
||||
animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }}
|
||||
className="aceternity-floating-panel fixed z-[var(--z-popover)] flex w-[224px] max-h-[calc(100vh-56px)] origin-top-left flex-col overflow-hidden rounded-xl border p-1.5 backdrop-blur-2xl"
|
||||
style={{ left: position.left, top: position.top, background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.node.text }}
|
||||
@@ -245,7 +244,6 @@ export function CanvasNodeContextMenu({
|
||||
parentPosition={position}
|
||||
workspaceMode={workspaceMode}
|
||||
isProjectLinked={isProjectLinked}
|
||||
reducedMotion={Boolean(reducedMotion)}
|
||||
onAddNode={(type) => runAction(() => onAddNode(type))}
|
||||
onAddFolder={() => runAction(onAddFolder)}
|
||||
onChooseStyle={() => runAction(onChooseStyle)}
|
||||
@@ -260,7 +258,7 @@ export function CanvasNodeContextMenu({
|
||||
);
|
||||
}
|
||||
|
||||
function AddNodeContextMenu({ parentPosition, workspaceMode, isProjectLinked, reducedMotion, onAddNode, onAddFolder, onChooseStyle, onOpenDirector, onUpload, onOpenAssets, onOpenProjectCharacters }: { parentPosition: { left: number; top: number }; workspaceMode: CanvasWorkspaceMode; isProjectLinked: boolean; reducedMotion: boolean; onAddNode: (type: CanvasNodeTypeId) => void; onAddFolder: () => void; onChooseStyle: () => void; onOpenDirector: () => void; onUpload: () => void; onOpenAssets: () => void; onOpenProjectCharacters: () => void }) {
|
||||
function AddNodeContextMenu({ parentPosition, workspaceMode, isProjectLinked, onAddNode, onAddFolder, onChooseStyle, onOpenDirector, onUpload, onOpenAssets, onOpenProjectCharacters }: { parentPosition: { left: number; top: number }; workspaceMode: CanvasWorkspaceMode; isProjectLinked: boolean; onAddNode: (type: CanvasNodeTypeId) => void; onAddFolder: () => void; onChooseStyle: () => void; onOpenDirector: () => void; onUpload: () => void; onOpenAssets: () => void; onOpenProjectCharacters: () => void }) {
|
||||
const theme = canvasThemes[useThemeStore((state) => state.theme)];
|
||||
const installations = usePluginStore((state) => state.installations);
|
||||
const pluginStates = usePluginStore((state) => state.pluginStates);
|
||||
@@ -299,9 +297,9 @@ function AddNodeContextMenu({ parentPosition, workspaceMode, isProjectLinked, re
|
||||
return (
|
||||
<SpotlightSurface
|
||||
spotlightColor={theme.toolbar.itemHover}
|
||||
initial={reducedMotion ? { opacity: 0 } : { opacity: 0, x: left > parentPosition.left ? -5 : 5, scale: 0.97 }}
|
||||
animate={{ opacity: 1, x: 0, scale: 1 }}
|
||||
exit={reducedMotion ? { opacity: 0 } : { opacity: 0, x: left > parentPosition.left ? -4 : 4, scale: 0.98 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }}
|
||||
className="aceternity-floating-panel fixed z-[var(--z-popover)] w-[260px] origin-top overflow-hidden rounded-[var(--dock-radius)] border p-2 backdrop-blur-2xl"
|
||||
style={{ left, top: parentPosition.top, background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.node.text }}
|
||||
@@ -342,7 +340,7 @@ function MenuButton({ icon, label, detail, shortcut, badge, chevron = false, act
|
||||
<span className="canvas-menu-item-icon grid size-7 shrink-0 place-items-center rounded-md border opacity-75 group-hover:opacity-100 [&_svg]:size-3.5" style={{ background: danger ? `${theme.accent.danger}12` : theme.spatial.surface, borderColor: danger ? `${theme.accent.danger}33` : theme.toolbar.border, color: danger ? theme.accent.danger : theme.node.text }}>{icon}</span>
|
||||
<span className="min-w-0 flex-1"><span className="flex items-center gap-1 text-xs font-medium"><span className="truncate">{label}</span>{badge ? <span className="rounded-full border px-1 py-0.5 text-[var(--fs-nano)] font-bold" style={{ background: theme.toolbar.activeBg, borderColor: theme.toolbar.border, color: theme.node.muted }}>{badge}</span> : null}</span>{detail ? <span className="mt-0.5 block truncate text-[var(--fs-micro)]" style={{ color: theme.node.muted }}>{detail}</span> : null}</span>
|
||||
{shortcut ? <span className="shrink-0 text-[var(--fs-micro)] opacity-38">{shortcut}</span> : null}
|
||||
{chevron ? <ChevronRight className="size-3 shrink-0 opacity-45 transition-transform group-hover:translate-x-0.5" /> : null}
|
||||
{chevron ? <ChevronRight className="size-3 shrink-0 opacity-45" /> : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user