{flowQuickCreate ? (
diff --git a/apps/web/src/components/DiagramToolbar.tsx b/apps/web/src/components/DiagramToolbar.tsx
new file mode 100644
index 00000000..58a071f1
--- /dev/null
+++ b/apps/web/src/components/DiagramToolbar.tsx
@@ -0,0 +1,131 @@
+import type { PointerEventHandler, ReactNode } from "react";
+import {
+ Boxes,
+ ChevronDown,
+ Download,
+ FileCode2,
+ FileImage,
+ Maximize2,
+ Redo2,
+ Trash2,
+ Undo2,
+ WandSparkles,
+ ZoomIn,
+ ZoomOut,
+} from "lucide-react";
+import { useTranslation } from "react-i18next";
+import type { DiagramTheme } from "@edgeever/shared";
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
+import { resolveDiagramPalette, type DiagramAppearance } from "@/lib/diagram-theme";
+
+type DiagramToolbarProps = {
+ appearance: DiagramAppearance;
+ canRedo: boolean;
+ canUndo: boolean;
+ hasSelection: boolean;
+ leading?: ReactNode;
+ onAutoLayout: () => void;
+ onDeleteSelection: () => void;
+ onExport: (format: "png" | "svg") => void;
+ onFit: () => void;
+ onRedo: () => void;
+ onThemeChange: (theme: DiagramTheme) => void;
+ onUndo: () => void;
+ onZoomIn: () => void;
+ onZoomOut: () => void;
+ readOnly: boolean;
+ selectionEditor?: ReactNode;
+ theme: DiagramTheme;
+};
+
+const ToolbarDivider = () =>
;
+
+export const DiagramToolbarAddTrigger = ({
+ onPointerEnter,
+}: {
+ onPointerEnter: PointerEventHandler
;
+}) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+ );
+};
+
+export const DiagramToolbar = ({
+ appearance,
+ canRedo,
+ canUndo,
+ hasSelection,
+ leading,
+ onAutoLayout,
+ onDeleteSelection,
+ onExport,
+ onFit,
+ onRedo,
+ onThemeChange,
+ onUndo,
+ onZoomIn,
+ onZoomOut,
+ readOnly,
+ selectionEditor,
+ theme,
+}: DiagramToolbarProps) => {
+ const { t } = useTranslation();
+ return (
+
+ {leading ? <>{leading}> : null}
+ {t("diagram.undo")}
+ {t("diagram.redo")}
+ {!readOnly && {t("diagram.deleteSelection")}}
+ {!readOnly && (
+
+
+
+
+ {t("diagram.autoLayout")}
+
+ )}
+
+ {t("diagram.zoomOut")}
+ {t("diagram.zoomIn")}
+ {t("diagram.fit")}
+
+
+
+
+
+
+ onExport("png")}>{t("diagram.exportPng")}
+ onExport("svg")}>{t("diagram.exportSvg")}
+
+
+ {selectionEditor}
+
+ );
+};
diff --git a/packages/shared/src/i18n/en-US.ts b/packages/shared/src/i18n/en-US.ts
index dbc73fbc..456a2ada 100644
--- a/packages/shared/src/i18n/en-US.ts
+++ b/packages/shared/src/i18n/en-US.ts
@@ -351,6 +351,7 @@ export const enUS = {
zoomOut: "Zoom out",
fit: "Fit diagram",
autoLayout: "Auto-arrange",
+ toolbar: "Diagram toolbar",
theme: "Diagram theme",
themeBrand: "Forest Green",
themeOcean: "Mint paper",
diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts
index afb66446..62ebf344 100644
--- a/packages/shared/src/i18n/zh-CN.ts
+++ b/packages/shared/src/i18n/zh-CN.ts
@@ -351,6 +351,7 @@ export const zhCN = {
zoomOut: "缩小",
fit: "适应画布",
autoLayout: "自动整理布局",
+ toolbar: "图形工具栏",
theme: "图表主题",
themeBrand: "森绿",
themeOcean: "薄荷纸",