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 +75 -0
- package/README.md +41 -11
- package/TROUBLESHOOTING.md +82 -0
- package/dist/accountRevisions.js +46 -0
- package/dist/accounts.js +5 -1
- package/dist/connectionStatus.js +21 -4
- package/dist/desktop.service.js +296 -96
- package/dist/desktopForm.js +5 -4
- package/dist/diskStorage.js +212 -0
- package/dist/fileTransfer.js +6 -53
- package/dist/help.js +10 -62
- package/dist/hostCompat.js +86 -0
- package/dist/index.js +5 -2
- package/dist/ironrdp.js +26 -0
- package/dist/keyboard.js +12 -1
- package/dist/packageChangeDialog.js +186 -0
- package/dist/rdcleanpath.js +17 -4
- package/dist/rdpFile.js +13 -4
- package/dist/rdpProfile.js +44 -14
- package/dist/releaseSettings.js +72 -0
- package/dist/releases.js +411 -0
- package/dist/settingsPage.js +110 -115
- package/dist/signin.js +73 -10
- package/dist/targets.js +9 -1
- package/dist/updates.js +77 -89
- package/docs/desk.md +157 -0
- package/package.json +11 -2
- package/vendor/SHA256SUMS +1 -0
- package/vendor/ironrdp-factory.js +3237 -0
package/dist/settingsPage.js
CHANGED
|
@@ -22,8 +22,10 @@ const drives_1 = require("./drives");
|
|
|
22
22
|
const osd_1 = require("./osd");
|
|
23
23
|
const help_1 = require("./help");
|
|
24
24
|
const updates_1 = require("./updates");
|
|
25
|
+
const releaseSettings_1 = require("./releaseSettings");
|
|
25
26
|
const targets_1 = require("./targets");
|
|
26
27
|
const unshowable_1 = require("./unshowable");
|
|
28
|
+
const hostCompat_1 = require("./hostCompat");
|
|
27
29
|
const REPO = 'https://github.com/meanaverage/tabby-rdp';
|
|
28
30
|
const k = (s) => `<kbd>${(0, help_1.esc)(s)}</kbd>`;
|
|
29
31
|
/** The page's tabs (as Tabby's "Profiles & connections" page has Profiles and Advanced), each holding sections by topic. */
|
|
@@ -33,7 +35,7 @@ const TABS = [
|
|
|
33
35
|
{ id: 'osd', title: 'Overlay', topics: ['osd'] },
|
|
34
36
|
{ id: 'desktops', title: 'Desktops', topics: ['desktops', 'certificates'] },
|
|
35
37
|
{ id: 'accounts', title: 'Accounts', topics: ['accounts'] },
|
|
36
|
-
{ id: '
|
|
38
|
+
{ id: 'updates', title: 'Updates', topics: ['updates'] },
|
|
37
39
|
];
|
|
38
40
|
/** The tab shown last, so that the page comes back to it. */
|
|
39
41
|
let lastTab = 'start';
|
|
@@ -43,13 +45,22 @@ const STYLE = `
|
|
|
43
45
|
.trd-settings { display: block; }
|
|
44
46
|
.trd-settings h3 { margin-bottom: 4px; }
|
|
45
47
|
.trd-settings .trd-lead { margin-bottom: 18px; opacity: 0.75; }
|
|
46
|
-
.trd-settings .trd-links {
|
|
47
|
-
.trd-settings .trd-lead a {
|
|
48
|
+
.trd-settings .trd-links { display: inline-flex; flex-wrap: wrap; gap: 6px 10px; margin-left: 10px; }
|
|
49
|
+
.trd-settings .trd-lead a { white-space: nowrap; }
|
|
48
50
|
.trd-settings section .trd-lead a { margin-left: 0; cursor: pointer; text-decoration: underline; }
|
|
49
|
-
.trd-settings .trd-update { display: flex; gap: 10px; align-items: center; margin-bottom: 18px; padding: 10px 12px; border-radius: 6px;
|
|
51
|
+
.trd-settings .trd-update { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 18px; padding: 10px 12px; border-radius: 6px;
|
|
50
52
|
border: 1px solid rgba(80, 150, 255, 0.45); background: rgba(80, 150, 255, 0.1); }
|
|
51
53
|
.trd-settings .trd-update > div { flex: auto; }
|
|
52
54
|
.trd-settings .trd-update:empty { display: none; }
|
|
55
|
+
.trd-settings .trd-release-summary { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; }
|
|
56
|
+
.trd-settings .trd-release-summary h4 { margin: 3px 0; }
|
|
57
|
+
.trd-settings .trd-release-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 10px 0; }
|
|
58
|
+
.trd-settings .trd-release-actions select { width: auto; max-width: 100%; flex: 1 1 240px; }
|
|
59
|
+
.trd-settings .trd-release-notice { padding: 12px; border: 1px solid rgba(80,150,255,.45); border-radius: 6px; margin-bottom: 18px; }
|
|
60
|
+
.trd-settings .trd-release-error { color: var(--bs-danger, #e88); }
|
|
61
|
+
.trd-settings [data-release-settings] .trd-sub { font-size: 12px; opacity: .75; }
|
|
62
|
+
.trd-settings .trd-release-pause { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
|
|
63
|
+
@media (max-width: 700px) { .trd-settings [data-release-settings] .form-line { flex-wrap: wrap; gap: 8px; } }
|
|
53
64
|
.trd-settings .nav-tabs { margin-bottom: 18px; }
|
|
54
65
|
.trd-settings .nav-tabs .nav-link { cursor: pointer; }
|
|
55
66
|
.trd-settings section { margin-bottom: 28px; scroll-margin-top: 12px; }
|
|
@@ -61,7 +72,7 @@ const STYLE = `
|
|
|
61
72
|
.trd-settings .trd-card { display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 6px;
|
|
62
73
|
border: 1px solid rgba(128, 128, 128, 0.25); }
|
|
63
74
|
.trd-settings .trd-card .trd-card-title { font-weight: 600; display: flex; align-items: center; gap: 6px; }
|
|
64
|
-
.trd-settings
|
|
75
|
+
.trd-settings .trd-card-text .trd-info, .trd-settings .trd-lead .trd-info, .trd-settings .description .trd-info { margin-left: 4px; vertical-align: middle; font-size: 13px; }
|
|
65
76
|
.trd-settings .trd-info { position: relative; display: inline-flex; opacity: 0.55; cursor: help; outline: none; font-weight: normal; }
|
|
66
77
|
.trd-settings .trd-info:hover, .trd-settings .trd-info:focus { opacity: 1; }
|
|
67
78
|
.trd-settings .trd-info::after { content: attr(data-tip); position: absolute; left: 0; top: calc(100% + 6px); z-index: 20; width: 260px;
|
|
@@ -150,10 +161,10 @@ exports.RemoteDesktopSettingsTab = RemoteDesktopSettingsTab = __decorate([
|
|
|
150
161
|
], RemoteDesktopSettingsTab);
|
|
151
162
|
/**
|
|
152
163
|
* The plugin's settings page: getting started, the settings, keys, desktops and certificates it keeps, and
|
|
153
|
-
*
|
|
164
|
+
* updates. Plain DOM like the rest of the plugin; the lists follow config changes made elsewhere.
|
|
154
165
|
*/
|
|
155
166
|
let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
156
|
-
constructor(element, desktop, help, config, platform, injector, zone, hotkeys) {
|
|
167
|
+
constructor(element, desktop, help, config, platform, injector, zone, hotkeys, compat) {
|
|
157
168
|
this.element = element;
|
|
158
169
|
this.desktop = desktop;
|
|
159
170
|
this.help = help;
|
|
@@ -162,6 +173,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
162
173
|
this.injector = injector;
|
|
163
174
|
this.zone = zone;
|
|
164
175
|
this.hotkeys = hotkeys;
|
|
176
|
+
this.compat = compat;
|
|
165
177
|
this.contentBox = true;
|
|
166
178
|
this.trdSettings = true;
|
|
167
179
|
this.subscriptions = [];
|
|
@@ -176,8 +188,6 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
176
188
|
document.head.appendChild(style);
|
|
177
189
|
}
|
|
178
190
|
this.render();
|
|
179
|
-
// A known newer version may be stale by now (the page is where it shows): ask npm again.
|
|
180
|
-
this.injector.get(updates_1.UpdateCheck).refresh();
|
|
181
191
|
// The preview is a desktop of the chosen resolution, scaled down to fit the page.
|
|
182
192
|
const frame = this.root.querySelector('.trd-osd-preview');
|
|
183
193
|
const scale = new ResizeObserver(() => this.scalePreview());
|
|
@@ -198,38 +208,33 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
198
208
|
}
|
|
199
209
|
render() {
|
|
200
210
|
const mac = process.platform === 'darwin';
|
|
201
|
-
const version = (()
|
|
202
|
-
try {
|
|
203
|
-
return require('../package.json').version;
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
return '';
|
|
207
|
-
}
|
|
208
|
-
})();
|
|
211
|
+
const version = (0, updates_1.installedVersion)();
|
|
209
212
|
this.root.innerHTML = `
|
|
210
213
|
<h3>Remote Desktop</h3>
|
|
211
214
|
<div class="trd-lead">Linux and Windows desktops in Tabby tabs, through SSH or directly.
|
|
212
215
|
${version ? `<span>tabby-rdp ${(0, help_1.esc)(version)}</span>` : ''}
|
|
213
|
-
<span class="trd-links"><a href="#" data-link="${REPO}#readme">Guide</a><a href="#" data-link="${REPO}/issues">Report a problem</a><a href="#" data-link="${REPO}/discussions/categories/q-a">Ask a question</a></span></div>
|
|
214
|
-
<div class="trd-update" data-update></div>
|
|
216
|
+
<span class="trd-links"><a href="#" data-link="${REPO}#readme">Guide</a><a href="#" data-link="${REPO}/issues">Report a problem</a><a href="#" data-link="${REPO}/discussions/categories/q-a">Ask a question</a><a href="#" data-link="${help_1.TROUBLESHOOTING_URL}">Troubleshooting</a></span></div>
|
|
215
217
|
<ul class="nav nav-tabs" data-nav></ul>
|
|
216
218
|
|
|
217
219
|
<section data-topic="start">
|
|
218
220
|
<h4>Getting started</h4>
|
|
219
221
|
<div class="trd-cards">
|
|
220
222
|
<div class="trd-card">
|
|
221
|
-
<div class="trd-card-title">Over SSH
|
|
222
|
-
<div class="trd-card-text">In an SSH tab, press <span data-hotkey></span> or click <b>Desktop</b
|
|
223
|
+
<div class="trd-card-title">Over SSH</div>
|
|
224
|
+
<div class="trd-card-text">In an SSH tab, press <span data-hotkey></span> or click <b>Desktop</b>.
|
|
225
|
+
${this.info('The host is set up on the first connection: GNOME, xrdp, or Windows with OpenSSH. Nothing to install here, no ports to open.')}</div>
|
|
223
226
|
<button class="btn btn-secondary btn-sm" data-action="ssh">Open a connection…</button>
|
|
224
227
|
</div>
|
|
225
228
|
<div class="trd-card">
|
|
226
|
-
<div class="trd-card-title">On your network
|
|
227
|
-
<div class="trd-card-text">A profile that connects straight to an RDP server
|
|
229
|
+
<div class="trd-card-title">On your network</div>
|
|
230
|
+
<div class="trd-card-text">A profile that connects straight to an RDP server.
|
|
231
|
+
${this.info('The desktop opens in a tab of its own. For a quick connection, type user@host in the profile selector.')}</div>
|
|
228
232
|
<button class="btn btn-secondary btn-sm" data-action="profile">New profile…</button>
|
|
229
233
|
</div>
|
|
230
234
|
<div class="trd-card">
|
|
231
|
-
<div class="trd-card-title">From an .rdp file
|
|
232
|
-
<div class="trd-card-text">The file becomes a profile
|
|
235
|
+
<div class="trd-card-title">From an .rdp file</div>
|
|
236
|
+
<div class="trd-card-text">The file becomes a profile.
|
|
237
|
+
${this.info('A file from Remote Desktop Connection, or from your admin. Its address, user name and domain make the profile; the password is asked for.')}</div>
|
|
233
238
|
<button class="btn btn-secondary btn-sm" data-action="import">Import an .rdp file…</button>
|
|
234
239
|
</div>
|
|
235
240
|
</div>
|
|
@@ -241,8 +246,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
241
246
|
<div class="form-line">
|
|
242
247
|
<div class="header">
|
|
243
248
|
<div class="title">When the pane is resized</div>
|
|
244
|
-
<div class="description">
|
|
245
|
-
shown at actual size with scroll bars.</div>
|
|
249
|
+
<div class="description">Fixed resolutions use scaling or scroll bars.</div>
|
|
246
250
|
</div>
|
|
247
251
|
<select class="form-control" data-setting="resize">
|
|
248
252
|
<option value="live">Resize to fit</option>
|
|
@@ -254,28 +258,27 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
254
258
|
<div class="form-line">
|
|
255
259
|
<div class="header">
|
|
256
260
|
<div class="title">Sharpness</div>
|
|
257
|
-
<div class="description">Retina
|
|
258
|
-
match. A desktop can have its own, in its tab's menu.</div>
|
|
261
|
+
<div class="description">Retina uses your screen's pixel density and matching remote scaling.</div>
|
|
259
262
|
</div>
|
|
260
263
|
<select class="form-control" data-setting="sharpness">
|
|
261
264
|
<option value="standard">Standard</option>
|
|
262
265
|
<option value="retina">Retina</option>
|
|
263
266
|
</select>
|
|
264
267
|
</div>
|
|
265
|
-
${this.toggleLine('h264', 'Video decoding (H.264)', '
|
|
266
|
-
${this.toggleLine('connectionStatus', 'Show connection status', '
|
|
268
|
+
${this.toggleLine('h264', 'Video decoding (H.264)', 'Hardware acceleration where available. Applies on the next connection.')}
|
|
269
|
+
${this.toggleLine('connectionStatus', 'Show connection status', 'Show throughput, frame rate, latency and connection details.')}
|
|
267
270
|
|
|
268
271
|
<h5>Sound</h5>
|
|
269
|
-
${this.toggleLine('sound', 'Sound', 'Play
|
|
270
|
-
${this.toggleLine('microphone', 'Microphone',
|
|
272
|
+
${this.toggleLine('sound', 'Sound', 'Play remote audio. Applies on the next connection.')}
|
|
273
|
+
${this.toggleLine('microphone', 'Microphone', `Share your microphone when a remote app requests it. Applies on the next connection; turning it off stops sharing immediately. ${this.info('A red indicator remains visible while sharing, including from background desktops. Its menu can stop sharing with an individual desktop. That desktop stays muted through reconnects until re-enabled or Tabby closes.')}`)}
|
|
271
274
|
|
|
272
275
|
<h5>Clipboard</h5>
|
|
273
276
|
<div class="form-line">
|
|
274
277
|
<div class="header">
|
|
275
|
-
<div class="title">Clipboard sharing
|
|
276
|
-
<div class="description">
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
<div class="title">Clipboard sharing</div>
|
|
279
|
+
<div class="description">Share text, images and files. Desktops can override this setting. Applies on reconnect;
|
|
280
|
+
restricting sharing stops local sends immediately.
|
|
281
|
+
${this.info('Sharing can expose passwords and let the remote replace your local clipboard. Only the focused desktop receives your clipboard. Off disables text, images and clipboard file transfers. Restricting sharing also withdraws previously offered files.')}</div>
|
|
279
282
|
</div>
|
|
280
283
|
<select class="form-control" data-setting="clipboard">
|
|
281
284
|
${Object.entries(clipboard_1.CLIPBOARD_LABELS).map(([mode, label]) => `<option value="${mode}">${(0, help_1.esc)(label)}</option>`).join('')}
|
|
@@ -283,33 +286,30 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
283
286
|
</div>
|
|
284
287
|
|
|
285
288
|
<h5>Shared folders</h5>
|
|
286
|
-
<div class="trd-lead">
|
|
287
|
-
|
|
288
|
-
${this.info('Every remote desktop you connect to sees these folders, so share only what each of them may have. Read-only keeps the remote from changing anything in a folder. Windows mounts them (xrdp can, with FUSE); GNOME Remote Desktop doesn\'t serve drives.')}</div>
|
|
289
|
+
<div class="trd-lead">Available as remote drives (<code>\\\\tsclient</code> on Windows). Applies on the next connection.
|
|
290
|
+
${this.info('Shared with every remote desktop. Use read-only to prevent remote changes. Supported by Windows and xrdp with FUSE; unavailable on GNOME Remote Desktop.')}</div>
|
|
289
291
|
<div class="trd-list" data-list="folders"></div>
|
|
290
292
|
<div class="trd-add"><button class="btn btn-secondary btn-sm" data-action="folder">Share a folder…</button></div>
|
|
291
293
|
|
|
292
294
|
<h5>Keyboard</h5>
|
|
293
|
-
${this.toggleLine('unicodeKeys', 'Send text as typed',
|
|
294
|
-
${mac ? this.toggleLine('macShortcuts', 'Mac-style shortcuts', '
|
|
295
|
+
${this.toggleLine('unicodeKeys', 'Send text as typed', `Send characters instead of physical keys. Modifier shortcuts still use key positions. ${this.info('Useful for dead keys or mismatched keyboard layouts. GNOME is limited to characters in its own layout. Chinese, Japanese and Korean input methods are not supported.')}`)}
|
|
296
|
+
${mac ? this.toggleLine('macShortcuts', 'Mac-style shortcuts', '⌘C copies and ⌘V pastes. Tap ⌘ for the Windows key; when disabled, ⌘ always sends the Windows key.') : ''}
|
|
295
297
|
<div class="form-line">
|
|
296
298
|
<div class="header">
|
|
297
299
|
<div class="title">Shortcuts</div>
|
|
298
|
-
<div class="description">
|
|
299
|
-
<a data-action="shortcuts">Getting started</a> tab, and in Tabby's Hotkeys.</div>
|
|
300
|
+
<div class="description">Manage in <a data-action="shortcuts">Getting started</a> or Tabby's Hotkeys.</div>
|
|
300
301
|
</div>
|
|
301
302
|
<div><span data-hotkey></span></div>
|
|
302
303
|
</div>
|
|
303
304
|
|
|
304
305
|
<h5>SSH hosts</h5>
|
|
305
|
-
${this.toggleLine('desk', 'desk
|
|
306
|
-
${this.toggleLine('discoverVMs', 'Find virtual machines on SSH hosts', '
|
|
306
|
+
${this.toggleLine('desk', 'desk integration', `Type <code>desk</code> to open your current SSH shell on the desktop. Installs a helper on GNOME hosts on the next desktop connection; disabling removes it.`)}
|
|
307
|
+
${this.toggleLine('discoverVMs', 'Find virtual machines on SSH hosts', 'List libvirt and Hyper-V VMs in the SSH tab menu. Discovery is read-only and runs at most once a minute.')}
|
|
307
308
|
<div class="form-line">
|
|
308
309
|
<div class="header">
|
|
309
|
-
<div class="title">Shut down VMs
|
|
310
|
-
<div class="description">
|
|
311
|
-
|
|
312
|
-
were already running are left alone.</div>
|
|
310
|
+
<div class="title">Shut down started VMs</div>
|
|
311
|
+
<div class="description">Shut down VMs started by this plugin after the selected time with no desktop open.
|
|
312
|
+
Continues after Tabby closes; previously running VMs are unaffected.</div>
|
|
313
313
|
</div>
|
|
314
314
|
<select class="form-control" data-setting="shutDownIdle">
|
|
315
315
|
<option value="0">Never</option>
|
|
@@ -319,16 +319,13 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
319
319
|
</select>
|
|
320
320
|
</div>
|
|
321
321
|
|
|
322
|
-
<h5>Updates</h5>
|
|
323
|
-
${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.')}
|
|
324
322
|
</section>
|
|
325
323
|
|
|
326
324
|
<section data-topic="osd">
|
|
327
325
|
<h4>Desktop name overlay</h4>
|
|
328
|
-
<div class="trd-lead">
|
|
329
|
-
into its pane, like a TV naming its input.</div>
|
|
326
|
+
<div class="trd-lead">Show the desktop name when connecting, switching desktops or focusing a pane.</div>
|
|
330
327
|
<div class="trd-osd-preview"><div class="trd-osd-stage"><div class="trd-osd"></div></div>
|
|
331
|
-
<select class="form-control form-control-sm trd-osd-resolution" data-preview-resolution title="
|
|
328
|
+
<select class="form-control form-control-sm trd-osd-resolution" data-preview-resolution title="Preview resolution">
|
|
332
329
|
<option value="1280x720">1280 × 720</option>
|
|
333
330
|
<option value="1920x1080">1920 × 1080</option>
|
|
334
331
|
<option value="2560x1440">2560 × 1440</option>
|
|
@@ -336,10 +333,9 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
336
333
|
</select></div>
|
|
337
334
|
<div class="form-line">
|
|
338
335
|
<div class="header"><div class="title">Show</div>
|
|
339
|
-
<div class="description">
|
|
340
|
-
connection, where the tab's title doesn't say which it is.</div></div>
|
|
336
|
+
<div class="description">Automatic shows names in split panes and when the tab title identifies a different connection.</div></div>
|
|
341
337
|
<select class="form-control" data-osd="show">
|
|
342
|
-
<option value="auto">
|
|
338
|
+
<option value="auto">Automatic</option>
|
|
343
339
|
<option value="always">Every time</option>
|
|
344
340
|
<option value="off">Never</option>
|
|
345
341
|
</select>
|
|
@@ -349,7 +345,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
349
345
|
<select class="form-control" data-osd="font">${Object.entries(osd_1.OSD_FONTS).map(([id, f]) => `<option value="${id}">${(0, help_1.esc)(f.label)}</option>`).join('')}</select>
|
|
350
346
|
</div>
|
|
351
347
|
<div class="form-line">
|
|
352
|
-
<div class="header"><div class="title">Size</div
|
|
348
|
+
<div class="header"><div class="title">Size</div></div>
|
|
353
349
|
<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>
|
|
354
350
|
</div>
|
|
355
351
|
<div class="form-line">
|
|
@@ -357,12 +353,11 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
357
353
|
<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>
|
|
358
354
|
</div>
|
|
359
355
|
<div class="form-line">
|
|
360
|
-
<div class="header"><div class="title">Color</div
|
|
361
|
-
shadow behind it. The line before the second row takes your theme's accent color.</div></div>
|
|
356
|
+
<div class="header"><div class="title">Color</div></div>
|
|
362
357
|
<div class="trd-color"><input type="color" data-osd="color"><button class="btn btn-link btn-sm" data-action="osd-white">White</button></div>
|
|
363
358
|
</div>
|
|
364
359
|
<div class="form-line">
|
|
365
|
-
<div class="header"><div class="title">
|
|
360
|
+
<div class="header"><div class="title">Duration</div></div>
|
|
366
361
|
<select class="form-control" data-osd="seconds">
|
|
367
362
|
<option value="1.5">1.5 seconds</option>
|
|
368
363
|
<option value="2.5">2.5 seconds</option>
|
|
@@ -370,12 +365,13 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
370
365
|
<option value="6">6 seconds</option>
|
|
371
366
|
</select>
|
|
372
367
|
</div>
|
|
373
|
-
<button class="btn btn-secondary btn-sm" data-action="osd-try">
|
|
368
|
+
<button class="btn btn-secondary btn-sm" data-action="osd-try">Preview on open desktops</button>
|
|
374
369
|
</section>
|
|
375
370
|
|
|
376
371
|
<section data-topic="keyboard">
|
|
377
|
-
<h4>Shortcuts
|
|
378
|
-
<div class="trd-lead">Also listed in Tabby's <a data-action="hotkeys">Hotkeys</a
|
|
372
|
+
<h4>Shortcuts</h4>
|
|
373
|
+
<div class="trd-lead">Also listed in Tabby's <a data-action="hotkeys">Hotkeys</a>.
|
|
374
|
+
${this.info('These work while the desktop has the keyboard. Tabby\'s other shortcuts go to the desktop then, except switching tabs and full screen.')}</div>
|
|
379
375
|
<div class="trd-hotkeys" data-list="hotkeys"></div>
|
|
380
376
|
<h4 style="margin-top: 24px">Keys and tips</h4>
|
|
381
377
|
<table class="trd-keys"><tbody>
|
|
@@ -395,13 +391,15 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
395
391
|
<div class="trd-lead">Remote desktop profiles, and desktops added from SSH tabs.
|
|
396
392
|
${this.info('A desktop added from an SSH tab\'s menu belongs to that host. That is for hosts without a profile, such as ssh run in a local terminal.')}</div>
|
|
397
393
|
<div class="trd-list" data-list="desktops"></div>
|
|
398
|
-
<div class="trd-add"><button class="btn btn-secondary btn-sm" data-action="desktop">Add
|
|
394
|
+
<div class="trd-add"><button class="btn btn-secondary btn-sm" data-action="desktop">Add desktop…</button></div>
|
|
395
|
+
<h4 style="margin-top: 24px">Open desktops</h4>
|
|
396
|
+
<div class="trd-list" data-list="sessions"></div>
|
|
399
397
|
</section>
|
|
400
398
|
|
|
401
399
|
<section data-topic="accounts">
|
|
402
400
|
<h4>Accounts</h4>
|
|
403
|
-
<div class="trd-lead">
|
|
404
|
-
${this.info('
|
|
401
|
+
<div class="trd-lead">Reusable sign-in details for multiple desktops.
|
|
402
|
+
${this.info('Choose an account in a desktop\'s settings or a profile group\'s defaults.')}</div>
|
|
405
403
|
<div class="trd-list" data-list="accounts"></div>
|
|
406
404
|
<div class="trd-add">
|
|
407
405
|
<button class="btn btn-secondary btn-sm" data-action="account">Add an account…</button>
|
|
@@ -414,14 +412,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
414
412
|
<div class="trd-list" data-list="certificates"></div>
|
|
415
413
|
</section>
|
|
416
414
|
|
|
417
|
-
<section data-topic="
|
|
418
|
-
<h4>Troubleshooting</h4>
|
|
419
|
-
<div data-list="troubleshooting">${help_1.TROUBLESHOOTING.map(e => `
|
|
420
|
-
<details data-entry="${(0, help_1.esc)(e.id)}"><summary>${(0, help_1.esc)(e.title)}</summary><div>${e.body}</div></details>`).join('')}
|
|
421
|
-
</div>
|
|
422
|
-
<h4 style="margin-top: 18px">Open desktops</h4>
|
|
423
|
-
<div class="trd-list" data-list="sessions"></div>
|
|
424
|
-
</section>`;
|
|
415
|
+
<section data-topic="updates"><h4>Updates</h4><div class="trd-update" data-update></div><div data-release-settings></div></section>`;
|
|
425
416
|
// The sections go into the tabs' bodies; one tab shows at a time.
|
|
426
417
|
const nav = this.root.querySelector('[data-nav]');
|
|
427
418
|
for (const tab of TABS) {
|
|
@@ -482,7 +473,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
482
473
|
'osd-white': () => this.changeOsd({ color: '' }),
|
|
483
474
|
'osd-try': () => {
|
|
484
475
|
if (!this.desktop.showOsdEverywhere()) {
|
|
485
|
-
this.injector.get(tabby_core_1.NotificationsService).info('No
|
|
476
|
+
this.injector.get(tabby_core_1.NotificationsService).info('No visible desktops.');
|
|
486
477
|
}
|
|
487
478
|
},
|
|
488
479
|
};
|
|
@@ -491,7 +482,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
491
482
|
}));
|
|
492
483
|
this.refresh();
|
|
493
484
|
}
|
|
494
|
-
/** An ⓘ
|
|
485
|
+
/** An ⓘ placed after a description, showing `text` while hovered or focused. */
|
|
495
486
|
info(text) {
|
|
496
487
|
return `<span class="trd-info" tabindex="0" role="note" data-tip="${(0, help_1.esc)(text)}"><i class="fas fa-info-circle"></i></span>`;
|
|
497
488
|
}
|
|
@@ -545,7 +536,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
545
536
|
el.value = String(settings[key]);
|
|
546
537
|
}
|
|
547
538
|
});
|
|
548
|
-
//
|
|
539
|
+
// Update notices and controls belong to the Updates page.
|
|
549
540
|
const updates = this.injector.get(updates_1.UpdateCheck);
|
|
550
541
|
const banner = this.root.querySelector('[data-update]');
|
|
551
542
|
if (banner) {
|
|
@@ -554,6 +545,10 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
554
545
|
banner.querySelector('[data-upgrade]')?.addEventListener('click', () => updates.upgrade());
|
|
555
546
|
banner.querySelector('[data-whatsnew]')?.addEventListener('click', () => updates.whatsNew());
|
|
556
547
|
}
|
|
548
|
+
(0, releaseSettings_1.renderReleaseSettings)(this.root.querySelector('[data-release-settings]'), updates, {
|
|
549
|
+
notes: version => updates.whatsNew(version), plugins: () => this.help.openSettings('plugins'),
|
|
550
|
+
link: url => this.platform.openExternal(url),
|
|
551
|
+
});
|
|
557
552
|
const osd = settings.osd;
|
|
558
553
|
this.root.querySelectorAll('[data-osd]').forEach(el => {
|
|
559
554
|
const key = el.dataset.osd;
|
|
@@ -602,29 +597,34 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
602
597
|
const accounts = this.desktop.accounts();
|
|
603
598
|
const accountOf = (id) => id ? accounts.find(a => a.id === id) : undefined;
|
|
604
599
|
// Remote desktop profiles first (Tabby's editor), then the desktops added from SSH tabs (the plugin's form).
|
|
605
|
-
const profiles = (this.config.store.profiles ?? []).filter((p) => p?.type === targets_1.RDP_PROFILE_TYPE
|
|
606
|
-
const
|
|
600
|
+
const profiles = (this.config.store.profiles ?? []).filter((p) => p?.type === targets_1.RDP_PROFILE_TYPE);
|
|
601
|
+
const gatewaySignIn = (id) => {
|
|
602
|
+
const account = accountOf(id);
|
|
603
|
+
return account ? `, as ${(0, help_1.esc)(account.name)}` : id === accounts_1.ASK_GATEWAY_ACCOUNT ? ', asks for a gateway account'
|
|
604
|
+
: id ? ', saved gateway account missing (asks when connecting)' : '';
|
|
605
|
+
};
|
|
607
606
|
// Every field as text whatever the config holds (see configText): one that can't be shown would leave this list,
|
|
608
607
|
// and the lists after it on the page, unbuilt.
|
|
609
|
-
const profileRows = profiles.
|
|
610
|
-
const o = p.options;
|
|
608
|
+
const profileRows = profiles.flatMap(p => {
|
|
609
|
+
const o = this.injector.get(tabby_core_1.ProfilesService).getConfigProxyForProfile(p).options;
|
|
610
|
+
if (!o?.host) {
|
|
611
|
+
return [];
|
|
612
|
+
}
|
|
611
613
|
const address = `${(0, desktops_1.configText)(o.host)}:${(0, desktops_1.configText)(o.port || 3389)}`;
|
|
612
614
|
const via = o.via ? (0, desktops_1.configText)((this.config.store.profiles ?? []).find((x) => x.id === o.via)?.name) || 'an SSH profile' : null;
|
|
613
|
-
const
|
|
614
|
-
const
|
|
615
|
-
// Its own clipboard, or its group's or the type's default: what it connects with, as Tabby resolves the profile.
|
|
616
|
-
const clipboard = (0, clipboard_1.ownClipboard)(this.injector.get(tabby_core_1.ProfilesService).getConfigProxyForProfile(p).options?.clipboard);
|
|
615
|
+
const account = accountOf(o.account);
|
|
616
|
+
const clipboard = (0, clipboard_1.ownClipboard)(o.clipboard);
|
|
617
617
|
const [username, kind, gateway] = [(0, desktops_1.configText)(o.username), (0, desktops_1.configText)(o.kind), (0, desktops_1.configText)(o.gateway)];
|
|
618
618
|
const details = [
|
|
619
619
|
account ? `Signs in as ${(0, help_1.esc)(account.name)}` : username ? `Signs in as ${(0, help_1.esc)(username)}` : '',
|
|
620
620
|
kind && kind !== 'windows' ? (0, help_1.esc)(kind) : '',
|
|
621
|
-
gateway ? `Through the gateway ${(0, help_1.esc)(gateway)}${
|
|
621
|
+
gateway ? `Through the gateway ${(0, help_1.esc)(gateway)}${gatewaySignIn(o.gatewayAccount)}` : '',
|
|
622
622
|
clipboard ? `Clipboard: ${(0, help_1.esc)(clipboard_1.CLIPBOARD_LABELS[clipboard].toLowerCase())}` : '',
|
|
623
623
|
].filter(Boolean).join('<br>');
|
|
624
|
-
return this.row((0, help_1.esc)((0, desktops_1.configText)(p.name) || address), `${(0, help_1.esc)(address)}${via ? ` via ${(0, help_1.esc)(via)}` : ''}${details ? `<br>${details}` : ''}`, [
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
624
|
+
return [this.row((0, help_1.esc)((0, desktops_1.configText)(p.name) || address), `${(0, help_1.esc)(address)}${via ? ` via ${(0, help_1.esc)(via)}` : ''}${details ? `<br>${details}` : ''}`, [
|
|
625
|
+
{ label: 'Edit…', run: () => { this.editProfile(p.id); } },
|
|
626
|
+
{ label: 'Remove…', danger: true, run: () => { this.removeProfile(p.id); } },
|
|
627
|
+
])];
|
|
628
628
|
});
|
|
629
629
|
const desktops = this.desktop.configuredDesktops();
|
|
630
630
|
// An item that is no entry describes no desktop, and isn't listed (see isDesktopEntry).
|
|
@@ -639,7 +639,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
639
639
|
const details = [
|
|
640
640
|
account ? `Signs in as ${(0, help_1.esc)(account.name)}` : username ? `Signs in as ${(0, help_1.esc)(username)}` : '',
|
|
641
641
|
kind && kind !== 'windows' ? (0, help_1.esc)(kind) : '',
|
|
642
|
-
gateway && !hyperv ? `Through the gateway ${(0, help_1.esc)(gateway)}${
|
|
642
|
+
gateway && !hyperv ? `Through the gateway ${(0, help_1.esc)(gateway)}${gatewaySignIn(d.gatewayAccount)}` : '',
|
|
643
643
|
vm ? `Starts VM ${(0, help_1.esc)(vm)} when off` : mac ? `Wakes ${(0, help_1.esc)(mac)} when off` : '',
|
|
644
644
|
(0, clipboard_1.ownClipboard)(d.clipboard) ? `Clipboard: ${(0, help_1.esc)(clipboard_1.CLIPBOARD_LABELS[(0, clipboard_1.ownClipboard)(d.clipboard)].toLowerCase())}` : '',
|
|
645
645
|
].filter(Boolean).join('<br>');
|
|
@@ -648,9 +648,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
648
648
|
{ label: 'Remove…', danger: true, run: () => { this.desktop.confirmRemoveDesktop(i); } },
|
|
649
649
|
])];
|
|
650
650
|
});
|
|
651
|
-
list.replaceChildren(...profileRows, ...desktopRows,
|
|
652
|
-
? 'Profiles are also in Profiles & connections; a desktop behind a host also in that host\'s SSH tab: right-click › Settings.'
|
|
653
|
-
: 'None yet. <b>Add a desktop…</b> makes a profile: an RDP server on your network, or one behind an SSH profile\'s host.'));
|
|
651
|
+
list.replaceChildren(...profileRows, ...desktopRows, ...(profileRows.length + desktopRows.length ? [] : [this.empty('No saved desktops.')]));
|
|
654
652
|
}
|
|
655
653
|
renderFolders() {
|
|
656
654
|
const list = this.root.querySelector('[data-list=folders]');
|
|
@@ -672,7 +670,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
672
670
|
}));
|
|
673
671
|
row.insertBefore(check, row.querySelector('button'));
|
|
674
672
|
return row;
|
|
675
|
-
}), ...folders.length ? [] : [this.empty('
|
|
673
|
+
}), ...folders.length ? [] : [this.empty('No shared folders.')]);
|
|
676
674
|
}
|
|
677
675
|
/** Asks for a folder and shares it under its own name (numbered when another share has it). */
|
|
678
676
|
async shareFolder() {
|
|
@@ -682,7 +680,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
682
680
|
}
|
|
683
681
|
const folders = this.desktop.sharedFolders();
|
|
684
682
|
if (folders.some(f => f.path === picked)) {
|
|
685
|
-
this.injector.get(tabby_core_1.NotificationsService).info('
|
|
683
|
+
this.injector.get(tabby_core_1.NotificationsService).info('This folder is already shared.');
|
|
686
684
|
return;
|
|
687
685
|
}
|
|
688
686
|
this.desktop.setSharedFolders([...folders, { path: picked, name: (0, drives_1.driveName)((0, path_1.basename)(picked) || picked, folders), readOnly: false }]);
|
|
@@ -703,7 +701,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
703
701
|
const used = uses.length
|
|
704
702
|
? `Used by ${uses.map(u => u.profileId ? `<a data-profile="${(0, help_1.esc)(u.profileId)}">${(0, help_1.esc)(u.name)}</a>`
|
|
705
703
|
: u.desktopIndex !== undefined ? `<a data-desktop="${u.desktopIndex}">${(0, help_1.esc)(u.name)}</a>` : (0, help_1.esc)(u.name)).join(', ')}`
|
|
706
|
-
: 'Not used
|
|
704
|
+
: 'Not used';
|
|
707
705
|
const row = this.row((0, help_1.esc)(a.name), `<span>${(0, help_1.esc)((0, accounts_1.signInName)(a))}</span><br><span>${used}</span><span data-password></span>`, [
|
|
708
706
|
{ label: 'Edit…', run: () => this.editAccount(a) },
|
|
709
707
|
{ label: 'Remove…', danger: true, run: () => { this.desktop.confirmRemoveAccount(a.id); } },
|
|
@@ -714,11 +712,11 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
714
712
|
checks = checks.then(() => row.isConnected ? this.desktop.accountHasPassword(a.id) : 'unknown').then(has => {
|
|
715
713
|
const note = row.querySelector('[data-password]');
|
|
716
714
|
if (note && has === 'no') {
|
|
717
|
-
note.innerHTML = '<br>No password saved
|
|
715
|
+
note.innerHTML = '<br>No password saved';
|
|
718
716
|
}
|
|
719
717
|
}, () => null);
|
|
720
718
|
return row;
|
|
721
|
-
}), ...accounts.length ? [] : [this.empty('
|
|
719
|
+
}), ...accounts.length ? [] : [this.empty('No saved accounts.')]);
|
|
722
720
|
}
|
|
723
721
|
/** The form under the list, for a new account or `account`. */
|
|
724
722
|
editAccount(account) {
|
|
@@ -745,7 +743,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
745
743
|
field('name').value = account?.name ?? '';
|
|
746
744
|
field('username').value = account?.username ?? '';
|
|
747
745
|
field('domain').value = account?.domain ?? '';
|
|
748
|
-
field('password').placeholder = account ? 'Leave empty to keep the saved
|
|
746
|
+
field('password').placeholder = account ? 'Leave empty to keep the saved password' : 'Optional; requested when connecting';
|
|
749
747
|
form.querySelector('button[type=submit]').textContent = account ? 'Save' : 'Add';
|
|
750
748
|
const close = () => {
|
|
751
749
|
form.remove();
|
|
@@ -762,7 +760,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
762
760
|
const username = field('username').value.trim();
|
|
763
761
|
const name = field('name').value.trim();
|
|
764
762
|
if (!username) {
|
|
765
|
-
error.textContent = '
|
|
763
|
+
error.textContent = 'Enter a user name.';
|
|
766
764
|
field('username').focus();
|
|
767
765
|
return;
|
|
768
766
|
}
|
|
@@ -999,7 +997,7 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
999
997
|
]);
|
|
1000
998
|
}), this.empty(entries.length
|
|
1001
999
|
? 'A forgotten certificate is asked about again on the next connection, unless a certificate authority vouches for it; one behind an SSH host is remembered again without asking.'
|
|
1002
|
-
: '
|
|
1000
|
+
: 'No saved certificates.'));
|
|
1003
1001
|
}
|
|
1004
1002
|
renderSessions() {
|
|
1005
1003
|
const list = this.root.querySelector('[data-list=sessions]');
|
|
@@ -1009,23 +1007,19 @@ let RemoteDesktopSettingsComponent = class RemoteDesktopSettingsComponent {
|
|
|
1009
1007
|
const sessions = this.desktop.sessionSummaries();
|
|
1010
1008
|
list.replaceChildren(...sessions.map(s => this.row((0, help_1.esc)(s.name), `${s.where ? `via ${(0, help_1.esc)(s.where)} · ` : ''}${(0, help_1.esc)(s.state)}`, [
|
|
1011
1009
|
{ label: 'Copy log', run: () => this.copyLog(s.name, s.log) },
|
|
1012
|
-
])), ...sessions.length ? [] : [this.empty('No
|
|
1010
|
+
])), ...sessions.length ? [] : [this.empty('No desktops are open.')]);
|
|
1013
1011
|
}
|
|
1014
1012
|
copyLog(name, log) {
|
|
1015
|
-
const version = (()
|
|
1016
|
-
|
|
1017
|
-
return require('../package.json').version;
|
|
1018
|
-
}
|
|
1019
|
-
catch {
|
|
1020
|
-
return '?';
|
|
1021
|
-
}
|
|
1022
|
-
})();
|
|
1023
|
-
this.platform.setClipboard({ text: [`tabby-rdp ${version} on ${process.platform}: ${name}`, ...log].join('\n') });
|
|
1013
|
+
const version = (0, updates_1.installedVersion)();
|
|
1014
|
+
this.platform.setClipboard({ text: [`tabby-rdp ${version} on ${process.platform}: ${name}`, ...this.compat.diagnosticLines(), ...log].join('\n') });
|
|
1024
1015
|
}
|
|
1025
1016
|
showTab(id) {
|
|
1026
1017
|
lastTab = TABS.some(t => t.id === id) ? id : TABS[0].id;
|
|
1027
1018
|
this.root.querySelectorAll('[data-tab]').forEach(el => el.classList.toggle('active', el.dataset.tab === lastTab));
|
|
1028
1019
|
this.root.querySelectorAll('[data-body]').forEach(el => { el.hidden = el.dataset.body !== lastTab; });
|
|
1020
|
+
if (lastTab === 'updates') {
|
|
1021
|
+
this.injector.get(updates_1.UpdateCheck).refresh();
|
|
1022
|
+
}
|
|
1029
1023
|
}
|
|
1030
1024
|
/** Shows the tab with a section (and opens a troubleshooting entry), with a short highlight. */
|
|
1031
1025
|
reveal(topic, entry) {
|
|
@@ -1170,5 +1164,6 @@ exports.RemoteDesktopSettingsComponent = RemoteDesktopSettingsComponent = __deco
|
|
|
1170
1164
|
tabby_core_1.PlatformService,
|
|
1171
1165
|
core_1.Injector,
|
|
1172
1166
|
core_1.NgZone,
|
|
1173
|
-
tabby_core_1.HotkeysService
|
|
1167
|
+
tabby_core_1.HotkeysService,
|
|
1168
|
+
hostCompat_1.HostCompatibility])
|
|
1174
1169
|
], RemoteDesktopSettingsComponent);
|