grammargem/Sources/GrammarGem/Grammar/WritingScore.swift
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

21 lines
1013 B
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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())))
}
}