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.

@@ -10,6 +10,7 @@ const {
10
10
  defaultConfigExample,
11
11
  discoverPluginFiles,
12
12
  loadConfig,
13
+ migrateLegacyLogKeybindings,
13
14
  migrateLegacyTerminalLineHeight,
14
15
  mergeConfig,
15
16
  missingConfigKeys,
@@ -47,6 +48,29 @@ assert.equal(platformDefaultConfig('linux', 'x64').security.osc8Open, false);
47
48
  assert.equal(platformDefaultConfig('linux', 'x64').security.oscNotifications, false);
48
49
  assert.equal(platformDefaultConfig('linux', 'x64').security.oscNotificationMinIntervalMs, 5000);
49
50
  assert.equal(platformDefaultConfig('linux', 'x64').keybindings.openCwd, 'o');
51
+ assert.equal(platformDefaultConfig('linux', 'x64').keybindings.logToggle, 's');
52
+ assert.equal(platformDefaultConfig('linux', 'x64').keybindings.logShow, 'l');
53
+ assert.equal(Object.hasOwn(platformDefaultConfig('linux', 'x64').keybindings, 'logMenu'), false);
54
+ assert.deepEqual(
55
+ migrateLegacyLogKeybindings({ keybindings: { logMenu: 'L', logShow: 'P' } }).keybindings,
56
+ { logShow: 'l' },
57
+ );
58
+ assert.deepEqual(
59
+ migrateLegacyLogKeybindings({ keybindings: { logShow: 'P' } }).keybindings,
60
+ { logShow: 'l' },
61
+ );
62
+ assert.deepEqual(
63
+ migrateLegacyLogKeybindings({ keybindings: { logToggle: 'S' } }).keybindings,
64
+ { logToggle: 's' },
65
+ );
66
+ assert.deepEqual(
67
+ migrateLegacyLogKeybindings({ keybindings: { copy: 'C', paste: 'V', menu: 'M', help: 'H', newWindow: 'N', broadcast: 'B', kill: 'K', tile: 'T', closeAll: 'X' } }).keybindings,
68
+ { copy: 'c', paste: 'v', menu: 'm', help: 'h', newWindow: 'n', broadcast: 'b', kill: 'k', tile: 't', closeAll: 'x' },
69
+ );
70
+ assert.deepEqual(
71
+ migrateLegacyLogKeybindings({ keybindings: { logShow: 'Ctrl+Shift+P' } }).keybindings,
72
+ { logShow: 'Ctrl+Shift+P' },
73
+ );
50
74
  assert.match(platformDefaultConfig('darwin', 'arm64').terminal.fontFamily, /^Menlo, "SF Mono"/);
51
75
  assert.match(platformDefaultConfig('win32', 'x64').terminal.fontFamily, /^"DejaVu Sans Mono"/);
52
76
  assert.match(platformDefaultConfig('win32', 'x64').terminal.fontFamily, /"Noto Sans CJK KR"/);
@@ -362,7 +386,7 @@ const migratedConfig = mergeConfig(writableConfigDefaults(), {
362
386
  keybindings: { prefix: 'Ctrl+Alt' },
363
387
  });
364
388
  assert.equal(migratedConfig.keybindings.prefix, 'Ctrl+Alt');
365
- assert.equal(migratedConfig.keybindings.newWindow, 'N');
389
+ assert.equal(migratedConfig.keybindings.newWindow, 'n');
366
390
  assert.equal(migratedConfig.terminal.images, undefined);
367
391
  const prunedConfig = pruneUnsupportedConfig(writableConfigDefaults(), {
368
392
  ...migratedConfig,
@@ -639,7 +663,7 @@ assert.equal(stateConfig.config.window.width, 1200);
639
663
  assert.equal(stateConfig.config.window.height, 900);
640
664
  assert.equal(stateConfig.config.terminal.shell, '');
641
665
  assert.equal(stateConfig.config.keybindings.prefix, 'Ctrl+Alt');
642
- assert.equal(stateConfig.config.keybindings.newWindow, 'N');
666
+ assert.equal(stateConfig.config.keybindings.newWindow, 'n');
643
667
  assert.ok(fs.existsSync(path.join(stateTestDir, 'fpasoterm', 'User', 'plugins')));
644
668
  fs.writeFileSync(path.join(stateTestDir, 'fpasoterm', 'User', 'config.toml'), [
645
669
  '[terminal]',
@@ -969,22 +993,13 @@ assert.match(rustMain, /fn window_arrange/);
969
993
  assert.match(rustMain, /fn window_close_all/);
970
994
  assert.match(rustMain, /fn window_new/);
971
995
  assert.match(rustMain, /spawn_new_instance/);
972
- assert.match(rustMain, /fn window_confirm_close_all/);
973
996
  assert.match(rustMain, /fn window_close_all_confirmed/);
974
- assert.match(rustMain, /fn windows_confirm_close_all/);
975
997
  assert.match(rustMain, /MessageBoxW/);
976
998
  assert.match(rustMain, /MB_OKCANCEL/);
977
- assert.match(rustMain, /WebviewWindowBuilder/);
999
+ assert.doesNotMatch(rustMain, /"close-all-confirm"/);
978
1000
  assert.match(rustMain, /OpenProcess/);
979
1001
  assert.match(rustMain, /GetExitCodeProcess/);
980
1002
  assert.match(rustMain, /PROCESS_QUERY_LIMITED_INFORMATION/);
981
- assert.match(rustMain, /\.decorations\(true\)/);
982
- assert.match(rustMain, /\.transparent\(false\)/);
983
- assert.match(rustMain, /\.visible\(true\)/);
984
- assert.match(rustMain, /\.always_on_top\(true\)/);
985
- assert.match(rustMain, /window\.set_focus\(\)/);
986
- assert.match(rustMain, /fn window_focus_main/);
987
- assert.match(rustMain, /fn window_cancel_close_all/);
988
1003
  assert.match(rustMain, /Ignore a close request left by an earlier application session/);
989
1004
  assert.match(rustMain, /close_all_request_path/);
990
1005
  assert.match(rustMain, /start_arrange_listener/);
@@ -1130,7 +1145,14 @@ const indexHtml = read('src/renderer/index.html');
1130
1145
  assert.match(indexHtml, /id="terminal-broadcast-target-list"/);
1131
1146
  assert.match(indexHtml, /id="terminal-broadcast-control"/);
1132
1147
  assert.match(indexHtml, /id="terminal-broadcast-control-insert"/);
1148
+ assert.match(indexHtml, /id="terminal-broadcast-examples"/);
1149
+ assert.match(indexHtml, /clear\\x0D\\x01jclear/);
1150
+ assert.match(indexHtml, /\\x02\\key\[ArrowUp\]/);
1133
1151
  assert.match(indexHtml, /Enter \/ CR \(0x0D\)/);
1152
+ assert.match(indexHtml, /Ctrl\+A \(0x01\)/);
1153
+ assert.match(indexHtml, /Ctrl\+B \(0x02\)/);
1154
+ assert.match(indexHtml, /Arrow Up/);
1155
+ assert.match(indexHtml, /Arrow Down/);
1134
1156
  assert.match(indexHtml, /id="terminal-broadcast-hint"/);
1135
1157
  assert.match(indexHtml, /<strong>Send: Shift\+Enter<\/strong>\. New line: Enter\./);
1136
1158
  assert.match(indexHtml, /id="terminal-broadcast-focus-status"/);
@@ -1151,7 +1173,6 @@ assert.match(indexHtml, /id="terminal-capability-preview"/);
1151
1173
  assert.match(indexHtml, /data-panel-drag-handle/);
1152
1174
  assert.match(indexHtml, /Select All/);
1153
1175
  assert.match(indexHtml, /Select None/);
1154
- const confirmHtml = read('src/renderer/confirm.html');
1155
1176
  assert.match(indexHtml, /id="drag-region"/);
1156
1177
  assert.match(indexHtml, /id="window-title"/);
1157
1178
  assert.match(indexHtml, /id="window-controls"/);
@@ -1228,7 +1249,7 @@ assert.match(indexHtml, /id="keybinding-prefix"/);
1228
1249
  assert.match(indexHtml, /Shortcut prefix: Ctrl\+Shift/);
1229
1250
  assert.match(indexHtml, /aria-keyshortcuts="[^"]*Control\+Shift\+L[^"]*"/);
1230
1251
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+S"/);
1231
- assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+P"/);
1252
+ assert.doesNotMatch(indexHtml, /aria-keyshortcuts="Control\+Shift\+P"/);
1232
1253
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+C"/);
1233
1254
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+V"/);
1234
1255
  assert.doesNotMatch(indexHtml, /id="terminal-log-clear"/);
@@ -1245,37 +1266,30 @@ assert.match(indexHtml, /id="terminal-log-confirm"/);
1245
1266
  assert.match(indexHtml, /role="dialog"/);
1246
1267
  assert.match(indexHtml, /id="terminal-log-confirm-ok"/);
1247
1268
  assert.match(indexHtml, /id="terminal-log-confirm-cancel"/);
1248
- assert.doesNotMatch(indexHtml, />Log \(\^L\)</);
1249
- assert.match(indexHtml, />Log Start \(S\)</);
1250
- assert.match(indexHtml, />Log Show \(P\)</);
1251
- assert.match(indexHtml, />Copy \(C\)</);
1252
- assert.match(indexHtml, />Paste \(V\)</);
1269
+ assert.match(indexHtml, />Log Start \(s\)</);
1270
+ assert.match(indexHtml, />Log Show \(l\)</);
1271
+ assert.match(indexHtml, />Copy \(c\)</);
1272
+ assert.match(indexHtml, />Paste \(v\)</);
1253
1273
  assert.doesNotMatch(indexHtml, />Clear</);
1254
1274
  assert.match(indexHtml, /id="minimize-window"/);
1255
1275
  assert.match(indexHtml, /id="maximize-window"/);
1256
1276
  assert.match(indexHtml, /id="new-window"/);
1257
- assert.match(indexHtml, />New \(N\)</);
1277
+ assert.match(indexHtml, />New \(n\)</);
1258
1278
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+N"/);
1259
1279
  assert.match(indexHtml, /id="arrange-window"/);
1260
1280
  assert.match(indexHtml, /id="close-all-windows"/);
1261
- assert.match(indexHtml, />Close All \(X\)</);
1281
+ assert.match(indexHtml, />Close All \(x\)</);
1262
1282
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+X"/);
1263
1283
  assert.match(indexHtml, /id="close-all-confirm" hidden role="dialog"/);
1264
1284
  assert.match(indexHtml, /id="close-all-confirm-ok"/);
1265
1285
  assert.match(indexHtml, /id="close-all-confirm-cancel"/);
1266
- assert.match(confirmHtml, /Close all fpasoterm windows/);
1267
- assert.match(confirmHtml, /window_close_all_confirmed/);
1268
- assert.match(confirmHtml, /event\.key === 'Tab'/);
1269
- assert.match(confirmHtml, /event\.key === 'Enter'/);
1270
- assert.match(confirmHtml, /currentWindow\.setFocus/);
1271
- assert.match(confirmHtml, /cancel\.focus/);
1272
- assert.match(confirmHtml, /window_cancel_close_all/);
1273
- assert.match(indexHtml, />Tile \(T\)</);
1286
+ assert.match(indexHtml, /id="close-all-confirm-unmount"/);
1287
+ assert.match(indexHtml, />Tile \(t\)</);
1274
1288
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+T"/);
1275
1289
  assert.match(indexHtml, /id="window-menu-toggle"/);
1276
- assert.match(indexHtml, /id="window-menu-toggle"[^>]+aria-keyshortcuts="Control\+Shift\+M Control\+Shift\+L"/);
1290
+ assert.match(indexHtml, /id="window-menu-toggle"[^>]+aria-keyshortcuts="Control\+Shift\+M"/);
1277
1291
  assert.match(indexHtml, /id="keyboard-shortcuts-help"/);
1278
- assert.match(indexHtml, />Help \(H\)</);
1292
+ assert.match(indexHtml, />Help \(h\)</);
1279
1293
  assert.match(indexHtml, /aria-keyshortcuts="Control\+Shift\+H"/);
1280
1294
  assert.match(indexHtml, /id="window-menu-items"/);
1281
1295
  assert.match(indexHtml, /id="plugin-menu-section"/);
@@ -1319,7 +1333,7 @@ assert.match(readme, /PowerShell\\7\\pwsh\.exe/);
1319
1333
  assert.match(readme, /PowerShell 7 \(`pwsh\.exe`\) by default/);
1320
1334
  assert.match(readme, /fpasoterm executable directory at the[\s\S]*front of `Path`/);
1321
1335
  assert.match(readme, /GTK application id is disabled/);
1322
- assert.match(readme, /Tile \(\^T\)/);
1336
+ assert.match(readme, /Tile \(\^t\)/);
1323
1337
  assert.match(readme, /ChromeOS shelf/);
1324
1338
  assert.match(readme, /removes fpasoterm-specific directories from the[\s\S]*current user's `Path`/);
1325
1339
  assert.match(readme, /By default, fpasoterm keeps its configured title/);
@@ -1416,9 +1430,9 @@ assert.match(configDocsEn, /\[sync\]/);
1416
1430
  assert.match(configDocsEn, /provider = "folder"/);
1417
1431
  assert.match(configDocsEn, /sync\.en\.md/);
1418
1432
  assert.match(configDocsEn, /\[logging\]/);
1419
- assert.match(configDocsEn, /Ctrl\+Shift\+L/);
1420
- assert.match(configDocsEn, /Ctrl\+Shift\+S/);
1421
- assert.match(configDocsEn, /Ctrl\+Shift\+P/);
1433
+ assert.match(configDocsEn, /Ctrl\+Shift\+l/);
1434
+ assert.match(configDocsEn, /Ctrl\+Shift\+s/);
1435
+ assert.match(configDocsEn, /Ctrl\+Shift\+p/);
1422
1436
  assert.match(configDocsEn, /Ctrl\+Esc.*invalid/);
1423
1437
  assert.match(configDocsEn, /Ctrl\+Alt\+Escape/);
1424
1438
  assert.match(configDocsEn, /absolute path of the configuration/);
@@ -1485,9 +1499,9 @@ assert.match(configDocsJa, /\[sync\]/);
1485
1499
  assert.match(configDocsJa, /provider = "folder"/);
1486
1500
  assert.match(configDocsJa, /sync\.ja\.md/);
1487
1501
  assert.match(configDocsJa, /\[logging\]/);
1488
- assert.match(configDocsJa, /Ctrl\+Shift\+L/);
1489
- assert.match(configDocsJa, /Ctrl\+Shift\+S/);
1490
- assert.match(configDocsJa, /Ctrl\+Shift\+P/);
1502
+ assert.match(configDocsJa, /Ctrl\+Shift\+l/);
1503
+ assert.match(configDocsJa, /Ctrl\+Shift\+s/);
1504
+ assert.match(configDocsJa, /Ctrl\+Shift\+p/);
1491
1505
  assert.match(configDocsJa, /Ctrl\+Esc.*無効/);
1492
1506
  assert.match(configDocsJa, /Ctrl\+Alt\+Escape/);
1493
1507
  assert.match(configDocsJa, /設定ファイルの絶対path/);
@@ -1520,11 +1534,12 @@ assert.doesNotMatch(specEn, removedTemporaryHttpUiPattern);
1520
1534
  assert.match(specEn, /OSC 52 copy requests/);
1521
1535
  assert.match(specEn, /selected terminal text/);
1522
1536
  assert.match(specEn, /hamburger window menu contains `Log Start/);
1523
- assert.match(specEn, /`Ctrl\+Shift\+L` opens that menu/);
1524
- assert.match(specEn, /`Ctrl\+Shift\+M` opens the window menu/);
1525
- assert.match(specEn, /`Help \(\^H\)` item or `Ctrl\+Shift\+H`/);
1526
- assert.match(specEn, /`Kill \(\^K\)`, `Copy \(\^C\)`, and `Paste \(\^V\)`/);
1527
- assert.match(specEn, /On Unix, `Kill \(\^K\)` or `Ctrl\+Shift\+K` sends `SIGKILL` to the foreground PTY process group/);
1537
+ assert.match(specEn, /`Ctrl\+Shift\+l` shows captured logs/);
1538
+ assert.match(specEn, /`Ctrl\+Shift\+p` is unassigned/);
1539
+ assert.match(specEn, /`Ctrl\+Shift\+m` opens the window menu/);
1540
+ assert.match(specEn, /`Help \(\^h\)` item or `Ctrl\+Shift\+h`/);
1541
+ assert.match(specEn, /`Kill \(\^k\)`, `Copy \(\^c\)`, and `Paste \(\^v\)`/);
1542
+ assert.match(specEn, /On Unix, `Kill \(\^k\)` or `Ctrl\+Shift\+k` sends `SIGKILL` to the foreground PTY process group/);
1528
1543
  assert.match(specEn, /On Windows, it force-terminates descendants of the terminal shell/);
1529
1544
  assert.match(specEn, /diagnostics and log panel textareas/);
1530
1545
  assert.match(specEn, /Terminal paste first reads the WebView clipboard API/);
@@ -1536,11 +1551,12 @@ assert.doesNotMatch(specJa, removedTemporaryHttpUiPattern);
1536
1551
  assert.match(specJa, /OSC 52 copy request/);
1537
1552
  assert.match(specJa, /選択した terminal text/);
1538
1553
  assert.match(specJa, /hamburger の window menu には `Log Start/);
1539
- assert.match(specJa, /`Ctrl\+Shift\+L` は log 操作に focus/);
1540
- assert.match(specJa, /`Ctrl\+Shift\+M` window menu を開き/);
1541
- assert.match(specJa, /`Help \(\^H\)` または `Ctrl\+Shift\+H`/);
1542
- assert.match(specJa, /`Kill \(\^K\)`、`Copy \(\^C\)`、`Paste \(\^V\)`/);
1543
- assert.match(specJa, /`Ctrl\+Shift\+K` が前景 PTY process group に `SIGKILL` を送ります/);
1554
+ assert.match(specJa, /`Ctrl\+Shift\+l` は captured log を表示/);
1555
+ assert.match(specJa, /`Ctrl\+Shift\+p` は将来のcommand palette用に未割り当て/);
1556
+ assert.match(specJa, /`Ctrl\+Shift\+m` で window menu を開き/);
1557
+ assert.match(specJa, /`Help \(\^h\)` または `Ctrl\+Shift\+h`/);
1558
+ assert.match(specJa, /`Kill \(\^k\)`、`Copy \(\^c\)`、`Paste \(\^v\)`/);
1559
+ assert.match(specJa, /`Ctrl\+Shift\+k` が前景 PTY process group に `SIGKILL` を送ります/);
1544
1560
  assert.match(specJa, /Windowsではterminal shellの子孫processを深い順に強制終了/);
1545
1561
  assert.match(specJa, /diagnostics \/ log panel の textarea/);
1546
1562
  assert.match(specJa, /terminal paste は、user gesture中のWebView clipboard APIを先に読み/);
@@ -1706,13 +1722,13 @@ assert.match(syncDocsEn, /debounced diagnostics snapshot/);
1706
1722
  assert.match(syncDocsEn, /sync\.enabled = false/);
1707
1723
  assert.match(syncDocsEn, /not the terminal output log/);
1708
1724
  assert.doesNotMatch(syncDocsEn, /titlebar shows a compact `Sync:` status/);
1709
- assert.match(syncDocsEn, /Ctrl\+Shift\+L/);
1710
- assert.match(syncDocsEn, /Ctrl\+Shift\+S/);
1711
- assert.match(syncDocsEn, /Ctrl\+Shift\+P/);
1725
+ assert.match(syncDocsEn, /Ctrl\+Shift\+l/);
1726
+ assert.match(syncDocsEn, /Ctrl\+Shift\+s/);
1727
+ assert.match(syncDocsEn, /Ctrl\+Shift\+p/);
1712
1728
  assert.match(syncDocsEn, /Delete All/);
1713
1729
  assert.match(syncDocsEn, /opens a selector/);
1714
1730
  assert.match(syncDocsEn, /delete the selected stopped log/);
1715
- assert.match(syncDocsEn, /press `Ctrl\+Shift\+C`/);
1731
+ assert.match(syncDocsEn, /press `Ctrl\+Shift\+c`/);
1716
1732
  assert.match(syncDocsEn, /same clipboard path/);
1717
1733
  assert.match(syncDocsEn, /tmux capture-pane/);
1718
1734
  assert.match(syncDocsEn, /Store terminal output logs in the sync folder/);
@@ -1763,13 +1779,13 @@ assert.match(syncDocsJa, /terminal output log ではありません/);
1763
1779
  assert.doesNotMatch(syncDocsJa, /titlebar に `Sync:` status が表示されます/);
1764
1780
  assert.doesNotMatch(syncDocsJa, /clipboard\.json/);
1765
1781
  assert.match(syncDocsJa, /diagnostics\.json/);
1766
- assert.match(syncDocsJa, /Ctrl\+Shift\+L/);
1767
- assert.match(syncDocsJa, /Ctrl\+Shift\+S/);
1768
- assert.match(syncDocsJa, /Ctrl\+Shift\+P/);
1782
+ assert.match(syncDocsJa, /Ctrl\+Shift\+l/);
1783
+ assert.match(syncDocsJa, /Ctrl\+Shift\+s/);
1784
+ assert.match(syncDocsJa, /Ctrl\+Shift\+p/);
1769
1785
  assert.match(syncDocsJa, /Delete All/);
1770
1786
  assert.match(syncDocsJa, /一覧から表示対象を選択/);
1771
1787
  assert.match(syncDocsJa, /選択した停止済み log/);
1772
- assert.match(syncDocsJa, /`Ctrl\+Shift\+C` を押す/);
1788
+ assert.match(syncDocsJa, /`Ctrl\+Shift\+c` を押す/);
1773
1789
  assert.match(syncDocsJa, /同じ clipboard 経路/);
1774
1790
  assert.match(syncDocsJa, /tmux capture-pane/);
1775
1791
  assert.match(syncDocsJa, /Store terminal output logs in the sync folder/);
@@ -1831,7 +1847,9 @@ assert.match(defaultConfig, /foreground = "#e8edf2"/);
1831
1847
  assert.match(defaultConfig, /cursor = "#f5d76e"/);
1832
1848
  assert.match(defaultConfig, /\[keybindings\]/);
1833
1849
  assert.match(defaultConfig, /prefix = "Mod\+Shift"/);
1834
- assert.match(defaultConfig, /newWindow = "N"/);
1850
+ assert.match(defaultConfig, /logShow = "l"/);
1851
+ assert.doesNotMatch(defaultConfig, /logMenu =/);
1852
+ assert.match(defaultConfig, /newWindow = "n"/);
1835
1853
 
1836
1854
  const minimalConfig = read('examples/config/minimal.toml');
1837
1855
  assert.match(minimalConfig, /\[keybindings\]/);
@@ -1862,8 +1880,14 @@ assert.match(pluginTypes, /registerCommand:/);
1862
1880
  assert.match(pluginTypes, /getOfficialPluginIndex:/);
1863
1881
 
1864
1882
  const renderer = read('src/renderer/renderer.js');
1883
+ const embeddedDefaultConfig = read('src-tauri/default-config.toml');
1884
+ assert.match(embeddedDefaultConfig, /logShow = "l"/);
1885
+ assert.doesNotMatch(embeddedDefaultConfig, /logMenu =/);
1865
1886
  assert.match(renderer, /function terminalBroadcastFocusItems\(\)/);
1866
1887
  assert.match(renderer, /function focusTerminalBroadcastItem\(delta\)/);
1888
+ assert.match(renderer, /function sshfsManagerFocusItems\(\)/);
1889
+ assert.match(renderer, /function focusSshfsManagerItem\(delta\)/);
1890
+ assert.match(renderer, /sshfsManagerDialog\.addEventListener\('focusout'/);
1867
1891
  assert.match(renderer, /function terminalBroadcastFocusLabel\(element\)/);
1868
1892
  assert.match(renderer, /function updateTerminalBroadcastFocusStatus\(element\)/);
1869
1893
  assert.match(renderer, /function terminalBroadcastSubmitKeyEvent\(\)/);
@@ -1872,6 +1896,10 @@ assert.match(renderer, /terminal:broadcast-key/);
1872
1896
  assert.match(renderer, /key: 'Enter', code: 'Enter', shiftKey: false/);
1873
1897
  assert.match(renderer, /function handleTerminalBroadcastKeyboard\(event\)/);
1874
1898
  assert.match(renderer, /event\.key === 'Enter' && event\.shiftKey && !event\.isComposing/);
1899
+ assert.match(renderer, /function enableCheckboxKeyboardToggle\(checkbox\)/);
1900
+ assert.match(renderer, /\[' ', 'Enter'\]\.includes\(event\.key\)/);
1901
+ assert.match(renderer, /enableCheckboxKeyboardToggle\(terminalBroadcastSync\)/);
1902
+ assert.match(renderer, /enableCheckboxKeyboardToggle\(checkbox\)/);
1875
1903
  assert.match(renderer, /decodeTerminalBroadcastControls\(normalizePasteText\(rawText\)\.replace\(\/\\r\+\$\/, ''\)\)/);
1876
1904
  assert.match(renderer, /registerPluginReadyCallback/);
1877
1905
  assert.match(renderer, /notifyPluginsReady/);
@@ -1894,6 +1922,12 @@ assert.match(renderer, /arrangeWindows/);
1894
1922
  assert.match(renderer, /closeAllWindows/);
1895
1923
  assert.match(renderer, /confirmCloseAllWindows/);
1896
1924
  assert.match(renderer, /confirmCloseAllWindows/);
1925
+ assert.match(renderer, /closeAllConfirmed/);
1926
+ assert.match(renderer, /on every platform/);
1927
+ assert.match(renderer, /closeAllConfirmCancelButton\.focus/);
1928
+ assert.match(renderer, /closeAllConfirmUnmountButton/);
1929
+ assert.match(renderer, /function scheduleCloseAllConfirmationFocus/);
1930
+ assert.match(renderer, /closeAllConfirmElement\.addEventListener\('focusout'/);
1897
1931
  assert.match(renderer, /resolveCloseAllWindows/);
1898
1932
  assert.match(renderer, /Close all running fpasoterm windows/);
1899
1933
  assert.match(renderer, /isCloseAllShortcut/);
@@ -2003,7 +2037,7 @@ assert.match(renderer, /event\.shiftKey \? -1 : 1/);
2003
2037
  assert.match(renderer, /event\.key !== 'Enter'/);
2004
2038
  assert.match(renderer, /key\.toLowerCase\(\) === 'j'/);
2005
2039
  assert.match(renderer, /key\.toLowerCase\(\) === 'k'/);
2006
- assert.match(renderer, /matchesKeybinding\(event, 'logMenu'\)/);
2040
+ assert.doesNotMatch(renderer, /matchesKeybinding\(event, 'logMenu'\)/);
2007
2041
  assert.match(renderer, /matchesKeybinding\(event, 'logToggle'\)/);
2008
2042
  assert.match(renderer, /matchesKeybinding\(event, 'logShow'\)/);
2009
2043
  assert.doesNotMatch(renderer, /document\.execCommand\('copy'\)/);
@@ -2220,6 +2254,7 @@ assert.match(renderer, /window\.innerHeight - top - 8/);
2220
2254
  assert.match(renderer, /window\.addEventListener\('resize', fitWindowMenuToViewport\)/);
2221
2255
 
2222
2256
  const styles = read('src/renderer/styles.css');
2257
+ assert.match(styles, /#close-all-confirm button:focus/);
2223
2258
  assert.match(styles, /This is visual-only and never writes to the PTY/);
2224
2259
  assert.match(styles, /#ime-preedit/);
2225
2260
  assert.match(styles, /#ime-preedit\s*\{[\s\S]*z-index: 10010/);
@@ -2281,15 +2316,31 @@ const broadcastControlDocsJa = read('docs/config.ja.md');
2281
2316
  assert.match(broadcastControlDocsEn, /Every send uses the target terminal's actual Enter key event/);
2282
2317
  assert.match(broadcastControlDocsEn, /Its \*\*Control byte\*\* picker inserts visible/);
2283
2318
  assert.match(broadcastControlDocsEn, /`\\x03`/);
2319
+ assert.match(broadcastControlDocsEn, /`\\x01`/);
2320
+ assert.match(broadcastControlDocsEn, /`\\x02`/);
2321
+ assert.match(broadcastControlDocsEn, /\\key\[Arrow/);
2322
+ assert.match(broadcastControlDocsEn, /clear\\x0D\\x01jclear/);
2284
2323
  assert.match(broadcastControlDocsEn, /second confirmation/);
2285
2324
  assert.match(broadcastControlDocsEn, /git reset/);
2286
2325
  assert.match(broadcastControlDocsJa, /送信時は常に本文の後にtarget terminalの実際のEnter key event/);
2287
2326
  assert.match(broadcastControlDocsJa, /dialogの \*\*Control byte\*\* pickerでは/);
2288
2327
  assert.match(broadcastControlDocsJa, /`\\x03`/);
2328
+ assert.match(broadcastControlDocsJa, /`\\x01`/);
2329
+ assert.match(broadcastControlDocsJa, /`\\x02`/);
2330
+ assert.match(broadcastControlDocsJa, /\\key\[Arrow/);
2331
+ assert.match(broadcastControlDocsJa, /clear\\x0D\\x01jclear/);
2289
2332
  assert.match(broadcastControlDocsJa, /追加確認/);
2290
2333
  assert.match(renderer, /function insertTerminalBroadcastControl/);
2291
2334
  assert.match(renderer, /function decodeTerminalBroadcastControls/);
2335
+ assert.match(renderer, /function extractTerminalBroadcastKeyEvent/);
2336
+ assert.match(renderer, /ArrowUp: 38/);
2337
+ assert.match(renderer, /\\\\key\[ArrowUp\]/);
2292
2338
  assert.match(renderer, /\\\\x0d/);
2339
+ assert.match(renderer, /'\\\\x01': '\\x01'/);
2340
+ assert.match(renderer, /'\\\\x02': '\\x02'/);
2341
+ assert.match(renderer, /0d\|1b\|09\|01\|02\|03/);
2342
+ assert.match(renderer, /'ctrl-a': \{ text: '\\\\x01'/);
2343
+ assert.match(renderer, /'ctrl-b': \{ text: '\\\\x02'/);
2293
2344
  assert.match(renderer, /function dangerousBroadcastCommandLabels/);
2294
2345
  assert.match(renderer, /'git reset --hard'/);
2295
2346
  assert.match(renderer, /function confirmDangerousBroadcast/);
@@ -2361,6 +2412,7 @@ assert.match(config, /Ctrl\+Alt\+KeyN/);
2361
2412
  assert.match(config, /enabled: false/);
2362
2413
  assert.match(config, /kittySupport: false/);
2363
2414
  assert.match(config, /function writableConfigDefaults/);
2415
+ assert.match(config, /function migrateLegacyLogKeybindings/);
2364
2416
  assert.match(config, /sixelSupport: false/);
2365
2417
  assert.match(config, /commandTtlSeconds: 60/);
2366
2418
  assert.match(config, /sync:/);
package/src/config.js CHANGED
@@ -89,19 +89,18 @@ const defaultConfig = Object.freeze({
89
89
  },
90
90
  keybindings: {
91
91
  prefix: 'Mod+Shift',
92
- logMenu: 'L',
93
- logToggle: 'S',
94
- logShow: 'P',
95
- copy: 'C',
96
- paste: 'V',
97
- menu: 'M',
98
- help: 'H',
99
- newWindow: 'N',
92
+ logToggle: 's',
93
+ logShow: 'l',
94
+ copy: 'c',
95
+ paste: 'v',
96
+ menu: 'm',
97
+ help: 'h',
98
+ newWindow: 'n',
100
99
  openCwd: 'o',
101
- broadcast: 'B',
102
- kill: 'K',
103
- tile: 'T',
104
- closeAll: 'X',
100
+ broadcast: 'b',
101
+ kill: 'k',
102
+ tile: 't',
103
+ closeAll: 'x',
105
104
  },
106
105
  plugins: {
107
106
  enabled: [],
@@ -182,6 +181,35 @@ function migrateLegacyTerminalLineHeight(config, platform = process.platform) {
182
181
  return config;
183
182
  }
184
183
 
184
+ // Moves former single-letter shortcut defaults to lowercase display form.
185
+ // Longer user-defined bindings remain unchanged.
186
+ function migrateLegacyLogKeybindings(config) {
187
+ const keybindings = config?.keybindings;
188
+ if (!keybindings) {
189
+ return config;
190
+ }
191
+ const legacyBindings = {
192
+ logShow: ['P', 'l'], logToggle: ['S', 's'], copy: ['C', 'c'], paste: ['V', 'v'],
193
+ menu: ['M', 'm'], help: ['H', 'h'], newWindow: ['N', 'n'], broadcast: ['B', 'b'],
194
+ kill: ['K', 'k'], tile: ['T', 't'], closeAll: ['X', 'x'],
195
+ };
196
+ const needsMigration = keybindings.logMenu === 'L'
197
+ || Object.entries(legacyBindings).some(([name, [legacy]]) => keybindings[name] === legacy);
198
+ if (!needsMigration) {
199
+ return config;
200
+ }
201
+ const migrated = mergeConfig(config, { keybindings: {} });
202
+ for (const [name, [legacy, replacement]] of Object.entries(legacyBindings)) {
203
+ if (migrated.keybindings[name] === legacy) {
204
+ migrated.keybindings[name] = replacement;
205
+ }
206
+ }
207
+ if (migrated.keybindings.logMenu === 'L') {
208
+ delete migrated.keybindings.logMenu;
209
+ }
210
+ return migrated;
211
+ }
212
+
185
213
  // Returns the settings persisted in user config files. Image protocol options
186
214
  // remain internal until their renderer support is stable.
187
215
  function writableConfigDefaults(platform = process.platform, architecture = process.arch) {
@@ -277,19 +305,18 @@ brightWhite = "#ffffff"
277
305
  # "Ctrl+Alt+KeyN". Use KeyN-style values for physical-key bindings.
278
306
  [keybindings]
279
307
  prefix = "Mod+Shift"
280
- logMenu = "L"
281
- logToggle = "S"
282
- logShow = "P"
283
- copy = "C"
284
- paste = "V"
285
- menu = "M"
286
- help = "H"
287
- newWindow = "N"
308
+ logToggle = "s"
309
+ logShow = "l"
310
+ copy = "c"
311
+ paste = "v"
312
+ menu = "m"
313
+ help = "h"
314
+ newWindow = "n"
288
315
  openCwd = "o"
289
- broadcast = "B"
290
- kill = "K"
291
- tile = "T"
292
- closeAll = "X"
316
+ broadcast = "b"
317
+ kill = "k"
318
+ tile = "t"
319
+ closeAll = "x"
293
320
 
294
321
  # Plugins are relative to ~/.config/fpasoterm/User/.
295
322
  # Example: enabled = ["plugins/hello.ts", "plugins/theme.ts"]
@@ -799,7 +826,7 @@ function loadConfig() {
799
826
  const dir = path.dirname(file);
800
827
  writeDefaultConfigExample(file);
801
828
 
802
- const userConfig = readUserConfig(file);
829
+ const userConfig = migrateLegacyLogKeybindings(readUserConfig(file));
803
830
  const selected = selectProfileConfig(userConfig);
804
831
  let config = removeUnsupportedConfigSections(mergeConfig(
805
832
  mergeConfig(platformDefaultConfig(), selected.baseConfig),
@@ -843,6 +870,7 @@ module.exports = {
843
870
  writeWindowState,
844
871
  loadConfig,
845
872
  migrateLegacyMacosFontFamily,
873
+ migrateLegacyLogKeybindings,
846
874
  migrateLegacyTerminalLineHeight,
847
875
  mergeConfig,
848
876
  missingConfigKeys,
@@ -14,14 +14,14 @@
14
14
  <div id="window-menu">
15
15
  <span id="terminal-log-status" hidden aria-live="polite">Logging</span>
16
16
  <span id="sshfs-mount-status" hidden aria-live="polite">SSHFS</span>
17
- <button id="window-menu-toggle" type="button" aria-label="Open window menu" aria-expanded="false" aria-keyshortcuts="Control+Shift+M Control+Shift+L">&#9776;</button>
17
+ <button id="window-menu-toggle" type="button" aria-label="Open window menu" aria-expanded="false" aria-keyshortcuts="Control+Shift+M">&#9776;</button>
18
18
  <div id="window-menu-items" role="menu" hidden>
19
19
  <div id="keybinding-prefix" role="note">Shortcut prefix: Ctrl+Shift</div>
20
20
  <div class="window-menu-submenu" data-menu-submenu="log">
21
21
  <button id="log-menu-toggle" class="window-menu-submenu-toggle" type="button" role="menuitem" aria-haspopup="menu" aria-controls="log-menu-items" aria-expanded="false">Log</button>
22
22
  <div id="log-menu-items" class="window-menu-submenu-items" role="menu" hidden>
23
- <button id="terminal-log-toggle" type="button" role="menuitem" aria-keyshortcuts="Control+Shift+S">Log Start (S)</button>
24
- <button id="terminal-log-show" type="button" role="menuitem" aria-keyshortcuts="Control+Shift+P">Log Show (P)</button>
23
+ <button id="terminal-log-toggle" type="button" role="menuitem" aria-keyshortcuts="Control+Shift+S">Log Start (s)</button>
24
+ <button id="terminal-log-show" type="button" role="menuitem" aria-keyshortcuts="Control+Shift+L">Log Show (l)</button>
25
25
  </div>
26
26
  </div>
27
27
  <div class="window-menu-submenu" data-menu-submenu="sync">
@@ -37,11 +37,11 @@
37
37
  <div id="diagnostics-menu-items" class="window-menu-submenu-items" role="menu" hidden>
38
38
  <button id="font-glyph-test" type="button" role="menuitem" aria-label="Show font and glyph diagnostics">Font / Glyph Test</button>
39
39
  <button id="terminal-capability-test" type="button" role="menuitem" aria-label="Show terminal capability diagnostics">Capability Test</button>
40
- <button id="terminal-kill" type="button" role="menuitem" aria-label="Force-stop the current terminal process" aria-keyshortcuts="Control+Shift+K">Kill (K)</button>
40
+ <button id="terminal-kill" type="button" role="menuitem" aria-label="Force-stop the current terminal process" aria-keyshortcuts="Control+Shift+K">Kill (k)</button>
41
41
  </div>
42
42
  </div>
43
- <button id="terminal-copy" type="button" role="menuitem" aria-label="Copy selected text" aria-keyshortcuts="Control+Shift+C">Copy (C)</button>
44
- <button id="terminal-paste" type="button" role="menuitem" aria-label="Paste clipboard text" aria-keyshortcuts="Control+Shift+V">Paste (V)</button>
43
+ <button id="terminal-copy" type="button" role="menuitem" aria-label="Copy selected text" aria-keyshortcuts="Control+Shift+C">Copy (c)</button>
44
+ <button id="terminal-paste" type="button" role="menuitem" aria-label="Paste clipboard text" aria-keyshortcuts="Control+Shift+V">Paste (v)</button>
45
45
  <div id="plugin-menu-section" class="window-menu-submenu" data-menu-submenu="plugins" hidden>
46
46
  <button id="plugin-menu-toggle" class="window-menu-submenu-toggle" type="button" role="menuitem" aria-haspopup="menu" aria-controls="plugin-command-items" aria-expanded="false">Plugins</button>
47
47
  <div id="plugin-command-items" class="window-menu-submenu-items" role="menu" hidden></div>
@@ -49,14 +49,14 @@
49
49
  <div class="window-menu-submenu" data-menu-submenu="window">
50
50
  <button id="window-actions-menu-toggle" class="window-menu-submenu-toggle" type="button" role="menuitem" aria-haspopup="menu" aria-controls="window-actions-menu-items" aria-expanded="false">Window</button>
51
51
  <div id="window-actions-menu-items" class="window-menu-submenu-items" role="menu" hidden>
52
- <button id="new-window" type="button" role="menuitem" aria-label="Open a new fpasoterm window" aria-keyshortcuts="Control+Shift+N">New (N)</button>
52
+ <button id="new-window" type="button" role="menuitem" aria-label="Open a new fpasoterm window" aria-keyshortcuts="Control+Shift+N">New (n)</button>
53
53
  <button id="new-window-cwd" type="button" role="menuitem" aria-label="Open a new terminal window in the OSC 7 current directory" aria-keyshortcuts="Control+Shift+O">New CWD (o)</button>
54
- <button id="terminal-broadcast" type="button" role="menuitem" aria-label="Send text to all fpasoterm windows" aria-keyshortcuts="Control+Shift+B">Broadcast (B)</button>
55
- <button id="arrange-window" type="button" role="menuitem" aria-label="Arrange fpasoterm windows" aria-keyshortcuts="Control+Shift+T">Tile (T)</button>
56
- <button id="close-all-windows" type="button" role="menuitem" aria-label="Close all fpasoterm windows" aria-keyshortcuts="Control+Shift+X">Close All (X)</button>
54
+ <button id="terminal-broadcast" type="button" role="menuitem" aria-label="Send text to all fpasoterm windows" aria-keyshortcuts="Control+Shift+B">Broadcast (b)</button>
55
+ <button id="arrange-window" type="button" role="menuitem" aria-label="Arrange fpasoterm windows" aria-keyshortcuts="Control+Shift+T">Tile (t)</button>
56
+ <button id="close-all-windows" type="button" role="menuitem" aria-label="Close all fpasoterm windows" aria-keyshortcuts="Control+Shift+X">Close All (x)</button>
57
57
  </div>
58
58
  </div>
59
- <button id="keyboard-shortcuts-help" type="button" role="menuitem" aria-label="Show keyboard shortcuts" aria-keyshortcuts="Control+Shift+H">Help (H)</button>
59
+ <button id="keyboard-shortcuts-help" type="button" role="menuitem" aria-label="Show keyboard shortcuts" aria-keyshortcuts="Control+Shift+H">Help (h)</button>
60
60
  </div>
61
61
  </div>
62
62
  <button id="minimize-window" type="button" aria-label="Minimize">-</button>
@@ -117,8 +117,9 @@
117
117
  <div id="close-all-confirm-title">Confirm</div>
118
118
  <pre id="close-all-confirm-message"></pre>
119
119
  <div class="terminal-log-confirm-actions">
120
- <button id="close-all-confirm-ok" type="button">OK</button>
121
120
  <button id="close-all-confirm-cancel" type="button">Cancel</button>
121
+ <button id="close-all-confirm-unmount" type="button" hidden>Unmount &amp; Close</button>
122
+ <button id="close-all-confirm-ok" type="button">OK</button>
122
123
  </div>
123
124
  </div>
124
125
  </section>
@@ -134,16 +135,28 @@
134
135
  <option value="">Select</option>
135
136
  <option value="enter">Enter / CR (0x0D)</option>
136
137
  <option value="tab">Tab (0x09)</option>
138
+ <option value="ctrl-a">Ctrl+A (0x01)</option>
139
+ <option value="ctrl-b">Ctrl+B (0x02)</option>
137
140
  <option value="ctrl-c">Ctrl+C (0x03)</option>
138
141
  <option value="ctrl-d">Ctrl+D (0x04)</option>
139
142
  <option value="ctrl-x">Ctrl+X (0x18)</option>
140
143
  <option value="ctrl-z">Ctrl+Z (0x1A)</option>
144
+ <option value="arrow-up">Arrow Up</option>
145
+ <option value="arrow-down">Arrow Down</option>
146
+ <option value="arrow-left">Arrow Left</option>
147
+ <option value="arrow-right">Arrow Right</option>
141
148
  <option value="alt-prefix">Alt prefix / Esc (0x1B)</option>
142
149
  </select>
143
150
  </label>
144
151
  <button id="terminal-broadcast-control-insert" type="button">Insert</button>
145
152
  <span id="terminal-broadcast-control-status" aria-live="polite"></span>
146
153
  </div>
154
+ <details id="terminal-broadcast-examples">
155
+ <summary>Examples</summary>
156
+ <p>Run a command, then send a multiplexer prefix and key: <code>clear\x0D\x01jclear</code></p>
157
+ <p>For a prefix followed by an arrow key, use a trailing key marker: <code>\x02\key[ArrowUp]</code></p>
158
+ <p>Select only the intended local windows. Each selected window receives the whole sequence.</p>
159
+ </details>
147
160
  <fieldset id="terminal-broadcast-targets">
148
161
  <legend>Local windows</legend>
149
162
  <div id="terminal-broadcast-target-list" aria-live="polite"></div>