Skip to content

feat(windows): add selected-text polish workflow - #851

Merged
H-Chris233 merged 9 commits into
Open-Less:betafrom
SeanZhang226:feat/selection-polish-core
Aug 3, 2026
Merged

feat(windows): add selected-text polish workflow#851
H-Chris233 merged 9 commits into
Open-Less:betafrom
SeanZhang226:feat/selection-polish-core

Conversation

@SeanZhang226

@SeanZhang226 SeanZhang226 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

OpenLess voice input is very convenient, but speaking is not always practical in meetings, shared offices, or public places.

This PR adds a Windows-first selected-text polish workflow:

  1. Select text in any application.
  2. Trigger OpenLess with a global shortcut.
  3. OpenLess sends the selected text to the configured LLM and applies the selected style.
  4. The result either replaces the original selection directly or opens in an editable preview before pasting.

The goal is to provide a small, practical keyboard-based complement to voice input, not a new chat workflow.

What changed

  • Added a global shortcut and settings section for selected-text polish.

    • Dictation keeps the existing default: Right Ctrl.
    • Selected-text polish defaults to Right Alt.
    • Users can record and use any shortcut they prefer.
  • Added a safe selected-text workflow.

    • Captures selected text through the clipboard.
    • Verifies the original target before replacing text.
    • Supports direct replacement and editable preview modes.
    • Stores selected-text results in history separately from voice sessions.
  • Added separate prompts for the two workflows.

    • Dictation / ASR uses the existing style-pack prompt.
    • Selected written text uses a dedicated selection prompt.
    • Built-in Light, Structured, and Formal styles now have editable and restorable selection prompts.
  • Added shortcut conflict checks and lightweight capsule feedback.

Platform scope

This feature was developed and tested on Windows through real desktop use.

OpenLess itself remains cross-platform, but this selected-text workflow currently relies on Windows-specific desktop integration for global shortcuts, foreground-window checks, and safe cross-application replacement. macOS and Linux support has not yet been implemented or validated.

Privacy and configuration

  • This change does not modify users' existing ASR or LLM provider settings, models, endpoints, or credentials.
  • No user configuration, history, style-pack data, or API keys are included in this PR.

Validation

  • npm test — passed (23 frontend checks)
  • cargo test --manifest-path src-tauri/Cargo.toml --lib — passed (788 tests)
  • Tested manually in real Windows desktop workflows.

Disclosure

This contribution was implemented and tested with assistance from an AI coding agent. The contributor reviewed the design, performed real-use testing, and is responsible for the final submission.

Note

A full Windows NSIS installer smoke test should still be completed through the project's existing IME packaging workflow before a release build is published.


PR Type

Enhancement, Bug fix


Description

  • Add Windows selected-text polish workflow with global shortcut.

  • Support direct replacement and editable preview modes.

  • Introduce selection-specific prompts and separate history source.

  • Gate to desktop and fix mobile compile.


Diagram Walkthrough

flowchart LR
  A["Select text in any app"] --> B["Global shortcut (Right Alt)"]
  B --> C["Capture selection and insertion target"]
  C --> D{"Output mode?"}
  D -- "DirectReplace" --> E["Validate target and selection"]
  D -- "PreviewConfirm" --> G["Show editable preview"]
  G --> H["User confirms"]
  H --> E
  E --> F["Insert polished text"]
  E --> I["Record history with source"]
Loading

File Walkthrough

Relevant files
Enhancement
18 files
selection_polish.rs
Implement selected-text polish workflow with safe insertion
+556/-0 
types.rs
Add selection polish types, prefs, and prompt dispatch     
+202/-0 
selection.rs
Capture and validate Windows insertion target safely         
+290/-0 
capsule_focus.rs
Add selection polish capsule feedback with epoch guarding
+118/-6 
hotkey.rs
Add selection polish shortcut event and monitor support   
+97/-9   
hotkey_loops.rs
Wire selection polish hotkey supervisor and bridge             
+171/-8 
hotkeys.rs
Add selection polish hotkey command with collision checks
+137/-0 
selection_polish.rs
Expose selection polish workflow as Tauri command               
+7/-0     
selection_polish_preview.rs
Add preview confirmation and cancel commands                         
+22/-0   
coordinator.rs
Integrate selection polish coordinator state and listeners
+130/-12
lib.rs
Register selection polish preview window and IPC handlers
+73/-11 
style_pack.rs
Persist and migrate selection prompt in style packs           
+17/-1   
dictation.rs
Tag dictation history with voice source                                   
+11/-2   
settings.rs
Refresh selection polish hotkey on settings save                 
+20/-0   
polish.rs
Harden prompt against question-like source text                   
+25/-1   
SelectionPolishPreview.tsx
Build editable preview window for polished text                   
+76/-0   
SelectionPolishSection.tsx
Add settings UI for selection polish options                         
+95/-0   
selection-polish-preview.ts
Add IPC helpers for preview confirmation flow                       
+20/-0   
Bug fix
1 files
hotkey.rs
Remove desktop-only event from mobile stub                             
+2/-0     
Tests
1 files
style_pack_tests.rs
Test selection prompt migration and sync                                 
+36/-2   
Additional files
24 files
history.rs +2/-1     
mod.rs +14/-0   
qa.rs +1/-0     
qa_session.rs +1/-0     
style_pack_archive.rs +2/-0     
selection_formal.md +19/-0   
selection_light.md +16/-0   
selection_structured.md +18/-0   
App.tsx +6/-1     
Capsule.tsx +131/-1 
en.ts +43/-0   
ja.ts +43/-0   
ko.ts +43/-0   
zh-CN.ts +43/-0   
zh-TW.ts +43/-0   
hotkey.ts +5/-0     
hotkeys.ts +15/-1   
index.ts +7/-0     
mock-data.ts +12/-0   
stylePrefs.test.ts +3/-0     
types.ts +16/-0   
main.tsx +2/-0     
Style.tsx +112/-30
tabs.tsx +2/-0     

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit a27023c)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

In confirm_selection_polish_preview, the success capsule always says "已替换" regardless of the InsertStatus returned by the inserter. When the inserter falls back to CopiedFallback (it could not send a paste and copied the text to the clipboard instead), the user is told the text was replaced even though it was only copied, and may never paste it. The DirectReplace path already distinguishes this case via selection_polish_success_message.

finish_selection_polish_capsule(&self.inner, CapsuleState::Done, "已替换");
Preview state leak

In confirm_selection_polish_preview, the pending preview is removed with take() before the target is reactivated and validated. If reactivation or validation fails, or the inserter returns Failed, the function returns an error without hiding the preview window or restoring the pending preview. The user is left with an open preview whose confirm action can no longer succeed ("selectionPolishPreviewUnavailable") until they manually cancel/close it.

if !crate::selection::reactivate_selection_insertion_target(&preview.insertion_target) {
    return Err("selectionPolishTargetUnavailable".into());
}
let validation = crate::selection::validate_selection_insertion_target(
    &preview.insertion_target,
    &preview.source_text,
);
if let Some(code) = validation.error_code() {
    return Err(code.to_string());
}

let prefs = self.inner.prefs.get();
let status = self.inner.inserter.insert(
    &text,
    prefs.restore_clipboard_after_paste,
    prefs.paste_shortcut,
);
if status == InsertStatus::Failed {
    return Err("selectionPolishInsertFailed".into());

@SeanZhang226
SeanZhang226 force-pushed the feat/selection-polish-core branch from 274a73f to e53af2e Compare July 21, 2026 16:35
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit e53af2e

@appergb

appergb commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

不是,哥们,你改啥了?新增 2000 行,减 300 行。

@H-Chris233

Copy link
Copy Markdown
Collaborator

不是,哥们,你改啥了?新增 2000 行,减 300 行。

我看这个不错啊,选区润色非常好的功能。你不要,我要了这个功能。

@H-Chris233

Copy link
Copy Markdown
Collaborator

这个PR由我接手

@H-Chris233 H-Chris233 self-assigned this Aug 2, 2026
SeanZhang226 and others added 2 commits August 2, 2026 21:30
PR Open-Less#851 新增的 coordinator/selection_polish 模块无条件编译,但 selection
模块在 mobile 走 mobile_stubs/selection.rs stub,缺少 SelectionInsertionTarget
等桌面 API,导致 CI 的 Android cargo check 失败(E0432/E0425)。

选区润色是桌面(Windows-first)工作流,mobile 不注册全局热键、不做跨应用
替换。将相关代码统一加 #[cfg(not(mobile))] 门控:
- coordinator.rs: selection_polish 模块、Inner 字段、热键绑定方法
- hotkey_loops.rs: 选区润色热键 supervisor/bridge 函数与事件分支
- mobile_stubs/hotkey.rs: 移除桌面专属事件变体
- commands: set_selection_polish_hotkey 与 SettingsWriter 实现

本地验证:cargo check (Windows) 与 cargo check --target
aarch64-linux-android 均通过。
@H-Chris233
H-Chris233 force-pushed the feat/selection-polish-core branch from e53af2e to 30ff274 Compare August 2, 2026 13:59
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 30ff274

按 review 反馈清理 PR diff 噪音,不改变任何行为:
- .gitignore: 移除作者本机诊断产物(/.codegraph/、/artifacts/、/baseline-*.log、
  /cargo-*.log、/docs/notes/、+/),恢复 beta 原样
- package.json / scripts/tauri-dev-visible.mjs: 移除开发验收专用 npm script 与脚本
- 恢复被重排的既有代码格式(import 分组、match arm、函数签名、空行、
  测试断言排版、serde 属性、mod 声明顺序等),仅保留功能改动

本地验证:cargo check (Windows) 与 --target aarch64-linux-android 通过,
cargo test --lib 885 passed(1 个环境相关的符号链接权限测试除外)。
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 3008b70

- types.rs: selection_polish_hotkey 注释从「默认右 Control」改为实际默认
  Right Alt(与 default_selection_polish_hotkey 及前端保持一致)
- coordinator.rs: inject_hotkey_click_for_dev 修正 beta 遗留的多余缩进
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 695b30e

macOS/Linux 尚未实现等效的前台窗口/焦点控件校验,无法保证云端等待
期间结果不会落到用户切换后的应用或控件上。此前非 Windows 分支的
selection_insertion_target_is_captured 直接返回 true,用户一旦在 macOS
手动启用该功能,会执行无校验替换,与「安全替换」的承诺相悖。

- selection.rs: 非 Windows 一律返回 false,阻止选区文本发给 provider
- SelectionPolishSection: 设置入口仅 Windows 显示(macOS/Linux 隐藏)
is_non_asr_selection_prompt / is_asr_style_prompt / is_generic_selection_prompt
是为从未合入 beta 的中间构建写的字符串匹配恢复逻辑('short-lived
pre-separation build'),属于 YAGNI 违反且匹配脆弱。删除恢复逻辑与
三个 helper,保留 v1 风格包 selection_prompt 为空的默认值填充;
对应测试同步为 migration_fills_empty_selection_prompts_with_style_defaults。
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit e30afaf

新增 settings.selectionPolish / style.pack.selection* / capsule.selectionPolish /
selectionPolishPreview 四组 key,覆盖 en/ja/ko/zh-CN/zh-TW。SelectionPolishSection、
SelectionPolishPreview、Capsule 提示、Style 工作流切换等 UI 文案全部改用 t(),
消除新组件与项目其余部分的 i18n 混用。风格包模板默认值(数据)保留中文。
PR 原改动把内置包的编辑按钮从禁用改为可编辑(行为变更未在描述中声明)。
按接手 review 意见恢复 beta 原样:内置包仅可查看/重置,不进入编辑器。
审查发现的收尾问题(子代理复核):
- 内联编辑器提示改用已存在的 selectionPromptHint / dictationPromptHint
- 编辑器头部改用独立 dictationPromptEditorDesc / selectionPromptEditorDesc
  (保留「当前编辑…输入对象是…」原意,避免与内联提示同屏重复)
- 风格卡片「当前」徽章改用 style.pack.current
- ja: selectionPromptHint 语感修正(ASRは通らず → ASRは経由せず)
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit b0f5635

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit a27023c

@H-Chris233
H-Chris233 merged commit 8a2febf into Open-Less:beta Aug 3, 2026
5 checks passed
H-Chris233 added a commit that referenced this pull request Aug 3, 2026
Resolve conflicts with latest beta:
- credentials.rs: keep PR's async spawn_blocking refactor, fold in beta's
  LLM_TEMPERATURE_ACCOUNT branch and new Volcengine auth-mode accounts
- CodingAgentSection.tsx: keep PR's OpenCode model discovery UI, port to
  beta's SelectLite component
- SelectionPolishSection.tsx: drop undefined ShortcutRecorder props
  (alignRecordButton/modifierPresets) from #851 so frontend tsc passes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants