From 7dece1e4b5f47bcdb58b056d0ae66f7b84652b8d Mon Sep 17 00:00:00 2001 From: Laura Sach <5183697+lawsie@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:25:57 +0100 Subject: [PATCH 1/2] Hide toolbar if workspace not visible --- main/view.js | 1 + ui/contextmenu.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/main/view.js b/main/view.js index 41cffa01..814b4e6c 100644 --- a/main/view.js +++ b/main/view.js @@ -548,6 +548,7 @@ export function togglePlayMode() { window.flockShortcutsPanel?.panel?.classList.contains('hidden') ?? true ); window.flockShortcutsPanel?.hide(); + window.flockBlockToolbar?.hide(); blocklyArea.style.display = 'none'; gizmoButtons.style.display = 'none'; bottomBar.style.display = 'none'; diff --git a/ui/contextmenu.js b/ui/contextmenu.js index e20d4dd5..8b30d539 100644 --- a/ui/contextmenu.js +++ b/ui/contextmenu.js @@ -884,10 +884,20 @@ export function initContextMenus(workspace) { }; } + // Play mode hides #codePanel without deselecting the block (main/view.js). + const workspaceIsVisible = () => { + const div = workspace.getInjectionDiv?.(); + return !!div && div.clientWidth > 0 && div.clientHeight > 0; + }; + function positionBlockToolbar() { if (!toolbarBlock) return; const svgRoot = toolbarBlock.getSvgRoot?.(); if (!svgRoot) return; + if (!workspaceIsVisible()) { + hideBlockToolbar(); + return; + } const rect = getOwnBlockScreenRect(toolbarBlock) ?? svgRoot.getBoundingClientRect(); const blockCenterX = Math.round(rect.left + rect.width / 2); blockToolbar.style.left = `${blockCenterX}px`; @@ -1006,6 +1016,8 @@ export function initContextMenus(workspace) { clearBadges(); } + window.flockBlockToolbar = { hide: hideBlockToolbar }; + const isToolbarBlock = (block) => block && !block.isInFlyout && !block.isShadow(); workspace.addChangeListener((e) => { From 844bec3870d7463367f98f0043d3585ac5221cbb Mon Sep 17 00:00:00 2001 From: Laura Sach <5183697+lawsie@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:39:55 +0100 Subject: [PATCH 2/2] Add test --- tests/contextmenu.test.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/contextmenu.test.js b/tests/contextmenu.test.js index 75b49e52..8ab1e079 100644 --- a/tests/contextmenu.test.js +++ b/tests/contextmenu.test.js @@ -13,6 +13,7 @@ export function runContextMenuTests(_flock) { let container; let createdBlocks; let previousMainWorkspace; + let blockToolbar; before(function () { // The full app registers block types during its Blockly-init sequence, @@ -30,6 +31,8 @@ export function runContextMenuTests(_flock) { document.body.appendChild(container); workspace = Blockly.inject(container, { collapse: true }); initContextMenus(workspace); + // initContextMenus appends its toolbar to ; ours is the newest. + blockToolbar = [...document.querySelectorAll('.fc-block-toolbar')].pop(); }); after(function () { @@ -272,5 +275,35 @@ export function runContextMenuTests(_flock) { expect(target.nextConnection.isConnected()).to.equal(true); }); }); + + describe('floating block toolbar', function () { + // Blockly fires its change events asynchronously. + const flush = () => new Promise((resolve) => setTimeout(resolve, 50)); + + it('dismisses itself when repositioned against a collapsed workspace', async function () { + const block = makeBlock(); + // Keyboard modality shows the toolbar at once; pointer waits for a hover. + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + block.select(); + await flush(); + expect(blockToolbar.classList.contains('visible')).to.equal(true); + + // Collapse rather than display:none — the toolbar survives this, so the + // dismissal below is the visibility guard rather than a Blockly deselect. + container.style.width = '0px'; + container.style.height = '0px'; + try { + await flush(); + expect(blockToolbar.classList.contains('visible')).to.equal(true); + + block.moveBy(1, 1); // any event that repositions the toolbar + await flush(); + expect(blockToolbar.classList.contains('visible')).to.equal(false); + } finally { + container.style.width = '300px'; + container.style.height = '200px'; + } + }); + }); }); }