fpasoterm 1.6.0 → 1.6.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.

Potentially problematic release.


This version of fpasoterm might be problematic. Click here for more details.

@@ -34,6 +34,7 @@ const closeAllConfirmElement = document.getElementById('close-all-confirm');
34
34
  const closeAllConfirmMessageElement = document.getElementById('close-all-confirm-message');
35
35
  const closeAllConfirmOkButton = document.getElementById('close-all-confirm-ok');
36
36
  const closeAllConfirmCancelButton = document.getElementById('close-all-confirm-cancel');
37
+ const closeAllConfirmUnmountButton = document.getElementById('close-all-confirm-unmount');
37
38
  const windowMenu = document.getElementById('window-menu');
38
39
  const windowMenuToggleButton = document.getElementById('window-menu-toggle');
39
40
  const windowMenuItems = document.getElementById('window-menu-items');
@@ -148,19 +149,18 @@ const fallbackConfig = {
148
149
  },
149
150
  keybindings: {
150
151
  prefix: 'Mod+Shift',
151
- logMenu: 'L',
152
- logToggle: 'S',
153
- logShow: 'P',
154
- copy: 'C',
155
- paste: 'V',
156
- menu: 'M',
157
- help: 'H',
158
- newWindow: 'N',
159
- openCwd: 'O',
160
- broadcast: 'B',
161
- kill: 'K',
162
- tile: 'T',
163
- closeAll: 'X',
152
+ logToggle: 's',
153
+ logShow: 'l',
154
+ copy: 'c',
155
+ paste: 'v',
156
+ menu: 'm',
157
+ help: 'h',
158
+ newWindow: 'n',
159
+ openCwd: 'o',
160
+ broadcast: 'b',
161
+ kill: 'k',
162
+ tile: 't',
163
+ closeAll: 'x',
164
164
  },
165
165
  plugins: {
166
166
  enabled: [],
@@ -352,7 +352,7 @@ function applyKeybindingLabels() {
352
352
  : '';
353
353
  }
354
354
  if (windowMenuToggleButton) {
355
- windowMenuToggleButton.setAttribute('aria-keyshortcuts', `${keybindingLabel('menu')} ${keybindingLabel('logMenu')}`);
355
+ windowMenuToggleButton.setAttribute('aria-keyshortcuts', keybindingLabel('menu'));
356
356
  }
357
357
  }
358
358
 
@@ -424,7 +424,7 @@ function installTauriApiAdapter() {
424
424
  newWindowAtCwd: (cwd, host) => invoke('window_new_at_cwd', { cwd, host: host || null }),
425
425
  arrangeWindows: (screen) => invoke('window_arrange', { screen }),
426
426
  closeAllWindows: () => invoke('window_close_all'),
427
- confirmCloseAllWindows: () => invoke('window_confirm_close_all'),
427
+ closeAllConfirmed: (unmountSshfs) => invoke('window_close_all_confirmed', { unmountSshfs }),
428
428
  startWindowDrag: () => invoke('window_start_drag'),
429
429
  startWindowResizeDrag: (direction) => window.__TAURI__.window.getCurrentWindow().startResizeDragging(direction),
430
430
  saveWindowBounds: () => invoke('window_save_bounds'),
@@ -2239,6 +2239,20 @@ function enableFloatingPanelDrag(panel, handle) {
2239
2239
  enableFloatingPanelDrag(diagnosticsPanel, diagnosticsPanel?.querySelector('[data-panel-drag-handle]'));
2240
2240
  enableFloatingPanelDrag(terminalBroadcastDialog, terminalBroadcastTitle);
2241
2241
 
2242
+ // WebView checkbox keyboard activation differs on Windows. Keep Space and Enter
2243
+ // consistent for Broadcast target controls and preserve their normal change event.
2244
+ function enableCheckboxKeyboardToggle(checkbox) {
2245
+ checkbox?.addEventListener('keydown', (event) => {
2246
+ if (event.isComposing || event.repeat || ![' ', 'Enter'].includes(event.key)) {
2247
+ return;
2248
+ }
2249
+ event.preventDefault();
2250
+ checkbox.click();
2251
+ });
2252
+ }
2253
+
2254
+ enableCheckboxKeyboardToggle(terminalBroadcastSync);
2255
+
2242
2256
  // Returns focusable controls in the visible Broadcast dialog, including its
2243
2257
  // dynamically rendered local-window target checkboxes.
2244
2258
  function terminalBroadcastFocusItems() {
@@ -2498,36 +2512,61 @@ function confirmTerminalLogAction(message, returnFocus) {
2498
2512
  });
2499
2513
  }
2500
2514
 
2501
- // Shows a separate confirmation dialog before broadcasting a close-all request.
2502
- function confirmCloseAllWindows() {
2515
+ // Keeps Close All confirmation inside the invoking window on every platform.
2516
+ // This gives Windows, macOS, and Linux the same focus and keyboard behavior.
2517
+ function focusCloseAllConfirmation() {
2518
+ if (closeAllConfirmElement.hidden) return;
2519
+ closeAllConfirmCancelButton.focus({ preventScroll: true });
2520
+ }
2521
+
2522
+ // WebViews can restore xterm focus while an overlay is first painted. Retry
2523
+ // briefly so the initial keyboard target is consistently the Cancel button.
2524
+ function scheduleCloseAllConfirmationFocus() {
2525
+ requestAnimationFrame(focusCloseAllConfirmation);
2526
+ for (const delay of [0, 50, 120, 240]) {
2527
+ setTimeout(focusCloseAllConfirmation, delay);
2528
+ }
2529
+ }
2530
+
2531
+ async function confirmCloseAllWindows() {
2503
2532
  if (!closeAllConfirmElement || !closeAllConfirmMessageElement || !closeAllConfirmOkButton) {
2504
2533
  showDiagnostic('close all confirmation UI is unavailable');
2505
- return Promise.resolve(false);
2534
+ return { confirmed: false, unmountSshfs: false };
2506
2535
  }
2507
- closeAllConfirmMessageElement.textContent =
2508
- 'Close all running fpasoterm windows?\n\nThis will terminate every fpasoterm window.';
2536
+ let status = null;
2537
+ if (typeof window.fpasoterm.getSshfsStatus === 'function') {
2538
+ status = await window.fpasoterm.getSshfsStatus().catch(() => null);
2539
+ }
2540
+ const mountCount = Array.isArray(status?.mounts) ? status.mounts.length : 0;
2541
+ closeAllConfirmMessageElement.textContent = mountCount
2542
+ ? `Close all running fpasoterm windows?\n\n${mountCount} SSHFS mount(s) will remain active unless you choose Unmount & Close.`
2543
+ : 'Close all running fpasoterm windows?\n\nThis will terminate every fpasoterm window.';
2544
+ closeAllConfirmUnmountButton.hidden = mountCount === 0;
2509
2545
  closeAllConfirmElement.hidden = false;
2510
- closeAllConfirmOkButton.focus({ preventScroll: true });
2546
+ scheduleCloseAllConfirmationFocus();
2511
2547
  return new Promise((resolve) => {
2512
2548
  closeAllConfirmResolver = resolve;
2513
2549
  });
2514
2550
  }
2515
2551
 
2516
2552
  // Resolves and hides the close-all confirmation dialog.
2517
- function resolveCloseAllWindows(confirmed) {
2553
+ function resolveCloseAllWindows(confirmed, unmountSshfs = false) {
2518
2554
  if (!closeAllConfirmElement || closeAllConfirmElement.hidden) {
2519
2555
  return;
2520
2556
  }
2521
2557
  closeAllConfirmElement.hidden = true;
2558
+ closeAllConfirmUnmountButton.hidden = true;
2522
2559
  const resolver = closeAllConfirmResolver;
2523
2560
  closeAllConfirmResolver = null;
2524
2561
  closeAllWindowsButton.focus({ preventScroll: true });
2525
- resolver?.(confirmed);
2562
+ resolver?.({ confirmed, unmountSshfs });
2526
2563
  }
2527
2564
 
2528
2565
  // Asks for confirmation before closing all application instances.
2529
- function requestCloseAllWindows() {
2530
- window.fpasoterm.confirmCloseAllWindows?.().catch((error) => {
2566
+ async function requestCloseAllWindows() {
2567
+ const result = await confirmCloseAllWindows();
2568
+ if (!result.confirmed) return;
2569
+ window.fpasoterm.closeAllConfirmed(result.unmountSshfs).catch((error) => {
2531
2570
  showDiagnostic(`close all confirmation failed: ${error}`);
2532
2571
  });
2533
2572
  }
@@ -2548,7 +2587,7 @@ function showTerminalOutputLogFromMenu() {
2548
2587
  showTerminalOutputLog().catch((error) => {
2549
2588
  terminalLogShowButton.textContent = 'Error';
2550
2589
  setTimeout(() => {
2551
- terminalLogShowButton.textContent = 'Log Show (^P)';
2590
+ terminalLogShowButton.textContent = 'Log Show (^l)';
2552
2591
  }, 1400);
2553
2592
  showDiagnostic(`terminal log show failed: ${error}`);
2554
2593
  }).finally(() => setWindowMenuOpen(false));
@@ -2565,6 +2604,7 @@ function renderTerminalBroadcastTargets(targets) {
2565
2604
  checkbox.checked = true;
2566
2605
  checkbox.dataset.instanceId = target.id;
2567
2606
  checkbox.addEventListener('change', refreshTerminalBroadcastSyncOption);
2607
+ enableCheckboxKeyboardToggle(checkbox);
2568
2608
  label.append(checkbox, document.createTextNode(`${target.title} (pid ${target.pid})`));
2569
2609
  terminalBroadcastTargetList.append(label);
2570
2610
  }
@@ -2637,12 +2677,29 @@ function decodeTerminalBroadcastControls(text) {
2637
2677
  '\\x0d': '\r',
2638
2678
  '\\x1b': '\x1b',
2639
2679
  '\\x09': '\t',
2680
+ '\\x01': '\x01',
2681
+ '\\x02': '\x02',
2640
2682
  '\\x03': '\x03',
2641
2683
  '\\x04': '\x04',
2642
2684
  '\\x18': '\x18',
2643
2685
  '\\x1a': '\x1a',
2644
2686
  };
2645
- return String(text || '').replace(/\\x(?:0d|1b|09|03|04|18|1a)/gi, (match) => controls[match.toLowerCase()] || match);
2687
+ return String(text || '').replace(/\\x(?:0d|1b|09|01|02|03|04|18|1a)/gi, (match) => controls[match.toLowerCase()] || match);
2688
+ }
2689
+
2690
+ // Extracts one trailing semantic key marker. Keeping the marker visible in the
2691
+ // dialog lets a control prefix such as Ctrl+B be inspected before it is sent.
2692
+ function extractTerminalBroadcastKeyEvent(text) {
2693
+ const value = String(text || '');
2694
+ const match = value.match(/\\key\[(ArrowUp|ArrowDown|ArrowLeft|ArrowRight)\]$/i);
2695
+ if (!match) {
2696
+ return { text: value, keyEvent: null };
2697
+ }
2698
+ const key = match[1];
2699
+ return {
2700
+ text: value.slice(0, -match[0].length),
2701
+ keyEvent: { key, code: key, shiftKey: false },
2702
+ };
2646
2703
  }
2647
2704
 
2648
2705
  // Uses xterm's active keyboard encoder instead of guessing a byte sequence.
@@ -2665,11 +2722,19 @@ function dispatchTerminalBroadcastKey(keyEvent) {
2665
2722
  showDiagnostic('terminal broadcast key event is unavailable');
2666
2723
  return;
2667
2724
  }
2725
+ const key = String(keyEvent.key || '');
2726
+ const keyCodes = {
2727
+ Enter: 13,
2728
+ ArrowUp: 38,
2729
+ ArrowDown: 40,
2730
+ ArrowLeft: 37,
2731
+ ArrowRight: 39,
2732
+ };
2668
2733
  const event = {
2669
2734
  type: 'keydown',
2670
- key: keyEvent.key === 'Enter' ? 'Enter' : String(keyEvent.key || ''),
2671
- code: keyEvent.code === 'Enter' ? 'Enter' : String(keyEvent.code || ''),
2672
- keyCode: keyEvent.key === 'Enter' ? 13 : 0,
2735
+ key,
2736
+ code: String(keyEvent.code || key),
2737
+ keyCode: keyCodes[key] || 0,
2673
2738
  shiftKey: keyEvent.shiftKey === true,
2674
2739
  altKey: false,
2675
2740
  ctrlKey: false,
@@ -2689,10 +2754,16 @@ function insertTerminalBroadcastControl() {
2689
2754
  const controls = {
2690
2755
  enter: { text: '\\x0D', label: 'Enter / CR (0x0D)' },
2691
2756
  tab: { text: '\\x09', label: 'Tab (0x09)' },
2757
+ 'ctrl-a': { text: '\\x01', label: 'Ctrl+A (0x01)' },
2758
+ 'ctrl-b': { text: '\\x02', label: 'Ctrl+B (0x02)' },
2692
2759
  'ctrl-c': { text: '\\x03', label: 'Ctrl+C (0x03)' },
2693
2760
  'ctrl-d': { text: '\\x04', label: 'Ctrl+D (0x04)' },
2694
2761
  'ctrl-x': { text: '\\x18', label: 'Ctrl+X (0x18)' },
2695
2762
  'ctrl-z': { text: '\\x1A', label: 'Ctrl+Z (0x1A)' },
2763
+ 'arrow-up': { text: '\\key[ArrowUp]', label: 'Arrow Up' },
2764
+ 'arrow-down': { text: '\\key[ArrowDown]', label: 'Arrow Down' },
2765
+ 'arrow-left': { text: '\\key[ArrowLeft]', label: 'Arrow Left' },
2766
+ 'arrow-right': { text: '\\key[ArrowRight]', label: 'Arrow Right' },
2696
2767
  'alt-prefix': { text: '\\x1B', label: 'Alt prefix / Esc (0x1B)' },
2697
2768
  };
2698
2769
  const selected = controls[control];
@@ -2764,12 +2835,12 @@ function resolveDangerousBroadcastConfirmation(confirmed) {
2764
2835
  // Writes the dialog text to local windows and optionally the configured sync channel.
2765
2836
  async function sendTerminalBroadcast() {
2766
2837
  const rawText = String(terminalBroadcastText?.value || '');
2767
- const normalizedText = decodeTerminalBroadcastControls(normalizePasteText(rawText).replace(/\r+$/, ''));
2768
- const text = normalizedText;
2769
- const keyEvent = shouldAppendTerminalBroadcastEnter(text)
2838
+ const decodedText = decodeTerminalBroadcastControls(normalizePasteText(rawText).replace(/\r+$/, ''));
2839
+ const { text, keyEvent: selectedKeyEvent } = extractTerminalBroadcastKeyEvent(decodedText);
2840
+ const keyEvent = selectedKeyEvent || (shouldAppendTerminalBroadcastEnter(text)
2770
2841
  ? terminalBroadcastSubmitKeyEvent()
2771
- : null;
2772
- if (!text) {
2842
+ : null);
2843
+ if (!text && !keyEvent) {
2773
2844
  showDiagnostic('terminal broadcast skipped: input is empty');
2774
2845
  terminalBroadcastText?.focus({ preventScroll: true });
2775
2846
  return;
@@ -2948,7 +3019,6 @@ async function showKeyboardShortcutsHelp() {
2948
3019
  `fpasoterm ${version}`,
2949
3020
  `Config: ${activeConfigPath || 'unknown'}`,
2950
3021
  '',
2951
- `${keybindingLabel('logMenu')} Open the window menu at Log actions`,
2952
3022
  `${keybindingLabel('logToggle')} Start or stop terminal output logging`,
2953
3023
  `${keybindingLabel('logShow')} Show terminal output logs`,
2954
3024
  `${keybindingLabel('copy')} Copy selected terminal or log text`,
@@ -3265,13 +3335,37 @@ closeAllConfirmCancelButton.addEventListener('click', () => {
3265
3335
  resolveCloseAllWindows(false);
3266
3336
  });
3267
3337
 
3338
+ closeAllConfirmUnmountButton.addEventListener('click', () => {
3339
+ resolveCloseAllWindows(true, true);
3340
+ });
3341
+
3268
3342
  closeAllConfirmElement.addEventListener('keydown', (event) => {
3269
3343
  if (event.key === 'Escape') {
3270
3344
  event.preventDefault();
3271
3345
  resolveCloseAllWindows(false);
3346
+ return;
3347
+ }
3348
+ if (event.key === 'Tab') {
3349
+ event.preventDefault();
3350
+ const buttons = [
3351
+ closeAllConfirmCancelButton,
3352
+ ...(!closeAllConfirmUnmountButton.hidden ? [closeAllConfirmUnmountButton] : []),
3353
+ closeAllConfirmOkButton,
3354
+ ];
3355
+ const index = buttons.indexOf(document.activeElement);
3356
+ const next = (index + (event.shiftKey ? buttons.length - 1 : 1) + buttons.length) % buttons.length;
3357
+ buttons[next].focus({ preventScroll: true });
3272
3358
  }
3273
3359
  });
3274
3360
 
3361
+ closeAllConfirmElement.addEventListener('focusout', () => {
3362
+ requestAnimationFrame(() => {
3363
+ if (!closeAllConfirmElement.hidden && !closeAllConfirmElement.contains(document.activeElement)) {
3364
+ focusCloseAllConfirmation();
3365
+ }
3366
+ });
3367
+ });
3368
+
3275
3369
  terminalCopyButton.addEventListener('click', () => {
3276
3370
  copyTerminalSelection().catch((error) => {
3277
3371
  showDiagnostic(`terminal menu copy failed: ${error}`);
@@ -3434,16 +3528,6 @@ document.addEventListener('keydown', (event) => {
3434
3528
  return;
3435
3529
  }
3436
3530
 
3437
- if (matchesKeybinding(event, 'logMenu')) {
3438
- event.preventDefault();
3439
- const open = windowMenuItems?.hidden !== false;
3440
- setWindowMenuOpen(open, terminalLogToggleButton);
3441
- if (open) {
3442
- setWindowMenuSubmenuOpen('log', true, terminalLogToggleButton);
3443
- }
3444
- return;
3445
- }
3446
-
3447
3531
  if (matchesKeybinding(event, 'menu')) {
3448
3532
  event.preventDefault();
3449
3533
  setWindowMenuOpen(windowMenuItems?.hidden !== false);
@@ -3565,9 +3649,12 @@ async function requestWindowClose() {
3565
3649
  function confirmSshfsWindowClose(mounts) {
3566
3650
  closeAllConfirmMessageElement.textContent =
3567
3651
  `Keep ${mounts.length} SSHFS mount(s) active after closing this terminal window?\n\nUse Window > SSHFS Mounts to unmount them later.`;
3652
+ closeAllConfirmUnmountButton.hidden = true;
3568
3653
  closeAllConfirmElement.hidden = false;
3569
- closeAllConfirmOkButton.focus({ preventScroll: true });
3570
- return new Promise((resolve) => { closeAllConfirmResolver = resolve; });
3654
+ requestAnimationFrame(() => closeAllConfirmOkButton.focus({ preventScroll: true }));
3655
+ return new Promise((resolve) => {
3656
+ closeAllConfirmResolver = (result) => resolve(result?.confirmed === true);
3657
+ });
3571
3658
  }
3572
3659
 
3573
3660
  // Closes the frameless window from the custom titlebar.
@@ -3670,6 +3757,26 @@ function selectedSshfsMountName() {
3670
3757
  return sshfsDialogMounts.some((mount) => mount.mountName === mountName) ? mountName : '';
3671
3758
  }
3672
3759
 
3760
+ // Keeps keyboard navigation inside the SSHFS modal instead of falling through
3761
+ // to titlebar controls or xterm.js on WebViews that do not enforce aria-modal.
3762
+ function sshfsManagerFocusItems() {
3763
+ if (!sshfsManagerDialog || sshfsManagerDialog.hidden) {
3764
+ return [];
3765
+ }
3766
+ return [...sshfsManagerDialog.querySelectorAll('input, select, button, a[href]')]
3767
+ .filter((element) => !element.hidden && !element.disabled && element.getClientRects().length > 0);
3768
+ }
3769
+
3770
+ function focusSshfsManagerItem(delta) {
3771
+ const items = sshfsManagerFocusItems();
3772
+ if (items.length === 0) {
3773
+ return;
3774
+ }
3775
+ const index = items.indexOf(document.activeElement);
3776
+ const next = index < 0 ? (delta < 0 ? items.length - 1 : 0) : (index + delta + items.length) % items.length;
3777
+ items[next].focus({ preventScroll: true });
3778
+ }
3779
+
3673
3780
  async function refreshSshfsDialog(showResult = true) {
3674
3781
  const status = await window.fpasoterm.getSshfsStatus();
3675
3782
  const selected = sshfsManagerSavedMounts.value;
@@ -3777,7 +3884,24 @@ sshfsManagerForgetButton.addEventListener('click', async () => {
3777
3884
  });
3778
3885
  sshfsManagerCloseButton.addEventListener('click', closeSshfsManagerModal);
3779
3886
  sshfsManagerDialog.addEventListener('keydown', (event) => {
3780
- if (event.key === 'Escape') { event.preventDefault(); closeSshfsManagerModal(); }
3887
+ if (event.key === 'Escape') {
3888
+ event.preventDefault();
3889
+ event.stopImmediatePropagation();
3890
+ closeSshfsManagerModal();
3891
+ return;
3892
+ }
3893
+ if (event.key === 'Tab') {
3894
+ event.preventDefault();
3895
+ event.stopImmediatePropagation();
3896
+ focusSshfsManagerItem(event.shiftKey ? -1 : 1);
3897
+ }
3898
+ });
3899
+ sshfsManagerDialog.addEventListener('focusout', () => {
3900
+ requestAnimationFrame(() => {
3901
+ if (!sshfsManagerDialog.hidden && !sshfsManagerDialog.contains(document.activeElement)) {
3902
+ focusSshfsManagerItem(1);
3903
+ }
3904
+ });
3781
3905
  });
3782
3906
 
3783
3907
  // Opens the shared SSHFS manager without creating another native WebView window.
@@ -368,6 +368,25 @@ body {
368
368
  color: #f5d76e;
369
369
  }
370
370
 
371
+ #terminal-broadcast-examples {
372
+ margin-top: 8px;
373
+ color: #c7d1dc;
374
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
375
+ }
376
+
377
+ #terminal-broadcast-examples summary {
378
+ cursor: pointer;
379
+ color: #f5d76e;
380
+ }
381
+
382
+ #terminal-broadcast-examples p {
383
+ margin: 6px 0 0;
384
+ }
385
+
386
+ #terminal-broadcast-examples code {
387
+ color: #e8edf2;
388
+ }
389
+
371
390
  #terminal-broadcast-sync-label {
372
391
  display: block;
373
392
  margin: 10px 0;
@@ -683,6 +702,19 @@ body {
683
702
  display: none;
684
703
  }
685
704
 
705
+ /* Keep Close All above the terminal in the current window. This avoids
706
+ * relying on compositor-controlled focus of a separate child window. */
707
+ #close-all-confirm {
708
+ position: fixed;
709
+ top: var(--titlebar-height);
710
+ right: 0;
711
+ bottom: 0;
712
+ left: 0;
713
+ z-index: 10060;
714
+ app-region: no-drag;
715
+ -webkit-app-region: no-drag;
716
+ }
717
+
686
718
  #sshfs-manager-dialog[hidden] {
687
719
  display: none;
688
720
  }
@@ -766,8 +798,8 @@ body {
766
798
  text-decoration: none;
767
799
  }
768
800
 
769
- .sshfs-manager-card :is(input, select, button):focus,
770
- .sshfs-manager-card :is(input, select, button):focus-visible {
801
+ .sshfs-manager-card :is(input, select, button, a[href]):focus,
802
+ .sshfs-manager-card :is(input, select, button, a[href]):focus-visible {
771
803
  outline: 3px solid #f5d76e;
772
804
  outline-offset: 2px;
773
805
  border-color: #f5d76e;
@@ -880,6 +912,9 @@ body {
880
912
 
881
913
  #terminal-log-confirm-ok,
882
914
  #terminal-log-confirm-cancel,
915
+ #close-all-confirm-ok,
916
+ #close-all-confirm-cancel,
917
+ #close-all-confirm-unmount,
883
918
  #terminal-broadcast-confirm-ok,
884
919
  #terminal-broadcast-confirm-cancel,
885
920
  #terminal-url-copy,
@@ -894,6 +929,12 @@ body {
894
929
  font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
895
930
  }
896
931
 
932
+ #close-all-confirm button:focus,
933
+ #close-all-confirm button:focus-visible {
934
+ outline: 3px solid #f5d76e;
935
+ outline-offset: 2px;
936
+ }
937
+
897
938
  #diagnostics-path {
898
939
  overflow: hidden;
899
940
  padding: 4px 8px;
@@ -1058,7 +1058,7 @@ dependencies = [
1058
1058
 
1059
1059
  [[package]]
1060
1060
  name = "fpasoterm"
1061
- version = "1.6.0"
1061
+ version = "1.6.1"
1062
1062
  dependencies = [
1063
1063
  "base64 0.22.1",
1064
1064
  "encoding_rs",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fpasoterm"
3
- version = "1.6.0"
3
+ version = "1.6.1"
4
4
  description = "Cross-platform terminal with Japanese IME support."
5
5
  authors = ["oyoguhito"]
6
6
  edition = "2021"
@@ -65,23 +65,22 @@ repeatedTextWindowMs = 140
65
65
 
66
66
  # Mod means Ctrl on Windows/Linux and Cmd on macOS. Set prefix = "Ctrl+Alt"
67
67
  # when Ctrl+Shift is reserved by a Windows application or keyboard layout.
68
- # An individual value may be a key such as "N" or a full shortcut such as
68
+ # An individual value may be a key such as "n" or a full shortcut such as
69
69
  # "Ctrl+Alt+KeyN". KeyN-style values bind by physical keyboard position.
70
70
  [keybindings]
71
71
  prefix = "Mod+Shift"
72
- logMenu = "L"
73
- logToggle = "S"
74
- logShow = "P"
75
- copy = "C"
76
- paste = "V"
77
- menu = "M"
78
- help = "H"
79
- newWindow = "N"
72
+ logToggle = "s"
73
+ logShow = "l"
74
+ copy = "c"
75
+ paste = "v"
76
+ menu = "m"
77
+ help = "h"
78
+ newWindow = "n"
80
79
  openCwd = "o"
81
- broadcast = "B"
82
- kill = "K"
83
- tile = "T"
84
- closeAll = "X"
80
+ broadcast = "b"
81
+ kill = "k"
82
+ tile = "t"
83
+ closeAll = "x"
85
84
 
86
85
  [plugins]
87
86
  enabled = []