fix some problems#1290
Open
xtyuns wants to merge 30 commits into
Open
Conversation
…er on Windows Bump all plugins-workspace deps from fa8ee1d4 to 1e407ed5. Fixes null pointer dereference in tauri-plugin-single-instance's Windows platform code.
…tListener Windows: replace 500ms polling loop with message-only window + AddClipboardFormatListener for true zero-polling clipboard monitoring. The hidden window runs its own message loop; WM_CLIPBOARDUPDATE triggers translations immediately. A 500ms timer checks the enabled flag and posts WM_CLOSE when toggled off. Non-Windows: keep adaptive polling (500ms→1s→2s backoff) as fallback. Also: atomic bool replaces Mutex<String> for the enabled flag, eliminating lock contention on every poll iteration.
Duplicate zip-packing logic appeared identically in webdav/put and local/put (~40 lines each). Zip-extraction logic appeared in webdav/get, local/get, and aliyun/get. backup_to_zip(zip_path) — packs config.json + history.db + plugins/ restore_from_zip(zip_path) — extracts zip into the app config dir local() shrinks from 55 to 13 lines. webdav and aliyun simplified similarly.
Previously init_lang_detect() built the detector and discarded it, and lang_detect() rebuilt it on every call — expensive since lingua model initialization is costly. Now the detector is a static Lazy<LanguageDetector>, initialized once on first use. init_lang_detect() and its call site in main.rs are removed since initialization is automatic.
…d mappings
Each of the 6 language detection engines (baidu, tencent, google,
niutrans, yandex, bing) defined its own lang_map with 15-20 nearly
identical entries. Only a handful of entries differ per engine.
COMMON_LANG_MAP holds the 17 universal mappings. Each engine builds
its full map via spread: { ...COMMON_LANG_MAP, ...engineSpecific }.
File shrinks from 334 to 228 lines.
Create useDebounce(value, delay) hook — returns a debounced value via useEffect + setTimeout/clearTimeout. In SourceArea: replace broken render-local let variable with useDebounce(sourceText, 300). An effect on debouncedSourceText triggers detect_language + syncSourceText when dynamicTranslate is enabled and the user stops typing for 300ms. Also removes an unused module-level 'timer' variable left from earlier refactoring.
Before: index.jsx — 880 lines, 5 responsibilities in one file
After:
index.jsx — 332 lines, orchestration only
useTranslate.js — 222 lines, translation + translate-back logic
ResultContent.jsx — 118 lines, string/dict result rendering
Key improvements:
- Shared callTranslate() eliminates ~100 lines of plugin-vs-builtin
duplication that appeared identically in translate() and
translateBack()
- Stale-request guard uses useRef(id) instead of module-level array
- ResultContent splits inline rendering into focused sub-views
(PronunciationView, ExplanationsView, etc.)
- Service selector extracted into serviceIcon/serviceLabel helpers
…ions, useTts Before: 3 files (index + useTranslate + ResultContent) = 672 lines After: 6 files = 713 lines Splits: ServiceSelector.jsx — dropdown + loader + collapse toggle (was inline in header) TranslateActions.jsx — speak/copy/translate-back/retry/collection buttons (was inline in footer) useTts.js — TTS plugin info loading + handleSpeak (was inline in index) index.jsx shrinks from 332 to 146 lines.
useHistory.js — SQLite history persistence, auto-creates table on first write useAutoCopy.js — consolidated auto-copy for all three modes (source/target/source_target) Both hooks are consumed internally by useTranslate, removing the need for index.jsx to import writeText, sendNotification, Database, or pass addToHistory as a parameter. All auto-copy logic is now unified in one place within the translate pipeline. index.jsx shrinks from 154 to 124 lines.
TargetArea was a misleading name — it's a card for one translation service instance, not a generic 'area'. Rename to TranslateCard to clarify its role as a single-service card within the translate window. Directory: components/TargetArea/ → components/TranslateCard/ Component: TargetArea → TranslateCard
When useAutoCopy.js absorbed writeText internally, the import in TranslateCard/index.jsx was incorrectly removed. TranslateActions still receives writeText as a prop and was silently undefined, causing the component to fail on render — window appeared blank.
…add debug logs - ServiceSelector was using a plain div instead of NextUI's CardHeader, losing styling, rounded corners, and drag-handle props - autoCopySource(text) was called before try-catch; a throw would silently kill translate() with no result or error shown - Cards defaulted to collapsed; add auto-expand effect when result or error arrives - Add info() traces at each translate pipeline stage for diagnosis
…nslate Translate/index.jsx had module-level side effects (blur listeners registered on import, never cleaned up) and 4 inline event effects with module-level timer variables. Extract: useTranslateWindow.js — blur-to-close, pin-to-top, window position/size usePluginList.js — plugin metadata loading + reload listener index.jsx shrinks from 345 to 175 lines. All event listeners use proper useEffect cleanup on unmount.
main.jsx calls await initStore() before ReactDOM.createRoot, so the store is always fully initialized before any component mounts. No race condition exists. Remove the empty at module load. Remove the Proxy-based lazy init (overengineering). Just init once in initStore() with an idempotent guard. 45 lines → 21 lines.
initEnv() also runs before React renders (called in main.jsx before createRoot), so no race condition. Just add an idempotent guard for consistency.
…sandbox Performance: cache compiled plugin functions in a Map — each plugin compiles once, subsequent calls skip disk I/O and re-compilation. Replace eval() with new Function() that only exposes pluginType and utils to the plugin code, not the entire local scope. Security: sandbox plugin execution via Proxy + with() statement. Only 40+ safe globals are allowed (Array, Object, Promise, etc.). Dangerous APIs (fetch, __TAURI__, window, document, eval, Worker, WebSocket, etc.) return undefined. Writes to globals are silently ignored.
Old translate_by_free used www2.deepl.com/jsonrpc with a complex JSON-RPC protocol (timestamp obfuscation, iCount, random ID). DeepL's new oneshot-free API is a simple REST POST: POST https://oneshot-free.www.deepl.com/v1/storefront/translate { text, source_lang, target_lang, language_model, usage_type } Language codes in info.ts stay unchanged (uppercase ZH, EN, JA). translate_by_free maps them to oneshot-free lowercase format via a local langMap. translate_by_key and info.ts are not modified.
…tore)
When restoring a backup via local(operate='get'), config.json on
disk is overwritten. The file watcher triggers reload_store which
loads the new config into memory — but never re-registers the global
shortcuts. Users would have to restart the app for shortcuts to work.
Now reload_store drops the store lock and calls register_shortcut('all')
to re-register all 4 shortcuts from the freshly loaded config.
… for oneshot-free - info.ts: distinguish zh_cn (ZH-HANS) from zh_tw (ZH-HANT) in Language enum, enabling proper Traditional Chinese translation - index.jsx: add langMap + toLowerCase() fallback to normalize uppercase enum codes to lowercase oneshot-free API format - Remove redundant local langMap that was duplicating the enum - Add info log on each free translation call
Replace Dropdown with Autocomplete + allowsCustomValue so users can
type a custom model name not in the predefined list.
- Use objects { key, label } as collection items (React Aria WeakMap
requires object keys, not strings)
- Use inputValue for controlled display (not selectedKey), so custom
typed values aren't cleared when unmatched
- Wrap CardHeader in plain div for dragHandleProps (NextUI v2.4.x
no longer forwards unknown DOM props to underlying element)
Closed
There was a problem hiding this comment.
Pull request overview
This PR refactors the Translate window UI into smaller hooks/components, improves runtime performance (especially for plugins and clipboard monitoring), and addresses several reported issues (DeepL language codes, hotkey restore, configurable ChatGLM models, etc.).
Changes:
- Refactor Translate window: extract window lifecycle (blur-to-close / always-on-top / persist size+pos) and plugin list loading into dedicated hooks; replace monolithic
TargetAreawith a componentizedTranslateCard. - Performance-focused runtime changes: cached plugin compilation (eval →
new Function+Mapcache), debounced dynamic translation, cached Rust language detector, and Windows event-driven clipboard monitoring. - Fixes/features: DeepL target language codes (ZH-HANS/ZH-HANT), ChatGLM model selection allows custom values, backup/restore uses unified app config dir helper, and hotkey re-registration on store reload.
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/window/Translate/useTranslateWindow.js | New hook managing translate window blur-close, always-on-top, and persisting window size/position. |
| src/window/Translate/usePluginList.js | New hook to load plugin metadata and respond to reload_plugin_list events. |
| src/window/Translate/index.jsx | Refactor Translate window to use new hooks and the new TranslateCard component. |
| src/window/Translate/components/TranslateCard/useTts.js | New hook encapsulating TTS behavior (builtin + plugin). |
| src/window/Translate/components/TranslateCard/useTranslate.js | New hook encapsulating translate + translate-back logic, history, and auto-copy. |
| src/window/Translate/components/TranslateCard/useHistory.js | New hook to append translation history to SQLite. |
| src/window/Translate/components/TranslateCard/useAutoCopy.js | New hook consolidating auto-copy modes (source/target/source_target). |
| src/window/Translate/components/TranslateCard/TranslateActions.jsx | New action bar component (speak/copy/translate-back/retry/collection). |
| src/window/Translate/components/TranslateCard/ServiceSelector.jsx | New service selector header with dropdown and loading indicator. |
| src/window/Translate/components/TranslateCard/ResultContent.jsx | New result renderer for string vs structured dictionary results. |
| src/window/Translate/components/TranslateCard/index.jsx | New TranslateCard component replacing the previous monolithic target area. |
| src/window/Translate/components/TargetArea/index.jsx | Removed prior monolithic TargetArea implementation. |
| src/window/Translate/components/SourceArea/index.jsx | Use new debounce hook to stabilize dynamic-translate triggering. |
| src/utils/store.js | Make store initialization explicit/idempotent; load config and watch for changes. |
| src/utils/lang_detect.js | Refactor detection language maps to shared COMMON_LANG_MAP for consistency. |
| src/utils/invoke_plugin.js | Replace eval with compilation cache + sandboxed new Function wrapper. |
| src/utils/env.js | Make env initialization idempotent and document init order. |
| src/services/translate/deepl/info.ts | Update DeepL Chinese codes to ZH-HANS / ZH-HANT. |
| src/services/translate/deepl/index.jsx | Switch free DeepL endpoint and adjust language normalization + error formatting. |
| src/services/translate/chatglm/Config.jsx | Replace dropdown with autocomplete allowing custom model strings. |
| src/hooks/useDebounce.js | Add shared debounce hook. |
| src/hooks/index.jsx | Export the new debounce hook. |
| src-tauri/src/tray.rs | Clipboard monitor state update uses AtomicBool (no mutex string). |
| src-tauri/src/main.rs | Use AtomicBool for clipboard monitor state; remove eager lang-detect init. |
| src-tauri/src/lang_detect.rs | Cache lingua LanguageDetector via Lazy (init once for app lifetime). |
| src-tauri/src/config.rs | Add app_config_dir() helper for consistent config-path resolution. |
| src-tauri/src/cmd.rs | Reload store now also re-registers shortcuts to restore hotkeys after backup restore. |
| src-tauri/src/clipboard.rs | Windows: event-driven clipboard monitor via AddClipboardFormatListener; non-Windows: adaptive polling. |
| src-tauri/src/backup.rs | Refactor backup/restore zip logic using app_config_dir() helper. |
| src-tauri/Cargo.toml | Add Windows crate feature flags needed for new clipboard monitor implementation. |
| src-tauri/Cargo.lock | Lockfile updates for dependency source revisions / Cargo lock format. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Line 162 in translateBack referenced targetLang and text which are not defined in that scope (they belong to the translate function). This would cause a ReferenceError when clicking the translate-back button. Fix: use newTargetLanguage (the computed target lang in this scope) and resultText (the parameter) instead.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…itor start_clipboard_monitor registers a Win32 window class via RegisterClassA on each call. When toggling the monitor off and back on, the class name 'PotClipMonWnd' already exists, causing RegisterClassA to fail with ERROR_CLASS_ALREADY_EXISTS and the function returns early — the monitor never restarts. Fix: check GetLastError() and only abort on errors other than ALREADY_EXISTS. Also reuse the PCSTR cast for both RegisterClassA and CreateWindowExA.
…import - clipboard.rs: remove redundant [clipboard] translate log (already logged in translate pipeline) - deepl/index.jsx: remove unused import after log cleanup - ResultContent.jsx: replace nanoid() with stable keys from result data for PronunciationView; minor formatting cleanup
Allows manual trigger of the release packaging workflow from the GitHub Actions UI, useful for testing builds without pushing to master.
The sandbox refactor commit (cd84e3d) accidentally dropped the closing brace of compilePlugin(), causing a SyntaxError at module load.
50caf59 to
d020a7a
Compare
Cargo.toml had version 0.0.0 while tauri.conf.json had 3.0.7. Running 'pnpm tauri build' directly (without the CI's change-version step) would fail because Tauri rejects 0.0.0 as a release version. Also update the CI workflow's sed pattern from matching '0.0.0' to a wildcard '.*' so it works with any current version.
375e64b to
f6bbaa1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixs
Performance
Features