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.
- package/README.ja.md +2 -2
- package/README.md +6 -6
- package/bin/fpasoterm +9 -3
- package/docs/config.en.md +20 -10
- package/docs/config.ja.md +12 -9
- package/docs/plugins.en.md +1 -1
- package/docs/plugins.ja.md +1 -1
- package/docs/spec.en.md +3 -3
- package/docs/spec.ja.md +3 -3
- package/docs/sync.en.md +3 -3
- package/docs/sync.ja.md +3 -3
- package/examples/config/default-appearance.toml +11 -12
- package/package.json +1 -1
- package/scripts/tests/smoke.js +110 -58
- package/src/config.js +53 -25
- package/src/renderer/index.html +25 -12
- package/src/renderer/renderer.js +173 -49
- package/src/renderer/styles.css +43 -2
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/default-config.toml +12 -13
- package/src-tauri/src/main.rs +93 -128
- package/src/renderer/confirm.html +0 -71
package/src-tauri/src/main.rs
CHANGED
|
@@ -25,8 +25,6 @@ use std::{ptr, slice};
|
|
|
25
25
|
use tauri::{
|
|
26
26
|
AppHandle, Emitter, Manager, PhysicalPosition, PhysicalSize, State, WebviewWindow, WindowEvent,
|
|
27
27
|
};
|
|
28
|
-
#[cfg(not(target_os = "windows"))]
|
|
29
|
-
use tauri::{WebviewUrl, WebviewWindowBuilder};
|
|
30
28
|
#[cfg(target_os = "windows")]
|
|
31
29
|
use windows_sys::Win32::Foundation::{CloseHandle, GlobalFree, INVALID_HANDLE_VALUE};
|
|
32
30
|
#[cfg(target_os = "windows")]
|
|
@@ -788,10 +786,7 @@ fn main() {
|
|
|
788
786
|
sshfs_unmount_all,
|
|
789
787
|
sshfs_forget,
|
|
790
788
|
sshfs_status,
|
|
791
|
-
window_confirm_close_all,
|
|
792
789
|
window_close_all_confirmed,
|
|
793
|
-
window_focus_main,
|
|
794
|
-
window_cancel_close_all,
|
|
795
790
|
window_save_bounds,
|
|
796
791
|
window_get_bounds,
|
|
797
792
|
window_set_bounds,
|
|
@@ -2065,22 +2060,12 @@ fn sshfs_mount(
|
|
|
2065
2060
|
write_sshfs_mount_records(&mounts)?;
|
|
2066
2061
|
Ok(SshfsMountResult {
|
|
2067
2062
|
mount_point: mount_point.display().to_string(),
|
|
2068
|
-
message:
|
|
2063
|
+
message:
|
|
2064
|
+
"SSHFS mount is ready; use this path from the terminal or another local application"
|
|
2065
|
+
.to_string(),
|
|
2069
2066
|
})
|
|
2070
2067
|
}
|
|
2071
2068
|
|
|
2072
|
-
// macFUSE retains the sshfs process after mounting, but the mount table has
|
|
2073
|
-
// already confirmed the requested filesystem is usable before this is called.
|
|
2074
|
-
#[cfg(target_os = "macos")]
|
|
2075
|
-
fn sshfs_mount_ready_message() -> String {
|
|
2076
|
-
"SSHFS mount is ready; use this path from the terminal or another local application".to_string()
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
#[cfg(not(target_os = "macos"))]
|
|
2080
|
-
fn sshfs_mount_ready_message() -> String {
|
|
2081
|
-
"SSHFS mount is ready; use this path from the terminal or another local application".to_string()
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
2069
|
// SSHFS-Win officially exposes SSHFS mounts as mapped network drives. Calling
|
|
2085
2070
|
// its Cygwin sshfs.exe directly from a GUI process produces orphaned FUSE
|
|
2086
2071
|
// mounts, so use Windows' network-drive API and matching disconnect API.
|
|
@@ -2739,109 +2724,16 @@ fn detach_nested_macos_launch() -> bool {
|
|
|
2739
2724
|
false
|
|
2740
2725
|
}
|
|
2741
2726
|
|
|
2742
|
-
//
|
|
2743
|
-
// when the terminal content is busy or visually obscured.
|
|
2744
|
-
#[tauri::command]
|
|
2745
|
-
fn window_confirm_close_all(app: AppHandle) -> Result<String, String> {
|
|
2746
|
-
#[cfg(target_os = "windows")]
|
|
2747
|
-
{
|
|
2748
|
-
// A native dialog remains operable even if a secondary WebView fails to
|
|
2749
|
-
// load, and Windows supplies keyboard navigation plus a working close button.
|
|
2750
|
-
if let Some(window) = app.get_webview_window("close-all-confirm") {
|
|
2751
|
-
let _ = window.close();
|
|
2752
|
-
}
|
|
2753
|
-
if windows_confirm_close_all() {
|
|
2754
|
-
broadcast_close_all_request()?;
|
|
2755
|
-
return Ok("closed all fpasoterm windows".to_string());
|
|
2756
|
-
}
|
|
2757
|
-
let _ = window_focus_main(app);
|
|
2758
|
-
return Ok("close all canceled".to_string());
|
|
2759
|
-
}
|
|
2760
|
-
|
|
2761
|
-
#[cfg(not(target_os = "windows"))]
|
|
2762
|
-
{
|
|
2763
|
-
if let Some(window) = app.get_webview_window("close-all-confirm") {
|
|
2764
|
-
let _ = window.show();
|
|
2765
|
-
let _ = window.unminimize();
|
|
2766
|
-
let _ = window.set_focus();
|
|
2767
|
-
return Ok("close confirmation already open".to_string());
|
|
2768
|
-
}
|
|
2769
|
-
let window = WebviewWindowBuilder::new(
|
|
2770
|
-
&app,
|
|
2771
|
-
"close-all-confirm",
|
|
2772
|
-
WebviewUrl::App("confirm.html".into()),
|
|
2773
|
-
)
|
|
2774
|
-
.title("Confirm close all fpasoterm windows")
|
|
2775
|
-
.inner_size(600.0, 280.0)
|
|
2776
|
-
.min_inner_size(520.0, 220.0)
|
|
2777
|
-
.resizable(true)
|
|
2778
|
-
.decorations(true)
|
|
2779
|
-
.transparent(false)
|
|
2780
|
-
.visible(true)
|
|
2781
|
-
.always_on_top(true)
|
|
2782
|
-
.focused(true)
|
|
2783
|
-
.center()
|
|
2784
|
-
.build()
|
|
2785
|
-
.map_err(|error| error.to_string())?;
|
|
2786
|
-
// Make the new native window receive keyboard input immediately.
|
|
2787
|
-
let _ = window.set_focus();
|
|
2788
|
-
Ok("close confirmation opened".to_string())
|
|
2789
|
-
}
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2792
|
-
#[cfg(target_os = "windows")]
|
|
2793
|
-
// Uses the Windows standard modal dialog instead of a failure-prone secondary WebView.
|
|
2794
|
-
fn windows_confirm_close_all() -> bool {
|
|
2795
|
-
let message: Vec<u16> =
|
|
2796
|
-
"Close all running fpasoterm windows?\n\nThis will terminate every fpasoterm window."
|
|
2797
|
-
.encode_utf16()
|
|
2798
|
-
.chain(std::iter::once(0))
|
|
2799
|
-
.collect();
|
|
2800
|
-
let title: Vec<u16> = "Confirm close all fpasoterm windows"
|
|
2801
|
-
.encode_utf16()
|
|
2802
|
-
.chain(std::iter::once(0))
|
|
2803
|
-
.collect();
|
|
2804
|
-
unsafe {
|
|
2805
|
-
MessageBoxW(
|
|
2806
|
-
ptr::null_mut(),
|
|
2807
|
-
message.as_ptr(),
|
|
2808
|
-
title.as_ptr(),
|
|
2809
|
-
MB_OKCANCEL | MB_ICONWARNING | MB_SETFOREGROUND | MB_TASKMODAL | MB_TOPMOST,
|
|
2810
|
-
) == IDOK
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
|
-
// Restores keyboard focus to the terminal after a confirmation window closes.
|
|
2815
|
-
#[tauri::command]
|
|
2816
|
-
fn window_focus_main(app: AppHandle) -> Result<(), String> {
|
|
2817
|
-
let window = app
|
|
2818
|
-
.get_webview_window("main")
|
|
2819
|
-
.ok_or_else(|| "main window is not available".to_string())?;
|
|
2820
|
-
window.set_focus().map_err(|error| error.to_string())
|
|
2821
|
-
}
|
|
2822
|
-
|
|
2823
|
-
// Closes the confirmation window and restores focus to the terminal atomically.
|
|
2824
|
-
#[tauri::command]
|
|
2825
|
-
fn window_cancel_close_all(app: AppHandle) -> Result<(), String> {
|
|
2826
|
-
if let Some(window) = app.get_webview_window("close-all-confirm") {
|
|
2827
|
-
let _ = window.close();
|
|
2828
|
-
}
|
|
2829
|
-
window_focus_main(app)
|
|
2830
|
-
}
|
|
2831
|
-
|
|
2832
|
-
// Broadcasts the close request from the independent confirmation window.
|
|
2727
|
+
// Broadcasts the close request after the in-window confirmation overlay.
|
|
2833
2728
|
#[tauri::command]
|
|
2834
2729
|
fn window_close_all_confirmed(
|
|
2835
|
-
|
|
2730
|
+
_app: AppHandle,
|
|
2836
2731
|
unmount_sshfs: Option<bool>,
|
|
2837
2732
|
) -> Result<String, String> {
|
|
2838
2733
|
if unmount_sshfs.unwrap_or(false) {
|
|
2839
2734
|
sshfs_unmount_all()?;
|
|
2840
2735
|
}
|
|
2841
2736
|
broadcast_close_all_request()?;
|
|
2842
|
-
if let Some(window) = app.get_webview_window("close-all-confirm") {
|
|
2843
|
-
let _ = window.close();
|
|
2844
|
-
}
|
|
2845
2737
|
Ok("closed all fpasoterm windows".to_string())
|
|
2846
2738
|
}
|
|
2847
2739
|
|
|
@@ -3065,6 +2957,7 @@ fn runtime_config() -> RuntimeConfig {
|
|
|
3065
2957
|
};
|
|
3066
2958
|
migrate_legacy_terminal_font_family(&mut config);
|
|
3067
2959
|
migrate_legacy_terminal_line_height(&mut config);
|
|
2960
|
+
migrate_legacy_log_keybindings(&mut config);
|
|
3068
2961
|
apply_direct_cli_overrides(&mut config);
|
|
3069
2962
|
config
|
|
3070
2963
|
}
|
|
@@ -3163,6 +3056,41 @@ fn migrate_legacy_terminal_line_height(runtime: &mut RuntimeConfig) {
|
|
|
3163
3056
|
}
|
|
3164
3057
|
}
|
|
3165
3058
|
|
|
3059
|
+
// Reserves Ctrl+Shift+p for a future command palette and normalizes former
|
|
3060
|
+
// single-letter defaults. Full user-defined shortcuts are left unchanged.
|
|
3061
|
+
fn migrate_legacy_log_keybindings(runtime: &mut RuntimeConfig) {
|
|
3062
|
+
let Some(keybindings) = runtime.config.keybindings.as_object_mut() else {
|
|
3063
|
+
return;
|
|
3064
|
+
};
|
|
3065
|
+
for (name, legacy, replacement) in [
|
|
3066
|
+
("logShow", "P", "l"),
|
|
3067
|
+
("logToggle", "S", "s"),
|
|
3068
|
+
("copy", "C", "c"),
|
|
3069
|
+
("paste", "V", "v"),
|
|
3070
|
+
("menu", "M", "m"),
|
|
3071
|
+
("help", "H", "h"),
|
|
3072
|
+
("newWindow", "N", "n"),
|
|
3073
|
+
("broadcast", "B", "b"),
|
|
3074
|
+
("kill", "K", "k"),
|
|
3075
|
+
("tile", "T", "t"),
|
|
3076
|
+
("closeAll", "X", "x"),
|
|
3077
|
+
] {
|
|
3078
|
+
if keybindings.get(name).and_then(serde_json::Value::as_str) == Some(legacy) {
|
|
3079
|
+
keybindings.insert(
|
|
3080
|
+
name.to_string(),
|
|
3081
|
+
serde_json::Value::String(replacement.to_string()),
|
|
3082
|
+
);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
if keybindings
|
|
3086
|
+
.get("logMenu")
|
|
3087
|
+
.and_then(serde_json::Value::as_str)
|
|
3088
|
+
== Some("L")
|
|
3089
|
+
{
|
|
3090
|
+
keybindings.remove("logMenu");
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3166
3094
|
// Keeps the platform rule testable from non-macOS CI hosts.
|
|
3167
3095
|
fn terminal_font_size_for(platform: &str, architecture: &str) -> u32 {
|
|
3168
3096
|
if platform == "macos" && architecture == "x86_64" {
|
|
@@ -3276,6 +3204,7 @@ fn merge_runtime_config_from_path(
|
|
|
3276
3204
|
config.config = serde_json::from_value(config_value).map_err(|error| error.to_string())?;
|
|
3277
3205
|
migrate_legacy_terminal_font_family(&mut config);
|
|
3278
3206
|
migrate_legacy_terminal_line_height(&mut config);
|
|
3207
|
+
migrate_legacy_log_keybindings(&mut config);
|
|
3279
3208
|
config.config_path = absolute_path.to_string_lossy().to_string();
|
|
3280
3209
|
config.config_dir = absolute_path
|
|
3281
3210
|
.parent()
|
|
@@ -3466,19 +3395,18 @@ fn default_runtime_config() -> RuntimeConfig {
|
|
|
3466
3395
|
}),
|
|
3467
3396
|
keybindings: serde_json::json!({
|
|
3468
3397
|
"prefix": "Mod+Shift",
|
|
3469
|
-
"
|
|
3470
|
-
"
|
|
3471
|
-
"
|
|
3472
|
-
"
|
|
3473
|
-
"
|
|
3474
|
-
"
|
|
3475
|
-
"
|
|
3476
|
-
"newWindow": "N",
|
|
3398
|
+
"logToggle": "s",
|
|
3399
|
+
"logShow": "l",
|
|
3400
|
+
"copy": "c",
|
|
3401
|
+
"paste": "v",
|
|
3402
|
+
"menu": "m",
|
|
3403
|
+
"help": "h",
|
|
3404
|
+
"newWindow": "n",
|
|
3477
3405
|
"openCwd": "o",
|
|
3478
|
-
"broadcast": "
|
|
3479
|
-
"kill": "
|
|
3480
|
-
"tile": "
|
|
3481
|
-
"closeAll": "
|
|
3406
|
+
"broadcast": "b",
|
|
3407
|
+
"kill": "k",
|
|
3408
|
+
"tile": "t",
|
|
3409
|
+
"closeAll": "x"
|
|
3482
3410
|
}),
|
|
3483
3411
|
plugins: serde_json::json!({ "enabled": [] }),
|
|
3484
3412
|
sync: serde_json::json!({
|
|
@@ -6410,8 +6338,8 @@ fn terminal_broadcast(
|
|
|
6410
6338
|
request: TerminalBroadcastRequest,
|
|
6411
6339
|
) -> Result<TerminalBroadcastStatus, String> {
|
|
6412
6340
|
let text = request.text;
|
|
6413
|
-
if text.is_empty() {
|
|
6414
|
-
return Err("broadcast text
|
|
6341
|
+
if text.is_empty() && request.key_event.is_none() {
|
|
6342
|
+
return Err("broadcast text and key event are empty".to_string());
|
|
6415
6343
|
}
|
|
6416
6344
|
let max_bytes = sync_max_bytes();
|
|
6417
6345
|
if text.len() > max_bytes {
|
|
@@ -9206,13 +9134,50 @@ installPath = "appearance/other.ts"
|
|
|
9206
9134
|
);
|
|
9207
9135
|
assert!(config["terminal"].get("images").is_none());
|
|
9208
9136
|
assert_eq!(config["keybindings"]["prefix"].as_str(), Some("Mod+Shift"));
|
|
9209
|
-
assert_eq!(config["keybindings"]["
|
|
9137
|
+
assert_eq!(config["keybindings"]["logShow"].as_str(), Some("l"));
|
|
9138
|
+
assert_eq!(config["keybindings"]["logToggle"].as_str(), Some("s"));
|
|
9139
|
+
assert!(config["keybindings"].get("logMenu").is_none());
|
|
9140
|
+
assert_eq!(config["keybindings"]["newWindow"].as_str(), Some("n"));
|
|
9210
9141
|
assert_eq!(config["sync"]["enabled"].as_bool(), Some(false));
|
|
9211
9142
|
assert_eq!(config["sync"]["commands"].as_bool(), Some(false));
|
|
9212
9143
|
assert_eq!(config["sync"]["commandSecret"].as_str(), Some(""));
|
|
9213
9144
|
assert_eq!(config["logging"]["enabled"].as_bool(), Some(true));
|
|
9214
9145
|
}
|
|
9215
9146
|
|
|
9147
|
+
#[test]
|
|
9148
|
+
fn legacy_keybindings_use_lowercase_defaults() {
|
|
9149
|
+
let mut runtime = default_runtime_config();
|
|
9150
|
+
runtime.config.keybindings = serde_json::json!({
|
|
9151
|
+
"logMenu": "L",
|
|
9152
|
+
"logShow": "P",
|
|
9153
|
+
"logToggle": "S",
|
|
9154
|
+
"copy": "C",
|
|
9155
|
+
"paste": "V",
|
|
9156
|
+
"menu": "M",
|
|
9157
|
+
"help": "H",
|
|
9158
|
+
"newWindow": "N",
|
|
9159
|
+
"broadcast": "B",
|
|
9160
|
+
"kill": "K",
|
|
9161
|
+
"tile": "T",
|
|
9162
|
+
"closeAll": "X"
|
|
9163
|
+
});
|
|
9164
|
+
|
|
9165
|
+
migrate_legacy_log_keybindings(&mut runtime);
|
|
9166
|
+
|
|
9167
|
+
assert_eq!(runtime.config.keybindings["logShow"].as_str(), Some("l"));
|
|
9168
|
+
assert_eq!(runtime.config.keybindings["logToggle"].as_str(), Some("s"));
|
|
9169
|
+
assert_eq!(runtime.config.keybindings["copy"].as_str(), Some("c"));
|
|
9170
|
+
assert_eq!(runtime.config.keybindings["paste"].as_str(), Some("v"));
|
|
9171
|
+
assert_eq!(runtime.config.keybindings["menu"].as_str(), Some("m"));
|
|
9172
|
+
assert_eq!(runtime.config.keybindings["help"].as_str(), Some("h"));
|
|
9173
|
+
assert_eq!(runtime.config.keybindings["newWindow"].as_str(), Some("n"));
|
|
9174
|
+
assert_eq!(runtime.config.keybindings["broadcast"].as_str(), Some("b"));
|
|
9175
|
+
assert_eq!(runtime.config.keybindings["kill"].as_str(), Some("k"));
|
|
9176
|
+
assert_eq!(runtime.config.keybindings["tile"].as_str(), Some("t"));
|
|
9177
|
+
assert_eq!(runtime.config.keybindings["closeAll"].as_str(), Some("x"));
|
|
9178
|
+
assert!(runtime.config.keybindings.get("logMenu").is_none());
|
|
9179
|
+
}
|
|
9180
|
+
|
|
9216
9181
|
#[test]
|
|
9217
9182
|
fn redacted_runtime_config_hides_sync_command_key() {
|
|
9218
9183
|
let mut runtime = default_runtime_config();
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Confirm</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root { color-scheme: dark; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
9
|
-
body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: #171b21; color: #e8edf2; }
|
|
10
|
-
main { width: min(500px, calc(100vw - 48px)); }
|
|
11
|
-
h1 { margin: 0 0 14px; color: #f5d76e; font-size: 17px; }
|
|
12
|
-
p { margin: 0 0 24px; white-space: pre-wrap; font-size: 14px; line-height: 1.5; }
|
|
13
|
-
footer { display: flex; justify-content: flex-end; gap: 10px; }
|
|
14
|
-
button { min-width: 110px; height: 38px; border: 1px solid #3b4652; border-radius: 4px; background: #222932; color: #e8edf2; font: inherit; font-size: 14px; }
|
|
15
|
-
button:focus-visible { outline: 2px solid #f5d76e; outline-offset: 2px; }
|
|
16
|
-
</style>
|
|
17
|
-
</head>
|
|
18
|
-
<body>
|
|
19
|
-
<main role="dialog" aria-modal="true" aria-labelledby="title">
|
|
20
|
-
<h1 id="title">Close all fpasoterm windows?</h1>
|
|
21
|
-
<p id="message">This will terminate every running fpasoterm window.</p>
|
|
22
|
-
<footer>
|
|
23
|
-
<button id="cancel" type="button">Cancel</button>
|
|
24
|
-
<button id="unmount" type="button" hidden>Unmount & Close</button>
|
|
25
|
-
<button id="ok" type="button">OK</button>
|
|
26
|
-
</footer>
|
|
27
|
-
</main>
|
|
28
|
-
<script>
|
|
29
|
-
const invoke = window.__TAURI__.core.invoke;
|
|
30
|
-
const currentWindow = window.__TAURI__.window.getCurrentWindow();
|
|
31
|
-
const ok = document.getElementById('ok');
|
|
32
|
-
const cancel = document.getElementById('cancel');
|
|
33
|
-
const unmount = document.getElementById('unmount');
|
|
34
|
-
const message = document.getElementById('message');
|
|
35
|
-
invoke('sshfs_status').then((status) => {
|
|
36
|
-
if (status.mounts?.length) {
|
|
37
|
-
message.textContent += `\n\n${status.mounts.length} SSHFS mount(s) will remain active. Reopen FpasoTerm and use Window > SSHFS Mounts to unmount them.`;
|
|
38
|
-
unmount.hidden = false;
|
|
39
|
-
}
|
|
40
|
-
}).catch(() => {});
|
|
41
|
-
const focusConfirmWindow = () => {
|
|
42
|
-
currentWindow.setFocus().catch(() => {});
|
|
43
|
-
cancel.focus({ preventScroll: true });
|
|
44
|
-
};
|
|
45
|
-
focusConfirmWindow();
|
|
46
|
-
window.addEventListener('load', focusConfirmWindow);
|
|
47
|
-
setTimeout(focusConfirmWindow, 80);
|
|
48
|
-
const focusMainAndClose = () => invoke('window_cancel_close_all').catch(() => currentWindow.close().catch(() => {}));
|
|
49
|
-
const closeConfirmWindow = () => currentWindow.close().catch(() => {});
|
|
50
|
-
ok.addEventListener('click', () => invoke('window_close_all_confirmed', { unmountSshfs: false }).catch(closeConfirmWindow));
|
|
51
|
-
unmount.addEventListener('click', () => invoke('window_close_all_confirmed', { unmountSshfs: true }).catch(closeConfirmWindow));
|
|
52
|
-
cancel.addEventListener('click', focusMainAndClose);
|
|
53
|
-
window.addEventListener('keydown', (event) => {
|
|
54
|
-
if (event.key === 'Tab') {
|
|
55
|
-
event.preventDefault();
|
|
56
|
-
(document.activeElement === cancel ? ok : cancel).focus();
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (event.key === 'Enter' && document.activeElement instanceof HTMLButtonElement) {
|
|
60
|
-
event.preventDefault();
|
|
61
|
-
document.activeElement.click();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (event.key === 'Escape') {
|
|
65
|
-
event.preventDefault();
|
|
66
|
-
focusMainAndClose();
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
</script>
|
|
70
|
-
</body>
|
|
71
|
-
</html>
|