+
+
+ 📐
+ {projectName || 'LVGL UI Editor'}
+
-
-
-
-
-
-
useEditorStore.getState().undo()} shortcut="Ctrl+Z" />
- useEditorStore.getState().redo()} shortcut="Ctrl+Y" />
-
- setShowResourcePanel(!showResourcePanel)}
- active={showResourcePanel}
- />
- setShowProjectSettings(true)}
- />
-
-
-
- setShowHelpPanel(true)}
- shortcut="F1"
- />
+ {/* Main tabs */}
+
+
+
+
+
+
+
+
+
+
+
+
+
useEditorStore.getState().undo()} shortcut="Ctrl+Z" />
+ useEditorStore.getState().redo()} shortcut="Ctrl+Y" />
+
+ setShowResourcePanel(!showResourcePanel)}
+ active={showResourcePanel}
+ />
+ setShowProjectSettings(true)}
+ />
+
+
+
+ setShowHelpPanel(true)}
+ shortcut="F1"
+ />
+
void;
+}
+
+interface DesktopMenuBarProps {
+ projectName: string;
+ activeTab: EditorTab;
+ showResourcePanel: boolean;
+ onNewProject: () => void;
+ onOpenProject: () => void;
+ onSaveProject: () => void;
+ onExportProject: () => void;
+ onImportProject: () => void;
+ onUndo: () => void;
+ onRedo: () => void;
+ onSelectTab: (tab: EditorTab) => void;
+ onToggleResources: () => void;
+ onOpenSettings: () => void;
+ onOpenHelp: () => void;
+}
+
+const DesktopMenuBar = ({
+ projectName,
+ activeTab,
+ showResourcePanel,
+ onNewProject,
+ onOpenProject,
+ onSaveProject,
+ onExportProject,
+ onImportProject,
+ onUndo,
+ onRedo,
+ onSelectTab,
+ onToggleResources,
+ onOpenSettings,
+ onOpenHelp,
+}: DesktopMenuBarProps) => {
+ const [openMenuId, setOpenMenuId] = useState
(null);
+ const hostMode = isDesktopHostAvailable() ? 'Desktop' : 'Web';
+ const containerRef = useRef(null);
+
+ useEffect(() => {
+ const handlePointerDown = (event: MouseEvent) => {
+ if (!containerRef.current?.contains(event.target as Node)) {
+ setOpenMenuId(null);
+ }
+ };
+
+ window.addEventListener('mousedown', handlePointerDown);
+ return () => window.removeEventListener('mousedown', handlePointerDown);
+ }, []);
+
+ const menus = useMemo(
+ () => [
+ {
+ id: 'file',
+ label: '文件',
+ items: [
+ { id: 'new', label: '新建项目', shortcut: 'Ctrl+N', onClick: onNewProject },
+ { id: 'open', label: '打开项目', shortcut: 'Ctrl+O', onClick: onOpenProject },
+ { id: 'save', label: '保存项目', shortcut: 'Ctrl+S', onClick: onSaveProject },
+ { id: 'export', label: '导出项目', onClick: onExportProject },
+ { id: 'import', label: '导入项目', onClick: onImportProject },
+ ] satisfies MenuAction[],
+ },
+ {
+ id: 'edit',
+ label: '编辑',
+ items: [
+ { id: 'undo', label: '撤销', shortcut: 'Ctrl+Z', onClick: onUndo },
+ { id: 'redo', label: '重做', shortcut: 'Ctrl+Y', onClick: onRedo },
+ ] satisfies MenuAction[],
+ },
+ {
+ id: 'view',
+ label: '视图',
+ items: [
+ { id: 'design', label: '设计视图', active: activeTab === 'design', onClick: () => onSelectTab('design') },
+ { id: 'logic', label: '逻辑视图', active: activeTab === 'logic', onClick: () => onSelectTab('logic') },
+ { id: 'code', label: '代码视图', active: activeTab === 'code', onClick: () => onSelectTab('code') },
+ { id: 'preview', label: '预览视图', active: activeTab === 'preview', onClick: () => onSelectTab('preview') },
+ { id: 'resources', label: showResourcePanel ? '隐藏资源面板' : '显示资源面板', active: showResourcePanel, onClick: onToggleResources },
+ { id: 'settings', label: '项目设置', onClick: onOpenSettings },
+ ] satisfies MenuAction[],
+ },
+ {
+ id: 'help',
+ label: '帮助',
+ items: [
+ { id: 'shortcuts', label: '快捷键帮助', shortcut: 'F1', onClick: onOpenHelp },
+ ] satisfies MenuAction[],
+ },
+ ],
+ [
+ activeTab,
+ onExportProject,
+ onImportProject,
+ onNewProject,
+ onOpenHelp,
+ onOpenProject,
+ onOpenSettings,
+ onRedo,
+ onSaveProject,
+ onSelectTab,
+ onToggleResources,
+ onUndo,
+ showResourcePanel,
+ ],
+ );
+
+ const handleMenuAction = (action: MenuAction) => {
+ setOpenMenuId(null);
+ action.onClick();
+ };
+
+ return (
+
+
+ {menus.map(menu => (
+
+
+ {openMenuId === menu.id && (
+
+ {menu.items.map(item => (
+
+ ))}
+
+ )}
+
+ ))}
+
+
+
+ {projectName || 'LVGL UI Editor'}
+ {hostMode}
+
+
+ );
+};
+
+export default DesktopMenuBar;
diff --git a/src/utils/desktopHost.ts b/src/utils/desktopHost.ts
new file mode 100644
index 0000000..12a7139
--- /dev/null
+++ b/src/utils/desktopHost.ts
@@ -0,0 +1,3 @@
+export function isDesktopHostAvailable() {
+ return typeof window !== 'undefined' && typeof window.omni !== 'undefined';
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index f10863c..5706733 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -6,3 +6,19 @@ declare module 'virtual:compile-preview' {
const CompilePreview: FC;
export default CompilePreview;
}
+
+interface OmniHostWindowApi {
+ minimize: () => void;
+ maximize: () => void;
+ close: () => void;
+}
+
+interface OmniHostBridge {
+ window: OmniHostWindowApi;
+ invoke?: (handler: string, payload?: unknown) => Promise;
+ on?: (event: string, handler: (payload: unknown) => void) => void;
+}
+
+interface Window {
+ omni?: OmniHostBridge;
+}