tabby-rdp 0.5.1 → 0.5.3

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 CHANGED
@@ -1,5 +1,80 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.3
4
+
5
+ **Added**
6
+ - **Each desktop connection has its own WebAssembly instance**, while sharing compiled IronRDP code. Reconnecting
7
+ or recovering from an instance fault creates a fresh backend for that desktop without replacing other desktops.
8
+ - **Instance lifecycle checks** cover independent memories, cleanup, reconnects and collection after disposal,
9
+ with a browser smoke test and a live two-desktop isolation suite.
10
+
11
+ **Security** (a review of 0.5.3-rc.1; all of these were in 0.5.2 too)
12
+ - **Moving the pointer over a desktop no longer gives it the keyboard and the clipboard.** IronRDP's canvas took the
13
+ focus without a click, so keys typed into the terminal next to it (a password at a prompt that doesn't echo
14
+ included) and this computer's clipboard went to that remote server; also from behind a maximized pane. A desktop
15
+ now takes the keyboard only in the pane in front, and gives it back to where it came from otherwise.
16
+ - **A desktop lets go of the keyboard when its tab is left by keyboard** for one that takes no focus (Settings, the
17
+ welcome tab): it went on taking the keys typed and the clipboard copied, view only included. Keys that reach a
18
+ desktop not in front now stop at the plugin. The shortcut's modifier, still down as the tab goes, is let go of on
19
+ the remote too, whichever tab is left for: its keyup goes where the user went, and IronRDP lets go of nothing as its
20
+ canvas loses the focus, so the modifier stayed held there until it was pressed again (a held Alt has the next key
21
+ open a menu on Windows).
22
+ - **A crafted .rdp file can't hang Tabby's window**: a line of whitespace took time that grew with the cube of its
23
+ length to parse (seconds for a few thousand spaces). Files over 1 MB are refused unread.
24
+
25
+ **Fixed**
26
+ - **Removing a profile without an address no longer forgets what was saved for 127.0.0.1:3389.**
27
+ - **The architecture document gives the update check's real limit** (4 MB for the version catalog, not 256 KB).
28
+ - **Tabby's window no longer crashes when a desktop reached directly is refused after TLS** (#69): on Linux, the
29
+ first connection to any desktop in a remote desktop tab (a profile, quick connect, an .rdp file), whose certificate
30
+ is asked about before anything of the sign-in goes to it, closed the window and left Tabby running in the
31
+ background. A changed certificate did the same. The plugin now closes TLS before the socket under it. Desktops
32
+ behind an SSH host were not affected.
33
+ - **Removing a saved gateway account keeps gateway credentials separate.** Inherited gateway uses are listed in
34
+ the removal confirmation; affected desktops and group defaults ask for a gateway account on the next connection.
35
+ Missing named gateway accounts also ask separately instead of silently using the destination credentials.
36
+ - **The desktop inventory shows effective profile settings**, including inherited gateway routing, gateway accounts,
37
+ SSH profiles, sign-in names and connection kinds.
38
+ - **Ended connections release their backend and browser resources**, including timers, global listeners, media,
39
+ and the statistics indicator's canvas hook, while retaining the last picture for the disconnected pane.
40
+ - **Profile Save validates the address and RD Gateway before committing either field.** Invalid edits keep the
41
+ editor open and focus the field to correct, instead of saving an invalid gateway or silently keeping an old address.
42
+ Blank destinations preserve custom and inherited default ports when saving unrelated changes.
43
+
44
+ **Changed**
45
+ - **The rollback minimum is 0.5.3.** Updates persists this minimum to protect saved separate gateway prompts
46
+ from older versions that would reuse desktop credentials, and records a target release's minimum on installation.
47
+ - **Preview publishing uses npm's beta tag** for both beta and release-candidate versions. GitHub release drafts
48
+ remain unpublished until the staged npm package is approved and verified.
49
+
50
+ Qualification and remaining stable-release checks are tracked in [#49](https://github.com/meanaverage/tabby-rdp/issues/49).
51
+
52
+ ## 0.5.2
53
+
54
+ **Added**
55
+ - **Updates settings** include stable and preview channels (beta releases and release candidates), automatic and
56
+ manual checks, compatible version selection, rollback and uninstall controls. Changes require confirmation and
57
+ take effect after restarting Tabby. Update preferences and installation history stay on this computer.
58
+ - **Installation progress stays in the confirmation dialog**, with elapsed time and a verified success or failure
59
+ result. The dialog remains visible if Settings closes. The installed version is checked before reporting success.
60
+
61
+ **Fixed**
62
+ - **Commands through the system SSH report SSH's own failure** when it exits before reading their input,
63
+ instead of letting a broken-pipe error escape unhandled. Commands that ignore their input still return their output.
64
+ - **Plugin startup waits for Tabby's configuration** before migrating update preferences, avoiding an error that
65
+ could disable third-party plugins for the session.
66
+
67
+ **Changed**
68
+ - **Troubleshooting opens the Markdown guide** from the links at the top of Remote Desktop settings. The former
69
+ Troubleshooting tab is now Updates; connection logs are under Desktops. Empty lists use concise status messages.
70
+ - **Settings descriptions are shorter**, and help icons appear consistently after their descriptions.
71
+ - **Troubleshooting and desk cleanup guides are included in the npm package.**
72
+ - **Host compatibility checks share one layer**, with host and runtime versions, the host's declared xterm dependency,
73
+ and observed capabilities included in copied connection logs. Modal drag fallbacks and terminal input handling use
74
+ shared adapters that check the host's actual features, including on forks and nightly builds.
75
+ - **CI and the release workflow check out the code with actions/checkout 7.0.1** (#56), pinned to its commit as before,
76
+ in place of 4.4.0. Nothing in the package changes.
77
+
3
78
  ## 0.5.1
4
79
 
5
80
  Security fixes from an audit of 0.5.0: saved passwords and certificates, what a host says about its desktops, `desk`,
package/README.md CHANGED
@@ -20,8 +20,8 @@ Linux and Windows, side by side, through the SSH connections you already have.
20
20
  ---
21
21
 
22
22
  tabby-rdp is a plugin for the [Tabby](https://tabby.sh) terminal. Press a key in an SSH tab and the machine's desktop
23
- appears in its place; press it again and you're back at the shell. Everything travels inside the SSH connection:
24
- nothing is opened on the network, and nothing has to be installed on the remote machine beforehand.
23
+ appears in its place; press it again and you're back at the shell. SSH desktops use the connection you already have;
24
+ remote desktop profiles can also connect directly or through an RD Gateway.
25
25
 
26
26
  - **Linux desktops without a monitor or a login.** The plugin starts a headless GNOME session for your SSH user with
27
27
  GNOME Remote Desktop, on the fly, without root. A server in a rack works as well as a workstation.
@@ -40,8 +40,8 @@ nothing is opened on the network, and nothing has to be installed on the remote
40
40
  - **`desk`:** type `desk` in the console and the same shell, with its history and running programs, moves into a
41
41
  terminal on the desktop.
42
42
 
43
- Everything is in **Settings › Remote Desktop**, in tabs: getting started and the shortcuts, the settings, the name
44
- overlay, the desktops and certificates the plugin keeps, saved accounts, and troubleshooting.
43
+ Everything is in **Settings › Remote Desktop**: Getting started, Settings, Overlay, Desktops, Accounts and Updates.
44
+ The **Troubleshooting** link at the top opens the [troubleshooting guide](TROUBLESHOOTING.md).
45
45
 
46
46
  The remote desktop itself is [IronRDP](https://github.com/Devolutions/IronRDP)'s web client, running inside Tabby.
47
47
  Making it work with GNOME took fixes in IronRDP, which we contribute upstream ([below](#built-on-ironrdp)).
@@ -49,6 +49,7 @@ Making it work with GNOME took fixes in IronRDP, which we contribute upstream ([
49
49
  ## Contents
50
50
 
51
51
  - [Install](#install)
52
+ - [Versions and recovery](#versions-and-recovery)
52
53
  - [Use](#use)
53
54
  - [Features](#features)
54
55
  - [Windows and other desktops behind a host](#windows-and-other-desktops-behind-a-host)
@@ -78,6 +79,25 @@ Or from a terminal, into Tabby's plugin folder (on macOS `~/Library/Application
78
79
  npm install tabby-rdp
79
80
  ```
80
81
 
82
+ ## Versions and recovery
83
+
84
+ Open **Settings › Remote Desktop › Updates** to manage the installed version:
85
+
86
+ - **Stable** is the default. **Preview** also offers beta releases and release candidates when available.
87
+ - Automatic checks look for updates once a day. **Check now** checks manually; installation is always your choice.
88
+ - Choose an exact compatible version, **Return to stable**, or **Restore previous version** after an installation
89
+ from this page. After running **0.5.3**, version changes require **0.5.3 or newer** to preserve separate
90
+ gateway sign-in prompts; returning to an older stable release is blocked.
91
+ - **Uninstall plugin…** removes the package. **Manage in Tabby…** opens Tabby's plugin controls to disable it.
92
+ Saved connections, settings, passwords and remote setup are kept.
93
+
94
+ The confirmation dialog stays open during installation or removal and shows progress, elapsed time and the verified
95
+ result. After success, close remote desktops and restart Tabby. Until then, Updates shows the running version and
96
+ the installed version waiting for restart. Preferences and version history stay on this computer.
97
+
98
+ See [troubleshooting and recovery](TROUBLESHOOTING.md#recovery) if the plugin cannot load or you want to clean up saved
99
+ data or remote helpers.
100
+
81
101
  ## Use
82
102
 
83
103
  Open an SSH tab to a Linux machine with GNOME, and:
@@ -371,7 +391,7 @@ the open desktops that follow it, takes back the files offered to them, and stop
371
391
  several files that is under way before its next step. Narrowing applies in full only once they reconnect: reconnect a
372
392
  desktop you don't trust that far. **Paste to all desktops** leaves out the desktops that don't take this computer's
373
393
  clipboard and says which. Shared folders are a setting of their own, and screenshots and **Copy log** (Settings ›
374
- Remote Desktop › Troubleshooting) still go to your clipboard: they are your own actions.
394
+ Remote Desktop › Desktops › Open desktops) still go to your clipboard: they are your own actions.
375
395
 
376
396
  ## Shared folders
377
397
 
@@ -495,7 +515,8 @@ desktop of a machine you don't trust with `desk` off, which also has Tabby forge
495
515
  ## Settings
496
516
 
497
517
  Open **Settings › Remote Desktop** in Tabby, right-click the desktop button in Tabby's header, or open **Settings** in the Remote Desktop section of a terminal's or tab's menu.
498
- They are stored in Tabby's config under `remoteDesktop`:
518
+ Desktop settings are stored in Tabby's config under `remoteDesktop`; update preferences and installation history
519
+ are local to this computer:
499
520
 
500
521
  | Setting | Default | |
501
522
  |---|---|---|
@@ -517,12 +538,12 @@ They are stored in Tabby's config under `remoteDesktop`:
517
538
  | Session backend (`sessionBackend`) | `native` | For `desk`: `native` (trd-pty) or `tmux`. Config file only. |
518
539
  | Find virtual machines on SSH hosts (`discoverVMs`) | On | Lists a host's libvirt VMs with a desktop in its menu ([VMs on a host](#vms-on-a-host)). |
519
540
  | Shut down VMs it started (`shutDownIdle`) | Never | 5, 15 or 60: minutes without a desktop open after which a VM the plugin started is shut down again ([VMs on a host](#vms-on-a-host)). |
520
- | Tell me about new versions (`checkUpdates`) | On | Once a day, asks npm for the latest tabby-rdp (nothing else is sent), and says so in a note, the menus and the settings page when there's a newer one: Tabby itself shows plugin upgrades only on its Plugins page. |
541
+ | Check for updates automatically (Updates tab) | On, Stable | Checks npm once a day; choose Stable or Preview, check manually, and install a compatible version explicitly. [Versions and recovery](#versions-and-recovery). |
521
542
  | Desktop name overlay (`osd`) | When it helps | `show` (`auto`, `always`, `off`), `font`, `size`, `position`, `color` (empty: white; else a color name, `#hex`, or a color function of numbers such as `rgb()`) and `seconds`; the settings page previews it, at a resolution you pick. |
522
543
 
523
544
  ## Requirements
524
545
 
525
- - **Tabby 1.0.236 or newer**, on macOS, Windows or Linux (tested with 1.0.236 and 1.0.237). On a fresh Windows, Tabby
546
+ - **Tabby 1.0.236 or newer**, on macOS, Windows or Linux. On a fresh Windows, Tabby
526
547
  itself needs the [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist)
527
548
  for SSH.
528
549
  - **Linux desktops:** GNOME Shell and GNOME Remote Desktop 46 or newer (Ubuntu 24.04, for example), a systemd user
@@ -536,7 +557,13 @@ They are stored in Tabby's config under `remoteDesktop`:
536
557
  host or running OpenSSH Server itself.
537
558
 
538
559
  Tested with Ubuntu 24.04 (GNOME Remote Desktop 46.3) and Windows 11 (Pro and Enterprise), from Tabby on macOS,
539
- Windows 11 and Ubuntu 24.04 (arm64).
560
+ Windows 11 and Ubuntu 24.04 (arm64). Recent host compatibility tests cover official Tabby **1.0.238** on macOS
561
+ with xterm 5, and upstream xterm 6 builds on macOS and Linux. The new package installation dialog is tested on
562
+ macOS with Tabby 1.0.238; Windows qualification of these changes remains pending. See the
563
+ [host test matrix](test/README.md#host-qualification) for the exact builds and coverage.
564
+
565
+ The plugin checks the host's available features through a shared compatibility layer. Connection logs include the
566
+ host and runtime versions, declared xterm dependency and observed capabilities to help diagnose forks and nightlies.
540
567
 
541
568
  ## Built on IronRDP
542
569
 
@@ -573,8 +600,10 @@ it, not just this plugin.
573
600
  ## Development
574
601
 
575
602
  ```sh
576
- npm install
603
+ npm ci --ignore-scripts
577
604
  npm run build # TypeScript to dist/
605
+ npm run typecheck # plugin and test TypeScript
606
+ npm run test:unit # standard unit suite; no remote machine needed
578
607
  scripts/sandbox.sh # a separate Tabby with this plugin linked in (macOS), DevTools on port 9334
579
608
  npm test # end-to-end suites against a test machine: see test/README.md
580
609
  npm run build:ironrdp # rebuild vendor/ from IronRDP and ironrdp/patches: see ironrdp/README.md
@@ -626,7 +655,8 @@ npm run build:ironrdp # rebuild vendor/ from IronRDP and ironrdp/patches:
626
655
 
627
656
  Questions ("how do I…", "is this expected?") go to [Discussions › Q&A](https://github.com/meanaverage/tabby-rdp/discussions/categories/q-a);
628
657
  bugs and feature requests to the [issues](https://github.com/meanaverage/tabby-rdp/issues). A bug report is most useful
629
- with the connection log: **Settings › Remote Desktop › Troubleshooting › Open desktops › Copy log**.
658
+ with the connection log: **Settings › Remote Desktop › Desktops › Open desktops › Copy log**.
659
+ Start with the [troubleshooting guide](TROUBLESHOOTING.md) for common connection and installation problems.
630
660
 
631
661
  ## License
632
662
 
@@ -0,0 +1,82 @@
1
+ # Troubleshooting
2
+
3
+ For connection logs, open **Settings › Remote Desktop › Desktops › Open desktops**, then choose **Copy log**. In version 0.5.1, the list is in **Troubleshooting**. Review logs before sharing them.
4
+
5
+ <a id="certificate"></a>
6
+
7
+ ## A certificate this computer can't verify, or one that has changed
8
+
9
+ A desktop you connect to directly (a remote desktop profile, an imported file) and an RD Gateway connect without asking when their certificate is valid for their name by this computer's certificate authorities, as an organisation's own desktops and gateways often are. Any other is shown the first time, with why it can't be verified (most often: it is the server's own, self-signed) and its fingerprint, before anything of your sign-in is sent: check the fingerprint with whoever runs it, some other way than this connection (it is also in the desktop's connection log), then choose **Trust the certificate**. The question also says whom the certificate says it was issued to and by, and when it is valid: its own word, which nothing has checked, unless its chain leads to an authority this computer trusts (another machine's certificate then shows that machine's name) and the TLS library didn't refuse it for a reason of its own (a limit set on the authority, say). An issuer you wouldn't expect there (one that inspects your network's connections, say) is worth asking about before you trust it, but the one you would expect proves nothing: anyone can make a certificate that names it. Only the fingerprint, checked some other way, does. A desktop behind an SSH host remembers its certificate on first use without asking, since the way there runs inside SSH. When a remembered certificate differs later, the plugin stops before signing in, so nothing of your sign-in has gone to that server; only a certificate an authority vouched for is replaced without a question, by another it vouches for too. Reinstalling the machine or renewing its certificate changes it: then choose **Trust the new certificate**. If neither happened, something else is answering at that address. A GNOME desktop only ever accepts the certificate the plugin made for it; a different one there means another program listens on its port. Remembered certificates are in Settings › Remote Desktop › Desktops.
10
+
11
+ <a id="sign-in"></a>
12
+
13
+ ## Wrong user name or password
14
+
15
+ Windows and xrdp desktops sign in with an account on that machine (`DOMAIN\user` works). A saved password that no longer works is asked for again; **Sign in again…** in the tab's menu replaces it on purpose. xrdp doesn't refuse a wrong password: it shows its own login window instead. GNOME desktops never ask: the plugin manages their sign-in.
16
+
17
+ <a id="no-desktop"></a>
18
+
19
+ ## No desktop found on a Linux host
20
+
21
+ The plugin uses GNOME Remote Desktop 46 or newer (Ubuntu 24.04, for example), with no root and no login screen. For KDE, XFCE, MATE and others, install xrdp and a desktop for its sessions: on Debian and Ubuntu, `sudo apt install xrdp xfce4`. A GNOME session already shared from the machine's own screen (GNOME's Desktop Sharing) has to be turned off first.
22
+
23
+ <a id="unreachable"></a>
24
+
25
+ ## Can't reach a Windows desktop
26
+
27
+ Windows needs Remote Desktop turned on (Settings › System › Remote Desktop; Pro, Enterprise or Server). Behind an SSH host, the address is as that host sees it: from there, `nc -z <address> 3389` should succeed. For a VM or machine that may be off, give the desktop a VM name or MAC address in its edit form and it is started when opened.
28
+
29
+ <a id="reconnecting"></a>
30
+
31
+ ## It keeps reconnecting
32
+
33
+ After sleep or a network change the desktop reconnects by itself, with growing pauses, and waits while the SSH connection is down (**Reconnect SSH** hurries that along). It stops after a few tries or on **Stop**. If it drops again right after connecting, check the host's free memory and that the desktop's session is still running there.
34
+
35
+ <a id="picture"></a>
36
+
37
+ ## The picture is blurry or slow
38
+
39
+ **Sharpness › Retina** renders at your screen's device pixels, with the remote's scale set to match. Video (H.264) is used when the remote sends it: Windows does, GNOME only with a hardware encoder (VA-API or NVENC). **Show connection status** shows the frame rate, the round trip and how the picture comes.
40
+
41
+ <a id="sound"></a>
42
+
43
+ ## No sound, or the microphone isn't heard
44
+
45
+ Sound and microphone changes apply on the next connection. xrdp needs `pipewire-module-xrdp` or `pulseaudio-module-xrdp`. The microphone needs the system's permission here (macOS: System Settings › Privacy & Security › Microphone, then restart Tabby). On GNOME, apps record from "Remoteaudio Source"; on Windows, the machine must allow audio recording redirection. A desktop whose microphone was stopped (**Stop** in the microphone's menu in Tabby's header, or **Send the microphone** unchecked in the desktop's menu) gets none, its reconnects included, until **Send the microphone** is checked again in its menu or that Tabby window is closed.
46
+
47
+ <a id="nested-ssh"></a>
48
+
49
+ ## I typed ssh to another machine in a terminal
50
+
51
+ In an SSH tab (or a split pane) where you typed `ssh` on to another machine, **Desktop** opens that machine's desktop, going through the first one, and `desk` works there too. Desktop asks which desktop you mean, since which `ssh` runs there is the first machine's to say; pick the other machine's "from now on" to have it open at once the next times. Desktops you set up behind a host open in a tab connected to that host, not through another one. The first machine has to log in to the other by itself for this: with a key there, or agent forwarding, as `ssh -o BatchMode=yes` would. Tabby's **Reconnect** belongs to the tab: it reconnects the first machine and ends the ssh typed in it. To have both desktops side by side, open the first machine's desktop in one pane, and ssh on in the other.
52
+
53
+ <a id="extra-monitor"></a>
54
+
55
+ ## The desktop is open somewhere else
56
+
57
+ GNOME Remote Desktop's headless mode gives each connection a screen of its own: a second computer, or another Tabby window, connected to the same account gets an empty extra screen rather than the same one. So when the desktop is open somewhere else already, you choose. **Take it over** disconnects the other connection, as Windows does; the session and its apps carry on, on this screen. **Open a second screen** keeps both, each with a screen of its own. A desktop taken over from here doesn't take itself back: it says so, and offers the same two choices.
58
+
59
+ <a id="recovery"></a>
60
+
61
+ ## Recover a version, disable the plugin, or remove it
62
+
63
+ In **0.5.2 or newer**, open **Settings › Remote Desktop › Updates**. **Stable** is the default; **Preview** includes beta releases and release candidates when available. Choose an exact published version and confirm installation. **Return to stable** can install an older version. **Restore previous version** uses the version saved after the last successful installation from this page. On older releases, manage the package in **Tabby › Settings › Plugins**.
64
+
65
+ The confirmation dialog stays open through progress and the verified result. If installation fails, the dialog shows the cause. After success, close remote desktops and restart Tabby; Updates shows the running version and the installed version waiting for restart until then.
66
+
67
+ After running **0.5.3**, use **0.5.3 or newer**, including after uninstalling and reinstalling. This version
68
+ can save a separate gateway sign-in prompt (`@ask`) in desktops, profiles and defaults, including when a saved gateway
69
+ account is removed. Versions **0.5.1 and 0.5.2** can silently use the desktop's credentials for that gateway instead.
70
+ Updates records this minimum on startup even if update checks are paused, and retains the highest recorded minimum.
71
+ The safeguard applies to this computer's Updates page; manual npm installation, Tabby's plugin controls and older
72
+ installations on other computers can bypass it. Use the same minimum wherever this configuration is copied or synced.
73
+
74
+ If the plugin cannot load:
75
+
76
+ 1. Open **Tabby › Settings › Plugins**. Disable **tabby-rdp** to stop it loading, or uninstall and reinstall it there. Restart Tabby after changing plugins. Tabby’s [configuration locations](https://github.com/Eugeny/tabby/wiki/Config-file) help locate your profile when the application cannot open.
77
+ 2. For a specific known compatible version, open a terminal in **Tabby's plugin folder** (the folder containing the plugin installation's `package.json`, with `node_modules/tabby-rdp` below it). Run `npm install tabby-rdp@0.5.3`, replacing `0.5.3` with your known compatible exact version at or above the minimum above. Do not run this in the tabby-rdp source checkout, the application bundle, or inside `node_modules/tabby-rdp` itself. Fully quit and reopen Tabby afterwards.
78
+ 3. If a later version has migrated saved data, respect that version's documented minimum. Reinstall a compatible stable version or the last working preview; do not force an older version across a migration boundary.
79
+
80
+ Disabling or uninstalling keeps desktops, profiles, settings, remembered certificates and saved credentials. It also leaves any remote helpers and remote applications in place. To remove saved entries, use **Remote Desktop › Desktops** and **Accounts**, and **Profiles & connections** for profiles, before uninstalling. Unlock the credential store first if required; check any reported failure rather than assuming a credential was deleted. Data synced to other computers may need attention there too.
81
+
82
+ For the optional `desk` remote helper, turn **Settings › Remote Desktop › Settings › desk: bring the console along** off and reconnect to each host while the plugin is still installed. The next connection removes the helper's own files and login hook; an offline host cannot be cleaned this way until it returns. Read the [desk cleanup details](docs/desk.md) for manual removal and read-only startup files. End remote applications you no longer need separately. Uninstalling this plugin does not shut down remote hosts or erase their files.
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccountRevisions = exports.ACCOUNT_REMEMBER_UNAVAILABLE = void 0;
4
+ const crypto_1 = require("crypto");
5
+ /** No secrets: invalidates old sign-in forms in other windows before an account edit touches the store. */
6
+ const PREFIX = 'tabby-rdp.account-revision.';
7
+ exports.ACCOUNT_REMEMBER_UNAVAILABLE = 'To remember this password, save the account again in Settings and reopen this sign-in form.';
8
+ class AccountRevisions {
9
+ constructor(storage = () => window.localStorage, coordinated = () => !!globalThis.navigator?.locks) {
10
+ this.storage = storage;
11
+ this.coordinated = coordinated;
12
+ }
13
+ /** A prompt may remember only when this window's account agrees with the shared revision. */
14
+ capture(account) {
15
+ const revision = account.credentialRevision ?? '';
16
+ return this.current(account.id, revision) ? revision : undefined;
17
+ }
18
+ current(id, revision) {
19
+ if (revision === undefined || !this.coordinated()) {
20
+ return false;
21
+ }
22
+ try {
23
+ const shared = this.storage().getItem(PREFIX + id);
24
+ return shared === null || shared === revision;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /** Synchronous publication: older writers fail their next check, including one already waiting on a store. */
31
+ change(id) {
32
+ if (!this.coordinated()) {
33
+ throw new Error('Saved account changes are unavailable. Restart or update Tabby and try again.');
34
+ }
35
+ const revision = (0, crypto_1.randomUUID)();
36
+ // A failed write must stop the edit: a revision held only in this window cannot invalidate another's form.
37
+ try {
38
+ this.storage().setItem(PREFIX + id, revision);
39
+ }
40
+ catch {
41
+ throw new Error('Saved account changes are unavailable. Restart Tabby and try again.');
42
+ }
43
+ return revision;
44
+ }
45
+ }
46
+ exports.AccountRevisions = AccountRevisions;
package/dist/accounts.js CHANGED
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ASK_GATEWAY_ACCOUNT = void 0;
3
4
  exports.accountsOf = accountsOf;
4
5
  exports.newAccountId = newAccountId;
5
6
  exports.signInName = signInName;
6
7
  exports.accountKey = accountKey;
8
+ /** A separate gateway sign-in prompt. The '@' cannot occur in a saved account's id. */
9
+ exports.ASK_GATEWAY_ACCOUNT = '@ask';
7
10
  /** The usable entries of `remoteDesktop.accounts` (hand-written ones may lack fields). */
8
11
  function accountsOf(store) {
9
12
  const list = Array.isArray(store?.accounts) ? store.accounts : [];
@@ -16,7 +19,8 @@ function accountsOf(store) {
16
19
  }
17
20
  seen.add(id);
18
21
  const domain = typeof a.domain === 'string' && a.domain.trim() ? a.domain.trim() : undefined;
19
- return [{ id, name: typeof a.name === 'string' && a.name.trim() ? a.name.trim() : signInName({ username, domain }), username, ...domain ? { domain } : {} }];
22
+ return [{ id, name: typeof a.name === 'string' && a.name.trim() ? a.name.trim() : signInName({ username, domain }), username,
23
+ ...domain ? { domain } : {}, ...typeof a.credentialRevision === 'string' ? { credentialRevision: a.credentialRevision } : {} }];
20
24
  });
21
25
  }
22
26
  /** A new account's id: nothing in it but that it is unused. */
@@ -34,7 +34,7 @@ class ConnectionStatus {
34
34
  this.ticks = 0;
35
35
  this.last = null;
36
36
  this.frames = 0;
37
- this.hooked = null;
37
+ this.frameHook = null;
38
38
  this.latency = null;
39
39
  this.pinging = false;
40
40
  this.active = false;
@@ -57,6 +57,9 @@ class ConnectionStatus {
57
57
  if (active && canvas) {
58
58
  this.countFrames(canvas);
59
59
  }
60
+ else if (!active) {
61
+ this.releaseCanvas();
62
+ }
60
63
  if (active === this.active) {
61
64
  return;
62
65
  }
@@ -90,13 +93,16 @@ class ConnectionStatus {
90
93
  */
91
94
  countFrames(canvas) {
92
95
  const ctx = canvas.getContext('2d');
93
- if (!ctx || ctx === this.hooked) {
96
+ if (ctx === this.frameHook?.context) {
97
+ return;
98
+ }
99
+ this.releaseCanvas();
100
+ if (!ctx) {
94
101
  return;
95
102
  }
96
- this.hooked = ctx;
97
103
  const original = ctx.putImageData;
98
104
  let batch = false;
99
- ctx.putImageData = ((...args) => {
105
+ const wrapped = ((...args) => {
100
106
  if (!batch) {
101
107
  batch = true;
102
108
  this.frames++;
@@ -104,6 +110,17 @@ class ConnectionStatus {
104
110
  }
105
111
  return original.apply(ctx, args);
106
112
  });
113
+ ctx.putImageData = wrapped;
114
+ this.frameHook = { context: ctx, original, wrapped };
115
+ }
116
+ /** A detached canvas's handlers retain its backend, even after its component has been removed. */
117
+ releaseCanvas() {
118
+ const hook = this.frameHook;
119
+ this.frameHook = null;
120
+ // Leave a later owner's replacement alone.
121
+ if (hook && hook.context.putImageData === hook.wrapped) {
122
+ hook.context.putImageData = hook.original;
123
+ }
107
124
  }
108
125
  tick() {
109
126
  const now = performance.now();