From 1b4dc08c540e2dbc961d232e83039cd8160bafe2 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Mon, 2 Mar 2026 18:08:03 +0300 Subject: [PATCH 1/9] Add multi-file tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open multiple documents in tabs within a single window, like Figma. - Tab bar appears when 2+ documents are open, with close (×) and new (+) buttons - ⌘N / ⌘T create a new empty tab - ⌘W closes the active tab (last tab closes and reopens as Untitled) - ⌘O opens .fig files in a new tab (reuses current tab if Untitled with no changes) - Middle-click closes a tab - Each tab has its own SceneGraph, UndoManager, viewport, and selection state - Switching tabs remounts the canvas to rebind the Skia renderer to the active store - Keyboard shortcuts, menu actions, and AI tools all operate on the active tab --- src/components/AppMenu.vue | 112 +++++++++++++++++--------------- src/components/TabBar.vue | 55 ++++++++++++++++ src/composables/use-chat.ts | 9 ++- src/composables/use-keyboard.ts | 97 +++++++++++++++------------ src/composables/use-menu.ts | 53 ++++++++------- src/stores/editor.ts | 14 +++- src/stores/tabs.ts | 108 ++++++++++++++++++++++++++++++ src/views/EditorView.vue | 30 +++++---- 8 files changed, 347 insertions(+), 131 deletions(-) create mode 100644 src/components/TabBar.vue create mode 100644 src/stores/tabs.ts diff --git a/src/components/AppMenu.vue b/src/components/AppMenu.vue index 9e30b2f01..89ac2307f 100644 --- a/src/components/AppMenu.vue +++ b/src/components/AppMenu.vue @@ -54,7 +54,12 @@ interface MenuItem { } const fileMenu: MenuItem[] = [ - { label: 'Open…', shortcut: `${mod}O`, action: () => openFileDialog(store) }, + { + label: 'New', + shortcut: `${mod}N`, + action: () => import('@/stores/tabs').then((m) => m.createTab()) + }, + { label: 'Open…', shortcut: `${mod}O`, action: () => openFileDialog() }, { separator: true }, { label: 'Save', shortcut: `${mod}S`, action: () => store.saveFigFile() }, { label: 'Save as…', shortcut: `${mod}⇧S`, action: () => store.saveFigFileAs() }, @@ -159,7 +164,8 @@ const topMenus = [ v-else class="min-w-0 flex-1 cursor-default truncate rounded px-1 py-0.5 text-xs text-surface hover:bg-hover" @dblclick="startRename" - >{{ store.state.documentName }} + >{{ store.state.documentName }}