pake-cli 3.1.1 → 3.2.0-beta1

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 (82) hide show
  1. package/README.md +82 -25
  2. package/dist/cli.js +148 -81
  3. package/dist/dev.js +39 -20
  4. package/dist/dev.js.map +1 -1
  5. package/package.json +15 -17
  6. package/src-tauri/.cargo/config.toml +5 -0
  7. package/src-tauri/Cargo.lock +1337 -1111
  8. package/src-tauri/Cargo.toml +12 -12
  9. package/src-tauri/assets/{com-tw93-weread.desktop → com-tw93-weekly.desktop} +4 -4
  10. package/src-tauri/gen/schemas/acl-manifests.json +1 -1
  11. package/src-tauri/gen/schemas/desktop-schema.json +906 -421
  12. package/src-tauri/gen/schemas/macOS-schema.json +906 -421
  13. package/src-tauri/icons/deepseek.icns +0 -0
  14. package/src-tauri/icons/grok.icns +0 -0
  15. package/src-tauri/icons/weekly.icns +0 -0
  16. package/src-tauri/pake.json +3 -2
  17. package/src-tauri/png/chatgpt_256.ico +0 -0
  18. package/src-tauri/png/chatgpt_32.ico +0 -0
  19. package/src-tauri/png/chatgpt_512.png +0 -0
  20. package/src-tauri/png/deepseek_256.ico +0 -0
  21. package/src-tauri/png/deepseek_32.ico +0 -0
  22. package/src-tauri/png/deepseek_512.png +0 -0
  23. package/src-tauri/png/excalidraw_256.ico +0 -0
  24. package/src-tauri/png/excalidraw_32.ico +0 -0
  25. package/src-tauri/png/excalidraw_512.png +0 -0
  26. package/src-tauri/png/flomo_256.ico +0 -0
  27. package/src-tauri/png/flomo_32.ico +0 -0
  28. package/src-tauri/png/flomo_512.png +0 -0
  29. package/src-tauri/png/gemini_256.ico +0 -0
  30. package/src-tauri/png/gemini_32.ico +0 -0
  31. package/src-tauri/png/gemini_512.png +0 -0
  32. package/src-tauri/png/grok_256.ico +0 -0
  33. package/src-tauri/png/grok_32.ico +0 -0
  34. package/src-tauri/png/grok_512.png +0 -0
  35. package/src-tauri/png/icon_256.ico +0 -0
  36. package/src-tauri/png/icon_32.ico +0 -0
  37. package/src-tauri/png/icon_512.png +0 -0
  38. package/src-tauri/png/lizhi_256.ico +0 -0
  39. package/src-tauri/png/lizhi_32.ico +0 -0
  40. package/src-tauri/png/lizhi_512.png +0 -0
  41. package/src-tauri/png/programmusic_256.ico +0 -0
  42. package/src-tauri/png/programmusic_32.ico +0 -0
  43. package/src-tauri/png/programmusic_512.png +0 -0
  44. package/src-tauri/png/qwerty_256.ico +0 -0
  45. package/src-tauri/png/qwerty_32.ico +0 -0
  46. package/src-tauri/png/qwerty_512.png +0 -0
  47. package/src-tauri/png/twitter_256.ico +0 -0
  48. package/src-tauri/png/twitter_32.ico +0 -0
  49. package/src-tauri/png/twitter_512.png +0 -0
  50. package/src-tauri/png/wechat_256.ico +0 -0
  51. package/src-tauri/png/wechat_32.ico +0 -0
  52. package/src-tauri/png/wechat_512.png +0 -0
  53. package/src-tauri/png/weekly_256.ico +0 -0
  54. package/src-tauri/png/weekly_32.ico +0 -0
  55. package/src-tauri/png/weekly_512.png +0 -0
  56. package/src-tauri/png/weread_256.ico +0 -0
  57. package/src-tauri/png/weread_32.ico +0 -0
  58. package/src-tauri/png/weread_512.png +0 -0
  59. package/src-tauri/png/xiaohongshu_256.ico +0 -0
  60. package/src-tauri/png/xiaohongshu_32.ico +0 -0
  61. package/src-tauri/png/xiaohongshu_512.png +0 -0
  62. package/src-tauri/png/youtube_256.ico +0 -0
  63. package/src-tauri/png/youtube_32.ico +0 -0
  64. package/src-tauri/png/youtube_512.png +0 -0
  65. package/src-tauri/png/youtubemusic_256.ico +0 -0
  66. package/src-tauri/png/youtubemusic_32.ico +0 -0
  67. package/src-tauri/png/youtubemusic_512.png +0 -0
  68. package/src-tauri/src/app/config.rs +1 -0
  69. package/src-tauri/src/app/setup.rs +3 -1
  70. package/src-tauri/src/app/window.rs +25 -3
  71. package/src-tauri/src/inject/component.js +6 -5
  72. package/src-tauri/src/inject/event.js +143 -62
  73. package/src-tauri/src/inject/style.js +29 -8
  74. package/src-tauri/src/lib.rs +18 -12
  75. package/src-tauri/src/util.rs +4 -4
  76. package/src-tauri/tauri.conf.json +3 -3
  77. package/src-tauri/tauri.linux.conf.json +4 -2
  78. package/src-tauri/tauri.macos.conf.json +1 -1
  79. package/src-tauri/tauri.windows.conf.json +2 -2
  80. package/src-tauri/.pake/pake.json +0 -30
  81. package/src-tauri/.pake/tauri.conf.json +0 -24
  82. package/src-tauri/.pake/tauri.macos.conf.json +0 -15
Binary file
Binary file
Binary file
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "windows": [
3
3
  {
4
- "url": "https://weread.qq.com",
4
+ "url": "https://weekly.tw93.fun/",
5
5
  "url_type": "web",
6
6
  "hide_title_bar": true,
7
7
  "fullscreen": false,
@@ -11,7 +11,8 @@
11
11
  "always_on_top": false,
12
12
  "dark_mode": false,
13
13
  "activation_shortcut": "",
14
- "disabled_web_shortcuts": false
14
+ "disabled_web_shortcuts": false,
15
+ "hide_on_close": true
15
16
  }
16
17
  ],
17
18
  "user_agent": {
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
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
@@ -13,6 +13,7 @@ pub struct WindowConfig {
13
13
  pub dark_mode: bool,
14
14
  pub disabled_web_shortcuts: bool,
15
15
  pub activation_shortcut: String,
16
+ pub hide_on_close: bool,
16
17
  }
17
18
 
18
19
  #[derive(Debug, Serialize, Deserialize)]
@@ -67,7 +67,9 @@ pub fn set_global_shortcut(app: &AppHandle, shortcut: String) -> tauri::Result<(
67
67
  let last_triggered = Arc::clone(&last_triggered);
68
68
  move |app, event, _shortcut| {
69
69
  let mut last_triggered = last_triggered.lock().unwrap();
70
- if Instant::now().duration_since(*last_triggered) < Duration::from_millis(300) {
70
+ if Instant::now().duration_since(*last_triggered)
71
+ < Duration::from_millis(300)
72
+ {
71
73
  return;
72
74
  }
73
75
  *last_triggered = Instant::now();
@@ -6,6 +6,9 @@ use tauri::{App, Config, Url, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
6
6
  #[cfg(target_os = "macos")]
7
7
  use tauri::{Theme, TitleBarStyle};
8
8
 
9
+ #[cfg(target_os = "windows")]
10
+ use tauri::Theme;
11
+
9
12
  pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) -> WebviewWindow {
10
13
  let package_name = tauri_config.clone().product_name.unwrap();
11
14
  let _data_dir = get_data_dir(app.handle(), package_name);
@@ -43,9 +46,13 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
43
46
  .initialization_script(include_str!("../inject/style.js"))
44
47
  .initialization_script(include_str!("../inject/custom.js"));
45
48
 
49
+ // Configure proxy if specified
46
50
  if !config.proxy_url.is_empty() {
47
- window_builder =
48
- window_builder.proxy_url(Url::from_str(config.proxy_url.as_str()).unwrap());
51
+ if let Ok(proxy_url) = Url::from_str(&config.proxy_url) {
52
+ window_builder = window_builder.proxy_url(proxy_url);
53
+ #[cfg(debug_assertions)]
54
+ println!("Proxy configured: {}", config.proxy_url);
55
+ }
49
56
  }
50
57
 
51
58
  #[cfg(target_os = "macos")]
@@ -62,11 +69,26 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
62
69
  }
63
70
  }
64
71
 
65
- #[cfg(not(target_os = "macos"))]
72
+ #[cfg(target_os = "windows")]
73
+ {
74
+ window_builder = window_builder
75
+ .data_directory(_data_dir)
76
+ .title(app.package_info().name.clone());
77
+
78
+ // Set theme to None for automatic system theme detection on Windows
79
+ // This allows the window to respond to system theme changes automatically
80
+ window_builder = window_builder.theme(None);
81
+ }
82
+
83
+ #[cfg(target_os = "linux")]
66
84
  {
67
85
  window_builder = window_builder
68
86
  .data_directory(_data_dir)
69
87
  .title(app.package_info().name.clone());
88
+
89
+ // Set theme to None for automatic system theme detection on Linux
90
+ // This allows the window to respond to system theme changes automatically
91
+ window_builder = window_builder.theme(None);
70
92
  }
71
93
 
72
94
  window_builder.build().expect("Failed to build window")
@@ -1,15 +1,16 @@
1
- document.addEventListener('DOMContentLoaded', () => {
1
+ document.addEventListener("DOMContentLoaded", () => {
2
2
  // Toast
3
3
  function pakeToast(msg) {
4
- const m = document.createElement('div');
4
+ const m = document.createElement("div");
5
5
  m.innerHTML = msg;
6
6
  m.style.cssText =
7
- 'max-width:60%;min-width: 80px;padding:0 12px;height: 32px;color: rgb(255, 255, 255);line-height: 32px;text-align: center;border-radius: 8px;position: fixed; bottom:24px;right: 28px;z-index: 999999;background: rgba(0, 0, 0,.8);font-size: 13px;';
7
+ "max-width:60%;min-width: 80px;padding:0 12px;height: 32px;color: rgb(255, 255, 255);line-height: 32px;text-align: center;border-radius: 8px;position: fixed; bottom:24px;right: 28px;z-index: 999999;background: rgba(0, 0, 0,.8);font-size: 13px;";
8
8
  document.body.appendChild(m);
9
9
  setTimeout(function () {
10
10
  const d = 0.5;
11
- m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
12
- m.style.opacity = '0';
11
+ m.style.transition =
12
+ "transform " + d + "s ease-in, opacity " + d + "s ease-in";
13
+ m.style.opacity = "0";
13
14
  setTimeout(function () {
14
15
  document.body.removeChild(m);
15
16
  }, d * 1000);
@@ -1,32 +1,32 @@
1
1
  const shortcuts = {
2
- '[': () => window.history.back(),
3
- ']': () => window.history.forward(),
4
- '-': () => zoomOut(),
5
- '=': () => zoomIn(),
6
- '+': () => zoomIn(),
7
- 0: () => setZoom('100%'),
2
+ "[": () => window.history.back(),
3
+ "]": () => window.history.forward(),
4
+ "-": () => zoomOut(),
5
+ "=": () => zoomIn(),
6
+ "+": () => zoomIn(),
7
+ 0: () => setZoom("100%"),
8
8
  r: () => window.location.reload(),
9
9
  ArrowUp: () => scrollTo(0, 0),
10
10
  ArrowDown: () => scrollTo(0, document.body.scrollHeight),
11
11
  };
12
12
 
13
13
  function setZoom(zoom) {
14
- const html = document.getElementsByTagName('html')[0];
14
+ const html = document.getElementsByTagName("html")[0];
15
15
  html.style.zoom = zoom;
16
- window.localStorage.setItem('htmlZoom', zoom);
16
+ window.localStorage.setItem("htmlZoom", zoom);
17
17
  }
18
18
 
19
19
  function zoomCommon(zoomChange) {
20
- const currentZoom = window.localStorage.getItem('htmlZoom') || '100%';
20
+ const currentZoom = window.localStorage.getItem("htmlZoom") || "100%";
21
21
  setZoom(zoomChange(currentZoom));
22
22
  }
23
23
 
24
24
  function zoomIn() {
25
- zoomCommon(currentZoom => `${Math.min(parseInt(currentZoom) + 10, 200)}%`);
25
+ zoomCommon((currentZoom) => `${Math.min(parseInt(currentZoom) + 10, 200)}%`);
26
26
  }
27
27
 
28
28
  function zoomOut() {
29
- zoomCommon(currentZoom => `${Math.max(parseInt(currentZoom) - 10, 30)}%`);
29
+ zoomCommon((currentZoom) => `${Math.max(parseInt(currentZoom) - 10, 30)}%`);
30
30
  }
31
31
 
32
32
  function handleShortcut(event) {
@@ -47,42 +47,45 @@ function isDownloadLink(url) {
47
47
  'svg', 'swf', 'tar', 'tif', 'tiff', 'ts', 'txt', 'wav', 'webm', 'webp',
48
48
  'wma', 'wmv', 'xls', 'xlsx', 'xml', 'zip', 'json', 'yaml', '7zip', 'mkv',
49
49
  ];
50
- const downloadLinkPattern = new RegExp(`\\.(${fileExtensions.join('|')})$`, 'i');
50
+ const downloadLinkPattern = new RegExp(
51
+ `\\.(${fileExtensions.join("|")})$`,
52
+ "i",
53
+ );
51
54
  return downloadLinkPattern.test(url);
52
55
  }
53
56
 
54
- document.addEventListener('DOMContentLoaded', () => {
57
+ document.addEventListener("DOMContentLoaded", () => {
55
58
  const tauri = window.__TAURI__;
56
59
  const appWindow = tauri.window.getCurrentWindow();
57
60
  const invoke = tauri.core.invoke;
58
61
 
59
- if (!document.getElementById('pake-top-dom')) {
60
- const topDom = document.createElement('div');
61
- topDom.id = 'pake-top-dom';
62
+ if (!document.getElementById("pake-top-dom")) {
63
+ const topDom = document.createElement("div");
64
+ topDom.id = "pake-top-dom";
62
65
  document.body.appendChild(topDom);
63
66
  }
64
67
 
65
- const domEl = document.getElementById('pake-top-dom');
68
+ const domEl = document.getElementById("pake-top-dom");
66
69
 
67
- domEl.addEventListener('touchstart', () => {
70
+ domEl.addEventListener("touchstart", () => {
68
71
  appWindow.startDragging();
69
72
  });
70
73
 
71
- domEl.addEventListener('mousedown', e => {
74
+ domEl.addEventListener("mousedown", (e) => {
72
75
  e.preventDefault();
73
76
  if (e.buttons === 1 && e.detail !== 2) {
74
77
  appWindow.startDragging();
75
78
  }
76
79
  });
77
80
 
78
- domEl.addEventListener('dblclick', () => {
79
- appWindow.isFullscreen().then(fullscreen => {
81
+ domEl.addEventListener("dblclick", () => {
82
+ appWindow.isFullscreen().then((fullscreen) => {
80
83
  appWindow.setFullscreen(!fullscreen);
81
84
  });
82
85
  });
83
86
 
84
- if (window['pakeConfig']?.disabled_web_shortcuts !== true) {
85
- document.addEventListener('keyup', event => {
87
+ if (window["pakeConfig"]?.disabled_web_shortcuts !== true) {
88
+ document.addEventListener("keyup", (event) => {
86
89
  if (/windows|linux/i.test(navigator.userAgent) && event.ctrlKey) {
87
90
  handleShortcut(event);
88
91
  }
@@ -96,7 +99,7 @@ document.addEventListener('DOMContentLoaded', () => {
96
99
  function collectUrlToBlobs() {
97
100
  const backupCreateObjectURL = window.URL.createObjectURL;
98
101
  window.blobToUrlCaches = new Map();
99
- window.URL.createObjectURL = blob => {
102
+ window.URL.createObjectURL = (blob) => {
100
103
  const url = backupCreateObjectURL.call(window.URL, blob);
101
104
  window.blobToUrlCaches.set(url, blob);
102
105
  return url;
@@ -104,7 +107,7 @@ document.addEventListener('DOMContentLoaded', () => {
104
107
  }
105
108
 
106
109
  function convertBlobUrlToBinary(blobUrl) {
107
- return new Promise(resolve => {
110
+ return new Promise((resolve) => {
108
111
  const blob = window.blobToUrlCaches.get(blobUrl);
109
112
  const reader = new FileReader();
110
113
 
@@ -116,7 +119,7 @@ document.addEventListener('DOMContentLoaded', () => {
116
119
  }
117
120
 
118
121
  function downloadFromDataUri(dataURI, filename) {
119
- const byteString = atob(dataURI.split(',')[1]);
122
+ const byteString = atob(dataURI.split(",")[1]);
120
123
  // write the bytes of the string to an ArrayBuffer
121
124
  const bufferArray = new ArrayBuffer(byteString.length);
122
125
 
@@ -129,7 +132,7 @@ document.addEventListener('DOMContentLoaded', () => {
129
132
  }
130
133
 
131
134
  // write the ArrayBuffer to a binary, and you're done
132
- invoke('download_file_by_binary', {
135
+ invoke("download_file_by_binary", {
133
136
  params: {
134
137
  filename,
135
138
  binary: Array.from(binary),
@@ -138,8 +141,8 @@ document.addEventListener('DOMContentLoaded', () => {
138
141
  }
139
142
 
140
143
  function downloadFromBlobUrl(blobUrl, filename) {
141
- convertBlobUrlToBinary(blobUrl).then(binary => {
142
- invoke('download_file_by_binary', {
144
+ convertBlobUrlToBinary(blobUrl).then((binary) => {
145
+ invoke("download_file_by_binary", {
143
146
  params: {
144
147
  filename,
145
148
  binary,
@@ -151,20 +154,20 @@ document.addEventListener('DOMContentLoaded', () => {
151
154
  // detect blob download by createElement("a")
152
155
  function detectDownloadByCreateAnchor() {
153
156
  const createEle = document.createElement;
154
- document.createElement = el => {
155
- if (el !== 'a') return createEle.call(document, el);
157
+ document.createElement = (el) => {
158
+ if (el !== "a") return createEle.call(document, el);
156
159
  const anchorEle = createEle.call(document, el);
157
160
 
158
161
  // use addEventListener to avoid overriding the original click event.
159
162
  anchorEle.addEventListener(
160
- 'click',
161
- e => {
163
+ "click",
164
+ (e) => {
162
165
  const url = anchorEle.href;
163
166
  const filename = anchorEle.download || getFilenameFromUrl(url);
164
167
  if (window.blobToUrlCaches.has(url)) {
165
168
  downloadFromBlobUrl(url, filename);
166
169
  // case: download from dataURL -> convert dataURL ->
167
- } else if (url.startsWith('data:')) {
170
+ } else if (url.startsWith("data:")) {
168
171
  downloadFromDataUri(url, filename);
169
172
  }
170
173
  },
@@ -176,18 +179,42 @@ document.addEventListener('DOMContentLoaded', () => {
176
179
  }
177
180
 
178
181
  // process special download protocol['data:','blob:']
179
- const isSpecialDownload = url => ['blob', 'data'].some(protocol => url.startsWith(protocol));
182
+ const isSpecialDownload = (url) =>
183
+ ["blob", "data"].some((protocol) => url.startsWith(protocol));
180
184
 
181
- const isDownloadRequired = (url, anchorElement, e) => anchorElement.download || e.metaKey || e.ctrlKey || isDownloadLink(url);
185
+ const isDownloadRequired = (url, anchorElement, e) =>
186
+ anchorElement.download || e.metaKey || e.ctrlKey || isDownloadLink(url);
182
187
 
183
- const handleExternalLink = url => {
184
- invoke('plugin:shell|open', {
188
+ const handleExternalLink = (url) => {
189
+ invoke("plugin:shell|open", {
185
190
  path: url,
186
191
  });
187
192
  };
188
193
 
189
- const detectAnchorElementClick = e => {
190
- const anchorElement = e.target.closest('a');
194
+ // Check if URL belongs to the same domain (including subdomains)
195
+ const isSameDomain = (url) => {
196
+ try {
197
+ const linkUrl = new URL(url);
198
+ const currentUrl = new URL(window.location.href);
199
+
200
+ if (linkUrl.hostname === currentUrl.hostname) return true;
201
+
202
+ // Extract root domain (e.g., bilibili.com from www.bilibili.com)
203
+ const getRootDomain = (hostname) => {
204
+ const parts = hostname.split(".");
205
+ return parts.length >= 2 ? parts.slice(-2).join(".") : hostname;
206
+ };
207
+
208
+ return (
209
+ getRootDomain(currentUrl.hostname) === getRootDomain(linkUrl.hostname)
210
+ );
211
+ } catch (e) {
212
+ return false;
213
+ }
214
+ };
215
+
216
+ const detectAnchorElementClick = (e) => {
217
+ const anchorElement = e.target.closest("a");
191
218
 
192
219
  if (anchorElement && anchorElement.href) {
193
220
  const target = anchorElement.target;
@@ -195,28 +222,61 @@ document.addEventListener('DOMContentLoaded', () => {
195
222
  const absoluteUrl = hrefUrl.href;
196
223
  let filename = anchorElement.download || getFilenameFromUrl(absoluteUrl);
197
224
 
198
- // Handling external link redirection, _blank will automatically open.
199
- if (target === '_blank') {
225
+ // Handle _blank links: same domain navigates in-app, cross-domain opens new window
226
+ if (target === "_blank") {
200
227
  e.preventDefault();
228
+ e.stopImmediatePropagation();
229
+
230
+ if (isSameDomain(absoluteUrl)) {
231
+ window.location.href = absoluteUrl;
232
+ } else {
233
+ const newWindow = originalWindowOpen.call(
234
+ window,
235
+ absoluteUrl,
236
+ "_blank",
237
+ "width=1200,height=800,scrollbars=yes,resizable=yes",
238
+ );
239
+ if (!newWindow) handleExternalLink(absoluteUrl);
240
+ }
201
241
  return;
202
242
  }
203
243
 
204
- if (target === '_new') {
244
+ if (target === "_new") {
205
245
  e.preventDefault();
206
246
  handleExternalLink(absoluteUrl);
207
247
  return;
208
248
  }
209
249
 
210
250
  // Process download links for Rust to handle.
211
- if (isDownloadRequired(absoluteUrl, anchorElement, e) && !isSpecialDownload(absoluteUrl)) {
251
+ if (
252
+ isDownloadRequired(absoluteUrl, anchorElement, e) &&
253
+ !isSpecialDownload(absoluteUrl)
254
+ ) {
212
255
  e.preventDefault();
213
- invoke('download_file', { params: { url: absoluteUrl, filename } });
256
+ e.stopImmediatePropagation();
257
+ invoke("download_file", { params: { url: absoluteUrl, filename } });
258
+ return;
259
+ }
260
+
261
+ // Handle regular links: same domain allows normal navigation, cross-domain opens new window
262
+ if (!target || target === "_self") {
263
+ if (!isSameDomain(absoluteUrl)) {
264
+ e.preventDefault();
265
+ e.stopImmediatePropagation();
266
+ const newWindow = originalWindowOpen.call(
267
+ window,
268
+ absoluteUrl,
269
+ "_blank",
270
+ "width=1200,height=800,scrollbars=yes,resizable=yes",
271
+ );
272
+ if (!newWindow) handleExternalLink(absoluteUrl);
273
+ }
214
274
  }
215
275
  }
216
276
  };
217
277
 
218
278
  // Prevent some special websites from executing in advance, before the click event is triggered.
219
- document.addEventListener('click', detectAnchorElementClick, true);
279
+ document.addEventListener("click", detectAnchorElementClick, true);
220
280
 
221
281
  collectUrlToBlobs();
222
282
  detectDownloadByCreateAnchor();
@@ -225,14 +285,35 @@ document.addEventListener('DOMContentLoaded', () => {
225
285
  const originalWindowOpen = window.open;
226
286
  window.open = function (url, name, specs) {
227
287
  // Apple login and google login
228
- if (name === 'AppleAuthentication') {
288
+ if (name === "AppleAuthentication") {
229
289
  //do nothing
230
- } else if (specs && (specs.includes('height=') || specs.includes('width='))) {
290
+ } else if (
291
+ specs &&
292
+ (specs.includes("height=") || specs.includes("width="))
293
+ ) {
231
294
  location.href = url;
232
295
  } else {
233
296
  const baseUrl = window.location.origin + window.location.pathname;
234
297
  const hrefUrl = new URL(url, baseUrl);
235
- handleExternalLink(hrefUrl.href);
298
+ const absoluteUrl = hrefUrl.href;
299
+
300
+ // Apply same domain logic as anchor links
301
+ if (isSameDomain(absoluteUrl)) {
302
+ // Same domain: navigate in app or open new window based on specs
303
+ if (name === "_blank" || !name) {
304
+ return originalWindowOpen.call(
305
+ window,
306
+ absoluteUrl,
307
+ "_blank",
308
+ "width=1200,height=800,scrollbars=yes,resizable=yes",
309
+ );
310
+ } else {
311
+ location.href = absoluteUrl;
312
+ }
313
+ } else {
314
+ // Cross domain: open in external browser
315
+ handleExternalLink(absoluteUrl);
316
+ }
236
317
  }
237
318
  // Call the original window.open function to maintain its normal functionality.
238
319
  return originalWindowOpen.call(window, url, name, specs);
@@ -247,27 +328,27 @@ document.addEventListener('DOMContentLoaded', () => {
247
328
 
248
329
  // Fix Chinese input method "Enter" on Safari
249
330
  document.addEventListener(
250
- 'keydown',
251
- e => {
331
+ "keydown",
332
+ (e) => {
252
333
  if (e.keyCode === 229) e.stopPropagation();
253
334
  },
254
335
  true,
255
336
  );
256
337
  });
257
338
 
258
- document.addEventListener('DOMContentLoaded', function () {
259
- let permVal = 'granted';
339
+ document.addEventListener("DOMContentLoaded", function () {
340
+ let permVal = "granted";
260
341
  window.Notification = function (title, options) {
261
342
  const { invoke } = window.__TAURI__.core;
262
- const body = options?.body || '';
263
- let icon = options?.icon || '';
343
+ const body = options?.body || "";
344
+ let icon = options?.icon || "";
264
345
 
265
346
  // If the icon is a relative path, convert to full path using URI
266
- if (icon.startsWith('/')) {
347
+ if (icon.startsWith("/")) {
267
348
  icon = window.location.origin + icon;
268
349
  }
269
350
 
270
- invoke('send_notification', {
351
+ invoke("send_notification", {
271
352
  params: {
272
353
  title,
273
354
  body,
@@ -276,19 +357,19 @@ document.addEventListener('DOMContentLoaded', function () {
276
357
  });
277
358
  };
278
359
 
279
- window.Notification.requestPermission = async () => 'granted';
360
+ window.Notification.requestPermission = async () => "granted";
280
361
 
281
- Object.defineProperty(window.Notification, 'permission', {
362
+ Object.defineProperty(window.Notification, "permission", {
282
363
  enumerable: true,
283
364
  get: () => permVal,
284
- set: v => {
365
+ set: (v) => {
285
366
  permVal = v;
286
367
  },
287
368
  });
288
369
  });
289
370
 
290
371
  function setDefaultZoom() {
291
- const htmlZoom = window.localStorage.getItem('htmlZoom');
372
+ const htmlZoom = window.localStorage.getItem("htmlZoom");
292
373
  if (htmlZoom) {
293
374
  setZoom(htmlZoom);
294
375
  }
@@ -296,5 +377,5 @@ function setDefaultZoom() {
296
377
 
297
378
  function getFilenameFromUrl(url) {
298
379
  const urlPath = new URL(url).pathname;
299
- return urlPath.substring(urlPath.lastIndexOf('/') + 1);
380
+ return urlPath.substring(urlPath.lastIndexOf("/") + 1);
300
381
  }