BigMarc 3d749e8139 feat: Implement LiveMonitor for real-time grammar and spelling checks across applications
- Added LiveMonitor class to scan text areas in the frontmost window for grammar and spelling issues.
- Introduced DetectedField struct to represent text areas with issues, including suggestions for corrections.
- Implemented functionality to apply corrections and dismiss suggestions.
- Added Permissions class to manage Accessibility permissions required for monitoring.
- Created SnippetStore for managing text-expansion snippets, including persistence to UserDefaults.
- Developed TextCapture class to read and write text from the frontmost application using Accessibility API and clipboard fallback.
- Implemented TextFieldScanner to traverse the Accessibility tree and collect editable text elements.
- Added TextReplacementCoordinator to orchestrate the text correction process.
- Integrated UpdaterManager for handling application updates using Sparkle.
- Created comprehensive unit tests for various components, including grammar corrections and entitlement checks.
2026-06-18 16:49:55 +02:00

40 lines
1.5 KiB
Swift

import SwiftUI
/// The "Ask" popover: type an instruction ("make it shorter", "translate to
/// German") that the local LLM applies to the current selection, in place.
struct AskView: View {
@EnvironmentObject private var app: AppState
@FocusState private var focused: Bool
var body: some View {
VStack(alignment: .leading, spacing: 14) {
Text("Ask GrammaGem")
.font(.headline)
Text("Acts on the text selected in the frontmost app. Runs on-device.")
.font(.caption)
.foregroundStyle(.secondary)
TextField("e.g. make it more formal", text: $app.askText)
.textFieldStyle(.roundedBorder)
.focused($focused)
.onSubmit { Task { await app.runAsk() } }
HStack {
if !app.gate.entitlements.unlimitedAIActions {
Text("\(app.gate.remainingAIActionsToday) free actions left today")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
Button("Cancel") { app.showAskDismiss() }
Button("Run") { Task { await app.runAsk() } }
.keyboardShortcut(.defaultAction)
.disabled(app.askText.trimmingCharacters(in: .whitespaces).isEmpty)
}
}
.padding(20)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.onAppear { focused = true }
}
}