refactor(app): align canvas menu file layout

This commit is contained in:
Danila Poyarkov 2026-05-17 15:29:14 +03:00
parent f37a263279
commit 1d8f4bf8c4
4 changed files with 1 additions and 86 deletions

View file

@ -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<Set<string>>) {
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 }
}

View file

@ -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'
}

View file

@ -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'