grammargem/Sources/GrammaGem/AI/CustomModesStore.swift
BigMarc a7bf45b466 Add management window; sales-focused README; FounderGem LLC
- Management app: sidebar window with Dashboard (usage insights), Devices
  (slot usage, deactivate this Mac), Writing Modes (custom + app-aware map),
  Dictionary, Snippets, Shortcuts, AI Model, Privacy, General, License, About.
- New local stores: Preferences (launch-at-login, appearance), UsageStats
  (privacy-friendly local insights), CustomModesStore, SnippetStore.
- License model now tracks device name + activation usage/limit for the
  Devices screen.
- README rewritten as a conversion-focused product page (no internal cost/setup).
- Legal entity set to FounderGem LLC across README, LICENSE, Info.plist, About.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:28:23 +02:00

72 lines
2.3 KiB
Swift

import Foundation
/// User-created Writing Modes (paid) plus per-app Mode overrides for App-Aware
/// switching. Persisted locally; the built-in modes live in `ModeRegistry`.
@MainActor
final class CustomModesStore: ObservableObject {
@Published private(set) var customModes: [WritingMode]
/// bundleID -> mode id (overrides the default App-Aware mapping).
@Published private(set) var appOverrides: [String: String]
private let modesKey = "GrammaGem.customModes"
private let mapKey = "GrammaGem.appModeOverrides"
private let d = UserDefaults.standard
init() {
if let raw = d.data(forKey: modesKey),
let decoded = try? JSONDecoder().decode([WritingMode].self, from: raw) {
customModes = decoded
} else {
customModes = []
}
appOverrides = (d.dictionary(forKey: mapKey) as? [String: String]) ?? [:]
}
/// Built-in + custom modes (custom ones are always paid).
func allModes() -> [WritingMode] { ModeRegistry.all + customModes }
func addCustom(name: String, prompt: String) {
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
let mode = WritingMode(
id: "custom-\(UUID().uuidString.prefix(8))",
name: trimmed,
systemPrompt: prompt.trimmingCharacters(in: .whitespacesAndNewlines),
isPaid: true,
lengthCap: nil,
autoFormat: "Custom"
)
customModes.append(mode)
persistModes()
}
func removeCustom(id: String) {
customModes.removeAll { $0.id == id }
appOverrides = appOverrides.filter { $0.value != id }
persistModes()
persistMap()
}
func setOverride(bundleID: String, modeID: String) {
let b = bundleID.trimmingCharacters(in: .whitespaces)
guard !b.isEmpty else { return }
appOverrides[b] = modeID
persistMap()
}
func clearOverride(bundleID: String) {
appOverrides.removeValue(forKey: bundleID)
persistMap()
}
private func persistModes() {
if let raw = try? JSONEncoder().encode(customModes) {
d.set(raw, forKey: modesKey)
}
}
private func persistMap() {
d.set(appOverrides, forKey: mapKey)
}
}