@wangyaoshen/remux 0.3.8-dev.bab6c95

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 (166) 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 +138 -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 +456 -0
  19. package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  20. package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
  21. package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
  22. package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
  23. package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
  24. package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
  25. package/apps/ios/Sources/Remux/RootView.swift +130 -0
  26. package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
  27. package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
  28. package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
  29. package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
  30. package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
  31. package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
  32. package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
  33. package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
  34. package/apps/macos/Package.swift +37 -0
  35. package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
  36. package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
  37. package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
  38. package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
  39. package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
  40. package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
  41. package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
  42. package/apps/macos/Resources/terminfo/67/ghostty +0 -0
  43. package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
  44. package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
  45. package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
  46. package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
  47. package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
  48. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
  49. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
  50. package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
  51. package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
  52. package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
  53. package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
  54. package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
  55. package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
  56. package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
  57. package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
  58. package/apps/macos/Sources/Remux/SocketController.swift +258 -0
  59. package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
  60. package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
  61. package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
  62. package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
  63. package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
  64. package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
  65. package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
  66. package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
  67. package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
  68. package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
  69. package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
  70. package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
  71. package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
  72. package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
  73. package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
  74. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
  75. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
  76. package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
  77. package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
  78. package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
  79. package/build.mjs +33 -0
  80. package/native/android/DecodeGoldenPayloads.kt +487 -0
  81. package/native/android/ProtocolModels.kt +188 -0
  82. package/native/ios/DecodeGoldenPayloads.swift +711 -0
  83. package/native/ios/ProtocolModels.swift +200 -0
  84. package/package.json +45 -0
  85. package/packages/RemuxKit/Package.swift +27 -0
  86. package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
  87. package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
  88. package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
  89. package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
  90. package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
  91. package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
  92. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
  93. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
  94. package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
  95. package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
  96. package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
  97. package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
  98. package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
  99. package/playwright.config.ts +17 -0
  100. package/pnpm-lock.yaml +1588 -0
  101. package/pty-daemon.js +303 -0
  102. package/release-please-config.json +14 -0
  103. package/scripts/auto-deploy.sh +46 -0
  104. package/scripts/build-dmg.sh +121 -0
  105. package/scripts/build-ghostty-kit.sh +43 -0
  106. package/scripts/check-active-terminology.mjs +132 -0
  107. package/scripts/sync-ghostty-web.sh +28 -0
  108. package/server.js +7074 -0
  109. package/src/adapters/agent-events.ts +246 -0
  110. package/src/adapters/claude-code.ts +158 -0
  111. package/src/adapters/codex.ts +210 -0
  112. package/src/adapters/generic-shell.ts +58 -0
  113. package/src/adapters/index.ts +15 -0
  114. package/src/adapters/registry.ts +99 -0
  115. package/src/adapters/types.ts +41 -0
  116. package/src/auth.ts +174 -0
  117. package/src/e2ee.ts +236 -0
  118. package/src/git-service.ts +168 -0
  119. package/src/message-buffer.ts +137 -0
  120. package/src/pty-daemon.ts +357 -0
  121. package/src/push.ts +127 -0
  122. package/src/renderers.ts +455 -0
  123. package/src/server.ts +2407 -0
  124. package/src/service.ts +226 -0
  125. package/src/session.ts +978 -0
  126. package/src/store.ts +1422 -0
  127. package/src/team.ts +123 -0
  128. package/src/tunnel.ts +126 -0
  129. package/src/types.d.ts +50 -0
  130. package/src/vt-tracker.ts +188 -0
  131. package/src/workspace-head.ts +144 -0
  132. package/src/workspace.ts +153 -0
  133. package/src/ws-handler.ts +1526 -0
  134. package/start.ps1 +83 -0
  135. package/tests/adapters.test.js +171 -0
  136. package/tests/auth.test.js +243 -0
  137. package/tests/codex-adapter.test.js +535 -0
  138. package/tests/durable-stream.test.js +153 -0
  139. package/tests/e2e/app.spec.js +530 -0
  140. package/tests/e2ee.test.js +325 -0
  141. package/tests/message-buffer.test.js +245 -0
  142. package/tests/message-routing.test.js +305 -0
  143. package/tests/pty-daemon.test.js +346 -0
  144. package/tests/push.test.js +281 -0
  145. package/tests/renderers.test.js +391 -0
  146. package/tests/search-shell.test.js +499 -0
  147. package/tests/server.test.js +882 -0
  148. package/tests/service.test.js +267 -0
  149. package/tests/store.test.js +369 -0
  150. package/tests/tunnel.test.js +67 -0
  151. package/tests/workspace-head.test.js +116 -0
  152. package/tests/workspace.test.js +417 -0
  153. package/tsconfig.backend.json +11 -0
  154. package/tsconfig.json +15 -0
  155. package/tui/client/client_test.go +125 -0
  156. package/tui/client/connection.go +342 -0
  157. package/tui/client/host_manager.go +141 -0
  158. package/tui/config/cache.go +81 -0
  159. package/tui/config/config.go +53 -0
  160. package/tui/config/config_test.go +89 -0
  161. package/tui/go.mod +32 -0
  162. package/tui/go.sum +50 -0
  163. package/tui/main.go +261 -0
  164. package/tui/tests/integration_test.go +283 -0
  165. package/tui/ui/model.go +310 -0
  166. package/vitest.config.js +10 -0
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # Remux
2
+
3
+ **Remote terminal workspace — powered by ghostty-web.**
4
+
5
+ [![GitHub stars](https://img.shields.io/github/stars/yaoshenwang/remux?style=social)](https://github.com/yaoshenwang/remux/stargazers)
6
+ ![GitHub contributors](https://img.shields.io/github/contributors/yaoshenwang/remux)
7
+
8
+ Remux lets you monitor and control terminal sessions from any device — phone, tablet, or another computer — through a web browser. It runs a lightweight Node.js server that manages shell sessions and streams them via WebSocket using [ghostty-web](https://github.com/coder/ghostty-web) for stable, high-quality terminal rendering.
9
+
10
+ ## Why Remux
11
+
12
+ - Access your terminal sessions from any browser, including mobile
13
+ - Multiple sessions and tabs, managed through a VS Code-style sidebar and tab bar
14
+ - Stable rendering with ghostty-web (Ghostty VT engine compiled to WASM)
15
+ - Server-side VT state tracking for instant session restore on reconnect
16
+ - Mobile-friendly compose bar for special keys (Esc, Tab, Ctrl, arrows)
17
+ - Token authentication for secure access
18
+ - Session persistence across server restarts
19
+ - Zero configuration — `npx @wangyaoshen/remux` and go
20
+
21
+ ## Quick Start
22
+
23
+ ### Prerequisites
24
+
25
+ - Node.js 20+
26
+
27
+ ### Run from npm
28
+
29
+ ```bash
30
+ npx @wangyaoshen/remux
31
+ ```
32
+
33
+ Remux prints a local URL. Open it from any browser.
34
+
35
+ ### Run with authentication
36
+
37
+ ```bash
38
+ REMUX_TOKEN=my-secret-token npx @wangyaoshen/remux
39
+ ```
40
+
41
+ Access via `http://localhost:8767/?token=my-secret-token`.
42
+
43
+ ### Run from source
44
+
45
+ ```bash
46
+ git clone https://github.com/yaoshenwang/remux.git
47
+ cd remux
48
+ pnpm install
49
+ pnpm start
50
+ ```
51
+
52
+ ## Features
53
+
54
+ - **Multiple sessions** — create, switch, and delete named sessions from the sidebar
55
+ - **Multiple tabs per session** — Chrome-style tab bar with create, close, and switch
56
+ - **ghostty-web rendering** — Ghostty VT engine in WASM, stable truecolor Canvas rendering
57
+ - **Server-side VT tracking** — ghostty-vt WASM tracks terminal state for instant snapshot restore
58
+ - **Session persistence** — sessions and scrollback survive server restarts
59
+ - **Multi-client support** — multiple browsers can connect simultaneously with coordinated terminal sizing
60
+ - **Token authentication** — protect access with `REMUX_TOKEN` environment variable
61
+ - **Mobile support** — responsive sidebar drawer, compose bar for special keys, viewport-aware layout
62
+ - **Auto reconnect** — WebSocket reconnects automatically on disconnection
63
+
64
+ ## Architecture
65
+
66
+ ```
67
+ Browser (ghostty-web Canvas)
68
+
69
+ └── WebSocket /ws (control + terminal data)
70
+
71
+
72
+ server.js (Node.js)
73
+ ├── HTTP server (serves app + ghostty-web assets)
74
+ ├── WebSocket server (session/tab control + terminal I/O)
75
+ ├── PTY management (node-pty, direct shell)
76
+ ├── VT tracking (ghostty-vt WASM, server-side snapshots)
77
+ └── Session persistence (JSON file, periodic save)
78
+ ```
79
+
80
+ ## Environment Variables
81
+
82
+ | Variable | Description |
83
+ |----------|-------------|
84
+ | `PORT` | Server port (default: 8767) |
85
+ | `REMUX_TOKEN` | Authentication token (optional; if set, required for access) |
86
+ | `REMUX_INSTANCE_ID` | Instance identifier for persistence file isolation |
87
+
88
+ ## Tech Stack
89
+
90
+ - **Runtime**: Node.js 20+
91
+ - **Terminal rendering**: [ghostty-web](https://github.com/coder/ghostty-web) (Ghostty VT engine, WASM + Canvas)
92
+ - **PTY management**: [node-pty](https://github.com/niclas-niclas-niclas/node-pty)
93
+ - **WebSocket**: [ws](https://github.com/websockets/ws)
94
+ - **Server-side VT**: ghostty-vt WASM (same engine as browser, loaded server-side)
95
+ - **Testing**: [Vitest](https://vitest.dev/)
96
+ - **TUI companion**: Go + Bubbletea (in `tui/`)
97
+
98
+ ## Development
99
+
100
+ ```bash
101
+ pnpm install
102
+ pnpm run dev # start server
103
+ pnpm test # run tests
104
+ ```
105
+
106
+ ## Contributors
107
+
108
+ Thanks to everyone who has helped shape Remux.
109
+
110
+ [![Contributors](https://contrib.rocks/image?repo=yaoshenwang/remux)](https://github.com/yaoshenwang/remux/graphs/contributors)
111
+
112
+ Made with [contrib.rocks](https://contrib.rocks).
113
+
114
+ ## Star History
115
+
116
+ [![Star History Chart](https://api.star-history.com/svg?repos=yaoshenwang/remux&type=Date)](https://star-history.com/#yaoshenwang/remux&Date)
117
+
118
+ ## License
119
+
120
+ MIT. See [LICENSE](./LICENSE).
@@ -0,0 +1,4 @@
1
+ DEVELOPMENT_TEAM = LY8QD6TJN6
2
+ CODE_SIGN_IDENTITY = Apple Development
3
+ CODE_SIGN_STYLE = Automatic
4
+ PRODUCT_BUNDLE_IDENTIFIER = com.remux.ios
@@ -0,0 +1,26 @@
1
+ // swift-tools-version: 6.0
2
+
3
+ import PackageDescription
4
+
5
+ // NOTE: iOS apps cannot be built with `swift build` (SPM limitation).
6
+ // Build with: xcodebuild -scheme Remux -destination 'platform=iOS Simulator,name=iPhone 17 Pro'
7
+ // This Package.swift is used by Xcode for SPM dependency resolution only.
8
+ let package = Package(
9
+ name: "RemuxiOS",
10
+ platforms: [.iOS(.v17)],
11
+ dependencies: [
12
+ .package(path: "../../packages/RemuxKit"),
13
+ ],
14
+ targets: [
15
+ .executableTarget(
16
+ name: "RemuxiOS",
17
+ dependencies: ["RemuxKit"],
18
+ path: "Sources/Remux",
19
+ linkerSettings: [
20
+ .linkedFramework("UIKit"),
21
+ .linkedFramework("AVFoundation"),
22
+ .linkedFramework("WebKit"),
23
+ ]
24
+ ),
25
+ ]
26
+ )
@@ -0,0 +1,456 @@
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
+ /* End PBXBuildFile section */
25
+
26
+ /* Begin PBXFileReference section */
27
+ 0B7269AB643DAA5051FBDACE /* MeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeView.swift; sourceTree = "<group>"; };
28
+ 1ED7C2F66BB76FB8DE51725E /* InspectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectView.swift; sourceTree = "<group>"; };
29
+ 3120562997C5EB5D054DAB21 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
30
+ 48C3DD287E421F1077A8923A /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
31
+ 5056D67965667A307294CFB1 /* FaceIDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceIDManager.swift; sourceTree = "<group>"; };
32
+ 662FEE0E78ADAADEA731106C /* RemuxiOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemuxiOSApp.swift; sourceTree = "<group>"; };
33
+ 6C491FD55D84AC2BED5C5EBD /* Remux.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Remux.entitlements; sourceTree = "<group>"; };
34
+ 6E3AE7922D1E43AE099D1D40 /* ManualConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualConnectView.swift; sourceTree = "<group>"; };
35
+ 734A97B1146E816099903C0D /* LiveTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTerminalView.swift; sourceTree = "<group>"; };
36
+ 8FAA68E03D60CC5F0A92A1DB /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
37
+ A47FAD14E232D67C4163B551 /* RemuxKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = RemuxKit; path = ../../packages/RemuxKit; sourceTree = SOURCE_ROOT; };
38
+ BF2A531BFD8446B4BCB5A0B2 /* QRScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerView.swift; sourceTree = "<group>"; };
39
+ C6233E6291A38BAAE86C727D /* Remux.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Remux.app; sourceTree = BUILT_PRODUCTS_DIR; };
40
+ D548B0C91D3AABA26641D898 /* ControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlView.swift; sourceTree = "<group>"; };
41
+ DBE379B0C119DDC9C8B324E5 /* NowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowView.swift; sourceTree = "<group>"; };
42
+ DEEEA01EB80CE2C1601F6228 /* InspectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectCache.swift; sourceTree = "<group>"; };
43
+ /* End PBXFileReference section */
44
+
45
+ /* Begin PBXFrameworksBuildPhase section */
46
+ 03BDC461530EDD09CDB7FF29 /* Frameworks */ = {
47
+ isa = PBXFrameworksBuildPhase;
48
+ buildActionMask = 2147483647;
49
+ files = (
50
+ EE0A712F52F48C1AB61D7AEC /* RemuxKit in Frameworks */,
51
+ );
52
+ runOnlyForDeploymentPostprocessing = 0;
53
+ };
54
+ /* End PBXFrameworksBuildPhase section */
55
+
56
+ /* Begin PBXGroup section */
57
+ 02177B1EEEF3A70CA8B1DC00 /* Onboarding */ = {
58
+ isa = PBXGroup;
59
+ children = (
60
+ 6E3AE7922D1E43AE099D1D40 /* ManualConnectView.swift */,
61
+ 3120562997C5EB5D054DAB21 /* OnboardingView.swift */,
62
+ BF2A531BFD8446B4BCB5A0B2 /* QRScannerView.swift */,
63
+ );
64
+ path = Onboarding;
65
+ sourceTree = "<group>";
66
+ };
67
+ 0E70947CCFEAEFEDCCF14E35 /* Remux */ = {
68
+ isa = PBXGroup;
69
+ children = (
70
+ 48C3DD287E421F1077A8923A /* MainTabView.swift */,
71
+ 6C491FD55D84AC2BED5C5EBD /* Remux.entitlements */,
72
+ 662FEE0E78ADAADEA731106C /* RemuxiOSApp.swift */,
73
+ 8FAA68E03D60CC5F0A92A1DB /* RootView.swift */,
74
+ E0BAA4E34FA2A6985DF4C973 /* Extensions */,
75
+ 34F4D5A768A03291C3584961 /* Views */,
76
+ );
77
+ name = Remux;
78
+ path = Sources/Remux;
79
+ sourceTree = "<group>";
80
+ };
81
+ 34F4D5A768A03291C3584961 /* Views */ = {
82
+ isa = PBXGroup;
83
+ children = (
84
+ 89F7EBDCDA60DF5612AE9A65 /* Control */,
85
+ E3592A5F25FDAC99767AF4F0 /* Inspect */,
86
+ E6ABC817B70CA54DD2647104 /* Live */,
87
+ DBC76E38E5D91242FED6C091 /* Now */,
88
+ 02177B1EEEF3A70CA8B1DC00 /* Onboarding */,
89
+ E77261034A2F42E559D8BC9E /* Settings */,
90
+ );
91
+ path = Views;
92
+ sourceTree = "<group>";
93
+ };
94
+ 89F7EBDCDA60DF5612AE9A65 /* Control */ = {
95
+ isa = PBXGroup;
96
+ children = (
97
+ D548B0C91D3AABA26641D898 /* ControlView.swift */,
98
+ );
99
+ path = Control;
100
+ sourceTree = "<group>";
101
+ };
102
+ 9383E7B61F907BDA3838C6DA /* Packages */ = {
103
+ isa = PBXGroup;
104
+ children = (
105
+ A47FAD14E232D67C4163B551 /* RemuxKit */,
106
+ );
107
+ name = Packages;
108
+ sourceTree = "<group>";
109
+ };
110
+ D5B969C9596603CB720FBE0D = {
111
+ isa = PBXGroup;
112
+ children = (
113
+ 9383E7B61F907BDA3838C6DA /* Packages */,
114
+ 0E70947CCFEAEFEDCCF14E35 /* Remux */,
115
+ F94500072A3763A6590AF480 /* Products */,
116
+ );
117
+ sourceTree = "<group>";
118
+ };
119
+ DBC76E38E5D91242FED6C091 /* Now */ = {
120
+ isa = PBXGroup;
121
+ children = (
122
+ DBE379B0C119DDC9C8B324E5 /* NowView.swift */,
123
+ );
124
+ path = Now;
125
+ sourceTree = "<group>";
126
+ };
127
+ E0BAA4E34FA2A6985DF4C973 /* Extensions */ = {
128
+ isa = PBXGroup;
129
+ children = (
130
+ 5056D67965667A307294CFB1 /* FaceIDManager.swift */,
131
+ DEEEA01EB80CE2C1601F6228 /* InspectCache.swift */,
132
+ );
133
+ path = Extensions;
134
+ sourceTree = "<group>";
135
+ };
136
+ E3592A5F25FDAC99767AF4F0 /* Inspect */ = {
137
+ isa = PBXGroup;
138
+ children = (
139
+ 1ED7C2F66BB76FB8DE51725E /* InspectView.swift */,
140
+ );
141
+ path = Inspect;
142
+ sourceTree = "<group>";
143
+ };
144
+ E6ABC817B70CA54DD2647104 /* Live */ = {
145
+ isa = PBXGroup;
146
+ children = (
147
+ 734A97B1146E816099903C0D /* LiveTerminalView.swift */,
148
+ );
149
+ path = Live;
150
+ sourceTree = "<group>";
151
+ };
152
+ E77261034A2F42E559D8BC9E /* Settings */ = {
153
+ isa = PBXGroup;
154
+ children = (
155
+ 0B7269AB643DAA5051FBDACE /* MeView.swift */,
156
+ );
157
+ path = Settings;
158
+ sourceTree = "<group>";
159
+ };
160
+ F94500072A3763A6590AF480 /* Products */ = {
161
+ isa = PBXGroup;
162
+ children = (
163
+ C6233E6291A38BAAE86C727D /* Remux.app */,
164
+ );
165
+ name = Products;
166
+ sourceTree = "<group>";
167
+ };
168
+ /* End PBXGroup section */
169
+
170
+ /* Begin PBXNativeTarget section */
171
+ 7125659F6728FEF22F3FDFB5 /* Remux */ = {
172
+ isa = PBXNativeTarget;
173
+ buildConfigurationList = 9A9EB61A159DAC867ECA3B9D /* Build configuration list for PBXNativeTarget "Remux" */;
174
+ buildPhases = (
175
+ 204B591A381758166DA82963 /* Sources */,
176
+ 03BDC461530EDD09CDB7FF29 /* Frameworks */,
177
+ );
178
+ buildRules = (
179
+ );
180
+ dependencies = (
181
+ );
182
+ name = Remux;
183
+ packageProductDependencies = (
184
+ EF63343AE24B477D7365EE17 /* RemuxKit */,
185
+ );
186
+ productName = Remux;
187
+ productReference = C6233E6291A38BAAE86C727D /* Remux.app */;
188
+ productType = "com.apple.product-type.application";
189
+ };
190
+ /* End PBXNativeTarget section */
191
+
192
+ /* Begin PBXProject section */
193
+ AB9B0F665DE3D1CF4BE38CF5 /* Project object */ = {
194
+ isa = PBXProject;
195
+ attributes = {
196
+ BuildIndependentTargetsInParallel = YES;
197
+ LastUpgradeCheck = 2630;
198
+ TargetAttributes = {
199
+ 7125659F6728FEF22F3FDFB5 = {
200
+ DevelopmentTeam = LY8QD6TJN6;
201
+ ProvisioningStyle = Automatic;
202
+ };
203
+ };
204
+ };
205
+ buildConfigurationList = F118F8E44563891091219E31 /* Build configuration list for PBXProject "Remux" */;
206
+ compatibilityVersion = "Xcode 14.0";
207
+ developmentRegion = en;
208
+ hasScannedForEncodings = 0;
209
+ knownRegions = (
210
+ Base,
211
+ en,
212
+ );
213
+ mainGroup = D5B969C9596603CB720FBE0D;
214
+ minimizedProjectReferenceProxies = 1;
215
+ packageReferences = (
216
+ 4F0952D532975C4F55CAE590 /* XCLocalSwiftPackageReference "../../packages/RemuxKit" */,
217
+ );
218
+ preferredProjectObjectVersion = 77;
219
+ projectDirPath = "";
220
+ projectRoot = "";
221
+ targets = (
222
+ 7125659F6728FEF22F3FDFB5 /* Remux */,
223
+ );
224
+ };
225
+ /* End PBXProject section */
226
+
227
+ /* Begin PBXSourcesBuildPhase section */
228
+ 204B591A381758166DA82963 /* Sources */ = {
229
+ isa = PBXSourcesBuildPhase;
230
+ buildActionMask = 2147483647;
231
+ files = (
232
+ 78E3310D198D772C606FBB0B /* ControlView.swift in Sources */,
233
+ 810D705F63FBA1B407B0FBC1 /* FaceIDManager.swift in Sources */,
234
+ BBCF34D61B0F6B8CFB234965 /* InspectCache.swift in Sources */,
235
+ F136D905F6E9BA7F2C1816F2 /* InspectView.swift in Sources */,
236
+ DD0F6EA123F2BCF28AAA676A /* LiveTerminalView.swift in Sources */,
237
+ CA2151750F645DA9BC49C7C8 /* MainTabView.swift in Sources */,
238
+ ECAFCC766BD77CCD21821612 /* ManualConnectView.swift in Sources */,
239
+ 622F7BF35D6BB2E6C7715426 /* MeView.swift in Sources */,
240
+ CB45E538B1DF947F0C8D9542 /* NowView.swift in Sources */,
241
+ 622B8BA7525C80F0416F5E1B /* OnboardingView.swift in Sources */,
242
+ 04EE3BCAD2C297F8696C82C2 /* QRScannerView.swift in Sources */,
243
+ 346FBA7A7057F163D7CC8F03 /* RemuxiOSApp.swift in Sources */,
244
+ D32ABBB3E21D0344B3B9455C /* RootView.swift in Sources */,
245
+ );
246
+ runOnlyForDeploymentPostprocessing = 0;
247
+ };
248
+ /* End PBXSourcesBuildPhase section */
249
+
250
+ /* Begin XCBuildConfiguration section */
251
+ 4CDD0720CAFFE2E1481FBFA7 /* Debug */ = {
252
+ isa = XCBuildConfiguration;
253
+ buildSettings = {
254
+ ALWAYS_SEARCH_USER_PATHS = NO;
255
+ CLANG_ANALYZER_NONNULL = YES;
256
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
257
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
258
+ CLANG_CXX_LIBRARY = "libc++";
259
+ CLANG_ENABLE_MODULES = YES;
260
+ CLANG_ENABLE_OBJC_ARC = YES;
261
+ CLANG_ENABLE_OBJC_WEAK = YES;
262
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263
+ CLANG_WARN_BOOL_CONVERSION = YES;
264
+ CLANG_WARN_COMMA = YES;
265
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
266
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
267
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
268
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
269
+ CLANG_WARN_EMPTY_BODY = YES;
270
+ CLANG_WARN_ENUM_CONVERSION = YES;
271
+ CLANG_WARN_INFINITE_RECURSION = YES;
272
+ CLANG_WARN_INT_CONVERSION = YES;
273
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
274
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
275
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
276
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
277
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
278
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
279
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
280
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
281
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
282
+ CLANG_WARN_UNREACHABLE_CODE = YES;
283
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
284
+ CODE_SIGN_STYLE = Automatic;
285
+ COPY_PHASE_STRIP = NO;
286
+ DEBUG_INFORMATION_FORMAT = dwarf;
287
+ DEVELOPMENT_TEAM = LY8QD6TJN6;
288
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
289
+ ENABLE_TESTABILITY = YES;
290
+ GCC_C_LANGUAGE_STANDARD = gnu11;
291
+ GCC_DYNAMIC_NO_PIC = NO;
292
+ GCC_NO_COMMON_BLOCKS = YES;
293
+ GCC_OPTIMIZATION_LEVEL = 0;
294
+ GCC_PREPROCESSOR_DEFINITIONS = (
295
+ "$(inherited)",
296
+ "DEBUG=1",
297
+ );
298
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
299
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
300
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
301
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
302
+ GCC_WARN_UNUSED_FUNCTION = YES;
303
+ GCC_WARN_UNUSED_VARIABLE = YES;
304
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
305
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
306
+ MTL_FAST_MATH = YES;
307
+ ONLY_ACTIVE_ARCH = YES;
308
+ PRODUCT_NAME = "$(TARGET_NAME)";
309
+ SDKROOT = iphoneos;
310
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
311
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
312
+ SWIFT_VERSION = 6.0;
313
+ };
314
+ name = Debug;
315
+ };
316
+ 89E46C713EAC3CEC87DDEBC6 /* Release */ = {
317
+ isa = XCBuildConfiguration;
318
+ buildSettings = {
319
+ ALWAYS_SEARCH_USER_PATHS = NO;
320
+ CLANG_ANALYZER_NONNULL = YES;
321
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
322
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
323
+ CLANG_CXX_LIBRARY = "libc++";
324
+ CLANG_ENABLE_MODULES = YES;
325
+ CLANG_ENABLE_OBJC_ARC = YES;
326
+ CLANG_ENABLE_OBJC_WEAK = YES;
327
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
328
+ CLANG_WARN_BOOL_CONVERSION = YES;
329
+ CLANG_WARN_COMMA = YES;
330
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
331
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
332
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
333
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
334
+ CLANG_WARN_EMPTY_BODY = YES;
335
+ CLANG_WARN_ENUM_CONVERSION = YES;
336
+ CLANG_WARN_INFINITE_RECURSION = YES;
337
+ CLANG_WARN_INT_CONVERSION = YES;
338
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
339
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
340
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
341
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
342
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
343
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
344
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
345
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
346
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
347
+ CLANG_WARN_UNREACHABLE_CODE = YES;
348
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
349
+ CODE_SIGN_STYLE = Automatic;
350
+ COPY_PHASE_STRIP = NO;
351
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
352
+ DEVELOPMENT_TEAM = LY8QD6TJN6;
353
+ ENABLE_NS_ASSERTIONS = NO;
354
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
355
+ GCC_C_LANGUAGE_STANDARD = gnu11;
356
+ GCC_NO_COMMON_BLOCKS = YES;
357
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
358
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
359
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
360
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
361
+ GCC_WARN_UNUSED_FUNCTION = YES;
362
+ GCC_WARN_UNUSED_VARIABLE = YES;
363
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
364
+ MTL_ENABLE_DEBUG_INFO = NO;
365
+ MTL_FAST_MATH = YES;
366
+ PRODUCT_NAME = "$(TARGET_NAME)";
367
+ SDKROOT = iphoneos;
368
+ SWIFT_COMPILATION_MODE = wholemodule;
369
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
370
+ SWIFT_VERSION = 6.0;
371
+ };
372
+ name = Release;
373
+ };
374
+ A8EBDFB043CD42C757E3F876 /* Debug */ = {
375
+ isa = XCBuildConfiguration;
376
+ buildSettings = {
377
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
378
+ CODE_SIGN_ENTITLEMENTS = Sources/Remux/Remux.entitlements;
379
+ CODE_SIGN_IDENTITY = "iPhone Developer";
380
+ GENERATE_INFOPLIST_FILE = YES;
381
+ INFOPLIST_KEY_NSCameraUsageDescription = "Remux uses the camera to scan QR codes for server pairing.";
382
+ INFOPLIST_KEY_NSFaceIDUsageDescription = "Remux uses Face ID to protect your terminal sessions.";
383
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
384
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
385
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
386
+ LD_RUNPATH_SEARCH_PATHS = (
387
+ "$(inherited)",
388
+ "@executable_path/Frameworks",
389
+ );
390
+ PRODUCT_BUNDLE_IDENTIFIER = com.remux.ios;
391
+ SDKROOT = iphoneos;
392
+ TARGETED_DEVICE_FAMILY = "1,2";
393
+ };
394
+ name = Debug;
395
+ };
396
+ C0E5593BA2CFEE44A2B659DC /* Release */ = {
397
+ isa = XCBuildConfiguration;
398
+ buildSettings = {
399
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
400
+ CODE_SIGN_ENTITLEMENTS = Sources/Remux/Remux.entitlements;
401
+ CODE_SIGN_IDENTITY = "iPhone Developer";
402
+ GENERATE_INFOPLIST_FILE = YES;
403
+ INFOPLIST_KEY_NSCameraUsageDescription = "Remux uses the camera to scan QR codes for server pairing.";
404
+ INFOPLIST_KEY_NSFaceIDUsageDescription = "Remux uses Face ID to protect your terminal sessions.";
405
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
406
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
407
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
408
+ LD_RUNPATH_SEARCH_PATHS = (
409
+ "$(inherited)",
410
+ "@executable_path/Frameworks",
411
+ );
412
+ PRODUCT_BUNDLE_IDENTIFIER = com.remux.ios;
413
+ SDKROOT = iphoneos;
414
+ TARGETED_DEVICE_FAMILY = "1,2";
415
+ };
416
+ name = Release;
417
+ };
418
+ /* End XCBuildConfiguration section */
419
+
420
+ /* Begin XCConfigurationList section */
421
+ 9A9EB61A159DAC867ECA3B9D /* Build configuration list for PBXNativeTarget "Remux" */ = {
422
+ isa = XCConfigurationList;
423
+ buildConfigurations = (
424
+ A8EBDFB043CD42C757E3F876 /* Debug */,
425
+ C0E5593BA2CFEE44A2B659DC /* Release */,
426
+ );
427
+ defaultConfigurationIsVisible = 0;
428
+ defaultConfigurationName = Debug;
429
+ };
430
+ F118F8E44563891091219E31 /* Build configuration list for PBXProject "Remux" */ = {
431
+ isa = XCConfigurationList;
432
+ buildConfigurations = (
433
+ 4CDD0720CAFFE2E1481FBFA7 /* Debug */,
434
+ 89E46C713EAC3CEC87DDEBC6 /* Release */,
435
+ );
436
+ defaultConfigurationIsVisible = 0;
437
+ defaultConfigurationName = Debug;
438
+ };
439
+ /* End XCConfigurationList section */
440
+
441
+ /* Begin XCLocalSwiftPackageReference section */
442
+ 4F0952D532975C4F55CAE590 /* XCLocalSwiftPackageReference "../../packages/RemuxKit" */ = {
443
+ isa = XCLocalSwiftPackageReference;
444
+ relativePath = ../../packages/RemuxKit;
445
+ };
446
+ /* End XCLocalSwiftPackageReference section */
447
+
448
+ /* Begin XCSwiftPackageProductDependency section */
449
+ EF63343AE24B477D7365EE17 /* RemuxKit */ = {
450
+ isa = XCSwiftPackageProductDependency;
451
+ productName = RemuxKit;
452
+ };
453
+ /* End XCSwiftPackageProductDependency section */
454
+ };
455
+ rootObject = AB9B0F665DE3D1CF4BE38CF5 /* Project object */;
456
+ }
@@ -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,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
+ }