mirror of
https://github.com/BigMarc/grammargem.git
synced 2026-10-13 17:01:55 +02:00
Native SwiftUI menu-bar app: global hotkey → capture selected text (AX API / clipboard fallback) → grammar (Harper) / local AI (MLX) → replace in place. Free/paid entitlement gate + Lemon Squeezy device-cap licensing, $0 marginal cost per user. Heavyweight engines behind protocol stubs so it builds out of the box. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40 lines
1.5 KiB
Swift
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 }
|
|
}
|
|
}
|