diff --git a/app/main.py b/app/main.py index a054f1c..cf3370f 100644 --- a/app/main.py +++ b/app/main.py @@ -39,7 +39,7 @@ async def lifespan(_app: FastAPI): app = FastAPI( title="FlowDeck", - version="2.4.2", + version="2.4.3", docs_url="/docs" if settings.log_level == "DEBUG" else None, redoc_url=None, lifespan=lifespan, diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index d490e49..26908ba 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -341,7 +341,6 @@ @dragleave="onDragLeave($event)" @drop.prevent="onDrop($event)" @keydown="onKeydown($event)" - @contextmenu.prevent="onContextMenu($event)" tabindex="0" x-ref="layout">
@@ -1027,6 +1026,41 @@ document.addEventListener('alpine:init', function() { var self = this; window.addEventListener('online', function() { self.online = true; }); window.addEventListener('offline', function() { self.online = false; }); + // Native context menu handler (bypasses Alpine scoping issues) + self.$el.addEventListener('contextmenu', function(ev) { + ev.preventDefault(); + var el = ev.target.closest('[data-ws-id]'); + if (!el) { self.ctxMenu.visible = false; return; } + var id = parseInt(el.getAttribute('data-ws-id')); + if (!id) { self.ctxMenu.visible = false; return; } + var node = self._findNodeById(self.displayTree, id); + if (!node) { self.ctxMenu.visible = false; return; } + self.ctxMenu.visible = true; + self.ctxMenu.x = ev.clientX; + self.ctxMenu.y = ev.clientY; + self.ctxMenu.node = node; + self.ctxMenu.tags = [...(node.tags||[])]; + self.ctxTagAdding = false; + self.ctxTagExistingOpen = false; + self.ctxNewTagColor = '#787774'; + self.ctxAvailTags = (self.workspaceTags || []).filter(function(t) { + return !(node.tags||[]).some(function(nt) { return nt.id === t.id; }); + }); + // Reposition after DOM update + self.$nextTick(function() { + var menu = self.$el.querySelector('.ctx-menu'); + if (!menu) return; + var rect = menu.getBoundingClientRect(); + var mx = self.ctxMenu.x; + var my = self.ctxMenu.y; + if (mx + rect.width > window.innerWidth - 4) mx = window.innerWidth - rect.width - 4; + if (my + rect.height > window.innerHeight - 4) my = window.innerHeight - rect.height - 4; + if (mx < 4) mx = 4; + if (my < 4) my = 4; + self.ctxMenu.x = mx; + self.ctxMenu.y = my; + }); + }); // Load workspace tags this.loadWorkspaceTags(); } catch(e) { console.error('Tree load error:', e); this.loaded = true; } @@ -1259,7 +1293,7 @@ document.addEventListener('alpine:init', function() { return {}; }, - // ── Context menu ── + // ── Context menu (fallback — native handler in init is primary) ── onContextMenu(ev) { var el = ev.target.closest('[data-ws-id]'); if (!el) { this.ctxMenu.visible = false; return; } @@ -1267,21 +1301,17 @@ document.addEventListener('alpine:init', function() { if (!id) { this.ctxMenu.visible = false; return; } var node = this._findNodeById(this.displayTree, id); if (!node) { this.ctxMenu.visible = false; return; } - this.ctxMenu = { - visible: true, - x: ev.clientX, - y: ev.clientY, - node: node, - tags: [...(node.tags||[])] - }; + this.ctxMenu.visible = true; + this.ctxMenu.x = ev.clientX; + this.ctxMenu.y = ev.clientY; + this.ctxMenu.node = node; + this.ctxMenu.tags = [...(node.tags||[])]; this.ctxTagAdding = false; this.ctxTagExistingOpen = false; this.ctxNewTagColor = '#787774'; - // Load available tags this.ctxAvailTags = (this.workspaceTags || []).filter(function(t) { return !(node.tags||[]).some(function(nt) { return nt.id === t.id; }); }); - // Reposition after DOM update var self = this; self.$nextTick(function() { var menu = self.$el.querySelector('.ctx-menu');