pake-cli 3.11.2 โ†’ 3.11.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pake-cli",
3
- "version": "3.11.2",
3
+ "version": "3.11.3",
4
4
  "description": "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with one command. ๐Ÿคฑ๐Ÿป ไธ€้”ฎๆ‰“ๅŒ…็ฝ‘้กต็”Ÿๆˆ่ฝป้‡ๆกŒ้ขๅบ”็”จใ€‚",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -73,7 +73,6 @@
73
73
  "@rollup/plugin-terser": "^0.4.4",
74
74
  "@types/fs-extra": "^11.0.4",
75
75
  "@types/node": "^25.3.2",
76
- "@types/page-icon": "^0.3.6",
77
76
  "@types/prompts": "^2.4.9",
78
77
  "@types/tmp": "^0.2.6",
79
78
  "@types/update-notifier": "^6.0.8",
@@ -88,7 +87,8 @@
88
87
  },
89
88
  "pnpm": {
90
89
  "overrides": {
91
- "sharp": "^0.34.5"
90
+ "sharp": "^0.34.5",
91
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
92
92
  },
93
93
  "onlyBuiltDependencies": [
94
94
  "esbuild",
@@ -2564,7 +2564,7 @@ dependencies = [
2564
2564
 
2565
2565
  [[package]]
2566
2566
  name = "pake"
2567
- version = "3.11.2"
2567
+ version = "3.11.3"
2568
2568
  dependencies = [
2569
2569
  "serde",
2570
2570
  "serde_json",
@@ -4575,9 +4575,7 @@ dependencies = [
4575
4575
  "bytes",
4576
4576
  "libc",
4577
4577
  "mio",
4578
- "parking_lot",
4579
4578
  "pin-project-lite",
4580
- "signal-hook-registry",
4581
4579
  "socket2",
4582
4580
  "tokio-macros",
4583
4581
  "windows-sys 0.61.2",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pake"
3
- version = "3.11.2"
3
+ version = "3.11.3"
4
4
  description = "๐Ÿคฑ๐Ÿป Turn any webpage into a desktop app with Rust."
5
5
  authors = ["Tw93"]
6
6
  license = "MIT"
@@ -20,7 +20,7 @@ tauri-build = { version = "2.5.5", features = [] }
20
20
  [dependencies]
21
21
  serde_json = "1.0.149"
22
22
  serde = { version = "1.0.228", features = ["derive"] }
23
- tokio = { version = "1.49.0", features = ["full"] }
23
+ tokio = { version = "1.49.0", features = ["time"] }
24
24
  tauri = { version = "2.10.2", features = [
25
25
  "tray-icon",
26
26
  "image-ico",
@@ -48,7 +48,7 @@ custom-protocol = ["tauri/custom-protocol"]
48
48
 
49
49
  [profile.release]
50
50
  panic = "abort"
51
- codegen-units = 16
51
+ codegen-units = 1
52
52
  lto = "thin"
53
53
  opt-level = "z"
54
54
  strip = true
@@ -24,9 +24,9 @@
24
24
  }
25
25
  ],
26
26
  "user_agent": {
27
- "macos": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15",
28
- "linux": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
29
- "windows": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
27
+ "macos": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15",
28
+ "linux": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
29
+ "windows": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36"
30
30
  },
31
31
  "system_tray": {
32
32
  "macos": false,
@@ -39,7 +39,7 @@ pub fn set_system_tray(
39
39
 
40
40
  app.app_handle().remove_tray_by_id("pake-tray");
41
41
 
42
- let tray = TrayIconBuilder::new()
42
+ let mut tray_builder = TrayIconBuilder::new()
43
43
  .menu(&menu)
44
44
  .on_menu_event(move |app, event| match event.id().as_ref() {
45
45
  "new_window" => {
@@ -47,12 +47,12 @@ pub fn set_system_tray(
47
47
  }
48
48
  "hide_app" => {
49
49
  if let Some(window) = app.get_webview_window("pake") {
50
- window.minimize().unwrap();
50
+ let _ = window.minimize();
51
51
  }
52
52
  }
53
53
  "show_app" => {
54
54
  if let Some(window) = app.get_webview_window("pake") {
55
- window.show().unwrap();
55
+ let _ = window.show();
56
56
  #[cfg(target_os = "linux")]
57
57
  if _init_fullscreen && !window.is_fullscreen().unwrap_or(false) {
58
58
  let _ = window.set_fullscreen(true);
@@ -61,8 +61,8 @@ pub fn set_system_tray(
61
61
  }
62
62
  }
63
63
  "quit" => {
64
- app.save_window_state(StateFlags::all()).unwrap();
65
- std::process::exit(0);
64
+ let _ = app.save_window_state(StateFlags::all());
65
+ app.exit(0);
66
66
  }
67
67
  _ => (),
68
68
  })
@@ -72,10 +72,10 @@ pub fn set_system_tray(
72
72
  if let Some(window) = tray.app_handle().get_webview_window("pake") {
73
73
  let is_visible = window.is_visible().unwrap_or(false);
74
74
  if is_visible {
75
- window.hide().unwrap();
75
+ let _ = window.hide();
76
76
  } else {
77
- window.show().unwrap();
78
- window.set_focus().unwrap();
77
+ let _ = window.show();
78
+ let _ = window.set_focus();
79
79
  #[cfg(target_os = "linux")]
80
80
  if _init_fullscreen && !window.is_fullscreen().unwrap_or(false) {
81
81
  let _ = window.set_fullscreen(true);
@@ -85,20 +85,23 @@ pub fn set_system_tray(
85
85
  }
86
86
  }
87
87
  _ => {}
88
- })
89
- .icon(if tray_icon_path.is_empty() {
90
- app.default_window_icon()
91
- .unwrap_or_else(|| panic!("Failed to get default window icon"))
92
- .clone()
93
- } else {
94
- tauri::image::Image::from_path(tray_icon_path).unwrap_or_else(|_| {
95
- // If custom tray icon fails to load, fallback to default
96
- app.default_window_icon()
97
- .unwrap_or_else(|| panic!("Failed to get default window icon"))
98
- .clone()
99
- })
100
- })
101
- .build(app)?;
88
+ });
89
+
90
+ let resolved_icon = if tray_icon_path.is_empty() {
91
+ app.default_window_icon().cloned()
92
+ } else {
93
+ tauri::image::Image::from_path(tray_icon_path)
94
+ .ok()
95
+ .or_else(|| app.default_window_icon().cloned())
96
+ };
97
+
98
+ if let Some(icon) = resolved_icon {
99
+ tray_builder = tray_builder.icon(icon);
100
+ } else {
101
+ eprintln!("[Pake] No tray icon available; tray will build without an icon.");
102
+ }
103
+
104
+ let tray = tray_builder.build(app)?;
102
105
 
103
106
  tray.set_icon_as_template(false)?;
104
107
  Ok(())
@@ -114,7 +117,10 @@ pub fn set_global_shortcut(
114
117
  }
115
118
 
116
119
  let app_handle = app.clone();
117
- let shortcut_hotkey = Shortcut::from_str(&shortcut).unwrap();
120
+ let shortcut_hotkey = match Shortcut::from_str(&shortcut) {
121
+ Ok(s) => s,
122
+ Err(_) => return Ok(()),
123
+ };
118
124
  let last_triggered = Arc::new(Mutex::new(Instant::now()));
119
125
 
120
126
  app_handle
@@ -123,7 +129,9 @@ pub fn set_global_shortcut(
123
129
  .with_handler({
124
130
  let last_triggered = Arc::clone(&last_triggered);
125
131
  move |app, event, _shortcut| {
126
- let mut last_triggered = last_triggered.lock().unwrap();
132
+ let Ok(mut last_triggered) = last_triggered.lock() else {
133
+ return;
134
+ };
127
135
  if Instant::now().duration_since(*last_triggered)
128
136
  < Duration::from_millis(300)
129
137
  {
@@ -133,12 +141,12 @@ pub fn set_global_shortcut(
133
141
 
134
142
  if shortcut_hotkey.eq(event) {
135
143
  if let Some(window) = app.get_webview_window("pake") {
136
- let is_visible = window.is_visible().unwrap();
144
+ let is_visible = window.is_visible().unwrap_or(false);
137
145
  if is_visible {
138
- window.hide().unwrap();
146
+ let _ = window.hide();
139
147
  } else {
140
- window.show().unwrap();
141
- window.set_focus().unwrap();
148
+ let _ = window.show();
149
+ let _ = window.set_focus();
142
150
  #[cfg(target_os = "linux")]
143
151
  if _init_fullscreen && !window.is_fullscreen().unwrap_or(false)
144
152
  {
@@ -153,7 +161,7 @@ pub fn set_global_shortcut(
153
161
  )
154
162
  .expect("Failed to set global shortcut");
155
163
 
156
- app.global_shortcut().register(shortcut_hotkey).unwrap();
164
+ let _ = app.global_shortcut().register(shortcut_hotkey);
157
165
 
158
166
  Ok(())
159
167
  }
@@ -1,6 +1,10 @@
1
1
  use crate::app::config::PakeConfig;
2
2
  use crate::util::get_data_dir;
3
- use std::{path::PathBuf, str::FromStr, sync::Mutex};
3
+ use std::{
4
+ path::PathBuf,
5
+ str::FromStr,
6
+ sync::atomic::{AtomicU32, Ordering},
7
+ };
4
8
  use tauri::{
5
9
  webview::{NewWindowFeatures, NewWindowResponse},
6
10
  AppHandle, Config, Manager, Url, WebviewUrl, WebviewWindow, WebviewWindowBuilder,
@@ -28,7 +32,7 @@ fn build_proxy_browser_arg(url: &Url) -> Option<String> {
28
32
  pub struct MultiWindowState {
29
33
  pub pake_config: PakeConfig,
30
34
  pub tauri_config: Config,
31
- next_window_index: Mutex<u32>,
35
+ next_window_index: AtomicU32,
32
36
  }
33
37
 
34
38
  impl MultiWindowState {
@@ -36,14 +40,13 @@ impl MultiWindowState {
36
40
  Self {
37
41
  pake_config,
38
42
  tauri_config,
39
- next_window_index: Mutex::new(0),
43
+ next_window_index: AtomicU32::new(0),
40
44
  }
41
45
  }
42
46
 
43
47
  fn next_window_label(&self) -> String {
44
- let mut index = self.next_window_index.lock().unwrap();
45
- *index += 1;
46
- format!("pake-{}", *index)
48
+ let index = self.next_window_index.fetch_add(1, Ordering::Relaxed) + 1;
49
+ format!("pake-{index}")
47
50
  }
48
51
  }
49
52
 
@@ -124,9 +127,25 @@ fn build_window_with_label(
124
127
  .first()
125
128
  .expect("At least one window configuration is required");
126
129
  let url = match window_config.url_type.as_str() {
127
- "web" => WebviewUrl::App(window_config.url.parse().unwrap()),
130
+ "web" => {
131
+ let parsed = window_config.url.parse().map_err(|err| {
132
+ tauri::Error::Io(std::io::Error::new(
133
+ std::io::ErrorKind::InvalidInput,
134
+ format!(
135
+ "Invalid 'web' url '{}' in pake.json: {err}",
136
+ window_config.url
137
+ ),
138
+ ))
139
+ })?;
140
+ WebviewUrl::App(parsed)
141
+ }
128
142
  "local" => WebviewUrl::App(PathBuf::from(&window_config.url)),
129
- _ => panic!("url type can only be web or local"),
143
+ other => {
144
+ return Err(tauri::Error::Io(std::io::Error::new(
145
+ std::io::ErrorKind::InvalidInput,
146
+ format!("url_type must be 'web' or 'local', got '{other}'"),
147
+ )));
148
+ }
130
149
  };
131
150
 
132
151
  build_window(
@@ -154,7 +173,10 @@ fn build_window(
154
173
  visible,
155
174
  new_window_features,
156
175
  } = opts;
157
- let package_name = tauri_config.clone().product_name.unwrap();
176
+ let package_name = tauri_config
177
+ .product_name
178
+ .clone()
179
+ .unwrap_or_else(|| "pake".to_string());
158
180
  let _data_dir = get_data_dir(app, package_name);
159
181
 
160
182
  let window_config = config
@@ -166,7 +188,7 @@ fn build_window(
166
188
 
167
189
  let config_script = format!(
168
190
  "window.pakeConfig = {}",
169
- serde_json::to_string(&window_config).unwrap()
191
+ serde_json::to_string(&window_config).unwrap_or_else(|_| "{}".to_string())
170
192
  );
171
193
 
172
194
  // Platform-specific title: macOS prefers empty, others fallback to product name
@@ -257,6 +257,21 @@ function isDownloadableFile(url) {
257
257
  }
258
258
  }
259
259
 
260
+ function normalizeAnchorHref(rawHref) {
261
+ return typeof rawHref === "string" ? rawHref.trim() : "";
262
+ }
263
+
264
+ function shouldBypassPakeLinkHandling(rawHref) {
265
+ const normalizedHref = normalizeAnchorHref(rawHref).toLowerCase();
266
+ if (!normalizedHref) {
267
+ return false;
268
+ }
269
+
270
+ return (
271
+ normalizedHref.startsWith("javascript:") || normalizedHref.startsWith("#")
272
+ );
273
+ }
274
+
260
275
  document.addEventListener("DOMContentLoaded", () => {
261
276
  const tauri = window.__TAURI__;
262
277
  const appWindow = tauri.window.getCurrentWindow();
@@ -506,6 +521,11 @@ document.addEventListener("DOMContentLoaded", () => {
506
521
  const anchorElement = e.target.closest("a");
507
522
 
508
523
  if (anchorElement && anchorElement.href) {
524
+ const rawHref = anchorElement.getAttribute("href") || "";
525
+ if (shouldBypassPakeLinkHandling(rawHref)) {
526
+ return;
527
+ }
528
+
509
529
  const target = anchorElement.target;
510
530
  const hrefUrl = new URL(anchorElement.href);
511
531
  const absoluteUrl = hrefUrl.href;
@@ -611,6 +631,16 @@ document.addEventListener("DOMContentLoaded", () => {
611
631
  // Rewrite the window.open function.
612
632
  const originalWindowOpen = window.open;
613
633
  window.open = function (url, name, specs) {
634
+ const normalizedUrl = normalizeAnchorHref(url);
635
+ if (normalizedUrl.startsWith("#")) {
636
+ window.location.href = new URL(normalizedUrl, window.location.href).href;
637
+ return window;
638
+ }
639
+
640
+ if (shouldBypassPakeLinkHandling(url)) {
641
+ return originalWindowOpen.call(window, url, name, specs);
642
+ }
643
+
614
644
  // Allow authentication popups to open normally
615
645
  if (window.isAuthPopup(url, name)) {
616
646
  return originalWindowOpen.call(window, url, name, specs);
@@ -997,23 +1027,41 @@ document.addEventListener("DOMContentLoaded", () => {
997
1027
 
998
1028
  document.addEventListener("DOMContentLoaded", function () {
999
1029
  let permVal = "granted";
1030
+ let lastNotifTime = 0;
1031
+ let lastNotif = null;
1032
+
1033
+ window.addEventListener("focus", () => {
1034
+ if (lastNotif?.onclick && Date.now() - lastNotifTime < 5000) {
1035
+ lastNotif.onclick(new Event("click"));
1036
+ lastNotif = null;
1037
+ }
1038
+ });
1039
+
1000
1040
  window.Notification = function (title, options) {
1001
1041
  const { invoke } = window.__TAURI__.core;
1002
1042
  const body = options?.body || "";
1003
1043
  let icon = options?.icon || "";
1004
1044
 
1005
- // If the icon is a relative path, convert to full path using URI
1006
1045
  if (icon.startsWith("/")) {
1007
1046
  icon = window.location.origin + icon;
1008
1047
  }
1009
1048
 
1010
- invoke("send_notification", {
1011
- params: {
1012
- title,
1013
- body,
1014
- icon,
1015
- },
1049
+ const notif = {
1050
+ onclick: null,
1051
+ onclose: null,
1052
+ onshow: null,
1053
+ onerror: null,
1054
+ close: () => {},
1055
+ };
1056
+
1057
+ lastNotifTime = Date.now();
1058
+ lastNotif = notif;
1059
+
1060
+ invoke("send_notification", { params: { title, body, icon } }).then(() => {
1061
+ if (notif.onshow) notif.onshow(new Event("show"));
1016
1062
  });
1063
+
1064
+ return notif;
1017
1065
  };
1018
1066
 
1019
1067
  window.Notification.requestPermission = async () => "granted";
@@ -119,15 +119,18 @@ window.addEventListener("DOMContentLoaded", (_event) => {
119
119
 
120
120
  #react-root [data-testid="placementTracking"] article,
121
121
  #react-root a[href*="quick_promote_web"],
122
- #react-root [data-testid="AppTabBar_Explore_Link"],
123
122
  #react-root a[href*="/lists"][role="link"][aria-label],
124
123
  #react-root a[href*="/i/communitynotes"][role="link"][aria-label],
125
124
  #react-root a[role="link"][aria-label="Communities"],
125
+ #react-root a[role="link"][aria-label="Premium"],
126
+ #react-root a[role="link"][aria-label="SuperGrok"],
126
127
  #react-root a[href*="/i/verified-orgs-signup"][role="link"][aria-label] {
127
128
  display: none !important;
128
129
  }
129
130
 
130
131
  #react-root [data-testid="DMDrawer"],
132
+ #react-root [data-testid="GrokDrawer"],
133
+ #react-root [data-testid="chat-drawer-root"],
131
134
  #root > main > footer.justify-center.ease-in {
132
135
  visibility: hidden !important;
133
136
  }
@@ -231,6 +234,28 @@ window.addEventListener("DOMContentLoaded", (_event) => {
231
234
  }
232
235
  }
233
236
 
237
+ @media only screen and (min-width: 1000px) and (max-width: 1264px) {
238
+ #react-root [data-testid="sidebarColumn"] form[role="search"] {
239
+ visibility: visible !important;
240
+ position: fixed !important;
241
+ top: 12px !important;
242
+ right: 16px !important;
243
+ }
244
+
245
+ #react-root [data-testid="sidebarColumn"] input[placeholder="Search"] {
246
+ width: 150px;
247
+ }
248
+
249
+ #react-root [data-testid="sidebarColumn"] form[role="search"]:focus-within {
250
+ width: 280px !important;
251
+ backdrop-filter: blur(12px) !important;
252
+ }
253
+
254
+ #react-root [data-testid="sidebarColumn"] input[placeholder="Search"]:focus {
255
+ width: 234px !important;
256
+ }
257
+ }
258
+
234
259
  @media only screen and (min-width: 1265px) {
235
260
  #react-root [data-testid="sidebarColumn"] form[role="search"] {
236
261
  visibility: visible !important;
@@ -239,7 +264,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
239
264
  right: 16px !important;
240
265
  }
241
266
 
242
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search Twitter"] {
267
+ #react-root [data-testid="sidebarColumn"] input[placeholder="Search"] {
243
268
  width: 150px;
244
269
  }
245
270
 
@@ -248,7 +273,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
248
273
  backdrop-filter: blur(12px) !important;
249
274
  }
250
275
 
251
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search Twitter"]:focus {
276
+ #react-root [data-testid="sidebarColumn"] input[placeholder="Search"]:focus {
252
277
  width: 328px !important;
253
278
  }
254
279
 
@@ -300,7 +325,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
300
325
  }
301
326
  `;
302
327
  const contentStyleElement = document.createElement("style");
303
- contentStyleElement.innerHTML = contentCSS;
328
+ contentStyleElement.textContent = contentCSS;
304
329
  document.head.appendChild(contentStyleElement);
305
330
 
306
331
  // Top spacing adapts to head-hiding scenarios
@@ -471,10 +496,10 @@ window.addEventListener("DOMContentLoaded", (_event) => {
471
496
  }
472
497
  }
473
498
  `;
474
- const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
499
+ const isMac = /Mac/i.test(navigator.userAgent);
475
500
  if (window["pakeConfig"]?.hide_title_bar && isMac) {
476
501
  const topPaddingStyleElement = document.createElement("style");
477
- topPaddingStyleElement.innerHTML = topPaddingCSS;
502
+ topPaddingStyleElement.textContent = topPaddingCSS;
478
503
  document.head.appendChild(topPaddingStyleElement);
479
504
  }
480
505
  });
@@ -107,9 +107,8 @@ pub fn run_app() {
107
107
  &pake_config.system_tray_path,
108
108
  init_fullscreen,
109
109
  multi_window,
110
- )
111
- .unwrap();
112
- set_global_shortcut(app.app_handle(), activation_shortcut, init_fullscreen).unwrap();
110
+ )?;
111
+ set_global_shortcut(app.app_handle(), activation_shortcut, init_fullscreen)?;
113
112
 
114
113
  // Show window after state restoration to prevent position flashing
115
114
  // Unless start_to_tray is enabled, then keep it hidden
@@ -117,13 +116,13 @@ pub fn run_app() {
117
116
  let window_clone = window.clone();
118
117
  tauri::async_runtime::spawn(async move {
119
118
  tokio::time::sleep(tokio::time::Duration::from_millis(WINDOW_SHOW_DELAY)).await;
120
- window_clone.show().unwrap();
119
+ let _ = window_clone.show();
121
120
 
122
121
  // Fixed: Linux fullscreen issue with virtual keyboard
123
122
  #[cfg(target_os = "linux")]
124
123
  {
125
124
  if init_fullscreen {
126
- window_clone.set_fullscreen(true).unwrap();
125
+ let _ = window_clone.set_fullscreen(true);
127
126
  // Ensure webview maintains focus for input after fullscreen
128
127
  let _ = window_clone.set_focus();
129
128
  } else {
@@ -148,22 +147,22 @@ pub fn run_app() {
148
147
  #[cfg(target_os = "macos")]
149
148
  {
150
149
  if window.is_fullscreen().unwrap_or(false) {
151
- window.set_fullscreen(false).unwrap();
150
+ let _ = window.set_fullscreen(false);
152
151
  tokio::time::sleep(Duration::from_millis(900)).await;
153
152
  }
154
153
  }
155
154
  #[cfg(target_os = "linux")]
156
155
  {
157
156
  if window.is_fullscreen().unwrap_or(false) {
158
- window.set_fullscreen(false).unwrap();
157
+ let _ = window.set_fullscreen(false);
159
158
  // Restore focus after exiting fullscreen to fix input issues
160
159
  let _ = window.set_focus();
161
160
  }
162
161
  }
163
162
  // On macOS, directly hide without minimize to avoid duplicate Dock icons
164
163
  #[cfg(not(target_os = "macos"))]
165
- window.minimize().unwrap();
166
- window.hide().unwrap();
164
+ let _ = window.minimize();
165
+ let _ = window.hide();
167
166
  });
168
167
  api.prevent_close();
169
168
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "productName": "Weekly",
3
3
  "identifier": "com.pake.weekly",
4
- "version": "3.11.2",
4
+ "version": "3.11.3",
5
5
  "app": {
6
6
  "withGlobalTauri": true,
7
7
  "trayIcon": {