@wangyaoshen/remux 0.3.8-dev.29e114b

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 (183) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  4. package/.github/dependabot.yml +33 -0
  5. package/.github/workflows/ci.yml +65 -0
  6. package/.github/workflows/deploy.yml +65 -0
  7. package/.github/workflows/publish.yml +312 -0
  8. package/.github/workflows/release-please.yml +21 -0
  9. package/.gitmodules +3 -0
  10. package/.nvmrc +1 -0
  11. package/.release-please-manifest.json +3 -0
  12. package/CLAUDE.md +104 -0
  13. package/Dockerfile +23 -0
  14. package/LICENSE +21 -0
  15. package/README.md +120 -0
  16. package/apps/ios/Config/signing.xcconfig +4 -0
  17. package/apps/ios/Package.swift +26 -0
  18. package/apps/ios/Remux.xcodeproj/project.pbxproj +477 -0
  19. package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  20. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/Contents.json +23 -0
  21. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png +0 -0
  22. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_120x120.png +0 -0
  23. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_152x152.png +0 -0
  24. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_167x167.png +0 -0
  25. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_180x180.png +0 -0
  26. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_20x20.png +0 -0
  27. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_29x29.png +0 -0
  28. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_40x40.png +0 -0
  29. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_58x58.png +0 -0
  30. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_60x60.png +0 -0
  31. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_76x76.png +0 -0
  32. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_80x80.png +0 -0
  33. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_87x87.png +0 -0
  34. package/apps/ios/Sources/Remux/Assets.xcassets/Contents.json +6 -0
  35. package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
  36. package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
  37. package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
  38. package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
  39. package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
  40. package/apps/ios/Sources/Remux/RootView.swift +130 -0
  41. package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
  42. package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
  43. package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
  44. package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
  45. package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
  46. package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
  47. package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
  48. package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
  49. package/apps/macos/Package.swift +37 -0
  50. package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
  51. package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
  52. package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
  53. package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
  54. package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
  55. package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
  56. package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
  57. package/apps/macos/Resources/terminfo/67/ghostty +0 -0
  58. package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
  59. package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
  60. package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
  61. package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
  62. package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
  63. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
  64. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
  65. package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
  66. package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
  67. package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
  68. package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
  69. package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
  70. package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
  71. package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
  72. package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
  73. package/apps/macos/Sources/Remux/SocketController.swift +258 -0
  74. package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
  75. package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
  76. package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
  77. package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
  78. package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
  79. package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
  80. package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
  81. package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
  82. package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
  83. package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
  84. package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
  85. package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
  86. package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
  87. package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
  88. package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
  89. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
  90. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
  91. package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
  92. package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
  93. package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
  94. package/build.mjs +33 -0
  95. package/native/android/DecodeGoldenPayloads.kt +487 -0
  96. package/native/android/ProtocolModels.kt +188 -0
  97. package/native/ios/DecodeGoldenPayloads.swift +711 -0
  98. package/native/ios/ProtocolModels.swift +200 -0
  99. package/package.json +45 -0
  100. package/packages/RemuxKit/Package.swift +27 -0
  101. package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
  102. package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
  103. package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
  104. package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
  105. package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
  106. package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
  107. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
  108. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
  109. package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
  110. package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
  111. package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
  112. package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
  113. package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
  114. package/playwright.config.ts +17 -0
  115. package/pnpm-lock.yaml +1588 -0
  116. package/pty-daemon.js +303 -0
  117. package/release-please-config.json +14 -0
  118. package/scripts/auto-deploy.sh +46 -0
  119. package/scripts/build-dmg.sh +121 -0
  120. package/scripts/build-ghostty-kit.sh +43 -0
  121. package/scripts/check-active-terminology.mjs +132 -0
  122. package/scripts/setup-ci-secrets.sh +80 -0
  123. package/scripts/sync-ghostty-web.sh +28 -0
  124. package/scripts/upload-testflight.sh +100 -0
  125. package/server.js +7074 -0
  126. package/src/adapters/agent-events.ts +246 -0
  127. package/src/adapters/claude-code.ts +158 -0
  128. package/src/adapters/codex.ts +210 -0
  129. package/src/adapters/generic-shell.ts +58 -0
  130. package/src/adapters/index.ts +15 -0
  131. package/src/adapters/registry.ts +99 -0
  132. package/src/adapters/types.ts +41 -0
  133. package/src/auth.ts +174 -0
  134. package/src/e2ee.ts +236 -0
  135. package/src/git-service.ts +168 -0
  136. package/src/message-buffer.ts +137 -0
  137. package/src/pty-daemon.ts +357 -0
  138. package/src/push.ts +127 -0
  139. package/src/renderers.ts +455 -0
  140. package/src/server.ts +2407 -0
  141. package/src/service.ts +226 -0
  142. package/src/session.ts +978 -0
  143. package/src/store.ts +1422 -0
  144. package/src/team.ts +123 -0
  145. package/src/tunnel.ts +126 -0
  146. package/src/types.d.ts +50 -0
  147. package/src/vt-tracker.ts +188 -0
  148. package/src/workspace-head.ts +144 -0
  149. package/src/workspace.ts +153 -0
  150. package/src/ws-handler.ts +1526 -0
  151. package/start.ps1 +83 -0
  152. package/tests/adapters.test.js +171 -0
  153. package/tests/auth.test.js +243 -0
  154. package/tests/codex-adapter.test.js +535 -0
  155. package/tests/durable-stream.test.js +153 -0
  156. package/tests/e2e/app.spec.js +530 -0
  157. package/tests/e2ee.test.js +325 -0
  158. package/tests/message-buffer.test.js +245 -0
  159. package/tests/message-routing.test.js +305 -0
  160. package/tests/pty-daemon.test.js +346 -0
  161. package/tests/push.test.js +281 -0
  162. package/tests/renderers.test.js +391 -0
  163. package/tests/search-shell.test.js +499 -0
  164. package/tests/server.test.js +882 -0
  165. package/tests/service.test.js +267 -0
  166. package/tests/store.test.js +369 -0
  167. package/tests/tunnel.test.js +67 -0
  168. package/tests/workspace-head.test.js +116 -0
  169. package/tests/workspace.test.js +417 -0
  170. package/tsconfig.backend.json +11 -0
  171. package/tsconfig.json +15 -0
  172. package/tui/client/client_test.go +125 -0
  173. package/tui/client/connection.go +342 -0
  174. package/tui/client/host_manager.go +141 -0
  175. package/tui/config/cache.go +81 -0
  176. package/tui/config/config.go +53 -0
  177. package/tui/config/config_test.go +89 -0
  178. package/tui/go.mod +32 -0
  179. package/tui/go.sum +50 -0
  180. package/tui/main.go +261 -0
  181. package/tui/tests/integration_test.go +283 -0
  182. package/tui/ui/model.go +310 -0
  183. package/vitest.config.js +10 -0
@@ -0,0 +1,477 @@
1
+ // !$*UTF8*$!
2
+ {
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 77;
7
+ objects = {
8
+
9
+ /* Begin PBXBuildFile section */
10
+ 04EE3BCAD2C297F8696C82C2 /* QRScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2A531BFD8446B4BCB5A0B2 /* QRScannerView.swift */; };
11
+ 346FBA7A7057F163D7CC8F03 /* RemuxiOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662FEE0E78ADAADEA731106C /* RemuxiOSApp.swift */; };
12
+ 622B8BA7525C80F0416F5E1B /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3120562997C5EB5D054DAB21 /* OnboardingView.swift */; };
13
+ 622F7BF35D6BB2E6C7715426 /* MeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7269AB643DAA5051FBDACE /* MeView.swift */; };
14
+ 78E3310D198D772C606FBB0B /* ControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D548B0C91D3AABA26641D898 /* ControlView.swift */; };
15
+ 810D705F63FBA1B407B0FBC1 /* FaceIDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5056D67965667A307294CFB1 /* FaceIDManager.swift */; };
16
+ BBCF34D61B0F6B8CFB234965 /* InspectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEEEA01EB80CE2C1601F6228 /* InspectCache.swift */; };
17
+ CA2151750F645DA9BC49C7C8 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48C3DD287E421F1077A8923A /* MainTabView.swift */; };
18
+ CB45E538B1DF947F0C8D9542 /* NowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE379B0C119DDC9C8B324E5 /* NowView.swift */; };
19
+ D32ABBB3E21D0344B3B9455C /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FAA68E03D60CC5F0A92A1DB /* RootView.swift */; };
20
+ DD0F6EA123F2BCF28AAA676A /* LiveTerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 734A97B1146E816099903C0D /* LiveTerminalView.swift */; };
21
+ ECAFCC766BD77CCD21821612 /* ManualConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E3AE7922D1E43AE099D1D40 /* ManualConnectView.swift */; };
22
+ EE0A712F52F48C1AB61D7AEC /* RemuxKit in Frameworks */ = {isa = PBXBuildFile; productRef = EF63343AE24B477D7365EE17 /* RemuxKit */; };
23
+ F136D905F6E9BA7F2C1816F2 /* InspectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED7C2F66BB76FB8DE51725E /* InspectView.swift */; };
24
+ AA1234560000000000000001 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AA1234560000000000000002 /* Assets.xcassets */; };
25
+ /* End PBXBuildFile section */
26
+
27
+ /* Begin PBXFileReference section */
28
+ 0B7269AB643DAA5051FBDACE /* MeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeView.swift; sourceTree = "<group>"; };
29
+ 1ED7C2F66BB76FB8DE51725E /* InspectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectView.swift; sourceTree = "<group>"; };
30
+ 3120562997C5EB5D054DAB21 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
31
+ 48C3DD287E421F1077A8923A /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
32
+ 5056D67965667A307294CFB1 /* FaceIDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceIDManager.swift; sourceTree = "<group>"; };
33
+ 662FEE0E78ADAADEA731106C /* RemuxiOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemuxiOSApp.swift; sourceTree = "<group>"; };
34
+ 6C491FD55D84AC2BED5C5EBD /* Remux.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Remux.entitlements; sourceTree = "<group>"; };
35
+ 6E3AE7922D1E43AE099D1D40 /* ManualConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualConnectView.swift; sourceTree = "<group>"; };
36
+ 734A97B1146E816099903C0D /* LiveTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTerminalView.swift; sourceTree = "<group>"; };
37
+ 8FAA68E03D60CC5F0A92A1DB /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
38
+ A47FAD14E232D67C4163B551 /* RemuxKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = RemuxKit; path = ../../packages/RemuxKit; sourceTree = SOURCE_ROOT; };
39
+ BF2A531BFD8446B4BCB5A0B2 /* QRScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerView.swift; sourceTree = "<group>"; };
40
+ C6233E6291A38BAAE86C727D /* Remux.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Remux.app; sourceTree = BUILT_PRODUCTS_DIR; };
41
+ D548B0C91D3AABA26641D898 /* ControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlView.swift; sourceTree = "<group>"; };
42
+ DBE379B0C119DDC9C8B324E5 /* NowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowView.swift; sourceTree = "<group>"; };
43
+ DEEEA01EB80CE2C1601F6228 /* InspectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectCache.swift; sourceTree = "<group>"; };
44
+ AA1234560000000000000002 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
45
+ /* End PBXFileReference section */
46
+
47
+ /* Begin PBXFrameworksBuildPhase section */
48
+ 03BDC461530EDD09CDB7FF29 /* Frameworks */ = {
49
+ isa = PBXFrameworksBuildPhase;
50
+ buildActionMask = 2147483647;
51
+ files = (
52
+ EE0A712F52F48C1AB61D7AEC /* RemuxKit in Frameworks */,
53
+ );
54
+ runOnlyForDeploymentPostprocessing = 0;
55
+ };
56
+ /* End PBXFrameworksBuildPhase section */
57
+
58
+ /* Begin PBXGroup section */
59
+ 02177B1EEEF3A70CA8B1DC00 /* Onboarding */ = {
60
+ isa = PBXGroup;
61
+ children = (
62
+ 6E3AE7922D1E43AE099D1D40 /* ManualConnectView.swift */,
63
+ 3120562997C5EB5D054DAB21 /* OnboardingView.swift */,
64
+ BF2A531BFD8446B4BCB5A0B2 /* QRScannerView.swift */,
65
+ );
66
+ path = Onboarding;
67
+ sourceTree = "<group>";
68
+ };
69
+ 0E70947CCFEAEFEDCCF14E35 /* Remux */ = {
70
+ isa = PBXGroup;
71
+ children = (
72
+ AA1234560000000000000002 /* Assets.xcassets */,
73
+ 48C3DD287E421F1077A8923A /* MainTabView.swift */,
74
+ 6C491FD55D84AC2BED5C5EBD /* Remux.entitlements */,
75
+ 662FEE0E78ADAADEA731106C /* RemuxiOSApp.swift */,
76
+ 8FAA68E03D60CC5F0A92A1DB /* RootView.swift */,
77
+ E0BAA4E34FA2A6985DF4C973 /* Extensions */,
78
+ 34F4D5A768A03291C3584961 /* Views */,
79
+ );
80
+ name = Remux;
81
+ path = Sources/Remux;
82
+ sourceTree = "<group>";
83
+ };
84
+ 34F4D5A768A03291C3584961 /* Views */ = {
85
+ isa = PBXGroup;
86
+ children = (
87
+ 89F7EBDCDA60DF5612AE9A65 /* Control */,
88
+ E3592A5F25FDAC99767AF4F0 /* Inspect */,
89
+ E6ABC817B70CA54DD2647104 /* Live */,
90
+ DBC76E38E5D91242FED6C091 /* Now */,
91
+ 02177B1EEEF3A70CA8B1DC00 /* Onboarding */,
92
+ E77261034A2F42E559D8BC9E /* Settings */,
93
+ );
94
+ path = Views;
95
+ sourceTree = "<group>";
96
+ };
97
+ 89F7EBDCDA60DF5612AE9A65 /* Control */ = {
98
+ isa = PBXGroup;
99
+ children = (
100
+ D548B0C91D3AABA26641D898 /* ControlView.swift */,
101
+ );
102
+ path = Control;
103
+ sourceTree = "<group>";
104
+ };
105
+ 9383E7B61F907BDA3838C6DA /* Packages */ = {
106
+ isa = PBXGroup;
107
+ children = (
108
+ A47FAD14E232D67C4163B551 /* RemuxKit */,
109
+ );
110
+ name = Packages;
111
+ sourceTree = "<group>";
112
+ };
113
+ D5B969C9596603CB720FBE0D = {
114
+ isa = PBXGroup;
115
+ children = (
116
+ 9383E7B61F907BDA3838C6DA /* Packages */,
117
+ 0E70947CCFEAEFEDCCF14E35 /* Remux */,
118
+ F94500072A3763A6590AF480 /* Products */,
119
+ );
120
+ sourceTree = "<group>";
121
+ };
122
+ DBC76E38E5D91242FED6C091 /* Now */ = {
123
+ isa = PBXGroup;
124
+ children = (
125
+ DBE379B0C119DDC9C8B324E5 /* NowView.swift */,
126
+ );
127
+ path = Now;
128
+ sourceTree = "<group>";
129
+ };
130
+ E0BAA4E34FA2A6985DF4C973 /* Extensions */ = {
131
+ isa = PBXGroup;
132
+ children = (
133
+ 5056D67965667A307294CFB1 /* FaceIDManager.swift */,
134
+ DEEEA01EB80CE2C1601F6228 /* InspectCache.swift */,
135
+ );
136
+ path = Extensions;
137
+ sourceTree = "<group>";
138
+ };
139
+ E3592A5F25FDAC99767AF4F0 /* Inspect */ = {
140
+ isa = PBXGroup;
141
+ children = (
142
+ 1ED7C2F66BB76FB8DE51725E /* InspectView.swift */,
143
+ );
144
+ path = Inspect;
145
+ sourceTree = "<group>";
146
+ };
147
+ E6ABC817B70CA54DD2647104 /* Live */ = {
148
+ isa = PBXGroup;
149
+ children = (
150
+ 734A97B1146E816099903C0D /* LiveTerminalView.swift */,
151
+ );
152
+ path = Live;
153
+ sourceTree = "<group>";
154
+ };
155
+ E77261034A2F42E559D8BC9E /* Settings */ = {
156
+ isa = PBXGroup;
157
+ children = (
158
+ 0B7269AB643DAA5051FBDACE /* MeView.swift */,
159
+ );
160
+ path = Settings;
161
+ sourceTree = "<group>";
162
+ };
163
+ F94500072A3763A6590AF480 /* Products */ = {
164
+ isa = PBXGroup;
165
+ children = (
166
+ C6233E6291A38BAAE86C727D /* Remux.app */,
167
+ );
168
+ name = Products;
169
+ sourceTree = "<group>";
170
+ };
171
+ /* End PBXGroup section */
172
+
173
+ /* Begin PBXNativeTarget section */
174
+ 7125659F6728FEF22F3FDFB5 /* Remux */ = {
175
+ isa = PBXNativeTarget;
176
+ buildConfigurationList = 9A9EB61A159DAC867ECA3B9D /* Build configuration list for PBXNativeTarget "Remux" */;
177
+ buildPhases = (
178
+ 204B591A381758166DA82963 /* Sources */,
179
+ 03BDC461530EDD09CDB7FF29 /* Frameworks */,
180
+ AA1234560000000000000003 /* Resources */,
181
+ );
182
+ buildRules = (
183
+ );
184
+ dependencies = (
185
+ );
186
+ name = Remux;
187
+ packageProductDependencies = (
188
+ EF63343AE24B477D7365EE17 /* RemuxKit */,
189
+ );
190
+ productName = Remux;
191
+ productReference = C6233E6291A38BAAE86C727D /* Remux.app */;
192
+ productType = "com.apple.product-type.application";
193
+ };
194
+ /* End PBXNativeTarget section */
195
+
196
+ /* Begin PBXProject section */
197
+ AB9B0F665DE3D1CF4BE38CF5 /* Project object */ = {
198
+ isa = PBXProject;
199
+ attributes = {
200
+ BuildIndependentTargetsInParallel = YES;
201
+ LastUpgradeCheck = 2630;
202
+ TargetAttributes = {
203
+ 7125659F6728FEF22F3FDFB5 = {
204
+ DevelopmentTeam = LY8QD6TJN6;
205
+ ProvisioningStyle = Automatic;
206
+ };
207
+ };
208
+ };
209
+ buildConfigurationList = F118F8E44563891091219E31 /* Build configuration list for PBXProject "Remux" */;
210
+ compatibilityVersion = "Xcode 14.0";
211
+ developmentRegion = en;
212
+ hasScannedForEncodings = 0;
213
+ knownRegions = (
214
+ Base,
215
+ en,
216
+ );
217
+ mainGroup = D5B969C9596603CB720FBE0D;
218
+ minimizedProjectReferenceProxies = 1;
219
+ packageReferences = (
220
+ 4F0952D532975C4F55CAE590 /* XCLocalSwiftPackageReference "../../packages/RemuxKit" */,
221
+ );
222
+ preferredProjectObjectVersion = 77;
223
+ projectDirPath = "";
224
+ projectRoot = "";
225
+ targets = (
226
+ 7125659F6728FEF22F3FDFB5 /* Remux */,
227
+ );
228
+ };
229
+ /* End PBXProject section */
230
+
231
+ /* Begin PBXResourcesBuildPhase section */
232
+ AA1234560000000000000003 /* Resources */ = {
233
+ isa = PBXResourcesBuildPhase;
234
+ buildActionMask = 2147483647;
235
+ files = (
236
+ AA1234560000000000000001 /* Assets.xcassets in Resources */,
237
+ );
238
+ runOnlyForDeploymentPostprocessing = 0;
239
+ };
240
+ /* End PBXResourcesBuildPhase section */
241
+
242
+ /* Begin PBXSourcesBuildPhase section */
243
+ 204B591A381758166DA82963 /* Sources */ = {
244
+ isa = PBXSourcesBuildPhase;
245
+ buildActionMask = 2147483647;
246
+ files = (
247
+ 78E3310D198D772C606FBB0B /* ControlView.swift in Sources */,
248
+ 810D705F63FBA1B407B0FBC1 /* FaceIDManager.swift in Sources */,
249
+ BBCF34D61B0F6B8CFB234965 /* InspectCache.swift in Sources */,
250
+ F136D905F6E9BA7F2C1816F2 /* InspectView.swift in Sources */,
251
+ DD0F6EA123F2BCF28AAA676A /* LiveTerminalView.swift in Sources */,
252
+ CA2151750F645DA9BC49C7C8 /* MainTabView.swift in Sources */,
253
+ ECAFCC766BD77CCD21821612 /* ManualConnectView.swift in Sources */,
254
+ 622F7BF35D6BB2E6C7715426 /* MeView.swift in Sources */,
255
+ CB45E538B1DF947F0C8D9542 /* NowView.swift in Sources */,
256
+ 622B8BA7525C80F0416F5E1B /* OnboardingView.swift in Sources */,
257
+ 04EE3BCAD2C297F8696C82C2 /* QRScannerView.swift in Sources */,
258
+ 346FBA7A7057F163D7CC8F03 /* RemuxiOSApp.swift in Sources */,
259
+ D32ABBB3E21D0344B3B9455C /* RootView.swift in Sources */,
260
+ );
261
+ runOnlyForDeploymentPostprocessing = 0;
262
+ };
263
+ /* End PBXSourcesBuildPhase section */
264
+
265
+ /* Begin XCBuildConfiguration section */
266
+ 4CDD0720CAFFE2E1481FBFA7 /* Debug */ = {
267
+ isa = XCBuildConfiguration;
268
+ buildSettings = {
269
+ ALWAYS_SEARCH_USER_PATHS = NO;
270
+ CLANG_ANALYZER_NONNULL = YES;
271
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
272
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
273
+ CLANG_CXX_LIBRARY = "libc++";
274
+ CLANG_ENABLE_MODULES = YES;
275
+ CLANG_ENABLE_OBJC_ARC = YES;
276
+ CLANG_ENABLE_OBJC_WEAK = YES;
277
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
278
+ CLANG_WARN_BOOL_CONVERSION = YES;
279
+ CLANG_WARN_COMMA = YES;
280
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
281
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
282
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
283
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
284
+ CLANG_WARN_EMPTY_BODY = YES;
285
+ CLANG_WARN_ENUM_CONVERSION = YES;
286
+ CLANG_WARN_INFINITE_RECURSION = YES;
287
+ CLANG_WARN_INT_CONVERSION = YES;
288
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
289
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
290
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
291
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
292
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
293
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
294
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
295
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
296
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
297
+ CLANG_WARN_UNREACHABLE_CODE = YES;
298
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
299
+ CODE_SIGN_STYLE = Automatic;
300
+ COPY_PHASE_STRIP = NO;
301
+ DEBUG_INFORMATION_FORMAT = dwarf;
302
+ DEVELOPMENT_TEAM = LY8QD6TJN6;
303
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
304
+ ENABLE_TESTABILITY = YES;
305
+ GCC_C_LANGUAGE_STANDARD = gnu11;
306
+ GCC_DYNAMIC_NO_PIC = NO;
307
+ GCC_NO_COMMON_BLOCKS = YES;
308
+ GCC_OPTIMIZATION_LEVEL = 0;
309
+ GCC_PREPROCESSOR_DEFINITIONS = (
310
+ "$(inherited)",
311
+ "DEBUG=1",
312
+ );
313
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
314
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
315
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
316
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
317
+ GCC_WARN_UNUSED_FUNCTION = YES;
318
+ GCC_WARN_UNUSED_VARIABLE = YES;
319
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
320
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
321
+ MTL_FAST_MATH = YES;
322
+ ONLY_ACTIVE_ARCH = YES;
323
+ PRODUCT_NAME = "$(TARGET_NAME)";
324
+ SDKROOT = iphoneos;
325
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
326
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
327
+ SWIFT_VERSION = 6.0;
328
+ };
329
+ name = Debug;
330
+ };
331
+ 89E46C713EAC3CEC87DDEBC6 /* Release */ = {
332
+ isa = XCBuildConfiguration;
333
+ buildSettings = {
334
+ ALWAYS_SEARCH_USER_PATHS = NO;
335
+ CLANG_ANALYZER_NONNULL = YES;
336
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
337
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
338
+ CLANG_CXX_LIBRARY = "libc++";
339
+ CLANG_ENABLE_MODULES = YES;
340
+ CLANG_ENABLE_OBJC_ARC = YES;
341
+ CLANG_ENABLE_OBJC_WEAK = YES;
342
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
343
+ CLANG_WARN_BOOL_CONVERSION = YES;
344
+ CLANG_WARN_COMMA = YES;
345
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
346
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
347
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
348
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
349
+ CLANG_WARN_EMPTY_BODY = YES;
350
+ CLANG_WARN_ENUM_CONVERSION = YES;
351
+ CLANG_WARN_INFINITE_RECURSION = YES;
352
+ CLANG_WARN_INT_CONVERSION = YES;
353
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
354
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
355
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
356
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
357
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
358
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
359
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
360
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
361
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
362
+ CLANG_WARN_UNREACHABLE_CODE = YES;
363
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
364
+ CODE_SIGN_STYLE = Automatic;
365
+ COPY_PHASE_STRIP = NO;
366
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
367
+ DEVELOPMENT_TEAM = LY8QD6TJN6;
368
+ ENABLE_NS_ASSERTIONS = NO;
369
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
370
+ GCC_C_LANGUAGE_STANDARD = gnu11;
371
+ GCC_NO_COMMON_BLOCKS = YES;
372
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
373
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
374
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
375
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
376
+ GCC_WARN_UNUSED_FUNCTION = YES;
377
+ GCC_WARN_UNUSED_VARIABLE = YES;
378
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
379
+ MTL_ENABLE_DEBUG_INFO = NO;
380
+ MTL_FAST_MATH = YES;
381
+ PRODUCT_NAME = "$(TARGET_NAME)";
382
+ SDKROOT = iphoneos;
383
+ SWIFT_COMPILATION_MODE = wholemodule;
384
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
385
+ SWIFT_VERSION = 6.0;
386
+ };
387
+ name = Release;
388
+ };
389
+ A8EBDFB043CD42C757E3F876 /* Debug */ = {
390
+ isa = XCBuildConfiguration;
391
+ buildSettings = {
392
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
393
+ CODE_SIGN_ENTITLEMENTS = Sources/Remux/Remux.entitlements;
394
+ CODE_SIGN_IDENTITY = "iPhone Developer";
395
+ GENERATE_INFOPLIST_FILE = YES;
396
+ INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
397
+ INFOPLIST_KEY_NSCameraUsageDescription = "Remux uses the camera to scan QR codes for server pairing.";
398
+ INFOPLIST_KEY_NSFaceIDUsageDescription = "Remux uses Face ID to protect your terminal sessions.";
399
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
400
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
401
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
402
+ LD_RUNPATH_SEARCH_PATHS = (
403
+ "$(inherited)",
404
+ "@executable_path/Frameworks",
405
+ );
406
+ CURRENT_PROJECT_VERSION = 1;
407
+ MARKETING_VERSION = 0.3.8;
408
+ PRODUCT_BUNDLE_IDENTIFIER = com.remux.ios;
409
+ SDKROOT = iphoneos;
410
+ TARGETED_DEVICE_FAMILY = "1,2";
411
+ };
412
+ name = Debug;
413
+ };
414
+ C0E5593BA2CFEE44A2B659DC /* Release */ = {
415
+ isa = XCBuildConfiguration;
416
+ buildSettings = {
417
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
418
+ CODE_SIGN_ENTITLEMENTS = Sources/Remux/Remux.entitlements;
419
+ CODE_SIGN_IDENTITY = "iPhone Developer";
420
+ GENERATE_INFOPLIST_FILE = YES;
421
+ INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
422
+ INFOPLIST_KEY_NSCameraUsageDescription = "Remux uses the camera to scan QR codes for server pairing.";
423
+ INFOPLIST_KEY_NSFaceIDUsageDescription = "Remux uses Face ID to protect your terminal sessions.";
424
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
425
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
426
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
427
+ LD_RUNPATH_SEARCH_PATHS = (
428
+ "$(inherited)",
429
+ "@executable_path/Frameworks",
430
+ );
431
+ CURRENT_PROJECT_VERSION = 1;
432
+ MARKETING_VERSION = 0.3.8;
433
+ PRODUCT_BUNDLE_IDENTIFIER = com.remux.ios;
434
+ SDKROOT = iphoneos;
435
+ TARGETED_DEVICE_FAMILY = "1,2";
436
+ };
437
+ name = Release;
438
+ };
439
+ /* End XCBuildConfiguration section */
440
+
441
+ /* Begin XCConfigurationList section */
442
+ 9A9EB61A159DAC867ECA3B9D /* Build configuration list for PBXNativeTarget "Remux" */ = {
443
+ isa = XCConfigurationList;
444
+ buildConfigurations = (
445
+ A8EBDFB043CD42C757E3F876 /* Debug */,
446
+ C0E5593BA2CFEE44A2B659DC /* Release */,
447
+ );
448
+ defaultConfigurationIsVisible = 0;
449
+ defaultConfigurationName = Debug;
450
+ };
451
+ F118F8E44563891091219E31 /* Build configuration list for PBXProject "Remux" */ = {
452
+ isa = XCConfigurationList;
453
+ buildConfigurations = (
454
+ 4CDD0720CAFFE2E1481FBFA7 /* Debug */,
455
+ 89E46C713EAC3CEC87DDEBC6 /* Release */,
456
+ );
457
+ defaultConfigurationIsVisible = 0;
458
+ defaultConfigurationName = Debug;
459
+ };
460
+ /* End XCConfigurationList section */
461
+
462
+ /* Begin XCLocalSwiftPackageReference section */
463
+ 4F0952D532975C4F55CAE590 /* XCLocalSwiftPackageReference "../../packages/RemuxKit" */ = {
464
+ isa = XCLocalSwiftPackageReference;
465
+ relativePath = ../../packages/RemuxKit;
466
+ };
467
+ /* End XCLocalSwiftPackageReference section */
468
+
469
+ /* Begin XCSwiftPackageProductDependency section */
470
+ EF63343AE24B477D7365EE17 /* RemuxKit */ = {
471
+ isa = XCSwiftPackageProductDependency;
472
+ productName = RemuxKit;
473
+ };
474
+ /* End XCSwiftPackageProductDependency section */
475
+ };
476
+ rootObject = AB9B0F665DE3D1CF4BE38CF5 /* Project object */;
477
+ }
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,23 @@
1
+ {
2
+ "images" : [
3
+ { "filename" : "icon_40x40.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" },
4
+ { "filename" : "icon_60x60.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" },
5
+ { "filename" : "icon_58x58.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" },
6
+ { "filename" : "icon_87x87.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" },
7
+ { "filename" : "icon_80x80.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" },
8
+ { "filename" : "icon_120x120.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" },
9
+ { "filename" : "icon_120x120.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" },
10
+ { "filename" : "icon_180x180.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" },
11
+ { "filename" : "icon_20x20.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" },
12
+ { "filename" : "icon_40x40.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" },
13
+ { "filename" : "icon_29x29.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" },
14
+ { "filename" : "icon_58x58.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" },
15
+ { "filename" : "icon_40x40.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" },
16
+ { "filename" : "icon_80x80.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" },
17
+ { "filename" : "icon_76x76.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" },
18
+ { "filename" : "icon_152x152.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" },
19
+ { "filename" : "icon_167x167.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" },
20
+ { "filename" : "icon_1024x1024.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" }
21
+ ],
22
+ "info" : { "author" : "xcode", "version" : 1 }
23
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+ }
@@ -0,0 +1,29 @@
1
+ import LocalAuthentication
2
+ import SwiftUI
3
+
4
+ /// Face ID / Touch ID authentication manager.
5
+ @MainActor
6
+ final class FaceIDManager {
7
+ @AppStorage("faceIdEnabled") private var faceIdEnabled = false
8
+
9
+ func authenticateIfNeeded() async -> Bool {
10
+ guard faceIdEnabled else { return true }
11
+
12
+ let context = LAContext()
13
+ var error: NSError?
14
+
15
+ guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
16
+ return true // No biometrics available, skip
17
+ }
18
+
19
+ do {
20
+ let success = try await context.evaluatePolicy(
21
+ .deviceOwnerAuthenticationWithBiometrics,
22
+ localizedReason: "Unlock Remux to access your terminal sessions"
23
+ )
24
+ return success
25
+ } catch {
26
+ return false
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,66 @@
1
+ import Foundation
2
+ import RemuxKit
3
+
4
+ /// Offline Inspect cache using file-based storage.
5
+ /// Caches up to 50MB of recent Inspect snapshots, LRU eviction.
6
+ actor InspectCache {
7
+ static let shared = InspectCache()
8
+
9
+ private let cacheDir: URL
10
+ private let maxSize: Int = 50 * 1024 * 1024 // 50MB
11
+
12
+ private init() {
13
+ let base = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
14
+ cacheDir = base.appendingPathComponent("remux-inspect-cache", isDirectory: true)
15
+ try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true)
16
+ }
17
+
18
+ func save(snapshot: InspectSnapshot, server: String, tabIndex: Int) {
19
+ let key = cacheKey(server: server, tabIndex: tabIndex)
20
+ let entry = CacheEntry(snapshot: snapshot, cachedAt: Date())
21
+ guard let data = try? JSONEncoder().encode(entry) else { return }
22
+ let file = cacheDir.appendingPathComponent(key)
23
+ try? data.write(to: file)
24
+ evictIfNeeded()
25
+ }
26
+
27
+ func load(server: String, tabIndex: Int) -> InspectSnapshot? {
28
+ let key = cacheKey(server: server, tabIndex: tabIndex)
29
+ let file = cacheDir.appendingPathComponent(key)
30
+ guard let data = try? Data(contentsOf: file),
31
+ let entry = try? JSONDecoder().decode(CacheEntry.self, from: data) else { return nil }
32
+ return entry.snapshot
33
+ }
34
+
35
+ private func cacheKey(server: String, tabIndex: Int) -> String {
36
+ let sanitized = server.replacingOccurrences(of: "/", with: "_")
37
+ .replacingOccurrences(of: ":", with: "_")
38
+ return "\(sanitized)_tab\(tabIndex).json"
39
+ }
40
+
41
+ private func evictIfNeeded() {
42
+ guard let files = try? FileManager.default.contentsOfDirectory(
43
+ at: cacheDir, includingPropertiesForKeys: [.fileSizeKey, .contentModificationDateKey]
44
+ ) else { return }
45
+
46
+ var totalSize = 0
47
+ var sorted = files.compactMap { url -> (URL, Int, Date)? in
48
+ guard let vals = try? url.resourceValues(forKeys: [.fileSizeKey, .contentModificationDateKey]),
49
+ let size = vals.fileSize,
50
+ let date = vals.contentModificationDate else { return nil }
51
+ totalSize += size
52
+ return (url, size, date)
53
+ }.sorted { $0.2 < $1.2 } // oldest first
54
+
55
+ while totalSize > maxSize, !sorted.isEmpty {
56
+ let oldest = sorted.removeFirst()
57
+ try? FileManager.default.removeItem(at: oldest.0)
58
+ totalSize -= oldest.1
59
+ }
60
+ }
61
+ }
62
+
63
+ private struct CacheEntry: Codable {
64
+ let snapshot: InspectSnapshot
65
+ let cachedAt: Date
66
+ }