Merge branch 'master' into fig-effective-text-shaping

This commit is contained in:
Danila Poyarkov 2026-08-05 16:23:42 +03:00 committed by GitHub
commit 2a4beb83ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 46 additions and 0 deletions

View file

@ -12,6 +12,10 @@
"label": "Open…",
"accelerator": "CmdOrCtrl+O"
},
{
"id": "open-storage-workspace",
"label": "Open Storage Workspace…"
},
{
"type": "separator"
},

View file

@ -7,6 +7,7 @@
"text": "Text",
"new": "Neu",
"open": "Öffnen…",
"openStorageWorkspace": "Speicher-Arbeitsbereich öffnen…",
"save": "Speichern",
"saveAs": "Speichern unter…",
"exportSelection": "Auswahl exportieren…",

View file

@ -7,6 +7,7 @@
"text": "Texto",
"new": "Nuevo",
"open": "Abrir…",
"openStorageWorkspace": "Abrir espacio de almacenamiento…",
"save": "Guardar",
"saveAs": "Guardar como…",
"exportSelection": "Exportar selección…",

View file

@ -7,6 +7,7 @@
"text": "Texte",
"new": "Nouveau",
"open": "Ouvrir…",
"openStorageWorkspace": "Ouvrir l’espace de stockage…",
"save": "Enregistrer",
"saveAs": "Enregistrer sous…",
"exportSelection": "Exporter la sélection…",

View file

@ -7,6 +7,7 @@
"text": "Testo",
"new": "Nuovo",
"open": "Apri…",
"openStorageWorkspace": "Apri area di archiviazione…",
"save": "Salva",
"saveAs": "Salva con nome…",
"exportSelection": "Esporta selezione…",

View file

@ -7,6 +7,7 @@
"text": "テキスト",
"new": "新規作成",
"open": "開く…",
"openStorageWorkspace": "ストレージワークスペースを開く…",
"save": "保存",
"saveAs": "名前を付けて保存…",
"exportSelection": "選択範囲をエクスポート…",

View file

@ -7,6 +7,7 @@
"text": "Tekst",
"new": "Nowy",
"open": "Otwórz…",
"openStorageWorkspace": "Otwórz obszar przechowywania…",
"save": "Zapisz",
"saveAs": "Zapisz jako…",
"exportSelection": "Eksportuj zaznaczenie…",

View file

@ -7,6 +7,7 @@
"text": "Текст",
"new": "Создать",
"open": "Открыть…",
"openStorageWorkspace": "Открыть рабочее пространство хранилища…",
"save": "Сохранить",
"saveAs": "Сохранить как…",
"exportSelection": "Экспорт выделения…",

View file

@ -7,6 +7,7 @@
"text": "文本",
"new": "新建",
"open": "打开…",
"openStorageWorkspace": "打开存储工作区…",
"save": "保存",
"saveAs": "另存为…",
"exportSelection": "导出所选内容…",

View file

@ -10,6 +10,7 @@ export const menuMessageDefaults = {
new: 'New',
open: 'Open…',
openStorageWorkspace: 'Open storage workspace…',
save: 'Save',
saveAs: 'Save as…',
exportSelection: 'Export selection…',

View file

@ -1,4 +1,5 @@
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import type { MenuEntry } from '@open-pencil/vue'
import { useEditorCommands, useI18n } from '@open-pencil/vue'
@ -6,6 +7,7 @@ import { useEditorCommands, useI18n } from '@open-pencil/vue'
import { useEditorStore } from '@/app/editor/active-store'
import { openSettingsDialog } from '@/app/settings/dialog'
import { createSharedEditorMenuActions } from '@/app/shell/menu/editor-actions'
import { openStorageWorkspace } from '@/app/shell/menu/navigation'
import type { AppMenuActionItem, AppMenuEntry, AppMenuGroupSchema } from '@/app/shell/menu/schema'
import { APP_MENU_SCHEMA } from '@/app/shell/menu/schema'
import { createSelectionMenuActions } from '@/app/shell/menu/selection-actions'
@ -29,6 +31,7 @@ function isSeparator(entry: AppMenuEntry): entry is Extract<AppMenuEntry, { type
export function useAppMenu() {
const store = useEditorStore()
const router = useRouter()
const {
commands,
menuItem: commandMenuItem,
@ -41,6 +44,7 @@ export function useAppMenu() {
const translatedMenuItemLabels: Partial<Record<string, keyof typeof menu.value>> = {
new: 'new',
open: 'open',
'open-storage-workspace': 'openStorageWorkspace',
save: 'save',
'save-as': 'saveAs',
'export-selection': 'exportSelection',
@ -94,6 +98,7 @@ export function useAppMenu() {
void import('@/app/tabs').then((m) => m.createTab())
},
open: () => void openFileDialog(),
'open-storage-workspace': () => openStorageWorkspace(router),
save: () => void store.saveFigFile(),
'save-as': () => void store.saveFigFileAs(),
'export-selection': () => exportSelection('png'),

View file

@ -0,0 +1,5 @@
import type { Router } from 'vue-router'
export function openStorageWorkspace(router: Router): void {
void router.push('/storage')
}

View file

@ -33,6 +33,7 @@ export const APP_MENU_SCHEMA = [
items: [
{ id: 'new', label: 'New', shortcut: 'MOD+N' },
{ id: 'open', label: 'Open…', shortcut: 'MOD+O' },
{ id: 'open-storage-workspace', label: 'Open Storage Workspace…' },
{ type: 'separator' },
{ id: 'save', label: 'Save', shortcut: 'MOD+S' },
{ id: 'save-as', label: 'Save As…', shortcut: 'MOD+SHIFT+S' },

View file

@ -7,6 +7,7 @@ import { useEditorStore } from '@/app/editor/active-store'
import { openSettingsDialog } from '@/app/settings/dialog'
import { createSharedEditorMenuActions } from '@/app/shell/menu/editor-actions'
import { importFileDialog, openFileDialog } from '@/app/shell/menu/files'
import { openStorageWorkspace } from '@/app/shell/menu/navigation'
import { APP_MENU_SCHEMA, type AppMenuEntry } from '@/app/shell/menu/schema'
import { createSelectionMenuActions } from '@/app/shell/menu/selection-actions'
import { useAppTheme } from '@/app/shell/theme'
@ -40,6 +41,9 @@ export function useMenu() {
const actions: Partial<Record<string, () => void>> = {
new: () => createTab(),
open: () => void openFileDialog(),
'open-storage-workspace': () => {
void import('@/router').then(({ default: router }) => openStorageWorkspace(router))
},
close: () => {
if (activeTab.value) closeTab(activeTab.value.id)
},

View file

@ -20,6 +20,7 @@ test('File menu opens and shows items', async () => {
const items = await menu.locator('[role="menuitem"]').allTextContents()
expect(items.some((t) => t.includes('Open'))).toBe(true)
expect(items.some((t) => t.includes('Open storage workspace'))).toBe(true)
expect(items.some((t) => t.includes('Save'))).toBe(true)
expect(items.some((t) => t.includes('Save as'))).toBe(true)
@ -155,3 +156,11 @@ test('Zoom to fit via View menu works', async () => {
const zoomAfter = await getStoreStateNumber('zoom')
expect(zoomAfter).not.toBe(zoomBefore)
})
test('Open storage workspace navigates from the File menu', async () => {
await editor.page.getByRole('menuitem', { name: 'File', exact: true }).click()
await editor.page.getByRole('menuitem', { name: 'Open storage workspace…' }).click()
await expect(editor.page).toHaveURL(/\/storage$/)
await expect(editor.page.getByRole('heading', { name: 'Storage workspace' })).toBeVisible()
})

View file

@ -43,6 +43,15 @@ describe('APP_MENU_SCHEMA', () => {
expect(commandIds).toContain('selection.moveToPage')
})
test('includes storage workspace navigation in the shared File menu', () => {
const fileMenu = APP_MENU_SCHEMA.find((group) => group.label === 'File')
const entries = fileMenu ? actionItems(fileMenu.items) : []
expect(entries).toContainEqual(
expect.objectContaining({ id: 'open-storage-workspace', label: 'Open Storage Workspace…' })
)
})
test('keeps move-to-page destination selection in the browser menu', () => {
const objectMenu = APP_MENU_SCHEMA.find((group) => group.label === 'Object')
const entries = objectMenu ? actionItems(objectMenu.items) : []