From 1d8f4bf8c484d632cee56b71db5b211d4ba2b893 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sun, 17 May 2026 15:29:14 +0300 Subject: [PATCH] refactor(app): align canvas menu file layout --- src/app/editor/canvas/menu-actions.ts | 71 ------------------- src/app/editor/canvas/menu-model.ts | 14 ---- .../{context-menu.ts => menu/context.ts} | 0 src/components/CanvasMenu.vue | 2 +- 4 files changed, 1 insertion(+), 86 deletions(-) delete mode 100644 src/app/editor/canvas/menu-actions.ts delete mode 100644 src/app/editor/canvas/menu-model.ts rename src/app/editor/canvas/{context-menu.ts => menu/context.ts} (100%) diff --git a/src/app/editor/canvas/menu-actions.ts b/src/app/editor/canvas/menu-actions.ts deleted file mode 100644 index 344fccdda..000000000 --- a/src/app/editor/canvas/menu-actions.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { useClipboard } from '@vueuse/core' -import type { Ref } from 'vue' - -import { nodeToXPath } from '@open-pencil/core/xpath' - -import type { EditorStore } from '@/app/editor/active-store' -import { toast } from '@/app/shell/ui' - -export const COPY_AS_PNG_SHORTCUT = 'MOD+SHIFT+C' - -function toArrayBuffer(data: Uint8Array): ArrayBuffer { - const bytes = new Uint8Array(data.length) - bytes.set(data) - return bytes.buffer -} - -export function createCanvasMenuActions(store: EditorStore, selectedIds: Ref>) { - const { copy } = useClipboard() - - function ids() { - return [...selectedIds.value] - } - - function execCommand(cmd: 'copy' | 'cut' | 'paste') { - try { - if (window.document.execCommand(cmd)) return - } catch (error) { - console.warn(`Clipboard command ${cmd} failed`, error) - } - - toast.error('Clipboard access is blocked in this browser context') - } - - async function clipboardWrite(text: string | null, label: string) { - if (!text) return - await copy(text) - toast.info(`Copied as ${label}`) - } - - async function copyNodeId() { - const nodeIds = ids() - if (nodeIds.length === 0) return - await copy(nodeIds.join(', ')) - toast.info(`Copied node ID${nodeIds.length > 1 ? 's' : ''}`) - } - - async function copyXPath() { - const nodeIds = ids() - if (nodeIds.length === 0) return - const xpaths = nodeIds - .map((id) => nodeToXPath(store.graph, id)) - .filter((xpath): xpath is string => xpath !== null) - if (xpaths.length === 0) return - await copy(xpaths.join('\n')) - toast.info(`Copied XPath${xpaths.length > 1 ? 's' : ''}`) - } - - async function copyAsPNG() { - if (typeof ClipboardItem === 'undefined') { - toast.error('PNG clipboard export is not available in this browser') - return - } - const data = await store.renderExportImage(ids(), 2, 'PNG') - if (!data) return - const blob = new Blob([toArrayBuffer(data)], { type: 'image/png' }) - await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]) - toast.info('Copied as PNG') - } - - return { ids, execCommand, clipboardWrite, copyNodeId, copyXPath, copyAsPNG } -} diff --git a/src/app/editor/canvas/menu-model.ts b/src/app/editor/canvas/menu-model.ts deleted file mode 100644 index d409343ad..000000000 --- a/src/app/editor/canvas/menu-model.ts +++ /dev/null @@ -1,14 +0,0 @@ -const COMPONENT_MENU_LABEL_PARTS = ['component', 'instance'] - -export function isComponentMenuItem(label: string) { - const normalized = label.toLowerCase() - return COMPONENT_MENU_LABEL_PARTS.some((part) => normalized.includes(part)) -} - -export function canvasMenuItemClass(label: string, classes: { item: string; component: string }) { - return isComponentMenuItem(label) ? classes.component : classes.item -} - -export function canvasMenuShortcutClass(label: string) { - return isComponentMenuItem(label) ? 'text-component/60' : 'text-muted' -} diff --git a/src/app/editor/canvas/context-menu.ts b/src/app/editor/canvas/menu/context.ts similarity index 100% rename from src/app/editor/canvas/context-menu.ts rename to src/app/editor/canvas/menu/context.ts diff --git a/src/components/CanvasMenu.vue b/src/components/CanvasMenu.vue index 98a6cc4d1..14aaefaa0 100644 --- a/src/components/CanvasMenu.vue +++ b/src/components/CanvasMenu.vue @@ -22,7 +22,7 @@ import type { EditorCommandId } from '@open-pencil/vue' import { useEditorStore } from '@/app/editor/active-store' import { appMenuShortcutLabel } from '@/app/shell/menu/shortcut' import { createCanvasMenuActions } from '@/app/editor/canvas/menu/actions' -import { useCanvasContextMenu } from '@/app/editor/canvas/context-menu' +import { useCanvasContextMenu } from '@/app/editor/canvas/menu/context' import { canvasMenuItemClass, canvasMenuShortcutClass } from '@/app/editor/canvas/menu/model' import { menu, useMenuUI } from '@/components/ui/menu'