aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/critical-ext-bundle.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ext/critical-ext-bundle.js')
-rw-r--r--modules/ext/critical-ext-bundle.js150
1 files changed, 100 insertions, 50 deletions
diff --git a/modules/ext/critical-ext-bundle.js b/modules/ext/critical-ext-bundle.js
index 0c92381..8e968fb 100644
--- a/modules/ext/critical-ext-bundle.js
+++ b/modules/ext/critical-ext-bundle.js
@@ -1473,6 +1473,20 @@ class FileManagerModule {
downloadBtn.onclick = () => this._actionDownload();
actionBar.appendChild(downloadBtn);
+ // 2026-10-26 (phone test): share the whole selection — the preview toolbar could only ever
+ // share the one previewed file, so three selected files produced one link.
+ const shareSelBtn = document.createElement('button');
+ shareSelBtn.textContent = '🔗 Share';
+ shareSelBtn.title = 'Share or copy the selected files';
+ shareSelBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
+ shareSelBtn.onclick = () => {
+ let t = this._getSelectedPaths ? this._getSelectedPaths() : [];
+ if (!t.length && this._selectedFile) t = [this._selectedFile];
+ if (!t.length) { this._updateStatus('🔗 Nothing selected — tap ☐ Select then tap files'); return; }
+ this._sharePaths(t);
+ };
+ actionBar.appendChild(shareSelBtn);
+
const editBtn = document.createElement('button');
editBtn.textContent = '✏ Edit';
editBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--accent-color);color:var(--accent-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
@@ -1908,15 +1922,77 @@ class FileManagerModule {
// 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:10px;justify-content:flex-end;' +
+ // 2026-10-26 (phone test): the pane had no close control at all — the only ✕ belonged to the
+ // whole file manager and wiped _multiSelect on the way out, so "select 3 files → Download"
+ // ended in "No file selected to download".
+ const btn = (cls, label, title) =>
+ '<button class="' + cls + '" title="' + title + '" aria-label="' + title + '" style="background:var(--bg-tertiary);' +
+ 'border:1px solid var(--border-color);color:var(--text-color);padding:0 10px;border-radius:4px;' +
+ 'cursor:pointer;font-size:1em;min-height:44px;min-width:44px;touch-action:manipulation;">' + label + '</button>';
+ return '<div class="fm-preview-toolbar" style="display:flex;gap:10px;justify-content:space-between;align-items:center;' +
'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:0 10px;border-radius:4px;' +
- 'cursor:pointer;font-size:1em;min-height:44px;min-width:44px;touch-action:manipulation;">⛶</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:0 10px;border-radius:4px;' +
- 'cursor:pointer;font-size:1em;min-height:44px;min-width:44px;touch-action:manipulation;">🔗</button>' +
- '</div><div class="fm-preview-body" style="flex:1;min-height:0;overflow:auto">' + innerHtml + '</div>';
+ btn('fm-btn-close', '✕', 'Close preview') +
+ '<div style="display:flex;gap:10px;align-items:center">' +
+ btn('fm-btn-fs', '⛶', 'Fullscreen preview') +
+ btn('fm-btn-copy', '📋', 'Copy link') +
+ btn('fm-btn-share', '🔗', 'Share link') +
+ '</div></div><div class="fm-preview-body" style="flex:1;min-height:0;overflow:auto">' + innerHtml + '</div>';
+ }
+
+ // 2026-10-26: one entry point for both the preview toolbar (1 path) and the action bar (N paths).
+ _sharePaths(paths, mode) {
+ if (!paths || !paths.length) { this._updateStatus('🔗 Nothing selected — tap ☐ Select then tap files'); return; }
+ const urls = (paths || []).map(p => this._previewShareUrl(p)).filter(Boolean);
+ if (!urls.length) { this._updateStatus('🔗 Share unavailable — no space resolved'); return; }
+ const n = urls.length, text = urls.join('\n');
+ const copy = () => {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(text)
+ .then(() => this._updateStatus('📋 Copied ' + n + (n === 1 ? ' link' : ' links')))
+ .catch(() => this._inlineLinkBox(text, n));
+ } else { this._inlineLinkBox(text, n); }
+ };
+ if (mode === 'copy') { copy(); return; }
+ if (navigator.share) {
+ const payload = n === 1 ? { url: urls[0] } : { title: n + ' newOS4 files', text };
+ navigator.share(payload)
+ .then(() => this._updateStatus('🔗 Shared ' + n + (n === 1 ? ' link' : ' links')))
+ .catch(copy);
+ return;
+ }
+ copy();
+ }
+
+ // Visible, selectable fallback: prompt() cannot appear in iOS standalone PWAs or most WebViews.
+ _inlineLinkBox(text, n) {
+ const pane = document.getElementById('filemgr-preview');
+ const bar = pane ? pane.querySelector('.fm-preview-toolbar') : null;
+ let box = document.querySelector('.fm-share-input');
+ if (!box) {
+ box = document.createElement(n > 1 ? 'textarea' : 'input');
+ box.className = 'fm-share-input';
+ box.readOnly = true;
+ box.setAttribute('aria-label', n > 1 ? 'Share links' : 'Share link');
+ box.style.cssText = 'flex:1;min-width:0;min-height:44px;padding:6px 10px;font-size:1em;box-sizing:border-box;' +
+ 'border:1px solid var(--border-color);border-radius:4px;background:var(--bg-primary);color:var(--text-color)';
+ if (bar) {
+ bar.insertBefore(box, bar.firstChild);
+ } else {
+ // 2026-10-26: sharing from the action bar happens with no preview open — the fallback
+ // box needs its own row above the buttons, otherwise the URL is only in the status line.
+ const anchor = document.getElementById('filemgr-action-bar');
+ const host = anchor && anchor.parentNode ? anchor.parentNode
+ : document.getElementById('filemgr-main-container');
+ if (!host) { this._updateStatus('📋 ' + text); return; }
+ const row = document.createElement('div');
+ row.className = 'fm-share-row';
+ row.style.cssText = 'display:flex;gap:8px;padding:6px 8px;flex-shrink:0';
+ row.appendChild(box);
+ host.insertBefore(row, anchor || null);
+ }
+ }
+ if (box) { box.value = text; box.focus(); box.select(); }
+ this._updateStatus('📋 Select and copy ' + n + (n === 1 ? ' link' : ' links'));
}
_previewShareUrl(path) {
@@ -1958,49 +2034,19 @@ class FileManagerModule {
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();
+ // 2026-10-26 (phone test): fullscreening .fm-preview-body hid the toolbar, so the only way
+ // out was the OS back gesture. Fullscreen the pane instead — ✕ and ⛶ stay on screen.
+ if (document.fullscreenElement) { document.exitFullscreen().catch(() => {}); return; }
+ const target = pane;
+ if (target.requestFullscreen) target.requestFullscreen().catch(() => this._updateStatus('⛶ Fullscreen blocked by this browser'));
+ else if (target.webkitRequestFullscreen) target.webkitRequestFullscreen();
else this._updateStatus('⛶ Fullscreen not supported here');
};
- if (shBtn) shBtn.onclick = () => {
- const url = this._previewShareUrl(path);
- if (!url) { this._updateStatus('🔗 Share unavailable — no space resolved'); return; }
- // T3: prompt() is suppressed in iOS standalone PWAs and most Android WebViews, so the
- // last resort is a real input the user can select — never a dialog that cannot appear.
- const inlineFallback = () => {
- const bar = pane.querySelector('.fm-preview-toolbar');
- let inp = pane.querySelector('.fm-share-input');
- if (!inp && bar) {
- inp = document.createElement('input');
- inp.className = 'fm-share-input';
- inp.readOnly = true;
- inp.setAttribute('aria-label', 'Share link');
- inp.style.cssText = 'flex:1;min-width:0;min-height:44px;padding:0 10px;font-size:1em;' +
- 'border:1px solid var(--border-color);border-radius:4px;' +
- 'background:var(--bg-primary);color:var(--text-color)';
- inp.value = url;
- bar.insertBefore(inp, bar.firstChild);
- }
- if (inp) { inp.focus(); inp.select(); }
- this._updateStatus('🔗 Select and copy');
- };
- const copyThen = () => {
- if (navigator.clipboard && navigator.clipboard.writeText) {
- navigator.clipboard.writeText(url)
- .then(() => this._updateStatus('🔗 Link copied: ' + url))
- .catch(inlineFallback);
- } else { inlineFallback(); }
- };
- if (navigator.share) {
- navigator.share({ title: 'newOS4 file', url })
- .then(() => this._updateStatus('🔗 Shared'))
- .catch(copyThen);
- return;
- }
- copyThen();
- };
+ const closeBtn = pane.querySelector('.fm-btn-close');
+ if (closeBtn) closeBtn.onclick = () => { this._clearPreview(); this._updateStatus('Preview closed — selection kept'); };
+ const copyBtn = pane.querySelector('.fm-btn-copy');
+ if (copyBtn) copyBtn.onclick = () => this._sharePaths([path], 'copy');
+ if (shBtn) shBtn.onclick = () => this._sharePaths([path]);
}
_renderPreviewError(msg) {
@@ -2012,7 +2058,11 @@ class FileManagerModule {
_clearPreview() {
const preview = document.getElementById('filemgr-preview');
+ if (document.fullscreenElement) {
+ document.exitFullscreen().catch(() => {});
+ }
if (preview) preview.innerHTML = '';
+ this._updateSelectionStatus(); // selection survives closing the preview
}
_updateStatus(msg) {
@@ -2033,7 +2083,7 @@ class FileManagerModule {
// T2: honour the multi-selection the way every sibling action does (_getSelectedPaths).
let targets = this._getSelectedPaths ? this._getSelectedPaths() : [];
if (!targets || !targets.length) targets = this._selectedFile ? [this._selectedFile] : [];
- if (!targets.length) { this._updateStatus('No file selected to download'); return; }
+ if (!targets.length) { this._updateStatus('⬇ Nothing selected — tap ☐ Select then tap files, or tap one file first'); return; }
const saved = [], skipped = [];
for (const p of targets) {
const content = this.terminal.fs.readFile(p);