diff --git a/webui/backend/data/tasks.db b/webui/backend/data/tasks.db index 32c94d5..3a2c117 100644 Binary files a/webui/backend/data/tasks.db and b/webui/backend/data/tasks.db differ diff --git a/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc b/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc index 1894687..44496a7 100644 Binary files a/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc and b/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc differ diff --git a/webui/backend/tests/golden/test_ui_smoke_golden.py b/webui/backend/tests/golden/test_ui_smoke_golden.py index c6ba17a..a20e9ed 100644 --- a/webui/backend/tests/golden/test_ui_smoke_golden.py +++ b/webui/backend/tests/golden/test_ui_smoke_golden.py @@ -50,6 +50,9 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('id="right-focus-line"', body) self.assertIn('id="function-bar"', body) self.assertIn('id="upload-btn"', body) + self.assertIn('id="upload-menu-toggle"', body) + self.assertIn('id="upload-menu-popup"', body) + self.assertIn('id="upload-folder-btn"', body) self.assertIn('id="upload-input"', body) self.assertIn('id="upload-progress"', body) self.assertIn('id="upload-target"', body) @@ -178,10 +181,12 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('function effectiveThemeKey(theme, colorMode)', app_js) self.assertIn("document.documentElement.dataset.theme", app_js) self.assertIn('document.getElementById("theme-toggle").onclick = toggleTheme;', app_js) - self.assertIn('document.getElementById("upload-btn").onclick = (event) => {', app_js) - self.assertIn('if (event.altKey) {', app_js) - self.assertIn("openFolderPicker();", app_js) - self.assertIn("openUploadPicker();", app_js) + self.assertIn('document.getElementById("upload-btn").onclick = openUploadPicker;', app_js) + self.assertIn('document.getElementById("upload-menu-toggle").onclick = (event) => {', app_js) + self.assertIn('document.getElementById("upload-folder-btn").onclick = openFolderPicker;', app_js) + self.assertIn('function closeUploadMenu()', app_js) + self.assertIn('function toggleUploadMenu()', app_js) + self.assertNotIn('if (event.altKey) {', app_js) self.assertIn('document.getElementById("settings-btn").onclick = () => openSettings("general");', app_js) self.assertIn('async function loadSettings()', app_js) self.assertIn('await loadSettings();', app_js) diff --git a/webui/html/app.js b/webui/html/app.js index 5b52c24..a2363d8 100644 --- a/webui/html/app.js +++ b/webui/html/app.js @@ -325,7 +325,11 @@ function infoElements() { function uploadElements() { return { + menu: document.getElementById("upload-menu"), button: document.getElementById("upload-btn"), + menuToggle: document.getElementById("upload-menu-toggle"), + menuPopup: document.getElementById("upload-menu-popup"), + folderButton: document.getElementById("upload-folder-btn"), input: document.getElementById("upload-input"), progress: document.getElementById("upload-progress"), target: document.getElementById("upload-target"), @@ -477,6 +481,25 @@ function setUploadProgressVisible(visible) { uploadElements().progress.classList.toggle("hidden", !visible); } +function closeUploadMenu() { + const elements = uploadElements(); + if (!elements.menuPopup || !elements.menuToggle) { + return; + } + elements.menuPopup.classList.add("hidden"); + elements.menuToggle.setAttribute("aria-expanded", "false"); +} + +function toggleUploadMenu() { + const elements = uploadElements(); + if (!elements.menuPopup || !elements.menuToggle) { + return; + } + const nextOpen = elements.menuPopup.classList.contains("hidden"); + elements.menuPopup.classList.toggle("hidden", !nextOpen); + elements.menuToggle.setAttribute("aria-expanded", nextOpen ? "true" : "false"); +} + function resetUploadProgress() { const elements = uploadElements(); uploadState.active = false; @@ -523,6 +546,7 @@ function openUploadPicker() { if (uploadState.active) { return; } + closeUploadMenu(); uploadState.targetPath = activePaneState().currentPath; const elements = uploadElements(); elements.input.value = ""; @@ -533,6 +557,7 @@ function openFolderPicker() { if (uploadState.active) { return; } + closeUploadMenu(); folderUploadPlanState = { targetPane: state.activePane, targetPath: activePaneState().currentPath, @@ -3040,6 +3065,11 @@ function clearSelectionForActivePane() { } function handleKeyboardShortcuts(event) { + if (event.key === "Escape" && !uploadElements().menuPopup.classList.contains("hidden")) { + event.preventDefault(); + closeUploadMenu(); + return; + } if (isInfoOpen()) { if (event.key === "Escape") { event.preventDefault(); @@ -3264,13 +3294,12 @@ function setupEvents() { setupPaneEvents("right"); document.addEventListener("keydown", handleKeyboardShortcuts); document.getElementById("theme-toggle").onclick = toggleTheme; - document.getElementById("upload-btn").onclick = (event) => { - if (event.altKey) { - openFolderPicker(); - return; - } - openUploadPicker(); + document.getElementById("upload-btn").onclick = openUploadPicker; + document.getElementById("upload-menu-toggle").onclick = (event) => { + event.stopPropagation(); + toggleUploadMenu(); }; + document.getElementById("upload-folder-btn").onclick = openFolderPicker; document.getElementById("settings-btn").onclick = () => openSettings("general"); document.getElementById("view-btn").onclick = openViewer; document.getElementById("edit-btn").onclick = openEditor; @@ -3280,6 +3309,13 @@ function setupEvents() { document.getElementById("move-btn").onclick = openF6Flow; document.getElementById("mkdir-btn").onclick = createFolderForActivePane; uploadElements().input.onchange = handleUploadSelection; + document.addEventListener("click", (event) => { + const elements = uploadElements(); + if (!elements.menu || elements.menu.contains(event.target)) { + return; + } + closeUploadMenu(); + }); const rename = renameElements(); rename.closeButton.onclick = closeRenamePopup; diff --git a/webui/html/base.css b/webui/html/base.css index 1ebdbd4..d8a4329 100644 --- a/webui/html/base.css +++ b/webui/html/base.css @@ -577,6 +577,50 @@ button:disabled { justify-content: center; } +.split-button { + position: relative; + display: inline-flex; + align-items: stretch; +} + +.split-button > button { + min-width: 0; +} + +#upload-btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +#upload-menu-toggle { + min-width: 32px; + padding-left: 7px; + padding-right: 7px; + border-left: 1px solid var(--color-border); + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.split-menu { + position: absolute; + left: 0; + bottom: calc(100% + 6px); + min-width: 148px; + display: grid; + gap: 4px; + padding: 6px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface-elevated); + box-shadow: var(--shadow-elevated); + z-index: 20; +} + +.split-menu button { + width: 100%; + justify-content: flex-start; +} + .shortcut-hint { color: var(--color-text-muted); font-size: 10px; diff --git a/webui/html/index.html b/webui/html/index.html index 68e0b4c..47b9fca 100644 --- a/webui/html/index.html +++ b/webui/html/index.html @@ -85,7 +85,15 @@