pake-cli 0.0.1-beta.8 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.ecrc.json +3 -0
  2. package/.editorconfig +24 -0
  3. package/.github/FUNDING.yml +2 -2
  4. package/.github/ISSUE_TEMPLATE/bug.md +13 -0
  5. package/.github/workflows/editorconfig-check.yml +23 -0
  6. package/.github/workflows/main.yml +17 -0
  7. package/.github/workflows/rust-code-quality-check.yml +89 -0
  8. package/.prettierignore +4 -0
  9. package/.vscode/settings.json +1 -0
  10. package/CODE_OF_CONDUCT.md +128 -0
  11. package/CONTRIBUTING.md +26 -0
  12. package/LICENSE +21 -0
  13. package/README.md +175 -54
  14. package/README_EN.md +91 -53
  15. package/app.csv +9 -0
  16. package/bin/README.md +72 -0
  17. package/bin/builders/MacBuilder.ts +13 -14
  18. package/bin/builders/base.ts +10 -1
  19. package/bin/builders/common.ts +7 -25
  20. package/bin/cli.ts +12 -5
  21. package/bin/defaults.ts +1 -0
  22. package/bin/options/icon.ts +51 -53
  23. package/bin/options/index.ts +3 -5
  24. package/bin/types.ts +3 -3
  25. package/bin/utils/shell.ts +1 -0
  26. package/bin/utils/tlds.ts +1489 -0
  27. package/bin/utils/url.ts +26 -0
  28. package/dist/cli.js +1620 -101
  29. package/dist/twitter.css +176 -0
  30. package/icns2png.py +38 -0
  31. package/package.json +8 -7
  32. package/pake-default.icns +0 -0
  33. package/rollup.config.js +1 -1
  34. package/script/build.bat +80 -0
  35. package/script/build.sh +122 -0
  36. package/script/sd-apple-x64 +0 -0
  37. package/script/sd-linux-x64 +0 -0
  38. package/script/sd.exe +0 -0
  39. package/src-tauri/Cargo.lock +344 -65
  40. package/src-tauri/Cargo.toml +14 -6
  41. package/src-tauri/assets/com-tw93-weread.desktop +10 -0
  42. package/src-tauri/build.rs +1 -1
  43. package/src-tauri/icons/icon.icns +0 -0
  44. package/src-tauri/icons/reference.icns +0 -0
  45. package/src-tauri/icons/translate.icns +0 -0
  46. package/src-tauri/icons/twitter.icns +0 -0
  47. package/src-tauri/icons/{weRead.icns → weread.icns} +0 -0
  48. package/src-tauri/icons/youtube.icns +0 -0
  49. package/src-tauri/png/code_256.ico +0 -0
  50. package/src-tauri/png/code_32.ico +0 -0
  51. package/src-tauri/png/code_512.png +0 -0
  52. package/src-tauri/png/flomo_256.ico +0 -0
  53. package/src-tauri/png/flomo_32.ico +0 -0
  54. package/src-tauri/png/flomo_512.png +0 -0
  55. package/src-tauri/png/reference_256.ico +0 -0
  56. package/src-tauri/png/reference_32.ico +0 -0
  57. package/src-tauri/png/reference_512.png +0 -0
  58. package/src-tauri/png/tool_256.ico +0 -0
  59. package/src-tauri/png/tool_32.ico +0 -0
  60. package/src-tauri/png/tool_512.png +0 -0
  61. package/src-tauri/png/translate_256.ico +0 -0
  62. package/src-tauri/png/translate_32.ico +0 -0
  63. package/src-tauri/png/translate_512.png +0 -0
  64. package/src-tauri/png/twitter_256.ico +0 -0
  65. package/src-tauri/png/twitter_32.ico +0 -0
  66. package/src-tauri/png/twitter_512.png +0 -0
  67. package/src-tauri/png/weread_256.ico +0 -0
  68. package/src-tauri/png/weread_32.ico +0 -0
  69. package/src-tauri/png/weread_512.png +0 -0
  70. package/src-tauri/png/witeboard_256.ico +0 -0
  71. package/src-tauri/png/witeboard_32.ico +0 -0
  72. package/src-tauri/png/witeboard_512.png +0 -0
  73. package/src-tauri/png/youtube_256.ico +0 -0
  74. package/src-tauri/png/youtube_32.ico +0 -0
  75. package/src-tauri/png/youtube_512.png +0 -0
  76. package/src-tauri/png/yuque_256.ico +0 -0
  77. package/src-tauri/png/yuque_32.ico +0 -0
  78. package/src-tauri/png/yuque_512.png +0 -0
  79. package/src-tauri/src/main.rs +130 -25
  80. package/src-tauri/src/pake.js +140 -77
  81. package/src-tauri/tauri.conf.json +30 -13
  82. package/bin/options/title.ts +0 -14
  83. package/src-tauri/icons/anymind.icns +0 -0
  84. package/src-tauri/icons/fanfou.icns +0 -0
  85. package/src-tauri/icons/fone.icns +0 -0
  86. package/src-tauri/icons/jdread.icns +0 -0
  87. package/src-tauri/icons/jike.icns +0 -0
  88. package/src-tauri/icons/roam.icns +0 -0
  89. package/src-tauri/icons/vercel.icns +0 -0
  90. package/src-tauri/icons/whatsapp.icns +0 -0
@@ -12,14 +12,20 @@ rust-version = "1.61.0"
12
12
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13
13
 
14
14
  [build-dependencies]
15
- tauri-build = { version = "1.1.1", features = [] }
15
+ tauri-build = { version = "1.2.0", features = [] }
16
+
16
17
 
17
18
  [dependencies]
18
- serde_json = "1.0.86"
19
- serde = { version = "1.0.145", features = ["derive"] }
20
- tauri = { version = "1.1.1", features = ["api-all"] }
21
- wry = "0.21.1"
22
- tauri-utils = "1.1.1"
19
+ serde_json = "1.0.88"
20
+ serde = { version = "1.0.147", features = ["derive"] }
21
+ tauri = { version = "1.2.0", features = ["api-all"] }
22
+ image = "0.24.5"
23
+ tauri-utils = "1.2.0"
24
+ webbrowser = "0.8.2"
25
+
26
+ [dependencies.wry]
27
+ git = "https://github.com/tauri-apps/wry.git"
28
+ rev = "7c6d64acea4414f7c960b38b80ea9ec3628db2a8"
23
29
 
24
30
  [features]
25
31
  # by default Tauri runs in production mode
@@ -28,3 +34,5 @@ default = [ "custom-protocol" ]
28
34
  # this feature is used used for production builds where `devPath` points to the filesystem
29
35
  # DO NOT remove this
30
36
  custom-protocol = [ "tauri/custom-protocol" ]
37
+ # Enable DevTools for debugging.
38
+ devtools = []
@@ -0,0 +1,10 @@
1
+ [Desktop Entry]
2
+ Encoding=UTF-8
3
+ Categories=Office
4
+ Exec=com-tw93-weread
5
+ Icon=com-tw93-weread
6
+ Name=com-tw93-weread
7
+ Name[zh_CN]=微信阅读
8
+ StartupNotify=true
9
+ Terminal=false
10
+ Type=Application
@@ -1,3 +1,3 @@
1
1
  fn main() {
2
- tauri_build::build()
2
+ tauri_build::build()
3
3
  }
Binary file
Binary file
Binary file
Binary file
File without changes
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,41 +1,103 @@
1
+ // at the top of main.rs - that will prevent the console from showing
2
+ #![windows_subsystem = "windows"]
3
+ extern crate image;
1
4
  use tauri_utils::config::{Config, WindowConfig};
5
+ #[cfg(target_os = "macos")]
6
+ use wry::application::platform::macos::WindowBuilderExtMacOS;
2
7
 
3
- fn main() -> wry::Result<()> {
4
- use wry::{
5
- application::{
6
- accelerator::{Accelerator, SysMods},
7
- event::{Event, StartCause, WindowEvent},
8
- event_loop::{ControlFlow, EventLoop},
9
- keyboard::KeyCode,
10
- menu::{MenuBar as Menu, MenuItem, MenuItemAttributes, MenuType},
11
- platform::macos::WindowBuilderExtMacOS,
12
- window::{Fullscreen, Window, WindowBuilder},
13
- },
14
- webview::WebViewBuilder,
15
- };
8
+ #[cfg(target_os = "macos")]
9
+ use wry::{
10
+ application::{
11
+ accelerator::{Accelerator, SysMods},
12
+ event::{Event, StartCause, WindowEvent},
13
+ event_loop::{ControlFlow, EventLoop},
14
+ keyboard::KeyCode,
15
+ menu::{MenuBar as Menu, MenuItem, MenuItemAttributes, MenuType},
16
+ window::{Fullscreen, Window, WindowBuilder},
17
+ },
18
+ webview::WebViewBuilder,
19
+ };
20
+
21
+ #[cfg(target_os = "windows")]
22
+ use wry::{
23
+ application::{
24
+ event::{Event, StartCause, WindowEvent},
25
+ event_loop::{ControlFlow, EventLoop},
26
+ menu::MenuType,
27
+ window::{Fullscreen, Icon, Window, WindowBuilder},
28
+ },
29
+ webview::WebViewBuilder,
30
+ };
16
31
 
32
+ #[cfg(target_os = "linux")]
33
+ use wry::{
34
+ application::{
35
+ event::{Event, StartCause, WindowEvent},
36
+ event_loop::{ControlFlow, EventLoop},
37
+ menu::MenuType,
38
+ window::{Fullscreen, Window, WindowBuilder},
39
+ },
40
+ webview::WebViewBuilder,
41
+ };
42
+
43
+ fn main() -> wry::Result<()> {
44
+ #[cfg(target_os = "macos")]
17
45
  let mut menu_bar_menu = Menu::new();
46
+ #[cfg(target_os = "macos")]
18
47
  let mut first_menu = Menu::new();
19
-
48
+ #[cfg(target_os = "macos")]
20
49
  first_menu.add_native_item(MenuItem::Hide);
50
+ #[cfg(target_os = "macos")]
21
51
  first_menu.add_native_item(MenuItem::EnterFullScreen);
52
+ #[cfg(target_os = "macos")]
22
53
  first_menu.add_native_item(MenuItem::Minimize);
54
+ #[cfg(target_os = "macos")]
23
55
  first_menu.add_native_item(MenuItem::Separator);
56
+ #[cfg(target_os = "macos")]
24
57
  first_menu.add_native_item(MenuItem::Copy);
58
+ #[cfg(target_os = "macos")]
25
59
  first_menu.add_native_item(MenuItem::Cut);
60
+ #[cfg(target_os = "macos")]
26
61
  first_menu.add_native_item(MenuItem::Paste);
62
+ #[cfg(target_os = "macos")]
27
63
  first_menu.add_native_item(MenuItem::Undo);
64
+ #[cfg(target_os = "macos")]
28
65
  first_menu.add_native_item(MenuItem::Redo);
66
+ #[cfg(target_os = "macos")]
29
67
  first_menu.add_native_item(MenuItem::SelectAll);
68
+ #[cfg(target_os = "macos")]
30
69
  first_menu.add_native_item(MenuItem::Separator);
70
+
71
+ #[cfg(target_os = "macos")]
31
72
  let close_item = first_menu.add_item(
32
73
  MenuItemAttributes::new("CloseWindow")
33
74
  .with_accelerators(&Accelerator::new(SysMods::Cmd, KeyCode::KeyW)),
34
75
  );
76
+
77
+ #[cfg(target_os = "macos")]
35
78
  first_menu.add_native_item(MenuItem::Quit);
36
79
 
80
+ #[cfg(target_os = "macos")]
37
81
  menu_bar_menu.add_submenu("App", true, first_menu);
38
-
82
+ #[cfg(target_os = "linux")]
83
+ let WindowConfig {
84
+ url,
85
+ width,
86
+ height,
87
+ resizable,
88
+ fullscreen,
89
+ ..
90
+ } = get_windows_config().unwrap_or_default();
91
+ #[cfg(target_os = "windows")]
92
+ let WindowConfig {
93
+ url,
94
+ width,
95
+ height,
96
+ resizable,
97
+ fullscreen,
98
+ ..
99
+ } = get_windows_config().unwrap_or_default();
100
+ #[cfg(target_os = "macos")]
39
101
  let WindowConfig {
40
102
  url,
41
103
  width,
@@ -44,21 +106,40 @@ fn main() -> wry::Result<()> {
44
106
  transparent,
45
107
  fullscreen,
46
108
  ..
47
- } = get_windows_config();
109
+ } = get_windows_config().unwrap_or_default();
48
110
  let event_loop = EventLoop::new();
49
- let window = WindowBuilder::new()
111
+
112
+ let common_window = WindowBuilder::new()
50
113
  .with_resizable(resizable)
51
- .with_titlebar_transparent(transparent)
52
114
  .with_fullscreen(if fullscreen {
53
115
  Some(Fullscreen::Borderless(None))
54
116
  } else {
55
117
  None
56
118
  })
119
+ .with_inner_size(wry::application::dpi::LogicalSize::new(width, height));
120
+ #[cfg(target_os = "windows")]
121
+ let icon_path = concat!(env!("CARGO_MANIFEST_DIR"), "/png/weread_32.ico");
122
+ #[cfg(target_os = "windows")]
123
+ let icon = load_icon(std::path::Path::new(icon_path));
124
+
125
+ #[cfg(target_os = "windows")]
126
+ let window = common_window
127
+ .with_decorations(true)
128
+ .with_title("")
129
+ .with_window_icon(Some(icon))
130
+ .build(&event_loop)
131
+ .unwrap();
132
+
133
+ #[cfg(target_os = "linux")]
134
+ let window = common_window.with_title("").build(&event_loop).unwrap();
135
+
136
+ #[cfg(target_os = "macos")]
137
+ let window = common_window
57
138
  .with_fullsize_content_view(true)
58
139
  .with_titlebar_buttons_hidden(false)
140
+ .with_titlebar_transparent(transparent)
59
141
  .with_title_hidden(true)
60
142
  .with_menu(menu_bar_menu)
61
- .with_inner_size(wry::application::dpi::LogicalSize::new(width, height))
62
143
  .build(&event_loop)
63
144
  .unwrap();
64
145
 
@@ -71,17 +152,25 @@ fn main() -> wry::Result<()> {
71
152
  } else {
72
153
  window.set_fullscreen(Some(Fullscreen::Borderless(None)));
73
154
  }
155
+ } else if req.starts_with("open_browser") {
156
+ let href = req.replace("open_browser:", "");
157
+ webbrowser::open(&href).expect("no browser");
74
158
  }
75
159
  };
76
160
 
77
- let _webview = WebViewBuilder::new(window)?
161
+ let webview = WebViewBuilder::new(window)?
78
162
  .with_url(&url.to_string())?
79
- // .with_devtools(true)
163
+ .with_devtools(cfg!(feature = "devtools"))
80
164
  .with_initialization_script(include_str!("pake.js"))
81
165
  .with_ipc_handler(handler)
166
+ .with_back_forward_navigation_gestures(true)
82
167
  .build()?;
83
168
 
84
- // _webview.open_devtools();
169
+ #[cfg(feature = "devtools")]
170
+ {
171
+ webview.open_devtools();
172
+ }
173
+
85
174
  event_loop.run(move |event, _, control_flow| {
86
175
  *control_flow = ControlFlow::Wait;
87
176
 
@@ -96,19 +185,35 @@ fn main() -> wry::Result<()> {
96
185
  origin: MenuType::MenuBar,
97
186
  ..
98
187
  } => {
188
+ #[cfg(target_os = "macos")]
99
189
  if menu_id == close_item.clone().id() {
100
- _webview.window().set_minimized(true);
190
+ webview.window().set_minimized(true);
101
191
  }
102
192
  println!("Clicked on {:?}", menu_id);
193
+ println!("Clicked on {:?}", webview.window().is_visible());
103
194
  }
104
195
  _ => (),
105
196
  }
106
197
  });
107
198
  }
108
199
 
109
- fn get_windows_config() -> WindowConfig {
200
+ fn get_windows_config() -> Option<WindowConfig> {
110
201
  let config_file = include_str!("../tauri.conf.json");
111
202
  let config: Config = serde_json::from_str(config_file).expect("failed to parse windows config");
112
203
 
113
- config.tauri.windows[0].clone()
204
+ config.tauri.windows.first().cloned()
205
+ }
206
+
207
+ #[cfg(target_os = "windows")]
208
+ fn load_icon(path: &std::path::Path) -> Icon {
209
+ let (icon_rgba, icon_width, icon_height) = {
210
+ // alternatively, you can embed the icon in the binary through `include_bytes!` macro and use `image::load_from_memory`
211
+ let image = image::open(path)
212
+ .expect("Failed to open icon path")
213
+ .into_rgba8();
214
+ let (width, height) = image.dimensions();
215
+ let rgba = image.into_raw();
216
+ (rgba, width, height)
217
+ };
218
+ Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
114
219
  }
@@ -1,6 +1,60 @@
1
- window.addEventListener('DOMContentLoaded', (_event) => {
2
- const style = document.createElement('style');
3
- style.innerHTML = `
1
+ /**
2
+ * @typedef {string} KeyboardKey `event.key` 的代号,
3
+ * 见 <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>
4
+ * @typedef {() => void} OnKeyDown 使用者按下 [CtrlKey] 或者 ⌘ [KeyboardKey]时应该执行的行为
5
+ * 以 Ctrl键或者Meta 键 (⌘) 为首的快捷键清单。
6
+ * 每个写在这里的 shortcuts 都会运行 {@link Event.preventDefault}.
7
+ * @type {Record<KeyboardKey, OnKeyDown>}
8
+ */
9
+
10
+ const metaKeyShortcuts = {
11
+ ArrowUp: () => scrollTo(0, 0),
12
+ ArrowDown: () => scrollTo(0, document.body.scrollHeight),
13
+ "[": () => window.history.back(),
14
+ "]": () => window.history.forward(),
15
+ r: () => window.location.reload(),
16
+ "-": () => zoomOut(),
17
+ "=": () => zoomIn(),
18
+ "+": () => zoomIn(),
19
+ 0: () => zoomCommon(() => "100%"),
20
+ };
21
+
22
+ const ctrlKeyShortcuts = {
23
+ ArrowUp: () => scrollTo(0, 0),
24
+ ArrowDown: () => scrollTo(0, document.body.scrollHeight),
25
+ ArrowLeft: () => window.history.back(),
26
+ ArrowRight: () => window.history.forward(),
27
+ r: () => window.location.reload(),
28
+ "-": () => zoomOut(),
29
+ "=": () => zoomIn(),
30
+ "+": () => zoomIn(),
31
+ 0: () => zoomCommon(() => "100%"),
32
+ };
33
+
34
+ window.addEventListener("DOMContentLoaded", (_event) => {
35
+ const style = document.createElement("style");
36
+ style.innerHTML = `
37
+ #page #footer-wrapper,
38
+ .drawing-board .toolbar .toolbar-action,
39
+ .c-swiper-container,
40
+ .download_entry,
41
+ .lang, .copyright,
42
+ .wwads-cn, .adsbygoogle,
43
+ #Bottom > div.content > div.inner,
44
+ #Rightbar .sep20:nth-of-type(5),
45
+ #Rightbar > div.box:nth-child(4),
46
+ #Main > div.box:nth-child(8) > div
47
+ #Wrapper > div.sep20,
48
+ #Main > div.box:nth-child(8),
49
+ #masthead-ad,
50
+ #Rightbar > div:nth-child(6) > div.sidebar_compliance {
51
+ display: none !important;
52
+ }
53
+
54
+ #page .main_header {
55
+ padding-top: 20px;
56
+ }
57
+
4
58
  .panel.give_me .nav_view {
5
59
  top: 154px !important;
6
60
  }
@@ -9,16 +63,21 @@ window.addEventListener('DOMContentLoaded', (_event) => {
9
63
  padding-top: 30px;
10
64
  }
11
65
 
12
- #page .main_header {
13
- padding-top: 20px;
66
+ ytd-masthead>#container.style-scope.ytd-masthead {
67
+ padding-top: 12px !important;
14
68
  }
15
69
 
16
- #page #footer-wrapper,
17
- .drawing-board .toolbar .toolbar-action,
18
- .c-swiper-container,
19
- .download_entry,
20
- .lang, .copyright {
21
- display: none !important;
70
+ .wrap.h1body-exist.max-container > div.menu-tocs > div.menu-btn{
71
+ top: 28px;
72
+ }
73
+
74
+ #Wrapper{
75
+ background-color: #F8F8F8 !important;
76
+ background-image:none !important;
77
+ }
78
+
79
+ #Top {
80
+ border-bottom: none;
22
81
  }
23
82
 
24
83
  .container-with-note #home, .container-with-note #switcher{
@@ -46,85 +105,89 @@ window.addEventListener('DOMContentLoaded', (_event) => {
46
105
  height: 20px;
47
106
  cursor: grab;
48
107
  cursor: -webkit-grab;
108
+ z-index: 90000;
49
109
  }
50
110
  `;
51
- document.head.append(style);
52
- const topDom = document.createElement("div");
53
- topDom.id = "pack-top-dom"
54
- document.body.appendChild(topDom);
55
-
56
- const domEl = document.getElementById('pack-top-dom');
57
-
58
- domEl.addEventListener('mousedown', (e) => {
59
- if (e.buttons === 1 && e.detail !== 2) {
60
- window.ipc.postMessage('drag_window');
61
- }
62
- })
63
-
64
- domEl.addEventListener('touchstart', (e) => {
65
- window.ipc.postMessage('drag_window');
66
- })
67
-
68
- domEl.addEventListener('dblclick', (e) => {
69
- window.ipc.postMessage('fullscreen');
70
- })
71
-
72
- document.addEventListener('keyup', function (event) {
73
- if (event.key === "ArrowUp" && event.metaKey){
74
- scrollTo(0,0);
75
- }
76
- if (event.key === "ArrowDown" && event.metaKey){
77
- window.scrollTo(0, document.body.scrollHeight);
78
- }
79
- if (event.key === "[" && event.metaKey){
80
- window.history.go(-1);
81
- }
82
- if (event.key === "]" && event.metaKey){
83
- window.history.go(1);
84
- }
85
- if (event.key === "r" && event.metaKey){
86
- window.location.reload();
87
- }
88
- if (event.key === "-" && event.metaKey){
89
- zoomOut();
90
- }
91
- if (event.key === "=" && event.metaKey){
92
- zoomIn();
93
- }
94
- if (event.key === "0" && event.metaKey){
95
- document.getElementsByTagName('html')[0].style.zoom = '100%';
96
- window.localStorage.setItem('htmlZoom', '100%');
97
- }
98
- })
99
-
100
- const pakeLinks = document.links;
101
- for (let linkIndex = 0; linkIndex < pakeLinks.length; linkIndex++) {
102
- pakeLinks[linkIndex].target = '_self';
103
- }
111
+ document.head.append(style);
112
+ const topDom = document.createElement("div");
113
+ topDom.id = "pack-top-dom";
114
+ document.body.appendChild(topDom);
115
+
116
+ const domEl = document.getElementById("pack-top-dom");
104
117
 
118
+ domEl.addEventListener("mousedown", (e) => {
119
+ if (e.buttons === 1 && e.detail !== 2) {
120
+ window.ipc.postMessage("drag_window");
121
+ }
122
+ });
123
+
124
+ domEl.addEventListener("touchstart", () => {
125
+ window.ipc.postMessage("drag_window");
126
+ });
127
+
128
+ domEl.addEventListener("dblclick", () => {
129
+ window.ipc.postMessage("fullscreen");
130
+ });
131
+
132
+ document.addEventListener("keyup", function (event) {
133
+ const preventDefault = (f) => {
134
+ event.preventDefault();
135
+ f();
136
+ };
137
+ if (/windows|linux/i.test(navigator.userAgent)) {
138
+ if (event.ctrlKey && event.key in ctrlKeyShortcuts) {
139
+ preventDefault(ctrlKeyShortcuts[event.key]);
140
+ }
141
+ }
142
+ if (/macintosh|mac os x/i.test(navigator.userAgent)) {
143
+ if (event.metaKey && event.key in metaKeyShortcuts) {
144
+ preventDefault(ctrlKeyShortcuts[event.key]);
145
+ }
146
+ }
147
+ });
148
+
149
+ document.addEventListener("click", (e) => {
150
+ const origin = e.target.closest("a");
151
+ if (origin && origin.href) {
152
+ origin.target = "_self";
153
+
154
+ //额外处理下 twitter 的外跳,对于其他需要外跳的可以改这里成对应域名
155
+ const href = origin.href;
156
+ if (
157
+ location.host === "twitter.com" &&
158
+ href.indexOf("twitter.com") === -1
159
+ ) {
160
+ e.preventDefault();
161
+ window.ipc.postMessage(`open_browser:${href}`);
162
+ }
163
+ }
164
+ });
105
165
  });
106
166
 
107
167
  setDefaultZoom();
108
168
 
109
169
  function setDefaultZoom() {
110
- const htmlZoom = window.localStorage.getItem('htmlZoom');
170
+ const htmlZoom = window.localStorage.getItem("htmlZoom");
111
171
  if (htmlZoom) {
112
- document.getElementsByTagName('html')[0].style.zoom = htmlZoom;
172
+ document.getElementsByTagName("html")[0].style.zoom = htmlZoom;
113
173
  }
114
174
  }
115
175
 
116
- function zoomIn() {
117
- const htmlZoom = window.localStorage.getItem('htmlZoom') || '100%';
118
- const html = document.getElementsByTagName('html')[0];
119
- const zoom = parseInt(htmlZoom) < 200 ? (parseInt(htmlZoom) + 10 +'%') : '200%';
176
+ /**
177
+ * @param {(htmlZoom: string) => string} [zoomRule]
178
+ */
179
+ function zoomCommon(zoomRule) {
180
+ const htmlZoom = window.localStorage.getItem("htmlZoom") || "100%";
181
+ const html = document.getElementsByTagName("html")[0];
182
+ const zoom = zoomRule(htmlZoom);
120
183
  html.style.zoom = zoom;
121
- window.localStorage.setItem('htmlZoom', zoom);
184
+ window.localStorage.setItem("htmlZoom", zoom);
185
+ }
186
+
187
+ function zoomIn() {
188
+ zoomCommon((htmlZoom) => `${Math.min(parseInt(htmlZoom) + 10, 200)}%`);
122
189
  }
123
190
 
124
191
  function zoomOut() {
125
- const htmlZoom = window.localStorage.getItem('htmlZoom') || '100%';
126
- const html = document.getElementsByTagName('html')[0];
127
- const zoom = parseInt(htmlZoom) > 30 ? (parseInt(htmlZoom) - 10 +'%') : '30%';
128
- html.style.zoom = zoom;
129
- window.localStorage.setItem('htmlZoom', zoom);
130
- }
192
+ zoomCommon((htmlZoom) => `${Math.max(parseInt(htmlZoom) - 10, 30)}%`);
193
+ }