feat: swap translation languages with Return on empty input
This commit is contained in:
@@ -136,9 +136,9 @@ Notes autosave locally as RTFD packages, including embedded images, under `~/Lib
|
|||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
Search for **Conjure Translate**, identified by its centered 🌏 icon, and open it. Choose a provider in the pill-shaped header selector, enter text in the search bar, then press Return or click **Translate**. Typing alone never sends text. When the translation is available, the action changes to **Copy**: press Return or click it to copy the full translation and close Conjure. Command-C still copies selected output. Editing the input restores **Translate**.
|
Search for **Conjure Translate**, identified by its centered 🌏 icon, and open it. With empty or whitespace-only input, Return or **Swap languages** reverses the source and target without submitting a request. Enter text to change the action to **Translate**; press Return or click it to submit. Typing alone never sends text. When the translation is available, the action changes to **Copy**: press Return or click it to copy the full translation and close Conjure. Command-C still copies selected output. Editing the input clears the result and restores **Translate**, or **Swap languages** if empty. The action is disabled while translating.
|
||||||
|
|
||||||
The footer places **Translate/Copy** and its Return keycap before **Actions**, whose Command and K keys use separate keycaps, matching the launcher.
|
The footer places the current primary action and its Return keycap before **Actions**, whose Command and K keys use separate keycaps, matching the launcher.
|
||||||
|
|
||||||
Press Command-P or click the provider selector to expand a searchable action-style popup from its top edge downwards, hiding the indicator until the popup closes. A checkmark marks the current provider. Use Up/Down and Return or click a provider to select it; Escape, Back, or clicking outside closes the popup. Changing providers clears the previous result without submitting a request.
|
Press Command-P or click the provider selector to expand a searchable action-style popup from its top edge downwards, hiding the indicator until the popup closes. A checkmark marks the current provider. Use Up/Down and Return or click a provider to select it; Escape, Back, or clicking outside closes the popup. Changing providers clears the previous result without submitting a request.
|
||||||
|
|
||||||
|
|||||||
@@ -239,8 +239,11 @@ final class TranslationController: NSViewController {
|
|||||||
func inputChanged() {
|
func inputChanged() {
|
||||||
cancelRequest()
|
cancelRequest()
|
||||||
output.string = ""
|
output.string = ""
|
||||||
translateButton.title = "Translate"
|
let isEmpty = (input?() ?? "").trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||||
translateButton.toolTip = "Translate the input (Return). Escape dismisses Conjure."
|
translateButton.title = isEmpty ? "Swap languages" : "Translate"
|
||||||
|
translateButton.toolTip = isEmpty
|
||||||
|
? "Swap source and target languages (Return)."
|
||||||
|
: "Translate the input (Return). Escape dismisses Conjure."
|
||||||
settingsButton.isHidden = true
|
settingsButton.isHidden = true
|
||||||
languageButton.title = "\(languageName(sourceLanguage)) → \(languageName(targetLanguage))"
|
languageButton.title = "\(languageName(sourceLanguage)) → \(languageName(targetLanguage))"
|
||||||
output.setAccessibilityLabel("\(languageName(targetLanguage)) translation")
|
output.setAccessibilityLabel("\(languageName(targetLanguage)) translation")
|
||||||
@@ -269,6 +272,10 @@ final class TranslationController: NSViewController {
|
|||||||
|
|
||||||
@objc func performPrimaryAction() {
|
@objc func performPrimaryAction() {
|
||||||
guard task == nil else { return }
|
guard task == nil else { return }
|
||||||
|
if (input?() ?? "").trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
|
swapLanguages()
|
||||||
|
return
|
||||||
|
}
|
||||||
guard !output.string.isEmpty else { translate(); return }
|
guard !output.string.isEmpty else { translate(); return }
|
||||||
pasteboard.clearContents()
|
pasteboard.clearContents()
|
||||||
if pasteboard.setString(output.string, forType: .string) {
|
if pasteboard.setString(output.string, forType: .string) {
|
||||||
|
|||||||
@@ -58,6 +58,26 @@ private func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.fl
|
|||||||
|
|
||||||
@Suite(.serialized)
|
@Suite(.serialized)
|
||||||
struct TranslationExecutionTests {
|
struct TranslationExecutionTests {
|
||||||
|
@Test @MainActor
|
||||||
|
func emptyPrimaryActionSwapsLanguagesWithoutSubmitting() {
|
||||||
|
_ = NSApplication.shared
|
||||||
|
let suite = "TranslateEmpty.\(UUID().uuidString)"
|
||||||
|
let defaults = UserDefaults(suiteName: suite)!
|
||||||
|
defer { defaults.removePersistentDomain(forName: suite) }
|
||||||
|
let tool = TranslationController(defaults: defaults,
|
||||||
|
myMemory: { _, _, _ in Issue.record("Empty input must not submit"); return "unexpected" })
|
||||||
|
tool.input = { " \n\t" }
|
||||||
|
_ = tool.view
|
||||||
|
let button = descendants(tool.view).compactMap { $0 as? NSButton }
|
||||||
|
.first { $0.action == NSSelectorFromString("performPrimaryAction") }!
|
||||||
|
button.performClick(nil)
|
||||||
|
#expect(tool.sourceLanguage == "ja")
|
||||||
|
#expect(tool.targetLanguage == "en")
|
||||||
|
tool.performPrimaryAction()
|
||||||
|
#expect(tool.sourceLanguage == "en")
|
||||||
|
#expect(tool.targetLanguage == "ja")
|
||||||
|
}
|
||||||
|
|
||||||
@Test @MainActor
|
@Test @MainActor
|
||||||
func languageActionsInvalidateOldResultsAndApplyTheSelectedPair() async throws {
|
func languageActionsInvalidateOldResultsAndApplyTheSelectedPair() async throws {
|
||||||
_ = NSApplication.shared
|
_ = NSApplication.shared
|
||||||
|
|||||||
Reference in New Issue
Block a user