Compare commits

...
2 Commits
6 changed files with 19 additions and 6 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mrterm",
"version": "0.19.1",
"version": "0.20.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mrterm",
"version": "0.19.1",
"version": "0.20.0",
"license": "MIT",
"dependencies": {
"@xterm/addon-fit": "^0.11.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "mrterm",
"productName": "MrTerm",
"version": "0.19.1",
"version": "0.20.0",
"description": "Moderner SSH-, SFTP- und RDP-Client",
"main": "src/main/main.js",
"author": "MrBlake",
+1
View File
@@ -151,6 +151,7 @@
'Send Enter automatically': 'Enter automatisch senden',
'Command is missing.': 'Befehl fehlt.',
'No open terminal': 'Kein offenes Terminal',
'Upload files …': 'Dateien hochladen …',
// Support-Assistent
'Delete chat': 'Chat löschen',
'Delete chat?': 'Chat löschen?',
+5
View File
@@ -408,6 +408,11 @@ handle('sftp:open', async (sessionId, hostRef) => {
});
handle('sftp:list', (id, dir) => ssh.sftpList(id, dir));
handle('sftp:op', (id, op, a, b) => ssh.sftpOp(id, op, a, b));
// Dateien für den SFTP-Upload auswählen (Mehrfachauswahl)
handle('sftp:pickUpload', async () => {
const r = await dialog.showOpenDialog(win, { title: i18n.t('Upload'), properties: ['openFile', 'multiSelections', 'showHiddenFiles'] });
return r.canceled ? [] : r.filePaths.map((p) => ({ path: p, name: path.basename(p) }));
});
handle('sftp:transfer', async (id, dir, localPath, remotePath, transferId) => {
let last = 0;
await ssh.transfer(id, dir, localPath, remotePath, (done, total) => {
+9 -2
View File
@@ -2042,7 +2042,7 @@ class FilePane {
<input class="path" spellcheck="false"/>
<button class="btn ghost sm" data-p="refresh" title="${T('Refresh')}">${ICONS.refresh}</button>
<button class="btn ghost sm" data-p="mkdir" title="${T('New folder')}">${ICONS.newdir}</button>
${WEB ? `<button class="btn ghost sm" data-p="upload" title="${T('Upload')}">${ICONS.upload}</button><button class="btn ghost sm" data-p="xfer" title="${T('Download')}">${ICONS.download}</button>` : `<button class="btn ghost sm" data-p="xfer" title="${side === 'local' ? T('Upload →') : T('← Download')}">${side === 'local' ? ICONS.arrowR : ICONS.arrowL}</button>`}</div>
${WEB ? `<button class="btn ghost sm" data-p="upload" title="${T('Upload')}">${ICONS.upload}</button><button class="btn ghost sm" data-p="xfer" title="${T('Download')}">${ICONS.download}</button>` : `${side === 'remote' ? `<button class="btn ghost sm" data-p="upload" title="${T('Upload files …')}">${ICONS.upload}</button>` : ''}<button class="btn ghost sm" data-p="xfer" title="${side === 'local' ? T('Upload →') : T('← Download')}">${side === 'local' ? ICONS.arrowR : ICONS.arrowL}</button>`}</div>
<div class="files"></div></div>`);
this.el.addEventListener('click', (e) => {
const p = e.target.closest('[data-p]')?.dataset.p;
@@ -2069,7 +2069,14 @@ class FilePane {
});
}
get other() { return this.side === 'local' ? this.sftp.remote : this.sftp.local; }
pickUpload() {
async pickUpload() {
if (!WEB) {
// Desktop: Dateiauswahl des Systems, Upload in den aktuellen Server-Ordner
const files = await call('sftp:pickUpload');
for (const f of files) await this.sftp.transfer('upload', f.path, await this.join(this.cwd, f.name), f.name);
if (files.length) this.go(this.cwd);
return;
}
const inp = h('<input type="file" multiple style="display:none"/>');
inp.onchange = () => { this.uploadFiles([...inp.files]); inp.remove(); };
document.body.append(inp);
+1 -1
View File
@@ -361,7 +361,7 @@ kbd { background: var(--card); border: 1px solid var(--border); border-bottom-wi
/* Firewall */
.seg.sm button { padding: 4px 10px; font-size: 12px; }
.session.docker .sbar .seg { margin: 0 6px 0 0; flex: none; }
.session .sbar .seg { margin: 0 6px 0 0; flex: none; }
.docker-table .dot.none { background: transparent; border: 1px solid var(--faint); }
.docker-table .badge, .tpl-it .badge { font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 6px; background: var(--icon-bg); color: var(--muted); margin-left: 4px; vertical-align: middle; }
.stack-bar { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--border); flex-wrap: wrap; position: sticky; top: 0; background: var(--bg); z-index: 2; }