tabby-rdp 0.3.4 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/README.md +27 -4
- package/dist/accountForm.js +90 -0
- package/dist/accountForm.js.map +1 -0
- package/dist/accounts.js +39 -0
- package/dist/accounts.js.map +1 -0
- package/dist/desktop.service.js +242 -19
- package/dist/desktop.service.js.map +1 -1
- package/dist/desktopForm.js +38 -2
- package/dist/desktopForm.js.map +1 -1
- package/dist/desktops.js +1 -0
- package/dist/desktops.js.map +1 -1
- package/dist/help.js +15 -1
- package/dist/help.js.map +1 -1
- package/dist/index.js +24 -5
- package/dist/index.js.map +1 -1
- package/dist/keyboard.js +3 -3
- package/dist/keyboard.js.map +1 -1
- package/dist/rdcleanpath.js +1 -3
- package/dist/rdcleanpath.js.map +1 -1
- package/dist/rdpProfile.js +33 -5
- package/dist/rdpProfile.js.map +1 -1
- package/dist/settingsPage.js +574 -57
- package/dist/settingsPage.js.map +1 -1
- package/dist/signin.js +163 -21
- package/dist/signin.js.map +1 -1
- package/dist/ui.js +5 -2
- package/dist/ui.js.map +1 -1
- package/dist/updates.js +9 -0
- package/dist/updates.js.map +1 -1
- package/package.json +1 -1
package/dist/desktop.service.js
CHANGED
|
@@ -25,6 +25,8 @@ const audio_1 = require("./audio");
|
|
|
25
25
|
const connectionStatus_1 = require("./connectionStatus");
|
|
26
26
|
const microphone_1 = require("./microphone");
|
|
27
27
|
const desktopForm_1 = require("./desktopForm");
|
|
28
|
+
const accounts_1 = require("./accounts");
|
|
29
|
+
const accountForm_1 = require("./accountForm");
|
|
28
30
|
const fileTransfer_1 = require("./fileTransfer");
|
|
29
31
|
const desktops_1 = require("./desktops");
|
|
30
32
|
const help_1 = require("./help");
|
|
@@ -322,6 +324,11 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
322
324
|
* long as Tabby runs; a Windows host is found out on the first connect there, which costs a failed setup.
|
|
323
325
|
*/
|
|
324
326
|
this.ownDesktops = new Map();
|
|
327
|
+
/**
|
|
328
|
+
* Forgets the saved account of the pane's desktop and connects again, with the sign-in form. For xrdp above all:
|
|
329
|
+
* with a wrong password it shows its own login window rather than refusing, so the form never comes back.
|
|
330
|
+
*/
|
|
331
|
+
this.askAgain = new WeakSet();
|
|
325
332
|
/** VMs found on SSH hosts (see vms.ts), by target key: when, and as desktops. */
|
|
326
333
|
this.vms = new Map();
|
|
327
334
|
this.lastUsed = new Map();
|
|
@@ -389,14 +396,12 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
389
396
|
const spec = this.sessions.get(pane)?.spec;
|
|
390
397
|
return !!spec && this.signsIn(spec);
|
|
391
398
|
}
|
|
392
|
-
/**
|
|
393
|
-
* Forgets the saved account of the pane's desktop and connects again, with the sign-in form. For xrdp above all:
|
|
394
|
-
* with a wrong password it shows its own login window rather than refusing, so the form never comes back.
|
|
395
|
-
*/
|
|
396
399
|
async signInAgain(pane) {
|
|
397
400
|
const session = this.sessions.get(pane);
|
|
398
401
|
if (session && this.signsIn(session.spec)) {
|
|
399
402
|
await (0, signin_1.forgetCredentials)(session.key);
|
|
403
|
+
// With a saved account, the form asks for its password anew (the saved one stays until then).
|
|
404
|
+
this.askAgain.add(pane);
|
|
400
405
|
await this.reopen(pane, session.spec);
|
|
401
406
|
}
|
|
402
407
|
}
|
|
@@ -408,9 +413,10 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
408
413
|
if (target.direct) {
|
|
409
414
|
return [target.direct];
|
|
410
415
|
}
|
|
416
|
+
// With their group's defaults (an account for the whole group), as Tabby resolves a profile when opening it.
|
|
411
417
|
const viaProfile = target.profileId
|
|
412
418
|
? (this.config.store.profiles ?? []).filter((p) => p?.type === targets_1.RDP_PROFILE_TYPE && p.options?.host && p.options.via === target.profileId)
|
|
413
|
-
.map((p) => ({ ...p.options, name: p.name }))
|
|
419
|
+
.map((p) => ({ ...this.profiles.getConfigProxyForProfile(p).options, name: p.name }))
|
|
414
420
|
: [];
|
|
415
421
|
const specs = (0, desktops_1.desktopsFor)(target, this.config.store.remoteDesktop?.desktops, this.ownDesktops.get(target.key), viaProfile);
|
|
416
422
|
// Found VMs come last, and not where a configured desktop has the same address.
|
|
@@ -480,6 +486,129 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
480
486
|
desktopOf(pane) {
|
|
481
487
|
return this.sessions.get(pane)?.spec ?? null;
|
|
482
488
|
}
|
|
489
|
+
/** `remoteDesktop.accounts`: the saved accounts desktops can sign in with (see accounts.ts). */
|
|
490
|
+
accounts() {
|
|
491
|
+
return (0, accounts_1.accountsOf)(this.config.store.remoteDesktop);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* The desktops that sign in with a saved account: configured ones behind SSH hosts, and RDP profiles (their own
|
|
495
|
+
* choice, or their profile group's default when they make none). Each comes with what opens its editor.
|
|
496
|
+
*/
|
|
497
|
+
accountUses(id) {
|
|
498
|
+
const desktops = this.configuredDesktops().flatMap((d, i) => d.account === id ? [{ name: String(d.name ?? (0, desktops_1.desktopIdOf)(d)), desktopIndex: i }] : []);
|
|
499
|
+
const groups = this.config.store.groups ?? [];
|
|
500
|
+
const inherited = (p) => p.options?.account === undefined && groups.find(g => g.id === p.group)?.defaults?.[targets_1.RDP_PROFILE_TYPE]?.options?.account === id;
|
|
501
|
+
const profiles = (this.config.store.profiles ?? [])
|
|
502
|
+
.filter((p) => p?.type === targets_1.RDP_PROFILE_TYPE && (p.options?.account === id || inherited(p)))
|
|
503
|
+
.map((p) => ({ name: String(p.name ?? p.options?.host ?? ''), profileId: p.id ? String(p.id) : undefined }));
|
|
504
|
+
return [...desktops, ...profiles];
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Adds a saved account (no id) or changes one. `password`: the new one for the keychain; undefined keeps what is
|
|
508
|
+
* there. A new user name or domain without a new password drops the old password: it was for the old account.
|
|
509
|
+
* Returns the account's id, and the keychain's complaint if the password couldn't be saved.
|
|
510
|
+
*/
|
|
511
|
+
async saveAccount(account, password) {
|
|
512
|
+
const list = this.accounts();
|
|
513
|
+
const old = list.find(a => a.id === account.id);
|
|
514
|
+
const entry = {
|
|
515
|
+
id: old?.id ?? (0, accounts_1.newAccountId)(list),
|
|
516
|
+
name: account.name.trim() || (0, accounts_1.signInName)(account),
|
|
517
|
+
username: account.username.trim(),
|
|
518
|
+
...account.domain?.trim() ? { domain: account.domain.trim() } : {},
|
|
519
|
+
};
|
|
520
|
+
// The password first: a new user name must not be written while the old password (for the old name) stays, nor
|
|
521
|
+
// a new password reported as saved when the store refused it.
|
|
522
|
+
let keychainError;
|
|
523
|
+
if (password !== undefined && password !== '') {
|
|
524
|
+
await (0, signin_1.saveCredentials)((0, accounts_1.accountKey)(entry.id), { username: (0, accounts_1.signInName)(entry), password }, `saved account ${entry.name}`).catch(e => { keychainError = String(e?.message ?? e); });
|
|
525
|
+
}
|
|
526
|
+
else if (old && (0, accounts_1.signInName)(old) !== (0, accounts_1.signInName)(entry)) {
|
|
527
|
+
await (0, signin_1.forgetCredentialsOrFail)((0, accounts_1.accountKey)(entry.id)).catch(e => { keychainError = `the old password couldn't be removed: ${e?.message ?? e}`; });
|
|
528
|
+
}
|
|
529
|
+
if (keychainError && !old) {
|
|
530
|
+
// Not added at all: a retry would otherwise make a second account.
|
|
531
|
+
return { id: entry.id, keychainError: `${keychainError}. The account wasn't added` };
|
|
532
|
+
}
|
|
533
|
+
if (keychainError && old && (0, accounts_1.signInName)(old) !== (0, accounts_1.signInName)(entry)) {
|
|
534
|
+
// The sign-in name stays as it was; the account's name can still change.
|
|
535
|
+
entry.username = old.username;
|
|
536
|
+
if (old.domain) {
|
|
537
|
+
entry.domain = old.domain;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
delete entry.domain;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
this.config.store.remoteDesktop.accounts = old ? list.map(a => a === old ? entry : a) : [...list, entry];
|
|
544
|
+
await this.config.save();
|
|
545
|
+
this.changed$.next();
|
|
546
|
+
return { id: entry.id, keychainError };
|
|
547
|
+
}
|
|
548
|
+
/** A desktop form's "New account…": saves it and hands it back for the form's list. */
|
|
549
|
+
async addAccountFromForm(input) {
|
|
550
|
+
const { id, keychainError } = await this.saveAccount({ name: input.name, username: input.username, domain: input.domain }, input.password);
|
|
551
|
+
if (keychainError) {
|
|
552
|
+
throw new Error(keychainError);
|
|
553
|
+
}
|
|
554
|
+
return this.accounts().find(a => a.id === id);
|
|
555
|
+
}
|
|
556
|
+
/** Whether a password is saved for the account; 'unknown' while the Vault is locked (it isn't unlocked for this). */
|
|
557
|
+
accountHasPassword(id) {
|
|
558
|
+
return (0, signin_1.hasCredentials)((0, accounts_1.accountKey)(id));
|
|
559
|
+
}
|
|
560
|
+
/** "Remove…" on a saved account: asks first, naming the desktops that use it. Resolves true when removed. */
|
|
561
|
+
async confirmRemoveAccount(id) {
|
|
562
|
+
const account = this.accounts().find(a => a.id === id);
|
|
563
|
+
if (!account) {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
const uses = this.accountUses(id);
|
|
567
|
+
const { response } = await this.platform.showMessageBox({
|
|
568
|
+
type: 'warning',
|
|
569
|
+
message: `Remove the account "${account.name}"?`,
|
|
570
|
+
detail: `${(0, accounts_1.signInName)(account)}. Its saved password is removed too. ` + (uses.length
|
|
571
|
+
? `${uses.length === 1 ? 'One desktop uses' : `${uses.length} desktops use`} it and will ask for an account when connecting: ${uses.map(u => u.name).join(', ')}.`
|
|
572
|
+
: 'No desktop uses it.'),
|
|
573
|
+
buttons: ['Remove', 'Keep'],
|
|
574
|
+
defaultId: 1,
|
|
575
|
+
cancelId: 1,
|
|
576
|
+
});
|
|
577
|
+
if (response !== 0) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
await this.removeAccount(id);
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
/** Removes a saved account and its password; desktops that used it go back to asking. */
|
|
584
|
+
async removeAccount(id) {
|
|
585
|
+
const store = this.config.store.remoteDesktop;
|
|
586
|
+
store.accounts = this.accounts().filter(a => a.id !== id);
|
|
587
|
+
if (this.configuredDesktops().some(d => d.account === id)) {
|
|
588
|
+
store.desktops = this.configuredDesktops().map(d => {
|
|
589
|
+
if (d.account !== id) {
|
|
590
|
+
return d;
|
|
591
|
+
}
|
|
592
|
+
const { account: _account, ...rest } = d;
|
|
593
|
+
return rest;
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
for (const profile of this.config.store.profiles ?? []) {
|
|
597
|
+
if (profile?.type === targets_1.RDP_PROFILE_TYPE && profile.options?.account === id) {
|
|
598
|
+
profile.options.account = '';
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// A profile group's default for remote desktop profiles, too.
|
|
602
|
+
for (const group of this.config.store.groups ?? []) {
|
|
603
|
+
const options = group?.defaults?.[targets_1.RDP_PROFILE_TYPE]?.options;
|
|
604
|
+
if (options?.account === id) {
|
|
605
|
+
options.account = '';
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
await this.config.save();
|
|
609
|
+
this.changed$.next();
|
|
610
|
+
await (0, signin_1.forgetCredentialsOrFail)((0, accounts_1.accountKey)(id)).catch(e => this.notifications.error(`The account is removed, but its password could not be: ${e?.message ?? e}`));
|
|
611
|
+
}
|
|
483
612
|
/** `remoteDesktop.desktops`: the desktops configured behind SSH hosts. */
|
|
484
613
|
configuredDesktops() {
|
|
485
614
|
const list = this.config.store.remoteDesktop?.desktops;
|
|
@@ -500,6 +629,8 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
500
629
|
const entry = await (0, desktopForm_1.askDesktop)(pane.element.nativeElement, {
|
|
501
630
|
title: `Add a desktop reached through ${target.label}`,
|
|
502
631
|
action: 'Add and open',
|
|
632
|
+
accounts: this.accounts(),
|
|
633
|
+
addAccount: input => this.addAccountFromForm(input),
|
|
503
634
|
entry: { via: hostname, host: '127.0.0.1', port: 3389 },
|
|
504
635
|
check: e => this.addressTaken(e, -1),
|
|
505
636
|
});
|
|
@@ -524,20 +655,37 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
524
655
|
* sharpness along; a new user name drops the saved account, which was for the old one. Open sessions keep running.
|
|
525
656
|
*/
|
|
526
657
|
async editDesktop(pane, index) {
|
|
527
|
-
|
|
528
|
-
if (!old) {
|
|
658
|
+
if (!this.configuredDesktops()[index]) {
|
|
529
659
|
return;
|
|
530
660
|
}
|
|
531
661
|
if (this.isVisible(pane)) {
|
|
532
662
|
this.showConsole(pane);
|
|
533
663
|
}
|
|
534
|
-
|
|
535
|
-
|
|
664
|
+
await this.editDesktopIn(pane.element.nativeElement, index);
|
|
665
|
+
pane.frontend?.focus();
|
|
666
|
+
}
|
|
667
|
+
/** What a desktop's `via` can name: the SSH profiles' names and hosts (see viaMatches), for the form to suggest. */
|
|
668
|
+
async sshHosts() {
|
|
669
|
+
const profiles = await this.profiles.getProfiles().catch(() => []);
|
|
670
|
+
const names = profiles.filter((p) => p.type === 'ssh' && !p.isTemplate)
|
|
671
|
+
.flatMap((p) => [p.name?.replace(/\s*\(\.ssh\/config\)$/, ''), p.options?.host].filter(Boolean));
|
|
672
|
+
return [...new Set(names)].sort((a, b) => a.localeCompare(b));
|
|
673
|
+
}
|
|
674
|
+
/** "Edit a desktop" with the form over any element (a pane, or the settings page). */
|
|
675
|
+
async editDesktopIn(host, index, askVia = false) {
|
|
676
|
+
const old = this.configuredDesktops()[index];
|
|
677
|
+
if (!old) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const entry = await (0, desktopForm_1.askDesktop)(host, {
|
|
681
|
+
title: `Edit ${old.name ?? (0, desktops_1.desktopIdOf)(old)}${askVia ? '' : ` (behind ${old.via})`}`,
|
|
536
682
|
action: 'Save',
|
|
683
|
+
accounts: this.accounts(),
|
|
684
|
+
addAccount: input => this.addAccountFromForm(input),
|
|
685
|
+
hosts: askVia ? await this.sshHosts() : undefined,
|
|
537
686
|
entry: old,
|
|
538
687
|
check: e => this.addressTaken(e, index),
|
|
539
688
|
});
|
|
540
|
-
pane.frontend?.focus();
|
|
541
689
|
// Compared with the entry as it is now: the config can change while the form shows.
|
|
542
690
|
const list = this.configuredDesktops();
|
|
543
691
|
if (!entry || list[index] !== old) {
|
|
@@ -547,7 +695,7 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
547
695
|
this.config.store.remoteDesktop.desktops = list;
|
|
548
696
|
this.config.save();
|
|
549
697
|
this.changed$.next();
|
|
550
|
-
const accountChanged = (old.username ?? '') !== (entry.username ?? '') || (old.domain ?? '') !== (entry.domain ?? '');
|
|
698
|
+
const accountChanged = (old.username ?? '') !== (entry.username ?? '') || (old.domain ?? '') !== (entry.domain ?? '') || (old.account ?? '') !== (entry.account ?? '');
|
|
551
699
|
await this.desktopEdited((0, desktops_1.desktopIdOf)(old), (0, desktops_1.desktopIdOf)(entry), accountChanged, false);
|
|
552
700
|
}
|
|
553
701
|
/**
|
|
@@ -610,6 +758,21 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
610
758
|
await this.importRdp(new Uint8Array(await file.arrayBuffer()), file.name);
|
|
611
759
|
}
|
|
612
760
|
}
|
|
761
|
+
/**
|
|
762
|
+
* The profile group for remote desktop profiles the plugin makes itself (an .rdp import, "New profile…" on the
|
|
763
|
+
* settings page): "Remote desktops", made the first time. Profiles made on Tabby's Profiles page go where the user
|
|
764
|
+
* puts them.
|
|
765
|
+
*/
|
|
766
|
+
async remoteDesktopGroup() {
|
|
767
|
+
const groups = await this.profiles.getProfileGroups({ includeNonUserGroup: false });
|
|
768
|
+
const existing = groups.find(g => g.name === 'Remote desktops');
|
|
769
|
+
if (existing?.id) {
|
|
770
|
+
return existing.id;
|
|
771
|
+
}
|
|
772
|
+
const group = { name: 'Remote desktops', profiles: [] };
|
|
773
|
+
await this.profiles.newProfileGroup(group, { genId: true });
|
|
774
|
+
return group.id;
|
|
775
|
+
}
|
|
613
776
|
/**
|
|
614
777
|
* Adds a "Remote desktop (RDP)" profile from a .rdp file's contents (address, user name, domain; see rdpFile.ts)
|
|
615
778
|
* and opens it; or opens the profile that already has that address and user. Returns the profile, or null.
|
|
@@ -631,6 +794,7 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
631
794
|
type: targets_1.RDP_PROFILE_TYPE,
|
|
632
795
|
name: fileName.replace(/^.*[\\/]/, '').replace(/\.rdp$/i, '') || parsed.host,
|
|
633
796
|
icon: 'fas fa-desktop',
|
|
797
|
+
group: await this.remoteDesktopGroup(),
|
|
634
798
|
options: { host: parsed.host, port: parsed.port, kind: 'windows', username: parsed.username ?? '', domain: parsed.domain ?? '', via: '' },
|
|
635
799
|
};
|
|
636
800
|
await this.profiles.newProfile(profile);
|
|
@@ -1437,23 +1601,43 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
1437
1601
|
spec.port = endpoint.port;
|
|
1438
1602
|
spec.username ??= endpoint.username;
|
|
1439
1603
|
}
|
|
1440
|
-
|
|
1604
|
+
// A saved account (Settings): its user name, and its password from the keychain. One that has none yet, or that
|
|
1605
|
+
// this desktop just refused, is asked for here and saved for every desktop that uses the account.
|
|
1606
|
+
const account = spec.account ? this.accounts().find(a => a.id === spec.account) : undefined;
|
|
1607
|
+
if (spec.account && !account) {
|
|
1608
|
+
session.log.push('sign-in: its saved account no longer exists; asking');
|
|
1609
|
+
}
|
|
1610
|
+
const again = this.askAgain.delete(pane);
|
|
1611
|
+
// A desktop whose account is gone asks, rather than falling back to a login of its own saved earlier.
|
|
1612
|
+
let saved = retryError || again || spec.account && !account ? null : await (0, signin_1.loadCredentials)(account ? (0, accounts_1.accountKey)(account.id) : session.key);
|
|
1613
|
+
if (saved && account && saved.username !== (0, accounts_1.signInName)(account)) {
|
|
1614
|
+
// Kept for an earlier user name of the account (its change didn't reach the store): not this one's.
|
|
1615
|
+
session.log.push(`sign-in: the account's saved password is for ${saved.username}, not ${(0, accounts_1.signInName)(account)}; asking`);
|
|
1616
|
+
saved = null;
|
|
1617
|
+
}
|
|
1441
1618
|
if (saved) {
|
|
1442
|
-
|
|
1619
|
+
if (account) {
|
|
1620
|
+
session.log.push(`sign-in: the saved account "${account.name}"`);
|
|
1621
|
+
}
|
|
1622
|
+
return { host: spec.host, port: spec.port, credentials: account ? { username: (0, accounts_1.signInName)(account), password: saved.password } : saved, remember: false, domain: account ? account.domain ?? '' : undefined };
|
|
1443
1623
|
}
|
|
1444
1624
|
session.status('');
|
|
1445
1625
|
const asked = (0, signin_1.askCredentials)(session.overlay, {
|
|
1446
1626
|
// The host's own desktops (its own, or its xrdp besides GNOME) and direct ones need no "via".
|
|
1447
1627
|
title: spec.id === desktops_1.OWN_DESKTOP || target.direct || spec.kind === 'xrdp' && spec.host === '127.0.0.1' ? `Sign in to ${spec.name}` : `Sign in to ${spec.name} (via ${target.label})`,
|
|
1448
|
-
username: spec.username,
|
|
1628
|
+
username: account ? (0, accounts_1.signInName)(account) : spec.username,
|
|
1449
1629
|
error: retryError,
|
|
1450
1630
|
canRemember: true,
|
|
1631
|
+
account: account?.name,
|
|
1451
1632
|
}, session.disposed);
|
|
1452
1633
|
if (session.visible) {
|
|
1453
1634
|
setTimeout(() => session.visible && session.focusDesktop());
|
|
1454
1635
|
}
|
|
1455
1636
|
const entered = await asked;
|
|
1456
|
-
return entered && {
|
|
1637
|
+
return entered && {
|
|
1638
|
+
host: spec.host, port: spec.port, credentials: entered, remember: entered.remember, domain: account ? account.domain ?? '' : undefined,
|
|
1639
|
+
saveKey: account && (0, accounts_1.accountKey)(account.id), saveLabel: account && `saved account ${account.name}`, saveFor: account && (0, accounts_1.signInName)(account),
|
|
1640
|
+
};
|
|
1457
1641
|
}
|
|
1458
1642
|
async connect(pane, target, spec, session) {
|
|
1459
1643
|
const alive = () => this.sessions.get(pane) === session;
|
|
@@ -1505,6 +1689,8 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
1505
1689
|
return;
|
|
1506
1690
|
}
|
|
1507
1691
|
if (outcome.signInFailed && this.signsIn(spec) && attempt < 5 && alive()) {
|
|
1692
|
+
// The desktop's own saved account goes. A shared one stays until a new password is entered: this
|
|
1693
|
+
// desktop refusing it doesn't make it wrong for the others.
|
|
1508
1694
|
await (0, signin_1.forgetCredentials)(session.key);
|
|
1509
1695
|
retryError = outcome.signInFailed;
|
|
1510
1696
|
endpoint = null;
|
|
@@ -1762,7 +1948,7 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
1762
1948
|
const size = remoteSizeFor(session.overlay.getBoundingClientRect(), settings);
|
|
1763
1949
|
const { width, height } = size;
|
|
1764
1950
|
// DOMAIN\user or user@domain go to the server as typed; only the former needs splitting.
|
|
1765
|
-
const [domain, username] = /^([^\\]+)\\(.+)$/.exec(credentials.username)?.slice(1) ?? [spec.domain ?? '', credentials.username];
|
|
1951
|
+
const [domain, username] = /^([^\\]+)\\(.+)$/.exec(credentials.username)?.slice(1) ?? [endpoint.domain ?? spec.domain ?? '', credentials.username];
|
|
1766
1952
|
session.status(spec.id === desktops_1.OWN_DESKTOP ? `Connecting to ${target.label}…`
|
|
1767
1953
|
: target.direct ? `Connecting to ${spec.name}…` : `Connecting to ${spec.name} via ${target.label}…`);
|
|
1768
1954
|
const config = session.ui.configBuilder()
|
|
@@ -1847,8 +2033,14 @@ let RemoteDesktopService = RemoteDesktopService_1 = class RemoteDesktopService {
|
|
|
1847
2033
|
this.syncIndicator(session);
|
|
1848
2034
|
this.changed$.next();
|
|
1849
2035
|
this.reconnects.delete(pane); // connected again: a later drop starts over
|
|
1850
|
-
|
|
1851
|
-
|
|
2036
|
+
// Under a saved account only while that account is still there with the same sign-in name: the prompt may
|
|
2037
|
+
// have been open while it was removed or changed in Settings.
|
|
2038
|
+
const accountNow = endpoint.saveKey ? this.accounts().find(a => (0, accounts_1.accountKey)(a.id) === endpoint.saveKey) : undefined;
|
|
2039
|
+
if (endpoint.remember && endpoint.saveKey && (!accountNow || (0, accounts_1.signInName)(accountNow) !== endpoint.saveFor)) {
|
|
2040
|
+
session.log.push(`sign-in: not saved: the account ${accountNow ? 'changed' : 'was removed'} meanwhile`);
|
|
2041
|
+
}
|
|
2042
|
+
else if (endpoint.remember) {
|
|
2043
|
+
(0, signin_1.saveCredentials)(endpoint.saveKey ?? session.key, credentials, endpoint.saveLabel).then(() => session.log.push(`sign-in: saved ${endpoint.saveKey ? 'as the account\'s password ' : ''}in ${(0, signin_1.storeName)()}`), e => session.log.push(`sign-in: ${(0, signin_1.storeName)()}: ${e?.message ?? e}`));
|
|
1852
2044
|
}
|
|
1853
2045
|
if (session.visible) {
|
|
1854
2046
|
session.focusDesktop();
|
|
@@ -1901,7 +2093,38 @@ function installStyle() {
|
|
|
1901
2093
|
}
|
|
1902
2094
|
styleInstalled = true;
|
|
1903
2095
|
const style = document.createElement('style');
|
|
1904
|
-
style.textContent = STYLE + signin_1.STYLE + fileTransfer_1.STYLE + connectionStatus_1.STYLE + osd_1.OSD_STYLE;
|
|
2096
|
+
style.textContent = STYLE + signin_1.STYLE + fileTransfer_1.STYLE + connectionStatus_1.STYLE + osd_1.OSD_STYLE + DRAGBAR_STYLE + accountForm_1.STYLE;
|
|
1905
2097
|
document.head.appendChild(style);
|
|
2098
|
+
keepWindowDraggable();
|
|
2099
|
+
}
|
|
2100
|
+
/** A strip the height of the tab bar that stays a window drag region over a dialog (the plugin's, and Tabby's). */
|
|
2101
|
+
const DRAGBAR_STYLE = `
|
|
2102
|
+
.trd-form-dragbar { display: none; }
|
|
2103
|
+
.modal > .trd-form-dragbar { display: block; position: fixed; left: 0; right: 0; top: 0; height: var(--tabs-height, 38px); -webkit-app-region: drag; }
|
|
2104
|
+
`;
|
|
2105
|
+
/**
|
|
2106
|
+
* Tabby's dialogs (ng-bootstrap modals) cover the whole window, tab bar included, with nothing marked as a drag
|
|
2107
|
+
* region, so the window can't be moved while one shows. Each gets a strip where the tab bar is; the dialog box itself
|
|
2108
|
+
* is below it. The plugin's own dialogs do the same (see settingsPage.ts).
|
|
2109
|
+
*/
|
|
2110
|
+
function keepWindowDraggable() {
|
|
2111
|
+
const strip = (modal) => {
|
|
2112
|
+
if (!modal.querySelector(':scope > .trd-form-dragbar')) {
|
|
2113
|
+
const bar = document.createElement('div');
|
|
2114
|
+
bar.className = 'trd-form-dragbar';
|
|
2115
|
+
modal.prepend(bar);
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
// ng-bootstrap appends each dialog (ngb-modal-window.modal) to the body itself: only the body's children are watched.
|
|
2119
|
+
document.querySelectorAll('body > .modal').forEach(strip);
|
|
2120
|
+
new MutationObserver(records => {
|
|
2121
|
+
for (const record of records) {
|
|
2122
|
+
record.addedNodes.forEach(node => {
|
|
2123
|
+
if (node instanceof Element && node.classList.contains('modal')) {
|
|
2124
|
+
strip(node);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
}).observe(document.body, { childList: true });
|
|
1906
2129
|
}
|
|
1907
2130
|
//# sourceMappingURL=desktop.service.js.map
|