From 8c5f5a4a06acc5e73803f5f0f4418491f607d81d Mon Sep 17 00:00:00 2001 From: "slu@innovaphone.com" Date: Thu, 10 Sep 2026 13:15:58 +0200 Subject: [PATCH] Make file browser path an editable, pasteable input Lets the current-directory display accept a typed or pasted path and jump the tree there on Enter, instead of only navigating by clicking through folders. Strips surrounding quotes from Windows Explorer's "Copy as path" so pasted paths work directly. Co-Authored-By: Claude Sonnet 5 --- public/file-browser.js | 35 ++++++++++++++++++++++++++++++++++- public/index.html | 2 +- public/style.css | 16 +++++++++++++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/public/file-browser.js b/public/file-browser.js index efd73805..b7da0856 100644 --- a/public/file-browser.js +++ b/public/file-browser.js @@ -46,6 +46,7 @@ export class FileBrowser { this.currentPath = null; this.setupDropTarget(); + this.setupPathInput(); } async load(dirPath) { @@ -64,7 +65,7 @@ export class FileBrowser { } this.currentPath = data.path; - this.pathEl.textContent = data.path; + this.pathEl.value = data.path; this.pathEl.title = data.path; this.render(data.items); } catch (err) { @@ -162,6 +163,38 @@ export class FileBrowser { if (this.onFileInserted) this.onFileInserted(filePath); } + setupPathInput() { + const input = this.pathEl; + + // Focused: left-align and select all, so a paste replaces the whole path. + input.addEventListener('focus', () => { + input.dir = 'ltr'; + input.select(); + }); + + // Unfocused: right-align (ellipsis at the start) to show the deepest folder. + input.addEventListener('blur', () => { + input.dir = 'rtl'; + if (this.currentPath) input.value = this.currentPath; + }); + + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + let path = input.value.trim(); + // Strip quotes added by "Copy as path" in Windows Explorer. + if (path.length > 1 && ((path.startsWith('"') && path.endsWith('"')) || (path.startsWith("'") && path.endsWith("'")))) { + path = path.slice(1, -1).trim(); + } + if (path && path !== this.currentPath) this.load(path); + input.blur(); + } else if (e.key === 'Escape') { + e.preventDefault(); + input.blur(); + } + }); + } + setupDropTarget() { const input = this.messageInput; diff --git a/public/index.html b/public/index.html index 9531c217..15bfcf7c 100644 --- a/public/index.html +++ b/public/index.html @@ -159,7 +159,7 @@ -
+
diff --git a/public/style.css b/public/style.css index 83fd5287..a1fcc287 100644 --- a/public/style.css +++ b/public/style.css @@ -3019,7 +3019,11 @@ body.hide-thinking .thinking-block { display: none !important; } } .file-sidebar-path { - padding: 6px 12px; + display: block; + width: calc(100% - 16px); + margin: 0 8px; + box-sizing: border-box; + padding: 6px 4px; font-size: 10px; font-family: 'SF Mono', 'Menlo', 'Monaco', monospace; color: var(--text-dim); @@ -3028,6 +3032,16 @@ body.hide-thinking .thinking-block { display: none !important; } text-overflow: ellipsis; direction: rtl; text-align: left; + border: none; + background: transparent; + outline: none; + cursor: text; +} + +.file-sidebar-path:focus { + color: var(--text-primary); + background: var(--bg-glass-hover); + border-radius: var(--radius-sm); } .file-list {