diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/core/module-loader.js | 2 | ||||
| -rw-r--r-- | modules/ext/critical-ext-bundle.js | 9 | ||||
| -rw-r--r-- | modules/ext/filemgr.js | 9 | ||||
| -rw-r--r-- | modules/ext/webdav.js | 59 |
4 files changed, 77 insertions, 2 deletions
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js index 5056cdb..252c34b 100644 --- a/modules/core/module-loader.js +++ b/modules/core/module-loader.js @@ -4,7 +4,7 @@ // Core module bundle loaded synchronously in main.html. // This loader only handles version cache-busting, splash, terminal init, and optional ext modules. - const LATEST_VER = 'v4.6.99'; + const LATEST_VER = 'v4.6.101'; const CACHE = '?v=' + LATEST_VER; const stored = localStorage.getItem('webx11_cache_ver'); if (stored !== LATEST_VER) { diff --git a/modules/ext/critical-ext-bundle.js b/modules/ext/critical-ext-bundle.js index 8e968fb..d1a2b83 100644 --- a/modules/ext/critical-ext-bundle.js +++ b/modules/ext/critical-ext-bundle.js @@ -1520,6 +1520,15 @@ class FileManagerModule { } const overlay = this._createOverlay(); document.body.appendChild(overlay); + // 2026-10-26 (Issue D2): the WebDAV pane lives in another module and needs a read-only view of + // this clipboard to implement "cut/copy here, paste there". + try { + if (typeof window !== 'undefined' && window.webx11) { + window.webx11.fileClipboard = () => ({ mode: this._clipboard.mode, paths: [...this._clipboard.paths] }); + window.webx11.fileClipboardClear = () => { this._clipboard = { mode: null, paths: [] }; }; + window.webx11.fileClipboardSet = (paths, mode) => { this._clipboard = { mode: mode || 'copy', paths: [...(paths || [])] }; }; + } + } catch (_e) {} this._navigateTo(this.currentPath); } diff --git a/modules/ext/filemgr.js b/modules/ext/filemgr.js index 7412578..b813815 100644 --- a/modules/ext/filemgr.js +++ b/modules/ext/filemgr.js @@ -587,6 +587,15 @@ class FileManagerModule { } const overlay = this._createOverlay(); document.body.appendChild(overlay); + // 2026-10-26 (Issue D2): the WebDAV pane lives in another module and needs a read-only view of + // this clipboard to implement "cut/copy here, paste there". + try { + if (typeof window !== 'undefined' && window.webx11) { + window.webx11.fileClipboard = () => ({ mode: this._clipboard.mode, paths: [...this._clipboard.paths] }); + window.webx11.fileClipboardClear = () => { this._clipboard = { mode: null, paths: [] }; }; + window.webx11.fileClipboardSet = (paths, mode) => { this._clipboard = { mode: mode || 'copy', paths: [...(paths || [])] }; }; + } + } catch (_e) {} this._navigateTo(this.currentPath); } diff --git a/modules/ext/webdav.js b/modules/ext/webdav.js index 88f1f59..7f3e7a7 100644 --- a/modules/ext/webdav.js +++ b/modules/ext/webdav.js @@ -1654,7 +1654,8 @@ class WebDAVModule { bulkBar.innerHTML = '<span id="bulk-count" style="font-size:0.95em;color:var(--text-color);flex:1;">0 selected</span>' + '<button class="btn" id="bulk-delete" style="font-size:0.9em;padding:5px 12px;">🗑 Delete</button>' + '<button class="btn" id="bulk-move" style="font-size:0.9em;padding:5px 12px;">📦 Move</button>' + - '<button class="btn" id="bulk-download" style="font-size:0.9em;padding:5px 12px;">⬇ Download</button>'; + '<button class="btn" id="bulk-download" style="font-size:0.9em;padding:5px 12px;">⬇ Download</button>' + + '<button class="btn" id="bulk-paste" style="font-size:0.9em;padding:5px 12px;display:none;">📥 Paste</button>'; content.appendChild(bulkBar); // Footer buttons @@ -2001,6 +2002,62 @@ class WebDAVModule { dest.focus(); dest.select(); }); + // ── 2026-10-26 (Issue D2): paste the VFS clipboard into this WebDAV directory ────────── + const refreshPaste = function() { + const btn = document.getElementById('bulk-paste'); + if (!btn) return; + const clip = (typeof webx11 !== 'undefined' && webx11.fileClipboard) ? webx11.fileClipboard() : null; + const n = clip && clip.paths ? clip.paths.length : 0; + btn.style.display = n ? '' : 'none'; + btn.textContent = '📥 Paste ' + n; + btn.title = n ? 'Upload ' + n + ' file(s) from 📁 Files (' + (clip.mode || 'copy') + ')' : ''; + }; + refreshPaste(); + document.getElementById('bulk-paste')?.addEventListener('click', async function() { + const clip = (typeof webx11 !== 'undefined' && webx11.fileClipboard) ? webx11.fileClipboard() : null; + if (!clip || !clip.paths || !clip.paths.length) { + self.terminal.print('📥 Clipboard empty — copy or cut files in 📁 Files first', 'error'); + return; + } + const destDir = (path || '').replace(/\/+$/, ''); + let ok = 0, failed = 0; + for (const src of clip.paths) { + const name = src.split('/').pop(); + const content = webx11.fs.readFile(src); + if (content === null || content === false) { + failed++; + self.terminal.print('📥 Skipped (no longer in VFS): ' + name, 'error'); + continue; + } + try { + const r = await self._webdavRequest('PUT', destDir + '/' + name, { body: content }); + if (!(r.ok || r.status === 201 || r.status === 204)) throw new Error('HTTP ' + r.status); + ok++; + // cut = move: delete locally only after the server accepted the bytes + if (clip.mode === 'cut') { + const fs = webx11.fs; + const del = typeof fs.rm === 'function' ? fs.rm(src) : fs.deleteFile(src); + if (del === false) self.terminal.print('📥 Uploaded but local delete failed: ' + name, 'error'); + } + } catch (e) { + failed++; + self.terminal.print('📥 Paste failed: ' + name + ' — ' + (e && e.message ? e.message : e), 'error'); + } + } + self.terminal.print('📥 Pasted ' + ok + ' item(s) to ' + (destDir || '/') + + (failed ? ' — ' + failed + ' failed' : ''), failed ? 'error' : 'success'); + // cut = move: only the files that actually moved leave the clipboard; a failed upload + // stays in it so the operator can retry instead of hunting for the file again. + if (clip.mode === 'cut' && ok) { + const left = clip.paths.filter(function(p) { + const c = webx11.fs.readFile(p); + return c !== null && c !== false; + }); + if (left.length && typeof webx11.fileClipboardSet === 'function') webx11.fileClipboardSet(left, 'cut'); + else if (typeof webx11.fileClipboardClear === 'function') webx11.fileClipboardClear(); + } + setTimeout(function() { self.openWebdavBrowser(path); }, 600); + }); document.getElementById('bulk-download')?.addEventListener('click', function() { const names = Object.entries(self._selected).filter(function(e) { return e[1]; }).map(function(e) { return e[0]; }); names.forEach(function(name) { |