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');