diff options
Diffstat (limited to 'modules/ext/critical-ext-bundle.js')
| -rw-r--r-- | modules/ext/critical-ext-bundle.js | 94 |
1 files changed, 87 insertions, 7 deletions
diff --git a/modules/ext/critical-ext-bundle.js b/modules/ext/critical-ext-bundle.js index 8f63081..dc3a858 100644 --- a/modules/ext/critical-ext-bundle.js +++ b/modules/ext/critical-ext-bundle.js @@ -1372,6 +1372,16 @@ class FileManagerModule { batchBar.appendChild(selModeBtn); toolbar.appendChild(batchBar); + // 2026-10-21: publish a VFS file to the user's WebDAV space was terminal-only. + const wdavBtn = document.createElement('button'); + wdavBtn.textContent = '☁ WebDAV'; + wdavBtn.title = 'Upload the selected/current file to your WebDAV space and get its public link'; + wdavBtn.style.cssText = 'background:var(--bg-tertiary);border:1px solid var(--border-color);' + + 'color:var(--text-color);cursor:pointer;padding:4px 10px;border-radius:4px;' + + 'font-family:inherit;font-size:0.9em;min-height:32px;touch-action:manipulation;'; + wdavBtn.onclick = () => this._uploadSelectionToWebdav(); + toolbar.appendChild(wdavBtn); + overlay.appendChild(toolbar); // Main area — wraps sidebar + preview in a responsive flex container @@ -1840,7 +1850,8 @@ class FileManagerModule { // For raw mode, show raw text regardless of type if (this._rawMode && typeof content === 'string') { - preview.innerHTML = '<div style="padding:12px"><div style="color:var(--text-muted);font-size:0.85em;margin-bottom:8px">RAW: ' + this._escapeHtml(path) + '</div><pre style="white-space:pre-wrap;font-family:var(--font-primary);font-size:0.9em;margin:0;overflow-x:auto">' + this._escapeHtml(content.substring(0, 10000)) + '</pre></div>'; + preview.innerHTML = this._previewShell(path, '<div style="padding:12px"><div style="color:var(--text-muted);font-size:0.85em;margin-bottom:8px">RAW: ' + this._escapeHtml(path) + '</div><pre style="white-space:pre-wrap;font-family:var(--font-primary);font-size:0.9em;margin:0;overflow-x:auto">' + this._escapeHtml(content.substring(0, 10000)) + '</pre></div>'); + this._wirePreviewToolbar(path); this._updateStatus('Preview: ' + path); return; } @@ -1850,19 +1861,88 @@ class FileManagerModule { const textTypes = ['md', 'text', 'json', 'code', 'csv', 'other']; if (textTypes.includes(type) && typeof content === 'string' && content.length > 51200) { content = content.substring(0, 51200); - preview.innerHTML = this._getPreviewHtml(path, content); - const truncatedMsg = document.createElement('div'); - truncatedMsg.style.cssText = 'color:var(--warning-color);padding:8px 12px;border-top:1px solid var(--border-color);font-size:0.85em;'; - truncatedMsg.textContent = '(truncated — file exceeds 50KB)'; - preview.appendChild(truncatedMsg); + preview.innerHTML = this._previewShell(path, + this._getPreviewHtml(path, content) + + '<div style="color:var(--warning-color);padding:8px 12px;border-top:1px solid var(--border-color);font-size:0.85em;">(truncated — file exceeds 50KB)</div>'); + this._wirePreviewToolbar(path); this._updateStatus('Preview: ' + path); return; } - preview.innerHTML = this._getPreviewHtml(path, content); + preview.innerHTML = this._previewShell(path, this._getPreviewHtml(path, content)); + this._wirePreviewToolbar(path); this._updateStatus('Preview: ' + path); } + // 2026-10-21: preview pane had no fullscreen and no share action. + _previewShell(path, innerHtml) { + return '<div class="fm-preview-toolbar" style="display:flex;gap:6px;justify-content:flex-end;' + + 'padding:6px 8px;border-bottom:1px solid var(--border-color);flex-shrink:0">' + + '<button class="fm-btn-fs" title="Fullscreen preview" aria-label="Fullscreen preview" style="background:var(--bg-tertiary);' + + 'border:1px solid var(--border-color);color:var(--text-color);padding:2px 8px;border-radius:4px;' + + 'cursor:pointer;font-size:0.85em;min-height:24px;">⛶</button>' + + '<button class="fm-btn-share" title="Copy share link" aria-label="Copy share link" style="background:var(--bg-tertiary);' + + 'border:1px solid var(--border-color);color:var(--text-color);padding:2px 8px;border-radius:4px;' + + 'cursor:pointer;font-size:0.85em;min-height:24px;">🔗</button>' + + '</div><div class="fm-preview-body" style="flex:1;min-height:0;overflow:auto">' + innerHtml + '</div>'; + } + + _previewShareUrl(path) { + const wdav = this.terminal && this.terminal.moduleManager && this.terminal.moduleManager.modules + ? this.terminal.moduleManager.modules.WebDAVModule : null; + const space = (wdav && wdav._resolveSpaceName && wdav._resolveSpaceName()) || ''; + const clean = String(path).replace(/^\/+/, ''); + return 'https://webx11.duckdns.org/spaces/' + space + '/' + + clean.split('/').map(encodeURIComponent).join('/'); + } + + // 2026-10-21: publish VFS file(s) to WebDAV (was terminal-only: webdav-push) + async _uploadSelectionToWebdav() { + let targets = this._getSelectedPaths ? this._getSelectedPaths() : []; + if (!targets || !targets.length) targets = this._selectedFile ? [this._selectedFile] : []; + if (!targets.length) { this._updateStatus('☁ Select a file first'); return; } + const wdav = this.terminal && this.terminal.moduleManager && this.terminal.moduleManager.modules + ? this.terminal.moduleManager.modules.WebDAVModule : null; + if (!wdav) { this._updateStatus('☁ WebDAV module not loaded'); return; } + if (!wdav.config || !wdav.config.url) { try { await wdav._autoBootstrap(); } catch (e) {} } + if (!wdav.config || !wdav.config.url) { this._updateStatus('☁ WebDAV not configured — run webdav-config'); return; } + for (const p of targets) { + const clean = String(p).replace(/^\/+/, ''); + try { + await this.terminal.security.checkAndRun('webdav-push ' + clean); + const shareUrl = this._previewShareUrl(clean); + this._updateStatus('☁ Uploaded ' + clean + ' → ' + shareUrl); + if (this.terminal.print) this.terminal.print('☁ WebDAV: ' + shareUrl, 'success'); + } catch (e) { + this._updateStatus('☁ Upload failed: ' + (e && e.message ? e.message : e)); + } + } + } + + _wirePreviewToolbar(path) { + const pane = document.getElementById('filemgr-preview'); + if (!pane) return; + const fsBtn = pane.querySelector('.fm-btn-fs'); + const shBtn = pane.querySelector('.fm-btn-share'); + if (fsBtn) fsBtn.onclick = () => { + const body = pane.querySelector('.fm-preview-body') || pane; + if (document.fullscreenElement) { document.exitFullscreen(); return; } + if (body.requestFullscreen) body.requestFullscreen().catch(() => this._updateStatus('⛶ Fullscreen blocked by this browser')); + else if (body.webkitRequestFullscreen) body.webkitRequestFullscreen(); + else this._updateStatus('⛶ Fullscreen not supported here'); + }; + if (shBtn) shBtn.onclick = () => { + const url = this._previewShareUrl(path); + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(url) + .then(() => this._updateStatus('🔗 Link copied: ' + url)) + .catch(() => prompt('Copy share link:', url)); + } else { + prompt('Copy share link:', url); + } + }; + } + _renderPreviewError(msg) { const preview = document.getElementById('filemgr-preview'); if (!preview) return; |