mirror of
https://github.com/BigMarc/grammargem.git
synced 2026-10-13 17:01:55 +02:00
- 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.
21 lines
1013 B
Swift
21 lines
1013 B
Swift
import Foundation
|
||
|
||
/// A deterministic, on-device "cleanliness" score (0–100) for a chunk of text.
|
||
///
|
||
/// Honest by construction: it's defined relative to *this* text — weighted issues
|
||
/// per 100 words — never a hidden cloud cohort, and grammar/spelling are always
|
||
/// fully resolvable, so a clean document genuinely reaches 100. (Contrast with
|
||
/// Grammarly's percentile-vs-other-users score, which needs telemetry + an
|
||
/// account; see docs/grammarly-value-plan.md §5.)
|
||
enum WritingScore {
|
||
/// - Parameters:
|
||
/// - weightedIssues: sum of `GrammarKind.severity` across outstanding issues.
|
||
/// - words: total words in the scanned text.
|
||
static func score(weightedIssues: Int, words: Int) -> Int {
|
||
guard words > 0 else { return 100 }
|
||
let perHundredWords = Double(weightedIssues) / Double(words) * 100.0
|
||
// Each weighted issue per 100 words costs ~4 points; clamp to 0…100.
|
||
return max(0, min(100, Int((100.0 - perHundredWords * 4.0).rounded())))
|
||
}
|
||
}
|