From 8b859f1658dd23bc523285acc297ceb291f05f3f Mon Sep 17 00:00:00 2001 From: Borber Date: Fri, 9 Feb 2024 22:12:16 +0800 Subject: [PATCH] Try to maintain the original format --- CHANGELOG.md | 6 ++- src-tauri/src/common.rs | 3 +- src-tauri/src/config.rs | 34 ++++++++------- src-tauri/src/main.rs | 17 +------- src-tauri/src/manager/api.rs | 81 ++++++++++++++++++++++++++---------- src-tauri/src/tray.rs | 4 +- src/App.css | 1 + src/App.tsx | 27 ++++++++++-- 8 files changed, 113 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d461eb5..f700297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## [0.2.3] +### 新年快乐! Happy New Year! + - 调整翻译界面样式 - - 安装 Fire Code 字体将使英语结果更加美观 + - (可选) 安装 Fire Code 字体将使英语结果更加美观 +- 尽量维持原格式, 对换行和空格进行特殊处理 + - ![tran](https://fastly.jsdelivr.net/gh/Borber/PublicPic1/tran/v1/tran-new-line.png) ## [0.2.2] diff --git a/src-tauri/src/common.rs b/src-tauri/src/common.rs index acc43f4..e5776db 100644 --- a/src-tauri/src/common.rs +++ b/src-tauri/src/common.rs @@ -3,11 +3,12 @@ use std::sync::{atomic::AtomicBool, Arc}; use once_cell::sync::Lazy; use reqwest::Client; -use crate::manager; +use crate::{config, manager}; pub static CLIENT: Lazy = Lazy::new(Client::new); pub static PIN: Lazy> = Lazy::new(|| Arc::new(AtomicBool::new(false))); pub async fn init() { + config::load(); manager::mirror::init().await; } diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 1894dcf..11f1990 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,8 +1,10 @@ -use std::sync::Arc; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; use anyhow::Result; use once_cell::sync::Lazy; -use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use crate::util; @@ -12,21 +14,21 @@ use crate::util; /// Config #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { - /// 模式, 0: 镜像模式, 1: 直连模式 + /// 模式, true: 镜像模式, false: 直连模式 /// - /// Mode, 0: mirror mode, 1: direct mode - pub mode: usize, + /// Mode, true: mirror mode, false: direct mode + pub mode: bool, } /// 全局配置 /// /// Global config -pub static CONFIG: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(load()))); +pub static MODE: Lazy> = Lazy::new(|| Arc::new(AtomicBool::new(true))); /// 读取配置 /// /// read config -pub fn load() -> Config { +pub fn load() { let exe_dir = util::get_exe_dir(); let path = exe_dir.join("config.json"); // 检测文件是否存在 @@ -34,19 +36,23 @@ pub fn load() -> Config { let exists = path.try_exists(); if exists.is_ok() && exists.expect("Failed to check if file exists") { let config = std::fs::read_to_string(path).expect("Failed to read config"); - serde_json::from_str(&config).expect("Failed to parse config") + let config = serde_json::from_str::(&config).expect("Failed to parse config"); + MODE.store(config.mode, Ordering::SeqCst); } else { - Config { mode: 0 } + MODE.store(true, Ordering::SeqCst); } } /// 保存配置 /// /// save config -fn save(config: &Config) -> Result<()> { +fn save() -> Result<()> { + let config = Config { + mode: MODE.load(Ordering::SeqCst), + }; let exe_dir = util::get_exe_dir(); let path = exe_dir.join("config.json"); - let config = serde_json::to_string_pretty(config).expect("Failed to serialize config"); + let config = serde_json::to_string_pretty(&config).expect("Failed to serialize config"); std::fs::write(path, config).expect("Failed to write config"); Ok(()) } @@ -54,7 +60,7 @@ fn save(config: &Config) -> Result<()> { /// 切换模式 /// /// switch mode -pub fn mode(mode: usize) { - CONFIG.lock().mode = mode; - save(&CONFIG.lock()).expect("Failed to save config after switch mode"); +pub fn mode(mode: bool) { + MODE.store(mode, Ordering::SeqCst); + save().expect("Failed to save config after switch mode"); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1486424..0949f18 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -39,14 +39,6 @@ fn copy(content: String) -> Resp<()> { clip::set(content).into() } -/// 切换模式 -/// -/// Switch mode -#[tauri::command] -fn switch_mode(mode: usize) { - config::mode(mode); -} - /// 打开指定链接 /// /// Open the specified link @@ -104,14 +96,7 @@ async fn main() { .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_single_instance::init(|_, _, _| {})) .setup(setup::handler) - .invoke_handler(tauri::generate_handler![ - copy, - open, - translate, - switch_mode, - pin, - update, - ]) + .invoke_handler(tauri::generate_handler![copy, open, translate, pin, update,]) .run(context) .expect("error while running tauri application"); } diff --git a/src-tauri/src/manager/api.rs b/src-tauri/src/manager/api.rs index b089d56..7a53f67 100644 --- a/src-tauri/src/manager/api.rs +++ b/src-tauri/src/manager/api.rs @@ -1,9 +1,11 @@ +use std::sync::atomic::Ordering; + use anyhow::Result; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use reqwest::Client; use serde::Serialize; -use crate::{common::CLIENT, config::CONFIG, lang, manager::mirror}; +use crate::{common::CLIENT, config::MODE, lang, manager::mirror}; /// 翻译结果 #[derive(Debug, Serialize)] @@ -11,7 +13,7 @@ pub struct TransVO { /// 是否为单词 pub word: bool, /// 翻译结果 - pub trans: Option, + pub trans: Option>, /// 词典 pub dicts: Option>, } @@ -22,6 +24,15 @@ pub struct Dict { pub terms: Vec, } +#[derive(Debug, Serialize)] +pub struct Tran { + // 0: 文本 + // 1: 换行 + // 2: 空格 + pub typ: u64, + pub data: Option, +} + pub async fn translate(content: &str) -> Result { // 翻译目标语言 let lang = lang::lang(content); @@ -29,17 +40,17 @@ pub async fn translate(content: &str) -> Result { // 转换为 url 编码 let content = utf8_percent_encode(content, NON_ALPHANUMERIC).to_string(); - let mode = CONFIG.lock().mode; - let host = match mode { - 0 => mirror::one(), - _ => "https://translate.googleapis.com".to_string(), + let host = if MODE.load(Ordering::SeqCst) { + mirror::one() + } else { + "https://translate.googleapis.com".to_string() }; send(&CLIENT, host, &lang, &content).await } async fn send(client: &Client, host: String, lang: &str, content: &str) -> Result { let resp = client - .get(format!("{}/translate_a/single?client=gtx&sl=auto&tl={}&dj=1&dt=t&dt=bd&dt=qc&dt=rm&dt=ex&dt=at&dt=ss&dt=rw&dt=ld&q=%22{}%22", host, lang, content)) + .get(format!("{}/translate_a/single?client=gtx&sl=auto&tl={}&dj=1&dt=t&dt=bd&dt=qc&dt=rm&dt=ex&dt=at&dt=ss&dt=rw&dt=ld&q={}", host, lang, content)) .header("Accept", "application/json, text/plain, */*") .header("Accept-Encoding", "gzip") .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6") @@ -83,24 +94,50 @@ async fn send(client: &Client, host: String, lang: &str, content: &str) -> Resul vec![] } }; - let trans = tran_list - .into_iter() - .map(|x| x["trans"].as_str().unwrap_or_default().to_string()) - .collect::>() - .join(""); - let trans = trans - .trim() - .trim_matches('"') - .trim_matches('“') - .trim_matches('”') - .trim_matches('「') - .trim_matches('」') - .trim_matches('《') - .trim_matches('》'); + let mut result = vec![]; + for trans in tran_list { + let trans = trans["trans"].as_str().unwrap_or_default(); + let mut tmp = String::new(); + + for c in trans.chars() { + match c { + '\r' => { + // Assuming '\r' is always followed by '\n' and they indicate a new 'Tran' type 1. + if !tmp.is_empty() { + result.push(Tran { + typ: 0, + data: Some(tmp.clone()), + }); + tmp.clear(); + } + result.push(Tran { typ: 1, data: None }); + } + '\n' => { + // Do nothing assuming '\n' is always preceded by '\r', handled above. + } + ' ' => { + result.push(Tran { + typ: 0, + data: Some(tmp.clone()), + }); + tmp.clear(); + result.push(Tran { typ: 2, data: None }); + } + _ => tmp.push(c), + } + } + if !tmp.is_empty() { + result.push(Tran { + typ: 0, + data: Some(tmp), + }); + } + } + let result = TransVO { word: false, - trans: Some(trans.to_string()), + trans: Some(result), dicts: None, }; Ok(result) diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index 6def813..343fbed 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -41,8 +41,8 @@ fn handler(app: &AppHandle, event: MenuEvent) { "github" => { let _ = open::that("https://github.com/Borber/Tran"); } - "mirror" => config::mode(0), - "google" => config::mode(1), + "mirror" => config::mode(true), + "google" => config::mode(false), "exit" => { let panel = app.get_webview_window("panel").unwrap(); let _ = panel.hide(); diff --git a/src/App.css b/src/App.css index b011142..5277f2c 100644 --- a/src/App.css +++ b/src/App.css @@ -59,6 +59,7 @@ background-color: #262a30; overflow-x: hidden; overflow-y: scroll; + word-wrap: break-word; hyphens: auto; line-height: 1.2; diff --git a/src/App.tsx b/src/App.tsx index 50a65b4..4bf7c74 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,7 +13,7 @@ import { Resp } from "./model/resp" interface TransVO { word: boolean - trans: string + trans: Tran[] dicts: Dict[] } @@ -22,6 +22,11 @@ interface Dict { terms: string[] } +interface Tran { + typ: number + data: string +} + const App = () => { const panel = getCurrent() const [result, Result] = createSignal() @@ -90,13 +95,15 @@ const App = () => { data-tauri-drag-region class="result" // 因为全局的可拖拽导致双击正好能触发点击事件 - onClick={async () => { + onClick={async (e) => { console.log("copy") let content if (result() == undefined) { return } else if (!result()?.word) { - content = result()!.trans + content = e.target.innerHTML + .replace(/
/g, "\r\n") + .replace(/ /g, " ") } else { content = result()!.dicts[0].terms[0] } @@ -131,7 +138,19 @@ const App = () => { )} - {result()?.trans} + + + {(tran) => { + if (tran.typ == 0) { + return tran.data + } else if (tran.typ == 1) { + return
+ } else { + return `\u00A0` + } + }} +
+