tabby-rdp 0.2.7 → 0.3.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +72 -2
  2. package/README.md +182 -21
  3. package/dist/connectionStatus.js +140 -0
  4. package/dist/connectionStatus.js.map +1 -0
  5. package/dist/desk.js +21 -10
  6. package/dist/desk.js.map +1 -1
  7. package/dist/deskScript.js +3 -0
  8. package/dist/deskScript.js.map +1 -1
  9. package/dist/desktop.service.js +994 -53
  10. package/dist/desktop.service.js.map +1 -1
  11. package/dist/desktopForm.js +64 -19
  12. package/dist/desktopForm.js.map +1 -1
  13. package/dist/desktops.js +60 -21
  14. package/dist/desktops.js.map +1 -1
  15. package/dist/fileTransfer.js +152 -5
  16. package/dist/fileTransfer.js.map +1 -1
  17. package/dist/header.js +22 -7
  18. package/dist/header.js.map +1 -1
  19. package/dist/help.js +241 -0
  20. package/dist/help.js.map +1 -0
  21. package/dist/index.js +16 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/keyboard.js +142 -5
  24. package/dist/keyboard.js.map +1 -1
  25. package/dist/microphone.js +295 -0
  26. package/dist/microphone.js.map +1 -0
  27. package/dist/osd.js +79 -0
  28. package/dist/osd.js.map +1 -0
  29. package/dist/probe.js +33 -0
  30. package/dist/probe.js.map +1 -0
  31. package/dist/rdcleanpath.js +132 -7
  32. package/dist/rdcleanpath.js.map +1 -1
  33. package/dist/rdpFile.js +47 -0
  34. package/dist/rdpFile.js.map +1 -0
  35. package/dist/rdpProfile.js +367 -0
  36. package/dist/rdpProfile.js.map +1 -0
  37. package/dist/remoteSetup.js +90 -11
  38. package/dist/remoteSetup.js.map +1 -1
  39. package/dist/settingsPage.js +554 -0
  40. package/dist/settingsPage.js.map +1 -0
  41. package/dist/signin.js +21 -2
  42. package/dist/signin.js.map +1 -1
  43. package/dist/ssh.js +28 -0
  44. package/dist/ssh.js.map +1 -1
  45. package/dist/targets.js +148 -8
  46. package/dist/targets.js.map +1 -1
  47. package/dist/ui.js +163 -24
  48. package/dist/ui.js.map +1 -1
  49. package/dist/updates.js +123 -0
  50. package/dist/updates.js.map +1 -0
  51. package/dist/vms.js +90 -0
  52. package/dist/vms.js.map +1 -0
  53. package/dist/wake.js +124 -0
  54. package/dist/wake.js.map +1 -0
  55. package/package.json +2 -1
  56. package/vendor/IRONRDP-LICENSE +3 -3
  57. package/vendor/iron-remote-desktop-rdp.js +698 -451
  58. package/vendor/iron-remote-desktop.js +87 -81
  59. package/vendor/ironrdp_web_bg.wasm +0 -0
@@ -0,0 +1,554 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RemoteDesktopSettingsComponent = exports.RemoteDesktopSettingsTab = void 0;
13
+ const core_1 = require("@angular/core");
14
+ const tabby_core_1 = require("tabby-core");
15
+ const tabby_settings_1 = require("tabby-settings");
16
+ const desktop_service_1 = require("./desktop.service");
17
+ const desktops_1 = require("./desktops");
18
+ const osd_1 = require("./osd");
19
+ const help_1 = require("./help");
20
+ const updates_1 = require("./updates");
21
+ const targets_1 = require("./targets");
22
+ const REPO = 'https://github.com/meanaverage/tabby-rdp';
23
+ const esc = (s) => String(s ?? '').replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`);
24
+ const k = (s) => `<kbd>${esc(s)}</kbd>`;
25
+ const STYLE = `
26
+ .trd-settings { display: block; }
27
+ .trd-settings h3 { margin-bottom: 4px; }
28
+ .trd-settings .trd-lead { margin-bottom: 18px; opacity: 0.75; }
29
+ .trd-settings .trd-links { white-space: nowrap; }
30
+ .trd-settings .trd-lead a { margin-left: 10px; }
31
+ .trd-settings .trd-update { display: flex; gap: 10px; align-items: center; margin-bottom: 18px; padding: 10px 12px; border-radius: 6px;
32
+ border: 1px solid rgba(80, 150, 255, 0.45); background: rgba(80, 150, 255, 0.1); }
33
+ .trd-settings .trd-update > div { flex: auto; }
34
+ .trd-settings .trd-update:empty { display: none; }
35
+ .trd-settings section { margin-bottom: 28px; scroll-margin-top: 12px; }
36
+ .trd-settings section > h4 { font-size: 15px; margin-bottom: 10px; }
37
+ .trd-settings .trd-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; }
38
+ .trd-settings .trd-card { display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 6px;
39
+ border: 1px solid rgba(128, 128, 128, 0.25); }
40
+ .trd-settings .trd-card .trd-card-title { font-weight: 600; }
41
+ .trd-settings .trd-card .trd-card-text { flex: auto; font-size: 12px; opacity: 0.8; }
42
+ .trd-settings .trd-card button { align-self: flex-start; }
43
+ .trd-settings kbd { padding: 1px 5px; border-radius: 3px; font-size: 11px; background: rgba(128, 128, 128, 0.25);
44
+ color: inherit; white-space: nowrap; }
45
+ .trd-settings .trd-keys { width: 100%; font-size: 13px; }
46
+ .trd-settings .trd-keys td { padding: 5px 0; vertical-align: top; border-bottom: 1px solid rgba(128, 128, 128, 0.15); }
47
+ .trd-settings .trd-keys td:first-child { width: 38%; padding-right: 12px; }
48
+ .trd-settings .trd-list { display: flex; flex-direction: column; gap: 6px; }
49
+ .trd-settings .trd-row { display: flex; gap: 10px; align-items: center; padding: 8px 10px; border-radius: 6px;
50
+ border: 1px solid rgba(128, 128, 128, 0.2); }
51
+ .trd-settings .trd-row > div { flex: auto; min-width: 0; }
52
+ .trd-settings .trd-row .trd-sub { font-size: 12px; opacity: 0.7; overflow: hidden; text-overflow: ellipsis; }
53
+ .trd-settings .trd-empty { font-size: 12px; opacity: 0.7; }
54
+ .trd-settings details { border-bottom: 1px solid rgba(128, 128, 128, 0.15); padding: 6px 0; }
55
+ .trd-settings details summary { cursor: pointer; }
56
+ .trd-settings details > div { padding: 6px 0 4px 16px; font-size: 13px; opacity: 0.85; }
57
+ .trd-settings .trd-flash { animation: trd-flash 1.6s ease-out; border-radius: 6px; }
58
+ @keyframes trd-flash { from { background: rgba(80, 150, 255, 0.28); } }
59
+ .trd-settings .form-line .description kbd { font-size: 10px; }
60
+ .trd-settings .form-line { gap: 24px; }
61
+ .trd-settings .form-line > .header { min-width: 0; }
62
+ .trd-settings .form-line select { min-width: 200px; flex: none; }
63
+ .trd-settings .trd-osd-preview { position: relative; aspect-ratio: 16 / 9; max-width: 520px; margin: 4px 0 14px; border-radius: 6px;
64
+ overflow: hidden; background: linear-gradient(115deg, #e9edf1 0%, #cfd6dd 38%, #3a4048 62%, #1c1f24 100%); }
65
+ .trd-settings .trd-osd-stage { position: absolute; top: 0; left: 0; width: 1280px; height: 720px; transform-origin: 0 0; }
66
+ .trd-settings .trd-osd-preview .trd-osd { opacity: 1; }
67
+ .trd-settings .trd-color { display: flex; gap: 8px; align-items: center; }
68
+ .trd-settings .trd-color input[type=color] { width: 40px; height: 28px; padding: 0; border: 0; background: none; }
69
+ `;
70
+ /** "Remote Desktop" in Tabby's settings sidebar. */
71
+ let RemoteDesktopSettingsTab = class RemoteDesktopSettingsTab extends tabby_settings_1.SettingsTabProvider {
72
+ constructor() {
73
+ super(...arguments);
74
+ this.id = help_1.SETTINGS_TAB_ID;
75
+ this.icon = 'desktop';
76
+ this.title = 'Remote Desktop';
77
+ }
78
+ getComponentType() {
79
+ return RemoteDesktopSettingsComponent;
80
+ }
81
+ };
82
+ exports.RemoteDesktopSettingsTab = RemoteDesktopSettingsTab;
83
+ exports.RemoteDesktopSettingsTab = RemoteDesktopSettingsTab = __decorate([
84
+ (0, core_1.Injectable)()
85
+ ], RemoteDesktopSettingsTab);
86
+ /**
87
+ * The plugin's settings page: getting started, the settings, keys, desktops and certificates it keeps, and
88
+ * troubleshooting. Plain DOM like the rest of the plugin; the lists follow config changes made elsewhere.
89
+ */
90
+ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
91
+ constructor(element, desktop, help, config, platform, injector, zone) {
92
+ this.element = element;
93
+ this.desktop = desktop;
94
+ this.help = help;
95
+ this.config = config;
96
+ this.platform = platform;
97
+ this.injector = injector;
98
+ this.zone = zone;
99
+ this.contentBox = true;
100
+ this.trdSettings = true;
101
+ this.subscriptions = [];
102
+ }
103
+ ngOnInit() {
104
+ if (!document.getElementById('trd-settings-style')) {
105
+ const style = document.createElement('style');
106
+ style.id = 'trd-settings-style';
107
+ style.textContent = STYLE + osd_1.OSD_STYLE;
108
+ document.head.appendChild(style);
109
+ }
110
+ this.render();
111
+ // The preview is a 1280×720 desktop, scaled down to fit the page.
112
+ const frame = this.root.querySelector('.trd-osd-preview');
113
+ const scale = new ResizeObserver(() => {
114
+ frame.querySelector('.trd-osd-stage').style.transform = `scale(${frame.clientWidth / 1280})`;
115
+ });
116
+ scale.observe(frame);
117
+ this.subscriptions.push({ unsubscribe: () => scale.disconnect() });
118
+ this.subscriptions.push(this.config.changed$.subscribe(() => this.refresh()), this.desktop.changed$.subscribe(() => this.refresh()), this.help.show$.subscribe(({ topic, entry }) => setTimeout(() => this.reveal(topic, entry))), this.injector.get(updates_1.UpdateCheck).changed$.subscribe(() => this.refresh()));
119
+ const pending = this.help.pending;
120
+ this.help.pending = null;
121
+ if (pending) {
122
+ setTimeout(() => this.reveal(pending.topic, pending.entry));
123
+ }
124
+ }
125
+ ngOnDestroy() {
126
+ this.subscriptions.forEach(s => s.unsubscribe());
127
+ }
128
+ get root() {
129
+ return this.element.nativeElement;
130
+ }
131
+ render() {
132
+ const mac = process.platform === 'darwin';
133
+ const version = (() => {
134
+ try {
135
+ return require('../package.json').version;
136
+ }
137
+ catch {
138
+ return '';
139
+ }
140
+ })();
141
+ this.root.innerHTML = `
142
+ <h3>Remote Desktop</h3>
143
+ <div class="trd-lead">Linux and Windows desktops in Tabby tabs, through SSH or directly.
144
+ ${version ? `<span>tabby-rdp ${esc(version)}</span>` : ''}
145
+ <span class="trd-links"><a href="#" data-link="${REPO}#readme">Guide</a><a href="#" data-link="${REPO}/issues">Report a problem</a></span></div>
146
+ <div class="trd-update" data-update></div>
147
+
148
+ <section data-topic="start">
149
+ <h4>Getting started</h4>
150
+ <div class="trd-cards">
151
+ <div class="trd-card">
152
+ <div class="trd-card-title">Over SSH</div>
153
+ <div class="trd-card-text">In an SSH tab, press <span data-hotkey></span> or click <b>Desktop</b> in its
154
+ toolbar. The host is set up the first time (GNOME, xrdp, or Windows with OpenSSH); nothing to
155
+ install here, no ports to open.</div>
156
+ <button class="btn btn-secondary btn-sm" data-action="ssh">Open a connection…</button>
157
+ </div>
158
+ <div class="trd-card">
159
+ <div class="trd-card-title">On your network</div>
160
+ <div class="trd-card-text">A remote desktop profile connects straight to an RDP server's address, in a
161
+ tab of its own. Or type <code>user@host</code> in the profile selector.</div>
162
+ <button class="btn btn-secondary btn-sm" data-action="profile">New profile…</button>
163
+ </div>
164
+ <div class="trd-card">
165
+ <div class="trd-card-title">From an .rdp file</div>
166
+ <div class="trd-card-text">A file from Remote Desktop Connection or your admin becomes a profile: its
167
+ address, user name and domain.</div>
168
+ <button class="btn btn-secondary btn-sm" data-action="import">Import an .rdp file…</button>
169
+ </div>
170
+ </div>
171
+ </section>
172
+
173
+ <section data-topic="settings">
174
+ <h4>Settings</h4>
175
+ <div class="form-line">
176
+ <div class="header">
177
+ <div class="title">When the pane is resized</div>
178
+ <div class="description">Resizing follows splits and the window; a kept resolution can be scaled or
179
+ shown at actual size with scroll bars.</div>
180
+ </div>
181
+ <select class="form-control" data-setting="resize">
182
+ <option value="live">Resize to fit</option>
183
+ <option value="reconnect">Reconnect at the new size</option>
184
+ <option value="fit">Keep the size, scaled</option>
185
+ <option value="actual">Keep the size, 1:1</option>
186
+ </select>
187
+ </div>
188
+ <div class="form-line">
189
+ <div class="header">
190
+ <div class="title">Sharpness</div>
191
+ <div class="description">Retina renders at your screen's pixels, with the remote's scale set to
192
+ match. A desktop can have its own, in its tab's menu.</div>
193
+ </div>
194
+ <select class="form-control" data-setting="sharpness">
195
+ <option value="standard">Standard</option>
196
+ <option value="retina">Retina</option>
197
+ </select>
198
+ </div>
199
+ ${this.toggleLine('sound', 'Sound', 'Play the remote desktop\'s sound here. Applies on the next connection.')}
200
+ ${this.toggleLine('microphone', 'Microphone', 'Send your microphone while an app there records, with a red dot in the corner meanwhile. Applies on the next connection.')}
201
+ ${this.toggleLine('h264', 'Video decoding (H.264)', 'Decodes what the remote sends as video, hardware-accelerated where available. Applies on the next connection.')}
202
+ ${this.toggleLine('connectionStatus', 'Show connection status', 'A small line in the desktop\'s corner: throughput, frames per second, round trip, and how it\'s connected.')}
203
+ ${mac ? this.toggleLine('macShortcuts', 'Mac-style shortcuts', 'Use ⌘ as Ctrl on the desktop, so ⌘C copies and ⌘V pastes. Tap ⌘ on its own for the Windows key. When off, ⌘ is always the Windows key.') : ''}
204
+ ${this.toggleLine('checkUpdates', 'Tell me about new versions', 'Once a day, asks npm (registry.npmjs.org) for the latest tabby-rdp, and says so here and in the menus when there is one. Nothing is sent but the request. Tabby itself shows plugin upgrades only on its Plugins page.')}
205
+ ${this.toggleLine('discoverVMs', 'Find virtual machines on SSH hosts', 'Lists the host\'s libvirt VMs that have a desktop (RDP answering, or Windows and shut off) in its tab\'s menu, ready to open or start. Read-only: it runs virsh as you there, at most once a minute.')}
206
+ ${this.toggleLine('desk', 'desk: bring the console along', `Type <code>desk</code> in an SSH console to show that same shell on the desktop. Installs a small helper and a login line on GNOME hosts the next time a desktop opens there; off removes them.`)}
207
+ <div class="form-line">
208
+ <div class="header">
209
+ <div class="title">Switch between desktop and console</div>
210
+ <div class="description">Also the <b>Desktop</b> button in an SSH tab's toolbar and in Tabby's header.</div>
211
+ </div>
212
+ <div><span data-hotkey></span> <button class="btn btn-link btn-sm" data-action="hotkeys">Change…</button></div>
213
+ </div>
214
+ </section>
215
+
216
+ <section data-topic="osd">
217
+ <h4>Desktop name overlay</h4>
218
+ <div class="trd-lead">Names the desktop for a moment when it connects, when you switch to it, and when you click
219
+ into its pane, like a TV naming its input.</div>
220
+ <div class="trd-osd-preview"><div class="trd-osd-stage"><div class="trd-osd"></div></div></div>
221
+ <div class="form-line">
222
+ <div class="header"><div class="title">Show</div>
223
+ <div class="description">"When it helps": in split panes, and for a desktop other than the tab's own
224
+ connection, where the tab's title doesn't say which it is.</div></div>
225
+ <select class="form-control" data-osd="show">
226
+ <option value="auto">When it helps</option>
227
+ <option value="always">Every time</option>
228
+ <option value="off">Never</option>
229
+ </select>
230
+ </div>
231
+ <div class="form-line">
232
+ <div class="header"><div class="title">Font</div></div>
233
+ <select class="form-control" data-osd="font">${Object.entries(osd_1.OSD_FONTS).map(([id, f]) => `<option value="${id}">${esc(f.label)}</option>`).join('')}</select>
234
+ </div>
235
+ <div class="form-line">
236
+ <div class="header"><div class="title">Size</div><div class="description">Smaller in a narrow pane, so a name fits.</div></div>
237
+ <select class="form-control" data-osd="size">${Object.keys(osd_1.OSD_SIZES).map(id => `<option value="${id}">${id === 'huge' ? 'Extra large' : id[0].toUpperCase() + id.slice(1)}</option>`).join('')}</select>
238
+ </div>
239
+ <div class="form-line">
240
+ <div class="header"><div class="title">Position</div></div>
241
+ <select class="form-control" data-osd="position">${osd_1.OSD_POSITIONS.map(p => `<option value="${p}">${p === 'middle' ? 'Middle' : p.replace('-', ' ').replace(/^./, c => c.toUpperCase())}</option>`).join('')}</select>
242
+ </div>
243
+ <div class="form-line">
244
+ <div class="header"><div class="title">Color</div><div class="description">White reads on any picture, with the soft
245
+ shadow behind it. The line before the second row takes your theme's accent color.</div></div>
246
+ <div class="trd-color"><input type="color" data-osd="color"><button class="btn btn-link btn-sm" data-action="osd-white">White</button></div>
247
+ </div>
248
+ <div class="form-line">
249
+ <div class="header"><div class="title">How long</div></div>
250
+ <select class="form-control" data-osd="seconds">
251
+ <option value="1.5">1.5 seconds</option>
252
+ <option value="2.5">2.5 seconds</option>
253
+ <option value="4">4 seconds</option>
254
+ <option value="6">6 seconds</option>
255
+ </select>
256
+ </div>
257
+ <button class="btn btn-secondary btn-sm" data-action="osd-try">Show it on open desktops</button>
258
+ </section>
259
+
260
+ <section data-topic="keyboard">
261
+ <h4>Keys and tips</h4>
262
+ <table class="trd-keys"><tbody>
263
+ <tr><td data-hotkey-cell></td><td>Switch between the desktop and the SSH console. The desktop keeps running.</td></tr>
264
+ ${mac ? `
265
+ <tr><td>${k('⌘C')} ${k('⌘V')} ${k('⌘Z')} …</td><td>Work as on a Mac (sent as Ctrl).</td></tr>
266
+ <tr><td>Tap ${k('⌘')}</td><td>The Windows key (Start, or GNOME's Activities).</td></tr>
267
+ <tr><td>${k('⌃⌘')} + key</td><td>The Windows key with it: ${k('⌃⌘R')} for Win+R, ${k('⌃⌘E')} for Explorer.</td></tr>` : ''}
268
+ <tr><td>Right-click › <b>Send keys</b></td><td>Ctrl+Alt+Del, Win+L, Task Manager and others this computer keeps for itself.</td></tr>
269
+ <tr><td>Drop files on the desktop</td><td>Then paste them in Files or Explorer. Files copied there offer <b>Save to Downloads</b>.</td></tr>
270
+ <tr><td>Right-click › <b>View only</b></td><td>Watch without touching: no keys or clicks go to the desktop.</td></tr>
271
+ <tr><td>Right-click › <b>Save a screenshot</b></td><td>The full-resolution screen, to Downloads and the clipboard.</td></tr>
272
+ <tr><td>Tabby's shortcuts</td><td>While a desktop shows, only switching tabs, full screen and the switch above stay Tabby's; the rest go to the desktop.</td></tr>
273
+ </tbody></table>
274
+ </section>
275
+
276
+ <section data-topic="desktops">
277
+ <h4>Desktops behind SSH hosts</h4>
278
+ <div class="trd-list" data-list="desktops"></div>
279
+ </section>
280
+
281
+ <section data-topic="certificates">
282
+ <h4>Remembered certificates</h4>
283
+ <div class="trd-list" data-list="certificates"></div>
284
+ </section>
285
+
286
+ <section data-topic="troubleshooting">
287
+ <h4>Troubleshooting</h4>
288
+ <div data-list="troubleshooting">${help_1.TROUBLESHOOTING.map(e => `
289
+ <details data-entry="${esc(e.id)}"><summary>${esc(e.title)}</summary><div>${e.body}</div></details>`).join('')}
290
+ </div>
291
+ <h4 style="margin-top: 18px">Open desktops</h4>
292
+ <div class="trd-list" data-list="sessions"></div>
293
+ </section>`;
294
+ this.root.querySelectorAll('[data-setting]').forEach(el => {
295
+ el.addEventListener('change', () => this.zone.run(() => this.changeSetting(el)));
296
+ });
297
+ this.root.querySelectorAll('[data-osd]').forEach(el => {
298
+ // The color picker reports while dragging (input) and when closed (change).
299
+ el.addEventListener(el.type === 'color' ? 'input' : 'change', () => this.zone.run(() => {
300
+ const key = el.dataset.osd;
301
+ this.changeOsd({ [key]: key === 'seconds' ? Number(el.value) : el.value });
302
+ }));
303
+ });
304
+ this.root.querySelectorAll('[data-link]').forEach(el => el.addEventListener('click', event => {
305
+ event.preventDefault();
306
+ this.platform.openExternal(el.dataset.link);
307
+ }));
308
+ const actions = {
309
+ ssh: () => this.openConnection(),
310
+ profile: () => this.newProfile(),
311
+ import: () => this.desktop.importRdpFile(),
312
+ hotkeys: () => this.showTabbySettings('hotkeys'),
313
+ 'osd-white': () => this.changeOsd({ color: '' }),
314
+ 'osd-try': () => {
315
+ if (!this.desktop.showOsdEverywhere()) {
316
+ this.injector.get(tabby_core_1.NotificationsService).info('No desktop is showing right now');
317
+ }
318
+ },
319
+ };
320
+ this.root.querySelectorAll('[data-action]').forEach(el => el.addEventListener('click', () => {
321
+ this.zone.run(() => actions[el.dataset.action]?.());
322
+ }));
323
+ this.refresh();
324
+ }
325
+ toggleLine(key, title, description) {
326
+ return `
327
+ <div class="form-line">
328
+ <div class="header"><div class="title">${esc(title)}</div><div class="description">${description}</div></div>
329
+ <div class="form-check form-switch"><input type="checkbox" class="form-check-input" data-setting="${key}"></div>
330
+ </div>`;
331
+ }
332
+ changeOsd(change) {
333
+ this.desktop.updateSettings({ osd: { ...this.desktop.settings().osd, ...change } });
334
+ }
335
+ changeSetting(el) {
336
+ const key = el.dataset.setting;
337
+ if (el instanceof HTMLInputElement) {
338
+ this.desktop.updateSettings({ [key]: el.checked });
339
+ }
340
+ else if (key === 'resize') {
341
+ const value = el.value;
342
+ this.desktop.updateSettings(value === 'fit' || value === 'actual' ? { resize: 'off', zoom: value } : { resize: value });
343
+ }
344
+ else {
345
+ this.desktop.updateSettings({ [key]: el.value });
346
+ }
347
+ }
348
+ /** Puts the current values in the controls and rebuilds the lists. */
349
+ refresh() {
350
+ const settings = this.desktop.settings();
351
+ this.root.querySelectorAll('[data-setting]').forEach(el => {
352
+ const key = el.dataset.setting;
353
+ if (el instanceof HTMLInputElement) {
354
+ el.checked = !!settings[key];
355
+ }
356
+ else if (key === 'resize') {
357
+ el.value = settings.resize === 'off' ? settings.zoom : settings.resize;
358
+ }
359
+ else {
360
+ el.value = String(settings[key]);
361
+ }
362
+ });
363
+ // A newer version: say so at the top, with where to get it.
364
+ const updates = this.injector.get(updates_1.UpdateCheck);
365
+ const banner = this.root.querySelector('[data-update]');
366
+ if (banner) {
367
+ banner.innerHTML = updates.available ? `<div><b>tabby-rdp ${esc(updates.available)} is available.</b> You have ${esc((0, updates_1.installedVersion)())}.</div>
368
+ <button class="btn btn-primary btn-sm" data-upgrade>Upgrade…</button><button class="btn btn-link btn-sm" data-whatsnew>What's new</button>` : '';
369
+ banner.querySelector('[data-upgrade]')?.addEventListener('click', () => updates.upgrade());
370
+ banner.querySelector('[data-whatsnew]')?.addEventListener('click', () => updates.whatsNew());
371
+ }
372
+ const osd = settings.osd;
373
+ this.root.querySelectorAll('[data-osd]').forEach(el => {
374
+ const key = el.dataset.osd;
375
+ // A color input takes #rrggbb only: white stands for the default.
376
+ el.value = key === 'color' ? (/^#[0-9a-f]{6}$/i.test(osd.color) ? osd.color : '#ffffff') : String(osd[key]);
377
+ });
378
+ const stage = this.root.querySelector('.trd-osd-stage .trd-osd');
379
+ if (stage) {
380
+ (0, osd_1.renderOsd)(stage, 'workstation', 'via jumphost · 1280×720', osd);
381
+ }
382
+ const hotkey = this.help.toggleHotkey();
383
+ this.root.querySelectorAll('[data-hotkey]').forEach(el => {
384
+ el.innerHTML = hotkey ? k(hotkey) : '<i>(no shortcut)</i>';
385
+ });
386
+ const cell = this.root.querySelector('[data-hotkey-cell]');
387
+ if (cell) {
388
+ cell.innerHTML = hotkey ? k(hotkey) : 'The <b>Desktop</b> button';
389
+ }
390
+ this.renderDesktops();
391
+ this.renderCertificates();
392
+ this.renderSessions();
393
+ }
394
+ row(title, sub, buttons) {
395
+ const row = document.createElement('div');
396
+ row.className = 'trd-row';
397
+ row.innerHTML = `<div><div>${title}</div><div class="trd-sub">${sub}</div></div>`;
398
+ for (const b of buttons) {
399
+ const button = document.createElement('button');
400
+ button.className = `btn btn-sm ${b.danger ? 'btn-outline-danger' : 'btn-secondary'}`;
401
+ button.textContent = b.label;
402
+ button.addEventListener('click', () => this.zone.run(b.run));
403
+ row.appendChild(button);
404
+ }
405
+ return row;
406
+ }
407
+ empty(html) {
408
+ const div = document.createElement('div');
409
+ div.className = 'trd-empty';
410
+ div.innerHTML = html;
411
+ return div;
412
+ }
413
+ renderDesktops() {
414
+ const list = this.root.querySelector('[data-list=desktops]');
415
+ if (!list) {
416
+ return;
417
+ }
418
+ const desktops = this.desktop.configuredDesktops();
419
+ list.replaceChildren(...desktops.map((d, i) => {
420
+ const address = `${d.host ?? '127.0.0.1'}:${d.port ?? 3389}`;
421
+ const wake = d.wake?.vm ? ` · starts VM ${esc(d.wake.vm)}` : d.wake?.mac ? ` · wakes ${esc(d.wake.mac)}` : '';
422
+ return this.row(esc(d.name ?? address), `${esc(address)} behind ${esc(d.via)} · ${esc(d.kind ?? 'windows')}${d.username ? ` · ${esc(d.username)}` : ''}${wake}`, [
423
+ { label: 'Remove…', danger: true, run: () => { this.desktop.confirmRemoveDesktop(i); } },
424
+ ]);
425
+ }), this.empty(desktops.length
426
+ ? 'To edit one, right-click its host\'s SSH tab › Settings › Edit a desktop.'
427
+ : 'None yet. A Windows VM or another computer an SSH host can reach: right-click that host\'s SSH tab › <b>Desktops › Add a desktop behind…</b>'));
428
+ }
429
+ renderCertificates() {
430
+ const list = this.root.querySelector('[data-list=certificates]');
431
+ if (!list) {
432
+ return;
433
+ }
434
+ const entries = this.desktop.trustedCertificates();
435
+ list.replaceChildren(...entries.map(({ desktop, sha256 }) => {
436
+ const [who, address] = desktop.includes('#') ? desktop.split('#', 2) : [desktop, ''];
437
+ const title = address ? who === desktops_1.DIRECT_KEY ? `${esc(address)} (direct)` : `${esc(address)} behind ${esc(who)}` : esc(who);
438
+ return this.row(title, `SHA-256 ${esc(sha256.slice(0, 23))}…`, [
439
+ { label: 'Forget', run: () => this.desktop.forgetCertificate(desktop) },
440
+ ]);
441
+ }), this.empty(entries.length
442
+ ? 'A forgotten certificate is remembered again, without asking, on the next connection.'
443
+ : 'None yet. Windows and xrdp desktops\' certificates are remembered on first use and checked every time after; GNOME desktops only accept the one the plugin made.'));
444
+ }
445
+ renderSessions() {
446
+ const list = this.root.querySelector('[data-list=sessions]');
447
+ if (!list) {
448
+ return;
449
+ }
450
+ const sessions = this.desktop.sessionSummaries();
451
+ list.replaceChildren(...sessions.map(s => this.row(esc(s.name), `${s.where ? `via ${esc(s.where)} · ` : ''}${esc(s.state)}`, [
452
+ { label: 'Copy log', run: () => this.copyLog(s.name, s.log) },
453
+ ])), ...sessions.length ? [] : [this.empty('No desktop is open. Each open desktop\'s connection log can be copied from here, for a bug report.')]);
454
+ }
455
+ copyLog(name, log) {
456
+ const version = (() => {
457
+ try {
458
+ return require('../package.json').version;
459
+ }
460
+ catch {
461
+ return '?';
462
+ }
463
+ })();
464
+ this.platform.setClipboard({ text: [`tabby-rdp ${version} on ${process.platform}: ${name}`, ...log].join('\n') });
465
+ }
466
+ /** Scrolls to a section (and opens a troubleshooting entry), with a short highlight. */
467
+ reveal(topic, entry) {
468
+ const target = (entry && this.root.querySelector(`details[data-entry="${CSS.escape(entry)}"]`))
469
+ || this.root.querySelector(`[data-topic="${topic}"]`);
470
+ if (!target) {
471
+ return;
472
+ }
473
+ if (target instanceof HTMLDetailsElement) {
474
+ target.open = true;
475
+ }
476
+ // Getting started is at the top: show the page's title with it.
477
+ ;
478
+ (topic === 'start' && !entry ? this.root : target).scrollIntoView({ block: 'start', behavior: 'smooth' });
479
+ target.classList.remove('trd-flash');
480
+ void target.offsetWidth;
481
+ target.classList.add('trd-flash');
482
+ }
483
+ async openConnection() {
484
+ const profiles = this.injector.get(tabby_core_1.ProfilesService);
485
+ const profile = await profiles.showProfileSelector().catch(() => null);
486
+ if (profile) {
487
+ await profiles.openNewTabForProfile(profile);
488
+ }
489
+ }
490
+ /** Tabby's own profile editor, for a new remote desktop profile; else its Profiles page. */
491
+ async newProfile() {
492
+ const profiles = this.injector.get(tabby_core_1.ProfilesService);
493
+ const template = (await profiles.getProfiles()).find(p => p.type === targets_1.RDP_PROFILE_TYPE && p.isTemplate);
494
+ const provider = template && profiles.providerForProfile(template);
495
+ let modal = null;
496
+ try {
497
+ const { NgbModal } = require('@ng-bootstrap/ng-bootstrap');
498
+ const { EditProfileModalComponent } = require('tabby-settings');
499
+ modal = provider && this.injector.get(NgbModal).open(EditProfileModalComponent, { size: 'lg' });
500
+ }
501
+ catch {
502
+ modal = null;
503
+ }
504
+ if (!modal || !template || !provider) {
505
+ this.showTabbySettings('profiles');
506
+ return;
507
+ }
508
+ const { id: _id, isBuiltin: _b, isTemplate: _t, ...base } = JSON.parse(JSON.stringify(template));
509
+ modal.componentInstance.partialProfile = { ...base, name: '' };
510
+ modal.componentInstance.profileProvider = provider;
511
+ const result = await modal.result.catch(() => null);
512
+ if (!result) {
513
+ return;
514
+ }
515
+ result.type = targets_1.RDP_PROFILE_TYPE;
516
+ if (!result.name) {
517
+ result.name = provider.getSuggestedName(profiles.getConfigProxyForProfile(result)) ?? 'Remote desktop';
518
+ }
519
+ await profiles.newProfile(result);
520
+ await this.config.save();
521
+ this.injector.get(tabby_core_1.NotificationsService).notice(`Added "${result.name}": it's in the profile list with your other connections`);
522
+ }
523
+ /** Another page of Tabby's settings (Hotkeys, Profiles & connections). */
524
+ showTabbySettings(id) {
525
+ const settings = this.injector.get(tabby_settings_1.SettingsTabComponent, null)
526
+ ?? this.injector.get(tabby_core_1.AppService).tabs.find(t => t instanceof tabby_settings_1.SettingsTabComponent);
527
+ if (settings) {
528
+ settings.activeTab = id;
529
+ }
530
+ }
531
+ };
532
+ exports.RemoteDesktopSettingsComponent = RemoteDesktopSettingsComponent;
533
+ __decorate([
534
+ (0, core_1.HostBinding)('class.content-box'),
535
+ __metadata("design:type", Object)
536
+ ], RemoteDesktopSettingsComponent.prototype, "contentBox", void 0);
537
+ __decorate([
538
+ (0, core_1.HostBinding)('class.trd-settings'),
539
+ __metadata("design:type", Object)
540
+ ], RemoteDesktopSettingsComponent.prototype, "trdSettings", void 0);
541
+ exports.RemoteDesktopSettingsComponent = RemoteDesktopSettingsComponent = __decorate([
542
+ (0, core_1.Component)({
543
+ selector: 'remote-desktop-settings',
544
+ template: '',
545
+ }),
546
+ __metadata("design:paramtypes", [core_1.ElementRef,
547
+ desktop_service_1.RemoteDesktopService,
548
+ help_1.RemoteDesktopHelp,
549
+ tabby_core_1.ConfigService,
550
+ tabby_core_1.PlatformService,
551
+ core_1.Injector,
552
+ core_1.NgZone])
553
+ ], RemoteDesktopSettingsComponent);
554
+ //# sourceMappingURL=settingsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settingsPage.js","sourceRoot":"","sources":["../src/settingsPage.ts"],"names":[],"mappings":"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"}
package/dist/signin.js CHANGED
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
- /** Sign-in for desktops that use their own accounts (Windows): a form in the desktop layer, and the keychain. */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.STYLE = void 0;
5
4
  exports.loadCredentials = loadCredentials;
6
5
  exports.saveCredentials = saveCredentials;
7
6
  exports.forgetCredentials = forgetCredentials;
8
7
  exports.forgetCredentialsFor = forgetCredentialsFor;
8
+ exports.moveCredentialsFor = moveCredentialsFor;
9
9
  exports.askCredentials = askCredentials;
10
+ /** Sign-in for desktops that use their own accounts (Windows): a form in the desktop layer, and the keychain. */
11
+ const desktops_1 = require("./desktops");
10
12
  const KEYCHAIN_SERVICE = 'tabby-rdp';
11
13
  // The system keychain can keep a call waiting indefinitely: on Linux, a locked or missing keyring waits for an unlock
12
14
  // prompt, which a session without a keyring prompter never shows. Give up after a while. A call that never returns
@@ -54,12 +56,29 @@ async function forgetCredentials(key) {
54
56
  }
55
57
  catch { }
56
58
  }
59
+ /** Whether a saved account is for a desktop at this address behind some SSH host (not a direct one: `rdp#<id>`). */
60
+ function isBehindHost(account, desktopId) {
61
+ return account.endsWith(`#${desktopId}`) && !account.startsWith(`${desktops_1.DIRECT_KEY}#`);
62
+ }
57
63
  /** Forgets saved accounts for a desktop on any SSH host (session keys ending in `#<id>`). */
58
64
  async function forgetCredentialsFor(desktopId) {
59
65
  try {
60
66
  const k = keytar();
61
67
  for (const { account } of await answered(k?.findCredentials(KEYCHAIN_SERVICE)) ?? []) {
62
- if (account.endsWith(`#${desktopId}`)) {
68
+ if (isBehindHost(account, desktopId)) {
69
+ await answered(k.deletePassword(KEYCHAIN_SERVICE, account));
70
+ }
71
+ }
72
+ }
73
+ catch { }
74
+ }
75
+ /** A desktop's address changed: its saved accounts (session keys ending in `#<from>`) move to the new address. */
76
+ async function moveCredentialsFor(fromId, toId) {
77
+ try {
78
+ const k = keytar();
79
+ for (const { account, password } of await answered(k?.findCredentials(KEYCHAIN_SERVICE)) ?? []) {
80
+ if (isBehindHost(account, fromId)) {
81
+ await answered(k.setPassword(KEYCHAIN_SERVICE, `${account.slice(0, -fromId.length)}${toId}`, password));
63
82
  await answered(k.deletePassword(KEYCHAIN_SERVICE, account));
64
83
  }
65
84
  }