@arach/lattices 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +28 -28
  2. package/app/Sources/ActionRow.swift +61 -0
  3. package/app/Sources/App.swift +1 -40
  4. package/app/Sources/AppDelegate.swift +154 -24
  5. package/app/Sources/CheatSheetHUD.swift +1 -0
  6. package/app/Sources/CommandModeState.swift +40 -19
  7. package/app/Sources/CommandModeView.swift +27 -2
  8. package/app/Sources/DaemonServer.swift +8 -0
  9. package/app/Sources/DiagnosticLog.swift +19 -1
  10. package/app/Sources/EventBus.swift +1 -0
  11. package/app/Sources/HotkeyManager.swift +1 -0
  12. package/app/Sources/HotkeyStore.swift +9 -1
  13. package/app/Sources/LatticesApi.swift +210 -0
  14. package/app/Sources/MainView.swift +46 -86
  15. package/app/Sources/MainWindow.swift +13 -0
  16. package/app/Sources/OcrModel.swift +309 -0
  17. package/app/Sources/OcrStore.swift +295 -0
  18. package/app/Sources/OmniSearchState.swift +283 -0
  19. package/app/Sources/OmniSearchView.swift +288 -0
  20. package/app/Sources/OmniSearchWindow.swift +105 -0
  21. package/app/Sources/PaletteCommand.swift +11 -1
  22. package/app/Sources/PermissionChecker.swift +12 -2
  23. package/app/Sources/Preferences.swift +44 -0
  24. package/app/Sources/ScreenMapState.swift +7 -17
  25. package/app/Sources/ScreenMapView.swift +3 -0
  26. package/app/Sources/SettingsView.swift +534 -122
  27. package/app/Sources/Theme.swift +39 -0
  28. package/app/Sources/WindowTiler.swift +59 -56
  29. package/bin/lattices-app.js +23 -7
  30. package/bin/lattices.js +123 -0
  31. package/docs/api.md +390 -249
  32. package/docs/app.md +75 -28
  33. package/docs/concepts.md +45 -136
  34. package/docs/config.md +8 -7
  35. package/docs/layers.md +16 -18
  36. package/docs/ocr.md +185 -0
  37. package/docs/overview.md +39 -34
  38. package/docs/quickstart.md +34 -35
  39. package/package.json +6 -2
@@ -13,14 +13,14 @@ final class PermissionChecker: ObservableObject {
13
13
 
14
14
  var allGranted: Bool { accessibility && screenRecording }
15
15
 
16
- /// Check current permission state without prompting.
16
+ /// Check current permission state, prompting on first launch if not granted.
17
17
  func check() {
18
18
  let diag = DiagnosticLog.shared
19
19
 
20
20
  let ax = AXIsProcessTrusted()
21
21
  let sr = CGPreflightScreenCaptureAccess()
22
22
 
23
- // First check: log detailed identity info to help debug TCC issues
23
+ // First check: log identity info and prompt if needed
24
24
  if !hasLoggedInitial {
25
25
  hasLoggedInitial = true
26
26
  let bundleId = Bundle.main.bundleIdentifier ?? "<no bundle id>"
@@ -30,6 +30,16 @@ final class PermissionChecker: ObservableObject {
30
30
  diag.info("PermissionChecker: exec=\(execPath)")
31
31
  diag.info("AXIsProcessTrusted() → \(ax)")
32
32
  diag.info("CGPreflightScreenCaptureAccess() → \(sr)")
33
+
34
+ // Prompt for missing permissions on first check
35
+ if !ax {
36
+ requestAccessibility()
37
+ return
38
+ }
39
+ if !sr {
40
+ requestScreenRecording()
41
+ return
42
+ }
33
43
  }
34
44
 
35
45
  // Log on state changes
@@ -20,6 +20,32 @@ class Preferences: ObservableObject {
20
20
  didSet { UserDefaults.standard.set(mode.rawValue, forKey: "mode") }
21
21
  }
22
22
 
23
+ // MARK: - Search & OCR
24
+
25
+ @Published var ocrEnabled: Bool {
26
+ didSet { UserDefaults.standard.set(!ocrEnabled, forKey: "ocr.disabled") }
27
+ }
28
+
29
+ @Published var ocrQuickInterval: Double {
30
+ didSet { UserDefaults.standard.set(ocrQuickInterval, forKey: "ocr.interval") }
31
+ }
32
+
33
+ @Published var ocrDeepInterval: Double {
34
+ didSet { UserDefaults.standard.set(ocrDeepInterval, forKey: "ocr.deepInterval") }
35
+ }
36
+
37
+ @Published var ocrQuickLimit: Int {
38
+ didSet { UserDefaults.standard.set(ocrQuickLimit, forKey: "ocr.quickLimit") }
39
+ }
40
+
41
+ @Published var ocrDeepLimit: Int {
42
+ didSet { UserDefaults.standard.set(ocrDeepLimit, forKey: "ocr.deepLimit") }
43
+ }
44
+
45
+ @Published var ocrAccuracy: String {
46
+ didSet { UserDefaults.standard.set(ocrAccuracy, forKey: "ocr.accuracy") }
47
+ }
48
+
23
49
  init() {
24
50
  if let saved = UserDefaults.standard.string(forKey: "terminal"),
25
51
  let t = Terminal(rawValue: saved), t.isInstalled {
@@ -44,5 +70,23 @@ class Preferences: ObservableObject {
44
70
  } else {
45
71
  self.mode = .learning
46
72
  }
73
+
74
+ // Search & OCR
75
+ self.ocrEnabled = !UserDefaults.standard.bool(forKey: "ocr.disabled")
76
+
77
+ let savedInterval = UserDefaults.standard.double(forKey: "ocr.interval")
78
+ self.ocrQuickInterval = savedInterval > 0 ? savedInterval : 60
79
+
80
+ let savedDeep = UserDefaults.standard.double(forKey: "ocr.deepInterval")
81
+ self.ocrDeepInterval = savedDeep > 0 ? savedDeep : 7200
82
+
83
+ let savedQL = UserDefaults.standard.integer(forKey: "ocr.quickLimit")
84
+ self.ocrQuickLimit = savedQL > 0 ? savedQL : 5
85
+
86
+ let savedDL = UserDefaults.standard.integer(forKey: "ocr.deepLimit")
87
+ self.ocrDeepLimit = savedDL > 0 ? savedDL : 15
88
+
89
+ let savedAcc = UserDefaults.standard.string(forKey: "ocr.accuracy") ?? "accurate"
90
+ self.ocrAccuracy = savedAcc
47
91
  }
48
92
  }
@@ -1438,9 +1438,8 @@ final class ScreenMapController: ObservableObject {
1438
1438
  // Tiling mode intercepts keys before anything else
1439
1439
  if editor?.isTilingMode == true {
1440
1440
  switch keyCode {
1441
- case 53: // Escape cancel tiling mode
1442
- exitTilingMode()
1443
- flash("Tiling cancelled")
1441
+ case 53: // Escape always dismiss
1442
+ onDismiss?()
1444
1443
  return true
1445
1444
  case 123: // ← → left
1446
1445
  tileSelectedWindowInEditor(to: .left)
@@ -1516,8 +1515,8 @@ final class ScreenMapController: ObservableObject {
1516
1515
  // Search mode intercepts keys before normal handling
1517
1516
  if isSearchActive {
1518
1517
  switch keyCode {
1519
- case 53: // Escape close search
1520
- closeSearch()
1518
+ case 53: // Escape always dismiss
1519
+ onDismiss?()
1521
1520
  return true
1522
1521
  case 36: // Enter → select or focus
1523
1522
  if modifiers.contains(.command) {
@@ -1539,18 +1538,9 @@ final class ScreenMapController: ObservableObject {
1539
1538
  }
1540
1539
 
1541
1540
  switch keyCode {
1542
- case 53: // Escape
1543
- if editor?.isPreviewing == true {
1544
- endPreview()
1545
- }
1546
- if let ed = editor, ed.pendingEditCount > 0 {
1547
- ed.discardEdits()
1548
- diag.info("[ScreenMap] discarded edits")
1549
- flash("Edits discarded")
1550
- } else {
1551
- diag.info("[ScreenMap] exit")
1552
- onDismiss?()
1553
- }
1541
+ case 53: // Escape — always dismiss
1542
+ diag.info("[ScreenMap] exit")
1543
+ onDismiss?()
1554
1544
  return true
1555
1545
 
1556
1546
  case 36: // Enter
@@ -1972,6 +1972,9 @@ struct ScreenMapView: View {
1972
1972
 
1973
1973
  private func installKeyHandler() {
1974
1974
  eventMonitor = NSEvent.addLocalMonitorForEvents(matching: [.keyDown, .keyUp]) { event in
1975
+ // Only handle keys when our window is the key window
1976
+ guard let win = ScreenMapWindowController.shared.nsWindow,
1977
+ win.isKeyWindow else { return event }
1975
1978
  // Track space key for canvas drag-to-pan
1976
1979
  if event.keyCode == 49 && !controller.isSearchActive {
1977
1980
  if event.type == .keyDown && !event.isARepeat {