pake-cli 1.3.0 → 2.0.0-alpha6

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.
@@ -1,39 +1,5 @@
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 = `
1
+ window.addEventListener('DOMContentLoaded', (_event) => {
2
+ const css = `
37
3
  #page #footer-wrapper,
38
4
  .drawing-board .toolbar .toolbar-action,
39
5
  .c-swiper-container,
@@ -49,6 +15,8 @@ window.addEventListener("DOMContentLoaded", (_event) => {
49
15
  #masthead-ad,
50
16
  #app > header > div > div.menu,
51
17
  #root > div > div.fixed.top-0.left-0.w-64.h-screen.p-10.pb-0.flex.flex-col.justify-between > div > div.space-y-4 > a:nth-child(3),
18
+ #app > div.layout > div.main-container > div.side-bar > div,
19
+ #app > div.layout > div.main-container > div.side-bar > li.divider,
52
20
  #Rightbar > div:nth-child(6) > div.sidebar_compliance {
53
21
  display: none !important;
54
22
  }
@@ -67,13 +35,17 @@ window.addEventListener("DOMContentLoaded", (_event) => {
67
35
  padding-top: 20px;
68
36
  }
69
37
 
38
+ .fui-FluentProvider .fui-Button[data-testid="HomeButton"]{
39
+ padding-top: 20px;
40
+ }
41
+
70
42
  .chakra-ui-light #app .chakra-heading,
71
43
  .chakra-ui-dark #app .chakra-heading,
72
44
  .chakra-ui-light #app .chakra-stack,
73
45
  .chakra-ui-dark #app .chakra-stack,
74
46
  .app-main .sidebar-mouse-in-out,
75
47
  .chakra-modal__content-container .chakra-modal__header > div > div {
76
- padding-top: 10px;
48
+ padding-top: 10px;
77
49
  }
78
50
 
79
51
  #__next .overflow-hidden>.hidden.bg-gray-900 span.rounded-md.bg-yellow-200 {
@@ -85,7 +57,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
85
57
  }
86
58
 
87
59
  #__next .absolute .px-3.pt-2.pb-3.text-center {
88
- visibility: hidden;
60
+ visibility: hidden;
89
61
  }
90
62
 
91
63
  .lark > .dashboard-sidebar, .lark > .dashboard-sidebar > .sidebar-user-info , .lark > .dashboard-sidebar .index-module_wrapper_F-Wbq{
@@ -126,7 +98,8 @@ window.addEventListener("DOMContentLoaded", (_event) => {
126
98
  top: 30px;
127
99
  }
128
100
 
129
- .geist-page nav.dashboard_nav__PRmJv {
101
+ .geist-page nav.dashboard_nav__PRmJv,
102
+ #app > div.layout > div.header-container.showSearchBoxOrHeaderFixed > header > a {
130
103
  padding-top:10px;
131
104
  }
132
105
 
@@ -281,6 +254,33 @@ window.addEventListener("DOMContentLoaded", (_event) => {
281
254
  }
282
255
  }
283
256
 
257
+ @media (min-width:1024px){
258
+ #__next .text-base.lg\\:max-w-xl, #__next form.stretch.lg\\:max-w-2xl {
259
+ max-width: 44rem;
260
+ }
261
+ }
262
+
263
+ @media (min-width:1280px){
264
+ #__next .text-base.xl\\:max-w-3xl, #__next form.stretch.xl\\:max-w-3xl {
265
+ max-width: 48rem;
266
+ }
267
+ }
268
+
269
+ @media (min-width:640px){
270
+ #__next .sticky.top-0{
271
+ padding-top: 15px;
272
+ }
273
+ }
274
+
275
+ #__next .prose ol {
276
+ overflow: hidden;
277
+ }
278
+
279
+ #__next .prose ol li p {
280
+ margin: 0;
281
+ display: inline;
282
+ }
283
+
284
284
  #pack-top-dom:active {
285
285
  cursor: grabbing;
286
286
  cursor: -webkit-grabbing;
@@ -293,109 +293,13 @@ window.addEventListener("DOMContentLoaded", (_event) => {
293
293
  width: 100%;
294
294
  height: 20px;
295
295
  cursor: grab;
296
- cursor: -webkit-grab;
296
+ -webkit-app-region: drag;
297
297
  user-select: none;
298
+ -webkit-user-select: none;
298
299
  z-index: 90000;
299
300
  }
300
301
  `;
301
- document.head.append(style);
302
- const topDom = document.createElement("div");
303
- topDom.id = "pack-top-dom";
304
- document.body.appendChild(topDom);
305
-
306
- const domEl = document.getElementById("pack-top-dom");
307
-
308
- domEl.addEventListener("mousedown", (e) => {
309
- e && e.preventDefault();
310
- if (e.buttons === 1 && e.detail !== 2) {
311
- window.ipc.postMessage("drag_window");
312
- }
313
- });
314
-
315
- domEl.addEventListener("touchstart", () => {
316
- window.ipc.postMessage("drag_window");
317
- });
318
-
319
- domEl.addEventListener("dblclick", () => {
320
- window.ipc.postMessage("fullscreen");
321
- });
322
-
323
- document.addEventListener("keyup", function (event) {
324
- const preventDefault = (f) => {
325
- event.preventDefault();
326
- f();
327
- };
328
- if (/windows|linux/i.test(navigator.userAgent)) {
329
- if (event.ctrlKey && event.key in ctrlKeyShortcuts) {
330
- preventDefault(ctrlKeyShortcuts[event.key]);
331
- }
332
- }
333
- if (/macintosh|mac os x/i.test(navigator.userAgent)) {
334
- if (event.metaKey && event.key in metaKeyShortcuts) {
335
- preventDefault(metaKeyShortcuts[event.key]);
336
- }
337
- }
338
- });
339
-
340
- document.addEventListener("click", (e) => {
341
- const origin = e.target.closest("a");
342
- if (origin && origin.href) {
343
- const target = origin.target
344
- origin.target = "_self";
345
- const hrefUrl = new URL(origin.href)
346
-
347
- if (
348
- window.location.host !== hrefUrl.host && // 如果 a 标签内链接的域名和当前页面的域名不一致 且
349
- target === '_blank' // a 标签内链接的 target 属性为 _blank 时
350
- ) {
351
- e.preventDefault();
352
- window.ipc.postMessage(`open_browser:${origin.href}`);
353
- }
354
- }
355
- });
302
+ const styleElement = document.createElement('style');
303
+ styleElement.innerHTML = css;
304
+ document.head.appendChild(styleElement);
356
305
  });
357
-
358
- setDefaultZoom();
359
-
360
- function setDefaultZoom() {
361
- const htmlZoom = window.localStorage.getItem("htmlZoom");
362
- if (htmlZoom) {
363
- document.getElementsByTagName("html")[0].style.zoom = htmlZoom;
364
- }
365
- }
366
-
367
- /**
368
- * @param {(htmlZoom: string) => string} [zoomRule]
369
- */
370
- function zoomCommon(zoomRule) {
371
- const htmlZoom = window.localStorage.getItem("htmlZoom") || "100%";
372
- const html = document.getElementsByTagName("html")[0];
373
- const zoom = zoomRule(htmlZoom);
374
- html.style.zoom = zoom;
375
- window.localStorage.setItem("htmlZoom", zoom);
376
- }
377
-
378
- function zoomIn() {
379
- zoomCommon((htmlZoom) => `${Math.min(parseInt(htmlZoom) + 10, 200)}%`);
380
- }
381
-
382
- function zoomOut() {
383
- zoomCommon((htmlZoom) => `${Math.max(parseInt(htmlZoom) - 10, 30)}%`);
384
- }
385
-
386
-
387
- function pakeToast(msg) {
388
- const m = document.createElement('div');
389
- m.innerHTML = msg;
390
- m.style.cssText = "max-width:60%;min-width: 180px;padding:0 8px;height: 36px;color: rgb(255, 255, 255);line-height: 36px;text-align: center;border-radius: 4px;position: fixed;bottom:16px;right: 16px;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0,.9);font-size: 14px;";
391
- document.body.appendChild(m);
392
- setTimeout(function() {
393
- const d = 0.5;
394
- m.style.transition = 'transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
395
- m.style.opacity = '0';
396
- setTimeout(function() {
397
- document.body.removeChild(m)
398
- }, d * 1000);
399
- }, 2500);
400
- }
401
-
@@ -1,277 +1,68 @@
1
- // at the top of main.rs - that will prevent the console from showing
2
- #![windows_subsystem = "windows"]
3
- extern crate image;
4
- use tauri_utils::config::{Config, WindowConfig};
5
- use wry::{
6
- application::{
7
- event::{Event, StartCause, WindowEvent},
8
- event_loop::{ControlFlow, EventLoop},
9
- menu::MenuType,
10
- window::{Fullscreen, Window, WindowBuilder},
11
- },
12
- webview::WebViewBuilder,
13
- };
14
-
15
- #[cfg(target_os = "macos")]
16
- use wry::application::{
17
- accelerator::{Accelerator, SysMods},
18
- keyboard::KeyCode,
19
- menu::{MenuBar as Menu, MenuItem, MenuItemAttributes},
20
- platform::macos::WindowBuilderExtMacOS,
21
- };
22
-
23
- #[cfg(target_os = "windows")]
24
- use wry::application::window::Icon;
25
-
26
- #[cfg(any(target_os = "linux", target_os = "windows"))]
27
- use wry::webview::WebContext;
28
-
29
- use dirs::desktop_dir;
30
- use std::path::PathBuf;
31
-
32
- enum UserEvent {
33
- DownloadStarted(String, String),
34
- DownloadComplete(Option<PathBuf>, bool),
35
- }
36
-
37
- fn main() -> wry::Result<()> {
38
- #[cfg(target_os = "macos")]
39
- let (menu_bar_menu, close_item) = {
40
- let mut menu_bar_menu = Menu::new();
41
- let mut first_menu = Menu::new();
42
- first_menu.add_native_item(MenuItem::Hide);
43
- first_menu.add_native_item(MenuItem::EnterFullScreen);
44
- first_menu.add_native_item(MenuItem::Minimize);
45
- first_menu.add_native_item(MenuItem::Separator);
46
- first_menu.add_native_item(MenuItem::Copy);
47
- first_menu.add_native_item(MenuItem::Cut);
48
- first_menu.add_native_item(MenuItem::Paste);
49
- first_menu.add_native_item(MenuItem::Undo);
50
- first_menu.add_native_item(MenuItem::Redo);
51
- first_menu.add_native_item(MenuItem::SelectAll);
52
- first_menu.add_native_item(MenuItem::Separator);
53
- let close_item = first_menu.add_item(
54
- MenuItemAttributes::new("CloseWindow")
55
- .with_accelerators(&Accelerator::new(SysMods::Cmd, KeyCode::KeyW)),
56
- );
57
- first_menu.add_native_item(MenuItem::Quit);
58
- menu_bar_menu.add_submenu("App", true, first_menu);
59
- (menu_bar_menu, close_item)
60
- };
61
-
62
- #[cfg(any(target_os = "linux", target_os = "windows"))]
63
- let (
64
- package_name,
65
- WindowConfig {
66
- url,
67
- width,
68
- height,
69
- resizable,
70
- fullscreen,
71
- ..
72
- },
73
- ) = {
74
- let (package_name, windows_config) = get_windows_config();
75
- (
76
- package_name
77
- .expect("can't get package name in config file")
78
- .to_lowercase(),
79
- windows_config.unwrap_or_default(),
80
- )
81
- };
82
-
83
- #[cfg(target_os = "macos")]
84
- let WindowConfig {
85
- url,
86
- width,
87
- height,
88
- resizable,
89
- transparent,
90
- fullscreen,
91
- ..
92
- } = get_windows_config().1.unwrap_or_default();
93
-
94
- let event_loop: EventLoop<UserEvent> = EventLoop::with_user_event();
95
- let proxy = event_loop.create_proxy();
96
- let common_window = WindowBuilder::new()
97
- .with_title("")
98
- .with_resizable(resizable)
99
- .with_fullscreen(if fullscreen {
100
- Some(Fullscreen::Borderless(None))
101
- } else {
102
- None
103
- })
104
- .with_inner_size(wry::application::dpi::LogicalSize::new(width, height));
105
-
106
- #[cfg(target_os = "windows")]
107
- let window = {
108
- let mut icon_path = format!("png/{}_32.ico", package_name);
109
- // If there is no setting, use the default one.
110
- if !std::path::Path::new(&icon_path).exists() {
111
- icon_path = "png/icon_32.ico".to_string();
112
- }
113
- let icon = load_icon(std::path::Path::new(&icon_path));
114
- common_window
115
- .with_decorations(true)
116
- .with_window_icon(Some(icon))
117
- .build(&event_loop)
118
- .unwrap()
119
- };
120
-
121
- #[cfg(target_os = "linux")]
122
- let window = common_window.build(&event_loop).unwrap();
123
-
124
- #[cfg(target_os = "macos")]
125
- let window = common_window
126
- .with_fullsize_content_view(true)
127
- .with_titlebar_buttons_hidden(false)
128
- .with_titlebar_transparent(transparent)
129
- .with_title_hidden(true)
130
- .with_menu(menu_bar_menu)
131
- .build(&event_loop)
132
- .unwrap();
133
-
134
- // Handling events of JS -> Rust
135
- let handler = move |window: &Window, req: String| {
136
- if req == "drag_window" {
137
- let _ = window.drag_window();
138
- } else if req == "fullscreen" {
139
- let is_maximized = window.is_maximized();
140
- window.set_maximized(!is_maximized);
141
- } else if req.starts_with("open_browser") {
142
- let href = req.replace("open_browser:", "");
143
- webbrowser::open(&href).expect("no browser");
144
- }
145
- };
146
-
147
- let download_started = {
148
- let proxy = proxy.clone();
149
- move |uri: String, default_path: &mut PathBuf| {
150
- let path = desktop_dir()
151
- .unwrap()
152
- .join(default_path.display().to_string())
153
- .as_path()
154
- .to_path_buf();
155
- *default_path = path.clone();
156
- let submitted = proxy
157
- .send_event(UserEvent::DownloadStarted(uri, path.display().to_string()))
158
- .is_ok();
159
- submitted
160
- }
161
- };
1
+ #![cfg_attr(
2
+ all(not(debug_assertions), target_os = "windows"),
3
+ windows_subsystem = "windows"
4
+ )]
5
+
6
+ mod app;
7
+ mod util;
8
+
9
+ use app::{invoke, menu, window};
10
+ use invoke::{download_file, drag_window, fullscreen, open_browser};
11
+ use menu::{get_menu, menu_event_handle};
12
+ use tauri_plugin_window_state::Builder as windowStatePlugin;
13
+ use util::{get_data_dir, get_pake_config};
14
+ use window::get_window;
15
+
16
+ pub fn run_app() {
17
+ let (pake_config, tauri_config) = get_pake_config();
18
+ let show_menu = pake_config.show_menu();
19
+ let menu = get_menu();
20
+ let data_dir = get_data_dir(tauri_config);
21
+
22
+ let mut tauri_app = tauri::Builder::default();
23
+
24
+ if show_menu {
25
+ tauri_app = tauri_app.menu(menu).on_menu_event(menu_event_handle);
26
+ }
162
27
 
163
- let download_completed = {
164
- move |_uri, path, success| {
165
- let _ = proxy.send_event(UserEvent::DownloadComplete(path, success));
166
- }
167
- };
28
+ #[cfg(not(target_os = "macos"))]
29
+ {
30
+ use menu::{get_system_tray, system_tray_handle};
168
31
 
169
- #[cfg(target_os = "macos")]
170
- let webview = {
171
- let user_agent_string = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15";
172
- WebViewBuilder::new(window)?
173
- .with_user_agent(user_agent_string)
174
- .with_url(&url.to_string())?
175
- .with_devtools(cfg!(feature = "devtools"))
176
- .with_initialization_script(include_str!("pake.js"))
177
- .with_ipc_handler(handler)
178
- .with_back_forward_navigation_gestures(true)
179
- .with_download_started_handler(download_started)
180
- .with_download_completed_handler(download_completed)
181
- .build()?
182
- };
32
+ let show_system_tray = pake_config.show_system_tray();
33
+ let system_tray = get_system_tray(show_menu);
183
34
 
184
- #[cfg(any(target_os = "linux", target_os = "windows"))]
185
- let webview = {
186
- let home_dir = match home::home_dir() {
187
- Some(path1) => path1,
188
- None => panic!("Error, can't found you home dir!!"),
189
- };
190
- #[cfg(target_os = "windows")]
191
- let data_dir = home_dir.join("AppData").join("Roaming").join(package_name);
192
- #[cfg(target_os = "linux")]
193
- let data_dir = home_dir.join(".config").join(package_name);
194
- if !data_dir.exists() {
195
- std::fs::create_dir(&data_dir)
196
- .unwrap_or_else(|_| panic!("can't create dir {}", data_dir.display()));
35
+ if show_system_tray {
36
+ tauri_app = tauri_app
37
+ .system_tray(system_tray)
38
+ .on_system_tray_event(system_tray_handle);
197
39
  }
198
- let mut web_content = WebContext::new(Some(data_dir));
199
- #[cfg(target_os = "windows")]
200
- let user_agent_string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
201
- #[cfg(target_os = "linux")]
202
- let user_agent_string = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
203
- WebViewBuilder::new(window)?
204
- .with_user_agent(user_agent_string)
205
- .with_url(&url.to_string())?
206
- .with_devtools(cfg!(feature = "devtools"))
207
- .with_initialization_script(include_str!("pake.js"))
208
- .with_ipc_handler(handler)
209
- .with_web_context(&mut web_content)
210
- .with_download_started_handler(download_started)
211
- .with_download_completed_handler(download_completed)
212
- .build()?
213
- };
214
- #[cfg(feature = "devtools")]
215
- {
216
- webview.open_devtools();
217
40
  }
218
41
 
219
- event_loop.run(move |event, _, control_flow| {
220
- *control_flow = ControlFlow::Wait;
221
-
222
- match event {
223
- Event::NewEvents(StartCause::Init) => println!("Wry has started!"),
224
- Event::WindowEvent {
225
- event: WindowEvent::CloseRequested,
226
- ..
227
- } => *control_flow = ControlFlow::Exit,
228
- Event::MenuEvent {
229
- menu_id,
230
- origin: MenuType::MenuBar,
231
- ..
232
- } => {
233
- #[cfg(target_os = "macos")]
234
- if menu_id == close_item.clone().id() {
235
- webview.window().set_minimized(true);
236
- }
237
- println!("Clicked on {menu_id:?}");
238
- }
239
- Event::UserEvent(UserEvent::DownloadStarted(uri, temp_dir)) => {
240
- println!("Download: {uri}");
241
- println!("Will write to: {temp_dir:?}");
242
- }
243
- Event::UserEvent(UserEvent::DownloadComplete(_, success)) => {
244
- println!("Succeeded: {success}");
245
- if success {
246
- let _ = webview.evaluate_script("window.pakeToast('Downloaded to Desktop~')");
247
- } else {
248
- println!("No output path")
249
- }
42
+ tauri_app
43
+ .plugin(windowStatePlugin::default().build())
44
+ .invoke_handler(tauri::generate_handler![
45
+ drag_window,
46
+ fullscreen,
47
+ open_browser,
48
+ download_file
49
+ ])
50
+ .setup(|app| {
51
+ let _window = get_window(app, pake_config, data_dir);
52
+ // Prevent initial shaking
53
+ _window.show().unwrap();
54
+ Ok(())
55
+ })
56
+ .on_window_event(|event| {
57
+ if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
58
+ event.window().minimize().unwrap();
59
+ api.prevent_close();
250
60
  }
251
- _ => (),
252
- }
253
- });
254
- }
255
-
256
- fn get_windows_config() -> (Option<String>, Option<WindowConfig>) {
257
- let config_file = include_str!("../tauri.conf.json");
258
- let config: Config = serde_json::from_str(config_file).expect("failed to parse windows config");
259
- (
260
- config.package.product_name.clone(),
261
- config.tauri.windows.first().cloned(),
262
- )
61
+ })
62
+ .run(tauri::generate_context!())
63
+ .expect("error while running tauri application");
263
64
  }
264
65
 
265
- #[cfg(target_os = "windows")]
266
- fn load_icon(path: &std::path::Path) -> Icon {
267
- let (icon_rgba, icon_width, icon_height) = {
268
- // alternatively, you can embed the icon in the binary through `include_bytes!` macro and use `image::load_from_memory`
269
- let image = image::open(path)
270
- .expect("Failed to open icon path")
271
- .into_rgba8();
272
- let (width, height) = image.dimensions();
273
- let rgba = image.into_raw();
274
- (rgba, width, height)
275
- };
276
- Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
66
+ fn main() {
67
+ run_app()
277
68
  }
@@ -0,0 +1,78 @@
1
+ use crate::app::config::PakeConfig;
2
+ use std::env;
3
+ use std::path::PathBuf;
4
+ use tauri::{api, Config, Window};
5
+
6
+ pub fn get_pake_config() -> (PakeConfig, Config) {
7
+ let pake_config: PakeConfig =
8
+ serde_json::from_str(include_str!("../pake.json")).expect("Failed to parse pake config");
9
+
10
+ let tauri_config: Config = serde_json::from_str(include_str!("../tauri.conf.json"))
11
+ .expect("Failed to parse tauri config");
12
+
13
+ (pake_config, tauri_config)
14
+ }
15
+
16
+ pub fn get_data_dir(_tauri_config: Config) -> PathBuf {
17
+ {
18
+ let package_name = _tauri_config.package.product_name.unwrap();
19
+ let data_dir = api::path::config_dir()
20
+ .expect("Failed to get data dirname")
21
+ .join(package_name);
22
+
23
+ if !data_dir.exists() {
24
+ std::fs::create_dir(&data_dir)
25
+ .unwrap_or_else(|_| panic!("Can't create dir {}", data_dir.display()));
26
+ }
27
+ data_dir
28
+ }
29
+ }
30
+
31
+ pub fn show_toast(window: &Window, message: &str) {
32
+ let script = format!(r#"pakeToast("{}");"#, message);
33
+ window.eval(&script).unwrap();
34
+ }
35
+
36
+ pub fn get_download_message() -> String {
37
+ let default_message = "Download successful, saved to download directory~";
38
+ let chinese_message = "下载成功,已保存到下载目录~";
39
+
40
+ env::var("LANG")
41
+ .map(|lang| {
42
+ if lang.starts_with("zh") {
43
+ chinese_message
44
+ } else {
45
+ default_message
46
+ }
47
+ })
48
+ .unwrap_or(default_message)
49
+ .to_string()
50
+ }
51
+
52
+ // Check if the file exists, if it exists, add a number to file name
53
+ pub fn check_file_or_append(file_path: &str) -> String {
54
+ let mut new_path = PathBuf::from(file_path);
55
+ let mut counter = 0;
56
+
57
+ while new_path.exists() {
58
+ let file_stem = new_path.file_stem().unwrap().to_string_lossy().to_string();
59
+ let extension = new_path.extension().unwrap().to_string_lossy().to_string();
60
+ let parent_dir = new_path.parent().unwrap();
61
+
62
+ let new_file_stem = match file_stem.rfind('-') {
63
+ Some(index) if file_stem[index + 1..].parse::<u32>().is_ok() => {
64
+ let base_name = &file_stem[..index];
65
+ counter = file_stem[index + 1..].parse::<u32>().unwrap() + 1;
66
+ format!("{}-{}", base_name, counter)
67
+ }
68
+ _ => {
69
+ counter += 1;
70
+ format!("{}-{}", file_stem, counter)
71
+ }
72
+ };
73
+
74
+ new_path = parent_dir.join(format!("{}.{}", new_file_stem, extension));
75
+ }
76
+
77
+ new_path.to_string_lossy().into_owned()
78
+ }