feat: file panes follow the machine going offline and back, reconnect a lost SSH host; zip and unzip over SSH where the host has them; phone keys only for terminals; version 0.2.0, README
This commit is contained in:
1 parent
d92696226e
commit
01dc45ecad
8 files changed
+164
-29
No files matched your search
@@ -111,6 +111,12 @@
|
||||
}
|
||||
function setActivePane(s) {
|
||||
activePane = s;
|
||||
// The phone's extra keys are a terminal's: a file pane goes without.
|
||||
const term = s.kind === 'term' || s.kind === 'ssh';
|
||||
if (touch && document.body.classList.contains('term-active') !== term) {
|
||||
document.body.classList.toggle('term-active', term);
|
||||
setTimeout(() => active && layout(active, false), 0);
|
||||
}
|
||||
document.querySelectorAll('.pane.active').forEach(p => p.classList.toggle('active', p === s.el));
|
||||
s.el.classList.add('active');
|
||||
showStatus();
|
||||
@@ -133,7 +139,8 @@
|
||||
function placeFloat() {
|
||||
const p = tabs.length === 1 ? tabs[0].panes.find(s => s.el.classList.contains('under-plus')) : null;
|
||||
const bars = p && /files$/.test(p.kind) ? p.el.querySelectorAll('.fm-bar') : [];
|
||||
const on = bars.length > 1 && bars[0].offsetParent !== null;
|
||||
// A bar folded in two (a phone) keeps the corner "+".
|
||||
const on = bars.length > 1 && bars[0].offsetParent !== null && bars[0].offsetHeight < 48;
|
||||
floatAdd.classList.toggle('in-files', on);
|
||||
if (!on) { floatAdd.style.top = floatAdd.style.right = ''; return; }
|
||||
const a = area.getBoundingClientRect(), top = bars[0].getBoundingClientRect(), row = bars[1].getBoundingClientRect();
|
||||
@@ -160,7 +167,7 @@
|
||||
function closeTab(t) {
|
||||
const i = tabs.indexOf(t);
|
||||
if (i < 0) return;
|
||||
if (t.panes.length > 1 && !confirm('Закрыть вкладку с ' + t.panes.length + ' терминалами?')) return;
|
||||
if (t.panes.length > 1 && !confirm('Закрыть вкладку с ' + t.panes.length + ' панелями?')) return;
|
||||
tabs.splice(i, 1);
|
||||
t.panes.forEach(s => s.close());
|
||||
t.el.classList.add('leave');
|
||||
@@ -170,7 +177,7 @@
|
||||
active = null;
|
||||
activate(tabs[Math.min(i, tabs.length - 1)]);
|
||||
}
|
||||
if (!tabs.length) { activePane = null; showStatus(); $('empty').hidden = false; }
|
||||
if (!tabs.length) { activePane = null; showStatus(); $('empty').hidden = false; document.body.classList.remove('term-active'); }
|
||||
updateMode();
|
||||
}
|
||||
function rename(t) {
|
||||
@@ -525,12 +532,14 @@
|
||||
|
||||
// ---------- the SSH dialog ----------
|
||||
const KEYI = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="7.5" cy="15.5" r="4.5"/><path d="m21 2-9.6 9.6M15.5 7.5l3 3L22 7l-3-3"/></svg>';
|
||||
let sshDlg = null, sshMode = 'term';
|
||||
let sshInto = null, sshDlg = null, sshMode = 'term';
|
||||
// openSSH asks where to connect; mode says what opens there: a terminal
|
||||
// ('term') or a file manager ('files', through Workspace.sshFiles).
|
||||
function openSSH(here, mode) {
|
||||
// into: a file pane whose SSH connection is lost - the dialog comes with
|
||||
// its host, port and user, and connects that pane again.
|
||||
function openSSH(here, mode, into) {
|
||||
if (!sshDlg) sshDlg = makeSSHDialog();
|
||||
sshDlg.show(here, mode || 'term');
|
||||
sshDlg.show(here, mode || 'term', into);
|
||||
}
|
||||
function makeSSHDialog() {
|
||||
const d = document.createElement('dialog');
|
||||
@@ -605,7 +614,7 @@
|
||||
const btn = f.querySelector('button[type=submit]');
|
||||
btn.disabled = true;
|
||||
msg.textContent = 'подключение…';
|
||||
window.Workspace.sshFiles(a, t).then(r => {
|
||||
window.Workspace.sshFiles(a, t, sshInto).then(r => {
|
||||
btn.disabled = false;
|
||||
if (!r.ok) { msg.textContent = r.error || 'не подключилось'; return; }
|
||||
msg.textContent = '';
|
||||
@@ -616,8 +625,9 @@
|
||||
close(() => openTerminal(a, t));
|
||||
};
|
||||
return {
|
||||
show(here, mode) {
|
||||
show(here, mode, into) {
|
||||
sshMode = mode;
|
||||
sshInto = into || null;
|
||||
d.querySelector('.dlg-h strong').textContent = mode === 'files' ? 'Файлы по SSH' : 'SSH-подключение';
|
||||
msg.textContent = '';
|
||||
let last = {};
|
||||
@@ -639,6 +649,7 @@
|
||||
if (here && !agents.some(a => a.id === here.id)) agents.unshift(here);
|
||||
fill();
|
||||
}).catch(() => {});
|
||||
if (into) last = { agent: here.id, host: into.ssh.host, port: into.ssh.port, user: into.ssh.user, auth: last.auth };
|
||||
if (last.host) { f.host.value = last.host; f.port.value = last.port || 22; f.user.value = last.user || ''; }
|
||||
setAuth(last.auth === 'key' || last.auth === 'agent' ? last.auth : 'password');
|
||||
d.showModal();
|
||||
@@ -692,7 +703,7 @@
|
||||
panes: () => tabs.flatMap(t => t.panes),
|
||||
activePane: () => activePane,
|
||||
renderPane: p => { const t = tabOf(p); if (t) renderTab(t); if (p === activePane) showStatus(); },
|
||||
setActivePane, tabOf, renderTab, showStatus, closePane, startDrag,
|
||||
setActivePane, tabOf, renderTab, showStatus, closePane, startDrag, openSSH,
|
||||
// paneAt, tabAt: the pane of the shown tab, the tab title at a point.
|
||||
paneAt: (x, y) => active && active.panes.find(p => in_(p.el, x, y)),
|
||||
tabAt: (x, y) => document.body.classList.contains('multi') ? tabs.find(t => in_(t.el, x, y)) : null
|
||||
|
||||
Reference in new issue
Block a user