From f50d445dc52c4b4a0b79f7b7a5e7984144d1958c Mon Sep 17 00:00:00 2001 From: DeBaschdi Date: Thu, 3 Sep 2026 05:54:44 +0200 Subject: [PATCH 1/2] Allow picker toolbar actions to be hidden --- modules/vfs-toolkit/vfs-client/README.md | 4 ++++ modules/vfs-toolkit/vfs-client/picker.css | 6 ++++- modules/vfs-toolkit/vfs-client/picker.html | 22 +++++++++---------- modules/vfs-toolkit/vfs-client/picker.mjs | 6 ++++- modules/vfs-toolkit/vfs-client/vfs-client.mjs | 20 +++++++++++++---- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/modules/vfs-toolkit/vfs-client/README.md b/modules/vfs-toolkit/vfs-client/README.md index 51552f0b..78398b18 100644 --- a/modules/vfs-toolkit/vfs-client/README.md +++ b/modules/vfs-toolkit/vfs-client/README.md @@ -28,6 +28,7 @@ Common features: - **Save as:** download a file to the local filesystem (right-click → "Save as…") - Provider dropdown in the location bar when multiple providers are available - File type filter dropdown and filename filter input in the toolbar +- Toolbar action buttons can be hidden without removing the filters - Storage usage shown in the footer - Toolbar buttons and context menu items disabled automatically based on provider capabilities - Progress bar with file counter for batch operations; **✕ cancel button** to abort mid-operation @@ -160,6 +161,7 @@ The picker always supports multi-selecting files for management (copy, move, del | `startIn` | `string` | `null` | Absolute path to open in initially. Ignored when `id` has saved state. | | `multiple` | `boolean` | `false` | Allow confirming multiple files at once. | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | +| `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | @@ -198,6 +200,7 @@ Opens a save file picker popup. The user can navigate to a folder and type (or e | `startIn` | `string` | `null` | Absolute path to open in initially. Ignored when `id` has saved state. | | `suggestedName` | `string` | - | Pre-filled filename in the save input | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | +| `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | @@ -221,6 +224,7 @@ Opens a directory picker popup. The user can navigate to and select a folder. Re | `id` | `string` | `null` | Picker context ID (remembers last-used directory and connection) | | `startIn` | `string` | `null` | Absolute path to open in initially | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | +| `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | diff --git a/modules/vfs-toolkit/vfs-client/picker.css b/modules/vfs-toolkit/vfs-client/picker.css index e9eec66b..85fd2f4f 100644 --- a/modules/vfs-toolkit/vfs-client/picker.css +++ b/modules/vfs-toolkit/vfs-client/picker.css @@ -157,6 +157,10 @@ body { display: contents; } +html.toolbar-actions-hidden #vfs-toolbar .vfs-toolbar-action { + display: none; +} + /* Filter input */ #vfs-type-select { height: 26px; @@ -1070,4 +1074,4 @@ body { display: flex; flex-direction: column; overflow: hidden; -} \ No newline at end of file +} diff --git a/modules/vfs-toolkit/vfs-client/picker.html b/modules/vfs-toolkit/vfs-client/picker.html index 2749a187..d17259a4 100644 --- a/modules/vfs-toolkit/vfs-client/picker.html +++ b/modules/vfs-toolkit/vfs-client/picker.html @@ -17,7 +17,7 @@
- - -
+
- - - - -
+
- -
+
@@ -149,4 +149,4 @@ - \ No newline at end of file + diff --git a/modules/vfs-toolkit/vfs-client/picker.mjs b/modules/vfs-toolkit/vfs-client/picker.mjs index ca7ce4e6..ca70d33d 100644 --- a/modules/vfs-toolkit/vfs-client/picker.mjs +++ b/modules/vfs-toolkit/vfs-client/picker.mjs @@ -9,6 +9,11 @@ import * as vfs from './vfs-client.mjs'; +const params = new URLSearchParams(location.search); +const SHOW_TOOLBAR_ACTIONS = params.get('showToolbarActions') !== '0'; +// Apply the reduced toolbar before locale loading can delay picker initialization. +document.documentElement.classList.toggle('toolbar-actions-hidden', !SHOW_TOOLBAR_ACTIONS); + // ── Locale resolution ───────────────────────────────────────────────────────── @@ -86,7 +91,6 @@ function localizeDocument(doc = document) { // ── Communication mode detection ───────────────────────────────────────────── -const params = new URLSearchParams(location.search); const SESSION_ID = params.get('session'); const MULTIPLE = params.get('multiple') === '1'; const TYPES = params.get('types') ? JSON.parse(params.get('types')) : null; diff --git a/modules/vfs-toolkit/vfs-client/vfs-client.mjs b/modules/vfs-toolkit/vfs-client/vfs-client.mjs index f970e2ad..56a3d9b3 100644 --- a/modules/vfs-toolkit/vfs-client/vfs-client.mjs +++ b/modules/vfs-toolkit/vfs-client/vfs-client.mjs @@ -872,6 +872,8 @@ export async function getStorageUsage(storageRef = null) { * @param {string} [options.opfsStorageName] * Display name for the built-in OPFS (local storage) option in the provider dropdown. * Has no effect on external providers, which use their own reported name. + * @param {boolean} [options.showToolbarActions=true] + * Show the toolbar action buttons. The filters remain visible when set to false. * @param {boolean} [options.multiple=false] - Allow selecting multiple files. When false * (default), the returned array always contains exactly one entry. * @returns {Promise} Array of `Entry` objects (each with `path` and `storageRef`), or null if cancelled. @@ -880,7 +882,7 @@ export function showSelectFilePicker(options = {}) { // mode=open is the default, no extra param needed return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); - const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, multiple = false, id = null, startIn = null, opfsStorageName = null, buttons = null } = options; + const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, multiple = false, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: [] }); @@ -896,6 +898,7 @@ export function showSelectFilePicker(options = {}) { if (startIn) pickerParams.set('startIn', startIn); if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); + if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -930,12 +933,14 @@ export function showSelectFilePicker(options = {}) { * confirm-button effect in browse mode since browse has no confirm button. * @param {string} [options.opfsStorageName] * Display name for the built-in OPFS (local storage) option in the provider dropdown. + * @param {boolean} [options.showToolbarActions=true] + * Show the toolbar action buttons. The filters remain visible when set to false. * @returns {Promise} Always resolves to `null`. */ export function showBrowseFilePicker(options = {}) { return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); - const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, buttons = null } = options; + const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -949,6 +954,7 @@ export function showBrowseFilePicker(options = {}) { if (startIn) pickerParams.set('startIn', startIn); if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); + if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -971,6 +977,8 @@ export function showBrowseFilePicker(options = {}) { * Restrict the picker to the connection given by `storageRef`. See * `showSelectFilePicker` for semantics. * @param {string} [options.opfsStorageName] + * @param {boolean} [options.showToolbarActions=true] + * Show the toolbar action buttons. The filters remain visible when set to false. * @param {number} [options.width=800] * @param {number} [options.height=600] * @returns {Promise} @@ -980,7 +988,7 @@ export function showSaveFilePicker(options = {}) { const sessionId = crypto.randomUUID(); const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, - suggestedName = null, buttons = null } = options; + suggestedName = null, buttons = null, showToolbarActions = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -997,6 +1005,7 @@ export function showSaveFilePicker(options = {}) { if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (suggestedName) pickerParams.set('suggestedName', suggestedName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); + if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -1015,6 +1024,8 @@ export function showSaveFilePicker(options = {}) { * Restrict the picker to the connection given by `storageRef`. See * `showSelectFilePicker` for semantics. * @param {string} [options.opfsStorageName] + * @param {boolean} [options.showToolbarActions=true] + * Show the toolbar action buttons. The filters remain visible when set to false. * @param {number} [options.width=800] * @param {number} [options.height=600] * @returns {Promise} @@ -1023,7 +1034,7 @@ export function showDirectoryPicker(options = {}) { return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, - startIn = null, opfsStorageName = null, buttons = null } = options; + startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -1037,6 +1048,7 @@ export function showDirectoryPicker(options = {}) { if (startIn) pickerParams.set('startIn', startIn); if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); + if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); From baf1c7673ec22208a563afc02b9c7c7bb97c8d3d Mon Sep 17 00:00:00 2001 From: DeBaschdi Date: Thu, 3 Sep 2026 05:58:50 +0200 Subject: [PATCH 2/2] Allow picker context menus to be hidden --- modules/vfs-toolkit/vfs-client/README.md | 4 ++++ modules/vfs-toolkit/vfs-client/picker.mjs | 3 +++ modules/vfs-toolkit/vfs-client/vfs-client.mjs | 21 +++++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/vfs-toolkit/vfs-client/README.md b/modules/vfs-toolkit/vfs-client/README.md index 78398b18..d3478fb4 100644 --- a/modules/vfs-toolkit/vfs-client/README.md +++ b/modules/vfs-toolkit/vfs-client/README.md @@ -29,6 +29,7 @@ Common features: - Provider dropdown in the location bar when multiple providers are available - File type filter dropdown and filename filter input in the toolbar - Toolbar action buttons can be hidden without removing the filters +- Context menus can be hidden independently of keyboard shortcuts - Storage usage shown in the footer - Toolbar buttons and context menu items disabled automatically based on provider capabilities - Progress bar with file counter for batch operations; **✕ cancel button** to abort mid-operation @@ -162,6 +163,7 @@ The picker always supports multi-selecting files for management (copy, move, del | `multiple` | `boolean` | `false` | Allow confirming multiple files at once. | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | | `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | +| `showContextMenu` | `boolean` | `true` | Show the picker context menu for files, folders, and the list background. Keyboard shortcuts remain available when set to `false`. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | @@ -201,6 +203,7 @@ Opens a save file picker popup. The user can navigate to a folder and type (or e | `suggestedName` | `string` | - | Pre-filled filename in the save input | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | | `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | +| `showContextMenu` | `boolean` | `true` | Show the picker context menu for files, folders, and the list background. Keyboard shortcuts remain available when set to `false`. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | @@ -225,6 +228,7 @@ Opens a directory picker popup. The user can navigate to and select a folder. Re | `startIn` | `string` | `null` | Absolute path to open in initially | | `opfsStorageName` | `string` | - | Display name for the `OPFS` backend. | | `showToolbarActions` | `boolean` | `true` | Show the built-in and custom toolbar action buttons. When `false`, the filters remain visible; context-menu actions and keyboard shortcuts remain available. | +| `showContextMenu` | `boolean` | `true` | Show the picker context menu for files, folders, and the list background. Keyboard shortcuts remain available when set to `false`. | | `width` | `number` | `800` | Popup width in pixels | | `height` | `number` | `600` | Popup height in pixels | diff --git a/modules/vfs-toolkit/vfs-client/picker.mjs b/modules/vfs-toolkit/vfs-client/picker.mjs index ca70d33d..20a1beec 100644 --- a/modules/vfs-toolkit/vfs-client/picker.mjs +++ b/modules/vfs-toolkit/vfs-client/picker.mjs @@ -104,6 +104,7 @@ const SUGGESTED_NAME = params.get('suggestedName') ?? null; // the client extension background via browser.runtime.sendMessage so the background // can react (e.g. open a test tab) without any provider involvement. const BUTTONS = params.get('buttons') ? JSON.parse(params.get('buttons')) : []; +const SHOW_CONTEXT_MENU = params.get('showContextMenu') !== '0'; // 'strict' | 'soft' | null. When set, the picker is locked to LOCKED_REF: // 'strict' hides the provider selector entirely; 'soft' disables the confirm // button while the user browses a different connection. @@ -428,6 +429,7 @@ function buildRow(entry) { // Context menu row.addEventListener('contextmenu', e => { + if (!SHOW_CONTEXT_MENU) return; e.preventDefault(); if (!state.selected.has(entry.name)) { selectEntry(entry.name, isDir ? null : entryPath); @@ -2160,6 +2162,7 @@ async function init() { if (!contextMenu.contains(e.target)) hideContextMenu(); }); listArea.addEventListener('contextmenu', e => { + if (!SHOW_CONTEXT_MENU) return; if (!e.target.closest('.vfs-row')) { e.preventDefault(); e.stopPropagation(); diff --git a/modules/vfs-toolkit/vfs-client/vfs-client.mjs b/modules/vfs-toolkit/vfs-client/vfs-client.mjs index 56a3d9b3..5e3c6687 100644 --- a/modules/vfs-toolkit/vfs-client/vfs-client.mjs +++ b/modules/vfs-toolkit/vfs-client/vfs-client.mjs @@ -874,6 +874,8 @@ export async function getStorageUsage(storageRef = null) { * Has no effect on external providers, which use their own reported name. * @param {boolean} [options.showToolbarActions=true] * Show the toolbar action buttons. The filters remain visible when set to false. + * @param {boolean} [options.showContextMenu=true] + * Show the picker context menu for files, folders, and the list background. * @param {boolean} [options.multiple=false] - Allow selecting multiple files. When false * (default), the returned array always contains exactly one entry. * @returns {Promise} Array of `Entry` objects (each with `path` and `storageRef`), or null if cancelled. @@ -882,7 +884,7 @@ export function showSelectFilePicker(options = {}) { // mode=open is the default, no extra param needed return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); - const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, multiple = false, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; + const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, multiple = false, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true, showContextMenu = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: [] }); @@ -899,6 +901,7 @@ export function showSelectFilePicker(options = {}) { if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); + if (!showContextMenu) pickerParams.set('showContextMenu', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -935,12 +938,14 @@ export function showSelectFilePicker(options = {}) { * Display name for the built-in OPFS (local storage) option in the provider dropdown. * @param {boolean} [options.showToolbarActions=true] * Show the toolbar action buttons. The filters remain visible when set to false. + * @param {boolean} [options.showContextMenu=true] + * Show the picker context menu for files, folders, and the list background. * @returns {Promise} Always resolves to `null`. */ export function showBrowseFilePicker(options = {}) { return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); - const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; + const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true, showContextMenu = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -955,6 +960,7 @@ export function showBrowseFilePicker(options = {}) { if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); + if (!showContextMenu) pickerParams.set('showContextMenu', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -979,6 +985,8 @@ export function showBrowseFilePicker(options = {}) { * @param {string} [options.opfsStorageName] * @param {boolean} [options.showToolbarActions=true] * Show the toolbar action buttons. The filters remain visible when set to false. + * @param {boolean} [options.showContextMenu=true] + * Show the picker context menu for files, folders, and the list background. * @param {number} [options.width=800] * @param {number} [options.height=600] * @returns {Promise} @@ -988,7 +996,7 @@ export function showSaveFilePicker(options = {}) { const sessionId = crypto.randomUUID(); const { types = null, excludeAcceptAllOption = false, width = 800, height = 600, storageRef = null, lockStorage = null, id = null, startIn = null, opfsStorageName = null, - suggestedName = null, buttons = null, showToolbarActions = true } = options; + suggestedName = null, buttons = null, showToolbarActions = true, showContextMenu = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -1006,6 +1014,7 @@ export function showSaveFilePicker(options = {}) { if (suggestedName) pickerParams.set('suggestedName', suggestedName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); + if (!showContextMenu) pickerParams.set('showContextMenu', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); }); @@ -1026,6 +1035,8 @@ export function showSaveFilePicker(options = {}) { * @param {string} [options.opfsStorageName] * @param {boolean} [options.showToolbarActions=true] * Show the toolbar action buttons. The filters remain visible when set to false. + * @param {boolean} [options.showContextMenu=true] + * Show the picker context menu for files, folders, and the list background. * @param {number} [options.width=800] * @param {number} [options.height=600] * @returns {Promise} @@ -1034,7 +1045,8 @@ export function showDirectoryPicker(options = {}) { return new Promise((resolve, reject) => { const sessionId = crypto.randomUUID(); const { width = 800, height = 600, storageRef = null, lockStorage = null, id = null, - startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true } = options; + startIn = null, opfsStorageName = null, buttons = null, showToolbarActions = true, + showContextMenu = true } = options; pendingPickers.set(sessionId, { resolve, reject, defaultValue: null }); @@ -1049,6 +1061,7 @@ export function showDirectoryPicker(options = {}) { if (opfsStorageName) pickerParams.set('opfsStorageName', opfsStorageName); if (buttons?.length) pickerParams.set('buttons', JSON.stringify(buttons)); if (!showToolbarActions) pickerParams.set('showToolbarActions', '0'); + if (!showContextMenu) pickerParams.set('showContextMenu', '0'); _openPopupWindow(sessionId, pickerParams, width, height).catch(reject); });