diff --git a/internal/server/web/static/app.css b/internal/server/web/static/app.css index 49b5995..1a0bc4f 100644 --- a/internal/server/web/static/app.css +++ b/internal/server/web/static/app.css @@ -333,7 +333,11 @@ body.multi .tabs, body.multi-hint .tabs { max-height:48px; opacity:1; padding-to .tab.on { background:var(--panel); color:var(--text); } .tab.enter, .tab.leave { max-width:0; opacity:0; padding:0; } .tab.dragging { opacity:.3; } -.tab-title { flex:1; overflow:hidden; text-overflow:ellipsis; min-width:0; } +.tab-title { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; } +/* What a tab holds, just before its name. */ +.tab-kinds { display:inline-flex; gap:3px; flex:none; color:var(--muted); opacity:.75; } +.tab.on .tab-kinds { color:var(--text); opacity:.6; } +.tab-kinds svg { display:block; } .tab-edit { width:auto; min-width:60px; max-width:150px; height:24px; padding:2px 6px; font-size:13px; } .tab-x, .pane-x { flex:none; width:20px; height:20px; padding:0; display:inline-flex; align-items:center; justify-content:center; background:transparent; border:0; border-radius:5px; color:var(--muted); opacity:.6; transition:opacity .15s, background .15s, color .15s; } diff --git a/internal/server/web/static/workspace.js b/internal/server/web/static/workspace.js index 3129952..797eb49 100644 --- a/internal/server/web/static/workspace.js +++ b/internal/server/web/static/workspace.js @@ -22,6 +22,10 @@ const tabs = []; let active = null, activePane = null; const tabOf = s => tabs.find(t => t.panes.includes(s)); + const KIND_ICON = { + term: '', + files: '' + }; function makeTab(panes, index) { const t = { title: null, panes: panes, layout: panes.length === 1 ? '1' : 'h', c: 0.5, r: 0.5, gutters: [] }; @@ -31,7 +35,7 @@ panes.forEach(s => t.view.appendChild(s.el)); t.el = document.createElement('div'); t.el.className = 'tab enter'; - t.el.innerHTML = '' + + t.el.innerHTML = '' + ''; t.el.addEventListener('click', ev => { if (!ev.target.closest('.tab-x, input') && !dragJustEnded) activate(t); }); t.el.querySelector('.tab-x').onclick = ev => { ev.stopPropagation(); closeTab(t); }; @@ -64,7 +68,8 @@ function fitTabs() { ruler.font = W.font + 'px ' + getComputedStyle(document.body).fontFamily; const live = tabs; - const want = live.map(t => Math.min(W.max, Math.max(W.base, Math.ceil(ruler.measureText(tabTitle(t)).width) + CHROME + (t.panes.some(p => p.ssh) ? 46 : 0)))); + const want = live.map(t => Math.min(W.max, Math.max(W.base, Math.ceil(ruler.measureText(tabTitle(t)).width) + CHROME + (t.panes.some(p => p.ssh) ? 46 : 0) + + 18 * new Set(t.panes.map(p => /files$/.test(p.kind))).size))); const strip = $('tabs'); const room = strip.clientWidth - parseFloat(getComputedStyle(strip).paddingLeft) * 2 - addBtn.offsetWidth - 4 * (live.length + 1); let cap = W.max; @@ -83,6 +88,14 @@ function renderTab(t) { t.el.querySelector('.tab-title').textContent = tabTitle(t); + // Before the name, what the tab holds: a terminal, files, or both. + const kinds = [...new Set(t.panes.map(p => /files$/.test(p.kind) ? 'files' : 'term'))]; + const k = t.el.querySelector('.tab-kinds'); + if (k.dataset.k !== kinds.join()) { + k.dataset.k = kinds.join(); + k.innerHTML = kinds.map(x => KIND_ICON[x]).join(''); + k.title = kinds.map(x => x === 'files' ? 'файлы' : 'терминал').join(' и '); + } const ssh = t.panes.some(s => s.ssh); if (ssh !== !!t.el.querySelector('.badge-ssh')) { if (ssh) t.el.querySelector('.dot').insertAdjacentHTML('afterend', 'SSH');