Skip to content

fix some problems#1290

Open
xtyuns wants to merge 30 commits into
pot-app:masterfrom
xtyuns:master
Open

fix some problems#1290
xtyuns wants to merge 30 commits into
pot-app:masterfrom
xtyuns:master

Conversation

@xtyuns

@xtyuns xtyuns commented Jun 12, 2026

Copy link
Copy Markdown
Member

Fixs

Performance

  • Windows clipboard monitor: Polling → AddClipboardFormatListener event-driven, zero polling
  • LanguageDetector: OnceCell-cached, lingua model initialized once across the entire app lifetime
  • Plugin compilation cache: eval → new Function + Map cache, eliminates repeated disk I/O and recompilation

Features

xtyuns added 20 commits June 12, 2026 15:16
…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)
@xtyuns xtyuns mentioned this pull request Jun 12, 2026
@xtyuns xtyuns requested a review from Copilot June 12, 2026 07:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TargetArea with a componentized TranslateCard.
  • Performance-focused runtime changes: cached plugin compilation (eval → new Function + Map cache), 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.

Comment thread src/utils/invoke_plugin.js
Comment thread src/window/Translate/components/TranslateCard/useTranslate.js
Comment thread src-tauri/src/clipboard.rs Outdated
Comment thread src-tauri/src/clipboard.rs
Comment thread src/services/translate/deepl/index.jsx Outdated
Comment thread src/window/Translate/components/TranslateCard/useHistory.js
Comment thread src/window/Translate/components/TranslateCard/ResultContent.jsx Outdated
Comment thread src-tauri/src/clipboard.rs
xtyuns and others added 5 commits June 12, 2026 16:00
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
@xtyuns xtyuns marked this pull request as ready for review June 12, 2026 08:16
xtyuns added 2 commits June 13, 2026 09:19
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.
@xtyuns xtyuns force-pushed the master branch 2 times, most recently from 50caf59 to d020a7a Compare June 13, 2026 02:07
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.
@xtyuns xtyuns force-pushed the master branch 4 times, most recently from 375e64b to f6bbaa1 Compare June 13, 2026 05:18
@xtyuns xtyuns marked this pull request as draft June 13, 2026 05:40
@xtyuns xtyuns marked this pull request as ready for review June 13, 2026 06:32
@xtyuns xtyuns assigned xtyuns and unassigned xtyuns Jun 13, 2026
@xtyuns xtyuns requested a review from Pylogmon June 13, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants