feat(app): 활성 창 캡쳐·캡쳐 목적지 선택·단축키 Ctrl+Alt 재배치 + UX 손질
- Ctrl+Alt+A/Z 맨 앞 창 통째 캡쳐(DWM 확장 프레임 경계), S/X 드래그 캡쳐. A/S 새 대화, Z/X 마지막 대화 - 캡쳐 후 창을 확실히 앞으로(AttachThreadInput), capture.image 에 목적지 path 실어 그 Composer 만 소비 - 드래그 선택영역 원본 밝기·더블버퍼(번쩍임) — code-assistant-v2 복사본에서 이식 - 단축키 Ctrl+Alt+Q/W: Eclipse·ADT·SAP GUI 바인딩 목록 대조해 비어 있는 조합으로 - 버튼 cursor:pointer(Tailwind v4), 가운데 스피너, 스니펫→챗 버튼, 스니펫 검색창 높이 84→88 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2869985139
commit
47d31dbe06
@@ -18,15 +18,29 @@ use shell::window;
|
||||
const HOTKEYS: &[HotkeySpec] = &[
|
||||
HotkeySpec {
|
||||
id: "snap",
|
||||
accelerator: "Ctrl+Shift+8",
|
||||
accelerator: "Ctrl+Alt+W",
|
||||
},
|
||||
HotkeySpec {
|
||||
id: "snippet",
|
||||
accelerator: "Ctrl+Shift+7",
|
||||
accelerator: "Ctrl+Alt+Q",
|
||||
},
|
||||
// 캡쳐 4종: 윗줄(A/S) = 새 대화, 아랫줄(Z/X) = 마지막 기존 대화(없으면 새 대화).
|
||||
// 왼쪽(A/Z) = 맨 앞 창 통째, 오른쪽(S/X) = 드래그 영역.
|
||||
HotkeySpec {
|
||||
id: "capture",
|
||||
accelerator: "Ctrl+Shift+9",
|
||||
accelerator: "Ctrl+Alt+A",
|
||||
},
|
||||
HotkeySpec {
|
||||
id: "capture_area",
|
||||
accelerator: "Ctrl+Alt+S",
|
||||
},
|
||||
HotkeySpec {
|
||||
id: "capture_last",
|
||||
accelerator: "Ctrl+Alt+Z",
|
||||
},
|
||||
HotkeySpec {
|
||||
id: "capture_area_last",
|
||||
accelerator: "Ctrl+Alt+X",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -149,6 +163,35 @@ fn show_palette(app: &AppHandle) -> tauri::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 캡쳐 핫키 id 로 목적지 route 결정. `*_last` 는 마지막 기존 대화, 없으면 새 대화.
|
||||
fn capture_target(id: &str, last_session: Option<&str>) -> String {
|
||||
if id.ends_with("_last") {
|
||||
last_session.unwrap_or("/snap/new").to_string()
|
||||
} else {
|
||||
"/snap/new".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// 캡쳐 결과 공통 처리 — 팔레트 소환 → 목적지 대화 화면 → 그 화면 Composer 에 이미지 첨부.
|
||||
fn on_captured(app: AppHandle, target: String, data_url: String) {
|
||||
if let Err(error) = show_palette(&app) {
|
||||
eprintln!("[capture] 창 소환 실패: {error}");
|
||||
}
|
||||
bridge::push(
|
||||
&app,
|
||||
Push::Navigate {
|
||||
path: target.clone(),
|
||||
},
|
||||
);
|
||||
bridge::push(
|
||||
&app,
|
||||
Push::CaptureImage {
|
||||
path: target,
|
||||
data_url,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 표준 전역 단축키가 실행할 CodeAssist 동작을 한 곳에서 정함.
|
||||
fn on_hotkey(app: &AppHandle, id: &str) {
|
||||
match id {
|
||||
@@ -162,17 +205,20 @@ fn on_hotkey(app: &AppHandle, id: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
"capture" => {
|
||||
shell::capture::start(app.clone(), |app, data_url| {
|
||||
let _ = show_palette(&app);
|
||||
bridge::push(
|
||||
&app,
|
||||
Push::Navigate {
|
||||
path: "/snap/new".into(),
|
||||
},
|
||||
);
|
||||
bridge::push(&app, Push::CaptureImage { data_url });
|
||||
});
|
||||
"capture" | "capture_area" | "capture_last" | "capture_area_last" => {
|
||||
let target = app
|
||||
.state::<commands::RouteState>()
|
||||
.lock()
|
||||
.map(|routes| capture_target(id, routes.last_session.as_deref()))
|
||||
.unwrap_or_else(|_| "/snap/new".to_string());
|
||||
let deliver =
|
||||
move |app: AppHandle, data_url: String| on_captured(app, target, data_url);
|
||||
if id.starts_with("capture_area") {
|
||||
shell::capture::start(app.clone(), deliver);
|
||||
} else {
|
||||
let own_hwnd = window::native_handle(app);
|
||||
shell::capture::start_foreground_window(app.clone(), own_hwnd, deliver);
|
||||
}
|
||||
}
|
||||
|
||||
// 챗봇 토글: 이미 챗봇 화면이 떠 있으면 숨기고, 아니면 마지막 챗봇 위치로 데려온다.
|
||||
@@ -199,7 +245,7 @@ fn on_hotkey(app: &AppHandle, id: &str) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{chat_toggle_target, commands::Routes};
|
||||
use super::{capture_target, chat_toggle_target, commands::Routes};
|
||||
|
||||
#[test]
|
||||
fn 챗봇_토글은_현재_화면과_창_표시에_따라_결정한다() {
|
||||
@@ -219,13 +265,28 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn 마지막_챗봇_위치는_챗봇_화면에서만_바뀐다() {
|
||||
fn 마지막_기존_대화는_새_대화나_다른_화면으로_가도_유지된다() {
|
||||
let mut routes = Routes::default();
|
||||
routes.report("/snap/session-1");
|
||||
routes.report("/snippet");
|
||||
routes.report("/snap/s/session-1");
|
||||
routes.report("/snap/new");
|
||||
routes.report("/files");
|
||||
|
||||
assert_eq!(routes.current, "/files");
|
||||
assert_eq!(routes.last_snap, "/snap/session-1");
|
||||
assert_eq!(routes.last_snap, "/snap/new");
|
||||
assert_eq!(routes.last_session.as_deref(), Some("/snap/s/session-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn 캡쳐_핫키에_따라_새_대화나_마지막_기존_대화를_고른다() {
|
||||
assert_eq!(capture_target("capture", Some("/snap/s/last")), "/snap/new");
|
||||
assert_eq!(
|
||||
capture_target("capture_area", Some("/snap/s/last")),
|
||||
"/snap/new"
|
||||
);
|
||||
assert_eq!(
|
||||
capture_target("capture_last", Some("/snap/s/last")),
|
||||
"/snap/s/last"
|
||||
);
|
||||
assert_eq!(capture_target("capture_area_last", None), "/snap/new");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user