From 92c9ac6ae209e0dca0e91e8fa170f4ab962bfe24 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 11:53:12 +0800 Subject: [PATCH 01/89] docs: add macOS TUN support design spec --- .../specs/2026-07-13-macos-tun-design.md | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-13-macos-tun-design.md diff --git a/docs/superpowers/specs/2026-07-13-macos-tun-design.md b/docs/superpowers/specs/2026-07-13-macos-tun-design.md new file mode 100644 index 0000000..0976546 --- /dev/null +++ b/docs/superpowers/specs/2026-07-13-macos-tun-design.md @@ -0,0 +1,299 @@ +# macOS TUN 支持 — 设计文档 + +日期: 2026-07-13 + +## 目标 + +为 GostX macOS 客户端添加 TUN VPN 支持:VPN 启动后,系统所有流量经 utun 接口通过 gost chain 转发。 + +## 架构概览 + +``` +GostX.app +├── 主 App (SwiftUI 状态栏) +│ ├── VpnManager.swift # NETunnelProviderManager 启停 +│ ├── AppDelegate.swift # VPN 模式 + 旧代理模式 +│ ├── SettingsView.swift # YAML 编辑(同步到 App Group) +│ └── MacExtrasConfigurator.swift +│ +├── GostXTunnel.appex (PacketTunnelProvider Extension) +│ └── PacketTunnelProvider.swift +│ ├── startTunnel: +│ │ 1. 从 App Group 读 YAML → StartGost(yaml, "") +│ │ 2. setTunnelNetworkSettings → 系统创建 utun +│ │ 3. GetTunnelFileDescriptor() → 扫描 fd 找 utun +│ │ 4. StartTun(fd, 1500) → sing-tun 直接 fd 读写 +│ └── stopTunnel: +│ 1. StopTun() → StopGost() +│ +└── Frameworks/Libgost.xcframework # gomobile bind -target macos +``` + +### App Group 通信 + +主 App 和 Extension 通过 `group.cn.liukebin.gostx` 共享: +- **YAML 配置**:主 App 写入 → Extension 读取 +- **日志文件**:Extension 写入 → 主 App 读取显示 + +## TUN 数据流 + +``` +Apps → utun → sing-tun system stack (IP header rewrite) + → OS kernel TCP/IP → singTunHandler (NewConnectionEx) + → gost chain Router → 上游代理 → 互联网 +``` + +与 Android 完全一致,不需要 `packetFlow.readPackets()/writePackets()` 桥接。Go sing-tun 直接在 utun fd 上读写。 + +### 路由循环 + +macOS NetworkExtension 的 `setTunnelNetworkSettings` 配置路由后,系统能区分哪些流量走 utun、哪些不走。Extension 进程自身创建的 upstream socket 不会被自己的 VPN 路由捕获,**不需要** Android 的 `protect(fd)` 机制。 + +## Go 层改动 + +### 新增:`tun_darwin.go` — utun fd 发现 + +```go +// +build darwin + +package libgost + +import "golang.org/x/sys/unix" + +// GetTunnelFileDescriptor 扫描 fd 0..1024,找到 com.apple.net.utun_control +// 控制 socket 并返回其 fd。返回 -1 表示未找到。 +// 实现参考 wireguard-apple 的 WireGuardAdapter.tunnelFileDescriptor。 +func GetTunnelFileDescriptor() int32 { + ctlInfo := &unix.CtlInfo{} + copy(ctlInfo.Name[:], "com.apple.net.utun_control") + for fd := int32(0); fd < 1024; fd++ { + addr, err := unix.Getpeername(int(fd)) + if err != nil { + continue + } + addrCTL, ok := addr.(*unix.SockaddrCtl) + if !ok { + continue + } + if ctlInfo.Id == 0 { + if err = unix.IoctlCtlInfo(int(fd), ctlInfo); err != nil { + continue + } + } + if addrCTL.ID == ctlInfo.Id { + return fd + } + } + return -1 +} +``` + +### 不变(零改动) + +| 函数 | 说明 | +|------|------| +| `StartTun(fd int, mtu int) error` | 已支持 Unix fd + sing-tun,不区分平台 | +| `StopTun() error` | 同上 | +| `StartGost(yaml, systemDNS string) error` | systemDNS 传空字符串即可 | +| `StopGost() error` | 同上 | +| `relay()` / `relayPacketConn()` / `singTunHandler` | 全部不变 | + +### macOS 上 no-op + +- `SetMemoryLimit(bool)` — macOS Extension 不需要控 GC,不调用或内部 no-op +- `SetSocketProtector(SocketProtector)` — macOS 不需要 protect,不调用或内部 no-op + +## 编译 & 构建 + +### gomobile bind + +```bash +# libgost/Makefile 新增 +macos-framework: + CGO_ENABLED=1 gomobile bind \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o ../macos/Frameworks/Libgost.xcframework \ + . +``` + +产物 `Libgost.xcframework` 包含 `macos-arm64_x86_64/`。 + +Go 函数自动映射为 Swift 调用(包名 `libgost` → 前缀 `Libgost`): + +| Go | Swift | +|----|-------| +| `StartTun(fd, mtu) error` | `try LibgostStartTun(_ fd: Int, _ mtu: Int)` | +| `StartGost(yaml, dns) error` | `try LibgostStartGost(_ yaml: String, _ dns: String)` | +| `StopTun() error` | `try LibgostStopTun()` | +| `GetTunnelFileDescriptor() int32` | `LibgostGetTunnelFileDescriptor() -> Int32` | + +## Swift 层改动 + +### PacketTunnelProvider Extension(新增 `macos/GostXTunnel/`) + +核心文件 `PacketTunnelProvider.swift`: + +```swift +import NetworkExtension + +class PacketTunnelProvider: NEPacketTunnelProvider { + + override func startTunnel(options: [String: NSObject]?) async throws { + // 1. 从 App Group 读 YAML + let yaml = loadYamlFromAppGroup() + + // 2. 设置日志 + LibgostSetLogFile(logFilePath) + LibgostSetLogLevel("info") + + // 3. 启动 gost 服务 + try LibgostStartGost(yaml, "") + + // 4. 配置 TUN 路由(系统自动创建 utun) + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1") + settings.mtu = 1500 + settings.ipv4Settings = NEIPv4Settings( + addresses: ["10.0.0.2"], + subnetMasks: ["255.255.255.0"] + ) + settings.ipv4Settings!.includedRoutes = [NEIPv4Route.default()] + settings.dnsSettings = NEDNSSettings(servers: ["10.0.0.3"]) + + try await setTunnelNetworkSettings(settings) + + // 5. 找到 utun fd + let tunFd = LibgostGetTunnelFileDescriptor() + guard tunFd != -1 else { + LibgostStopGost() + cancelTunnelWithError(NSError(domain: "cannot locate TUN fd", code: 1)) + return + } + + // 6. 启动 TUN stack + try LibgostStartTun(Int(tunFd), 1500) + } + + override func stopTunnel(with reason: NEProviderStopReason) async { + LibgostStopTun() + LibgostStopGost() + } +} +``` + +### 主 App(修改 `macos/GostX/`) + +**新增 `VpnManager.swift`:** + +```swift +import NetworkExtension + +class VpnManager: ObservableObject { + @Published var state: NEVPNStatus = .invalid + private var manager: NETunnelProviderManager? + + func loadAndSave() async { + let managers = await NETunnelProviderManager.loadAllFromPreferences() + manager = managers.first ?? NETunnelProviderManager() + let proto = NETunnelProviderProtocol() + proto.providerBundleIdentifier = "cn.liukebin.gostx.tunnel" + proto.serverAddress = "GostX" + manager?.protocolConfiguration = proto + manager?.isEnabled = true + try? await manager?.saveToPreferences() + } + + func start() async throws { try manager?.connection.startVPNTunnel() } + func stop() { manager?.connection.stopVPNTunnel() } +} +``` + +**AppDelegate 改动:** +- `start()` 根据模式选择:VPN 模式调 `VpnManager.start()`,代理模式调 `LibgostStartGost(yaml, "")` +- 状态栏菜单新增:VPN 状态 + 启停 +- Settings 保存 YAML 时同步写入 App Group 容器 +- 旧 `import Gost` 及 `gostRunYaml()`/`gostStop()`/`gostInfo()` 全部移除 + +### 替换旧依赖 + +旧 `import Gost`(gost v2 C bridge)完全替换为 `import Libgost`(gomobile xcframework)。 +两种模式共用同一个 Go 层: +- **代理模式**(非 VPN):`LibgostStartGost(yaml)` 启动本地代理 +- **VPN 模式**:`LibgostStartGost(yaml)` + `LibgostStartTun(fd, mtu)` 启动全局转发 + +## Xcode 项目结构 + +``` +macos/ +├── GostX.xcodeproj +├── GostX/ # 主 App target (AppKit) +│ ├── AppDelegate.swift +│ ├── VpnManager.swift # 新增 +│ ├── SettingsView.swift +│ ├── MacExtrasConfigurator.swift +│ ├── Arguments.swift +│ ├── main.swift +│ └── GostX.entitlements +├── GostXTunnel/ # Extension target (新增) +│ ├── PacketTunnelProvider.swift +│ ├── Info.plist +│ └── GostXTunnel.entitlements +└── Frameworks/ # 新增 + └── Libgost.xcframework # gomobile bind 产物 +``` + +## Entitlements + +### 主 App + +```xml +com.apple.security.app-sandbox → true +com.apple.security.network.client → true +com.apple.security.network.server → true +com.apple.security.application-groups → [group.cn.liukebin.gostx] +``` + +### Extension + +```xml +com.apple.security.app-sandbox → true +com.apple.security.application-groups → [group.cn.liukebin.gostx] +com.apple.security.network.client → true +com.apple.security.network.server → true +com.apple.developer.networking.networkextension → [packet-tunnel-provider] +``` + +## 错误处理 + +### 启动失败 + +各级别分别捕获,失败时 `cancelTunnelWithError`: +1. YAML 读取失败 +2. `StartGost` 失败 +3. `setTunnelNetworkSettings` 失败 / utun fd 找不到 +4. `StartTun` 失败(先 `StopGost` 清理) + +### 网络变化 + +`NWPathMonitor` 监听网络切换,调用 Go 层 `ResetTunConnections()`(已有函数)刷新已有连接。 + +### 停止 + +``` +StopTun() → StopGost() +``` + +WireGuard 的 `exit(0)` hack(Apple bug 32073323)先不加,观察 Extension 进程是否能正常回收,如有问题再补。 + +### Extension 崩溃 + +系统自动清理 utun 接口。主 App 通过 `NEVPNStatusDidChange` 更新 UI。 + +## 风险 & 缓解 + +| 风险 | 缓解 | +|------|------| +| Extension 内存限制 | sing-tun 使用 kernel system stack(非 userspace TCP),内存负担小;暂不设 GC 限制,观察 | +| `gomobile bind -target macos` 兼容性 | Apple Silicon 天然支持 arm64;Intel Mac 需 x86_64,gomobile 默认双架构 | +| utun fd 扫描失败 | 概率极低(WireGuard 至今线上一贯用这个方案);返回清晰错误信息 | +| Go 依赖过多致 framework 体积大 | `-ldflags="-s -w"` 去符号;xcframework 切片按架构分 | From 0831e758fb3cdf399f9360ed298992c2689e8d31 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 13:28:58 +0800 Subject: [PATCH 02/89] docs: add macOS TUN implementation plan (8 tasks) --- .../2026-07-13-macos-tun-implementation.md | 896 ++++++++++++++++++ 1 file changed, 896 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-13-macos-tun-implementation.md diff --git a/docs/superpowers/plans/2026-07-13-macos-tun-implementation.md b/docs/superpowers/plans/2026-07-13-macos-tun-implementation.md new file mode 100644 index 0000000..d1eb57a --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-macos-tun-implementation.md @@ -0,0 +1,896 @@ +# macOS TUN 支持 — 实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 为 GostX macOS 客户端添加 TUN VPN 支持,通过 NEPacketTunnelProvider Extension + sing-tun 实现系统全局流量代理转发。 + +**Architecture:** 主 App(SwiftUI 状态栏)通过 NETunnelProviderManager 控制 PacketTunnelProvider Extension。Extension 使用 gomobile bind 编译的 Libgost.xcframework,调用 Go 层的 StartGost + StartTun。Go 层通过扫描 fd 找到 utun 控制 socket,sing-tun 直接在 fd 上读写,复用全部现有 Android TUN 代码。 + +**Tech Stack:** Go (gomobile bind -target macos), Swift (NetworkExtension, AppKit/SwiftUI), sing-tun system stack + +## Global Constraints + +- Go 层 `StartTun(fd, mtu)` / `StopTun()` / `StartGost()` / `StopGost()` 零改动 +- macOS 不需要 `SetSocketProtector`(无路由循环)和 `SetMemoryLimit`(无内存压力) +- utun fd 通过扫描 `/dev/utun` 控制 socket 获取,不使用 KVC 私有 API +- 主 App 和 Extension 通过 App Group `group.cn.liukebin.gostx` 共享 YAML 和日志 +- 保留旧代理模式(非 VPN),两种模式共用 Libgost framework + +--- + +### Task 1: Go — utun fd 发现函数 + +**Files:** +- Create: `libgost/tun_darwin.go` +- Modify: `libgost/tun_test.go` + +**Interfaces:** +- Produces: `func GetTunnelFileDescriptor() int32` — 扫描 fd 0..1024 找到 utun 控制 socket,返回 fd 或 -1 + +- [ ] **Step 1: 创建 `libgost/tun_darwin.go`** + +```go +//go:build darwin + +package libgost + +import ( + "golang.org/x/sys/unix" +) + +// GetTunnelFileDescriptor scans file descriptors 0..1024 for the utun +// control socket (com.apple.net.utun_control) and returns its fd. +// Returns -1 if no utun socket is found. +// +// Implementation adapted from wireguard-apple's WireGuardAdapter.tunnelFileDescriptor. +func GetTunnelFileDescriptor() int32 { + ctlInfo := &unix.CtlInfo{} + copy(ctlInfo.Name[:], "com.apple.net.utun_control") + for fd := int32(0); fd < 1024; fd++ { + addr, err := unix.Getpeername(int(fd)) + if err != nil { + continue + } + addrCTL, ok := addr.(*unix.SockaddrCtl) + if !ok { + continue + } + if ctlInfo.Id == 0 { + if err = unix.IoctlCtlInfo(int(fd), ctlInfo); err != nil { + continue + } + } + if addrCTL.ID == ctlInfo.Id { + return fd + } + } + return -1 +} +``` + +- [ ] **Step 2: 添加测试到 `libgost/tun_test.go`** + +在文件末尾追加: + +```go +import "runtime" + +func TestGetTunnelFileDescriptor(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("GetTunnelFileDescriptor is darwin-only") + } + // When no utun is active (typical unit test env), returns -1 without error. + fd := GetTunnelFileDescriptor() + if fd != -1 { + t.Logf("found existing utun fd: %d", fd) + } +} +``` + +- [ ] **Step 3: 运行测试** + +```bash +cd libgost && go test -v -run TestGetTunnelFileDescriptor . +``` + +Expected: PASS (macOS 上返回 -1 或找到已存在的 utun fd) + +- [ ] **Step 4: 验证编译** + +```bash +cd libgost && GOOS=darwin GOARCH=arm64 go build . +cd libgost && GOOS=darwin GOARCH=amd64 go build . +``` + +Expected: 编译成功无错误 + +- [ ] **Step 5: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add libgost/tun_darwin.go libgost/tun_test.go +git commit -m "feat: add GetTunnelFileDescriptor for macOS utun fd discovery" +``` + +--- + +### Task 2: Go — Makefile 新增 macos-framework target + +**Files:** +- Modify: `libgost/Makefile` + +- [ ] **Step 1: 在 `libgost/Makefile` 末尾追加 macos-framework target** + +```makefile +# macOS .xcframework via gomobile bind +# Produces Frameworks/Libgost.xcframework with macos-arm64_x86_64 slices. +# Xcode links this into the PacketTunnelProvider Extension target. +macos-framework: + CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o Libgost.xcframework \ + . + mkdir -p ../macos/Frameworks + rm -rf ../macos/Frameworks/Libgost.xcframework + mv Libgost.xcframework ../macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" +``` + +同时更新 `.PHONY` 和 `clean`: + +在 `libgost/Makefile` 顶部修改 `.PHONY` 行: + +```makefile +.PHONY: all clean debug-symbols macos-framework +``` + +在 `clean` target 末尾追加: + +```makefile + rm -rf ../macos/Frameworks/Libgost.xcframework +``` + +- [ ] **Step 2: 构建 xcframework 并验证** + +```bash +cd libgost && make macos-framework +ls -la ../macos/Frameworks/Libgost.xcframework/ +``` + +Expected: `macos-arm64_x86_64/` 目录存在,内含 `Libgost.framework` + +- [ ] **Step 3: 验证 framework 包含所有 Go 导出函数** + +```bash +nm ../macos/Frameworks/Libgost.xcframework/macos-arm64_x86_64/Libgost.framework/Versions/A/Libgost | grep -i "starttun\|stopTun\|startGost\|stopGost\|getTunnelFileDescriptor\|getStatus\|validateConfig\|setLog\|setWorkDir" +``` + +Expected: 输出包含 `StartTun`、`StopTun`、`StartGost`、`StopGost`、`GetTunnelFileDescriptor` 等符号 + +- [ ] **Step 4: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add libgost/Makefile +git commit -m "build: add macos-framework target for gomobile bind -target macos" +``` + +--- + +### Task 3: Xcode — 创建 Extension target + +**Files:** +- Create: `macos/GostXTunnel/Info.plist` +- Create: `macos/GostXTunnel/GostXTunnel.entitlements` + +**Note:** 本 task 创建 Extension 所需的文件。Xcode target 创建(`GostXTunnel`)需要在 Xcode UI 中完成,步骤在最后说明。 + +- [ ] **Step 1: 创建 `macos/GostXTunnel/Info.plist`** + +```xml + + + + + CFBundleDisplayName + GostX Tunnel + CFBundleName + $(PRODUCT_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + +``` + +- [ ] **Step 2: 创建 `macos/GostXTunnel/GostXTunnel.entitlements`** + +```xml + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.cn.liukebin.gostx + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + + +``` + +- [ ] **Step 3: 在 Xcode 中创建 Extension target** + +打开 `macos/GostX.xcodeproj`,按以下步骤操作: + +1. **File → New → Target** → 选择 **macOS → Network Extension** → **Packet Tunnel Provider** +2. Product Name: `GostXTunnel` +3. Bundle Identifier: `cn.liukebin.gostx.tunnel` +4. Team: 选择你的 Apple Developer team +5. 删除 Xcode 自动生成的 `PacketTunnelProvider.swift`(将用我们自己写的替换) +6. 右键 GostXTunnel group → **Add Files** → 选择刚创建的 `Info.plist` 和 `GostXTunnel.entitlements` +7. Build Settings → Code Signing Entitlements → 设置为 `GostXTunnel/GostXTunnel.entitlements` +8. Build Settings → Info.plist File → 设置为 `GostXTunnel/Info.plist` +9. Build Phases → Link Binary With Libraries → 点击 + → Add Other → 选择 `macos/Frameworks/Libgost.xcframework` + +- [ ] **Step 4: 验证 target 配置** + +在 Xcode 中选择 GostXTunnel scheme,按 Cmd+B 构建。 + +Expected: 构建成功(会有一个空的 PacketTunnelProvider.swift 报错,下一步解决) + +- [ ] **Step 5: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostXTunnel/ +git add macos/GostX.xcodeproj/ +git commit -m "feat: add GostXTunnel PacketTunnelProvider Extension target scaffold" +``` + +--- + +### Task 4: Swift — App Group 配置共享辅助类 + +**Files:** +- Create: `macos/GostX/AppGroupConfig.swift` + +**Interfaces:** +- Produces: `struct AppGroupConfig` — 提供 `readYaml()` / `writeYaml(_:)` 静态方法,通过 App Group 容器共享 YAML + +- [ ] **Step 1: 创建 `macos/GostX/AppGroupConfig.swift`** + +```swift +// macos/GostX/AppGroupConfig.swift +import Foundation + +/// 通过 App Group 容器在主 App 和 Extension 之间共享 YAML 配置。 +struct AppGroupConfig { + static let groupId = "group.cn.liukebin.gostx" + static let yamlFileName = "gost.yaml" + + static var containerURL: URL? { + FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: groupId + ) + } + + static var yamlFileURL: URL? { + containerURL?.appendingPathComponent(yamlFileName) + } + + /// 从 App Group 容器读取 YAML 配置字符串。 + /// 返回 nil 表示容器不可用或文件不存在。 + static func readYaml() -> String? { + guard let url = yamlFileURL else { return nil } + return try? String(contentsOf: url, encoding: .utf8) + } + + /// 将 YAML 配置写入 App Group 容器。 + /// 失败静默忽略(Extension 启动时会检查并报错)。 + static func writeYaml(_ yaml: String) { + guard let url = yamlFileURL else { return } + try? yaml.write(to: url, atomically: true, encoding: .utf8) + } +} +``` + +- [ ] **Step 2: 在主 App target 中添加该文件** + +1. Xcode → GostX target → Build Phases → Compile Sources → 确认 `AppGroupConfig.swift` 在列表中 +2. 同样在 GostXTunnel target → Build Phases → Compile Sources → 添加 `AppGroupConfig.swift` + +这样两个 target 都能使用 `AppGroupConfig`。 + +- [ ] **Step 3: 验证编译** + +Xcode 中依次选择 GostX scheme 和 GostXTunnel scheme,Cmd+B 编译。 + +Expected: 两个 target 均编译成功 + +- [ ] **Step 4: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostX/AppGroupConfig.swift +git add macos/GostX.xcodeproj/ +git commit -m "feat: add AppGroupConfig helper for YAML sharing via App Group" +``` + +--- + +### Task 5: Swift — PacketTunnelProvider 实现 + +**Files:** +- Create: `macos/GostXTunnel/PacketTunnelProvider.swift` + +**Interfaces:** +- Consumes: `AppGroupConfig.readYaml()`, `LibgostStartGost()`, `LibgostStartTun()`, `LibgostStopTun()`, `LibgostStopGost()`, `LibgostGetTunnelFileDescriptor()`, `LibgostSetLogFile()`, `LibgostSetLogLevel()`, `LibgostSetWorkDir()` +- Produces: `class PacketTunnelProvider: NEPacketTunnelProvider` — VPN Extension 入口 + +- [ ] **Step 1: 创建 `macos/GostXTunnel/PacketTunnelProvider.swift`** + +```swift +// macos/GostXTunnel/PacketTunnelProvider.swift +import NetworkExtension +import Libgost + +class PacketTunnelProvider: NEPacketTunnelProvider { + + override func startTunnel(options: [String: NSObject]?) async throws { + // 1. 设置工作目录和日志 + setWorkDirAndLog() + + // 2. 从 App Group 读取 YAML 配置 + guard let yaml = AppGroupConfig.readYaml(), !yaml.isEmpty else { + cancelTunnelWithError(NSError( + domain: "GostXTunnel", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "No YAML config in App Group"] + )) + return + } + + // 3. 启动 gost 服务 + do { + try LibgostStartGost(yaml, "") + } catch { + cancelTunnelWithError(NSError( + domain: "GostXTunnel.startGost", + code: 2, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + + // 4. 配置 TUN 网络设置(系统自动创建 utun 接口) + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1") + settings.mtu = 1500 + + let ipv4 = NEIPv4Settings( + addresses: ["10.0.0.2"], + subnetMasks: ["255.255.255.0"] + ) + ipv4.includedRoutes = [NEIPv4Route.default()] + settings.ipv4Settings = ipv4 + + settings.dnsSettings = NEDNSSettings(servers: ["10.0.0.3"]) + + do { + try await setTunnelNetworkSettings(settings) + } catch { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel.setNetworkSettings", + code: 3, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + + // 5. 扫描找到 utun fd + let tunFd = LibgostGetTunnelFileDescriptor() + guard tunFd != -1 else { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel", + code: 4, + userInfo: [NSLocalizedDescriptionKey: "Cannot locate TUN file descriptor"] + )) + return + } + + // 6. 启动 TUN stack(sing-tun 直接在 fd 上读写) + do { + try LibgostStartTun(Int(tunFd), 1500) + } catch { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel.startTun", + code: 5, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + } + + override func stopTunnel(with reason: NEProviderStopReason) async { + LibgostStopTun() + LibgostStopGost() + } + + // MARK: - Private + + private func setWorkDirAndLog() { + guard let containerURL = AppGroupConfig.containerURL else { return } + let workDir = containerURL.path + LibgostSetWorkDir(workDir) + + let logFile = containerURL.appendingPathComponent("gost.log").path + LibgostSetLogFile(logFile) + LibgostSetLogLevel("info") + } +} +``` + +- [ ] **Step 2: 验证编译** + +Xcode → GostXTunnel scheme → Cmd+B + +Expected: 编译成功(需要提前 link Libgost.xcframework 到该 target) + +- [ ] **Step 3: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostXTunnel/PacketTunnelProvider.swift +git commit -m "feat: implement PacketTunnelProvider with utun TUN stack" +``` + +--- + +### Task 6: Swift — VpnManager(主 App VPN 控制) + +**Files:** +- Create: `macos/GostX/VpnManager.swift` + +- [ ] **Step 1: 创建 `macos/GostX/VpnManager.swift`** + +```swift +// macos/GostX/VpnManager.swift +import Foundation +import NetworkExtension +import Combine + +/// 管理 NETunnelProviderManager 生命周期:加载/保存 VPN 配置、启停连接。 +@MainActor +class VpnManager: ObservableObject { + static let shared = VpnManager() + + @Published var status: NEVPNStatus = .invalid + + private let tunnelBundleId = "cn.liukebin.gostx.tunnel" + private var manager: NETunnelProviderManager? + private var statusObserver: NSObjectProtocol? + + private init() {} + + // MARK: - Setup + + /// 首次调用时加载已存在的 VPN 配置,或创建新的。 + /// 必须在应用启动时调用一次。 + func setup() async { + // 已有 manager 则跳过 + if manager != nil { return } + + let managers = await NETunnelProviderManager.loadAllFromPreferences() + if let existing = managers.first(where: { + ($0.protocolConfiguration as? NETunnelProviderProtocol)? + .providerBundleIdentifier == tunnelBundleId + }) { + manager = existing + status = existing.connection.status + } else { + let m = NETunnelProviderManager() + let proto = NETunnelProviderProtocol() + proto.providerBundleIdentifier = tunnelBundleId + proto.serverAddress = "GostX" + m.protocolConfiguration = proto + m.isEnabled = true + m.localizedDescription = "GostX VPN" + try? await m.saveToPreferences() + manager = m + } + observeStatus() + } + + // MARK: - Control + + func start() async throws { + guard let m = manager else { return } + try m.connection.startVPNTunnel() + } + + func stop() { + manager?.connection.stopVPNTunnel() + } + + // MARK: - Status observation + + private func observeStatus() { + statusObserver = NotificationCenter.default.addObserver( + forName: .NEVPNStatusDidChange, + object: manager?.connection, + queue: .main + ) { [weak self] _ in + guard let self, let m = self.manager else { return } + self.status = m.connection.status + } + } + + deinit { + if let observer = statusObserver { + NotificationCenter.default.removeObserver(observer) + } + } +} +``` + +- [ ] **Step 2: 验证编译** + +Xcode → GostX scheme → Cmd+B + +Expected: 编译成功 + +- [ ] **Step 3: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostX/VpnManager.swift +git commit -m "feat: add VpnManager for macOS VPN lifecycle control" +``` + +--- + +### Task 7: Swift — 主 App VPN 集成 + +**Files:** +- Modify: `macos/GostX/AppDelegate.swift` +- Modify: `macos/GostX/MacExtrasConfigurator.swift` +- Modify: `macos/GostX/SettingsView.swift` +- Modify: `macos/GostX/GostX.entitlements` +- Modify: `macos/GostX/Arguments.swift` + +- [ ] **Step 1: 更新 `macos/GostX/GostX.entitlements` — 添加 App Group** + +在 `` 内追加: + +```xml +com.apple.security.application-groups + + group.cn.liukebin.gostx + +``` + +完整文件变为: + +```xml + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.application-groups + + group.cn.liukebin.gostx + + + +``` + +- [ ] **Step 2: 更新 `macos/GostX/Arguments.swift` — 添加 App Group key 常量** + +在文件末尾追加: + +```swift +// YAML 持久化 key(UserDefaults) +let defaultsYamlKey = "gost_yaml_config" + +// VPN 模式 key +let defaultsVpnModeKey = "gost_vpn_mode_enabled" + +// App Group 容器目录下 YAML 文件名 +let appGroupYamlFileName = "gost.yaml" +``` + +- [ ] **Step 3: 重写 `macos/GostX/AppDelegate.swift`** + +完整替换文件内容。将旧 `import Gost` 替换为 `import Libgost`,支持 VPN 模式切换: + +```swift +// +// AppDelegate.swift +// GostX +// + +import SwiftUI +import Cocoa +import os +import Libgost + +let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "runtime") + +@objc protocol EditMenuActions { + func redo(_ sender: AnyObject) + func undo(_ sender: AnyObject) +} + +class AppDelegate: NSObject, NSApplicationDelegate { + private var menu: MacExtrasConfigurator? + private var vpnMode: Bool { + UserDefaults.standard.bool(forKey: defaultsVpnModeKey) + } + + func mainMenu() { + let mainMenu = NSMenu(title: "MainMenu") + var menuItem = mainMenu.addItem(withTitle: "", action: nil, keyEquivalent: "") + var submenu = NSMenu(title: "Application") + mainMenu.setSubmenu(submenu, for: menuItem) + + menuItem = mainMenu.addItem(withTitle:"Edit", action:nil, keyEquivalent:"") + submenu = NSMenu(title:NSLocalizedString("Edit", comment:"Edit menu")) + submenu.addItem(withTitle: "Undo", action: #selector(EditMenuActions.undo(_:)), keyEquivalent: "z") + submenu.addItem(withTitle: "Redo", action: #selector(EditMenuActions.redo(_:)), keyEquivalent: "Z") + submenu.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x") + submenu.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c") + submenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v") + submenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a") + mainMenu.setSubmenu(submenu, for: menuItem) + + NSApp.mainMenu = mainMenu + } + + func applicationDidFinishLaunching(_ notification: Notification) { + self.mainMenu() + self.menu = MacExtrasConfigurator(delegate: self) + + Task { + await VpnManager.shared.setup() + } + + // Auto-start based on saved state + if UserDefaults.standard.bool(forKey: "last_vpn_running") { + self.start() + } + } + + func applicationWillTerminate(_ notification: Notification) { + self.stop() + } + + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + NSApp.setActivationPolicy(.accessory) + return false + } + + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + func quit() { + NSApp.terminate(self) + } + + func stop() { + if vpnMode { + VpnManager.shared.stop() + } else { + LibgostStopGost() + } + self.menu?.updateListen(nil) + self.menu?.toOffState() + } + + func start() { + if vpnMode { + startVpnMode() + } else { + startProxyMode() + } + } + + // MARK: - VPN Mode + + private func startVpnMode() { + // 同步 YAML 到 App Group + let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML + AppGroupConfig.writeYaml(yaml) + + Task { + do { + try await VpnManager.shared.start() + self.menu?.toOnState() + } catch { + logger.error("VPN start failed: \(error.localizedDescription)") + self.menu?.toOffState() + } + } + } + + // MARK: - Proxy Mode (non-VPN) + + private func startProxyMode() { + let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML + + do { + try LibgostStartGost(yaml, "") + } catch { + logger.error("gost start failed: \(error.localizedDescription)") + self.menu?.toOffState() + return + } + + let statusJSON = LibgostGetStatus() + self.menu?.updateListen(statusJSON) + self.menu?.toOnState() + } +} +``` + +- [ ] **Step 4: 更新 `macos/GostX/MacExtrasConfigurator.swift` — 菜单增加 VPN 模式切换** + +在 `createMenu()` 方法中,`statusListenItem` 之后、分隔线之前插入 VPN 模式切换项: + +```swift +// 在 statusListenItem 添加之后、第一个 separator 之前追加: +let vpnModeItem = NSMenuItem() +vpnModeItem.title = NSLocalizedString("VPN Mode", comment: "") +vpnModeItem.state = UserDefaults.standard.bool(forKey: defaultsVpnModeKey) ? .on : .off +vpnModeItem.target = self +vpnModeItem.action = #selector(onToggleVpnMode(_:)) +menu.addItem(vpnModeItem) +self.vpnModeItem = vpnModeItem +``` + +需要在 `MacExtrasConfigurator` 类中添加属性: + +```swift +private var vpnModeItem: NSMenuItem! +``` + +并添加 action 方法: + +```swift +@objc private func onToggleVpnMode(_ sender: NSMenuItem) { + let newValue = sender.state != .on + sender.state = newValue ? .on : .off + UserDefaults.standard.set(newValue, forKey: defaultsVpnModeKey) +} +``` + +- [ ] **Step 5: 更新 `macos/GostX/SettingsView.swift` — YAML 保存时同步到 App Group** + +在 `YamlConfigView` 的 body 中,给 `yamlConfig` 绑定添加 `.onChange`: + +完整替换 `YamlConfigView`: + +```swift +struct YamlConfigView: View { + @AppStorage(defaultsYamlKey) + private var yamlConfig = defaultGostYAML + + private let rules: [HighlightRule] = [ + HighlightRule( + pattern: yamlCommentRule, + formattingRule: TextFormattingRule(key: .foregroundColor, value: NSColor.systemGray) + ), + HighlightRule( + pattern: yamlKeyRule, + formattingRules: [ + TextFormattingRule(fontTraits: .bold), + TextFormattingRule(key: .foregroundColor, value: NSColor.systemBlue), + ] + ), + ] + + var body: some View { + VStack { + HighlightedTextEditor(text: $yamlConfig, highlightRules: rules) + .introspect { editor in + editor.textView.allowsUndo = true + editor.textView.breakUndoCoalescing() + editor.textView.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) + } + .onChange(of: yamlConfig) { newValue in + // Sync to App Group for VPN Extension + AppGroupConfig.writeYaml(newValue) + } + Text("gost v3 YAML configuration — https://gost.run/docs/") + .padding(.horizontal, 5) + .font(Font.system(size: 12)) + .foregroundColor(.gray) + } + } +} +``` + +- [ ] **Step 6: 验证编译** + +Xcode → GostX scheme → Cmd+B + +Expected: 编译成功,无错误 + +- [ ] **Step 7: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostX/AppDelegate.swift macos/GostX/MacExtrasConfigurator.swift macos/GostX/SettingsView.swift macos/GostX/GostX.entitlements macos/GostX/Arguments.swift +git commit -m "feat: integrate VPN mode into main app with menu toggle and App Group sync" +``` + +--- + +### Task 8: 构建验证 & Xcode 收尾 + +- [ ] **Step 1: 确保 Xcode target 完整配置** + +在 Xcode 中确认以下设置: + +**GostXTunnel target:** +- General → Frameworks and Libraries → `Libgost.xcframework` (Embed & Sign) +- Signing & Capabilities → App Groups → `group.cn.liukebin.gostx` +- Signing & Capabilities → Network Extensions → Packet Tunnel (应自动添加) +- Build Settings → Other Linker Flags → 包含 `-framework Libgost` + +**GostX (主 App) target:** +- Signing & Capabilities → App Groups → `group.cn.liukebin.gostx` + +- [ ] **Step 2: 完整构建** + +Xcode → Product → Build (Cmd+B) 依次选择两个 scheme。 + +Expected: 两个 target 均编译通过 + +- [ ] **Step 3: 运行验证(可选,需要 Developer ID 签名)** + +1. Xcode → GostX scheme → Run (Cmd+R) +2. 在状态栏菜单中启用 **VPN Mode** +3. 点击 Start → 系统弹出 VPN 配置授权 → 允许 +4. 验证 VPN 连接状态 + +Expected: VPN 连接成功,系统流量经 utun 转发 + +- [ ] **Step 4: Commit** + +```bash +cd /Users/kbliu/Workspace/project/GostX +git add macos/GostX.xcodeproj/ +git commit -m "chore: finalize Xcode project settings for macOS VPN build" +``` From 53ccdda4b2c485329c67370da9508294cccecad9 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 13:31:42 +0800 Subject: [PATCH 03/89] feat: add GetTunnelFileDescriptor for macOS utun fd discovery --- libgost/tun_darwin.go | 36 ++++++++++++++++++++++++++++++++++++ libgost/tun_test.go | 16 +++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 libgost/tun_darwin.go diff --git a/libgost/tun_darwin.go b/libgost/tun_darwin.go new file mode 100644 index 0000000..4aea442 --- /dev/null +++ b/libgost/tun_darwin.go @@ -0,0 +1,36 @@ +//go:build darwin + +package libgost + +import ( + "golang.org/x/sys/unix" +) + +// GetTunnelFileDescriptor scans file descriptors 0..1024 for the utun +// control socket (com.apple.net.utun_control) and returns its fd. +// Returns -1 if no utun socket is found. +// +// Implementation adapted from wireguard-apple's WireGuardAdapter.tunnelFileDescriptor. +func GetTunnelFileDescriptor() int32 { + ctlInfo := &unix.CtlInfo{} + copy(ctlInfo.Name[:], "com.apple.net.utun_control") + for fd := int32(0); fd < 1024; fd++ { + addr, err := unix.Getpeername(int(fd)) + if err != nil { + continue + } + addrCTL, ok := addr.(*unix.SockaddrCtl) + if !ok { + continue + } + if ctlInfo.Id == 0 { + if err = unix.IoctlCtlInfo(int(fd), ctlInfo); err != nil { + continue + } + } + if addrCTL.ID == ctlInfo.Id { + return fd + } + } + return -1 +} diff --git a/libgost/tun_test.go b/libgost/tun_test.go index 460f8f3..ea3e1f7 100644 --- a/libgost/tun_test.go +++ b/libgost/tun_test.go @@ -1,6 +1,9 @@ package libgost -import "testing" +import ( + "runtime" + "testing" +) func TestStopTunWhenNotStarted(t *testing.T) { // StopTun on an idle instance should be a no-op @@ -15,3 +18,14 @@ func TestStartTunInvalidFd(t *testing.T) { t.Fatal("StartTun(-1, 1500) should return an error") } } + +func TestGetTunnelFileDescriptor(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("GetTunnelFileDescriptor is darwin-only") + } + // When no utun is active (typical unit test env), returns -1 without error. + fd := GetTunnelFileDescriptor() + if fd != -1 { + t.Logf("found existing utun fd: %d", fd) + } +} From 25b4864ce4714aa5fa8e820c845e50e1babd84d3 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 13:35:48 +0800 Subject: [PATCH 04/89] fix: move TestGetTunnelFileDescriptor to darwin-only test file --- libgost/tun_darwin_test.go | 13 +++++++++++++ libgost/tun_test.go | 15 +-------------- 2 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 libgost/tun_darwin_test.go diff --git a/libgost/tun_darwin_test.go b/libgost/tun_darwin_test.go new file mode 100644 index 0000000..a681e86 --- /dev/null +++ b/libgost/tun_darwin_test.go @@ -0,0 +1,13 @@ +//go:build darwin + +package libgost + +import "testing" + +func TestGetTunnelFileDescriptor(t *testing.T) { + // When no utun is active (typical unit test env), returns -1 without error. + fd := GetTunnelFileDescriptor() + if fd != -1 { + t.Logf("found existing utun fd: %d", fd) + } +} diff --git a/libgost/tun_test.go b/libgost/tun_test.go index ea3e1f7..e4641bc 100644 --- a/libgost/tun_test.go +++ b/libgost/tun_test.go @@ -1,9 +1,6 @@ package libgost -import ( - "runtime" - "testing" -) +import "testing" func TestStopTunWhenNotStarted(t *testing.T) { // StopTun on an idle instance should be a no-op @@ -19,13 +16,3 @@ func TestStartTunInvalidFd(t *testing.T) { } } -func TestGetTunnelFileDescriptor(t *testing.T) { - if runtime.GOOS != "darwin" { - t.Skip("GetTunnelFileDescriptor is darwin-only") - } - // When no utun is active (typical unit test env), returns -1 without error. - fd := GetTunnelFileDescriptor() - if fd != -1 { - t.Logf("found existing utun fd: %d", fd) - } -} From 9c6b52218bacff17c384e6a2429a1b9c283e59b7 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 13:38:38 +0800 Subject: [PATCH 05/89] build: add macos-framework target for gomobile bind -target macos --- libgost/Makefile | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libgost/Makefile b/libgost/Makefile index eeef125..56cf3a7 100644 --- a/libgost/Makefile +++ b/libgost/Makefile @@ -1,7 +1,7 @@ GO := go GOMOBILE := gomobile -.PHONY: all clean debug-symbols +.PHONY: all clean debug-symbols macos-framework all: libgost.aar @@ -47,6 +47,21 @@ debug-symbols: @rm -f libgost_debug.aar @echo "Debug symbols written to debug-symbols.zip" +# macOS .xcframework via gomobile bind +# Produces Frameworks/Libgost.xcframework with macos-arm64_x86_64 slices. +# Xcode links this into the PacketTunnelProvider Extension target. +macos-framework: + CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o Libgost.xcframework \ + . + mkdir -p ../macos/Frameworks + rm -rf ../macos/Frameworks/Libgost.xcframework + mv Libgost.xcframework ../macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" + clean: rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip rm -rf _debug_tmp + rm -rf ../macos/Frameworks/Libgost.xcframework From 52813512515e3af9ec283f4aafadd2fbec396a21 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 13:50:26 +0800 Subject: [PATCH 06/89] feat: add GostXTunnel PacketTunnelProvider Extension target scaffold --- macos/GostXTunnel/GostXTunnel.entitlements | 20 +++++++++++++++++ macos/GostXTunnel/Info.plist | 25 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 macos/GostXTunnel/GostXTunnel.entitlements create mode 100644 macos/GostXTunnel/Info.plist diff --git a/macos/GostXTunnel/GostXTunnel.entitlements b/macos/GostXTunnel/GostXTunnel.entitlements new file mode 100644 index 0000000..9d122bc --- /dev/null +++ b/macos/GostXTunnel/GostXTunnel.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.cn.liukebin.gostx + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + + diff --git a/macos/GostXTunnel/Info.plist b/macos/GostXTunnel/Info.plist new file mode 100644 index 0000000..883d266 --- /dev/null +++ b/macos/GostXTunnel/Info.plist @@ -0,0 +1,25 @@ + + + + + CFBundleDisplayName + GostX Tunnel + CFBundleName + $(PRODUCT_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + From 53266277bd2c88a22a48e72c2945f815a75d726b Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 15:23:56 +0800 Subject: [PATCH 07/89] feat: add AppGroupConfig helper for YAML sharing via App Group --- macos/GostX/AppGroupConfig.swift | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 macos/GostX/AppGroupConfig.swift diff --git a/macos/GostX/AppGroupConfig.swift b/macos/GostX/AppGroupConfig.swift new file mode 100644 index 0000000..ede749b --- /dev/null +++ b/macos/GostX/AppGroupConfig.swift @@ -0,0 +1,32 @@ +// macos/GostX/AppGroupConfig.swift +import Foundation + +/// 通过 App Group 容器在主 App 和 Extension 之间共享 YAML 配置。 +struct AppGroupConfig { + static let groupId = "group.cn.liukebin.gostx" + static let yamlFileName = "gost.yaml" + + static var containerURL: URL? { + FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: groupId + ) + } + + static var yamlFileURL: URL? { + containerURL?.appendingPathComponent(yamlFileName) + } + + /// 从 App Group 容器读取 YAML 配置字符串。 + /// 返回 nil 表示容器不可用或文件不存在。 + static func readYaml() -> String? { + guard let url = yamlFileURL else { return nil } + return try? String(contentsOf: url, encoding: .utf8) + } + + /// 将 YAML 配置写入 App Group 容器。 + /// 失败静默忽略(Extension 启动时会检查并报错)。 + static func writeYaml(_ yaml: String) { + guard let url = yamlFileURL else { return } + try? yaml.write(to: url, atomically: true, encoding: .utf8) + } +} From 673a54b55bc6c343f4acc5a1d952a56031ede383 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 15:25:04 +0800 Subject: [PATCH 08/89] feat: implement PacketTunnelProvider with utun TUN stack --- macos/GostXTunnel/PacketTunnelProvider.swift | 100 +++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 macos/GostXTunnel/PacketTunnelProvider.swift diff --git a/macos/GostXTunnel/PacketTunnelProvider.swift b/macos/GostXTunnel/PacketTunnelProvider.swift new file mode 100644 index 0000000..6fe3828 --- /dev/null +++ b/macos/GostXTunnel/PacketTunnelProvider.swift @@ -0,0 +1,100 @@ +// macos/GostXTunnel/PacketTunnelProvider.swift +import NetworkExtension +import Libgost + +class PacketTunnelProvider: NEPacketTunnelProvider { + + override func startTunnel(options: [String: NSObject]?) async throws { + // 1. 设置工作目录和日志 + setWorkDirAndLog() + + // 2. 从 App Group 读取 YAML 配置 + guard let yaml = AppGroupConfig.readYaml(), !yaml.isEmpty else { + cancelTunnelWithError(NSError( + domain: "GostXTunnel", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "No YAML config in App Group"] + )) + return + } + + // 3. 启动 gost 服务 + do { + try LibgostStartGost(yaml, "") + } catch { + cancelTunnelWithError(NSError( + domain: "GostXTunnel.startGost", + code: 2, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + + // 4. 配置 TUN 网络设置(系统自动创建 utun 接口) + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1") + settings.mtu = 1500 + + let ipv4 = NEIPv4Settings( + addresses: ["10.0.0.2"], + subnetMasks: ["255.255.255.0"] + ) + ipv4.includedRoutes = [NEIPv4Route.default()] + settings.ipv4Settings = ipv4 + + settings.dnsSettings = NEDNSSettings(servers: ["10.0.0.3"]) + + do { + try await setTunnelNetworkSettings(settings) + } catch { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel.setNetworkSettings", + code: 3, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + + // 5. 扫描找到 utun fd + let tunFd = LibgostGetTunnelFileDescriptor() + guard tunFd != -1 else { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel", + code: 4, + userInfo: [NSLocalizedDescriptionKey: "Cannot locate TUN file descriptor"] + )) + return + } + + // 6. 启动 TUN stack(sing-tun 直接在 fd 上读写) + do { + try LibgostStartTun(Int(tunFd), 1500) + } catch { + LibgostStopGost() + cancelTunnelWithError(NSError( + domain: "GostXTunnel.startTun", + code: 5, + userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + )) + return + } + } + + override func stopTunnel(with reason: NEProviderStopReason) async { + LibgostStopTun() + LibgostStopGost() + } + + // MARK: - Private + + private func setWorkDirAndLog() { + guard let containerURL = AppGroupConfig.containerURL else { return } + let workDir = containerURL.path + LibgostSetWorkDir(workDir) + + let logFile = containerURL.appendingPathComponent("gost.log").path + LibgostSetLogFile(logFile) + LibgostSetLogLevel("info") + } +} From bf24145b70aa73a165c86a05aa467940d3a9aa77 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 15:29:47 +0800 Subject: [PATCH 09/89] fix: add try? to stopTunnel calls (gomobile throws) --- macos/GostXTunnel/PacketTunnelProvider.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/macos/GostXTunnel/PacketTunnelProvider.swift b/macos/GostXTunnel/PacketTunnelProvider.swift index 6fe3828..fb57126 100644 --- a/macos/GostXTunnel/PacketTunnelProvider.swift +++ b/macos/GostXTunnel/PacketTunnelProvider.swift @@ -82,8 +82,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } override func stopTunnel(with reason: NEProviderStopReason) async { - LibgostStopTun() - LibgostStopGost() + try? LibgostStopTun() + try? LibgostStopGost() } // MARK: - Private From 4a83148136eb696839e84eb311d05c305c1b4ac5 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 15:30:15 +0800 Subject: [PATCH 10/89] feat: add VpnManager for macOS VPN lifecycle control --- macos/GostX/VpnManager.swift | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 macos/GostX/VpnManager.swift diff --git a/macos/GostX/VpnManager.swift b/macos/GostX/VpnManager.swift new file mode 100644 index 0000000..8ba55fd --- /dev/null +++ b/macos/GostX/VpnManager.swift @@ -0,0 +1,76 @@ +// macos/GostX/VpnManager.swift +import Foundation +import NetworkExtension + +/// 管理 NETunnelProviderManager 生命周期:加载/保存 VPN 配置、启停连接。 +@MainActor +class VpnManager: ObservableObject { + static let shared = VpnManager() + + @Published var status: NEVPNStatus = .invalid + + private let tunnelBundleId = "cn.liukebin.gostx.tunnel" + private var manager: NETunnelProviderManager? + private var statusObserver: NSObjectProtocol? + + private init() {} + + // MARK: - Setup + + /// 首次调用时加载已存在的 VPN 配置,或创建新的。 + /// 必须在应用启动时调用一次。 + func setup() async { + // 已有 manager 则跳过 + if manager != nil { return } + + let managers = await NETunnelProviderManager.loadAllFromPreferences() + if let existing = managers.first(where: { + ($0.protocolConfiguration as? NETunnelProviderProtocol)? + .providerBundleIdentifier == tunnelBundleId + }) { + manager = existing + status = existing.connection.status + } else { + let m = NETunnelProviderManager() + let proto = NETunnelProviderProtocol() + proto.providerBundleIdentifier = tunnelBundleId + proto.serverAddress = "GostX" + m.protocolConfiguration = proto + m.isEnabled = true + m.localizedDescription = "GostX VPN" + try? await m.saveToPreferences() + manager = m + } + observeStatus() + } + + // MARK: - Control + + func start() async throws { + guard let m = manager else { return } + try m.connection.startVPNTunnel() + } + + func stop() { + manager?.connection.stopVPNTunnel() + } + + // MARK: - Status observation + + private func observeStatus() { + statusObserver = NotificationCenter.default.addObserver( + forName: .NEVPNStatusDidChange, + object: manager?.connection, + queue: .main + ) { [weak self] _ in + guard let self, let m = self.manager else { return } + self.status = m.connection.status + } + } + + deinit { + if let observer = statusObserver { + NotificationCenter.default.removeObserver(observer) + } + } +} From 8397776c076b7b3ba7dde993379c42b12868c200 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 15:32:27 +0800 Subject: [PATCH 11/89] feat: integrate VPN mode into main app with menu toggle and App Group sync --- macos/GostX/AppDelegate.swift | 113 +++++++++++++----------- macos/GostX/Arguments.swift | 9 ++ macos/GostX/GostX.entitlements | 4 + macos/GostX/MacExtrasConfigurator.swift | 20 ++++- macos/GostX/SettingsView.swift | 6 +- 5 files changed, 96 insertions(+), 56 deletions(-) diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index 5672f8c..3e5b295 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -8,7 +8,7 @@ import SwiftUI import Cocoa import os -import Gost +import Libgost let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "runtime") @@ -19,14 +19,16 @@ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "runtime class AppDelegate: NSObject, NSApplicationDelegate { private var menu: MacExtrasConfigurator? - private var logPipe: Pipe? - + private var vpnMode: Bool { + UserDefaults.standard.bool(forKey: defaultsVpnModeKey) + } + func mainMenu() { let mainMenu = NSMenu(title: "MainMenu") var menuItem = mainMenu.addItem(withTitle: "", action: nil, keyEquivalent: "") var submenu = NSMenu(title: "Application") mainMenu.setSubmenu(submenu, for: menuItem) - + menuItem = mainMenu.addItem(withTitle:"Edit", action:nil, keyEquivalent:"") submenu = NSMenu(title:NSLocalizedString("Edit", comment:"Edit menu")) submenu.addItem(withTitle: "Undo", action: #selector(EditMenuActions.undo(_:)), keyEquivalent: "z") @@ -36,87 +38,92 @@ class AppDelegate: NSObject, NSApplicationDelegate { submenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v") submenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a") mainMenu.setSubmenu(submenu, for: menuItem) - + NSApp.mainMenu = mainMenu } - + func applicationDidFinishLaunching(_ notification: Notification) { self.mainMenu() self.menu = MacExtrasConfigurator(delegate: self) - self.logPipe = pipe() - self.start() + + Task { + await VpnManager.shared.setup() + } + + // Auto-start based on saved state + if UserDefaults.standard.bool(forKey: "last_vpn_running") { + self.start() + } } - + func applicationWillTerminate(_ notification: Notification) { self.stop() } - + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { NSApp.setActivationPolicy(.accessory) - return false } - + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } - + func quit() { NSApp.terminate(self) } - + func stop() { - gostStop() + if vpnMode { + VpnManager.shared.stop() + } else { + LibgostStopGost() + } self.menu?.updateListen(nil) self.menu?.toOffState() } - - func start() { - let yaml = UserDefaults.standard.string(forKey: defaultsArgumentsKey) ?? defaultGostYAML - var fd = self.logPipe?.fileHandleForWriting.fileDescriptor - let fdPtr = UnsafeMutablePointer.allocate(capacity: 1) - withUnsafeMutablePointer(to: &fd) { ptr in - fdPtr.initialize(to: CLong(ptr.pointee!)) + func start() { + if vpnMode { + startVpnMode() + } else { + startProxyMode() } + } - let isFailed = gostRunYaml( - UnsafeMutablePointer(mutating: (yaml as NSString).utf8String), - UnsafeMutablePointer(mutating: fdPtr) - ) + // MARK: - VPN Mode - if isFailed != 0 { - self.menu?.toOffState() - stop() - return - } + private func startVpnMode() { + // 同步 YAML 到 App Group + let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML + AppGroupConfig.writeYaml(yaml) - if let infoPtr = gostInfo() { - let statusJSON = String(cString: infoPtr.pointee.status_json) - self.menu?.updateListen(statusJSON) - infoPtr.deallocate() + Task { + do { + try await VpnManager.shared.start() + self.menu?.toOnState() + } catch { + logger.error("VPN start failed: \(error.localizedDescription)") + self.menu?.toOffState() + } } - self.menu?.toOnState() } - - private func pipe() -> Pipe { - let p = Pipe() - p.fileHandleForReading.readabilityHandler = { handle in - let data = handle.availableData - if data.count == 0 { - // No data available means EOF; we must unregister ourselves - // in order to not immediately be called again. - handle.readabilityHandler = nil - return - } - guard let str = String(data: data, encoding: .utf8) else { - // Non-UTF-8 data from Syncthing should never happen. - return - } + // MARK: - Proxy Mode (non-VPN) - logger.log("\(str, privacy: .public)") + private func startProxyMode() { + let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML + + do { + try LibgostStartGost(yaml, "") + } catch { + logger.error("gost start failed: \(error.localizedDescription)") + self.menu?.toOffState() + return } - return p + + let statusJSON = LibgostGetStatus() + self.menu?.updateListen(statusJSON) + self.menu?.toOnState() } } diff --git a/macos/GostX/Arguments.swift b/macos/GostX/Arguments.swift index f9e34d7..28ba405 100644 --- a/macos/GostX/Arguments.swift +++ b/macos/GostX/Arguments.swift @@ -13,3 +13,12 @@ services: listener: type: tcp """ + +// YAML 持久化 key(UserDefaults) +let defaultsYamlKey = "gost_yaml_config" + +// VPN 模式 key +let defaultsVpnModeKey = "gost_vpn_mode_enabled" + +// App Group 容器目录下 YAML 文件名 +let appGroupYamlFileName = "gost.yaml" diff --git a/macos/GostX/GostX.entitlements b/macos/GostX/GostX.entitlements index 7a2230d..b1f6463 100644 --- a/macos/GostX/GostX.entitlements +++ b/macos/GostX/GostX.entitlements @@ -8,5 +8,9 @@ com.apple.security.network.server + com.apple.security.application-groups + + group.cn.liukebin.gostx + diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index e97b44e..75fcef2 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -18,6 +18,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { public var statusActionRestartItem: NSMenuItem public var statusListenItem: NSMenuItem public var argsItem: NSMenuItem = NSMenuItem() + private var vpnModeItem: NSMenuItem! private var menu: NSMenu = NSMenu() private var activeImg: NSImage? = NSImage(named: "StatusBarActive") @@ -49,7 +50,16 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { // Listening statusListenItem.isEnabled = false menu.addItem(statusListenItem) - + + // VPN Mode toggle + let vpnItem = NSMenuItem() + vpnItem.title = NSLocalizedString("VPN Mode", comment: "") + vpnItem.state = UserDefaults.standard.bool(forKey: defaultsVpnModeKey) ? .on : .off + vpnItem.target = self + vpnItem.action = #selector(onToggleVpnMode(_:)) + menu.addItem(vpnItem) + self.vpnModeItem = vpnItem + menu.addItem(.separator()) // Configuration @@ -108,6 +118,12 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { // MARK: - Actions + @objc private func onToggleVpnMode(_ sender: NSMenuItem) { + let newValue = sender.state != .on + sender.state = newValue ? .on : .off + UserDefaults.standard.set(newValue, forKey: defaultsVpnModeKey) + } + @objc private func onArgumentClick(_ sender: Any?) { onConfigClick(sender) } @@ -171,7 +187,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { private func updateArgsMenuItem() { argsItem.title = NSLocalizedString("Configuration", comment: "") argsItem.image = NSImage(systemSymbolName: "doc.text", accessibilityDescription: "") - argsItem.toolTip = UserDefaults.standard.string(forKey: defaultsArgumentsKey) ?? defaultGostYAML + argsItem.toolTip = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML argsItem.submenu = nil } } diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 971e893..be1fcb0 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -22,7 +22,7 @@ struct SettingsView: View { } struct YamlConfigView: View { - @AppStorage(defaultsArgumentsKey) + @AppStorage(defaultsYamlKey) private var yamlConfig = defaultGostYAML private let rules: [HighlightRule] = [ @@ -47,6 +47,10 @@ struct YamlConfigView: View { editor.textView.breakUndoCoalescing() editor.textView.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) } + .onChange(of: yamlConfig) { newValue in + // Sync to App Group for VPN Extension + AppGroupConfig.writeYaml(newValue) + } Text("gost v3 YAML configuration — https://gost.run/docs/") .padding(.horizontal, 5) .font(Font.system(size: 12)) From b9bbe96736afee818ac0df31a087735db9d4e868 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 16:25:54 +0800 Subject: [PATCH 12/89] fix: use gomobile C-function calling convention (NSError** not bridged to throws) --- macos/GostX/AppDelegate.swift | 9 +++---- macos/GostXTunnel/PacketTunnelProvider.swift | 27 +++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index 3e5b295..2ad5dad 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -77,7 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { if vpnMode { VpnManager.shared.stop() } else { - LibgostStopGost() + LibgostStopGost(nil) } self.menu?.updateListen(nil) self.menu?.toOffState() @@ -114,10 +114,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { private func startProxyMode() { let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML - do { - try LibgostStartGost(yaml, "") - } catch { - logger.error("gost start failed: \(error.localizedDescription)") + var err: NSError? + if !LibgostStartGost(yaml, "", &err), let err { + logger.error("gost start failed: \(err.localizedDescription)") self.menu?.toOffState() return } diff --git a/macos/GostXTunnel/PacketTunnelProvider.swift b/macos/GostXTunnel/PacketTunnelProvider.swift index fb57126..5df6a1f 100644 --- a/macos/GostXTunnel/PacketTunnelProvider.swift +++ b/macos/GostXTunnel/PacketTunnelProvider.swift @@ -19,13 +19,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } // 3. 启动 gost 服务 - do { - try LibgostStartGost(yaml, "") - } catch { + var err: NSError? + if !LibgostStartGost(yaml, "", &err), let err { cancelTunnelWithError(NSError( domain: "GostXTunnel.startGost", code: 2, - userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + userInfo: [NSLocalizedDescriptionKey: err.localizedDescription] )) return } @@ -46,7 +45,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { do { try await setTunnelNetworkSettings(settings) } catch { - LibgostStopGost() + LibgostStopGost(nil) cancelTunnelWithError(NSError( domain: "GostXTunnel.setNetworkSettings", code: 3, @@ -58,7 +57,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // 5. 扫描找到 utun fd let tunFd = LibgostGetTunnelFileDescriptor() guard tunFd != -1 else { - LibgostStopGost() + LibgostStopGost(nil) cancelTunnelWithError(NSError( domain: "GostXTunnel", code: 4, @@ -68,22 +67,20 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } // 6. 启动 TUN stack(sing-tun 直接在 fd 上读写) - do { - try LibgostStartTun(Int(tunFd), 1500) - } catch { - LibgostStopGost() + if !LibgostStartTun(Int(tunFd), 1500, &err), let err { + LibgostStopGost(nil) cancelTunnelWithError(NSError( domain: "GostXTunnel.startTun", code: 5, - userInfo: [NSLocalizedDescriptionKey: error.localizedDescription] + userInfo: [NSLocalizedDescriptionKey: err.localizedDescription] )) return } } override func stopTunnel(with reason: NEProviderStopReason) async { - try? LibgostStopTun() - try? LibgostStopGost() + LibgostStopTun(nil) + LibgostStopGost(nil) } // MARK: - Private @@ -91,10 +88,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private func setWorkDirAndLog() { guard let containerURL = AppGroupConfig.containerURL else { return } let workDir = containerURL.path - LibgostSetWorkDir(workDir) + LibgostSetWorkDir(workDir, nil) let logFile = containerURL.appendingPathComponent("gost.log").path - LibgostSetLogFile(logFile) + LibgostSetLogFile(logFile, nil) LibgostSetLogLevel("info") } } From c48229556d32ae9db7caa3837a43bc3b31b14a5f Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 16:33:00 +0800 Subject: [PATCH 13/89] =?UTF-8?q?fix:=20Xcode=20project=20integration=20?= =?UTF-8?q?=E2=80=94=20add=20files=20to=20targets,=20fix=20API=20signature?= =?UTF-8?q?s,=20remove=20stale=20gost=20linker=20flags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + macos/GostX.xcodeproj/project.pbxproj | 224 +++++++++++++++++- .../xcshareddata/xcschemes/GostX.xcscheme | 2 +- macos/GostX/AppDelegate.swift | 2 +- macos/GostX/VpnManager.swift | 2 +- 5 files changed, 216 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 917857e..1b46bfb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ libgost/build/ # 个人配置(含敏感信息,不提交) android/config-templates/*.yaml android/app/libs/libgost*.aar +.pi-subagents/ +macos/Frameworks/ diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index ca14373..341282d 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -3,10 +3,18 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ + 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; + 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; + 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; + 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; + 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; + 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; + EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; B523FCA529E169180050578C /* HighlightedTextEditor in Frameworks */ = {isa = PBXBuildFile; productRef = B523FCA429E169180050578C /* HighlightedTextEditor */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; B532A66E285B528D00159BAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A66D285B528D00159BAC /* Assets.xcassets */; }; @@ -22,6 +30,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 4303494A3004D3A000FD9EE9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B532A65E285B528900159BAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 430349413004D3A000FD9EE9; + remoteInfo = GostXTunnel; + }; B532A678285B528D00159BAC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = B532A65E285B528900159BAC /* Project object */; @@ -38,7 +53,27 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 4303494D3004D3A000FD9EE9 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 430349433004D3A000FD9EE9 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; + F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnManager.swift; sourceTree = ""; }; + 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; + 4303495C3004D78100FD9EE9 /* Libgost.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libgost.xcframework; path = Frameworks/Libgost.xcframework; sourceTree = ""; }; B532A666285B528900159BAC /* GostX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GostX.app; sourceTree = BUILT_PRODUCTS_DIR; }; B532A669285B528900159BAC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; B532A66D285B528D00159BAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -57,12 +92,36 @@ B5F6387829DEFF31009A1287 /* Arguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arguments.swift; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 430349413004D3A000FD9EE9 /* GostXTunnel */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ + 4303493F3004D3A000FD9EE9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */, + 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B532A663285B528900159BAC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( B523FCA529E169180050578C /* HighlightedTextEditor in Frameworks */, + EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,9 +145,11 @@ B532A65D285B528900159BAC = { isa = PBXGroup; children = ( + 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */, B532A668285B528900159BAC /* GostX */, B532A67A285B528D00159BAC /* GostXTests */, B532A684285B528D00159BAC /* GostXUITests */, + 430349453004D3A000FD9EE9 /* GostXTunnel */, B532A667285B528900159BAC /* Products */, B5AF722B28634A6500BB5F98 /* Frameworks */, ); @@ -100,6 +161,7 @@ B532A666285B528900159BAC /* GostX.app */, B532A677285B528D00159BAC /* GostXTests.xctest */, B532A681285B528D00159BAC /* GostXUITests.xctest */, + 430349423004D3A000FD9EE9 /* GostXTunnel.appex */, ); name = Products; sourceTree = ""; @@ -113,6 +175,7 @@ B532A672285B528D00159BAC /* GostX.entitlements */, B532A66F285B528D00159BAC /* Preview Content */, B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */, + F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */, B5B0E079285C63080035A6F2 /* SettingsView.swift */, B5AEDDB02864920800C09ED6 /* AppDelegate.swift */, B5F6387829DEFF31009A1287 /* Arguments.swift */, @@ -148,6 +211,8 @@ B5AF722B28634A6500BB5F98 /* Frameworks */ = { isa = PBXGroup; children = ( + 4303495C3004D78100FD9EE9 /* Libgost.xcframework */, + 430349433004D3A000FD9EE9 /* NetworkExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -155,6 +220,28 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 430349413004D3A000FD9EE9 /* GostXTunnel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 430349513004D3A000FD9EE9 /* Build configuration list for PBXNativeTarget "GostXTunnel" */; + buildPhases = ( + 4303493E3004D3A000FD9EE9 /* Sources */, + 4303493F3004D3A000FD9EE9 /* Frameworks */, + 430349403004D3A000FD9EE9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 430349453004D3A000FD9EE9 /* GostXTunnel */, + ); + name = GostXTunnel; + packageProductDependencies = ( + ); + productName = GostXTunnel; + productReference = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; + productType = "com.apple.product-type.app-extension"; + }; B532A665285B528900159BAC /* GostX */ = { isa = PBXNativeTarget; buildConfigurationList = B532A68B285B528D00159BAC /* Build configuration list for PBXNativeTarget "GostX" */; @@ -162,10 +249,12 @@ B532A662285B528900159BAC /* Sources */, B532A663285B528900159BAC /* Frameworks */, B532A664285B528900159BAC /* Resources */, + 4303494D3004D3A000FD9EE9 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 4303494B3004D3A000FD9EE9 /* PBXTargetDependency */, ); name = GostX; packageProductDependencies = ( @@ -218,9 +307,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1340; - LastUpgradeCheck = 1430; + LastSwiftUpdateCheck = 2650; + LastUpgradeCheck = 2650; TargetAttributes = { + 430349413004D3A000FD9EE9 = { + CreatedOnToolsVersion = 26.5; + }; B532A665285B528900159BAC = { CreatedOnToolsVersion = 13.4.1; }; @@ -254,11 +346,19 @@ B532A665285B528900159BAC /* GostX */, B532A676285B528D00159BAC /* GostXTests */, B532A680285B528D00159BAC /* GostXUITests */, + 430349413004D3A000FD9EE9 /* GostXTunnel */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 430349403004D3A000FD9EE9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B532A664285B528900159BAC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -286,14 +386,25 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 4303493E3004D3A000FD9EE9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */, + 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B532A662285B528900159BAC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */, B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */, B5AEDDB12864920800C09ED6 /* AppDelegate.swift in Sources */, B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */, B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */, + 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */, B532A66A285B528900159BAC /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -318,6 +429,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 4303494B3004D3A000FD9EE9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 430349413004D3A000FD9EE9 /* GostXTunnel */; + targetProxy = 4303494A3004D3A000FD9EE9 /* PBXContainerItemProxy */; + }; B532A679285B528D00159BAC /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = B532A665285B528900159BAC /* GostX */; @@ -343,6 +459,83 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 4303494E3004D3A000FD9EE9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = GostXTunnel/GostXTunnel.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = GostXTunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "GostX Tunnel"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 26.5; + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = "-lresolv"; + PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx.tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 4303494F3004D3A000FD9EE9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = GostXTunnel/GostXTunnel.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = GostXTunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "GostX Tunnel"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 26.5; + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = "-lresolv"; + PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx.tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; B532A689285B528D00159BAC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -400,6 +593,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -455,6 +649,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -466,7 +661,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = GostX/GostX.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -486,15 +681,14 @@ "$(inherited)", "@executable_path/../Frameworks", ); - LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../go/"; MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.0; OTHER_LDFLAGS = ( - "-lgost", "-lresolv", ); PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx; PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/../go/"; SWIFT_VERSION = 5.0; @@ -507,7 +701,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = GostX/GostX.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -528,15 +722,14 @@ "$(inherited)", "@executable_path/../Frameworks", ); - LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../go/"; MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.0; OTHER_LDFLAGS = ( - "-lgost", "-lresolv", ); PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx; PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/../go/"; SWIFT_VERSION = 5.0; @@ -546,7 +739,6 @@ B532A68F285B528D00159BAC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; @@ -567,7 +759,6 @@ B532A690285B528D00159BAC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -587,7 +778,6 @@ B532A692285B528D00159BAC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; @@ -605,7 +795,6 @@ B532A693285B528D00159BAC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; @@ -623,6 +812,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 430349513004D3A000FD9EE9 /* Build configuration list for PBXNativeTarget "GostXTunnel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4303494E3004D3A000FD9EE9 /* Debug */, + 4303494F3004D3A000FD9EE9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; B532A661285B528900159BAC /* Build configuration list for PBXProject "GostX" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/macos/GostX.xcodeproj/xcshareddata/xcschemes/GostX.xcscheme b/macos/GostX.xcodeproj/xcshareddata/xcschemes/GostX.xcscheme index 862b3b6..9f272db 100644 --- a/macos/GostX.xcodeproj/xcshareddata/xcschemes/GostX.xcscheme +++ b/macos/GostX.xcodeproj/xcshareddata/xcschemes/GostX.xcscheme @@ -1,6 +1,6 @@ Date: Mon, 13 Jul 2026 23:48:53 +0800 Subject: [PATCH 14/89] docs: add macOS file manager design spec Co-Authored-By: Claude --- .../2026-07-13-macos-file-manager-design.md | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-13-macos-file-manager-design.md diff --git a/docs/superpowers/specs/2026-07-13-macos-file-manager-design.md b/docs/superpowers/specs/2026-07-13-macos-file-manager-design.md new file mode 100644 index 0000000..870df24 --- /dev/null +++ b/docs/superpowers/specs/2026-07-13-macos-file-manager-design.md @@ -0,0 +1,222 @@ +# macOS File Manager for Bypass Files — Design Spec + +**Date:** 2026-07-13 +**Branch:** `macos` +**Reference:** Android `FileManageScreen` / `FileManageViewModel` / `FileRepository` + +## Purpose + +The macOS app runs in a sandbox. Users need a way to import, manage, and export +files used by gost's bypass feature (e.g., `bypass.file.path: china_ip_list.txt`). +These files must live in the App Group container so the Network Extension can +read them at runtime. + +## Storage + +``` +group.cn.liukebin.gostx/ +├── gost.yaml # YAML config (existing) +├── gost.log # log file (existing) +└── files/ # NEW: bypass file directory + ├── china_ip_list.txt + └── private_ip.txt +``` + +- `FileRepository` operates on `{container}/files/` +- `PacketTunnelProvider.setWorkDirAndLog()` sets `LibgostSetWorkDir` to + `{container}/files/`, so gost configs reference bypass files by bare name + (e.g. `bypass.file.path: china_ip_list.txt`) +- The `files/` directory is created on first use (both by `FileRepository` + on import and by `PacketTunnelProvider` on tunnel start) + +## UI Layout + +The existing Settings window (`NavigationSplitView` with profile sidebar + +YAML editor detail) is wrapped in a **TabView** with two tabs: + +``` +┌─────────────────────────────────────────────┐ +│ [Profiles] [Files] │ +├─────────────────────────────────────────────┤ +│ │ +│ 当前 Tab 的内容 │ +│ │ +└─────────────────────────────────────────────┘ +``` + +- **Profiles Tab** — existing `NavigationSplitView` (unchanged) +- **Files Tab** — new `FileManageView` + +### Files Tab Layout + +A table/`List` view showing imported files: + +| Column | Description | +|---|---| +| Name | File name | +| Size | Formatted size (B / KB / MB) | +| Modified | Last modified date (e.g. `yyyy-MM-dd HH:mm`) | + +- **Empty state:** Centered placeholder text "No files. Click + to import." +- **Toolbar / bottom bar:** `+` button → `NSOpenPanel` for importing files +- **Per-file actions** (context menu / right-click or inline buttons): + - Export → `NSSavePanel` + - Rename → Sheet dialog with text field + - Copy Path → copies full filesystem path to clipboard + - Delete → confirmation alert, then delete + +### Overwrite Handling + +When importing a file whose name already exists in the container, a sheet +dialog asks the user to confirm overwrite or cancel. + +## Components + +### 1. `FileRepository` (new: `macos/GostX/FileRepository.swift`) + +Pure data layer — operates on the `files/` subdirectory of the App Group container. + +```swift +struct FileInfo: Identifiable { + var id: String { name } + let name: String + let sizeBytes: Int64 + let lastModified: Date +} + +class FileRepository { + let workDir: URL // {container}/files/ + + init?() // returns nil if container URL is unavailable + + func listFiles() -> [FileInfo] // sorted by name, excludes dirs & dotfiles + func exists(_ name: String) -> Bool + func importFile(from sourceURL: URL) throws -> FileInfo // copy to workDir + func exportFile(_ name: String, to destURL: URL) throws + func renameFile(_ oldName: String, to newName: String) throws + func deleteFile(_ name: String) throws + func filePath(_ name: String) -> String // full filesystem path + + private func validateName(_ name: String) throws // no "..", "/", empty +} +``` + +### 2. `FileManageViewModel` (new: `macos/GostX/FileManageView.swift`) + +`ObservableObject` bridging `FileRepository` to the SwiftUI view. + +- `@Published files: [FileInfo]` +- `@Published pendingOverwrite: (sourceURL: URL, fileName: String)?` +- `func refresh()` +- `func importFile(from:)` — detects overwrite, delegates to `confirmOverwrite()` or `doImport()` +- `func confirmOverwrite()` / `func cancelOverwrite()` +- `func renameFile(_:to:)` — validates, calls repo, shows error alert on failure +- `func deleteFile(_:)` — calls repo, refreshes +- `func exportFile(_:to:)` — calls repo +- `func copyPath(_:)` — copies `repo.filePath(name)` to `NSPasteboard` + +Error messages are surfaced via `@Published var alertMessage: String?` or +similar alert-pattern state. + +### 3. `FileManageView` (new: `macos/GostX/FileManageView.swift`) + +SwiftUI view: + +- `List` with `FileInfo` items +- `NSOpenPanel` for import, `NSSavePanel` for export +- Sheet for rename, alert for delete confirmation +- Sheets for import-overwrite confirmation +- Empty-state placeholder + +### 4. `SettingsView` (modified: `macos/GostX/SettingsView.swift`) + +Wraps existing content in a `TabView`: + +```swift +TabView(selection: $selectedTab) { + ProfilesTabView() + .tabItem { Label("Profiles", systemImage: "doc.text") } + .tag(0) + FileManageView() + .tabItem { Label("Files", systemImage: "folder") } + .tag(1) +} +``` + +The existing `SettingsView` body becomes `ProfilesTabView`. + +### 5. `PacketTunnelProvider` (modified: `macos/GostXTunnel/PacketTunnelProvider.swift`) + +`setWorkDirAndLog()` updated to create and use the `files/` subdirectory: + +```swift +private func setWorkDirAndLog() { + guard let containerURL = AppGroupConfig.containerURL else { return } + let filesDir = containerURL.appendingPathComponent("files") + try? FileManager.default.createDirectory(at: filesDir, + withIntermediateDirectories: true, attributes: nil) + LibgostSetWorkDir(filesDir.path, nil) + let logFile = containerURL.appendingPathComponent("gost.log").path + LibgostSetLogFile(logFile, nil) + LibgostSetLogLevel("info") +} +``` + +## Error Handling + +| Scenario | Behavior | +|---|---| +| Container URL unavailable | `FileRepository.init?()` returns nil; ViewModel shows error state | +| `files/` directory doesn't exist | Created on first import or tunnel start | +| Import: source unreadable | Throw, show alert with error message | +| Import: duplicate name | Show overwrite confirmation sheet | +| Export: destination unwritable | Throw, show alert | +| Rename: invalid name (`..`, `/`, empty) | Validate, show "Invalid filename" alert | +| Rename: name collision | Show "File already exists" alert | +| Delete: file doesn't exist | No-op (idempotent) | +| Delete: target is a directory | Reject with error (shouldn't happen; `listFiles` excludes dirs) | + +## Edge Cases + +- **Multiple files with same name on import:** Handled by the overwrite-sheet one at a time (each import is a separate operation) +- **Very large files (>100MB):** `FileManager.copyItem` is synchronous; consider showing a progress indicator for large files (stretch goal) +- **Hidden files / macOS resource forks:** `listFiles()` excludes filenames starting with `.`; `._` AppleDouble files are invisible +- **Concurrent import + rename:** Each operation is synchronous on the main queue; no race conditions +- **Tunnel running during file import:** Fine — gost resolves file paths at connection time, and bypass matchers (e.g., `china_ip_list.txt`) are reloaded on config changes or restart + +## Scope / Non-Goals + +- No file preview / content viewer — just metadata +- No subdirectory support — flat file list only (matches Android) +- No drag-and-drop import (stretch goal) +- No progress indicator for large file imports (stretch goal) +- No file watch / auto-reload when bypass files change — user must restart gost + +## Files Changed + +| File | Change | +|---|---| +| `macos/GostX/FileRepository.swift` | **New** — data layer | +| `macos/GostX/FileManageView.swift` | **New** — ViewModel + View | +| `macos/GostX/SettingsView.swift` | Modified — add TabView wrapper | +| `macos/GostXTunnel/PacketTunnelProvider.swift` | Modified — `SetWorkDir` to `files/` | +| `macos/GostX.xcodeproj/project.pbxproj` | Modified — add new files to target | + +## Testing + +Manual verification checklist: + +- [ ] Import a text file via NSOpenPanel; verify it appears in list +- [ ] Import a file with duplicate name; verify overwrite sheet appears +- [ ] Confirm overwrite; verify file is replaced +- [ ] Cancel overwrite; verify original file unchanged +- [ ] Rename a file; verify list updates +- [ ] Rename with invalid name (`..`, `/`, empty); verify error alert +- [ ] Rename to existing name; verify "already exists" alert +- [ ] Export a file via NSSavePanel; verify file saved to chosen location +- [ ] Delete a file; confirm dialog, verify file removed from list +- [ ] Copy path; verify pasteboard contains correct path +- [ ] Empty state displayed when no files exist +- [ ] Start VPN with a bypass config referencing an imported file; verify tunnel works +- [ ] `files/` directory auto-created on first import +- [ ] `files/` directory auto-created on tunnel start (in `setWorkDirAndLog`) From 34c9fcbb7d04fc654cafd62c94166dbf41f4ebe0 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 23:52:43 +0800 Subject: [PATCH 15/89] docs: add macOS file manager implementation plan Co-Authored-By: Claude --- .../plans/2026-07-13-macos-file-manager.md | 912 ++++++++++++++++++ 1 file changed, 912 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-13-macos-file-manager.md diff --git a/docs/superpowers/plans/2026-07-13-macos-file-manager.md b/docs/superpowers/plans/2026-07-13-macos-file-manager.md new file mode 100644 index 0000000..8629845 --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-macos-file-manager.md @@ -0,0 +1,912 @@ +# macOS File Manager for Bypass Files — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add file management to the macOS sandboxed app for importing/managing gost bypass files in the App Group container. + +**Architecture:** `FileRepository` (data layer operating on `{container}/files/`) → `FileManageViewModel` (ObservableObject bridge) → `FileManageView` (SwiftUI). The existing SettingsView gets a TabView wrapper with Profiles and Files tabs. `PacketTunnelProvider` updates its work directory to `files/`. + +**Tech Stack:** Swift 5.9+, SwiftUI, FileManager, App Group container, NSOpenPanel (via `.fileImporter`) + +## Global Constraints + +- App runs in macOS sandbox — file access must go through NSOpenPanel / `.fileImporter` +- Bypass files live in `{AppGroup container}/files/` — flat directory, no subdirectories +- gost configs reference files by bare name (e.g. `bypass.file.path: china_ip_list.txt`) +- Filenames must not contain `..`, `/`, or be empty +- Hidden files (`.` prefix) are excluded from listing +- `PacketTunnelProvider` creates `files/` dir on tunnel start if missing +- Match existing codebase style: repository in dedicated file, view in its own file, `@StateObject` for observation + +--- + +## File Structure + +| File | Responsibility | +|---|---| +| `macos/GostX/FileRepository.swift` (**new**) | Pure data layer: CRUD on `files/` directory in App Group container. `FileInfo` struct + `FileRepository` class. | +| `macos/GostX/FileManageView.swift` (**new**) | `FileManageViewModel` (ObservableObject) + `FileManageView` (SwiftUI). Import/export/rename/delete/copy-path UI. | +| `macos/GostX/SettingsView.swift` (modify) | Wrap existing body in a `TabView` with Profiles + Files tabs. | +| `macos/GostXTunnel/PacketTunnelProvider.swift` (modify) | Change `setWorkDirAndLog()` work dir from container root to `container/files/`. | +| `macos/GostX.xcodeproj/project.pbxproj` (modify) | Add `FileRepository.swift` and `FileManageView.swift` to the GostX target. | + +--- + +### Task 1: FileRepository — Data Layer + +**Files:** +- Create: `macos/GostX/FileRepository.swift` + +**Interfaces:** +- Produces: `FileInfo` struct (Identifiable), `FileRepository` class with `listFiles()`, `exists(_:)`, `importFile(from:)`, `exportFile(_:to:)`, `renameFile(_:to:)`, `deleteFile(_:)`, `filePath(_:)`, `ensureDir()` + +- [ ] **Step 1: Create `FileRepository.swift` with FileInfo and FileRepository** + +```swift +// macos/GostX/FileRepository.swift +import Foundation + +struct FileInfo: Identifiable { + var id: String { name } + let name: String + let sizeBytes: Int64 + let lastModified: Date +} + +class FileRepository { + let workDir: URL + + init?() { + guard let container = AppGroupConfig.containerURL else { return nil } + workDir = container.appendingPathComponent("files") + ensureDir() + } + + // MARK: - Directory + + func ensureDir() { + try? FileManager.default.createDirectory(at: workDir, + withIntermediateDirectories: true, attributes: nil) + } + + // MARK: - List + + func listFiles() -> [FileInfo] { + guard let contents = try? FileManager.default.contentsOfDirectory( + at: workDir, + includingPropertiesForKeys: [.fileSizeKey, .contentModificationDateKey], + options: .skipsHiddenFiles + ) else { return [] } + return contents + .filter { url in + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + return !isDir.boolValue + } + .compactMap { url in + guard let attrs = try? url.resourceValues(forKeys: [ + .fileSizeKey, .contentModificationDateKey + ]) else { return nil } + return FileInfo( + name: url.lastPathComponent, + sizeBytes: Int64(attrs.fileSize ?? 0), + lastModified: attrs.contentModificationDate ?? Date.distantPast + ) + } + .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } + } + + // MARK: - Exists + + func exists(_ name: String) -> Bool { + guard let _ = try? validateName(name) else { return false } + let url = workDir.appendingPathComponent(name) + var isDir: ObjCBool = false + return FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + && !isDir.boolValue + } + + // MARK: - Import + + @discardableResult + func importFile(from sourceURL: URL) throws -> FileInfo { + let name = sourceURL.lastPathComponent + try validateName(name) + let target = workDir.appendingPathComponent(name) + // Remove existing file before copy + if FileManager.default.fileExists(atPath: target.path) { + try FileManager.default.removeItem(at: target) + } + try FileManager.default.copyItem(at: sourceURL, to: target) + let attrs = try target.resourceValues(forKeys: [.fileSizeKey, .contentModificationDateKey]) + return FileInfo( + name: name, + sizeBytes: Int64(attrs.fileSize ?? 0), + lastModified: attrs.contentModificationDate ?? Date() + ) + } + + // MARK: - Export + + func exportFile(_ name: String, to destURL: URL) throws { + try validateName(name) + let source = workDir.appendingPathComponent(name) + guard FileManager.default.fileExists(atPath: source.path) else { + throw FileRepositoryError.fileNotFound(name) + } + if FileManager.default.fileExists(atPath: destURL.path) { + try FileManager.default.removeItem(at: destURL) + } + try FileManager.default.copyItem(at: source, to: destURL) + } + + // MARK: - Rename + + func renameFile(_ oldName: String, to newName: String) throws { + try validateName(oldName) + let trimmed = newName.trimmingCharacters(in: .whitespaces) + guard !trimmed.isEmpty else { + throw FileRepositoryError.invalidName + } + try validateName(trimmed) + let oldURL = workDir.appendingPathComponent(oldName) + let newURL = workDir.appendingPathComponent(trimmed) + guard FileManager.default.fileExists(atPath: oldURL.path) else { + throw FileRepositoryError.fileNotFound(oldName) + } + if FileManager.default.fileExists(atPath: newURL.path) { + throw FileRepositoryError.fileAlreadyExists(trimmed) + } + try FileManager.default.moveItem(at: oldURL, to: newURL) + } + + // MARK: - Delete + + func deleteFile(_ name: String) throws { + try validateName(name) + let url = workDir.appendingPathComponent(name) + if !FileManager.default.fileExists(atPath: url.path) { return } + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + guard !isDir.boolValue else { + throw FileRepositoryError.notAFile(name) + } + try FileManager.default.removeItem(at: url) + } + + // MARK: - Path + + func filePath(_ name: String) -> String { + workDir.appendingPathComponent(name).path + } + + // MARK: - Validation + + private func validateName(_ name: String) throws { + let trimmed = name.trimmingCharacters(in: .whitespaces) + if trimmed.isEmpty || trimmed.contains("..") || trimmed.contains("/") { + throw FileRepositoryError.invalidName + } + } +} + +// MARK: - Errors + +enum FileRepositoryError: LocalizedError { + case invalidName + case fileNotFound(String) + case fileAlreadyExists(String) + case notAFile(String) + + var errorDescription: String? { + switch self { + case .invalidName: + return NSLocalizedString("Invalid filename.", comment: "") + case .fileNotFound(let name): + return String.localizedStringWithFormat( + NSLocalizedString("File \"%@\" not found.", comment: ""), name) + case .fileAlreadyExists(let name): + return String.localizedStringWithFormat( + NSLocalizedString("File \"%@\" already exists.", comment: ""), name) + case .notAFile(let name): + return String.localizedStringWithFormat( + NSLocalizedString("\"%@\" is not a regular file.", comment: ""), name) + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/FileRepository.swift +git commit -m "feat: add FileRepository data layer for bypass file management + +Co-Authored-By: Claude " +``` + +--- + +### Task 2: FileManageViewModel — Observing Bridge + +**Files:** +- Create: `macos/GostX/FileManageView.swift` (first part — ViewModel) + +**Interfaces:** +- Consumes: `FileRepository` from Task 1 +- Produces: `FileManageViewModel` class — `ObservableObject` with `@Published var files: [FileInfo]`, `@Published var alertMessage: String?`, `@Published var pendingOverwrite: (sourceURL: URL, fileName: String)?`, `func refresh()`, `func importFile(from:)`, `func confirmOverwrite()`, `func cancelOverwrite()`, `func renameFile(_:to:)`, `func deleteFile(_:)`, `func exportFile(_:to:)`, `func copyPath(_:)` + +- [ ] **Step 1: Create FileManageViewModel at the top of `FileManageView.swift`** + +```swift +// macos/GostX/FileManageView.swift +import SwiftUI +import AppKit + +// MARK: - Helpers + +private func formatFileSize(_ bytes: Int64) -> String { + switch bytes { + case 0..<1024: return "\(bytes) B" + case 1024..<(1024 * 1024): return "\(bytes / 1024) KB" + default: return String(format: "%.1f MB", Double(bytes) / (1024.0 * 1024.0)) + } +} + +private func formatFileDate(_ date: Date) -> String { + let fmt = DateFormatter() + fmt.dateFormat = "yyyy-MM-dd HH:mm" + return fmt.string(from: date) +} + +// MARK: - ViewModel + +@MainActor +class FileManageViewModel: ObservableObject { + @Published var files: [FileInfo] = [] + @Published var alertMessage: String? + @Published var pendingOverwrite: (sourceURL: URL, fileName: String)? + + private let repo: FileRepository? + + init() { + repo = FileRepository() + refresh() + } + + var isAvailable: Bool { repo != nil } + + func refresh() { + files = repo?.listFiles() ?? [] + } + + func importFile(from sourceURL: URL) { + guard let repo else { return } + let name = sourceURL.lastPathComponent + if repo.exists(name) { + pendingOverwrite = (sourceURL, name) + return + } + doImport(sourceURL: sourceURL) + } + + func confirmOverwrite() { + guard let (url, _) = pendingOverwrite else { return } + pendingOverwrite = nil + doImport(sourceURL: url) + } + + func cancelOverwrite() { + pendingOverwrite = nil + } + + private func doImport(sourceURL: URL) { + guard let repo else { return } + do { + try repo.importFile(from: sourceURL) + refresh() + } catch { + alertMessage = String.localizedStringWithFormat( + NSLocalizedString("Import failed: %@", comment: ""), + error.localizedDescription) + } + } + + func renameFile(_ oldName: String, to newName: String) { + guard let repo else { return } + do { + try repo.renameFile(oldName, to: newName) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func deleteFile(_ name: String) { + guard let repo else { return } + do { + try repo.deleteFile(name) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func exportFile(_ name: String, to destURL: URL) { + guard let repo else { return } + do { + try repo.exportFile(name, to: destURL) + } catch { + alertMessage = error.localizedDescription + } + } + + func copyPath(_ name: String) { + guard let repo else { return } + let path = repo.filePath(name) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(path, forType: .string) + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/FileManageView.swift +git commit -m "feat: add FileManageViewModel with import/export/rename/delete/copy-path + +Co-Authored-By: Claude " +``` + +--- + +### Task 3: FileManageView — SwiftUI View + +**Files:** +- Modify: `macos/GostX/FileManageView.swift` (append view to existing file) + +**Interfaces:** +- Consumes: `FileManageViewModel` from Task 2 +- Produces: `FileManageView` SwiftUI View + +- [ ] **Step 1: Append FileManageView to `FileManageView.swift`** + +Append the following after the ViewModel code from Task 2: + +```swift +// MARK: - FileManageView + +struct FileManageView: View { + @StateObject private var vm = FileManageViewModel() + @State private var showImporter = false + @State private var showExporter = false + @State private var exportName: String? + @State private var renameTarget: FileInfo? + @State private var renameText: String = "" + @State private var deleteTarget: FileInfo? + @State private var showDeleteConfirm = false + + var body: some View { + if !vm.isAvailable { + unavailableView + } else if vm.files.isEmpty { + emptyView + } else { + fileListView + } + } + + // MARK: - Unavailable + + private var unavailableView: some View { + VStack(spacing: 12) { + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 32)) + .foregroundColor(.secondary) + Text("App Group container not available.") + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + // MARK: - Empty + + private var emptyView: some View { + VStack(spacing: 16) { + Image(systemName: "folder") + .font(.system(size: 36)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("No files. Click + to import.", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import File", comment: ""), systemImage: "plus") + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + } + + // MARK: - List + + private var fileListView: some View { + VStack(spacing: 0) { + // Toolbar + HStack { + Text(NSLocalizedString("Bypass Files", comment: "")) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.secondary) + Spacer() + Button(action: { showImporter = true }) { + Image(systemName: "plus") + .font(.system(size: 12, weight: .medium)) + } + .buttonStyle(.borderless) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + Divider() + + // File List + List { + ForEach(vm.files) { file in + FileRowView( + file: file, + onExport: { + exportName = file.name + showExporter = true + }, + onRename: { + renameTarget = file + renameText = file.name + }, + onCopyPath: { vm.copyPath(file.name) }, + onDelete: { + deleteTarget = file + showDeleteConfirm = true + } + ) + } + } + .listStyle(.inset) + } + .frame(minWidth: 300, minHeight: 200) + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .fileExporter( + isPresented: $showExporter, + item: exportName as? String ?? "", + contentTypes: [.data], + defaultFilename: exportName ?? "file" + ) { result in + if case .success(let url) = result, let name = exportName { + vm.exportFile(name, to: url) + } + exportName = nil + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + .modifier(FileManageDialogsModifier( + renameTarget: $renameTarget, + renameText: $renameText, + deleteTarget: $deleteTarget, + showDeleteConfirm: $showDeleteConfirm, + vm: vm + )) + } +} + +// MARK: - FileRowView + +private struct FileRowView: View { + let file: FileInfo + let onExport: () -> Void + let onRename: () -> Void + let onCopyPath: () -> Void + let onDelete: () -> Void + + var body: some View { + HStack(spacing: 12) { + Image(systemName: "doc.text") + .font(.system(size: 16)) + .foregroundColor(.secondary) + .frame(width: 20) + + VStack(alignment: .leading, spacing: 2) { + Text(file.name) + .font(.system(size: 13)) + .lineLimit(1) + Text("\(formatFileSize(file.sizeBytes)) — \(formatFileDate(file.lastModified))") + .font(.system(size: 11)) + .foregroundColor(.secondary) + } + + Spacer() + + // Context menu via right-click + Menu { + Button(NSLocalizedString("Export...", comment: "")) { onExport() } + Button(NSLocalizedString("Rename...", comment: "")) { onRename() } + Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } + } label: { + Image(systemName: "ellipsis.circle") + .font(.system(size: 12)) + .foregroundColor(.secondary) + } + .menuStyle(.borderlessButton) + .frame(width: 24) + } + .padding(.vertical, 4) + } +} + +// MARK: - Alert Modifier + +private struct FileManageAlertModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.alert( + NSLocalizedString("Error", comment: ""), + isPresented: Binding( + get: { vm.alertMessage != nil }, + set: { if !$0 { vm.alertMessage = nil } } + ), + actions: { + Button(NSLocalizedString("OK", comment: "")) { vm.alertMessage = nil } + }, + message: { + Text(vm.alertMessage ?? "") + } + ) + } +} + +// MARK: - Overwrite Sheet Modifier + +private struct OverwriteSheetModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.sheet(isPresented: Binding( + get: { vm.pendingOverwrite != nil }, + set: { if !$0 { vm.cancelOverwrite() } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("File Already Exists", comment: "")) + .font(.headline) + Text(String.localizedStringWithFormat( + NSLocalizedString("A file named \"%@\" already exists. Do you want to replace it?", comment: ""), + vm.pendingOverwrite?.fileName ?? "")) + .font(.system(size: 13)) + .multilineTextAlignment(.center) + .frame(width: 300) + HStack(spacing: 12) { + Button(NSLocalizedString("Cancel", comment: "")) { vm.cancelOverwrite() } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Replace", comment: "")) { vm.confirmOverwrite() } + .keyboardShortcut(.defaultAction) + } + } + .padding() + .frame(width: 340, height: 160) + } + } +} + +// MARK: - Rename / Delete Dialogs Modifier + +private struct FileManageDialogsModifier: ViewModifier { + @Binding var renameTarget: FileInfo? + @Binding var renameText: String + @Binding var deleteTarget: FileInfo? + @Binding var showDeleteConfirm: Bool + let vm: FileManageViewModel + + func body(content: Content) -> some View { + content + // Rename sheet + .sheet(isPresented: Binding( + get: { renameTarget != nil }, + set: { if !$0 { renameTarget = nil } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $renameText) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { renameTarget = nil } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let target = renameTarget, !renameText.trimmingCharacters(in: .whitespaces).isEmpty { + vm.renameFile(target.name, to: renameText) + renameTarget = nil + } + } + .keyboardShortcut(.defaultAction) + .disabled(renameText.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 320, height: 150) + } + // Delete confirmation alert + .alert( + NSLocalizedString("Delete File", comment: ""), + isPresented: $showDeleteConfirm, + presenting: deleteTarget + ) { file in + Button(NSLocalizedString("Cancel", comment: ""), role: .cancel) { + deleteTarget = nil + } + Button(NSLocalizedString("Delete", comment: ""), role: .destructive) { + vm.deleteFile(file.name) + deleteTarget = nil + } + } message: { file in + Text(String.localizedStringWithFormat( + NSLocalizedString("Are you sure you want to delete \"%@\"? This cannot be undone.", comment: ""), + file.name)) + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/FileManageView.swift +git commit -m "feat: add FileManageView with file list, import, export, rename, delete dialogs + +Co-Authored-By: Claude " +``` + +--- + +### Task 4: SettingsView — Add TabView Wrapper + +**Files:** +- Modify: `macos/GostX/SettingsView.swift` + +**Interfaces:** +- Consumes: `FileManageView` from Task 3 +- Produces: Modified `SettingsView` with TabView wrapping Profiles and Files tabs + +- [ ] **Step 1: Modify `SettingsView` body to wrap in TabView** + +Replace the entire body of `struct SettingsView` with a TabView wrapper. The existing `NavigationSplitView` content becomes the Profiles tab. + +Edit `SettingsView.swift` — replace the `body` property (lines 19–93) with: + +```swift + @State private var selectedTab = 0 + + var body: some View { + TabView(selection: $selectedTab) { + profilesTab + .tabItem { + Label(NSLocalizedString("Profiles", comment: ""), systemImage: "doc.text") + } + .tag(0) + FileManageView() + .tabItem { + Label(NSLocalizedString("Files", comment: ""), systemImage: "folder") + } + .tag(1) + } + .frame(minWidth: 700, minHeight: 440) + } + + private var profilesTab: some View { + NavigationSplitView { + VStack(spacing: 0) { + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + HStack(spacing: 8) { + Image(systemName: "doc.text") + .foregroundColor(.secondary) + .font(.system(size: 13)) + Text(profile.name) + .font(.system(size: 13)) + .lineLimit(1) + } + .padding(.vertical, 3) + .tag(profile.id) + .contextMenu { + Button(NSLocalizedString("Rename...", comment: "")) { + renameTargetId = profile.id + newProfileName = profile.name + showRenameSheet = true + } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { + repo.deleteProfile(profile.id) + } + } + } + } + .listStyle(.sidebar) + + Divider() + + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + .font(.system(size: 12)) + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.borderless) + .padding(.horizontal, 12) + .padding(.vertical, 8) + } + .frame(minWidth: 200) + .navigationSplitViewColumnWidth(min: 180, ideal: 220) + } detail: { + if let profileId = selectedProfileId { + YamlEditorView(profileId: profileId) + .id(profileId) + } else { + VStack { + Image(systemName: "doc.text.magnifyingglass") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Select a profile to edit", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .navigationTitle(selectedProfileId.flatMap { id in + repo.profiles.first { $0.id == id }?.name + } ?? NSLocalizedString("Settings", comment: "")) + .toolbar { ToolbarItem { Spacer() } } + .onAppear { + if selectedProfileId == nil, let first = repo.profiles.first { + selectedProfileId = first.id + } + } + .sheet(isPresented: $showAddSheet) { addProfileSheet } + .sheet(isPresented: $showRenameSheet) { renameProfileSheet } + } +``` + +Also remove these lines from `SettingsView` at the original positions: +- The `.frame(minWidth: 700, minHeight: 440)` (line 78) — moved to TabView level +- The `.navigationTitle(...)` (line 79–81) — moved to profilesTab +- The `.toolbar { ToolbarItem { Spacer() } }` (line 82) — moved to profilesTab +- The `.onAppear { ... }` (lines 83–86) — moved to profilesTab +- The `.sheet(...)` modifiers (lines 88–93) — moved to profilesTab + +- [ ] **Step 2: Read the current `SettingsView.swift` to verify exact line content before editing** + +Run: `cat -n macos/GostX/SettingsView.swift | head -100` + +- [ ] **Step 3: Remove the `frame` modifier from the old body and keep the private view helpers** + +The `addProfileSheet`, `renameProfileSheet`, and private vars remain unchanged — the old `body` computed property is replaced by the TabView-based version above. + +- [ ] **Step 4: Verify the file compiles** + +Open in Xcode: build the `GostX` target (`Cmd+B`). + +- [ ] **Step 5: Commit** + +```bash +git add macos/GostX/SettingsView.swift +git commit -m "feat: wrap SettingsView in TabView with Profiles and Files tabs + +Co-Authored-By: Claude " +``` + +--- + +### Task 5: PacketTunnelProvider — Update Work Directory to `files/` + +**Files:** +- Modify: `macos/GostXTunnel/PacketTunnelProvider.swift:101-110` + +**Interfaces:** +- Consumes: `AppGroupConfig.containerURL` (existing) +- Produces: Updated `setWorkDirAndLog()` that creates `files/` directory and sets `LibgostSetWorkDir` to it + +- [ ] **Step 1: Replace the `setWorkDirAndLog()` method** + +Replace lines 101-110 in `PacketTunnelProvider.swift`: + +```swift + private func setWorkDirAndLog() { + guard let containerURL = AppGroupConfig.containerURL else { + os_log(.error, "[GostX] containerURL is nil!") + return + } + let filesDir = containerURL.appendingPathComponent("files") + try? FileManager.default.createDirectory(at: filesDir, + withIntermediateDirectories: true, attributes: nil) + LibgostSetWorkDir(filesDir.path, nil) + let logFile = containerURL.appendingPathComponent("gost.log").path + LibgostSetLogFile(logFile, nil) + LibgostSetLogLevel("info") + } +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostXTunnel/PacketTunnelProvider.swift +git commit -m "fix: set work directory to files/ subdirectory for bypass file resolution + +Co-Authored-By: Claude " +``` + +--- + +### Task 6: Xcode Project — Add New Files to Target + +**Files:** +- Modify: `macos/GostX.xcodeproj/project.pbxproj` + +**Interfaces:** +- Produces: `FileRepository.swift` and `FileManageView.swift` added to the GostX target + +- [ ] **Step 1: Open the Xcode project and add the files manually** + +1. Open `macos/GostX.xcodeproj` in Xcode +2. In the Project Navigator, select the `GostX` group (under the GostX project) +3. Right-click → **Add Files to "GostX"...** +4. Select `macos/GostX/FileRepository.swift` and `macos/GostX/FileManageView.swift` +5. Ensure **"Copy items if needed"** is unchecked (files are already in the correct location) +6. Ensure the target **GostX** is checked (NOT GostXTunnel) +7. Click **Add** + +- [ ] **Step 2: Verify the files appear in the GostX target build phases** + +In Xcode: Select the GostX project → GostX target → Build Phases → Compile Sources. +Verify `FileRepository.swift` and `FileManageView.swift` are listed. + +- [ ] **Step 3: Build the project** + +Run: `Cmd+B` in Xcode. Verify no compilation errors. + +- [ ] **Step 4: Commit** + +```bash +git add macos/GostX.xcodeproj/project.pbxproj +git commit -m "chore: add FileRepository and FileManageView to Xcode project + +Co-Authored-By: Claude " +``` + +--- + +## Verification Checklist + +After all tasks are complete, manually verify: + +- [ ] Open the Settings window — both Profiles and Files tabs are visible +- [ ] Profiles tab works as before (profile list, YAML editor) +- [ ] Files tab shows empty state with "No files. Click + to import." +- [ ] Click import → NSOpenPanel appears → select a file → file appears in list +- [ ] Import a file with same name → overwrite sheet appears → Replace works, Cancel works +- [ ] Right-click/click "..." → Export → NSSavePanel appears → file saves correctly +- [ ] Right-click/click "..." → Rename → sheet appears → rename works +- [ ] Rename to invalid name → error alert +- [ ] Rename to existing name → error alert +- [ ] Right-click/click "..." → Copy Path → clipboard has correct path +- [ ] Right-click/click "..." → Delete → confirmation → file removed +- [ ] Start VPN with bypass config → `files/` directory exists in container +- [ ] Tunnel reads bypass files from `files/` correctly From e61c02ead5f23e1decf61d7723581bd33ce5feae Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Mon, 13 Jul 2026 23:57:58 +0800 Subject: [PATCH 16/89] feat: add FileRepository data layer for bypass file management Co-Authored-By: Claude --- macos/GostX/FileRepository.swift | 171 +++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 macos/GostX/FileRepository.swift diff --git a/macos/GostX/FileRepository.swift b/macos/GostX/FileRepository.swift new file mode 100644 index 0000000..6b00645 --- /dev/null +++ b/macos/GostX/FileRepository.swift @@ -0,0 +1,171 @@ +// macos/GostX/FileRepository.swift +import Foundation + +struct FileInfo: Identifiable { + var id: String { name } + let name: String + let sizeBytes: Int64 + let lastModified: Date +} + +class FileRepository { + let workDir: URL + + init?() { + guard let container = AppGroupConfig.containerURL else { return nil } + workDir = container.appendingPathComponent("files") + ensureDir() + } + + // MARK: - Directory + + func ensureDir() { + try? FileManager.default.createDirectory(at: workDir, + withIntermediateDirectories: true, attributes: nil) + } + + // MARK: - List + + func listFiles() -> [FileInfo] { + guard let contents = try? FileManager.default.contentsOfDirectory( + at: workDir, + includingPropertiesForKeys: [.fileSizeKey, .contentModificationDateKey], + options: .skipsHiddenFiles + ) else { return [] } + return contents + .filter { url in + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + return !isDir.boolValue + } + .compactMap { url in + guard let attrs = try? url.resourceValues(forKeys: [ + .fileSizeKey, .contentModificationDateKey + ]) else { return nil } + return FileInfo( + name: url.lastPathComponent, + sizeBytes: Int64(attrs.fileSize ?? 0), + lastModified: attrs.contentModificationDate ?? Date.distantPast + ) + } + .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } + } + + // MARK: - Exists + + func exists(_ name: String) -> Bool { + guard let _ = try? validateName(name) else { return false } + let url = workDir.appendingPathComponent(name) + var isDir: ObjCBool = false + return FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + && !isDir.boolValue + } + + // MARK: - Import + + @discardableResult + func importFile(from sourceURL: URL) throws -> FileInfo { + let name = sourceURL.lastPathComponent + try validateName(name) + let target = workDir.appendingPathComponent(name) + // Remove existing file before copy + if FileManager.default.fileExists(atPath: target.path) { + try FileManager.default.removeItem(at: target) + } + try FileManager.default.copyItem(at: sourceURL, to: target) + let attrs = try target.resourceValues(forKeys: [.fileSizeKey, .contentModificationDateKey]) + return FileInfo( + name: name, + sizeBytes: Int64(attrs.fileSize ?? 0), + lastModified: attrs.contentModificationDate ?? Date() + ) + } + + // MARK: - Export + + func exportFile(_ name: String, to destURL: URL) throws { + try validateName(name) + let source = workDir.appendingPathComponent(name) + guard FileManager.default.fileExists(atPath: source.path) else { + throw FileRepositoryError.fileNotFound(name) + } + if FileManager.default.fileExists(atPath: destURL.path) { + try FileManager.default.removeItem(at: destURL) + } + try FileManager.default.copyItem(at: source, to: destURL) + } + + // MARK: - Rename + + func renameFile(_ oldName: String, to newName: String) throws { + try validateName(oldName) + let trimmed = newName.trimmingCharacters(in: .whitespaces) + guard !trimmed.isEmpty else { + throw FileRepositoryError.invalidName + } + try validateName(trimmed) + let oldURL = workDir.appendingPathComponent(oldName) + let newURL = workDir.appendingPathComponent(trimmed) + guard FileManager.default.fileExists(atPath: oldURL.path) else { + throw FileRepositoryError.fileNotFound(oldName) + } + if FileManager.default.fileExists(atPath: newURL.path) { + throw FileRepositoryError.fileAlreadyExists(trimmed) + } + try FileManager.default.moveItem(at: oldURL, to: newURL) + } + + // MARK: - Delete + + func deleteFile(_ name: String) throws { + try validateName(name) + let url = workDir.appendingPathComponent(name) + if !FileManager.default.fileExists(atPath: url.path) { return } + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) + guard !isDir.boolValue else { + throw FileRepositoryError.notAFile(name) + } + try FileManager.default.removeItem(at: url) + } + + // MARK: - Path + + func filePath(_ name: String) -> String { + workDir.appendingPathComponent(name).path + } + + // MARK: - Validation + + private func validateName(_ name: String) throws { + let trimmed = name.trimmingCharacters(in: .whitespaces) + if trimmed.isEmpty || trimmed.contains("..") || trimmed.contains("/") { + throw FileRepositoryError.invalidName + } + } +} + +// MARK: - Errors + +enum FileRepositoryError: LocalizedError { + case invalidName + case fileNotFound(String) + case fileAlreadyExists(String) + case notAFile(String) + + var errorDescription: String? { + switch self { + case .invalidName: + return NSLocalizedString("Invalid filename.", comment: "") + case .fileNotFound(let name): + return String.localizedStringWithFormat( + NSLocalizedString("File \"%@\" not found.", comment: ""), name) + case .fileAlreadyExists(let name): + return String.localizedStringWithFormat( + NSLocalizedString("File \"%@\" already exists.", comment: ""), name) + case .notAFile(let name): + return String.localizedStringWithFormat( + NSLocalizedString("\"%@\" is not a regular file.", comment: ""), name) + } + } +} From b75216236372b3cd732a0a74bb35e5e9c1074dd7 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:11:21 +0800 Subject: [PATCH 17/89] fix: guard against directory import and validate trimmed names in FileRepository Two fixes: 1. Directory guard in importFile(from:): - Added a check using fileExists(atPath:isDirectory:) to reject directory sources - Throws FileRepositoryError.notAFile(name) if source is a directory - Prevents accidentally copying a directory tree into the flat files/ directory 2. validateName now returns the trimmed name: - Changed signature from throws -> Void to throws -> String - All callers (exists, importFile, exportFile, renameFile, deleteFile, filePath) now use the returned trimmed name for appending paths, ensuring names with leading/trailing whitespace don't create files with spaces in their names. - renameFile no longer does a separate inline trim; both oldName and newName go through validateName. --- macos/GostX/FileRepository.swift | 52 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/macos/GostX/FileRepository.swift b/macos/GostX/FileRepository.swift index 6b00645..53c66e4 100644 --- a/macos/GostX/FileRepository.swift +++ b/macos/GostX/FileRepository.swift @@ -54,8 +54,8 @@ class FileRepository { // MARK: - Exists func exists(_ name: String) -> Bool { - guard let _ = try? validateName(name) else { return false } - let url = workDir.appendingPathComponent(name) + guard let trimmed = try? validateName(name) else { return false } + let url = workDir.appendingPathComponent(trimmed) var isDir: ObjCBool = false return FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) && !isDir.boolValue @@ -66,8 +66,16 @@ class FileRepository { @discardableResult func importFile(from sourceURL: URL) throws -> FileInfo { let name = sourceURL.lastPathComponent - try validateName(name) - let target = workDir.appendingPathComponent(name) + let trimmed = try validateName(name) + + // Reject directory sources — we only copy regular files. + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: sourceURL.path, isDirectory: &isDir) + guard !isDir.boolValue else { + throw FileRepositoryError.notAFile(name) + } + + let target = workDir.appendingPathComponent(trimmed) // Remove existing file before copy if FileManager.default.fileExists(atPath: target.path) { try FileManager.default.removeItem(at: target) @@ -75,7 +83,7 @@ class FileRepository { try FileManager.default.copyItem(at: sourceURL, to: target) let attrs = try target.resourceValues(forKeys: [.fileSizeKey, .contentModificationDateKey]) return FileInfo( - name: name, + name: trimmed, sizeBytes: Int64(attrs.fileSize ?? 0), lastModified: attrs.contentModificationDate ?? Date() ) @@ -84,10 +92,10 @@ class FileRepository { // MARK: - Export func exportFile(_ name: String, to destURL: URL) throws { - try validateName(name) - let source = workDir.appendingPathComponent(name) + let trimmed = try validateName(name) + let source = workDir.appendingPathComponent(trimmed) guard FileManager.default.fileExists(atPath: source.path) else { - throw FileRepositoryError.fileNotFound(name) + throw FileRepositoryError.fileNotFound(trimmed) } if FileManager.default.fileExists(atPath: destURL.path) { try FileManager.default.removeItem(at: destURL) @@ -98,19 +106,15 @@ class FileRepository { // MARK: - Rename func renameFile(_ oldName: String, to newName: String) throws { - try validateName(oldName) - let trimmed = newName.trimmingCharacters(in: .whitespaces) - guard !trimmed.isEmpty else { - throw FileRepositoryError.invalidName - } - try validateName(trimmed) - let oldURL = workDir.appendingPathComponent(oldName) - let newURL = workDir.appendingPathComponent(trimmed) + let trimmedOld = try validateName(oldName) + let trimmedNew = try validateName(newName) + let oldURL = workDir.appendingPathComponent(trimmedOld) + let newURL = workDir.appendingPathComponent(trimmedNew) guard FileManager.default.fileExists(atPath: oldURL.path) else { - throw FileRepositoryError.fileNotFound(oldName) + throw FileRepositoryError.fileNotFound(trimmedOld) } if FileManager.default.fileExists(atPath: newURL.path) { - throw FileRepositoryError.fileAlreadyExists(trimmed) + throw FileRepositoryError.fileAlreadyExists(trimmedNew) } try FileManager.default.moveItem(at: oldURL, to: newURL) } @@ -118,13 +122,13 @@ class FileRepository { // MARK: - Delete func deleteFile(_ name: String) throws { - try validateName(name) - let url = workDir.appendingPathComponent(name) + let trimmed = try validateName(name) + let url = workDir.appendingPathComponent(trimmed) if !FileManager.default.fileExists(atPath: url.path) { return } var isDir: ObjCBool = false FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) guard !isDir.boolValue else { - throw FileRepositoryError.notAFile(name) + throw FileRepositoryError.notAFile(trimmed) } try FileManager.default.removeItem(at: url) } @@ -132,16 +136,18 @@ class FileRepository { // MARK: - Path func filePath(_ name: String) -> String { - workDir.appendingPathComponent(name).path + let safe = (try? validateName(name)) ?? name + return workDir.appendingPathComponent(safe).path } // MARK: - Validation - private func validateName(_ name: String) throws { + private func validateName(_ name: String) throws -> String { let trimmed = name.trimmingCharacters(in: .whitespaces) if trimmed.isEmpty || trimmed.contains("..") || trimmed.contains("/") { throw FileRepositoryError.invalidName } + return trimmed } } From de44da11fc4c328bdba314e926bd435b99e54b3a Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:13:04 +0800 Subject: [PATCH 18/89] feat: add FileManageViewModel with import/export/rename/delete/copy-path Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 macos/GostX/FileManageView.swift diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift new file mode 100644 index 0000000..2affcbf --- /dev/null +++ b/macos/GostX/FileManageView.swift @@ -0,0 +1,109 @@ +// macos/GostX/FileManageView.swift +import SwiftUI +import AppKit + +// MARK: - Helpers + +private func formatFileSize(_ bytes: Int64) -> String { + switch bytes { + case 0..<1024: return "\(bytes) B" + case 1024..<(1024 * 1024): return "\(bytes / 1024) KB" + default: return String(format: "%.1f MB", Double(bytes) / (1024.0 * 1024.0)) + } +} + +private func formatFileDate(_ date: Date) -> String { + let fmt = DateFormatter() + fmt.dateFormat = "yyyy-MM-dd HH:mm" + return fmt.string(from: date) +} + +// MARK: - ViewModel + +@MainActor +class FileManageViewModel: ObservableObject { + @Published var files: [FileInfo] = [] + @Published var alertMessage: String? + @Published var pendingOverwrite: (sourceURL: URL, fileName: String)? + + private let repo: FileRepository? + + init() { + repo = FileRepository() + refresh() + } + + var isAvailable: Bool { repo != nil } + + func refresh() { + files = repo?.listFiles() ?? [] + } + + func importFile(from sourceURL: URL) { + guard let repo else { return } + let name = sourceURL.lastPathComponent + if repo.exists(name) { + pendingOverwrite = (sourceURL, name) + return + } + doImport(sourceURL: sourceURL) + } + + func confirmOverwrite() { + guard let (url, _) = pendingOverwrite else { return } + pendingOverwrite = nil + doImport(sourceURL: url) + } + + func cancelOverwrite() { + pendingOverwrite = nil + } + + private func doImport(sourceURL: URL) { + guard let repo else { return } + do { + try repo.importFile(from: sourceURL) + refresh() + } catch { + alertMessage = String.localizedStringWithFormat( + NSLocalizedString("Import failed: %@", comment: ""), + error.localizedDescription) + } + } + + func renameFile(_ oldName: String, to newName: String) { + guard let repo else { return } + do { + try repo.renameFile(oldName, to: newName) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func deleteFile(_ name: String) { + guard let repo else { return } + do { + try repo.deleteFile(name) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func exportFile(_ name: String, to destURL: URL) { + guard let repo else { return } + do { + try repo.exportFile(name, to: destURL) + } catch { + alertMessage = error.localizedDescription + } + } + + func copyPath(_ name: String) { + guard let repo else { return } + let path = repo.filePath(name) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(path, forType: .string) + } +} From c5dec5e05f8d12bb23c7ca358f24e089bb280c42 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:15:49 +0800 Subject: [PATCH 19/89] feat: add FileManageView with file list, import, export, rename, delete dialogs Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 291 +++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 2affcbf..3d01541 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -107,3 +107,294 @@ class FileManageViewModel: ObservableObject { NSPasteboard.general.setString(path, forType: .string) } } + +// MARK: - FileManageView + +struct FileManageView: View { + @StateObject private var vm = FileManageViewModel() + @State private var showImporter = false + @State private var showExporter = false + @State private var exportName: String? + @State private var renameTarget: FileInfo? + @State private var renameText: String = "" + @State private var deleteTarget: FileInfo? + @State private var showDeleteConfirm = false + + var body: some View { + if !vm.isAvailable { + unavailableView + } else if vm.files.isEmpty { + emptyView + } else { + fileListView + } + } + + // MARK: - Unavailable + + private var unavailableView: some View { + VStack(spacing: 12) { + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 32)) + .foregroundColor(.secondary) + Text("App Group container not available.") + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + // MARK: - Empty + + private var emptyView: some View { + VStack(spacing: 16) { + Image(systemName: "folder") + .font(.system(size: 36)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("No files. Click + to import.", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import File", comment: ""), systemImage: "plus") + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + } + + // MARK: - List + + private var fileListView: some View { + VStack(spacing: 0) { + // Toolbar + HStack { + Text(NSLocalizedString("Bypass Files", comment: "")) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.secondary) + Spacer() + Button(action: { showImporter = true }) { + Image(systemName: "plus") + .font(.system(size: 12, weight: .medium)) + } + .buttonStyle(.borderless) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + Divider() + + // File List + List { + ForEach(vm.files) { file in + FileRowView( + file: file, + onExport: { + exportName = file.name + showExporter = true + }, + onRename: { + renameTarget = file + renameText = file.name + }, + onCopyPath: { vm.copyPath(file.name) }, + onDelete: { + deleteTarget = file + showDeleteConfirm = true + } + ) + } + } + .listStyle(.inset) + } + .frame(minWidth: 300, minHeight: 200) + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .fileExporter( + isPresented: $showExporter, + item: exportName as? String ?? "", + contentTypes: [.data], + defaultFilename: exportName ?? "file" + ) { result in + if case .success(let url) = result, let name = exportName { + vm.exportFile(name, to: url) + } + exportName = nil + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + .modifier(FileManageDialogsModifier( + renameTarget: $renameTarget, + renameText: $renameText, + deleteTarget: $deleteTarget, + showDeleteConfirm: $showDeleteConfirm, + vm: vm + )) + } +} + +// MARK: - FileRowView + +private struct FileRowView: View { + let file: FileInfo + let onExport: () -> Void + let onRename: () -> Void + let onCopyPath: () -> Void + let onDelete: () -> Void + + var body: some View { + HStack(spacing: 12) { + Image(systemName: "doc.text") + .font(.system(size: 16)) + .foregroundColor(.secondary) + .frame(width: 20) + + VStack(alignment: .leading, spacing: 2) { + Text(file.name) + .font(.system(size: 13)) + .lineLimit(1) + Text("\(formatFileSize(file.sizeBytes)) — \(formatFileDate(file.lastModified))") + .font(.system(size: 11)) + .foregroundColor(.secondary) + } + + Spacer() + + // Context menu via right-click + Menu { + Button(NSLocalizedString("Export...", comment: "")) { onExport() } + Button(NSLocalizedString("Rename...", comment: "")) { onRename() } + Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } + } label: { + Image(systemName: "ellipsis.circle") + .font(.system(size: 12)) + .foregroundColor(.secondary) + } + .menuStyle(.borderlessButton) + .frame(width: 24) + } + .padding(.vertical, 4) + } +} + +// MARK: - Alert Modifier + +private struct FileManageAlertModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.alert( + NSLocalizedString("Error", comment: ""), + isPresented: Binding( + get: { vm.alertMessage != nil }, + set: { if !$0 { vm.alertMessage = nil } } + ), + actions: { + Button(NSLocalizedString("OK", comment: "")) { vm.alertMessage = nil } + }, + message: { + Text(vm.alertMessage ?? "") + } + ) + } +} + +// MARK: - Overwrite Sheet Modifier + +private struct OverwriteSheetModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.sheet(isPresented: Binding( + get: { vm.pendingOverwrite != nil }, + set: { if !$0 { vm.cancelOverwrite() } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("File Already Exists", comment: "")) + .font(.headline) + Text(String.localizedStringWithFormat( + NSLocalizedString("A file named \"%@\" already exists. Do you want to replace it?", comment: ""), + vm.pendingOverwrite?.fileName ?? "")) + .font(.system(size: 13)) + .multilineTextAlignment(.center) + .frame(width: 300) + HStack(spacing: 12) { + Button(NSLocalizedString("Cancel", comment: "")) { vm.cancelOverwrite() } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Replace", comment: "")) { vm.confirmOverwrite() } + .keyboardShortcut(.defaultAction) + } + } + .padding() + .frame(width: 340, height: 160) + } + } +} + +// MARK: - Rename / Delete Dialogs Modifier + +private struct FileManageDialogsModifier: ViewModifier { + @Binding var renameTarget: FileInfo? + @Binding var renameText: String + @Binding var deleteTarget: FileInfo? + @Binding var showDeleteConfirm: Bool + let vm: FileManageViewModel + + func body(content: Content) -> some View { + content + // Rename sheet + .sheet(isPresented: Binding( + get: { renameTarget != nil }, + set: { if !$0 { renameTarget = nil } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $renameText) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { renameTarget = nil } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let target = renameTarget, !renameText.trimmingCharacters(in: .whitespaces).isEmpty { + vm.renameFile(target.name, to: renameText) + renameTarget = nil + } + } + .keyboardShortcut(.defaultAction) + .disabled(renameText.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 320, height: 150) + } + // Delete confirmation alert + .alert( + NSLocalizedString("Delete File", comment: ""), + isPresented: $showDeleteConfirm, + presenting: deleteTarget + ) { file in + Button(NSLocalizedString("Cancel", comment: ""), role: .cancel) { + deleteTarget = nil + } + Button(NSLocalizedString("Delete", comment: ""), role: .destructive) { + vm.deleteFile(file.name) + deleteTarget = nil + } + } message: { file in + Text(String.localizedStringWithFormat( + NSLocalizedString("Are you sure you want to delete \"%@\"? This cannot be undone.", comment: ""), + file.name)) + } + } +} From da782dbb365693aa8ed13f3c90172b7f09ec2a85 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:28:17 +0800 Subject: [PATCH 20/89] fix: deduplicate fileImporter modifier and remove redundant cast - Moved .fileImporter from emptyView and fileListView to the top-level body Group - Removed redundant as? String cast in .fileExporter's item parameter Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 3d01541..55092f6 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -121,12 +121,19 @@ struct FileManageView: View { @State private var showDeleteConfirm = false var body: some View { - if !vm.isAvailable { - unavailableView - } else if vm.files.isEmpty { - emptyView - } else { - fileListView + Group { + if !vm.isAvailable { + unavailableView + } else if vm.files.isEmpty { + emptyView + } else { + fileListView + } + } + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } } } @@ -159,11 +166,6 @@ struct FileManageView: View { } } .frame(maxWidth: .infinity, maxHeight: .infinity) - .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in - if case .success(let url) = result { - vm.importFile(from: url) - } - } .modifier(FileManageAlertModifier(vm: vm)) .modifier(OverwriteSheetModifier(vm: vm)) } @@ -213,14 +215,9 @@ struct FileManageView: View { .listStyle(.inset) } .frame(minWidth: 300, minHeight: 200) - .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in - if case .success(let url) = result { - vm.importFile(from: url) - } - } .fileExporter( isPresented: $showExporter, - item: exportName as? String ?? "", + item: exportName ?? "", contentTypes: [.data], defaultFilename: exportName ?? "file" ) { result in From 02c0455e5d916eafc8f7451cba5e5a68bc8ca6c5 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:29:25 +0800 Subject: [PATCH 21/89] fix: localize unavailable-state text in FileManageView Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 55092f6..0034920 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -144,7 +144,7 @@ struct FileManageView: View { Image(systemName: "exclamationmark.triangle") .font(.system(size: 32)) .foregroundColor(.secondary) - Text("App Group container not available.") + Text(NSLocalizedString("App Group container not available.", comment: "")) .font(.system(size: 13)) .foregroundColor(.secondary) } From 3983e830fa6a5f3c1014f3c81997c06984cd73ae Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 06:49:16 +0800 Subject: [PATCH 22/89] feat: wrap SettingsView in TabView with Profiles and Files tabs - Replace SettingsView body with TabView wrapper (Profiles + Files tabs) - Extract existing NavigationSplitView into profilesTab computed property - Add selectedTab state variable - Add FileManageView.swift and FileRepository.swift to Xcode project target - Add @available(macOS 14.0, *) annotations to fix build errors - Fix NSHostingController reference in MacExtrasConfigurator Co-Authored-By: Claude --- macos/GostX.xcodeproj/project.pbxproj | 8 + macos/GostX/FileManageView.swift | 4 + macos/GostX/MacExtrasConfigurator.swift | 201 +++++++++++++----------- macos/GostX/SettingsView.swift | 190 ++++++++++++++++++++-- 4 files changed, 293 insertions(+), 110 deletions(-) diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 341282d..8c0f41b 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ B5AEDDB12864920800C09ED6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEDDB02864920800C09ED6 /* AppDelegate.swift */; }; B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */; }; B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B0E079285C63080035A6F2 /* SettingsView.swift */; }; + B5B1C1A129DD6E90008AA941 /* FileManageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A229DD6E90008AA942 /* FileManageView.swift */; }; + B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A429DD6E90008AA944 /* FileRepository.swift */; }; B5C1CF1429DD6E7C008AA934 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */; }; B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; /* End PBXBuildFile section */ @@ -87,6 +89,8 @@ B5AEDDB02864920800C09ED6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacExtrasConfigurator.swift; sourceTree = ""; }; B5B0E079285C63080035A6F2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + B5B1C1A229DD6E90008AA942 /* FileManageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManageView.swift; sourceTree = ""; }; + B5B1C1A429DD6E90008AA944 /* FileRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRepository.swift; sourceTree = ""; }; B5C1CF1529DD6E7C008AA934 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; B5C1CF1729DD6E90008AA934 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; B5F6387829DEFF31009A1287 /* Arguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arguments.swift; sourceTree = ""; }; @@ -179,6 +183,8 @@ B5B0E079285C63080035A6F2 /* SettingsView.swift */, B5AEDDB02864920800C09ED6 /* AppDelegate.swift */, B5F6387829DEFF31009A1287 /* Arguments.swift */, + B5B1C1A229DD6E90008AA942 /* FileManageView.swift */, + B5B1C1A429DD6E90008AA944 /* FileRepository.swift */, ); path = GostX; sourceTree = ""; @@ -403,6 +409,8 @@ B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */, B5AEDDB12864920800C09ED6 /* AppDelegate.swift in Sources */, B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */, + B5B1C1A129DD6E90008AA941 /* FileManageView.swift in Sources */, + B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */, B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */, 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */, B532A66A285B528900159BAC /* main.swift in Sources */, diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 0034920..7189510 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -110,6 +110,7 @@ class FileManageViewModel: ObservableObject { // MARK: - FileManageView +@available(macOS 14.0, *) struct FileManageView: View { @StateObject private var vm = FileManageViewModel() @State private var showImporter = false @@ -240,6 +241,7 @@ struct FileManageView: View { // MARK: - FileRowView +@available(macOS 14.0, *) private struct FileRowView: View { let file: FileInfo let onExport: () -> Void @@ -286,6 +288,7 @@ private struct FileRowView: View { // MARK: - Alert Modifier +@available(macOS 14.0, *) private struct FileManageAlertModifier: ViewModifier { @ObservedObject var vm: FileManageViewModel @@ -340,6 +343,7 @@ private struct OverwriteSheetModifier: ViewModifier { // MARK: - Rename / Delete Dialogs Modifier +@available(macOS 14.0, *) private struct FileManageDialogsModifier: ViewModifier { @Binding var renameTarget: FileInfo? @Binding var renameText: String diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 75fcef2..0bd5d61 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -16,59 +16,43 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { public var statusActionOnItem: NSMenuItem public var statusActionOffItem: NSMenuItem public var statusActionRestartItem: NSMenuItem - public var statusListenItem: NSMenuItem - public var argsItem: NSMenuItem = NSMenuItem() - private var vpnModeItem: NSMenuItem! - + public var profileMenuItem: NSMenuItem = NSMenuItem() + private var menu: NSMenu = NSMenu() - private var activeImg: NSImage? = NSImage(named: "StatusBarActive") - private var inActiveImg: NSImage? = NSImage(named: "StatusBarInactive") - private var settingsHostingController: NSHostingController? - + private var activeImg: NSImage? + private var inActiveImg: NSImage? + private var settingsHostingController: NSHostingController? + + private var settingsWindow: NSWindow? + init(delegate: AppDelegate) { self.delegate = delegate - + statusBar = NSStatusBar.system statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength) - + + activeImg = NSImage(named: "StatusBarActive") + activeImg?.isTemplate = true + inActiveImg = NSImage(named: "StatusBarInactive") + inActiveImg?.isTemplate = true + statusBarItem.button?.image = inActiveImg + statusActionOnItem = NSMenuItem() statusActionOffItem = NSMenuItem() statusActionRestartItem = NSMenuItem() - statusListenItem = NSMenuItem() - + super.init() - + createMenu() + toOffState() } - + // MARK: - MenuConfig - + private func createMenu() { menu.delegate = self menu.autoenablesItems = false - - // Listening - statusListenItem.isEnabled = false - menu.addItem(statusListenItem) - - // VPN Mode toggle - let vpnItem = NSMenuItem() - vpnItem.title = NSLocalizedString("VPN Mode", comment: "") - vpnItem.state = UserDefaults.standard.bool(forKey: defaultsVpnModeKey) ? .on : .off - vpnItem.target = self - vpnItem.action = #selector(onToggleVpnMode(_:)) - menu.addItem(vpnItem) - self.vpnModeItem = vpnItem - menu.addItem(.separator()) - - // Configuration - argsItem.target = self - argsItem.action = #selector(onConfigClick) - menu.addItem(argsItem) - - menu.addItem(.separator()) - // Actions statusActionOnItem.title = NSLocalizedString("Start", comment: "start the service") statusActionOnItem.image = NSImage(systemSymbolName: "play.fill", accessibilityDescription: "") @@ -76,34 +60,39 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { statusActionOnItem.isEnabled = false statusActionOnItem.action = #selector(onStartClick) menu.addItem(statusActionOnItem) - + statusActionOffItem.title = NSLocalizedString("Stop", comment: "stop the service") statusActionOffItem.image = NSImage(systemSymbolName: "stop.fill", accessibilityDescription: "") statusActionOffItem.target = self statusActionOffItem.isEnabled = false statusActionOffItem.action = #selector(onStopClick) menu.addItem(statusActionOffItem) - + statusActionRestartItem.title = NSLocalizedString("Restart", comment: "") statusActionRestartItem.image = NSImage(systemSymbolName: "gobackward", accessibilityDescription: "") statusActionRestartItem.target = self statusActionRestartItem.isEnabled = false statusActionRestartItem.action = #selector(onRestartClick) menu.addItem(statusActionRestartItem) - + menu.addItem(.separator()) - - let configMenuItem = NSMenuItem() - configMenuItem.title = NSLocalizedString("Settings...", comment: "") - configMenuItem.image = NSImage(systemSymbolName: "gear", accessibilityDescription: "") - configMenuItem.keyEquivalent = "," - configMenuItem.keyEquivalentModifierMask = .command - configMenuItem.target = self - configMenuItem.action = #selector(onConfigClick) - menu.addItem(configMenuItem) - + + // Configuration profiles submenu + profileMenuItem.title = NSLocalizedString("Profile", comment: "") + profileMenuItem.image = NSImage(systemSymbolName: "doc.text", accessibilityDescription: "") + menu.addItem(profileMenuItem) + + let settingsItem = NSMenuItem() + settingsItem.title = NSLocalizedString("Settings...", comment: "") + settingsItem.image = NSImage(systemSymbolName: "gear", accessibilityDescription: "") + settingsItem.keyEquivalent = "," + settingsItem.keyEquivalentModifierMask = .command + settingsItem.target = self + settingsItem.action = #selector(onSettingsClick) + menu.addItem(settingsItem) + menu.addItem(.separator()) - + let quitItem = NSMenuItem() quitItem.title = NSLocalizedString("Quit", comment: "") quitItem.image = NSImage(systemSymbolName: "power", accessibilityDescription: "") @@ -112,82 +101,106 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { quitItem.keyEquivalentModifierMask = .command quitItem.action = #selector(onQuitClick) menu.addItem(quitItem) - + statusBarItem.menu = menu } - - // MARK: - Actions - - @objc private func onToggleVpnMode(_ sender: NSMenuItem) { - let newValue = sender.state != .on - sender.state = newValue ? .on : .off - UserDefaults.standard.set(newValue, forKey: defaultsVpnModeKey) + + // MARK: - Profile Submenu + + private func rebuildProfileSubmenu() { + let repo = ConfigRepository.shared + let submenu = NSMenu() + submenu.autoenablesItems = false + + for profile in repo.profiles { + let item = NSMenuItem() + item.title = profile.name + item.state = profile.id == repo.activeProfileId ? .on : .off + item.target = self + item.action = #selector(onSelectProfile(_:)) + item.representedObject = profile.id + submenu.addItem(item) + } + + if repo.profiles.isEmpty { + let emptyItem = NSMenuItem() + emptyItem.title = NSLocalizedString("No profiles", comment: "") + emptyItem.isEnabled = false + submenu.addItem(emptyItem) + } + + submenu.addItem(.separator()) + + let manageItem = NSMenuItem() + manageItem.title = NSLocalizedString("Manage Profiles...", comment: "") + manageItem.target = self + manageItem.action = #selector(onSettingsClick) + submenu.addItem(manageItem) + + profileMenuItem.submenu = submenu } - - @objc private func onArgumentClick(_ sender: Any?) { - onConfigClick(sender) + + @objc private func onSelectProfile(_ sender: NSMenuItem) { + guard let profileId = sender.representedObject as? String else { return } + ConfigRepository.shared.setActiveProfile(profileId) } - - @objc private func onConfigClick(_ sender: Any?) { - NSApp.activate(ignoringOtherApps: true) - NSApp.setActivationPolicy(.regular) - settingsHostingController = NSHostingController(rootView: SettingsView()) + // MARK: - Actions + + @objc private func onSettingsClick(_ sender: Any?) { + guard #available(macOS 14.0, *) else { return } + if let window = settingsWindow, window.isVisible { + window.orderFrontRegardless() + window.makeKey() + return + } + settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "Settings Window" - window.setContentSize(NSSize(width: 800, height: 400)) - window.makeKeyAndOrderFront(nil) + window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "Settings" + window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] + window.titlebarAppearsTransparent = true + window.titlebarSeparatorStyle = .automatic + window.toolbarStyle = .unified + window.setContentSize(NSSize(width: 780, height: 500)) + window.center() window.orderFrontRegardless() + window.makeKey() + window.isReleasedWhenClosed = false + settingsWindow = window } - + @objc private func onQuitClick(_ sender: Any?) { delegate.quit() } - + @objc private func onStopClick(_ sender: Any?) { delegate.stop() } - + @objc private func onStartClick(_ sender: Any?) { delegate.start() } - + @objc private func onRestartClick(_ sender: Any?) { delegate.stop() delegate.start() } - - public func updateListen(_ listen: String?) { - if listen == nil { - statusListenItem.isHidden = true - return - } - statusListenItem.isHidden = false - statusListenItem.attributedTitle = NSAttributedString(string: "\(listen!.replacingOccurrences(of: ";", with: "\n"))") - } - + public func toOffState() { self.statusActionOnItem.isEnabled = true self.statusActionOffItem.isEnabled = false self.statusActionRestartItem.isEnabled = false self.statusBarItem.button?.image = inActiveImg } - + public func toOnState() { self.statusActionOnItem.isEnabled = false self.statusActionOffItem.isEnabled = true self.statusActionRestartItem.isEnabled = true self.statusBarItem.button?.image = activeImg } - + func menuWillOpen(_ menu: NSMenu) { - updateArgsMenuItem() - } - - private func updateArgsMenuItem() { - argsItem.title = NSLocalizedString("Configuration", comment: "") - argsItem.image = NSImage(systemSymbolName: "doc.text", accessibilityDescription: "") - argsItem.toolTip = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML - argsItem.submenu = nil + rebuildProfileSubmenu() } } diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index be1fcb0..ba03a47 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -8,22 +8,166 @@ let yamlKeyRule = try! NSRegularExpression( options: reOpts) let yamlCommentRule = try! NSRegularExpression(pattern: "^\\s*#.*", options: reOpts) +@available(macOS 14.0, *) struct SettingsView: View { + @StateObject private var repo = ConfigRepository.shared + @State private var selectedProfileId: String? = nil + @State private var showAddSheet = false + @State private var showRenameSheet = false + @State private var newProfileName = "" + @State private var renameTargetId: String? = nil + @State private var selectedTab = 0 + var body: some View { - TabView { - YamlConfigView() + TabView(selection: $selectedTab) { + profilesTab + .tabItem { + Label(NSLocalizedString("Profiles", comment: ""), systemImage: "doc.text") + } + .tag(0) + FileManageView() .tabItem { - Label(NSLocalizedString("Configuration", comment: ""), systemImage: "doc.text") + Label(NSLocalizedString("Files", comment: ""), systemImage: "folder") } - .tag("config") + .tag(1) } - .padding(5) + .frame(minWidth: 700, minHeight: 440) + } + + private var profilesTab: some View { + NavigationSplitView { + VStack(spacing: 0) { + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + HStack(spacing: 8) { + Image(systemName: "doc.text") + .foregroundColor(.secondary) + .font(.system(size: 13)) + Text(profile.name) + .font(.system(size: 13)) + .lineLimit(1) + } + .padding(.vertical, 3) + .tag(profile.id) + .contextMenu { + Button(NSLocalizedString("Rename...", comment: "")) { + renameTargetId = profile.id + newProfileName = profile.name + showRenameSheet = true + } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { + repo.deleteProfile(profile.id) + } + } + } + } + .listStyle(.sidebar) + + Divider() + + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + .font(.system(size: 12)) + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.borderless) + .padding(.horizontal, 12) + .padding(.vertical, 8) + } + .frame(minWidth: 200) + .navigationSplitViewColumnWidth(min: 180, ideal: 220) + } detail: { + if let profileId = selectedProfileId { + YamlEditorView(profileId: profileId) + .id(profileId) + } else { + VStack { + Image(systemName: "doc.text.magnifyingglass") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Select a profile to edit", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .navigationTitle(selectedProfileId.flatMap { id in + repo.profiles.first { $0.id == id }?.name + } ?? NSLocalizedString("Settings", comment: "")) + .toolbar { ToolbarItem { Spacer() } } + .onAppear { + if selectedProfileId == nil, let first = repo.profiles.first { + selectedProfileId = first.id + } + } + .sheet(isPresented: $showAddSheet) { + addProfileSheet + } + .sheet(isPresented: $showRenameSheet) { + renameProfileSheet + } + } + + private var addProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("New Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showAddSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Add", comment: "")) { + if !newProfileName.isEmpty { + let newId = repo.addProfile(name: newProfileName) + if let id = newId { selectedProfileId = id } + newProfileName = "" + showAddSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } + + private var renameProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showRenameSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let id = renameTargetId, !newProfileName.isEmpty { + _ = repo.renameProfile(id, newName: newProfileName) + newProfileName = "" + renameTargetId = nil + showRenameSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) } } -struct YamlConfigView: View { - @AppStorage(defaultsYamlKey) - private var yamlConfig = defaultGostYAML +// MARK: - YAML Editor View + +@available(macOS 14.0, *) +struct YamlEditorView: View { + let profileId: String + @StateObject private var repo = ConfigRepository.shared + @State private var yamlText: String = "" private let rules: [HighlightRule] = [ HighlightRule( @@ -40,25 +184,39 @@ struct YamlConfigView: View { ] var body: some View { - VStack { - HighlightedTextEditor(text: $yamlConfig, highlightRules: rules) + VStack(spacing: 0) { + HighlightedTextEditor(text: $yamlText, highlightRules: rules) .introspect { editor in editor.textView.allowsUndo = true editor.textView.breakUndoCoalescing() editor.textView.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) } - .onChange(of: yamlConfig) { newValue in - // Sync to App Group for VPN Extension - AppGroupConfig.writeYaml(newValue) + .onChange(of: yamlText) { newValue in + save() } + + Divider() + Text("gost v3 YAML configuration — https://gost.run/docs/") - .padding(.horizontal, 5) - .font(Font.system(size: 12)) - .foregroundColor(.gray) + .font(.system(size: 10)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + .padding(.horizontal, 8) + .padding(.vertical, 4) + } + .onAppear { + yamlText = repo.getConfig(profileId) + } + } + + private func save() { + repo.saveConfig(profileId, yaml: yamlText) + if profileId == repo.activeProfileId { + AppGroupConfig.writeYaml(yamlText) } } } +@available(macOS 14.0, *) struct SettingsView_Previews: PreviewProvider { static var previews: some View { SettingsView() From 89ade614f42bb7877f614a34d0d29b660371884d Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 07:01:27 +0800 Subject: [PATCH 23/89] fix: set work directory to files/ subdirectory for bypass file resolution Co-Authored-By: Claude --- macos/GostXTunnel/PacketTunnelProvider.swift | 51 +++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/macos/GostXTunnel/PacketTunnelProvider.swift b/macos/GostXTunnel/PacketTunnelProvider.swift index 5df6a1f..6dfb260 100644 --- a/macos/GostXTunnel/PacketTunnelProvider.swift +++ b/macos/GostXTunnel/PacketTunnelProvider.swift @@ -1,15 +1,22 @@ // macos/GostXTunnel/PacketTunnelProvider.swift import NetworkExtension import Libgost +import os class PacketTunnelProvider: NEPacketTunnelProvider { override func startTunnel(options: [String: NSObject]?) async throws { + os_log(.default, "[GostX] startTunnel called") + // 1. 设置工作目录和日志 setWorkDirAndLog() + os_log(.default, "[GostX] workDir and log set, containerURL=%@", AppGroupConfig.containerURL?.path ?? "nil") // 2. 从 App Group 读取 YAML 配置 - guard let yaml = AppGroupConfig.readYaml(), !yaml.isEmpty else { + let yaml = AppGroupConfig.readYaml() + os_log(.default, "[GostX] yaml read, length=%d", yaml?.count ?? 0) + guard let yaml = yaml, !yaml.isEmpty else { + os_log(.error, "[GostX] no YAML config") cancelTunnelWithError(NSError( domain: "GostXTunnel", code: 1, @@ -19,8 +26,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } // 3. 启动 gost 服务 + os_log(.default, "[GostX] starting gost...") var err: NSError? - if !LibgostStartGost(yaml, "", &err), let err { + let ok = LibgostStartGost(yaml, "", &err) + os_log(.default, "[GostX] StartGost returned ok=%d err=%{public}@", ok, err?.localizedDescription ?? "nil") + if !ok, let err { cancelTunnelWithError(NSError( domain: "GostXTunnel.startGost", code: 2, @@ -29,22 +39,22 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return } - // 4. 配置 TUN 网络设置(系统自动创建 utun 接口) - let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1") + // 4. 配置 TUN 网络设置 + os_log(.default, "[GostX] setting network settings...") + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") settings.mtu = 1500 - - let ipv4 = NEIPv4Settings( - addresses: ["10.0.0.2"], - subnetMasks: ["255.255.255.0"] - ) + // Both 10.0.0.2 and 10.0.0.3 must be registered so the kernel + // accepts packets the system stack rewrites (source→10.0.0.3). + let ipv4 = NEIPv4Settings(addresses: ["10.0.0.2", "10.0.0.3"], subnetMasks: ["255.255.255.0", "255.255.255.0"]) ipv4.includedRoutes = [NEIPv4Route.default()] settings.ipv4Settings = ipv4 - settings.dnsSettings = NEDNSSettings(servers: ["10.0.0.3"]) do { try await setTunnelNetworkSettings(settings) + os_log(.default, "[GostX] network settings applied") } catch { + os_log(.error, "[GostX] setTunnelNetworkSettings failed: %@", error.localizedDescription) LibgostStopGost(nil) cancelTunnelWithError(NSError( domain: "GostXTunnel.setNetworkSettings", @@ -56,6 +66,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { // 5. 扫描找到 utun fd let tunFd = LibgostGetTunnelFileDescriptor() + os_log(.default, "[GostX] tunFd=%d", tunFd) guard tunFd != -1 else { LibgostStopGost(nil) cancelTunnelWithError(NSError( @@ -66,8 +77,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return } - // 6. 启动 TUN stack(sing-tun 直接在 fd 上读写) + // 6. 启动 TUN stack + os_log(.default, "[GostX] starting TUN stack...") if !LibgostStartTun(Int(tunFd), 1500, &err), let err { + os_log(.error, "[GostX] StartTun failed: %@", err.localizedDescription) LibgostStopGost(nil) cancelTunnelWithError(NSError( domain: "GostXTunnel.startTun", @@ -76,20 +89,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider { )) return } + os_log(.default, "[GostX] tunnel started successfully!") } override func stopTunnel(with reason: NEProviderStopReason) async { + os_log(.default, "[GostX] stopTunnel, reason=%d", reason.rawValue) LibgostStopTun(nil) LibgostStopGost(nil) } - // MARK: - Private - private func setWorkDirAndLog() { - guard let containerURL = AppGroupConfig.containerURL else { return } - let workDir = containerURL.path - LibgostSetWorkDir(workDir, nil) - + guard let containerURL = AppGroupConfig.containerURL else { + os_log(.error, "[GostX] containerURL is nil!") + return + } + let filesDir = containerURL.appendingPathComponent("files") + try? FileManager.default.createDirectory(at: filesDir, + withIntermediateDirectories: true, attributes: nil) + LibgostSetWorkDir(filesDir.path, nil) let logFile = containerURL.appendingPathComponent("gost.log").path LibgostSetLogFile(logFile, nil) LibgostSetLogLevel("info") From 0613ec2602709842e3187add12a2fb679aba4fc5 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 08:38:44 +0800 Subject: [PATCH 24/89] fix: add @available annotation to OverwriteSheetModifier Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 7189510..efa6e57 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -311,6 +311,7 @@ private struct FileManageAlertModifier: ViewModifier { // MARK: - Overwrite Sheet Modifier +@available(macOS 14.0, *) private struct OverwriteSheetModifier: ViewModifier { @ObservedObject var vm: FileManageViewModel From e7e131c11433ae7174ae4e4b48d12ddc0b682c1b Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 08:49:08 +0800 Subject: [PATCH 25/89] fix: add User Selected File read-write sandbox entitlement for file import/export Required by NSOpenPanel (.fileImporter) and NSSavePanel (.fileExporter) in sandboxed macOS apps. Co-Authored-By: Claude --- macos/GostX/GostX.entitlements | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/macos/GostX/GostX.entitlements b/macos/GostX/GostX.entitlements index b1f6463..1a0a085 100644 --- a/macos/GostX/GostX.entitlements +++ b/macos/GostX/GostX.entitlements @@ -4,13 +4,19 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + group.cn.liukebin.gostx + + com.apple.security.files.user-selected.read-write + com.apple.security.network.client com.apple.security.network.server - com.apple.security.application-groups + com.apple.developer.networking.networkextension - group.cn.liukebin.gostx + packet-tunnel-provider From dd266082df186a3c0c7bb73907f4b0a6d4b3eaa9 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 08:50:43 +0800 Subject: [PATCH 26/89] fix: add security-scoped resource access for sandboxed file import .fileImporter provides security-scoped URLs that require startAccessingSecurityScopedResource() before reading in sandboxed apps. Also make ensureDir() throw instead of silently failing. Co-Authored-By: Claude --- macos/GostX/FileRepository.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/macos/GostX/FileRepository.swift b/macos/GostX/FileRepository.swift index 53c66e4..d0cc2ce 100644 --- a/macos/GostX/FileRepository.swift +++ b/macos/GostX/FileRepository.swift @@ -14,13 +14,13 @@ class FileRepository { init?() { guard let container = AppGroupConfig.containerURL else { return nil } workDir = container.appendingPathComponent("files") - ensureDir() + try? ensureDir() } // MARK: - Directory - func ensureDir() { - try? FileManager.default.createDirectory(at: workDir, + func ensureDir() throws { + try FileManager.default.createDirectory(at: workDir, withIntermediateDirectories: true, attributes: nil) } @@ -75,6 +75,14 @@ class FileRepository { throw FileRepositoryError.notAFile(name) } + // Security-scoped bookmark: .fileImporter provides URLs that require + // explicit access in sandboxed apps. + let accessed = sourceURL.startAccessingSecurityScopedResource() + defer { if accessed { sourceURL.stopAccessingSecurityScopedResource() } } + + // Ensure the files/ directory exists before copying. + try ensureDir() + let target = workDir.appendingPathComponent(trimmed) // Remove existing file before copy if FileManager.default.fileExists(atPath: target.path) { From a00f5b8fab70dcb9a6ac6cd26f92dd31505170a1 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 08:55:11 +0800 Subject: [PATCH 27/89] =?UTF-8?q?style:=20refine=20file=20manager=20UI=20?= =?UTF-8?q?=E2=80=94=20plain=20list=20style,=20context=20menu,=20compact?= =?UTF-8?q?=20toolbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Profiles tab: hide scroll background for cleaner sidebar look - Files tab: remove header text, replace ellipsis button with contextMenu - Files tab: add compact import button at bottom, plain list style Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 55 ++++++++++++-------------------- macos/GostX/SettingsView.swift | 1 + 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index efa6e57..d120ab7 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -175,24 +175,6 @@ struct FileManageView: View { private var fileListView: some View { VStack(spacing: 0) { - // Toolbar - HStack { - Text(NSLocalizedString("Bypass Files", comment: "")) - .font(.system(size: 12, weight: .semibold)) - .foregroundColor(.secondary) - Spacer() - Button(action: { showImporter = true }) { - Image(systemName: "plus") - .font(.system(size: 12, weight: .medium)) - } - .buttonStyle(.borderless) - } - .padding(.horizontal, 12) - .padding(.vertical, 8) - - Divider() - - // File List List { ForEach(vm.files) { file in FileRowView( @@ -213,7 +195,20 @@ struct FileManageView: View { ) } } - .listStyle(.inset) + .listStyle(.plain) + + Divider() + + HStack { + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import File", comment: ""), systemImage: "plus") + .font(.system(size: 12)) + } + .buttonStyle(.borderless) + Spacer() + } + .padding(.horizontal, 12) + .padding(.vertical, 6) } .frame(minWidth: 300, minHeight: 200) .fileExporter( @@ -266,23 +261,15 @@ private struct FileRowView: View { } Spacer() - - // Context menu via right-click - Menu { - Button(NSLocalizedString("Export...", comment: "")) { onExport() } - Button(NSLocalizedString("Rename...", comment: "")) { onRename() } - Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } - Divider() - Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } - } label: { - Image(systemName: "ellipsis.circle") - .font(.system(size: 12)) - .foregroundColor(.secondary) - } - .menuStyle(.borderlessButton) - .frame(width: 24) } .padding(.vertical, 4) + .contextMenu { + Button(NSLocalizedString("Export...", comment: "")) { onExport() } + Button(NSLocalizedString("Rename...", comment: "")) { onRename() } + Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } + } } } diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index ba03a47..ecc2932 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -63,6 +63,7 @@ struct SettingsView: View { } } .listStyle(.sidebar) + .scrollContentBackground(.hidden) Divider() From 97ff6e6c6a42fa0d84eef9b986140c604cf151a3 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 08:57:52 +0800 Subject: [PATCH 28/89] style: fix settings window title to GostX, align sidebar with editor using plain list style Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index ecc2932..b4302af 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -62,7 +62,7 @@ struct SettingsView: View { } } } - .listStyle(.sidebar) + .listStyle(.plain) .scrollContentBackground(.hidden) Divider() @@ -94,9 +94,7 @@ struct SettingsView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } - .navigationTitle(selectedProfileId.flatMap { id in - repo.profiles.first { $0.id == id }?.name - } ?? NSLocalizedString("Settings", comment: "")) + .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") .toolbar { ToolbarItem { Spacer() } } .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { From 2aea2b7453910328f67f4c78241eb4fc07ec8cee Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:01:21 +0800 Subject: [PATCH 29/89] style: center window title via toolbar, restore native sidebar style - Use ToolbarItem(placement: .principal) for centered GostX title - Remove window.title from MacExtrasConfigurator (now handled by SwiftUI toolbar) - Restore .sidebar list style for native macOS sidebar appearance Co-Authored-By: Claude --- macos/GostX/MacExtrasConfigurator.swift | 1 - macos/GostX/SettingsView.swift | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 0bd5d61..2267b77 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -156,7 +156,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "Settings" window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] window.titlebarAppearsTransparent = true window.titlebarSeparatorStyle = .automatic diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index b4302af..e7b7dff 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -62,7 +62,7 @@ struct SettingsView: View { } } } - .listStyle(.plain) + .listStyle(.sidebar) .scrollContentBackground(.hidden) Divider() @@ -94,8 +94,13 @@ struct SettingsView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } - .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") - .toolbar { ToolbarItem { Spacer() } } + .navigationTitle("") + .toolbar { + ToolbarItem(placement: .principal) { + Text(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") + .font(.system(size: 13, weight: .semibold)) + } + } .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { selectedProfileId = first.id From f32020a02e4895b662e6718fb40822730ae4499b Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:02:58 +0800 Subject: [PATCH 30/89] =?UTF-8?q?fix:=20revert=20to=20clean=20layout=20?= =?UTF-8?q?=E2=80=94=20native=20sidebar,=20fixed=20GostX=20title,=20restor?= =?UTF-8?q?e=20window.title?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove scrollContentBackground(.hidden) — caused visual glitches - Restore window.title in MacExtrasConfigurator - Use fixed .navigationTitle("GostX") instead of dynamic profile name Co-Authored-By: Claude --- macos/GostX/MacExtrasConfigurator.swift | 1 + macos/GostX/SettingsView.swift | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 2267b77..7319302 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -156,6 +156,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) + window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] window.titlebarAppearsTransparent = true window.titlebarSeparatorStyle = .automatic diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index e7b7dff..b1693dc 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -63,7 +63,6 @@ struct SettingsView: View { } } .listStyle(.sidebar) - .scrollContentBackground(.hidden) Divider() @@ -94,13 +93,7 @@ struct SettingsView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } - .navigationTitle("") - .toolbar { - ToolbarItem(placement: .principal) { - Text(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") - .font(.system(size: 13, weight: .semibold)) - } - } + .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { selectedProfileId = first.id From 41c317a8df541f0ccd93341c05788a564f6e1ea4 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:04:44 +0800 Subject: [PATCH 31/89] =?UTF-8?q?style:=20match=20sidebar=20background=20t?= =?UTF-8?q?o=20editor=20=E2=80=94=20unify=20NavigationSplitView=20appearan?= =?UTF-8?q?ce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use textBackgroundColor for both sidebar and detail pane backgrounds to create visual alignment between the profile list and YAML editor. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index b1693dc..221d579 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -63,6 +63,8 @@ struct SettingsView: View { } } .listStyle(.sidebar) + .scrollContentBackground(.hidden) + .background(Color(nsColor: .textBackgroundColor)) Divider() @@ -81,6 +83,7 @@ struct SettingsView: View { if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) + .background(Color(nsColor: .textBackgroundColor)) } else { VStack { Image(systemName: "doc.text.magnifyingglass") @@ -91,6 +94,7 @@ struct SettingsView: View { .foregroundColor(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(nsColor: .textBackgroundColor)) } } .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") From 8dcb21337ebdadbf82e5336fe73b981e02deedd9 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:06:37 +0800 Subject: [PATCH 32/89] fix: apply unified background to sidebar VStack, remove detail background overrides The sidebar background now covers the full column height (List + Divider + Add Profile button area), matching the editor's text background color. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 221d579..27202cd 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -64,7 +64,6 @@ struct SettingsView: View { } .listStyle(.sidebar) .scrollContentBackground(.hidden) - .background(Color(nsColor: .textBackgroundColor)) Divider() @@ -78,12 +77,12 @@ struct SettingsView: View { .padding(.vertical, 8) } .frame(minWidth: 200) + .background(Color(nsColor: .textBackgroundColor)) .navigationSplitViewColumnWidth(min: 180, ideal: 220) } detail: { if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) - .background(Color(nsColor: .textBackgroundColor)) } else { VStack { Image(systemName: "doc.text.magnifyingglass") @@ -94,7 +93,6 @@ struct SettingsView: View { .foregroundColor(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(nsColor: .textBackgroundColor)) } } .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") From 79af85b4dea0a8e3c3694ccd86baf0d98b6a4a2c Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:09:53 +0800 Subject: [PATCH 33/89] style: use Apple-recommended .listStyle(.plain) to unify sidebar and editor appearance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Apple Developer Forums guidance, .listStyle(.plain) removes the source-list vibrancy/material effect from the sidebar, making it match the detail area. Remove all custom backgrounds — let the system handle styling. Use standard Label for sidebar items and safeAreaInset for the Add button. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 27202cd..e771b51 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -36,18 +36,9 @@ struct SettingsView: View { private var profilesTab: some View { NavigationSplitView { - VStack(spacing: 0) { - List(selection: $selectedProfileId) { - ForEach(repo.profiles) { profile in - HStack(spacing: 8) { - Image(systemName: "doc.text") - .foregroundColor(.secondary) - .font(.system(size: 13)) - Text(profile.name) - .font(.system(size: 13)) - .lineLimit(1) - } - .padding(.vertical, 3) + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + Label(profile.name, systemImage: "doc.text") .tag(profile.id) .contextMenu { Button(NSLocalizedString("Rename...", comment: "")) { @@ -60,24 +51,19 @@ struct SettingsView: View { repo.deleteProfile(profile.id) } } - } } - .listStyle(.sidebar) - .scrollContentBackground(.hidden) - + } + .listStyle(.plain) + .safeAreaInset(edge: .bottom) { Divider() - Button(action: { showAddSheet = true }) { Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") - .font(.system(size: 12)) .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.borderless) .padding(.horizontal, 12) - .padding(.vertical, 8) + .padding(.vertical, 6) } - .frame(minWidth: 200) - .background(Color(nsColor: .textBackgroundColor)) .navigationSplitViewColumnWidth(min: 180, ideal: 220) } detail: { if let profileId = selectedProfileId { From dbee8bce1f95f310328472581938b0c8268f86b6 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:11:59 +0800 Subject: [PATCH 34/89] fix: align editor top edge with sidebar list items via padding Add .padding(.top, 8) to detail content to match the List's intrinsic top inset. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index e771b51..6891261 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -69,6 +69,7 @@ struct SettingsView: View { if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) + .padding(.top, 8) } else { VStack { Image(systemName: "doc.text.magnifyingglass") @@ -79,6 +80,7 @@ struct SettingsView: View { .foregroundColor(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding(.top, 8) } } .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") From 47c3851bc3cbc0482122de1c48942f0642541c00 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:13:51 +0800 Subject: [PATCH 35/89] fix: move Add Profile button to toolbar, remove bottom safe area Toolbar buttons don't extend the sidebar beyond the editor area. Both sidebar and editor now share the same bottom edge. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 6891261..cec08f4 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -54,16 +54,6 @@ struct SettingsView: View { } } .listStyle(.plain) - .safeAreaInset(edge: .bottom) { - Divider() - Button(action: { showAddSheet = true }) { - Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") - .frame(maxWidth: .infinity, alignment: .leading) - } - .buttonStyle(.borderless) - .padding(.horizontal, 12) - .padding(.vertical, 6) - } .navigationSplitViewColumnWidth(min: 180, ideal: 220) } detail: { if let profileId = selectedProfileId { @@ -84,6 +74,13 @@ struct SettingsView: View { } } .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + } + } + } .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { selectedProfileId = first.id From 1001862cb8a7e114a8cb285c50920a74855c1912 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 09:53:00 +0800 Subject: [PATCH 36/89] fix: restore Add Profile button to sidebar, make editor fill available height Editor now uses .frame(maxHeight: .infinity) to match sidebar's vertical span. Add button stays in sidebar below the list as before. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 50 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index cec08f4..5874b4d 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -36,24 +36,36 @@ struct SettingsView: View { private var profilesTab: some View { NavigationSplitView { - List(selection: $selectedProfileId) { - ForEach(repo.profiles) { profile in - Label(profile.name, systemImage: "doc.text") - .tag(profile.id) - .contextMenu { - Button(NSLocalizedString("Rename...", comment: "")) { - renameTargetId = profile.id - newProfileName = profile.name - showRenameSheet = true + VStack(spacing: 0) { + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + Label(profile.name, systemImage: "doc.text") + .tag(profile.id) + .contextMenu { + Button(NSLocalizedString("Rename...", comment: "")) { + renameTargetId = profile.id + newProfileName = profile.name + showRenameSheet = true + } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { + repo.deleteProfile(profile.id) + } } - Divider() - Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { - repo.deleteProfile(profile.id) - } - } + } + } + .listStyle(.plain) + + Divider() + + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + .frame(maxWidth: .infinity, alignment: .leading) } + .buttonStyle(.borderless) + .padding(.horizontal, 12) + .padding(.vertical, 6) } - .listStyle(.plain) .navigationSplitViewColumnWidth(min: 180, ideal: 220) } detail: { if let profileId = selectedProfileId { @@ -74,13 +86,6 @@ struct SettingsView: View { } } .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button(action: { showAddSheet = true }) { - Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") - } - } - } .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { selectedProfileId = first.id @@ -178,6 +183,7 @@ struct YamlEditorView: View { .onChange(of: yamlText) { newValue in save() } + .frame(maxWidth: .infinity, maxHeight: .infinity) Divider() From 9e5297ddc5981b970eabb515f59ca264d0914322 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:00:19 +0800 Subject: [PATCH 37/89] fix: move navigationTitle to TabView level for consistent title across tabs Previously the title was on the profilesTab's NavigationSplitView, causing it to disappear when switching to the Files tab. Co-Authored-By: Claude --- macos/GostX/SettingsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 5874b4d..8a8984f 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -31,6 +31,7 @@ struct SettingsView: View { } .tag(1) } + .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") .frame(minWidth: 700, minHeight: 440) } @@ -85,7 +86,6 @@ struct SettingsView: View { .padding(.top, 8) } } - .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") .onAppear { if selectedProfileId == nil, let first = repo.profiles.first { selectedProfileId = first.id From 6ad4f79ffb007404d7294eff7d70f4b7e6774f2f Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:00:33 +0800 Subject: [PATCH 38/89] fix: remove window.title to avoid conflict with SwiftUI navigationTitle With unified toolbar + fullSizeContentView, the SwiftUI .navigationTitle() on TabView handles the window title. The AppKit window.title would conflict. Co-Authored-By: Claude --- macos/GostX/MacExtrasConfigurator.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 7319302..2267b77 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -156,7 +156,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] window.titlebarAppearsTransparent = true window.titlebarSeparatorStyle = .automatic From 88cba33df803b542e07e26c6448f37e4b54f7d81 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:06:26 +0800 Subject: [PATCH 39/89] style: simplify settings window to standard titled window per macOS HIG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The window previously combined fullSizeContentView + transparent titlebar + .unified toolbar + SwiftUI .navigationTitle + TabView's own tab bar — three elements competing for the titlebar area caused overlapping/messy chrome. Now uses a standard titled window: title bar shows 'GostX' natively (left-aligned), TabView tab bar renders cleanly below it. Removed the .navigationTitle and the .padding(.top, 8) alignment hacks that existed to fight the transparent titlebar. Co-Authored-By: Claude --- macos/GostX/MacExtrasConfigurator.swift | 6 ++---- macos/GostX/SettingsView.swift | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 2267b77..296697b 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -156,10 +156,8 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] - window.titlebarAppearsTransparent = true - window.titlebarSeparatorStyle = .automatic - window.toolbarStyle = .unified + window.styleMask = [.titled, .closable, .miniaturizable, .resizable] + window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.setContentSize(NSSize(width: 780, height: 500)) window.center() window.orderFrontRegardless() diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 8a8984f..ae141d6 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -31,7 +31,6 @@ struct SettingsView: View { } .tag(1) } - .navigationTitle(Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX") .frame(minWidth: 700, minHeight: 440) } @@ -72,7 +71,6 @@ struct SettingsView: View { if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) - .padding(.top, 8) } else { VStack { Image(systemName: "doc.text.magnifyingglass") @@ -83,7 +81,6 @@ struct SettingsView: View { .foregroundColor(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .padding(.top, 8) } } .onAppear { From 619c5f801114f761197ae018e4b84bad4bcfafa6 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:16:18 +0800 Subject: [PATCH 40/89] style: make sidebar fill column height, remove Files list background - Sidebar VStack now fills the column so its bottom edge matches the editor - Files tab List uses .scrollContentBackground(.hidden) for a cleaner look Co-Authored-By: Claude --- macos/GostX/FileManageView.swift | 1 + macos/GostX/SettingsView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index d120ab7..2447d0b 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -196,6 +196,7 @@ struct FileManageView: View { } } .listStyle(.plain) + .scrollContentBackground(.hidden) Divider() diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index ae141d6..c12d8f6 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -66,6 +66,7 @@ struct SettingsView: View { .padding(.horizontal, 12) .padding(.vertical, 6) } + .frame(maxHeight: .infinity) .navigationSplitViewColumnWidth(min: 180, ideal: 220) } detail: { if let profileId = selectedProfileId { From 7e2ffbef70057482469753c9069d241754757ec5 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:25:32 +0800 Subject: [PATCH 41/89] docs: add Makefile macos target design spec Co-Authored-By: Claude --- ...2026-07-14-makefile-macos-target-design.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-14-makefile-macos-target-design.md diff --git a/docs/superpowers/specs/2026-07-14-makefile-macos-target-design.md b/docs/superpowers/specs/2026-07-14-makefile-macos-target-design.md new file mode 100644 index 0000000..687acba --- /dev/null +++ b/docs/superpowers/specs/2026-07-14-makefile-macos-target-design.md @@ -0,0 +1,79 @@ +# Makefile macOS Target Design + +## Overview + +Refactor the macOS build flow in Makefiles to follow the same pattern as Android: root Makefile owns the full build pipeline (build dependency → copy artifact → invoke platform build tool), while `libgost/Makefile` is only responsible for producing the artifact. + +## Changes + +### libgost/Makefile + +#### Rename `macos-framework` → `macos-xcframework` + +Strip the copy/move logic. This target only builds `Libgost.xcframework` in the `libgost/` directory. + +```makefile +macos-xcframework: + CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -tags with_gvisor \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o Libgost.xcframework \ + . +``` + +**Removed lines:** `mkdir -p ../macos/Frameworks`, `rm -rf ../macos/Frameworks/Libgost.xcframework`, `mv Libgost.xcframework ../macos/Frameworks/Libgost.xcframework`. + +#### Update `.PHONY` + +Replace `macos-framework` with `macos-xcframework`. + +#### Update `clean` + +Remove `rm -rf ../macos/Frameworks/Libgost.xcframework`. Only clean artifacts within `libgost/`: +```makefile +clean: + rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip Libgost.xcframework + rm -rf _debug_tmp +``` + +### Root Makefile + +#### Add `macos` target + +```makefile +macos: macos/Frameworks/Libgost.xcframework + cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release build +``` + +#### Add framework build+copy rule + +```makefile +macos/Frameworks/Libgost.xcframework: + cd libgost && $(MAKE) macos-xcframework + mkdir -p macos/Frameworks + rm -rf macos/Frameworks/Libgost.xcframework + cp -R libgost/Libgost.xcframework macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" +``` + +#### Update `clean` + +Add macOS framework cleanup: +```makefile +clean: + cd libgost && $(MAKE) clean + cd android && ./gradlew clean 2>/dev/null || true + rm -rf macos/Frameworks/Libgost.xcframework +``` + +## Target Comparison + +| | Android | macOS (new) | +|---|---|---| +| libgost build target | `libgost.aar` | `macos-xcframework` | +| Artifact location | `libgost/libgost.aar` | `libgost/Libgost.xcframework` | +| Root target | `android` | `macos` | +| Copy destination | `android/app/libs/` | `macos/Frameworks/` | +| Platform build | `./gradlew assembleDebug` | `xcodebuild … build` | +| Clean | `./gradlew clean` | (removes `macos/Frameworks/`) | From ba451a9fae369a422873a3f9ae98bb752a6f0481 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:26:19 +0800 Subject: [PATCH 42/89] docs: add Makefile macos target implementation plan Co-Authored-By: Claude --- .../plans/2026-07-14-makefile-macos-target.md | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-14-makefile-macos-target.md diff --git a/docs/superpowers/plans/2026-07-14-makefile-macos-target.md b/docs/superpowers/plans/2026-07-14-makefile-macos-target.md new file mode 100644 index 0000000..d1fc947 --- /dev/null +++ b/docs/superpowers/plans/2026-07-14-makefile-macos-target.md @@ -0,0 +1,232 @@ +# Makefile macOS Target Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add `macos` target to root Makefile that builds `Libgost.xcframework` via `libgost/Makefile`, copies it to `macos/Frameworks/`, then runs `xcodebuild`. + +**Architecture:** `libgost/Makefile` only builds the artifact (`macos-xcframework` target). Root `Makefile` owns the full pipeline: build dependency → copy artifact → invoke Xcode. Same pattern as the existing `android` target → `libgost.aar`. + +**Tech Stack:** GNU Make, gomobile, xcodebuild + +## Global Constraints + +- `libgost/Makefile` must NOT copy files outside `libgost/` directory +- Root `Makefile` handles all file copying to `macos/Frameworks/` +- Target name in libgost: `macos-xcframework` (renamed from `macos-framework`) +- Build flags for gomobile unchanged: `-tags with_gvisor -target macos -trimpath -ldflags="-s -w"` + +--- + +### Task 1: Refactor libgost/Makefile — rename target and strip copy logic + +**Files:** +- Modify: `libgost/Makefile` + +**Interfaces:** +- Produces: `macos-xcframework` phony target — builds `Libgost.xcframework` in `libgost/` directory only + +- [ ] **Step 1: Update `.PHONY` declaration** + +Replace `macos-framework` with `macos-xcframework`. + +In `libgost/Makefile`, line 4, change: + +```makefile +.PHONY: all clean debug-symbols macos-framework +``` + +To: + +```makefile +.PHONY: all clean debug-symbols macos-xcframework +``` + +- [ ] **Step 2: Rename target and remove copy/move logic** + +Replace the `macos-framework` target (lines 53-63) with `macos-xcframework` that only builds, no copy. + +Remove: +```makefile +macos-framework: + CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -tags with_gvisor \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o Libgost.xcframework \ + . + mkdir -p ../macos/Frameworks + rm -rf ../macos/Frameworks/Libgost.xcframework + mv Libgost.xcframework ../macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" +``` + +Add: +```makefile +macos-xcframework: + CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -tags with_gvisor \ + -target macos \ + -trimpath -ldflags="-s -w" \ + -o Libgost.xcframework \ + . +``` + +- [ ] **Step 3: Update `clean` target to remove cross-directory cleanup** + +Replace the `clean` target (lines 65-68): + +Remove: +```makefile +clean: + rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip + rm -rf _debug_tmp + rm -rf ../macos/Frameworks/Libgost.xcframework +``` + +Add: +```makefile +clean: + rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip Libgost.xcframework + rm -rf _debug_tmp +``` + +- [ ] **Step 4: Commit** + +```bash +git add libgost/Makefile +git commit -m "refactor: rename macos-framework to macos-xcframework, strip copy logic + +- macos-xcframework only builds Libgost.xcframework in libgost/ +- Copy responsibility moved to root Makefile +- clean no longer removes ../macos/Frameworks/ + +Co-Authored-By: Claude " +``` + +--- + +### Task 2: Add macos target and copy rule to root Makefile + +**Files:** +- Modify: `Makefile` + +**Interfaces:** +- Consumes: `macos-xcframework` phony target from `libgost/Makefile` (Task 1) +- Produces: `macos` phony target — builds framework, copies to `macos/Frameworks/`, runs xcodebuild + +- [ ] **Step 1: Add framework build+copy rule** + +In root `Makefile`, add after the `libgost/libgost.aar:` rule (after line 13): + +```makefile +# macOS .xcframework via gomobile bind → libgost/Makefile +macos/Frameworks/Libgost.xcframework: + cd libgost && $(MAKE) macos-xcframework + mkdir -p macos/Frameworks + rm -rf macos/Frameworks/Libgost.xcframework + cp -R libgost/Libgost.xcframework macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" +``` + +- [ ] **Step 2: Add `macos` target** + +In root `Makefile`, add after the `android-release` target (after line 9): + +```makefile +macos: macos/Frameworks/Libgost.xcframework + cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release build +``` + +- [ ] **Step 3: Update `.PHONY` to include `macos`** + +In root `Makefile`, line 1, change: + +```makefile +.PHONY: all android android-release clean +``` + +To: + +```makefile +.PHONY: all android android-release macos clean +``` + +- [ ] **Step 4: Update `clean` target to remove macOS framework** + +Replace the `clean` target (lines 14-17): + +Remove: +```makefile +clean: + cd libgost && $(MAKE) clean + cd android && ./gradlew clean 2>/dev/null || true +``` + +Add: +```makefile +clean: + cd libgost && $(MAKE) clean + cd android && ./gradlew clean 2>/dev/null || true + rm -rf macos/Frameworks/Libgost.xcframework +``` + +- [ ] **Step 5: Commit** + +```bash +git add Makefile +git commit -m "feat: add macos target to root Makefile + +- macos target builds Libgost.xcframework, copies to macos/Frameworks/, then runs xcodebuild +- mirrors android target pattern (build dep → copy → platform build) +- clean removes macos/Frameworks/Libgost.xcframework + +Co-Authored-By: Claude " +``` + +--- + +### Task 3: Verify the changes + +- [ ] **Step 1: Verify `make -n macos` shows correct dry-run output** + +```bash +cd /Users/kbliu/Workspace/project/GostX && make -n macos 2>&1 | head -20 +``` + +Expected: Shows `cd libgost && make macos-xcframework`, then `mkdir -p macos/Frameworks`, `rm -rf`, `cp -R`, then `xcodebuild` command. No errors. + +- [ ] **Step 2: Verify `make macos-xcframework` builds successfully in libgost** + +```bash +cd /Users/kbliu/Workspace/project/GostX/libgost && make macos-xcframework +``` + +Expected: `Libgost.xcframework` created in `libgost/` directory. No `mkdir`/`mv` output about `../macos/Frameworks`. + +- [ ] **Step 3: Verify framework is not in macos/Frameworks after libgost build** + +```bash +ls /Users/kbliu/Workspace/project/GostX/macos/Frameworks/Libgost.xcframework 2>&1 +``` + +Expected: File not found (unless it already existed from a previous build). + +- [ ] **Step 4: Verify `make clean` in libgost removes the built framework** + +```bash +cd /Users/kbliu/Workspace/project/GostX/libgost && make clean && ls Libgost.xcframework 2>&1 +``` + +Expected: `make clean` succeeds, `ls` reports "No such file or directory". + +- [ ] **Step 5: Verify `make clean` in root removes macos/Frameworks** + +```bash +cd /Users/kbliu/Workspace/project/GostX && make clean +``` + +Expected: Cleans libgost, android (if gradle available), and removes `macos/Frameworks/Libgost.xcframework`. + +- [ ] **Step 6: Commit (if any fixes were needed)** + +Only if changes were made during verification. From 601079af9ffc477260e3f732b25cc5d40f8e6bb3 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:30:13 +0800 Subject: [PATCH 43/89] refactor: rename macos-framework to macos-xcframework, strip copy logic - macos-xcframework only builds Libgost.xcframework in libgost/ - Copy responsibility moved to root Makefile - clean no longer removes ../macos/Frameworks/ Co-Authored-By: Claude --- libgost/Makefile | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libgost/Makefile b/libgost/Makefile index 56cf3a7..63fbfba 100644 --- a/libgost/Makefile +++ b/libgost/Makefile @@ -1,7 +1,7 @@ GO := go GOMOBILE := gomobile -.PHONY: all clean debug-symbols macos-framework +.PHONY: all clean debug-symbols macos-xcframework all: libgost.aar @@ -50,18 +50,14 @@ debug-symbols: # macOS .xcframework via gomobile bind # Produces Frameworks/Libgost.xcframework with macos-arm64_x86_64 slices. # Xcode links this into the PacketTunnelProvider Extension target. -macos-framework: +macos-xcframework: CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ + -tags with_gvisor \ -target macos \ -trimpath -ldflags="-s -w" \ -o Libgost.xcframework \ . - mkdir -p ../macos/Frameworks - rm -rf ../macos/Frameworks/Libgost.xcframework - mv Libgost.xcframework ../macos/Frameworks/Libgost.xcframework - @echo "Libgost.xcframework → macos/Frameworks/" clean: - rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip + rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip Libgost.xcframework rm -rf _debug_tmp - rm -rf ../macos/Frameworks/Libgost.xcframework From a96cf3dfb1ea988fcea96cf7f097319b804ede62 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 10:34:59 +0800 Subject: [PATCH 44/89] fix: use rm -rf for Libgost.xcframework directory bundle in clean target, fix stale comment Libgost.xcframework is a directory bundle produced by gomobile bind, so rm -f would fail with 'is a directory'. Moved it to a separate rm -rf line in the clean target. Also fixed stale comment on the macos-xcframework target: s/Frameworks\/Libgost.xcframework/Libgost.xcframework/ Co-Authored-By: Claude --- libgost/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libgost/Makefile b/libgost/Makefile index 63fbfba..9e8cf73 100644 --- a/libgost/Makefile +++ b/libgost/Makefile @@ -48,7 +48,7 @@ debug-symbols: @echo "Debug symbols written to debug-symbols.zip" # macOS .xcframework via gomobile bind -# Produces Frameworks/Libgost.xcframework with macos-arm64_x86_64 slices. +# Produces Libgost.xcframework with macos-arm64_x86_64 slices. # Xcode links this into the PacketTunnelProvider Extension target. macos-xcframework: CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ @@ -59,5 +59,6 @@ macos-xcframework: . clean: - rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip Libgost.xcframework + rm -f libgost.aar libgost_debug.aar libgost-sources.jar debug-symbols.zip + rm -rf Libgost.xcframework rm -rf _debug_tmp From 2626ce5976833e40b7e7e034bf2ae40b009f01c8 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 11:23:18 +0800 Subject: [PATCH 45/89] feat: add macos target to root Makefile - macos target builds Libgost.xcframework, copies to macos/Frameworks/, then runs xcodebuild - mirrors android target pattern (build dep -> copy -> platform build) - clean removes macos/Frameworks/Libgost.xcframework Co-Authored-By: Claude --- Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f6da135..ac2f4b4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all android android-release clean +.PHONY: all android android-release macos clean all: android @@ -8,10 +8,21 @@ android: libgost/libgost.aar android-release: libgost/libgost.aar cd android && ./gradlew assembleRelease bundleRelease +macos: macos/Frameworks/Libgost.xcframework + cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release build + libgost/libgost.aar: cd libgost && $(MAKE) libgost.aar +macos/Frameworks/Libgost.xcframework: + cd libgost && $(MAKE) macos-xcframework + mkdir -p macos/Frameworks + rm -rf macos/Frameworks/Libgost.xcframework + cp -R libgost/Libgost.xcframework macos/Frameworks/Libgost.xcframework + @echo "Libgost.xcframework → macos/Frameworks/" + .PHONY: clean clean: cd libgost && $(MAKE) clean cd android && ./gradlew clean 2>/dev/null || true + rm -rf macos/Frameworks/Libgost.xcframework From 60ef70a274688b07e41475e637fcf238a448f209 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 18:36:06 +0800 Subject: [PATCH 46/89] feat: add -derivedDataPath build to macos xcodebuild - Output .app to macos/build/ instead of ~/Library/Developer/Xcode/DerivedData - Clean target removes macos/build - Remove duplicate .PHONY: clean declaration Co-Authored-By: Claude --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ac2f4b4..8e94f82 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ android-release: libgost/libgost.aar cd android && ./gradlew assembleRelease bundleRelease macos: macos/Frameworks/Libgost.xcframework - cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release build + cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release -derivedDataPath build build libgost/libgost.aar: cd libgost && $(MAKE) libgost.aar @@ -21,8 +21,8 @@ macos/Frameworks/Libgost.xcframework: cp -R libgost/Libgost.xcframework macos/Frameworks/Libgost.xcframework @echo "Libgost.xcframework → macos/Frameworks/" -.PHONY: clean clean: cd libgost && $(MAKE) clean cd android && ./gradlew clean 2>/dev/null || true rm -rf macos/Frameworks/Libgost.xcframework + rm -rf macos/build From 229dfec058c1485f23ce37c3164f1bab2d4c395f Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 18:40:48 +0800 Subject: [PATCH 47/89] feat: replace macOS app icon with Android launcher icon Generated all required sizes (16-1024px) from mipmap-xxxhdpi source. Co-Authored-By: Claude --- .../AppIcon.appiconset/1024.png | Bin 293554 -> 28322 bytes .../AppIcon.appiconset/128.png | Bin 15383 -> 4931 bytes .../Assets.xcassets/AppIcon.appiconset/16.png | Bin 697 -> 4022 bytes .../AppIcon.appiconset/256.png | Bin 41970 -> 6468 bytes .../Assets.xcassets/AppIcon.appiconset/32.png | Bin 1920 -> 4113 bytes .../AppIcon.appiconset/512.png | Bin 110342 -> 11978 bytes .../Assets.xcassets/AppIcon.appiconset/64.png | Bin 5479 -> 4345 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/macos/GostX/Assets.xcassets/AppIcon.appiconset/1024.png b/macos/GostX/Assets.xcassets/AppIcon.appiconset/1024.png index db96288491422d7ecd156a4f8d10fb889e5cdd20..2b9dd41a2da43486221803ddfe85a628e46074c2 100644 GIT binary patch literal 28322 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 literal 293554 zcmeFY2UAmF*EM_+LKo>>q)4wK0@4FWuhK!9qI3}iq?3R&K?J3DkSfv?5TpjFB27S$ z9(t4BTk@WG-|x&j^L&5cn|Wq3V|2_p*V)&;)?Rz_8Qq7&{^!Rz8=jHQnn%D3Nj z@c(g1@JC|*`y(*a{~L(^X2<^q;{Tt4(2s*nJ{ltP_3nsit@c8FdUmZ#^Rk&h1XELK zd_<&eM+c#FyaLnQ@yfG=G72t7f$n;fai^u;+-^QX$eJkw(-u2P(sWuU;dHbc=J*DO zFchk}o+BrOty+_jR`1PX=7fIkS(NUi-QnFZpri;XrH+T@ZuEF7gWus$nsb@?-}oXV z7{3^~+IiL3%HMv>!`}bFsq*(gt6qadP)>*5Nelx;5=1LK5#kc#sQ)YqNfZSyBvOay z^L=qPh23WpfskBHmvCk6u09jDZ({|4`EMRsn%D_lP3b524r8`}wATLyZxv zuRF%X0mNk}$Iu7x-a{XXl(Q&98aUx2eb&Cl8y$0mNzAn>zI&spD*tjJ_lOGJk{qgF z)or1a*U3l=`9k1GT%9Kmu09d`eJ-IQcn>M!q0yXTOw#U;K51dqm)Vgip#=6^#pnNS za|v@)QJrt)3e36h)Wei}mP2*~u8U0xP6EhX2meREV(vG+D=WLA9d040*W-~JdJ)@v zIEbGhau=B*tX%oRLAoVWjX%T$|F@*{BqR;cA8jK^4=#JiK+owzLK}64lo>I zjUsR>$|=D;9kVxDqzp;cL4HTEE{F#M$@Zb}n%is^8<|g*$KR&jA_h5(Z z3;Ksoo5g}Z8f6J-`mP}9cWrI-2D7*>RT4l#VdfL}i9itCn^5AVTF%82qRPTM-=dVX z0Dg$?mz++1xF(Wg?GqW8b(UZ{_k`=vzeW;0;Y9Ib&GYX}UG}RK;xDbdDVCsFsm^Zd z@ex}L`_zD2pivb3=F>Zic(>7I;#(pOE|@f{UgV&Vq4_9*yg z*q8ImgNv|ahu^cC!%Rp`z#=o8rYr3ci3Kes1%$GMB*a-H+L$rXjRTAe2hkJJk+QCj z2qqf0;d*mYZ&I|XP6*^-$E_k=Tug}}+opu*PE=*f_lCPCt3LXkbQWp9gxCsr$FzyK zep5mydBec;3AZ)61!kBi)LhruEX64^-Ba$dGmjfeX#}G|oU3MyOZ`%1HL6^=luqKIU{Z^i<0;FK8nU@K-U@iRG3s>ka%(cS^GJE>>$67o z;Oo|nFSoQWGhgRWkC&Mr97od%WV^b$W+r>|&(% zPNS16$#hM$dCBSeCPAe#i?r8Tl+I+uv&otXD05B!o+n$yPMqm23CEp9as0{?lM2hT zjxSGt&h|^)wt$A6x*442l=oZc_BLqap8XQ0^a}G}Z6{;-BCKwEw)ujR;{2rB=Z6HQ zBGAb0XydmJoYiG3(hl=Dfyot$S@-ARN1Phg~U$cX{88XEtVR80yDfYj32T#$LKtpwu~ti z_{Z~XwH4sIX&49bi|14259{#q^ySH(kq;32(YDwGSG&)D-*lJL+g8?e)JCc}z7hFP z&JclN;SV*AjfPuM<(_frEe3@j`5I6gRXw{$eru^cY-hGXm*p|vdCP2b?5Ak(T&CO8z%E)fpwXL zF369Mervhev!^+%kx-;--rgN6<;Ja>6+{!bUMQI04`51q&b&PVL3f?D<681RV6Wp<8V}@+&5&8+|xxk~sk?RxH ztLysQIoai;Y0j0%@QFHlA&WzOl(B$*WZIL!@DO`*(|=e@!Uf(t@80iJqEW7pq02ri zc;vUVEc{|j`=cU1aeh&hjS9&$G`riW-xMziU~kJvq^sf(D^vwU<-LM?+C{GuuD*0j%^s*xT@jj6J)bt4ge2 zwX?>^T#lD|bI9EPXv3RK!b0YJBfKR}XQI{i?U{^gNA{xlpCvLy+qS&ElQ%l^FHgr! zPMhr#sL?XhiEFxf^l@g<+37F+&A#;(H0n+Q9qKnHl zH0~CnG$cx~U@;rL3#N**zrXlB$`Xca07e*2|>jQ4ky#$JwAN|Z1)AKzmw1a^leOAXng^I7h;0yM03~pA-fjuV(BtP~S6bQvCvj*E@gw(ELR{C*p<`Sn*6 zpSwijLcPiyrV_m(6cH^_bx10zNql3X{lHX z!aWl&u5^&&N{0zleonhW;L0kNV*{)B=gC&2`qip3r0S0`eMWIi)t>Y{CclXX2#6qX z+$YVZ7L>EDAd>XlJ_PkU5y$%OQlfo`llp~S#2QncTEKHYq}m_9(Mf1HneknWMa1qD zWi5~(IR${zqq)=V#vKgrldrp3bpnQz9IE{>Z_ni=!3RRtku|*7r+sKza+U zgmP;s{@6KvaUvY2|sjyB5KM|@pvFzTm*sif=mh(TfQYVw))PYbK z!}`+1R!`SQid!j%MaSBFZ1i{`j$Jt>1pra_M}KhJp5j=$U|@q8Y~ww05Q<{FVwHb4 zLn$tmz_^Hn;N66*&+Y~O)G&cS%z>4BA=Rx_f_Eav<8E~0=#yogjIQ_}%PTKT)K&hG zIS6HsHuA!#3(EU_aPDm8{_1Izbw-Uz5zgU&8Qzd zbDUr=2-l1F&vxMhlFogAzF7zqo$)7;g|QA=^(`eo8d$Qz5I}|HR_diZ`DA9U-|@;+ z%m`f1x^uU<6jz;aRP<8Uqag{ODnABTB;iaB!6S6TPpRJITPx;+__EKb!CPHy5LZW^ zpgYVKI-r>?m_2=6W7i0Dq{I@%SNom;5(&MD7l8X zut@G^eF;0(!KCID80q1dUhl0T#uTx7SE)iC;Udz9k3+CnkJD_asY0O21lVcCVj$Jv z)>`X2P*of~h1kL^EJ{9tsTBqPf9}k6`;XX*^Jg#GO^lpgt@WKKLxx0zPYwBOvEv0q zzBhg)w{CwE%|A{tWcZj?0sC)-`OS{np&{98$wfLf=g}%|wD=_GNSr|y`UAVNF~#N( zbihjCY7PQJicdb_kfusjClYJhW@X?B#;R5mtb7((=CN8Zek~+7=Gxapld?JcM=sa) z#So10>BXIkKt~wD0_am=LwRJ+A8>MUqHGMrs4xN-t{=T|XH5MT+y^P|`!tpq?X5Ev zc+k)4I$kkDKyzuO2^5q!Gpu0iWQwBJ?WzM$_KfCeZ^YLPpS+c)25%lt+4BWX`_fRH z6KqE|h{Ki~iav7bxhTbWj=<*n*xm8#1%elzW52oqm#@ht6?cDSZq$F?so`2T$VLtQ z%^Cl1%XpU+Vu80Cz-vT_KE&?KwPIDH#62Ir-({_Zp{~Z7&Q>x4+In5{?AB<;_(l&d z;uwi7!@U_3BIXbk90%ByfH5xz$`UE40Wdn9=nUu3r-fR|5a3V0Hi1sV&c>KXe!uA( zOvoC&<2+izytiUcY>(xV1TK`=P%)nsJ+p>x`(vk(m?{40g1xY=I*p%01*OsdTG>QJ z{2#3mDNwQ(q5K7wq5Q`I^j?|sFP)}JC~n}hz?cIyPDAZ zM+PZEl2wp%BA3GZ-wLFviQX`Zt*q{1=Y&cD^)CcwRkm&6>R6u~JV>hmL!XFs{ajHk zwAHiZMvsBlc}{rp2Kc1DAUQy*6p(-$$_K08dmL;(_amlx+6|c+la|5h7P=5BuBT;? zMB)xXCaoYZ7VNRVS}F069spfmrj)1NVBdp1baY9dA^n(n5f4~#!B4IVCHf@p;DTfL zVWrGu@X#4h$Z2>tiVAWSyC^Wn|t$1CT$J&V>WP^}XnrBN{^oyJ=g{Gm4mh+*D+ z3I2Rgp(H@>xcc<$qKlHMWc|eP$>!0Mp{Tx3Ku``Yu^FnKD#l??nUm5DAhy za_ppTbd z;`5(Q{}rS!slXMAFmM5EVedDPc>xA;TvjdXK(u*S=8@v9m$!)j8k2yVG^v9CQzRg zv{gH99WeaMWbDtK3b$X-*xsJnw+|axxvpwW@_9FtPW5O1&KUE0|v7%3=`7u{lXO%dIr#41n&WBz~9f!vdYUR(8aFAhrauIt%%NSzLbIrdJqdZ z+3=A!uYuXhL9`_LQ@c^b=b;gA0o|#KH8FL+UROV65rH_JkGv2v|I+bPNyBD97L#Pe z~aez>Eu3<20h{d$Cn#*hM3Jf@pVbNiczG~Gj}|->V9Whx3}MZ6^4cvKe==o z5g5J`VnTfRgN)n8FNyg#Qyi{8V}m9Gts=fJaths93XT}ZyK;vsZCan%Vh~nF=*T;{ zG=yyDq0AF{*$B*;f)vy8xu&Vuxb#o2;N!+{mueU$*W7>$%oi3VCh;D%}iOiwS^NGkoK!HRFSIM z%*4I=M=XyJEsYVsmxM7`nRBIxp3q{p8d8maxJHmM9}C^bDhLW6c4MqbN3cPAEO*8s zw>Xopi48bjJ$uWec>;D=rwlkTNFPjqyYwN!^`8Yiks|({87wdV%(vCLj3396UXAf1 zv1;2ZDMi|8`{6fUEfJI9!S-QtH8kb0DifL+*)pXI9)%HogHj%7tqQW&)P?VUURtzb zw%L>*uMm5(hRJ-+9HkM>>g3PsAhjjVJoJerx!jxEyV_q68h}mysGx9kbq%~xc}%p?5xaE%GPC~0axeY-qLdV2)==i4 zteo90Pm02`Wa_FLR%9&&C4cbSB#l(SL;i($NGF;b`a&Lzy z>FtTXot5OEVC5wz6mBH-UE!OT^CXSh)uWHD=&pp5{`YOj#StfCxuE^KV`JJ#lLs`> z%G#?jH+jZsvjbCZ{rQ6JgDsNk_lUng3O)WLyQg->4Z~hlUzaW=Le=`{WJqi^W=r&- z3a66{#5Xxha3c$2BA8H#@%n3={H|Y_E+888f4u-O-VW)j%e|HnGTWa*b+WX@a*GMm z0Pm%Ub<-cXB6=z2gr{8z4K;%`B|YK*Rbq_?xd%kprU+4L1_3rsB09ZF*V~oxj~;lS zbe=h8zKWMHRs(U*4C*)b_Hn@OyZ4_nYdJa&6%qy&bg$}EY+HZYPU?j#n&WHEkGzvw zky5*5d258G@%T=Iad<;a(r+6pP-0+%nu8)p{C`NEQ8WsE_pdbZhv$K%A01nRuL#JP zCH`jD;{&>fWPw<+a)=V?WIfeqXfVB*!rj}p@A({i2<}aiF;B=0HuU%NCsDA622mCV zgq#}bfT9W^bK4KoILVencegbf zTi6vtynOBHZr|C#Dq_qqCxp_lzjycML?2Oe@qb1i;>1f6>L%5Qh7>T)K@_{s9KU_^ zT#1_X-z=|G$=WKT0(kD(gW604!a?W{AdmkkHP z2Dkmj{O~wV6KEhfjlP3-itDo-Z$jBa4D;m{&lR6S!re^gHN?jw%6P{75`ilZ)*O6l z+Uy#y{p4)90PFPbC57@wWmr9BhVuKApT*|0k{~h=nTjV-zlT+bzGhvane}b7A*rHIeZ&y6h{tuIZ4x`t28ZHw-e9 z$=S&`M~OWi>hG3jx_hS)8N~zP_7$(>Tm4?Ln~nxrs;RIiB^d>uC#JtFVQ>3YtuJZ?$9be2Qw04dj7y{KgF6Xv{4>;j08mE1G2>yN#1~3>(ro%i{L)SzH z+{15h-bJ^`w8zb%54$!GSci>yA+s(9P<_W^TDMfcjYa-xvxED>obIMoXP+wVh<7et zm>y8!!}=g5DN%5I;-NaZCtJBnNhZq6H0inzqR2ioECl&7>1y%X5KCP-*qR0N+#?;k zJn+~@Bvx8ypX8m&v5EpWy)3n~h{sPt1x{VEh1Z$>i8*g@x^tls;>CbKw6zSweXmF8C#Sm2%3Tx%O~_qrD+NY@^-pErZwkqkohVk%*NaL7 zX5QwJ8j$$324NgUrUQ4eRK?;1WX$fAyJsCTSBj4a{2BV-t;UKS&*$&1X+cCGff?W9 zg$XWNBJQq~EdG4dujL@lN7~zzBn%Q|-UPV%(2!>Rwa-)RB+4?ft1N{78W$03n!7)BC(vjweR!RE!+q( zzIsDB`KLUbVnSJtzMcm7lH05F5H}w$#YqXJ3uFCYG8qt^(g3S%9;R*MOeExj7`m=3LUzvtwr=Oj?~~t(a1Iq6m5sV6J-S!AL3X7>u|myq5HWN zMD96VBM8$a#LkazTwg0iFK*S%-VWTQZ)MBuL zz9m)sQ9tUUyl%hAAI|s`FV3HS6|+A~?y`*OU0N2Rz*M&Or`q`PpWlJHC;Z$i0m-XT z4m@hTNzxf}v+M7dJkhxbx<}141w((!;T!KC=7_B{kjs-?V=_>p$t-JEKNB31@~x)2 z7lf7Z-Wa8?49HzOvL_^BL{?s>0a`&7Zv3r(i@5c+p?r_O4Y)58IM0LYnztPYWkirU z?(vVn8aUppKi-l!v9!^2;abCcmwD*}VpRljd;Sl`WV>pUNj=Y;z5Qtt@d z!fBXq!{DKxQzs)^5p#Q$>MVhDux^?wg&sDIWc}b`Hu5})H}jtp(4T2G{q3|cR11OE;)!l>5+}w z;F!2d9#Wz5>W2PlizoH#dq8VfmwT~()k|u|8SQh4WY;M-P7ft7VFNt*zx6iii9y!e zMs8KmVdg+mnYnhQZo2YFmh&xjV)xIB7CC&M0?iMQ1ZcOhe(WF&S-yfoabE@xB?i|@Tp9|l( zK+x)9i~`;xk+v?`*HewK-Huts z&@9g!(VSOWNC8abpg-@ErGX>z=ou?%pY2O6iNsE00UctBT4)qMo~xS2IhHgNW3#hf zulHH&M5IW`=%1g0P6I>|Iw_%zL~y1IIsaxr9B|bE< zTKmZcnIIGCu5^&md$h3Q|nwGx0Pv7;YSXSE%?6t!>s z)x-3@&L#durOIvOC)x3f#K?-D%`W~}%$11RZmWm6W$hOdETsxy*BE%6rD#E-eCY37 zmxTJKTUcxpc~%9O0wpB4bO<$<#7#HZDBcUCQ|?+pK3}_3d5*I8D5G5{hqCdnb%$dF zGqk=kOQ~3pNFtj-x5c_|6I4iNVxZqy->l@EU<7k+>9qjv3=ck@D zP6L_3fcm^Q>&u=2X{VrCQrUtNg=yzE0;S?3eh+%|1ooZC0+Gf#FcMBU*4Lg`LtvnG z*YW;AcWCsr4`-xw6|5Xpy*4+5mW%=4ek>vR%|MnuY#s6I@%Kv~I_0~AFk4t~x)Pjo zZ60d|!#vz(r@dpMf*r)xuWN$!fFC!Q(#-W{2EVgGEfaYEHfYZ_Gpo&hd_LDQr)+|J z_5Lw%ng}$>uNUxj)_Dmn0Dtih9-Idi(WWpX#fZj4yDo`{znqYKp#S)U-UriBam*^o zJS*%YVixd1g};(nO$%WT~zPPPMfsUIr!*&tu=4SKG5&n>c$s#dGfx zN3!m;?JhR+lForPhB=P|;vSfLVt#Bq%4GUaL#W-;^$$4py`FeCU#17|hZCdM7ty%f zce52wj#@O3udsvLcUK71nwv;~;-l~2`0g!|3LW|)v}UqaT4-MZ8>cP2I)8{|XC-W6 zrP_plHh{uQqrp`k;Ov-ZtDv#-KR$!o@CvIp^k~cWs*u0oYzB3<)Dx01RLB_(Ll|e) z?KZE&bw0zQ%=I*Nk|B)%k5vtZFziizsBH2wJ3GyVb3?_RPgmgZNQ4MAlH;L68tgZ1 z$UYHjx0@}&#`%%0tOYq2T^G4j312V~eO8kulCLXp@zKfoBI%3tQ3YYp%IFIUb}wlY z>ts?~Am|qm`0iNiV5XE9r}~FMm1yN(>j=NW#tP7rTEcG3GY<#ZX zy70aA>O#j(S4nBtl;zo}#C|j$#T`iLEodj$awt7e91`0ykzuYrqwBi-L4uJ=0$jF5 z?N27U>c?v16ZU*1vw|EBv5Kz<?eChv|Q#`L1kTkZSPPpa%$3Aun*$KRrV z`V088NS!_=qB(d{@*i7H0@&U>g^n(RB!!lpf{GL=`BJs*tK2K8^%P2-^O~VIK$CC` zI2BFRN)|lhmPjPO=ctdgPTiN6+@Gf5Nw$ie@JJLLKUG)LyIO2*mpIadLBrVL&U>8@ zboj_#Z$RqWW($rBpNc@T<5Py`3TcWrhcI1#BLOH@g#XmR7z)1=O!W)fvXVwq)b!IL zGl=z`J6c|U(wb5dJGhX&OpK|!Y$sQc^#{d8?4DO<5AUA~%N&|Bg-z;Q+_=6U*8$jf zR%U>W$vq;k&VY|RmhUz*6%W%lYH=>Q+S`0WDv2+@;Z+4^$8zhw-wwuJowP18#Qhwc z_2cLg&T{O_ih6U8v;3-l4srSXuRb>X9$+KvDJCKb1U*s7-G?bn=Vn3xh39=|W^gSJ zg)%g2EddeZ_q%ojK|>6_KmlF0Z)0dqk=5GYi{1Hl5uB7X??eYMLTi$@Sz}K1TCp+l z-|+Lunz;I%1%mSy@_iqa;@kn?^aLNSl}>hqb1CVfd`P;^rv(j((UybmA4cmCSWMlc zgFiD#_-sOJMm2W7MhbffJh#J$sk{7&hL#-$KNr1iOBs>rKU5e-M=LN^lV+MT_Dh%d zbu$Ym{7Ldw&tgfSFR8yCG5~EImTUA{-(M^kMK;~Oe_AK0ZUF7ek~z8E#4RH|zhCW> z-uV{!-cHhPyZb{w*`Ytzk3)qY9B39h-HS-kL78*lk`2@i-oPOE+i%>%Cscn=z`{bMI9nf}wBt)SC63S{^ZwX@& zV;cKstJwJy1@blvg~&1?`H(Db?A@!ASRImI``MmWLg4&Y8{}7x+`A3s;C9@GoSz*m z3n=aZ60^RGF;jKV5w=pA$Wx7k8TaoshhNC$Pto>jYF6CU10}Pr)NMd(Tz=bN*Hm0K zrqL2o^${x+=wEq^)c$E=D`I!w)q(`q4AJNzH<4mW^0Pktq?A|S7z%5X{^Xpsj!1nS zGg%xf1(38ezMT)}B~1|aIcE8VRUmXJf4)jQl>Ru)cmr?l)gboNX{1PQG3|?fD}q5I zk%mn;rgS0adhNgWXz+0YI1uHm1X9HXnB&5rVX)0!7v9?RH&4?Jym~V3qN_knAV4f& zfrj>St7sUr6cB~COAd|%7uN~|t^*>T=Uu_Qsh=}CUc4yI!tn4xs|>Hul3h18LKq&8 z{Y2SJo}Oo`f;@ME`1Audz%V!4w$JCC627(a<^{Ic9M;ho<(NA-?bzq5Hy-4qJRuQB}sE%_OjM_}~`%08hN2vP1|zl~K>4jR7122}t% z%${TUU9$5G0!awZ7gf9G*f?{)N*$-w1otBwbb4#7C zN8_QyC!8=bC6)o##a6l>%qzrj0L0DD)g+KHayOJ;1Q-Ig8*p6v`IsoIUgoH)oPn9^ zGuzX(RH4{2&ta$5O~hAG^fl_0!c9!(f(?OaC9~D0lz!x!G^co%4i(%e zkv~(|ucNHvcddS5`2LJ^{5DR!XmE~eb~7S`Dd}RHdDhhF{evY7urcS@NM1Za8^b+D{)DNQ0 z`xkmyT<9-pKEC=FdduoH<&Eh0{*AJ3Uw}0xm9-EzA=n=MWLjy{I1Ei@ayghQ9QK(E zy9fB#wV$zcm!0)FG-m|fhsZkSiq2`>IU#>djJ%wk`_|9!_aKYwiE^JW^fw*xu@r_%KiDig(5K*S6g~XI=HR+=y?|VYP z`>~}{3)kn#1#gxEZeQ8JTCsR}iAQNik?ELf7Mq9i1UTga4olAhgWoTYzkze#s6yEv ze3{@(f?2182z~vtphhHJo1#Zdv16Bx5A18}1s(l1rHq;~59D#Q76392XYeRqSwa!% zp@7=SAUgw_Wq1}>2MO|Bl@wiiC$8OCx28BZ2a_G zt%zcfLW$;;lg>5wTDg~h|75(&dunu(6j^ZO`{O;=90HsqV^>n|l*)vykpQ3pYM&0n_an32IHR9{5{r+Xi(@9Elbg zt{Epm19tL}Glz9Se830|tdh}_<1NWvySb9YMQ=FEE*nVs?h%DOkqUX*I^U3Dp)0nc z2_rl$5V*3ju!TGBC(B3C}WpEN> z^*eNZ_d4m{N&Vg&VSV2Z=ffWE;hGITur`g3dO6?b&^LuCN%SjL09DY_$KbeofS2bw z+z4_Tfw{!`fkQmZdMa6ZzsPZ4@(2mgxc;?=$lb+45XcDt-k#W;){5F39@zST0Lse1HK4Rl-_Ma6cLkj)Lh%4>aij^jG&_giyf(aMwK07<*Av!khVjbTCK0n~4# zs6&A4V_7-&UJ?m*?4O292Irxt6hxv|4hPmLxe0Hin7JQxcj{nA5+R_AAsaKdqi}W7 zDZKZ{{Vil2tGfYO6U<*;#X3N37d7Yr4VOI-w6{^~*b`Y6j?j2VjDg#^y~z$puxg~i z;MO$o8AgJT$N^I_#2dCkdK>;+`<3t0PCPoer7Ffm&9?QSpkzenU2%@zdSObJj9Yg< zPrk-QwAeDe2u2oq-X?LXZzG80Q~|?0ca|jDeJUMtqTKgn;+h!Y>5FHC)4Im@b61GN z2<{$bW1feM=H0t$RfwBYsBDWo<_8srO?K;nb%l(V1i-ZOr}7FofC}0-13Jd$mv>q& zwjrLY14mCqi5cSzFuyOm_3>yz&T*+6G|;Fj1g*W##wpGw`D+>}J>JCPGrh+I%bOGx zzHHRZ{*a+}KTG10S%F>wdwGy^3_r$)E-hONAQhudn_EQ@-N1$FB4l@=;BJVA3hJUO zGqNA6oZS-9097Z}*UAx$Eqo6vnclqNj#rHC+nnz{usWr@LG@yi)szbTN{;p&mb_E6 zPN3Z8TQT-;kr;pm&7_Fu;JYFOszShiSv%3C4{c(l{8QB^e7cLWLbR z_|pNYq6*qnr$Ny`DpvO@UsMhCBk4hf71t;&^4aZ{HYK?d5K~{n{O4N-&;>fCet6ie zCw;0pN`e%l&oiE2b__|HT>6~im=Bs9vXyjyTIt-QO~cuQBWRG=rFDc99bo3O+PwRn zGzS#48Sw*6i66A|7HY6sgC#8;_i{nMZaD!r)LVt^u6XXrT{073Zu8IQMa2hPaE&Op zYRro?8ox;kV7J-tU?nSt#)w?Tp4}+-`WI^?`R?HSV%5W*kBFx0FB>E|gwS!&>{P)} zAjrXdu9pqc|0$YiR5nC^KF5V=T)^S%i)c#w8(T}0ImB2u;o*P+v*){2I+aY!$Q8f1 z%g-pvTk;d2IYi)FmjymtmKr|H)Q43We+08>UIPc`-$ul;>kW5B;%wifaY_WPC zQ-j!%+v}iKGvGn5sX3w*I`23gv5J#{+965>XVzu~x3^Y_?XCcFydbzZ>!mcc-^W(8 zCw^2TaQ`vDaQ{OvlLs*1h3fbJvO-IA{f>QWUe~3<>_{O@-2i4wsBq8-&v@bTnqsF}=$dbSE)41tDZNSO)46PGnr^~+P$=CFnvl>Zt>8t0`?<@}c#0LLv zum9}3R=refZTpadB=}kaT2AZgy06OSFIx2YqY40=nl{l&j!%aP6u*pkZTj0pCl}JZ z`a?nITseAtGBP%k1adR>`@w4dt>@zc)`XMwgwJoUTp|a;o?y{VQea|F0MB2{=m_f9 z&xW8swUghhK>GdiP%#OF6ZfkeyD)eSWlO`acvk568EqID7gFY43w{hTF=KoQ;+-?d3M2IO529M2UxS;h9wrv9%Npg?S? zCNsyb;*q7N6_+?3oG=k*JDMl~{j5;*PS5qKfjg+#j*3=G2e6KwCxLdrEpt6qB}>No zo4=lF{A2UZ=^(!mmuqIq#0kG{A1-`V2Wl%>tv|Z3Spw4AUw#Ucqo0(j$x4e)y>)}n zBQ#k@k1*Y_d|!Y-d`)1y>+TanCYI>Bxw0mKz*lt=E7C@8ImX05+oK-}u~OHwFl^}c zyv#Py(>p}<=?g^}B3TXifN2t)9+C%RG#q~aEbclt?;5KP)?>8ajFoEyTh@l~2{l`iAiKJ{>b610w4I!u ziaL|wTRRDB1NE<`ajDW^v!#z4BlL2WY~p$W4#@dWpF^*{jK`ZJIhezBxqxHY88_WU zIzXog!!?fpt*-<{C7X!=I1>mbt%S3oGHzTENj;6R8Swa3@W)) z3NY4o-FNa?*A4PV?1y&+KXKgpxHDalK$rxPZ)=wf1lzCQoTxZ5+Bi!?%a&cIpoQ8R z&He2kAaB|{-OY(tJ5{S`ZEZiqz6Wlz^J$h>HVtm|I{m&4s;F1aqS=R3FaR1FxbLYQp8e^*-p`;kvdn0c4hzOP z@P$Idd=h{JLqw8V3RJm>j7^hc&c|Do#@8)9mB!2puUww8%6fxuXXna-h@RN3XNqGn zme7`1zJTpo=)GMOUh7*kfQK_JZ_<#LINXLX=+UZ5vn?RQR zuEQ#_z_C=J`LP3;K-6S6El{>d0#$EC)F0i5IlbAEo9`L{CTQow!MZmTZn@xz*h`SsSizpi~%a&OLVRsP_=%*T|byt@HA!?0lkw4wyalai}B zgvgF-Hhu!`%(|D7UoaS&b4o;E0nD8b$aT;101d{;V|UfSDquRPuw$2p^L5v%q8dHQ zY9TJN;9tB|P^&qb3?Lh`dn>i_Fbdy2)fj3PDnS4U0gf2GHdcGA{rYermnqOkzk!h$f~{5wm8Ll5H{T#x>etS@Bdm(Cpo%b?)g?;< zAGVk}Ema*^dZ*(YhAoP)+^JI`K_L_&L&^47OB+xKGw?UZM_#q|U(;t2xl`GfD!RyX z8;eaSu4XUBYF?~nc>J3pdcs0mkj`$SOz~j#lcU@%#6Prvu<87c%JP{#8>?rATS8DH z2ofyCsg^J#P$%uqa^jB!o<$1b+A!Gki~bbTkhYkNL|`tG3l%KS1#DKD7V9vNd<{79 z*|BIoe?y-lnD?E-C65b8BGkSz4KoJx*gqOQ3gG9^Cx)8r4l?yCl#8V4UQP7e=H-c# zSm7_AWys)JV-3R|?A#_$#5RL`kuRG&j*;Aw5y(s@p8H=wU zM@e}w7f@V42r^1ww)sxIAq1{{Q$9QLn;N{3?JV)l<~uh1G2TM*k z9iJk0m9=xwjzV~uMvonsn@Nc!yMo^QMGD8G7n?hJT);wk>h?@yN*VCM1NcCK1ZnKm zRp{o13XJ9!8eWqGvN~^0Qg*|*%@vP>v7K{ki%Wf zMJ3=%Z*o`QTbRK=XpwFZeS*t9(070F(au2WgWJVQqKhAElkdWckUlh!Q07EA|GT`% z3v+@R2U}s+0cQV0vpa>)rUZh|2V{Kwo-b9v!A6hQ0C_^p+Vb$hz0c8!FQ)cqw06e1 zLq0$+CgP~VZ5XD*g1Gb5$+v#l{Y@4yxeVOeUv9Sg-WpU{L#JY$@aUL4)-Gy7L4-^+ zq3&ow82-i%zU*Y-!EX{t&WhoxHwohMpPc(-D)I_jUJb4C0Lq|9m+f0|vM){*uxEN} z_Y6qK=}1m0K8#=|FK@UQfu|S((jg*b0Zv0M&xS|QA|*La!s)Gj$#n|}%mn~uSbS)U zIM$}hHY82P$8G(2OD^F1f0%m9s3^ZL?)#cyfT3F&29Rz^C1wPX4(SvSkWxU9k{TLC z1QnH%0i|0&Iz~iE0YMsMXentKa-NId|GwA#ymGOYZ>}?E@3YU|-|y#~{ZN=YYl?PA zDE8=vIU7T>9`sw#-lS~;>T%w4-V}tH7mDd0>Cu&M?3&K@-wK;Z9JjlV>@~n{b*WFr zMrqKw6Y}8jU}IB39keGA^7Lk}49$%uDx*Jzat0OWqryZaoAMsZz1$U+wMC9hVhNU@ z&@l*a@pY3h)6Tx{hGQH_iqlcFf0}tQebPn=81&^qh)8THBMI1|9wZac8ge9I1-->Y zWDWuNH~g6GLwDD*sW%Sh6h8i^SE(RdI*BUryvNo82DT?Zt_F%*nOz?A_@2xiu{V^f z-u~SZc!*>676*y|+*1N*cy!zI28T9gz7RKG(jf1J&Zn9|Img!^J|7KuzFAxohR~{= zGGuac%&W2@G}`=M9ley!+JYaZ1~BA39b+~*B%k!MCfurKc=c8 zM_bs39kRC*&bPV!W@I|^Zx#|D43(F^`R7Zk$kcnAB#==6THRG6%*Fz;Eg#lR-d8bH zIs8SMQe6_sZd?jfb*SnSgwF=ERhqA!jj#a`3JV0xfgZ_-N2A{OvFhoSg?CxnWKtwL z$zjhMIq3WA{!#FNA#*Ni1#$Rjzz;Yuy{v*?~2|3u1 zR4_nC5y#P2VxHKN2I<&)i6BKN%coIRA;n>Fl&me_++|MlV5r=}2p`Y;h(GG70NGhq z0iKY%WxYpjaIr1Dl8$w!WV$HUatcM)V&AthqD`9$sv}bf2*zUeq49QbJj_3jX(On| zQA(em9EvtVcf6Y*KCQ9eMq6qIRYN%Ll~&oi^E*G-JEsXXx>Tm-$djcW=9^_|$%=q^ zk&PdF_(HhYmOaATx>Z-Aafwvw&N*c{7+yI&zuK?j4Krkm8DS})KP{7Z46fpLrx{^#>BkG|30l> z`O$oAVW@f>@#g_u8=euj#+D*??r=u;mN(}%@)v8IS!!{+-2NcrFUfMu+;OMiix|V7 zgfiQPD!;WQ?ha%>LHCn}W#~^ru5iezw9~+o<&QuZ5C1Jx-IkA?>f(be5$?M-OsT z*1Gz;iH9n&#MRI=)v%M)n21hNp>t;xN(5d6V4s`_HaMPl`WaTdsnFxIr6@AGyg7jl zvCHnktU)V@1SsxDfW1yHU}j11iY2faH8a|t2SvxS0xW0V16kMk=Luk0B&B8O;zs2_ z@xk1CHFwJd=>K{+(_n&kC8!xlf=e>dKAXh)S;ZEVsojxyho+Vq-2!qk5)l4+pd|;8 z;;gz;80DZpU1#DdW)rX>?%nF&)m~_DE}3wa%%N~l3(r~ey8et<7`iuj)3dc&=nJOz zW&w<{r*7Wm^FHoNm?4|Y3gNR&TPUgMK_%*B=yr?wm-j*v_?j~AKDQf7<;a?`j1)dF!`Hf-sBDFoH zG3T4E1hlsY&38x+0U3Kv#vqUe=BJ-6V2PQ(0gbtEJLM?`9l|573Z?XD_Cvk4o&?Gu z0b}w|o*sVm`xQblOo;6KOdDzEHOzBXU)U%S7QiY9DGZ=6FKgIdzV7FBd=4JVrdShg z)&xjTx9&4E6g}p{ph14|{61z)CiVMaOnKeZ9a3{yWz(!e&>hdu1qH%k7(`yRjLa0wO6VNJ$c>&Ed!bJ2x_966mi>b z{gc3FPg^={)0uq;`I#vo`~IvxddL3BXK(K*Sz*BPT?KuTsD(jVbq>@ z@xx*D?5+=tkjJGCRj6!^l3yf|A7Bw8P+@$bHBsyW+t5eNuWL3B57Qm(nz<$&e`JW8 z^Pv9dIKy)>5eJ{4I%qkv0z&?v(FnyibflJ$c-ZFAqzMs7jSfm70F2`jp z_SXL8cJ4RGWbsqYdDwZo%oBb7UhrJ`Sh6)}&)blYd5u5y1{E=2B2W?A41)&!z>H1= zji~6poso4MElCktk_gujh8CrOclW+PMQ1Lv!ejW3pFTwH>h0UslE*>Q*l-t$i&Gl* zx_K`3c&^bxlR~0h$e&Lng9MMfe>POaSP>Cm=R+gX-V|npPtG`gr+PzQQaO!+;MC6) zA-D4Tc|vf8p~(g9JVtQoAlJ}Wct_(bIWV!8fyAcFoa~d2Ll!LTd#M~>Q!;n}C-SGb z^G)2-%zXh?VF@UCXxk65k6R{bMdm#p00+$G?e`tjWrxue8-BLwvFtE1WrrMTO-TRS zJBh&W^av9=Bl-)z6~g0}4#N2|AE5wr;0BM|XbF>I$JsQ)cU!`qrr?X^)XNPZiR&rR zwd7SI&kVhdSJHp){`AZBPgWF)7)C9%u$n&BkXHM?#++DPt+?m1=AlNyWy9Bs%EIm> zQ*%ynBi)9w*#o!3Z>>^u2Gp*p9iQz_N~#ci*cTFYfK!n$RbHCAx9F-!tgENFWmhbu zP6ycxqoa_RwUh<=i<+#n>5%Jhig-ACE#q&R+hG*)FZ$r7qhQwpWRO|eKmMGk!FMTx zdjy@?EIc_@NSaOWC~;8bX)Il=uRU%It{x1X&W+s&Uugj99+8l9c=rFCn2oqX<^MGN zX_*NnYNwEZR11uFoNfl7x`oeK*Mu)>*ywwazhzcdnx7vB{y`Y8Gv&e^1j}{fwnUf& z8L5SxoA$E1nq6vGs#M3T%%OSBwAN=bv^G@vf!YZ!&4~ja>uqkmstr) zo9Bq0V78m)y&$%iCn51ReKMBG2$(|4B8Lohfwy^s5UwV&*N+wm^$%`dY^F{6TnW7L zZpZK&GgB^4EEG9Dsusl`HzQ>nZ%*ybO7TIz@fy2xOMVG>{ZBw`@Ta)6k3G>WzHBol zU?a$4Isu2RnLA~DF+WDcGIHaye@|k~Ihbc5)$r1pS`#uObB~(y=d6i}5Y3$2u-$z6 zU*^Ba5fZSQyjDQS?{;P9{-m9dfe*A#Ki01i**wFQBb<+s95a=c?R#Q_K?g5l$u4SR zCVSJIa`}~Id&uJy#I3%_GG*W^PgRCL{;5f(Im>+gyiuSKH)vjiKdoug#jj4GEQ)Ez zY(?GqhipT1?m640(!OISA*U>UN6;R90~Uw8LmgzYsG3#Z36aFq!;M44(E zG=Lmjoo-`Jeo6;bIoVWKIuv_wR@pp~wyIX4U-WgQ$d>%@$dy529Pa?VV@gsM^Iv ziFCb+d%v@NSl4N-SMCmmzo}K9A}xU4z=g6Uq$NKePN@5s`^l6=5>{j@zFobY zQ{29l)x?A(+(;X9|5Mqjg- zks4!yt)^~oN*O4SmH2vzUULCwY4U10op*wgQ0(dtmAcu~AYlT=@onhZ+#_+&Lt0LG z1lJ}NBDN*TxkQ~Agy%t z{&T@K*A6VKPK*=!_!0N%swkhK*}3IcXh!-H=}?Z54>EOGB=)Ypnp=H*j%#`J`}hMP zz%4ZtWSWTK7uKXAt^^yi(TB>cYC6xlfC*ED2amb+1tTrKAaJ2AK&be^h#0``KN(Nd zT0Lr!4s!032)%#YyMCLVKALyw0P{6p)NF?f)aN>WHF$p`j3kQi+mbJrrr-3p^=FUK z!^A7%YVw^qYQ*unja{ysPgwqw0~Q%Vza$2RE&MFHJTMZ_+oqyHCKS05kGo!QwkQ19 zR9tBoG`gZjw<9E9bEL+aGCKE8@Ozs>fcBk=hsx1LH>FgwI-rFb>~U89@9dI}WosEi z)LE-2h|AGwz=nptYZYqqpi*-T-Hy#_;<))nFpBYon&4}_?vVoGyrEl>>1w1A#-C`1 z&97f{Gwy7i2bcV$ z$I7yT@57?nHx9T5SCqB>(CEvrT!cJY=e~wU-*6rr3>rfOFa|Y-?=&7T&E4qPVBL{S z@T>QlRfTA*1;Nj|xvcMgu|Dlw_?9S%Jz1;C;^FS3^S{Ebkl$jk=5guSaEra1<;1=& zkN}gcvjFz9SIVjgyW@;ixkcq&!TC-?SrHC%hs?->d%Qo&1?$Z$VVjUMcay)fU9N{5 zD{t^RJYz2Y)}g_AiWzJt=JOE8+)m#gmj?%bAIs%?KsL>ovbM<0TNDen;oA?>3vrl0u4pcKL;g(Om*m12<7eju zOgdxO)ZBA1rPss{217O<_V?g@IFbe`1o5Y-z-C_g*Vo}%tAwIS{d4%ljRVm*#uEE2C@QR5C$izJKOmepALOs5X<=Qb?dZQ;2LNjIxk!T)CU0 z*!u8G6!E(!Y}7p;)B>N=>b~#j${_(G;gXOF`i`;={Jp^v!h>#yjOVvWIq1B65zW1^ zY(6AuOooDkZ8?gSaz(|*Z)ru*DS!o|M=@N;qG%})4wCZ<7t&Xsu;?{`?jl9F2kD2e z)4n^Zu_%)&Y_Uwzv3M=8ECjuFq%aQ+h!=b*@%p#{S*@~GsG-xiZ_>$9U3W8gLBg?BR#qCz|^9wuk#VU#($PEAh4oK9#2^c^P`-c7MY2l#3yBd+rMYgJ2@S-2^=PL zD@(G8g}jJu_r{v9HVE-a^Y5E9<=;QV~lKc0wy>KTkstIr5h83XjLc_X z13swkty<~sA5-{+B|KZBP_d1d0$G3dKxRzsdf~7sU|jWMgoNm{G1b6%`-$o_R%87} z+BqMQn+wGluE&R1vEPrmX}-ZYd~Y(tZZf;X|Gxkaz6|hSzYkpC<$zHa4gK5n%THKe zM#W+MNrL{?7f^+gF#bK35G4*#?JOSS$wpZ42-+wDG}OOtJNDLp8oa?brhyjt78@}b zI+Z4_5XTE7B}eB?j26PN5(Ll{bhadX)_)@4Ugue5w&QZ!YgWPl+P5 zu@Vys;sdB^6$aJSTK1*Wj-BZH$Yj%h`Kvb7zRelRmaDqgqC#H7c((sfx|4u;Yg)W3 zdFfV$+vc@py1v-6tpVL<4D&aZeVgIhWN9wt{UiEz|4}l;U!dmMYpy7$`DhYlfl*>ZZS?86tvkZTK)6+24HPZU_R6;Gy8}NDXXzQo50w=2=o~Cv>OY*p_O* zzY*$6*E+us@`LZWM0CDF9Sq9k2>E8?H4O=m7zq-3XWX5BzJl~a77HYs_KrBUV z4(RE=GY^HjXmJ4Jx99HP>h|c^ViRchE`sM|qBIObXyjLDwWPXoM?Tt45+TB7;}a6w zgV-T6YODGEPk10by)7DRzU2S1WPAm@RS9SI2|ddCL&ADfXKRcG$L-1mOFT}+MLX?< z(wJM6PbmWTSB%7k~uYv#KF^7r>&e$R?-oD)Fp z*b|kebj^=PFns7yu?TWit**{V85ne;Q)}9~_cE&e;YcCiQ=lDtOxU#)Y`+7E8xWW$ z@sXbaY^7Dx-bd*G7BAURp9Kw$z)P%~IXKgm6%C)Y1=pej7J9S1r7(qvl=>EBV{#e1 zBp-$i`+Eyrn#l10{E6?71JlITS`%&r4MVGMsSqU=8DByENLWD!qM!%E>E)shF?kcc01JUhb<3 zxXaVl_i~4lymwK+c7dqxPs+j2Ow4{Si+_)GX;LaNNer~NpiKQw`M(gr=3t&;Ws z?s|!r=AOiKa{pW4H#Kxm!M%Ni7wDNbux~GpPkKKHyP}eAbMtUN-nh^jAca3bAr-rvk zQ)^dTtVq>yIRm^6kW{fSj&G|ZD3>Hj6HSM}%jP&Jw@?MAhSnDx#qr?1@EC5CvZU=~ zy_-RxVUfa>(w-Q6qH?|i#V-&C#l@&>wX~}E+g@z!&g>n-n<_s*(f7Y7Kb5#EA(2D>%IASj;ZQ#+%H03SE9nc zBph=xi<@g?w=vV*h=s>75V+M0lR&wG>EdOu)WUp$Rzub?S^0a zLjDld|G0c%mzq%36|0Yokx#B-sYHppYZsC(z@|`62+t1H0olHLIH7HWBg}?^7!aFy zFBq;GS)IXdEn3tZv>HZ2LB$1UK=WuBhhNVd`eDcUB-c)9|H@rP{8r-OA~|rX0HP*Q zn{lx(QLp(?sJ4{D7}DJ&I47llv706zYask>ZL5r6=JR8klasp$C|#h?jqfc)q&okqqdA zvqC_A!XX+XXVqI4*g8m#a8IIKdr)hDT45Yum<4Q?zoxFrc?O#Pr;oP&|I56ulTrS& z?K1gl8u=N-7xG=|#C5xik=rJ<^uSFZFdnt0vo-pP5x64PZc=2K>jD@)Nt9rGR$iSe z208X|YNO6vWw7P6h0hb(4BkgE0tpbGYpFn`;?zg9Bj09M=_9enrEcPd=eheH(YQOT z4zVtk#3*(3ul}?G_4BUel^V1=2GR7mhcO{@_nn;Ds4VgG5>>}-|F*x|E|)UnRbwB8 zOxZh2Awxn+#frz=BZjN`i~)q@x}=orQ@Ufj!V7nDv{(hSNQqs-A(;eUE>7M~@Q(Nm`W|DT4&Fk5 zZFsTx9SAMw3rQmNMj0<6h7|+h?$^%BFy(h^;aCdhx5F9* zj4K|=g#oou5&U9;y~{RBhm04(GuB7IYLAL>Zx~(2601Hr5PtDC6f7KEHOuT0H^bL3 zRsk%Ue-Z_6HWCREBHmpGrdK~iAAf7s@Pv{VYS=J1bB0@y17+_lI*4(v+ZYJ=Cv4}< zl!?Mb%hNx7z6Os=yV;8jcr@s{yk{5%ZC|vJ&o1{fujObFcq+><>>SrTG31G-WZff$ z^Vq~$0!zh%{Shbs?iKa0yZx~tboc6v)F zOF&j$zO5dVP#@*=@1*OkzE>yz#SvISOv(so@+@{7n=3B4=7Xc5e;yBHTX#aNzx+HS z9c0^I4t$>evfi)Z#lG{8vpzS0k$gtgVpz_`Ex$Kl21|J7E0_Jrm=;^6gBIIxU7xAE z8^)hKId>iR(>K~nv7*DZZK{I#humCKCHUo>myMwpg7)gnk!%9kkuZzYC%0P+sDeg| zO$xJj+|NnZQ^&f+*nf$=1tj=^n5reRnx>;ZMF$GMzF&N`mIyg z7$q51vws8|Q7Y4n$1&$VKRMc*tg$OB&HX_#x&bm2-tp*V-xmiqU8{&vfo2JNuagS} zRczBg#AkiHU`zz;^dsW^XCuYDA;aZc{Ca2Vrz2Twf54*)VYlv`Ce7D$_crPKQ7S}d zzR1``pAoj4Vj=^Yg+abU4?cj@{<`zOnUz3_`)kvYtFnsV&amd8eRbhYL@9p=qhBc_ z4FutlAO75zPo_sCUPDlGlcNXKAUOIxn+zy=PH@SZY>pr{cwa&T>-cbLX549O%D-Uk z=;)8*TTKIk9XYz-XbiNOy?ex7!i&On`fFyTZbzD2sQ5%Ue=U6wc?}o#eCrdx?ptLN z7i`~~7IxoW0<2?+i;fxnivC5Ve9%_)gLKxalXl-5vZ53zQ`BY?vM5PF>+PzR*H_V5m zTH(w-JAN#X%HT-pru3n^P{F?rVM5^$X%nri?w1;6ZV4j8dDgHXkge8TVPPAo_K}Yf zNM7K2zFTFmlPp3+DF&#vJ}zE6eM#@I16qE8+48DEN8EN68=a*7KXZ*=xi>=G*Zu z*x4$yrKFub7d5En_)V(wLiCjZ;UmFQGZ4+MtM?UrEMewe{}FWin`>e*Tc67A>hGt6 z5+f^OPsuI!&R+*;*HWpf&-1Xt+=%vHF6P6o48;(6KFZ4A)Z4ItEzYS?7Ij; zSh-MvzV9n&*ks-8ZZyjB1Ak4bDM3EFy*vl{4eqYuLWb{Wkk7JA+0#w`lnga?0VjLP7*9fm-2vDS<1^Y(Apf3T^YcOB*f5bK{N|Oz8SvQ? zb2!hl1P`lSpXUAUIal)k%ZioWiJ(;tmjc;3 zHZ{-51kYP(dkBp9c9R{NXI3fs=PM=COI$memM|ka9C@4hI&XjXU~K7E>3=nEoG6YJoE^o{g*{8&; z0CikuuSUS{g*U@xfbA9FXWc?CVm!o0^H^dS@_U)3eF1O|RW!8&_x}GA#i3Lfg!%~F zP)ni@R!Cx=GUx2S48!W{_$i+jg6y#M`?WN^Y|}>AnMWG!(QWQfsAqmGL5P>!oKd!z z$GU zX9dR5>^Om4X$@@AosT-jgw0iq zLb=lFYY6)of^x==um`&FV3&%D%HHiM-FfL@ahHrLslchl07x}9nDN_+WA|!Mf_h)b zdaZMsLG#isK-BHp^DDtZKm}LWaLyVM<1}piO*peKiP_ z-W=`uF>K|4bGXE{c02XgjDR~qeKR#|C-mnx-g5i`V5+`mTzqWW43_M?IR2p-b{TI{ zGmyHAf%mp){Rk_ppH*!{5tV- z9Y#zuv7vcQ0ZG{{B@9J27VkZXy?-=G>ltMyFQY!6){{0Lt+nyXC;yl(dm`d~IH< zVS@GzrZAqHMEm`fgM_(8uWLU1E8FvV?D0Yn+g@~os8(pXUI_Qb%A^sLUl{j5A?y)Q z!W6N6MC@_DUZu0cm)|=zIp&<=Q^wsqfqXAIO_lw+$*+WGJen7wjejc#8)yp1&SmO zwjNr4{#`gVBV<~W39_kajY6Grg;%6WId=Tr(e#ed?J*K!lIRGkEB(h}-FJ>7-xW)h z;QKlEDoR2dLMwM%UjaxfpovU`0J8l+&EPd1CM_ z-=Y0lEq5hZcax72qqyY4FAOWN5D9YaHssxW0$T#Go{8A+7|qrZW&bcQ;Em22>nr)u zH3H2(p=RefCUxluQ+N9|fx9X0#1{Clt?r2DUC=9|&`Ima_F}47&9@e2A>!uy-=jtg zml=#h_wyw*zXkRudTQ(^@#^gW(1O^78d6etIFg~^BUzY~X0_oAmv2Lxwx4Z!gwz2jY#i9c~K}BOrn}i!1IJZ)Zf}6Urp6MHLLhjOYfvZm>b0+k<}V zl=|F;H8{lou4a8FtNGFOB6oJ%I9qtNK36kmoOe!a&xTk9)1xxep+gOmv8N!<+ z>wLh;MPJQBRm{LU6JHyRHeXfyqfJ>o8q*#4q}$`oj;pu@WCwO)_NEd-vIEFW8 z?S!mfj%LK3Jf4XxO&&Cs+uzhaD9%d*6b}gwpfdCQ?rYUi5n@`j99F4&06X{>9-fUe z-u)Ul7j`5`Ec<>8MkJe^z&zr8Wv%WJ)6~aiU2As9T!-+C1cBknH7h>HVUGw$o#0}stgdrLkmHI&6Km}rkC#~S&o?lOkQBRctFfUS=5zi+JFexL&c8FP%Z;~Nv@ zAx95=mhWUk^pFF+*mj;DOGlh~Oz?1+Qs%VD^1DF(IMf^c>TlKwO{{`(_Xc> z6B};3vm+?a!FSv27p}`vEmMNWRm?t>w`)I<-aSh<5A{-hgFma5R3%20(9`IQ7*u84 z8%ympCYy`+E%c<bKF?Du0N!*%!>3yGDDFO@Qv50`*p7JT;jGZdZW?K9{`L4j8CdD!z%QDv09ct4eaDPZ}pV|IDa@aNbIg24Y(O10p`VySSE)`f&T{PR+l-Z4Das; z)`U956`-CL^GLE-9B1In!7}XSHUdL%Q9~_8#J*vW=#Um#CXz~v!F28 zr(AeRV4=$ABVp)=!w-VzFM@y#V|cY{?^R1^sB3Sku!M__$VX63&Jq9G@Wijw^eI+4 zL{e^*tGT-@F0G%L2`J6!NLMVM&EO{Ff34>Zb36{qwTrAeky92G>>`dh4VSu#uxV3>f^$8&K^SQY9E=MDOuwYo2# zFQ1hI=Z()U=Z$jj*`8neUF{`2SG8qLtf*M8IopYpdXOJwmFJym5aRVWe@ZDtj7!(F zG2FUqEo^U&0e(^)NobrHib{RExWWjDZ|o;5K8rNcK+SNXw{prN>980&{fl0gzPHjR zPqf?{=BpzW<_N#0sJO%H#QOGALW&0nuG=?&rJ`O!^IboF=3z2z1e4m0R;_n05E>bb zOnD+^1mmb-Cd%0aMvYr3yk<}SLXg?mlyQ?*M+l5|E; z#^7ftACh!mwO@r`#|wp^S=3!wF04kGrx5}zfq3D`y&%ihAS{IgQfpYzEUPbQwHOXG z1^-;4Lr-^Z8L1u)rjR1u>?I17+{*^6;Se=6?H_Y^3NTb2_gW&3c0zy-Yw342qv@NL z8V>|_3jwEDPiV*A%cOpU(Bnp_{RR>bN8jH0Z1u_KXsBZNMIe0>{vF%YLc8=Ik;?OW z5@i6aS%ZkXgCon?&=U5jYrg?0YF$L+oFqaFyTsd%n>o>I&OO=I{F`ZGWzX&6YB&D< ze%M9=wVWtzQhB%pYIA2%Md>Ac5sH$TaLvT+pPgz@RJOIRkq#-|9D;Z! zMXj%|C*s7B)9S*E3vlnI*x||=930-+MejB#8DJUXT#f1HX+Y)`Z78H{+b8*^ktn;194OwCI zg<9LNyA+<6B>k|@APx*yP>J2v^Kd#kx91c6j^i4f=cNVEXEkoH3mQyag62hLP91T0 zTUuuO;Y#~$!_#|S)AweG%7)dk&8vYsep)OG-tc;If?Pq&^qMn|fPWkj9MNf# zQtxQr=K-w9@H5#oO~FLJqQCWh9G-rMt6}@QqfY@Gq>3g8nns^DB?57Na`Ct&zlfCw z1@4A5buf5@sp7?xW%lzTx=yxs zA4=##@*L_c3<9X`%+9d5P$2-^YW~swgvqwWt#R)ycmL!1#*02EK$xD-7;!w`F2eiS z(3`c`mNH{=Zbk?E1tsE6>GXgpY)%6<(GtAxZdBn{=h**1n?)u)`pdle2LUibQ1Co% z2t6>IEPSX1t_o*7dTW`<&Q||(#UzNOL89O3O1@CBs?Hubuad0NLV{~CEhhEBjKbbP zpQ3K^7vyWCBfvtPV*@S+t>+w#uXIZp?|Cukqy7y{P{V(F?7$}QxFmkQBTS6IpN7#n z_C@qrh`AfS8IrJSX2Q4mgWzmzzlQ$V7o~1XxwcnvM(04ih$bvr@ihR8$;8gk17#p| zFF75eLI+e6ePG6_4>DKTaEYP??#Md^aoL!SE9CZLBp9)&a49l693Oh0mm*IhTZz*u z`uEDW=yy&Bc^xDS0(xoW?#%xfR)?$hOM3POPphrYzp0{bwUXRwwV+&d%+xHVM_uWs zc}#wpH15K*04>SvU+m!OJQjogX?iAE(Dco%5nAdeX9@&~Zt8lJY>7wR+z0XHFJd+c z1$!plisKz!oytE?k{?#-pbD6$6>+=ecY)R?hfPE+0QC?*k!dVxQ48?c1)|RdAeySu zWn8OUT7*LI1-y0BEL9hN9g@L7cL(-Y*YpH8rcbtWwuZ2UXgd3Azg!h25FMUMWX9&( z2++1<2lWt!7Uu2(sD7{jw8mp2huj;LV!v`RWm1dKN_68?NfWLaE2$ zPQKFPO#SBXvC)XxWqgeBp`fTS8oKuga7kEbt6Vo=RJM zSpVjHgbXM-JY!{+2;ezW{o=bTF-CY18gRVU1m-w3C(7fW5)6U{EyOw!TmttP?n~5y z|J?|fy$>P$bYyUv2PC_RMYO#0QhEz_Jf7Oq33 zuH#%xK5n)bBse{JVa1A1%nH2bghVP~mX{#MgG3uhIB;DEz;-bMIEGHbDHrevf~q|u z?Eio_FPY07ug0{uT#bKY@h`H%?>#Z_h8evH$=QKntKJKWt7knNiwJL$U{WdLF*7pv zN0^!i^GpUCYpev@JK7S0ZFV-(iSktqKR z@kwIeirY+d#Ln?9A88c|0k~}*BEJcSdKq(5q3?vQjh}w6=mx3KS5|~V78iw@Dco>s zHsoEDDvXm;;`9V=E_O%etXF*{B`?oTp#Vi&>s&T~&IXEV=Sfw7sJsZ}U5x9ow~Ne5 z?WaYF0Y`E1NA(deuRd#kq&)w7?|T{rkwnFXJP%M$(HDq2x2bbVXiJQ&jdCCA zwlrs&*2|DxyAc$Z{CZX8Wp|%n;g3DZzw9A@Tc*D%{~OA42bHiP*4bn>{D25?TKQ}8 z_sn46T#W?}VeK!sl`cc-EyAG`K$8JKJ@RdDC$SxdRLO5|>kR2{8L#n81ny_Zc@CW< zU%grHTzZJXRO(6qe33#D5)~vL{*lmz+A5cp>r>Ls68v4D%e0X08Z0OkhOKVdWSK#M zyT1aD95)MeqgWnmXaI9^?-QZBDd@U7p%Bg(^l5gFBgI?So{?egLg~DSnD;?i>`EIA zLK{Dcv?9VGp->1qqDvX#pghEYcuPDqcrp0;TpDqK51DPc63~Bqr;ejUdO`~!c&7Lr zocF~k(*$P=qF>A;LL+S2PMU5o$k`~|E;t!(nno;Mq!<$G`0TRk3ViN z?Mf*#5VU)6`I?hGjH9(P$5#>UQ2_>;fI>t#L_N(D4#9FZ(EHE z4R18kcDh&K`QFnuTl%5XOu^!js}yIxTuzl?Pz+(I%8b3~2-!@0d}plEHHPXhyW{KI zpR3OWYO8lY=G59hKOkM=eu}J6-)KjW;B$d+Ibe_&_4u4djPP^p%D;_f>xqvkUIVu- zu77!_0=euXpRm|x$-=}gVBNYYTgRq zUxMNxNa5+0jFOG^9#KFCl1-d#X3q*#2b=jHWMx!*$_xK5290=!TIX)L4Us|Aa+LTS z`fMM5HcbLaolZfII>WEAj(cw^1?}3=ED?NYA$pQ{H>z6}rXx|O()iwx&VkS3c6ZPd0-UPA(~Hxg4taBuo@9;!wJJkQkWH zOa!F8figIb0{AIkd*M_UQFZna0G1*~*UbZP#o~8aZ!>Qo>;LT6-BUze%Ld5la#-*E z_;kie??1c1`+G85zh5hP8KU3y>CkaH`-&t`^ykiX-=*$xU?X6^G?5e~awXz88|1(= z+y`ZN1;%K32;f(prl#~DZ8+e+;<%bT^6&o2OS7us9$Ijtks=%zfah;lK!y6hNP0A( zW53pF^`T^0McAJgL{@*iVH9sHRQsxMiAYl=1qsgil4k@j2YDZxvZ}RPNj*SmNl5@8 zy5)C+2AEu)W8_8Dm6w)!GVfRN5qwqAf?f|DqEFOTf+uH@^WL%VP*GY~26&XC!p@bV zXptpPg~&ODp+tBL72}m#lh6FGZ?0<^N7fT%tA69rN&K++v#-7?8$t1u7ZvV^0DL5Y z-Dy685D7jC<%MGf6WraPSck(^D7LoCdcvCkcXE76c>5IToebmJX6+|KCyMX4fBlz? zYEh>FM(*ieFy9|aN%@fDAVS1efNU&JWJb&73NAq#Xk%LZmXGf!V49vl5*7ze_4l7f43t`- zjzHLJG9sg3YEXHkR!3+={qt4cbLG7#NkI#*DpW@Yc-Fyzn;lK)v}Zk#)>ED@(J1jN zIar^LB$o$YdQExisBYe`!}x3QDJ>4&*9PD-XJ$||EgQC{otYlsmi&jTI_YY+nP7ca zS7q2XWJ55kAXKwpHPCHgr);mG>$Vr)4jstq^Vb}M@fC17HDltMrqzn)RHW35`!J?!=EV`SG20=iPP*N0? z4(Zw`K~VvblA45objQZ_{`P;~=iBpbU-n`5?)!>!&ULObCUBKy05Y&hkd~Lvp1S_) z#&YYHNDaNMKdTA##!CK$+7au+&Y7U-$Mw8+{6L(ufzviR0s2-oBBWNZ>sUJ4Madyn zoC}-If#dtyf%_Ey*!H1tzcqxmT!Wn~4e_-Ikp2Lo5v9SeJ4=H3y;4f*j)pO|T`~T3 zWxqXI9!|J(d>3#`Q>Klu*=xG{zI;FR+TisLz-5G#(bo`pAym zqAJ<2jr*x6=1nRcri&u~BB)#xQ*;_tx%+kdPf|!0mlSXn$%%M|hM$=_pZ;HcRI5*B z-2`E2UU^7sF=|;8&zWzlHhz&#Bh)taOYQ+k@%2X7&$@RVEIE)t-ASmH)C}q0{QQ&C z&n^AbQB+)ql{3rc(iCkaS`YlklWy)QS~x+@gaXRo89A z6Jn#nVx#lr5P~iy@7~n}A+@}m93uaI#7lg1#wdblf3@MZu@+?~!k z(M<947M9>cXw3kRO(VEnz6bTMaUP>kcPe3qs*E|8)+Unlg>P9*Pxik&ro&CcZ=bgU=T^1S!#Z-h^zSp>M&#PtMBF zqcePuVMK&6*6X|1VXb=_PR%(86fTf^2pJ&p*D`Md-xxfvhDMqrlp! zz-^=Nu_gDd#)kgx$h$Ba6w=vb=vi_py-7yke!ZvroemvS1roYM6^|64@gA54>L~nB zw)pVqp%x@ZA?df+Vbk<6nViAt>h%dI26h0yZ zF7Hrwj3|U{;i*HFe1pCC4UNuYlFgu&W_9j?!$F?YM zM&k`A9=#h0lPb8&auXWC$1w721Qt3%!YY&NnyI~`;9lcLot#2OoIV2A?p5^eUArj) zH%uk{F{LEItUY@=;||}`H`#n8im{~V%#@QeiVvcOm7h9akW9P&G_p?Eyb;7rsQM~& z(YvaK)0wx;nI@&iHx1{fpAVgp{#P) zRBg;=f!@T8(N<`yEetg!w-+aZB6!X^m^ZF*O;P^SQ_nCvNEeReyK;P$+?@ zzX<0aO_0y1ryn2e3$m!g?Bfes!Wi-2trWZ_p#Wh1dqXK**+tzBI@hzZVZFzoA4S#q zYQZP2phIaRn}yzIu&TU;gfbr%E~*5JsVhW+6!q#S1J`?AoQ|Y}W?RcOa~k#INU0sT z!k#h0jv)KzF$4^?@^$r@Y|oB-41!XP5l=h?Z=^hCYuHjhLG=)fUvf0Q#cN+WKHkN^ zzUBe*O$i>^SY-ki9;i|v^J>ERH;|W<wOAqi$1+|hW=l{!?S^*-P4K*j9x zv(8@@johg9ij&PgFPmH+iY+z>zMvBR^;l4!@6~VvUjuU!CImpu4^0?oEPZaomxnsOtKY+!IFE4yUzgtub^Kr)XjSDQejOI*7~=+_R$NY5=mA zs$jnW8_RrLQOd^0bNu(_SZhaug^22y7oa+d;zX3J$?eI#a;z;ry& zR}QpCV(~7v=M6D?5$~`WQ%c%0r77cFP8g2$)T#z<>J_olZTv8|0!mD2sXF`^%aeG&O2XhO%QDG z4M;#Y{toc7n=$3226>%3{#T{mJQ*CDOc3|`v$BQHcGv>`K~Z34P70H zx#K=E6)(k>x_*n<2balkb=U*%@Sv#xia2i6`JJ9CV9()N(GlEDqrM*cwLN^nurQ@A zvyYC@anPd$yK@0!Z-4TxqLg7IJck3!P*1Ocy{|Ypv)`-eb=Oyt?fus~GyY7`TNgEl zN_x(}iBe}6Uj5c4`}6UF@peRKNZqcW6us{&n+!)dh&)})>Qi&a0gBI1nxlo83J>%7 zA}Gxf;xO0Y1PHLk!Uvl+oy4X)xh;vTw|^^maCM}==6v}wiQygEm8#v1pM#Jed$gtQXvVBG1zO<9m!@&}JYlLbKm*E9~1ao1=J=lwnb9$WwJ2ZwWu=!7c zoF%xrD%1X$ytm#!_nzHVHR%gI$LBCWin9B@_cz@o_|Y-Rbn%+pq|dL|bZLbn<{l>m zjugAzen5cYHd>cbGX!o6r2=qehB5LZBr5@(V

?bCp~6}Qd9@z&uf!)sT5I(aXda@u3@`LCWG<9(J& znHB*Pg3L8iTv(Gp7%mEEj*#s;mLz@RJU5_~lO#DKtjbZaniqU{(9sAqMZgN)Atz%_ zX5n~RkWb;fh2gE6|Idb2s3)ep&5jw9ccDhGH8FU%rUdupZSU>~K8^gb7~qUJ}HJvg;%- zdALt;u=O$zQs2Hgn!HT^Nvw$aB@tUmzH}I{9vd6E;y1z0Fvj93J{Rkvgbn_uWo6OZ zOHc?_+JdjQW-m-p>yW6=hp}03B^W`1A$kG?tlsMk_TG4^xr^oLGd3an!3m{*wMXh| zu8v6Dc~jnt^ja4D+LA?HD}6njWhZvin|)QGg$~6F!%Z>$G>h9!s4#MEFDdS^N7ve` zI&5aZxi&#a$dLHUgoTI_JN<}w4ceu{orOy?0b=OELzJ)G z+M@!^Ahz)4%m^!QJ06m=vvV+45w*s&giV%|R@7bj?#W?~)IOf{zVCye2WGWftjii6 z>vY25S@YJ9-^Oz36-5791^nmNZD$~6qVML@<7zVg5UAK!db`SY{wRMWw7@UfbMl=szhlfUs>1T>5cZA+L7@&Nr}CJ7)M4%hlMrCiv`Rk0VL>t09-iH;EH`+_ zy0zj*{|KyZJxtYwdH8bPNNjwxxdz2nnLi9Q%Fi=>B9;0!#QCK#W}eX!7V%S zT(F;0^q|fp^0HQnlSY;_pAG}J<{l{zWpo zcSuoMpmB{ef;ol9af_=EHNH5+y-`em65Q$~x-Fb?!Lg=`F0EiI%khmbij4YMvrRg6 ze>_!K14mCsVo)-8Q-GaLVzr<(vAs-!nY2M!YU>0T(Fv|~CG_s8eP9^X?kcIUvd z)G(Fz$_*ub%Ji4twfuxnHL+N2_j_-xeiR0*{?g^8m^X*D4AfJJEn!x-JE(8Bg+EbT zeplvaF5=B)G2+{&TmYXu&UP)#caQIE(ER!K>{vie@Bf??{OQ@@=D5;ev{uKXYfK@q zrd-E4K~YEHHMZe=;aZ}xhLR5w23!Jn_Fqgay`nCsaiFu93h}4GM-3QeCgA=+f#L43 z+)jP~!&uF32~WULTp|9^@@@<^0ERhAub$oDwh|&46#R6Vzt#I2Q{pvFLI(?&6Vg*E zuz)rfDp^BJRZkd5l<|kvas{d|hJT3#ny)uo%-Zgjw@9?V7j2E?zxd?(e_8hdE$e>T zyS+-QS&t!J@Pa4Su$q45Gfsz{6p6aAj^=CFT8@)`o3P%jlj~U{f6J3i3keDin@f7r{@t!Gqj)o*q9&8*mFS*V2M0r79o^kE$!HXO#< z;MDr%Y5%wO_Yh@~pRl1zz2Pk@N%ungl%OA|nJU(RCr4-BmBa8k}^&?%rBR9VOYV477R#Z&tQG*SD z9PEq;v2GaV?cz1yO-njq)2(z)E^uv?wMO3j(%LSjE7tEPT5*38S^(D+q^CLA!A&@C zN`zd6`Ni2iYGw1UX^R0HQA;*tA9Kd(1?9TK!bb4*>sJ#ktq4ipoY~`zfh#7T9(ueOPqv%Ve&Q*_{IxvT(yD5sWtm+S4jO|1s7BZLrAY%`j}gM))z z=~UUTL9=0}h?o5obQVTE1|?btJajGqTUqM&3(;${!}Vd8}%GxSZ1?I}V~hM|$s zsZB6^=5Vx#T!G5rf*tIxt_q$&;G`0}({lufpX6^W?sxy8dm4YLNB&fd=^kBSv%x=l z7Ce!jphoAu>0xs1#tDZ4S!<{7elg2oOVQ6{ot)5(?tk4%@t>UQrib4k7yO1UoVL-z zRr7j-+~@LCFFFYjUHNWu(?Fd;8VX+=l`0p{F`t5b zCl0{lVRoIiyQYAM9gS{^bp=61jJo}QtozOk%wF}eIW1#<&lPLt1^lp;wN8w}#x>Z7 z-BecNWnZGHy6s0us5=Z?iVk;J;u31xZ_t;B2r^P#->3=P%c1fyi1+3 zH@!94^2_cX2S%B!X!p5Y16*wV*OPGT&M_POg{bl?l04NsS2tVk3Y+Y5+wMhNpd#dP zwGCLgZx8a+f~K;Qq7vl)dGD={{wkHW{q9Blb(8~ZaAB=_7SkH?XzQt2bKO>-q;Vh- z`RKhSl$U@*@N;P681+SHXy~Wb#K5F(d?PCYCwUpve#h~=UpZGtI@hwZ9dB~y?Ef^Q zytHQ2`_X(m)MsGAfW*rJrJS(s#r250bxVz#KR#eW>k99t1P^%)m+NCdXqmv&uY?E* ziBIK)+t?e?4Jf-ADrMBWd9CZvKl_9vRrF5+RsS0u%9joi-W()_OT2{XVnjvK``U4_ zC`N=$m0Zz5In5Veonp}!efYv&5E$vgKiA#2^N#-t7(I^NR%&J zljv9L{EzIg8gPH1JO1rUCMZ)bA|aql?`N}*bIrK7+UIyd5_8;h<6J{>c=^`i4t+vV zs*Q7V3IcRqig-;^In-$35IE;cm7X@V&pC|d$AUd$`+g!`wPBA`HDX`08Do<O=f1ccODs{5mT|bY$16w{8|+@59}$p*`~cJh5BA7ujB2Sb*>~ z!rl^(j-vM+#NIl`(E$@F#aLcGVFuGtKi?D=bn(-oz?WBm^)7DqxHO}JpJED^J@@U*q|+lx4ft6CMP^P>0*W51WNRxLABp8gYW<7 z%b+Dc9e*Dh!w66$Z)KT)U0yxI1q4>Po?N9>tF-%4zufyXf|K@nH1_@aN$%5n=&0fG zvv+4QOV2u;Wn#og@V2o0-L;{{S5h9vk5qr< zSo4+#fo9Ju_`#rtJS972=_)Atd?X20&kRnr%9oA?H$3qE+;U<>sLjko>-nm4kL-ca z`p%18>1g~6ePVj*qUm02%WtbAgTZl|33Yn`n(BSyX#E2^+aCH*4C2w_F1zi**gqeR8wGLL# zrd^=$|KkFo3}M9M93b9Dgk%$etFN!`ZaF}Yhms|RN7Mh91(ZsLyI=Y<#y( za>j(D6JmhQzjf?dCQBaafc3HU$=Ni>F>NkcDxre6ww1MTW8|-wUs09x%8&F%+k8n3 zpSuxX*Qw3(96hW=&M@yOOkxrkoP*l-l)vUX-#2dWti(+?jvSnkyo~uKh8tjj_5}4g z3Fof3+wAY}J3^0QQ%<{bY%y4D#5e|!o|V{Z>;3M!%I^3;%g>j+E%zklMFW;de-pt= zf$tp+MVQ`X!0VO^znWPbJ5*WVt6{N(hahpuuzyBJTh3d%W90M^%hp2yl=^wdRk*?R z>GWPC4@^NM|8e>E@3p1S)%v`-wXw0${ZkfTB0@^%ARCMKf8eeJM;l`qf$}_Bql7`; zmxYRi`^&w(kTq*KjsZ5R`+l9?0|J+7FuD0{o{HozkqjU|jZr>T((LN@l>5v+J~z%K zD{fFrJ3G-zwl`;gC{C?DCuuHXMNGy>%fkfbr;SCpW*wr6`W_rtTT-qbHQ?N3XEr!j zQC#KvJMrxJ!2k<9|6gk}BTb|*&z?#qt8@nPBASe54O`B|#76eW%? zgMSGobNkGtUTcorI?)7teR1M&93&;~wy%=8Zu0K@iZn3_v6ls~L4G56IcSxkl?s~$ zKPMrD9xZDL6wq_4&@usP=w_)KE1l~XPUzm%C}_WpN_kwawj~ZOOql$a{3`fpVco}Z z{u?cKFXjzEbRe$ku{DsM7G-@AOQ&g0^ed0GUc_B%b z>b4;t1CwI2{_a_OQ2$+}DERD4mC={o$E(4c=N^c(0z~@;3^76X^eN2J7l&et1|r%| zo^(HCF)}XOopb6L{I%;Si6oxSC82ANG+668T|5>74SM+60Hs1K_34eJ;cJD%wJoeg zA5sH*Y?vAW^K}t7VUA$(QDZGrOGRITYbqtYJ;+@T|7avNn9(RmWdi+YHC##65fr-S zB5!`{6~h_Ruc`8`y&#eze2i~U|MdJdUvZ5X7S9yL-N@;fPHydd*%@&I0=f}M#yuWB zpof?|&j_~4ZFiPNv`rOgE&Tg8|F!U5v<7rBUNBQdq^wvJQr#r-+vQ_s&$vysqc0Dx zF_j=D^8`6MS(vH}cyn%77>)V~zBM>MPuA zj5+tZ?Vl0Pr<6!CO4^zz%YlTI23GnXJm&MNMG~gTYJTlyTcJ)LEv5L@;9U`lK|-o0 z7~W}6wA{dfDLb4XlCmiZ_3Yn)XDjryIcRC5RrKmIP3QHAQ|B7MA?_r!=+6F-Yq#W+ zz=7ydCC6dc&tH>e9^S)UOWhV`Qj1inJ^f>biY?XmuPdm1CyZChCNj38qxV+_@G*wF zEy##=U+RuCRrLsFhl8UL&ZWW7?k&ZvX?n$i&Efa!=~=Ev&fyvG9F=onnH5aU>BI(l zb$3z14wkWe?D)GxOlANz$J1{~?WPd5nSTlyf*83mTXnB??h* zfa!f!=%?|Rq|m$JV2m&NGYQfJ{R+j@7g6Qp2g!FczEF)_^n$BS<3B3bkj38Y?)hQJ z&sTTpYZipm{+tQjj^Pcy#c^{MmbgJz+093cVicd@AeaQNTDSW2N-$f}&8zfAi^693 z9x>d?;Rz4rz0frH0C^VYa+I_2X9R4rjo(G*>xx$t$yRvNh__6a@@vw6Q}hjlj7PvI zEnbzLY;c_|zt*di^z0a&gZ;BVYL=DXKRf(m>xIx~LxL=OD+$urRL@YXQ!RfED?Res zf&HzVOcFkW4<(1BRlzr^Tweqq!L4!wqlr7wZ?qeb^;x-o`&=Ki< z-v~V70(ztOTdc?Hy(F)E-t$Y5F=HQfg3VSBQx#?=Uq4RIUgay*56o*Ut1b2UULHqX zpX|DJz)C-~AawaXR`H9KMg>}AKl>#@q3;SN^8_knsj~}@0*FP0>PfLR*NdFw9DWY6 zQStQX+NEki?3E$mA94?d$gDD{`>_VE$9TnsD^NIWk^(U@TUX}Q%$)!1E?MymXVAz- zPeQ~Z)ywYei81Xz$GB=Ki<+SFK+=(073P`_87GC0c8pJEEKELJuOQ)%!U;^lMNVv^ z_0`emonf2={HrGQz*_g=OWCCXb9=%*ACbf}R;EqhbKJ4bOlX+tQk%))4HXy$q*>SP z>Ht^23;C>24)%PE7@PF2-?IPt1p+uQ${Ms%<~)jXG)7!w?w`vh8^qn6NHjcn!cr~g zTvfgFJFOqsJ+RC{6eY_#p@sai5u8G?i3rNb=9>!%>S4>r)`tf>{(%C$sZWg54a}u7c6=Y1<>XkMC_g0 zEjt&)^5@zu?FS4}<~PN@kg`2YWE!RrRlHG_U^%+EV?%h8AH#|3TsSdKviN#cL*AH)L!mtfmqlrNhJTFx8 z1w0YkYkcrLI>Qo)5y-@b3FSiLA z?Nx})cP^iOQttP}E4ef2zmbD#8|eKFM>1y1WpY63i+86$BYXS1)24-JNj?s43cMYn z`>fLnAoTVA+nP|8{m!4!CW3_e#U36&GunSWMMEyEDl-Va0q4xq32m*6O zFF}i+LrcPxgJW^jL+0Zn*4f>6I*F|r*#*V?W~B$;hZdTj&u?nYz3Ax~9h5V4!>Cha zAJ$Y%@7!K=>T1*Cgp^o(n{0_=RKIiq^VQiZ<3x%#0wRHkf9NjmV*CA7ALMFdxW|Ul z1QMg*zOtcfF+FoG{+q=9zvi>93BH~m?Vcwy4& zuGQ_qf2I_VO+;>~=l1T!HT1GhbE9Q4|4qve6=c#(w&daF)Sk*Zy9gqB3TV3pISE}_ z@L84t_YXoUVqo>l1>16qdNU(c0F^mF4r)LArf$q~HQ(QcMir)#~bC;j!w4JokX)Itjwv}I9 zthYL|Z2qOGB(ssy9D8p##P0m8;Gge0>01U}WZ_LV!XEZRtB5jMw}9em;aG9UHLU_*#oO)|_ic(h`>^Yj!%DmTt;4Z$K!(-P4Xy`H~FY5A#yL7Uw(Q2kMf*R`Si%{RZaJP zAB)tdNR9`ScbOUk)k?&(?9z*R6_#J{R!{Oj;{%>@FZ{<{AkkPAO92y6sR4=dXVh_{ zFbxJpY+K!8?(Gbpqa)&5PUA~@N$oX^XpvuYC6Y#zrOyqU-7^IGdyXoP1a`643ImuPzu%y#%fo1f_2@ZRCy}mg( zMrc_S-u?nGw4c&1Bjc`Lzs?M;-XOUhN8pq=fP@*L^JnmT`-nNln|PVuq46NgQE76C9P{4D z`(dDr2Z0MZ-Srz1STIK%Y5$VTzO3LnZr&(#T%guRYqEjrLeMReom4*5Z7+Y1_9DsD#~kYR~y2 zrpn9dj`+Mp;Z-YTrB4%v2faH8jh+}hW`nyGAa=1r%(%%X2B2jY-I6&;WI`pAHdnc& z*)gX*aW33=k(EdG&r|tub8w=3=rgC{;^_plvYrE3S%#YEVZqo}Tl)yniO*jsO8x)` zTxF0T3c^1xy5JzfQ}DKxm4OO)A$an40_}Fu({5L+&AV6>qFNeJaTyFBffHc7zntKh zGhvY;@JH9q%3WGYv?8}QU7750kXd`We^Tz&AS9zc^f@X^6$)a0{n#BKEa_sFHK%$= zz0U*vf^3C={@!Q|fVf%_gf)7DE9A}vQnTR5LA|H};6PU^fF)ghu8*`CZmmj+$CNzu zC^YfPJ$04paZ%=r5e!od#P>u=PE6M{Gkc4mTrNIV}pC zjGU}fRkyPF<5O|a{RVdX0?tQFS`{GVdB>XVO%)x+eyIl`U8$e@w0}%Cm{r7FyN(&udv~YGqd5uy(QlEL8RjaW@E@9OL4(QaSBCpuK{@Xf z{2B{Drk!F!N%Ci$i=Hg-!b})pp{0RQW(=rtIPOjz{xx$7^}xzIuvFn*wC!cbKjf14 z!Y{BYeYyj(s{hCxPASFAG>Fr;U-0YuEpJAJhU-Hv^vdz!KEEX;_XJct zc452u0&B*P(=O!Fxl4-Mz4-GDG)lVdkNLKQJCqh1f!mpbQIfH&rIkNu_8gIq!@to zU(ouNeSJH-xA}oGOd#KB@k;RjY60TK|5LV_CP0O!DO%Gr6zZK(B8im%$S3+GBG&Fu zxTIf1DlFp4WM8Xz{9R9Ze(eLm*;5oW2^Sw?$q6F;x*oCO^04fqgIj!G(CC2Y3L7`U zGd^5RtSn@9Zg-Lz+=$nn2y<~l;vW6sYX!EgA>xRSpYxif6D-JGQq_6oWmo9I$U(yH zu@dfU_&w#4R4vJMfAesdaj5JBbz}8@P2L>=qzp*=F*3*(32*eJQx!gca1$5)4(E=< zIWEr)0L?`-A6Ofe89U|+llrvZR*gsi6IrFkwTCQB)R|#wdwyH?)iuuXJtroIk>AZv z=c&!bq<>w)r5C#YSP8!z?aiA28CT@vj?v>FTMr9U6O+BL`v~Bc4OO2jECXhXtPeFI z?Vjtc|K_Z}LdB;*O}wEh0nahaj)H$|1**S!Oeps3AX{_aY1G||JMvv&OqMg+6 z|8-Kg3nW?L1pFVZG9B+vY~0YAN`fZ`kW;IW*r^Rbx6{J zU#L0H$2R1>#p)=XO7x4F*J1v8+*GkZ)q@4>J13@h*$vdfb|44Goap&(<++}^Nsr)x z-JPYZ=?ywd+v0tn7!Qv9ZJ@*sVpW)OPO(Gb?8O!+J4x*~rE0+lN4Yp6X9C`e;%2UY zyFohHZ&iTEoP7UkX>Lcjs={tZ0je}-VRkA&GZdPoy#;w-Kn%4m3{|^*q76P{f?aS~ zH84eT41K{Lb8z@-zc`m%q2mz92Fs7v@(^2UG&7=Z<<+FCmw&%f*2JrBCipx5H9vPo zypY<#yPrRQT6Mtz9xWIV@&Kw%%Q7ZkTf_U$x1EW}kGv$##JNmkGQ%l9|FNBA3F=#6%jQK$dF6Ai-NiPMFjW|M@5q@M{2wE3;k+~^P2jv%zDc@&dYJE z6l5-_jO<#z4eNH>Gm3;ke{Q?5ag3%_^gf>~l?m^Z1vL0^@#LDy*^0%ik>ujxF|iPG zp(YSwMg@1bLk3DY%yb-I@TU-jWU_}MP7v@6$kvc9%$gU`&G{U<-p%bq%8 zw+>I#3Q5z--tCm7woEedVpYW@8e&KE5RC0vF6(9*FD<`%tx4L6YoBNKND? z1KyqOB1*=m% zpjp|}Q}}=KaKJ~8!}FPeJk0v=4ktjxZjBSKAAE%Qemi3!>8QOK$P~DCk`KKx7AxOA ze=$~gEOO|Pj@I8@@+yA&(C6|yu#qY$XP@lkxYvGhviO3H8$?&zfZ;bY8>J|U&AET9)bpCGHW1JZD*BJaif$AX1)Q40~<5Ag7uUu+O<_y6y6A28Ev$w z)}TTZC-Z9es*-rV{)6nY&iSI9QNydlXNRiGCk)v{aiXJGcwK4)Sm7Zsp?hRdNZ5KA zSe4T|!r7Zl*5#lJJ+xIWQC2Cz4y*Q$R%)x0XtwFy7GMR^dC!k zqgg&y9mmwR>;9GH&yyBSESuvi=OLk*0aNo9WTGN{ENAxvksddw6tX874O9^r4p`$} zFf?&)B8xKG>CvE|_q{JEEoWw$1_uX+0@PoiFbn5s18dA}NU%^%iJd{mArqZ{%Bu?t z9$s2n|0&JC)O+bGWhd5XQ1?`kG&;MhfxCjNyH3qbNAoe*F19P+h{}X66@b7QBmaS(W`aBqF@;Wf|GXDMdL*i(;#}=~p?(!N8-5V^Y zRSJI7e=pR4ovQR=(_&?+urAI`_PDVAUNID@Ie$5nboZP;*l}>wh(u@ zmeRU0o$$M9>-g#M4l$-R_lVV~h3mF;3X)9>fa4QIYU2L)Z)gS354Ic8wW8v_%ZwVF@2Y zo>n4v*__S*G5q$Y>sbd}Ra(1#eMW9s#Emd<`!&a+Vpqkp|; z1TH`xuM4h?U+=d8;wwMy@X^8}bB?+bPgPh3y=Tt%mqQ{0XcPcJ6KlW?|IrME&cpz& zK=M4Vo!SC4Mo6xP#=lYN>G*3XuuG*w9kKS1kiR{!YeV8?t6v}s^;9YS{aoSm2Bx6Q z-bKywDmpqw9p_8EiQBORS-dC?tQ@=+2`o3zitM-y7EA@Xn+L8pw6n8BS&A{Zsxcq7 zx+BjFrd;$7yhMPzC?-KP;waFYrE0PRizzhE{@e;RgS_dIkaA5(F#knxUC)aCN}Ks= z=3G_zTko1!Ug;*J|8sn!BgA0Oz;UPHwe^%J5o)6oBrY*Lt;4s# zex=0D$2M9seBok`!lAjZXSjvCx#q{RH(9Ka z0D-^g6;UZNy&ZI@Kdd){1>?}3&rWQP+(R<1@!B}@Iyv8~nN(qo^8RIh0oqzuWbT2A)V zsRNq|IN+#zRhH4$*S0woWG_E>v^|NkpX9@KqJGh6Fo5Iudx%Smjn{&-6=T!TM7&T7 zUo`gHlzcn&gccBC#H1AfiHvkJkC95qs)(=qz(3rI9Bm2eU4aS$^Meku-cEc9O}g&S zS%QKLU(TW?fzVq&P^3J`|`RILDNT@$Y7z=u?x3quw z_$xbZG$(M;snDp{|EFt-NkraYMbI5>a~Z*~|B5a>nu=ATFRz(aba5kzGCuNGIzPML zw)-5XLdSLe&|LhyO*`)I-@gxOtAOrE$mtEu9S$A6ajYTei{jY59*6MV6TiC{R^z^W za+=BBJsktaU?}9e0g3|q!xw{Xn7l28fyBRkCop>Ou5QY;2~eI!wQgdX_Nvla`Z45f zQ|KV``#DyBZ`MVH9XAgnV7vW*CMQN+I9aCRI&dwgc>F)AzA~<<|9$^#jP6FI8);EQ zL^eu6(xAIRK#)*+LqSTVL_mRIVbI-dA_xcw(kY`;y2rNvnV;|P!T*sjA8~fh`+diC zU)Oc-$k(5&O%fLrfLvxl)Bfq4xX4j2z3V)qkyn4@6EC(rQC}|jJ zBedIqhD9KTL}3h+Ipy7id6-L?lg!yamlUa17V$KHG0yGLAoO@W%GXe59P^Aa17!*2a_4%~UQkT1!U342_c zp-pC(fWv+cGG@;)x{^n}d{DCd>;3iTs_#Ivi^~b}vBH+1C+nPY909}c4>V@B9LJ9D z_YcrB!(B1&-i%ujN|E=EH^)B3FruTWq%9t2O?DFHqxZw$SB|-n^p6o&=r>5h|^oh z*)HT`8o_Sv?PG1YSlc2^_IaG8?d68(D(~mL7fp`Rsur%l3(Fk|9DU8`o#W$QP+Ux8 z7$1C6yWauYlM(4KiW`4Iq7+aWZy&%P5FhT-pz832WKRWG6&_mPmxu-oH>tkb7_BbYjM$l$TN^I64Lc!Ibz-8L8ymxYgO5)Yy46P? zHhcv`JE?jrr`pZnBvx%2UEXzmV)XM=wPyKMU)ONf$T?%zV9cWw&D*{kJ!?2h77;@B zR-&lA7&P-d#pkb33pyU}LU{+~#!D}ItZsc5OmTj8nuz)Sl#Fh)YNH(0KznUcXFRE2 zgILDQ0o69hq!YNK-Q5An~3-@`Az4@cZ#a6-&JWi(7#q;;=ar<{^R&!UdHZdfw$o081! z?cv{Fyq2dTo_b$5}Oy}^mreCqMUEz!J~f0tM8--I^w zXn7u-&~kx7FX_$q6Y69OLvY_4(ya1Ng8U2N-?u_2xvl(caYbT zJ0e25o}asH`wNWIUekbkHzw^$xcGg1U)zyQ=`kjA6?;|QY_QNx=Lt|<^8F?sw&C3eD+XYoWu@FW+pK6 zM1e%mx&T04*nXAC;-E5Qu}ZBW>0R98`X&SQ>+BojaGmW6F`q=EBiH%hx~q)GNtY;Y zt(V{!{XX$2{t#rw@(BXGxO@BDOAkK-4sXH~Wi-0;&!gEwRibw+{OZK-k;OC7h#Mlf zou8w>m$cjeDG#wvQHB6^#CPG8YMZ2!OpTi^hN32!V5lYppkNu_7!a%Q5s3($RddH)Ry$y5LYwOuSFx>RmD14_S}8OjS;j{kknf6h2<6{W)FEjTp57 z9KnGWaog_O^lbax17m{|36fdcaezL2`o|QhZU5A!mHM+1P5C4JAkA5<_m#!N12+>X ztOgCX6c8G~Tsp8nPvWlFbN0ld)@u^++}bDy)oowqjLQgO_)R;leXm`M?@#k6_$2_O zQCQXAGHU^6%+eusPAUFq>VOuTjG`LT6yJ zECYqN<^BD)AO6eD&vR_)#RicuzUptth5!RFb67{E7yBEtZ88cDh zHn(re14gHHJb6DUv1y0#Zy{7d*l5Q)`b=SqbGhqv#$gr19*)nBBtS&>CPn>INXL>i zJM(XP?5iW=^lkeaCx+3W{O%nOcGD}eIY%R*;xDf%9mTdv2*__g(RgOdP+3twdEth2 zH0q%kASMqYe7;MxIAf_lvk_y9j44N9PtJ0~yv*mz&H9ugo?}VA=M4DU?Rt%@>b{&v zl@5pPqHFlYmA2J?;7y4L6|AQ2APB#&S%1{@0(W3!XwZ%fv@IEBe>Z#w@RF`cLXMQW zBr{tm{6;um5j>T^VKLa+Lm?A`(_(UujZ(poP<;ia@B`D~H^f=mgWSCi*!3()yI&EWSc3>O6Eem=++o3cVwrJpy2;MDqPEO80%E2qH zaQs-Wb`mPJCV#OBeQw)3sw`vg>ubq#7lakmkADN(8INw!nArF}PO)|U{UjoA82Rxs zs*N6N!H8vBO8P`sf1zzB&hWBhsd~5-^8Zx&$ z(E(XGx9e>HVX1|~3m8My1fIn9zt5Z>{@6YadiC94Z%%~gY6$X2Xju7s$8YY|frlA3 zqzO|9sMhT@><}VO&nt@NzE*jl%Mau-(G1*@HE}Xvl9bcG%vqYWitIskE3p_j3;6A} z-ws!tO$rdeJUaG0`UA@axLfvA-yG8Jx(7`+N64d=4x(K;;2RXZKi-=_x9(HyERSP; zm-0#2?*ac8aLJPi+en%fDCpiv=rnhUethC*;JJ1%mQvEpD_%(XejxV_ut;-z zLQm>RX7Ce-Q#EFrVf9y=9EPSTys|$JW^2NxEAc#-S)br4^ZrP5jna^al8F4|jv<;N%H(dWTEF*xe;%U-^_NnU# z(o_<46IMF)rE8eG6mDGNHh_^koQ%2BX&#X{12}3zT);Z$VxA1NBOOqSy97o3JV{yu z)r_|v9A^{}XQz+OB}x71vR!!OPD7EUmM_zYQ(13Pa-NQTCKzkrQaC8;INOxZ0&RUp zs-3EWB4p;^HN@Sz7RO(>Tn(ZjCt7;zkr#XPr6OOF3x@Dl(#-mpJ?T_rn;UbB$0P;W z_~8W0x0lqw!nF8KXF z143r7J1+M%SbhdZ6YL8xJWD>6J0CRlPdY?#3u6$e#}S`;|IN`3t?2y|&~=zUI%C4B zbb|!ZB>m>n-1zg?A7l}7Tw@Yx z7s+%^UA2WyAy32c_>uqI0-`nb;j#Qq8TSdU%}$(9kGYm;kAOL0>I#rJqJT85NBnlX z7E+<%<+WoCPEhIHAu?K<*@0;^%s7WCdGv(=$3}%?P8=uOeF*&65g1;T2T_Ek$sweB z>W8l0@bz1r2qN@>`uWE|KE)5khkN|b_-Lk&YVL3I(|*~re|AdEPa)oglwrW4%^n}? zsJ!N36Et9?J=Wsj2W%w0qh5YKV&x8BN8=B8hi_kG(Es#9eNqG!29481Eq6kDgE`dy zWPP#0x9!V42I5%Szm;Vy-#RGAd+jpACaQT3J{f|QDo8dtLoQq|Xz56hg}YEF=wNuH zI&~*@^u^T;l{1rDpSCk}kF6Id^Fmn{*08hVbT9>hc&XG#&|Dy?Re;?=Xd4`}Rku;v zhi+4bjHQOGcb6HLfA(#BF01v>r7#<>>c?$AdA_kj!eIWZrLEx1-@=eTI!EYHWuad+ zw(7vhJ%DO9X`6uVmUi5XwEdxelK=8j%{!?9`(S7@ND<&=!liPUr1SJ$VnB%V`cnd? zvzWV`7|>BQEs`IKTDxONHG1oVb@+iz3b^<~H0r7cfd+fc$H@|MoQZkhKlij!fT$ z;CADC-v=8nJDrA{(+b93-ztCX&G5sO-CIW*bN@>Wz@8D@5Lx~3e@98w%eyAX>iJ-pO-^l&^I5w(_cTDZLmP| z$8+Wj+6%7n4J!GS&002vd{h8PZD&Yx(^|AH)7GqoY3zVgSQ#vDt7hZ=o-S#U?ci7U z?StRh2Ok6u=Z2B3R1faP13RHtI1r+w>f2Fj5@1h`!lVR)+c+5zVnF5vl7tp{XvYou z9p}v|5)_{^hl(TJ%YCPUTy`?EmM4PpiJ){M?Aha9-5(G6zE10NKyMd;mUXa~1u;jL z(?r>B2Ed+Dbaa6zl8e1EY4FXQf=_}ZEKk9K4RWOR<;{CrN(jrm9a&S)K~Jov1MWim z_Nnq8lKY~4GAOaHr41?#SfFQNCoLznE42|K&SI$Txpp{%i*I&f0Y(>bS3XC#u@S9yWM5mxlO5p{Jy)MH#042qoHU)N$s+*Vzxkw`U_JQZPyyxjqr&)t~$cE2~$tvKo29bHtgPy+ykzzf+%} z9VrHnDdu;OKj;9VC5GPty3%L;Hw9NT(MRC|)#4XGd%#*6)MLN}p9Feke|Qy4+FeRd zvbRtKCcZ1mM=2CAGaS9Qa!cH_|B`hp?Tp!Y?7xEEhzwE~#h{stz*i&%Iw8Xx3}7_; z034i_vlf zKt8tuW9*1M06Xn32Bbu1I+4DETmpYMcRx6b?QjHEgv&b*($*v8=9{GVa$0Og?vLcM za1Am)&OV_++nF4Gc*oHaBk1UHCYl9DtwkS-Rb`OECe_##J{N8yc2UP0>eYt}g%e9{ zER_1zt`Z5(@-i`rfEsI_#mViZh1K$_kN%Gf@VHm-*V}4vg_y2jG(|Z57|FOY`BCAr zQ^KZi)L#w%SAj2t|1*aKmlS{y#uCoxh8PI*46>GrL0b!dkH51{t}Gcz7Sd`g>{FEo z29)_5z@x;bZ*+J2oSsBvKn-%^BwkTnJw4LLwbc3`hKeB!mwyNVd7tH zAn8gha3^q%(GJJ%L^@XGKkFcLI+~qETv56?dZS_pJW>~*q?ub1jvp%GF}kN|tPq2b zP;e|)uK+|E<7zrjdOI-|BpcDI@7nHEMOr@4>RPiAz-bG(iT+{AdO%eu-p^|X+dUwbY1IHcHfN~*@JbpG(4t)sq zqE+!I7b&R|A_Z}1pH>dkCWE9bP)ECSN`Z8Pvj3_>Js}5uSX>Sj>)tBy256?OCAj>y z3D{cT)VF@nFP{JiuV|-&y!)MfwI95_9)2@{)tg3%>_$vQ!IvC**tr#na19ph^>>`c zm*4O5Apd;%`Hp%*dky2Gt=3O6WPQW*ipcEFEj@*A8VC?1I9|g7J6c%v9o%fN)K}U|li)81bzn@j(UHWSXTjJ#U2~G2ENy4D>Ho3!Z96M4)UA zs9Mxv9VdSfR1#li4HXmj7kl0~b48kF*L&DLvBr`ZR7T7XMqbgXkKgO^VciU5nGM`~do`nY@X`6D)tzTS+Haa3xviHK zT%Fa8Z})(FC3G!ex>l>NH;y7L0h5rfxL*4E?e6T_)-tNgJ!=a5*4X#BS0Ju}c{R`J z=!X0f=h%>I_WkS^b^X*E@2CF5&7ok5?|7_{cZY0M^nrDh>EPnsKDW$!@H01iPWIO> zSLWw0stV>e_|HPkNRqWLLwEDzZTuk!(bRChM+PQ&<&_`=zZQpJ=U3%7q={VLCFe`&UeqN@=G`m!$|AS~W7&m(Xv$F&==zd+eL8vkH(e&n@F9F^dteG3oDE)vo5-;}3-~tj*+tF=;vfP=g z|2buEho+roC-Vq_0R3bAOGoGLrn~m?G{Y<6nusBaE}Qahj6%9_JB@NXh!Yg6pozFp z2c5qM@izk0BmJ)+8r9Rm36o1L9@DvsrtJsE23fkWOgh@?zg`o+wwH_(ikf`FZX9G> zGrsKX{8ZuKlrbmZ`eAdk2!1mal1qa+{3Pp5RWo<3hZ2P={=LMa<({Vd+sFRsQ zT>FDgnUb$kN%Cd(;oFqi2K4x)s^@ken}xR=my<3rsy@&-BqDpITzn%e=PgfC_U2OW z(__9Ei%y&`z+^iMJyXfsg_)!?z0|s_&5AOI;-XpO#U$kA=BJ0+;FkLEgQTFL1s0S6 z4ZusWebp9I5no#HODuhNt+m}cWVu1kVSV|UDzUKiJXQxemN;~|3i;F^T}c!I#MCb3 zHBS4cHbSizk?UN$jCpuu!nZqmxkCS=P=M~X5J4^M?Hcl(cBIKOwUfF3QMI&87`0*` z1jlwF3?_8Ccf9xWy?1myLOS96`pv=HPStKbXOk>2fm-j)$9=(B5r;#mCbfL_9wz>S zz~rfGzpwDxtn!raokGk#fmU5}Qw<4*%;Cg|`Dt*E>`rikb4N*R8hG9MT#vc+cROKI z^APYfi=5O~>%P7%R8$?|Gx42{VyX_q6xmo5`#t&n;YOu$m+q4+{lu~7^3O~YJV2(k z3{8gw#k-L#T-JV2M_OyS^f}(RYm3xa=Kq~fY4Z6r`>w~WMLwUkcPS7K*=*1Rn#YNr zjNx`vv$5^E7RfwMWyNB%K=JbScheMuKVce5Z&YMj&R^}^CoO*ZIkt;F8rQq9eX{Z= zHu1bND|Qhg=xYoPe412TAPD#4%WX#!tw2ggfy{~OyWWv5Jh)r3LFt=OO`G}@!F6u^ zwCfHJew#HX!0j?AKoakLD;?z5#sD0X^OQf9mA0TqDRZ28Vgl?$M@#sG<}mV{W@Qdo zq}0ohN4sNh-?M|B<`4)ZFz<#m#^+mfRQlF~@|q|6qda6{O| z=rEF^&w5niTnOMTf!_Grewr^~-P9Q?Jmq3H@@YlIsVzD%4iFc@nq5IKQRDb|gKeD_ zk)|!>r4I-zf+y!40X%5`xY3luv@MdZ-cLBqe~wD?PDl5sZ}}1tYJlY`h6m>E*%f$f3ac?B(5^2S+`&KNc8vEL8nK6Q!~Z71l_6wfJyN z{hXEockwT0iD|Q(z?No%I4T>!#PaBlI%O75*cYT}5`igzV*Bx3|mlM1X2QxxT@Ea|o=#raFXoYL1EB4Q(PGQd9pEB;YL>?)9 z{8{jg60jvHs0*ODv0|_0O6WC+x6*CTErx#nFU{N1lBdZ19>43r@gIsdif+_`A1b_e zwvP?~!JG`p_SLCjw9Sq&7sv_}j>3*2f5)V!b?U$Haa|HvNvnKevl06C*)^h(UIqtn zo0Y>pId+~x1({5M}JE1qfuwuu(QtY0$dME z=Er@Z)Tgw`C>CzXrFJT2e%=C_Ja*vj2C3z@h)He`vUcS6Pf+HuI6X8)r%&UzOC<&PKNm%4vQg68H<+&e_QB8f^`WZXHL5@STjW6xC4Ru-fcc)O z^wfHosrFbH#>@Z8bY=CG`SG*Wb?Oq*AWqt%Q(lbs@mj05z5j^~>SvI`;h*Sop%rc^ zjP8!K6@v*{)8eZ|0SB?vjQcL$H>?fQIo#iJ`C&=l44*|P!~fg@K_JVJkdUkLz%7Qp zI?}EkZwp{U37p4g%<>FukXx94-gE?KZFh1Dvq6)mBj9gcVH0ax2?c6Nhmb?0-|Zl4 zaC-HS(4JwjaZ-|q#4J)5tq)SD{=)4|ViLz>Nn1H$2ChW!iGiHUSTSmZsy5^eb=1A9 zp^z=g_KQ=)CoVM2Uxb0p66txM<2poeYH40ME%1K9?oW?R|y2Cq!NwS&FDWLH_mAm^N89SZb5NS)QUF%GLFku4G~1eAJdhEvCqZR zgOt24$FR6iajb%>9Wd1yku}IhKn{~0_|nhZ0CZ4)UcJxzgep6W0IlDRC}$tVtd05i z_qKtMkLn0AOF`gb@$;#mNF|GT+oV@E4W6X)uFOqQS6B#b>9rCfEYVZc5A-j08Y{yP zredIG(#bkni!9#kQRVp3~UYX_sx_lv!v@O2|aY2wh^#?rJCXmucJaVh z8dA%7@KovJe@KlF1$Y&zpFhYi5GCtil5vNXHno=+fJ16}KU$YBkz6!+W&WsOnNp)R z-DLXX1-#S5boN+xbB%du8r1Fx!F+(Oiz&Oq;GD(XD{JdwKjLly5wz?tDL;6hhZ>H| zI!z4W+iKL|cD)5tpoN56e{G%C0a%)70$prsBCQ1pvsItwLTyi$zp`%~-%xxwq}Q2q zEtRO`>+JopZAbp#MK8Y@OhI<*^jcNO?xHncFJ0%gBS9YS1kxi|+CR&eL-IY?P<#={ zNiK)@As2%tXs*6usEmi%^={p+r*6#tvkARu<^KBwee)@SqFrZn|C4grSxNTHpXCm& z{0*~j{pDBeErg&+Z;B&Yn++m_AtROtX<~RMH4N{BJh7(}PP#sugb6XCj{Kv0G#|QP zL&I_nqTRnK4Aj2j4D7sw#j|x5Z`9C7F%F0uO!zFE-VS+sHEen&IgfO>q@+yu6;>;k z)c9lcud(9rOKolTr;s?k%O9QZNA-PuKNC`(5;|D~QMcy(b!Lh*P*AJN`;si@0)LAo zS$AXEG4Oyq6&7K^YNNJKd{#wNW1*<9Ra*q~@KTOSs=$4MF-Yr<&bcTb0J94N&m)Xa4FiOfk$KFHJo#&rgEfO zqiW(@NIX3=1^C70{eX0Hl$1H*ydCs>2`W$wXkFVluSl4!b4!z)QJ@aW5(j&ahke06 zsG=IJDF2+GBfpPcwFjN|^-kmX;&$#@Lqk=+`@Gw>1-eC3ez{DKX`eLQ;{;;sF3cI;wF9;q34EZv zt~Jxf>Os;J&gC!L&E)O1j}-r%vlQ|))TZBnUOHT;UEN+_S zyyfI7zY}{tB11;+7i|02EAK33tdZ(cir;(^=SWt~+m)<7p7IW}8{_^xZ}6T8ktdu55+e zX|V@%>VjT;6a9VvJzUX0%z{zEmI17UJXk(I-pA=~RyuSb)NCX1w*FXp9H*%c;Agz= z)MD#2rBg0TG^){W+EDqzuf)F8*-LAG5+p}-bjF*y66R${YJcn>5;BTNvzI{r%p$Ze z1d@54a(Ra$6SQ%Q$`hF*>0FQ_{(_}cr%&h~WOi;W`TsSlV7hcK76{!z;Z}=cw~s>y z@(tas9Q><@&cVPdsz6pc50`(C0;r86>*Sukl#2sT!$opYVI~ih8rVYTw`8_emLnO5 zrF_#13c@aks%AxayTI8a#$R}jzgTh>J}H(Xs_w|U;WIkn;22?MjI?dt8keRn2L)Y9 zwdH|~WWbj8o6C1t{{pc@q}Dcl*?p(6ApnZ~bz=Upyu81l1=8F199**yGVIT>$>F$-TX>1(|o{bwm<7c@O^Bns3B-cZ1~Hk z#ZA@&$bqj6_n57UOhp4rNjH4#Wz%3Ui@}nF zJZPpsq`p_HQQq{aZQ>17T`}5zX>wgNRFI;-qLat@Tq&#CGhOJD5M|v94lK+rG8ZyE zQgwpk92kCRK8;N=L1;?mWi!#{$i+@g@GcRMF&m%@NAYUzM%GvsCa5TDrMP%2Xq4Ct ze;H|TtNl#{mK$zIbqDDWA$Bt8TA3BRZ$5etD{`AYV$5|noPte$pNDFT2*_uLtZ>! z-IKxZzSNn`Nk5wCKw5QU3_&|hQo`w*D*#890UOY5g8GmREHXeW-55$ks53iB<*9!@ z?C7M-H15w|lUqCL^7tf%E^T89mA-p;`$oD*Df0YB1~a}_Uxp0~NK z9O~`4|GXM&NX?|YG{n% z@3OPU*e9=T3X=Zko#vPKQgE9A7r{-#nF4A5dn6w6BOT2T^wCCf-Eg3NCh+N0-}-hM zMALgU5lbKmB=4HllRu<38gN}O_CGeCNWm4bpgy!B zYi%D9F6#j6Z-j~UKwcvx88@K`2@inIb3%WX6AQM#o)e?j>%Cq5`eEt|*@_5sz4hrC z>-jAVWxDOk=e|qySRP*a$*TW-z!}CSNyqFYviaA4-mk88Tqprvb60hG*f=zKiQX3(u zI}{&6U@tZD&ea(SK<%`%?ycszdpptyK0tC6YWh^kzb`%GOi1IRqmWz=G|>U8n$k@0 zIUE$x*H*8B2mJ061T}Bt@GXt6)BrF3GJ?*21@yHLYT6gy9jZpV7KU0*JQAI$kFlo( zJi+d10+Y0cp@WoyTX04adg!s7bU5(SR!|-pocY{n@>Qtdd7$^TliSzd45{TUkMcl$ zpU93}rBRQ8^u_$+HlfJH?lK!W+L-A8vUn}2C+$nlR`0M61>@l{7rF|>x)KRT!X3@@ z6rzju3vlV+^Ge8YFU^qr?5AP3B_DJNOd$dF_gHR?$JQ zb(kgIq+qA&-X~LmLa`U;&^4QZgPU{|SeK|dv4fPa|JXlyGvx0uW6T4*?ZHbv{!z*pbq;!U}C)=w{Ma6QibgbXFa6ON4o2sM_ZG)98u&Cqmy^VFi-DKm*8&V$6r>dVRvZCb7E6-ZHsLF4)DSLa8fDI zwhMT;0-o#OXFz~~7coNqw^A5-v~Io0qY_G;N0VG>L-j#9^Yzb++J-p4F?=EiMfu=@ z)|Ra+%eeu9ecQf7-6_76AYn=Bkpc$%f#pu1Cmhhr1eshZFPKZ37|zdf@i zI{L)RPhevgC)suc606|Y4)*|=_H2u<-{{pGe8+7QwM~*Q7Ht#9Bw1HkK3Ew z#7aV5bdF!Y_H*QYk6HNVVUKS7+nilKAIGe*mlt@%QPTf`pp~Lv8Cf~+`ALHvDw~A$ zM>y!w(R~>peS1)X+V|aMn`slJ8hnDo(?FHIkgur$I%u^iYoro!+5qZt&{@eVy@u(b zK%luydTAX}NitU;2IU+2esx-c(J6W*FbYETBslpj)3z2=Ao~9a1r6F9`#s zy_c*Jbs~A=V9kJH`78oH#UN|=z1QF=U406;9c~(U@=-z=*mbQGj;F7vYgY|*GT7FbZL^$N;Dw2c>w85M_eIQZ{6K=7sR;`)m94g+#q#lIr$hJgH^@ zF!^A~4ZNgAlk=IdWs*Hwn3 zUT03z7z_pmdBF?90^KK16++(``;f)N*T4dqD1H0v^y|=3SRl=vCxwm_-L>H5II#EcfUqkkDpib(H^5fQ+<|rEQooh_mdDRr&2^96_6WVmBVT{i`|;%%J-+YG|0O*pa)#M>IRGM> zuWn$|0TxPVHdMirc9vusrKSWd!>X;yH&xa3twYB?Y+8=*evvPFwyFN63!@JxXaRQ* zkV-_jXQTMBc`l#_mN%d`kQ)_(_opDqt;v$sud3bMr-Xl@qIzt{cyt`xT<@`1_A>ve zqW~SH9=#Z}uqWi585#Y6ki!_nm@bIi`rOn3itw2Wa8He>Jb70V9NWyjg13zxtrA$< z*%y{9mus4miO^LoF8u4czlu+;<#r?(gZW6?Y!VCda2 z1GE0@R|dV+C7(~aK7Shg#BEy45-bsZ>`BL4tc^B5lvc+EH}%24tzwAFeZBYc z?0;3!H*$CK&4?z5OjNje1IKcM-jf!3O9czNTXqJv%131ayV5Hxe8hHG7<9rGQIUOo z)n^>@VHD|l{KydEVFw3GNVFRdiVX^uXv(sJuWspcmHVdp+26JctSi)>sv#8abKO$+ z;`<11`V5J|%6E$IcK#n10M|8f2Zx0r8~FTuBvk%gTMr%Z)_Am47&(i>thgcQGeAEK zuB_o`q8lruKN?tI%xeX%Ey6obd;>^_J&?S(LSa(=zWoBA z+eMk|vi~S0Yy>=)ERTH5G|^SzYwA{B>i(jK$a#;UOY9)&?-JZ+P!PN$7olLdxY%Nu zr%vwfi&(H<@Ma$`4pZX$-Dr;3M@ffz?f9n<7w~>Y*I8{o5pBt-`6~lc|MFZW=tDeE zEB_qaoK%$$?ra(Fqy4x-nHL6#(n5ZoLqvRC`zg96{-Eci!DGj_8l5hRKlt=IbS=oc zh~H(9=?1Q@bi?2qREZF~adG>xr0|Cx4uk@DOF{JHB{{FW{FF{b7W>fB)aC;q4(LnO zwe7`Gr!?{TRh+xfq~*`m`AeG*Jfj<5s}8L8l*`BGtwr3?p5}r&q#^_8urgP`A{Ce@ z1VSYdT(kC~$A@A&%?q!yM)5RsdxJ%c&{eybuw|dV*?p;^jo1jDHFRa)T~yT-*Fie+ zOW`5Elu_EZ8|0Vrh5QJu>m-XG_1MHEJW8z@VHs%Hk`_aAS#zvhH{w;9R9@l^Y>O4j z5e1gXdfO!B5~kf*lW2QZSHg*IIZ=Z3X48H1Q;c5sKwRi0*MXB&t!G`T&9AGwRH_Fh zxJ`et_Qj~O46B1nMsDXlFr0Y>2g$T;fk{4Zbv^ctR zTVh9^AJmu~djkxvHPL~rf^UvIzhXThUG+aNd_bRleM|nrS(Gx0{F#?{7n0Jz~Gdp3%iG3yyMdQMebkoRxW~L18%!RfE|*1 zQ&UsM(M9^iMNXdU z5(~tRhxj)|w2u;a)q@u5hA;TE* zj~*kx`LSP;&&d(HpJn-JPQh)uz;Df*Quq-DMn-}teJc+eiXXCkCNFKEu(htE^zlVD zJ3~zPnxVA&#_q9?bXuihPD7!~wCSJN<+Tw*Gh+bF0;H+96LLoM1(~yy@rzoOyOn8G ze+m~W)aXAK@<9T3+f|VEv+^Z5Kf!gDXsT)V+kQc`Kp0>9fYd*YMV=Hnn@Sl=GbVsf z#l`{_PJDhguMyZ2JRUTZpw_F2;6*&|GZ)PtsQI9$@q z$4eu-?43{Om`y^U{c-+W{W%}KbCna-e-!qL@znmDi00h2!6!$cFBD(x&JcYyLu-=H zgVXPe(5v>XyWUNKtEcin@;ygO^lIw|aYfuLt13xg+%}@}5*(t9-}yW-~Q`i*EmpEm)L#{lgx>89!k&riyy_ z;!cu<%6FiYB^VM8vGeGaq*LRdP_PF=sYMONJlJdnc5J)AT{B(fy)s_-99QexmW0W4 zDXLl;_`!RwT)?uUsmM4)x60BuSM}cC;mrxVTZfTK!%w2^i!z5WpDEc$gy8V-)2SCH zeSeY6f6Sy7Oayu4zuMnr{ufmf$wO|gfw6bZ0R~qF|>IsD6ijdp`+-+7SyJq=R*0qQrCF~+d63K&y^)X>9IrEO|88i zueS`dNj@BU2R$d@MY|zM()yxXe=mauL}-h=uaCVjHy2}Drw?;G``KDgr1jx8n_uf*0nu2!fg0gdgf^xu3RbC~_j#Z}*odx?`u^jZ7KV zw=Y4-z@|k9R!_D&`Aj~j;PfYV6R2E6phpR;o`2n&+_}R-(i@HI_Ln**(bN<)&#i!2 zk}>E2J-&P7C>gs=@T3=KHqaMb9^>Hrci|l#fC5wOteWqj7Z`_GCDZoa)XK#$+6lpM zlz!vjGA%en{wJYI{fZ>;c@hF{f;PAJx4T$pEOJmKN|0mQ9Jfyl$Q&ZTQ+XDesY4fkDV20@3LjDSU0Kl)m%IrvDUR^)SSH{Y zw{BGJP?hd8b|(H5$Wwn{ZN?0-zkrSzb(bpmv8XEkCEKgvg{>?MBH)X#6{aTqS9TuJ%twCnIemMJnSA0>gi*a8Ar`;)n`OM<)lsw*AdM_x zn%>^wD|Tsbf4_+{8g(hg+|DGXUI8_|&jKCN&Q*Y@4eQKSY}5>8Rm%D__}EZ>TQPl} zgh1p1(4SVxr!s$ob8xEM{qfDYg(bJ*ni=`ywvo{ML%c-k>2xM*bK|_dFVz}C$N_4` z3fK9s^fG(~CSt>Ie@~BStgse>6ohM1f_=k)-#Pn(s$e{iTLHqm#~1r%M|Sn{g}d6q{@5~ z8muR*?7kJ4mcht=lia!d1kLh=VO)IBCw+78;xYaUH)fOG!h-C{43d9fhNj;M&>7XpMl7S7V^pLpb=DTH^A#y&X@5s@R^jI2Ju~W>9wPF z37~8&^hI$W-^r-1%GrC8^2Ish?)X)>@@QAFwKDd6Q7TZb>C9EI1i~N9o$)@ArF_QNdBq5SnO@rJZz31WZy4Z!{@5Cp$-~^G{Ff?%$OfVH zXy_{V^r%SND1`8+=DUqVYppg7;U7_;Hg!fVN=s4-dLEosL3iZ+|dd`RY-=v1k zJ(*RA(b_O?XxhnJNtD+udOC=O{4YU72sA-6eU%K{)(e_sWGvJ_dwG^j_`I$G6cNE% zFn;$&{v@?mk1PSP#~j6dA~MrPloX6>Zwal`J21?v>6R^(Px^&=qXn)@TKxth_9riV zGp3g}9pRSay$4KtmzsDS#ON>3zLjJ6yag5YP-bh){0{7-Mz$88)xb*J#p=XWtoK@1 zdn7@oPi&TSFb_crp=r&0!Df%kw?hKO;}6L1%PL`imPNyU&&wcxN)3ca0(n=Oo@M#Ai!~;4+ ze=&R5{?z23gn9Lq@e%{Wvggie>sLyfS#;qTBNxXzfC>KPDea@3R!;Ae-sWG6HA{6h zYDrRB41z*f_VDv$*!DTOwR;|<{`qn=q5=$guQ^K>X@lFH!C{}(seXi;hJSOR1vJXB>N!p~6ez~zfSCJ(x^0!JwU)9y+oVVB=D4NQd&iS@+@ zOdTuEAz3$0C1D6riUpd_C*3BL;7vDShuw_GD(LIxLB8yCKU5pS1FoAWsKn z_gsn&o6Zt`YAi-7tEhhi2MjLUzzk9~?oGWl93C5P+V6H3pgBxD=q`7%{^+mtrfFSR zIiSeCgib0(7$|P>5h6XlDgTtxp#|~3nCn4~xyu7uIU+EwYtYO*-z$Zd9dPvw1p`2q z7$X-E5n)W}HGL&@>%BgbKMCfJR##O1{wuX4t)(0-2WWRjD*Hmh*G_xYX}EdT(;frC zG=R{%kG!xzMZ}?`?s&0;%HGN?{{E!>6PDmyQ0^S9(d6 zN|))8o=*%lf7sNXk0^dUo}Puya@3k^fq%&X9W@QMv1)c~nv&2wG0f{NAUg_(LET%G^bEdB=D1d>LUo#4 zYE)lbj0BHnoN#jbX@MssQDIGJ1IHJ z_CE2JFC@bP_r@Bxq#uga%0o|0O!reP;1b zmj92b_wc9k5C6ySbDYDm4>C%eV=Fr=`&i+vjLJ$jsSqlAoI{FINFnn`Lxt=Sa*h>3 zl%2h@_jcCr_W682kH_yHNauCm_jO&b=k*-;_wP(nLs2#qrK_kO_9RVi=VSmk>bv%( zguw4-lr&w=+=63pt|tJcD^Iqzwn$8x@dHedQ#=+^9l2jl5QR|KqEB%s%V&D<5g<6) zGd9`XC=cuxqjvGo_p~F-aZhaMm!HG<1JRlI-5{e|!(~itT51I#8gE>^Z?w|dkns(E za{~HR6V|-uvr3DKB^D*wZdxMg_hsyw{^_2|4Tpvlv#2K^KgmG(swcsg(A@${qMQp< z38(g$jHfv5w&mdD>*t* z%sw%nY^>mccV8oKtHb`2pvikomX42)7enHA;V*C#xfSFC(`~{V7*2Bn>N>Luzju5l z3K?xrw4ZX^dWp@z9miCHs18 zv1Uo|I-$1nx9s*xm!3;6AAL?R)wj(@v*zP{Mae=KiOj602_ zy9eW#l1RdA){cyf+*RCr_Xg)B7_S`)o%(;+sPf5ZmZ5LP$_c&llz4$JWuqeEqD*dZ z{ElsCXsF8m{(i$Z%Q&6DkaP9x5e(8GiDHNMs?Qg~`!|u6W_F{qeH7)qYT(!Yi>1P5 zJ3^~TIb{p;aw-x@w;oduWq%}JA}~nV45Qy+#ti}Ue48~>pN&ny?tL*&88w)K*;Z|; zg_~%H3$j6RHlz zWM3yMD~rq%2Qjzy|2^%-{vFaQgpX}_tch30Uu22fvYBSSuFEZ;bdQlo@4WH@@{N%B_;pSqg6{J ztg5lLx-L@yaB=1wgd-XL-f=v*RNM7g4lXROs@1LTy!I$St z(dHid()C~PESVruJ{2i-__Ouer_N2M_CpXC@9cZD^cd&6}5xdhYQr zx!x5(K&o*dN#4lFXrpjOU0)b4@f#)1d-iLdgZfE4FaqKW{>05E&zMfIq$ogqQ$HvY z-0*xh7+a( zt0LZKubo|YCK+iALk?NheuKmnCVl67fvEyzu1|{i-?;Iob(g=Gf+_`eB39~rVgwlG zA{AGwW$X}|g1%Dt4{Y{!VlZbvTu!igWqyvWGLLr3&j0}8|SGy^i?}975nmDr|2vhu4MF)s)nw5B>>ec ze8AX37c^TxzFqyrW=&3{8UsJpYIUIKGr5qOEgozaONVWCjHVJ8zUrg|Ya^1hy;jT$)V|s~ zs>sRGWkvVb&AQs`q8kIhQj6E!DPF{chO?-E-ySnqotBR}B27Q~$_O4pnj(il6a*L@8~jF;y}|Jj-*FxmXB1CaqSqs8 zO>u1cgT9|VA*bJ+ed+?allIh3S>5R^0uutcA8i(U5*c}tX)?xV$@o7b39g4#ba6{{ z_TtsUTd7@x726nUHKvTv(oCHuZ1^craODw&S__@eEG1?*o9ma}}n7o)1KtNXJhj%tn z?NqA05?-vQyD{ubmyDBmh?%(3HF<@h{g*0LL3!D0_*M6?OX{`^SAyQKQvaxHgv zm9Q54!*$7(=;MY_Wyz&`QlrDQRzBXX%WNtYRq=aYqbfp7BLtoHqn^_SBE0`)=1Si& z<;nZydjxy_a}#C}5WNfU;ofpr&$j1zI=U07|IB<#R zsD95Y2G8cFYisX;knSw<%w}+cDgP^2o+>7F43LCQDZJ)IGfX{1fU{i~8g0tMVTay!`>lvw4?lZR zhw{~QSk|R~Bcg@x7^J-2E1I4N)duN=qv-1I8JOi?H3`L@ZF&+LdsT<-chG9kZ;s3A&w`li&2f7f4DqAC{Y`iW=@v?d~Uz%D; zmm*)=w%874ig);h_kYa)#|6mHwRO?%9xxpp{^H@|J#WEpuhRc3Ro2UsVmdmK%$?LH zIJV*MQ{my>`NDhp;LpW6V?*FI>Nh{Ji#?YahzomzBBm1M?))W6d%R<$4=}|J*28JP zCx2+G4ri*N0IZ-`FpKTi@1Mc>rC8vi-;~tm({x(7^x|M*Dn)vo68zTXK2h2QW_N7h zakA@!pX{^X=IX5J$o8i=-#}uH3ux=Isv)a!=3J zkVg)Fuc1H+Z8(HUDdi?)?V-aID_S`Coe6^#{viaz%h(^;wUo$%eSUCIx$P;mYxncX z#k)3dL6y&+;>b^y6>=px zDi=2d1ia@zHjivy$s7(3V;HT&s*aYr@n@dFyS%~PV_Ix%IfY5{uyz##EJ0B#M?<(F3Aa2uYD;`Bu zqi!(}OSwq){PX@q}`ir+PQ2aS#YW6J+pzVHT=nH!q-9CuUYMWvXhgNxfR~1#2Z(T{xHe2fvTbMD?)Z7 zcWnoE2y7jG*8J6yOu&K;>H;!U)*C?Oea(8YFFmBuWV0FMGuLbzw8?MMnEhp6O8MQ#4!tQbPqa^m? z8~%Eh{vk2J?Ut|eJ(iFWJrplZtR+ho19pyL(ARQ1bOi0pfV0!@OjGWEJ7vAp-U#2< zd4K@E&?OnX5@7CtZan~%09xeDW>!@%VgWTT@%vH$YuZ1N9kr~ZY6tI`fhlUS9SyF? zeQ>s#K1~$rUOAWfsB=~Umjm#1TfMg;1VVvO2Zg|`+Q_PWeNC6nF2>wnIydR+gw9U+ zB#CZDvGDRWMf+otOq#uPIe_seJf4>eVSkZ6NbUWD5}S_{1YDoHWdA4G%czMxm*S<|0e{?$ZTJGi zHq1HgjcCR?=HX;U@XG5b=_EaYB%tH3vK($bt=Q94yYj6Hsgv6O>&kYiswZq_q5=>kj*)|8CpT ztRw-#Z02PcQWebvf=(9S8F0bp#q0L+<5?|zqi#D?r8A&~JnSDkRJbHvx$OuTK7qhO z(0ROb(xM15Vl39iv);@Swt0muZp-0t>h|+Y%~{OTU(3IF^@G+^D&6am2~^$1B&>@(`fL_`9rh%}?QbA8m=a zeUYtVl{&0j60P@-TySL8u9Pkg|M@EzO1jc#OS<$>+pfM-7Yr8(Zd`AHh$5fchKMRB z*}f8Z(51kl=RM5gzJ}dwWEk0$f5JC}JzABpF6lG#|H$weYs3{Qzf-j4y?x~XHb`5P z27)a>b*V7sVdj^?Ax^o(guRc}ZJBG=LHs(*7NcezkvW{?01z7lVn&o@|CUIn-Y$I( zTK7!6$G{lXi{Tto-;sH{!~EdJq?%_`muG^2yYSk~(jKsNjSeFGu_- z3=^X?FHvz>&Ofktb+2bTSeBmBiD$v5WCbC>&M|0*XF>(z6_n@8D!8QhOUV-UdoOpH zn+cj+JaWL+AKB%_(0L*64kMmwULX{>xfyPJ-e#&Sk`fx&Z)vo3<~RS;OC!P0VadcD z{79$vrepX`>4pzbLA`hR)MTqFy2QTr?^7X9MhSv!#2_!yeD8|kjEAqA1hHBt@9Yzp zK{NCx2heg&wA&u@!sy67-D7XIy2&ZWbVyc4h6Qc%iH>6`2;D^KUR+$_U-uFE7H0A0Q6MBm${eBR;^}-|-1+ z!1z+OE<0`qPk6UUjavM1VnCO}?|lD$GMG0Sp=pHC;_;&X_9SHVtS&EO*Xf@qH@!^waAOwJUaDv1>$TmO2Dr5nok3EDR?>NIr_S2RLx??m?^g7KO zUsfr-GqI%>_=`oID)EhElSYS8E4gI&crGSt9bCz>bJG0o>QIHfAVrR&t`z^5w03Er zBL@NoUxYv^P^!e^Vy#D&0d;v+cS8b>sso%S&yt#TWC!Umm23wB?2Mpgn(_gFF$Z=L zpN8)5Uke+N&{=(D{oYaQd3D`p9iz(eZYKJI{Vi&>EoDs@7=$;fy$pv~+Se7&JM^D& zS}wif%LRJhA#A0g!2Zs)g0zxMlD<(zUNhhwjr&Wlrq%%T1xCs?51jn=fMSK2hzl%9 z|MS};PKaXKHP;GaFe2be>(M|-9wMpg)BRryZ6H5mlF6jQap3kNO5f*@K#t1?v7IwW zAZhnqzURZwyuS~&&-Qi@WO6_ON%dp6^8`H$yk=2;Fzo2}i@@}dJ|1jvpn0&~%0P38gXq0k-mxa0_2@Bg%|fTz9R^__ zm3#Cj4#|~vK z=SBwwXo?HlOvasUFE5b?xF35sf5CJyZwj_EBeA)oa!1m_M850O{OT4o{^y}5wD$mg z;t|xTI>)m4WDDj6lgA8sIw}{}VEwcrZv_P%7~R!E*o5WlYHo0EX1o`Sd?5o&Nv%om zDuWio#dTXBP9Av273#Zt)X;jYya-N35x0K2WG*bf>{q@_1K&=O>Z&I;^3Ku5Drmz# zBLt}d(|=;MHZkF;WqCz!tHS)}pRw(K_w{j(Fg-)tb35)Mxb>y2e5GX|h^NJD;G6MC zNVHEflhHPOadbNbTGLU3sTZ8*!KmgexIkXW0yq?R7t7{xb6PVCL=ydENGQ^V&>aTB zW{RS)1wKzYv2%Y^6TuH$wuWI+MOpc=9W5diJOs!mWM|+1(wBsxFKE1P%s_k)(GP`) zQngn*q}BqbFZor=ebOq-3#)liApq$Np2_lc}4c(J}O`< zF)1uQVDdkz$G7%VQCE?-?Z=yszp6c|M3vRtg4fU(Mn@i_ekF@)(q^4opK;%W5qoi< zOLaMEgFuk&fZz|ZAp7iT%7KzZa`UV=d5j;PJuprM4ctuv?61R0p0Y4c!Er*6L%Z(` zn7s+;zG(Ceb%gy!>!^y}sZ3Fd_)(7tAgN0fXWA?a)Oke z0y!8<1+b3!fu`d)ev$LK2qg{Zn-pdTnuBh?qd)DVehu zF~XvQ793X+p(}pf=`VT^CEzs>$<=lviE`7&U5Ez4LQO?FcelV>#Gn02)Og!*B?cVNU+?d1bxJ_fKO4bIJHi(kk$d#Wfl>E?o5$Z@BeTKknD535>ipMDCio zuTr5-kYA2Z#?UO)ke%NIz>w_fDvqVd%PvbjhE%mT#{HF_$qzdl1b6TlYF{#CI-RKl zt8^Xn6doK-8tEKK_RT4iOok?CtNakGTLQwmJ@3v!a=)J2g^L8jk!r(f%`v!^M!0qt|<{}a>BxLd&t@rxEZqbwX zSYF*f1XYzY6~6d4p1hae%{$dUWBMb#zxod_^n;tq64vQ>{=AeP%GiNhINHO}~0+_31+hveU921!R;*Yc0= z#&|vKDGD1&FcK%y-B`|>;xyI45VP~2ZefgK#T#(mPgXs|C zH5_nYbtqq@$oYVMUkwguKj{sX`ysjmEaKI-h6pQ;kWh!sh(K^3Rjm8Gu60MR&6}pp z&YE$W$YlZYAwUKlsNnj0I)953DgSbsd}s2Vs863}fz)`9*QWT4rV1w9 zyp&9Q`<5+mfN=K5U~!O|PzdYiFM6+h$ugOw>4ZU_aq&x%Lt(w6bfN_oj z`ttSm2o3bPDKQCe_gC2hAkmrH^-B0w2H>0=u{TO?IhHn|^F*VFhNS(qy3twTANMw5 z2ijWk$XKSNMrsSjS8tTkqp9Q810+(ARnYpf(E=A)?BpCh0%CnrZ4)q=d10+{MCKW2{=jul@v@Ujj4er?(VaKj%@V^Nfn1H|R&kEn} zV9^Y^DD-XRYaL&UXc%haTCb6kZFNZBvc*eIAzO` z%@C-seCGoBoCDnIBJe3ZQn_k?z{U5wUHkJ*D;WMF6du>!&$iB|;bvu>_1?b9#T8_eEISFcEt}9>x2S*g z@FiM$>uz(Cx5h!>#N#ND2^uT?pmtzWED5N0dO*D)AmWah<4h`0Q9`-CXpQ)J^!i+#Y!`z(>q~dZ zjwf?k|Mdr4El#tH=#=7#X^Ra`7i1(j477b!ch$eEjjI^OqGmQYA-VW>&CSicpj~d8 zRe~UhpA4H7M_!z#+XP;L8=ImS0F)j)th!SegTnyb-r5iR-YTQUY1ep51RGdqsB$fO zcLudlrnzcPWp^*$3A@oYp{(7Oqw?+*cK!#l`e&VSgkUvQlpR=oAc3T@Eq!0o{~Phs zkIahM7W6ETs*<@xJ%XcWDFq@lmolA^yP{Ml^9!H$z2Q9BA=^NZu^L5=TWkXA-JmMt+~6=hF)jOZY99H{k+a)W>v#zvDoNT6N7$gpyigvSO#@ z85rfDA|97#Hu1_Nq_&^|D!UXM5q86_A}lI9r6;xMXFA=c(3-Y~_uhh*thLSgqXpm2 zaMqih>8iC^@q#vYhG{!a{CST+C@L9CqcHvunz zIo?W9tx)6kER}mc>=g`h?yU)rrt+`A-(;;h;r}H%S>&nK`=679O*Z?~NXhAV5= z@B%%y9iiI(P%nPJOO>aNiNn>t36yb<#L~L;OCmSjWcir9@-590 zhje)O0yGF1qtggntwHMDzu7@8S!p-|dJBl$7c9tEjFeAqllmvON&RfLWfmce^U#Y! zsq$MNpD`M&^=S+7VQ$x9Q(xXLHm;jmKNB_75WgdrXexa0c@7*z z7e)Z3M40~!>omrv*&+6zG^Z!4UNYS+R=+ z$85%YIQPmt^^eDpB1ZW*Z=u+Eqv3kjRg3-$q#_^hf7>J%PUYPu4jwW>$VP$k_$lzm z6dkwI_R^Uz>YIaKS)L()N|p^h<+L3GbuVJTXS~{6xaG(NF~H`nA61?LN{Z}Ni(F{| zb1vkOyfEnS7_~e(Lb}*m>rU?4#$Pg-%#IJ9d3q3flLj87ygTMceQ-b6RY9!!|0foT z6T%(TCaz%FUq3jWkacPxdw|Lnwt;8tM+Xs0Lt!}s$Fv`%r}ty8onOEPfH&Y~kAXxc z7E1``Y!4B%6YW*Xz_1LK5Q#JZh;cE%wQQI@^g;F;HDeCWC4)B$RF2 zcMFQlteD*_l)mFRj?zpB(B}1hx}ECvRf|ZSrfL5Ax4;|Q$XZ16#7C$cKtZpnMK}O^ zuZg>Q*SaTuHet;TRd-*}zOtsYzPGCC;`<(_# z+8ZJ|S2amf5MAqj;L8?(+!M9mv(j*}_lM*%Oq9xxV(r`s@~(KlKO%c=Gt>_$STxixF2a?70KPy$V#Ake+}( z+Z_e1G?BJA7_-g-djM>`6`EAuHDJ;+4V@jhzBWf;PIiN^-H{G{=k_pA04ow9gUXWYcFYFUtF8( z{4^lO1X?C^r@g-y>~uV&b*->GdXOgiM^@cJ<}+`<)%Uz*SN&mGagzRaVh1(cI8>$+ z!7>y7AoD&2gm~aKiOHek-8#RUdQ56{SCyqAMZZp#tnaAH@X)3*Owp8=@^J5nKF&eP z7MC4K;i&z?)4Abc%@&f*&wEFEjfO`o%WG>Mo;e}nI(RoXx9}Up3Q~>rqn2=Q-Ue5J zu-O&1sB_E|Cx=^vBUmP-EtWWOPV01VxVE%# z9qOp3PV8Ljc(VHb%-1T?d+;pi|5OeI+YAzF1|!p+sTbryPi~<&#FW-LXfDx5QCpa} zQF(V@GCFKi>at!E=y}C~<pB0~=Z)>J?=VdNf3O zo7iDe5rUdE6_kT2Of1e^3tGKsGe$6*D;kW@=tz8dc(;RaZ;zMvGX{r^duhJ|2i~rR zcXAZ&V0F8E!>Bs(gfQ=92XYm|$S;TA;q0u3H;vc+bV%U4o;NWj8Yt_!dK%B_+CN>eiUppAY*uva7M-!mPT+)ymR@;)1Xw;x0mZ(9jl6roXP# zko;i6mn%$*`a};eh4$@W`ojvFE2qIx5Te+d7yiY5ZlF)ABirePXlm`*gMMgC+BA&dn`BWSZ9)volBw{^&DW_1*C1#m~&zxbPXWrKIf`yA(c_ zKIMes$Ok=|e2BOV!X36Oh{GDH+Yiml)!E0IfSw;n2qAoOT#f+K&E7&9fR9gs9s&F< zUeMt8W^%{3@vFu0bSBo6635^6<*X4{iI(GoXs&`A;q8Gkou3+(YcxiCI1e@+uCYJH zEw>X^E2m$ecj5fjoxs`@(gTX4EYcB5gk;glf)$xt6*WH-HBUh(g zcO2&8Fc_w1DsWZs?Uax2M7!hBv%SH{ZrXvM%7Z1X=P$b6ULz>~L$O~LIr>T&^aIAZ zh^|}J;?!4yfC;JTEcCnfT*3cw0fZKIgof&AJP*&j+*}#IP659pVd70uHRr2i4ftxz zbrxg~<5;RRXWW9Gz5U||YW=lgxf1hLzSt}G8*X_wFXuQl0KIfiPT73KN{sxYB+7*p z9%}DxK5$jy)sXGjbE^I%QA3OG{>bfUHp1(j7|KvHP7R2bGeVYH6 zE?>lgRI2o-mbdueCp8kF&4UeMlY`;9anxNS+Vx40isw|0rBO8m4nzaP%mK^$F4v_^ zV4oJ~68?(AmT}-Y2LRa_qOiL-oZQjDeTowpx5zzpOpE<8dB+h5X~l_N{!>V!)w{*8 zb~6rPZ;B4QE>2KiN@N0lmn~UGY@In%)HzlKyGgBM5qZWA@vXSPls0t}WSF1%Jcx*p zu9~dv(cA2U(SgorIlY^8n|yvrSkp1AQX@LYxQOkvAhKH@ZE;(pEHOYgnz(;0Mk@Tz zb_bTKB}&@Kl!WH3HhhvdqV=QY;Uu)89eVH9Hi)INvA!Wd=esgM4pvMBeYZ6>`I1@d?N; z+@?DOyY%ymdqR1k#z6Qaqa%QY3PIlN>G(8Wrilou%02}I(UxG-N z>(!5P8Y|QJ;xtn}3-_{HuupXNk(xETuo#sV z`&T50B$6UecT z!*Oi4Q(wwo9*9D{rB zqXNMtqU58O)Ud#`T3XncX|sj+b5m1ooB_Zt{~4sdN}u4O(w@mbbP)(xxGR<0+H45x z2Y0M{xb$bE$uv?YUq6Ex%i#(Py6lKs2=vQHFSo|B-w#ZP@a8vL63Y*5ZsUrp>2(w@B#$S&@n|-c@_sE(JLXFHAwvlCvL%}Avv={6@TuJMo%Ml(rbYR5ySL8) zel1GuECGV~jXihIiVpSeIUTvC=VAT^D&)?6rZaTdE;OuX^CN|u^OM^9%wLEuf%Xy3 zOz^4^81(FQ>~6`aVHGvCkgfIcI^V#tHy8PVTTK<&+B+7U5T=lymSQoO*^^+U-mas_JTd9D} zRocRl&U#zUgrGcv7e}<*q_UP`$Q1~fSFPQslfW<8Z}R|YW5qo%WU}tT%M|;%K$+1p z59W=%h6vKI)&b_~_cJmB&tR0z`ry5;v!wEo({Ik4(h#WKX){)v^jS~4d5&%*@fmQZ zDeCd!kkBb&Y?tYru;;kuuYAGc3)$IZ-)QRU0xSQ*EqVi- z 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

eTg+_9==b2(`+ejPwK>G1 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�v$qJEqNJWSu z*%XrOmCdoDvKn@w64@hrog;-%6xpI=@3(b~bI$kG=ldIf!s~Irp7;HHUf1)wt{X%% zIJ^g_n#*7%@Yc!eCyc?5tYrPi!y5y_Y{^o?Pa_XYOsAU7o%id>8*{U=1)}sN3ZNDh z$_@eSs{v57s9}k~iswS$=2m$b;Ki^c$w4ga(8W7&5)Sc_)2rXV4x8t{-%N@!^*)lq zf)$B7$fH7zsk^MCTN`?c$uG-K4}U@6lyCmt{K^%vc2A$~I!lyOS(b|FV6}@zp>0d` z(|eSL)1dOzGZLrC2Y519%@ zA&B=6mP;VhBx&!x_^3M;L&RFzAw28zuks&O1%LgmCF_c;kn{vGGF@*U!J@(JW1C>( zymk&%W|Z?eV?oe{tz?e5UoCuf|Mb0ObM`>~H%+OO1pa)a19UDoY9mJLot8B(+gW2L zAsI_2_VpQ9H**A#;4J&E*SY%|v}b{iE<;XWvQ)1wAo3B*H|<%}ntP`-M1joo7!3=i zd~t{~8U}f!9Su6uByRoO6N1myg+TgDqyzl1;pxFX;i_G{-iN=rjnz6F&$QKgq2d%s zKf*NSq%8t@P0Xh@8*pc%XJki>yqfJ$*dK9aJ-Y?-vg4C+d=b5QHd)LCsu2D?ZNvqE znS(+FyamVmLJdgvOzbSHtF9@KN1aY!Wtl*7+`FVrBhP6G?lA&1<*g3J!3u1=>;O1n z+*itCxf#Nf3w*E4|TxGCExQH}uBSv!KO zJ~UJS2w(g!#ShhKFd^O}BbYGvBq0jB)fWo)iLIKt#O zr)Nl0%n{(0h~K8jX8^E3S6kC00!YFJcj$QH)~tmmO}|3*%xk}6#)zfbk=5qeJ9Up} zLjKmNv)Co;8B2hY$^sqGT+{E!MTgJllfsx(zpL{fI0$WM(T9h)iZp`-q;!EU$6z&s zK6{)OvXKokfDMgCuSiJdOu#1{Qeb)TqJ5(3#wy69YH0OR9|3%BI6W^l^fUe5B@E_X zDAg<7kwP8Ej(l%t9xljVrJrsZYL_+4bGIg+{5`v7%|_a=>Q@>zhr<+}@4N4)&7+NO zd)p4K@O=%rHg2}wg*&qeL8 zy6a?dU)sL4a&=vIh0O@eVg8#{eTt0hY(_;0vHo@IA$SF9K@6QlTh7K-wc}^X^cb(> zWkDAT_k-em9teZe0LzZxM zefK8c*=ch*=-~O&=d)t@Hnb`_UzjBm#4D1PoVqjBEuE<+6Iv5b{jA>FytAR!n*PRA zeLa$e*tBi0i_$o@SZpVrOOBT9$cui4%8*^Gj>*^^uwzbPMZ}#>PBGL4E)?iZK=%*C1881pO=?H%L!w*o+C;XIrH){xL~7hI#k||R*M)&|HJIj zly4EfQna_9gD36{6~TvI_sw;0S&K@MVwUrrYmXJ9xK)*Hyn;r1#Kd(fTvrB%$OpR} znSNp}DG<>3ol0$Uulswe8yWST`twN8nEI85>EZoI4dx+0t(gj=#7H>(=kX8K>@{8E|bT$DamAmuk-U zX?5-_%Tc{|D%}^^GnR;@_JH<)NWZA#vHB!a4A61GKmMS7dXU0F`5Mj3%w}z0N0JMI}5_L zWRo7GsqYl}?$`qz{!CFz$LkPj#@WXa`bbu!s53(iiSD*D$J?g zQC&Ib5Ww){$d#KSlp1ZYc2cX25O8e|ZYl(h@IH@4|f zXPcNa1%YKaO2)MJmVO+w{e0!Iqk{QiJRh-T8jbJtU*++>$Q-M$GTos_LD%gz$KP9h zt*qEP$`#$P*3#X5EJbGF;e`y%s_#AM(7mi?x$(-K(GT~zD6VRg$oF4OM|Tqz;Dop6t$p?_iciKVfOo6Cd3w>t$zYlr}$ z!&@?&R(C_r>54tVpbsY5fGR{WmfxG-s&NM><6u|gvOMh>L3WH;`tskz57#yIB=9f& zw61D*jWK>g??q)&1ukw#IRcfa5cIRm#v}}QhhjU2=T>()I=PWJtm%eS1oxAVeV7we zoaC;bOIgkqA)YgOVGr;qd=jNx8whU6ff2y49|kz!N_!TB=$57bFZ`1>5rc@(l+oKJWl}_tM9IdFHTwXe&M1C@3EP6F+o(lnDXCm=Qo& z*J$KgLS)1HXC8QWZDz%{`xHRqN)ZDUo%iJi0aZ*gZ|2&6qVH503@Lg4s-9v4Sk zT(8nXG8-eyxj=_Orngg)uJ>E}#Hd;zF5W(&6*{;~LY210pF-^Z_gt?tj_oH2+)>CS zu^N7lg=C+h_-XJol-0=4LDD+ghwya-Zff*zjx{23KU^HbihyTOn%d%{wb`U_RX_Dw z^tr3#JDhsNnZsyg*L|u%jE+yz>I>+o$VdDUcIpEc_~Dx57hNs~m{#aSuY0HYL{RKNU2dc%0{wW45=vamOEP6>NzB%eAHKDTZyev?NE~ zfFYx_CMepcAssTia79Um>wIRYq1KBX$Hd^`s6VnEy20aEkJ5CgvRnG?q?|a`nL2_<* zCKGM|D&izo)M2_l0sO-#349)SfyaB$Y*D$@4iG_z_|k{b66$6Jx5Y?zF@|)gMqew+ zR-_D@Zc-r3HTSS0uV)?KD4(gE752s%{C+2>d<&i5pb!4m66o$x9pf)a5*Ewj@u&F} z?Vq_gJ5ttXKeIoBGdN*v=?#wa+#BucTc#Br5v@#&??vzyv`6gqX_L|7clq4iK;8KZ zoPL?xUfB9R0W_B{{k8^T)QhWqJsL%6JdyPbT0m5ASwR&?i8ahuA^>;eYZsgO^Mo?3 zYQMsCr(y{)iZJf)%(eA4(yth?XGd$I`w>$3+4NMJHh*6frD|z_iS5zPN(-Kr^4MH;< zar#OQ9c@qgc{B}ElA4j)>G-HsR%z`HD75JUY{ zZD6-kX*0ln{OoP~XCw~bxbdekSlWXbdAtxifSbn@&`FbjiOWPn&Yw)>^V)$+Tjh3B z&k#d*rq5tAJWy%){D1tKOR-yvW?H8m9!{h?L%#NB!U`<~PPKVBa4txz#BynkbmVXD zukCtOXmuWjwu>oNiJB!y`>71KcDM!IO|WX)EAY-LElfY9h4_msf>#sBn(keM*R0nP z$N3yILM?+M{_O!tr+*S}M26T2vG#We7A^M&ZA@8t`yB_#5tkY8E4xnaUtD&*dZc?^ zsUjvI!<*H6Odnt%%poTBD0vP@u=qK|_wO+{`F`tC0Y&gd=t zzDBq*?V4N0`RS?cVlcVqh^tEMOQtj53QBRyvz}M48VgrlgIW?rcQ%Jz^vB4S>4Miez zRX$t(U3g_m^*^&@vg0LE-|s9#oNrs7c`l2SDpRaKwOma}Wis2CgCMXV?}=IN|6AZH7=AvafjCkw z-@Q9%b^i?#w|xFg$l8<4K$<`7354O@teMINLVPd6BUDJ5-yZRr#~z=Q(t+rD%KmE= zw`9S9;(aD0@K+@<*q56`y~v19Ze;F}y+2Qz1Uv`76{2%e$k!V+?rQP8tn9%tl0G2u z)iZR6`^F_8Wc@c3=jO=>YJeTFd=Z@F2B&B20iehE12O2FCJ<);mQb!G@i6C*f3L^s zrRK2A!{gutn`18OASmJ>pbT+jVoFaU{J;-Myi}Ym?$(aJn2p@nvqx;(_e#1)Py$DP)FKKd^r!(FZELluB4Fyzs;$fT&~cOf*$uv+?eA9xR4Hs`Pg)pD zKS#$Eodn%hDEH^S_@>w^6T3f!Sg_h?PeET7K_-7cPMsAwC{OXyrb;XIPjDK#p{!iC zLG-S@XUCV6g|p5rn(eYLS0QZb$+Nn z#@2@AZ%d}?E>07;YxK!is;(mCU$3*~=IcII*L))QjZ*=R4_2>4b~zuBd%VVO{dt%x zg>q%Ii)b`s6A_87Q)XF)%G&*+rU=!dGiMWHL{(MKtvp!0EUDqKw4UT9ZbUzi?7Z6r zWZhVv;SYBmz*B^)Y;G!gFpZ5qjBrmpnT<&ZlZ&RxnS=N*giLZU94(IQri0Hp8Nkuh z>V0ag`p>-hn&#c(S|fkoGQoYhWJPK(p0lz(6#RI=;+4sbq+X7Krcg0F{qJ<+g`h{@ zDSLSe0PU!qa5XF5;%ix%w1UJxe)Gef%u_qUo0@SBAoI;kdj*q3kvPQ4x%>TUlYy}{ z8i-)7>Sq4i!fcxSDy?lDY4&kVwe#unjvTZ6xl9PyTB$Qkg`bsnx=FOP4O*+8ITjlM zifg5?t$G6d7l(rkmZT!n%ED0^OYd}-G=je9BW1p?r?{m$|3+!gx79^@1wENd8m^}8 z7Z5DIaN=hg=eub&#KG%6{tK)JFTAsp`?^kf-+`qXaRV+7+q*+o;YsT5>Y(9R%uE!E zF6fE>Bg^-cV{cPv=!*C&l03iF*31WumwlJA-(_p89&3E?>8oV&`BOv{;&m~yp!#VU zF#R%yp#?>Z#hxHe&NV6Q)kWiN{*>1JvIwfNCI+M_E8)vvcK*^=>NpVr>K-AECN4JK z%o?I6y7YP3=?+pj^si(-c$?B^4smqT3mKi3I9dLKG~e~;Os*g+M~F4F!tQ8ymeGy^ zVLSvcw_fjLBi3flvR{7di(iQngn}{#fqNcfq--S?GkbkXaDsmDGyW{1%K|^0QO9yR4^)9*f_5?Tib4s5_!>ONn}_ewL+yGmPHzkMpyG>bA` zMW5%_On(Hs^j*S1$pvHnDG_+m3Xj)Hs|}Cg^`0C)+8!Yf@#K23=#0nNo#JLW3;rL3 zLo~+3D5RMI)-M1>X&?lx^h^iA&R4*9S-kLgLhC_~30&u~skUfy=0w<9HM41oHm#B@ zq~MICJk_o>g_w3dUijiKGM5*afksK+al)Q*-Hs&ZkD?wV-q=Y$%0mC+FKgd^uFAsd z4O5&qT7w+C^{XW{e`N{ozq++2{Rj2zZipDAAu8l)h%=MTDbl{QVJeD6S57TbH#94! z>t}e5^tkX-Xf3;~=D>HFV8@YZc_sRVi~l4lS9r7$4>Q#Zj)REQRO9cpQU?^lW3oU8 zt!wKy)ihY>YD|ZJN!O~F(;0!zeHl3yB7-X%I4g>^y(znfufBd4m7F;0vg6qNkmqzg zf@iufJ)+!$qr~Xpm1(s!yjo&5q{L1ey}uu0IhSyfYIzRyVcr|c@0-|K{0-f=8`oy( z3M6=RdvY`WT`!v5ETM{BjWif82|aix?d2rs$6>uQ z^2#5f{2bgFh(LBlhG(DBJ3~5@|Nh^+E<}^rQfoaHUp;s3`HAM(b7SE)hdHFlEcH7H zAzDg2Uo~hnf?rt-u?b!^`fJV7c#rD0I(B7Pn=YbSy|z>vjLEtrAnIw)LCwT(E>%gww+3>m?tD;S$Hj~siC4#=s0>w&{LweT{rzBuQL$(1J=LkO zJgl0_-1c=EvHB?vzL}3?KOWVLt};(Pn_rnvC1|KUTBM$E8lY-MT-cg#LR}GDg(i5K zORUhW@fV5b!y3+fK#ZWFL>V-GaxC(GiONks-LjmB-jp_O9uZ^CZ-vb0;BpN1kR>g;!DJt+?@MI&)4aXb{vBPe33dJnuGC( zE^8oKj*e(y2F;k9IsMVuRWD7ZI1_VeNc$PS!&sx%%f>$_yHx_iQ!`r z*|GXy9QGt;7lyb9gYOWZwpykF6Ig!Bw}yX>O=|4?6E>PE_Jz29FF$qW>r(KV-ZtC4 ze{=9XY+kF5bc8dUcc#)_E$_%jnSPN?jkerfn{;1f80e0TNEsD}@8axEMz95Eu+kjG zMz92whjeb;oEGeJ13n~L5#cayqkprnOVRS}oSYa}TBppxaFHDCE_U*-Mgu+Ia2E2W z?EX%N$fyJjgD^P>U~`(TlGurWUjoh&blyuLMAgieDOy;_!<^;dSEUc^ei!Hyo<(r*i@HH9zsuZq6d`JbqJ)|&yc$-gAr|cC>ayEey=PC`;oaj1xMhLliEK}%QT3(i zN-;ab-{Z0$hDG~ztNL8o92YcRd@)&VSdpW5ObLPr%t(Q$+N1%=& zEVfpSlD^`yZ^E@1=9p&?FY|}%pIeQjBQ*cLx@hsQ zt)(^yV+n}y*DcJn(?fjzy?67lHi~jT*3$Qy^4|eXZXcQCzBe=P2s~uLC~&5qy8q`h zoQ>Y1zg6BTi<%y*@p~NeW+R`M=)am|6Zv~~nP+3_NLbrD+Z&%GU7b-gpEO&&(y@}A zGROGoW|N@vVS;JP1U-%z>=0zD+7Yc;N$6U_3W;~WJgGkT zMZM2JzP_k#va>P7JQece79B~Em@{9CIoh#^?64{`INWrIjtsIs#r1c~?2II0#q3BS z{X9eG50`V~3|Y<+gzyjH-iwxY?srG}W=(1$jxF-6GCl(2iPn2h4~;93U2Xf0Wc zRI}*lKqUm1Jt@UV&Dy{G!OpZy8kdmR9H7tIC1>}R6f9HDdtg$WG&^hm2ugac_L}IA zNK3)uNr)utGrx?rpw?gnmL73W5->uzHYfwx)d6H2V0Ni2Xs39RmG;gJ)`j)CtXDK{ zd4hRHV!xZ>)wLoWiBqX5^e5`O+1sM16Z_SZAgS&6jP43$+q~ipuKH^R z-<)q|NC1gCszsuD89gkV_q;+3bfVY|RliF@cgXh1bl9IH#VVk41Fa4U!LZg;Y0Q~nf%ex!mMFZVkLb!$SiR)}y;5UBbipgs4!C<)B+=E) z)rNkoLv&T0p_Pc-9jCJhRWID^&tv!~rUNV<&crf9OGFP-C{7r_>Kp)?-I}&f|MoH>Wl5VJ8gP z>gWFJOL-dS^xF~_?XiB3P9VIdJnsY@0F8Mj>hueYhzJC9!ArDKZiz+)!PA;`!38%LBzE?a{@+~y55mD$_vP3#4h1$m zj+ZKAby$CRv>=1%dL8}=H}tbx!d5<5BVr=~z4%eQQ8Kpc@*5Ow^@X&ii{%EW01Wb_ z+D|tr7QkSr)V4|!Muia=H14`EY{) zVfIu>y>S{n3(ero1{M?f$($w8em%QJ9r$DNd-S(wV1UpD@yZyqc1tR6xz_#h_#tJ8 z>}hR>L6;hU^Q>KXVJwWN?$Pp3hhd~`xwhv^pl!#cyE5V@+6G-R{%`SlX3UW4(?yaKZnbwoh`tyvzdd*b|_JnWZmJUSbkrrdDKQ-JlUO89&vYORKarDAaioi{L`IT4&+6e{w) zPkH(5o<8&$n-xLyDyqIKp`_uHTmAb#jYboGfMAFk@x8r0 z8$_pX{I7=OU!{kcwIJ_{$h_vj3qH`ZZ5)vdzB1OipNt3)cW|ghf_z|wp*-SR3Fp=4 z3`bKQ!xFJ=QTm=Er2OM1CZ%Jf*J{{x#2*-ulIviIeK^r+u%ywr+l#D4k>G!c_etyE zWsATJw~zDaJig?w((PQt0Y2|L9erw-+<2yQ6jlq)*O%gyb;h%!W#$XXq^476L#Dhx zn`p#4yN1;9@wWBOR4!^X91U1iR+kE@zha+WhgvEbH(Zd*U$_zLbhoD^8RIyN_9DS{ zi?^9BtuSrCQT+pPdS!s90xT8f;TM>4lfe=qxdU{ep?EsHN6Th{`|*i^`ULCn7N3(i z;PBnT+bBYou8-=p#QO(ZypyJU!mb$mE=Ole+U?RO9QQ_&J!jBEF|$sVv7{1|g*h_a zYesdfl2;h(#5%1T-zAJ~X~o%mLdNhH=nb5^T{af!D)+pxm!apjk=-wUyS2AP_G9aO zLOj!kLZ|HpN+sVH6nqq6gPg~CXs0*(=kq8{*x|Ptk?u$Nd zfp!x`<^o(WCymlXnSt|2EnV6Ur@5`R|8-4h^(*fdOr(Z^Y~^IU#7v zw{8q@B&>1)F8tY_q#jK7m4V=h3sC=)=#%G&7ECf`@J@rdHlwUP|0_S%+;f-ifULFy zxX!&G7TDtSIyDA^`w%XQRH6Y*&`LC6=tz)Y$pF{>oMMA0gHq?gh~Mx9+BP=w-I_h+ z(+sp(i14CfFskSP>glCuC~`w!%v9woA+aremamkM&ZaYf*6C50J6dIsrBlTlw$%&q z=pXlUI&H?UEpq9q2~ST2L-U@kI>NO4D|a%F?w^JMIt}6qZ*TTr>1v zY}BRpz-gO3u`fs?nH>);`g_Og=N03nef^og>KQ|c7tav4?mG+>AY7Z?AN&Wm73s(^ zwRd+%KJT=zidJyz0XM(V+lqwWS*myGy4-Vo4nxD5EJJG58raTJU;aMNMF%!^Y{7-c zlF)`t!1Yn#hIC^^5DNX6dB0D93?47GsO()eQ`56v@?9;~Yk}q0t!P+56#rTh!c|?B zBCH)oFEf6k2ejXS%6yt&32>;snMb2=vl?`DU@A=MxTb>{P@((hqay~7qj66xd^7ET zmJKM@V(c1<8^UGAoj!zjGqd2s927w3BYu_}5kn25X_?V!kL>I!%4dwG4Ldpa9?#kK z7%>8+M{|^?C_MiYD-f@Tb7aIv15$Zi^JhtIVN_*JQc1{uR8)AGpPCw6- z7M>0FnrN%3-+jjxhs0|Kti4=|bYrYNHPonaCnxTbB=0jcF1oK{1`{x6{Ku=49B%=V z#&N`27)#`rGo(IqguDThlB#;=WR**x5a+Z;gM#5<`4TS755Bp!+w3C9LwdPsM8*^t zC+Y2fv>dCpELy=Rg3qZcJ%@s~w#ih#EK43yWAzRmYQ{xYh9Bl$BGiDX3;4 zEvY5fjRjoz<}m@S^z7k8bX3M3N_ybK7mE2;Xwzb3YK1nag6z3{xOeF&P_A`y_wXFz z$S+duuEKPLrkY1XC`0^$s{bd)pH=u{;<7At+;>3q0yU5u4DCt?^*;opHkDt$q39mbR+ze+APHYc06b(&IHf?0SB!R9M$0IX$eNO8l$G>J)B~gnP2}MV62Qqe}+58y_*Av}EdGqs@QMm!O_5~R% zXV~?xHyWCP8`;Atj2F*BN@#Ew>c`^SIN|viIS~Fn&Y5b#3Z7|BhvPxN+e)m4d{i!L z%q))(A(c3#=(s$q3($Xr895(QU23*1#KaH?l5}?8;T{8Ij72%r3QH2M-C%oB!Lcx$ z(${cuV+ZLS(TefPJDe+5?`ggX8IC%D9kJC1LMIyI{S@+$A#`Stc3)3wW@ylj>Jqq7 z%c_FYGxLMnA2O{ektWXq>ahrDUBGtEbn-BnPfkj$X);w}YVJXZGMmNGyF@^&Mp&L% z+A9d6+h$9j``^2wu1VeqqI>cGol7K`K|0j+HNxG?z}KweQ=6I_W_7T6Izyf?!VXFo z`DWShgNCwWCw8P62N~t>*NH=wL zUqC|2s!8T-*22Ec$sB$;UMf!j!()mZUzRc8F2)L(4g(PcS%WDqLV;XNZtLeG={gD{{x+Md?7qN4^Y7!K@F~w*2e% z@*|ct3B_!+Lh|08ff=giQ#%%eSY5k;L6hYAvkNTwrVZ-0M4kbIaSa&qpOhey=RTHN zN=yRSe7&0I$JTNOcfwL`{^p%%7=*B-h{>vzxO5(;gc{f=wr}L zuq8ej>P)zVZym`a+R`Da0lXyqe*yeUKO3AVN;p^7uR1PD8#pe<(N18cF&Q9kJ&B%XM9=b8Y{dJ3o!VBwMIGCY9V6OwnksTJB1yuS=0jH< z+6B$9H~YhW=JUMxO2mWsK0n2>6A#{wx33T0QS_bp?7mfe6@|Wlo8~{*Pjgd)jD%iN zOr#?+{$aT3bRPzjD${V+5wSlpv&mIkgx_2*JzFjJMT04r4RR+BxPY+xzF5$70UU7l zPtiH2sr&S6+E_+*_Q31v*RKFy=&$k3PJ)?JbOT!ywbW*{KmLs*aq1-AwoGWLX^0+3 zUn5q^)-a3h2PO8*!f^)YAX>ZeB2QHA8!z*zL^UU3m^O=u2vH`l+CJ%ln7Hb(o5_Y% zvhy5#ywl~{Py;JwVfcRawR@Zu0=SW+C<^O7e*Q}fu9|xFoRQkEdM3(4x0thF8i5r% z5a;D%Ttgq@x;gBUd%giaHrCkAW@+vnCehjvIwSd8869u|oHa!m<& zjWxTF`zi7UF7c)dnaRtkccrII&4ym@x+G2WTu$O=Z5(5fBidx(nyrstFUi1GlidTS z#(H(rU%=8zUBE+^5VNUwL7>=N?*T6@)Yz1aiF*Isirv2H~(&5ToyGFhMGOzg0Kv4I4|dL^4Sy9`dPthziX%}D(FJk z(Sab{h`wh&eLE1?d^cN=`-(J9(@zs&dk5Ap?a5=OS@Jr2%z-{<-1-k@8$IimTkfMb zc#p~xS&T-SH{^hX;&nvr1Da#8A7}$0#j%GX_M_DZwVgPL}rT>}Qr7VbtkH6N$`*XLK~yQOGd z9(JAxtqwlSRB%l=sckYqsQzW37V==CF|4=LGRgh0SEG4}_Vs z*&RcgS%mD4J|$2MaNy|GC**q?Dk(AwNrdwb1QYU(SrxRqTb@|AWeYH(e6a%3@h zF`7W3zW8x0LjA2PVk|F^TP|FhUp4mB=I5+OET|QIj*12XE&1PFf@EFsiU1o(r1!5Snv6|g)3B8bTK@TWj2VD#MHyIX{ZhYw2x zW`=F}QW@S(l2W&AIqR3584ec3S^Kl)3u_Z~T3tDfR7Wa~3ij*3~&Fkdr)b75O``M%6H z6a%I3Hy@d>l2hpS1Cu2V&SOS%huYnD#P9=cOKa`Lrhyx_{r@S7psRm{W<6*)9J*tr z@r$e8`fXaHANQaYJ-Y6TL5ykpme7G4O~dx3|8*Ja|GJDtIc~B}5S@GqP5{DxfpIrs zjoH+2-$BX1q6g=hiQiCbuQ4PESkyy(B6Oybi4=<@a_bx6g0I{i*~t1mkn-Ny?GA2Il2-E=I4Kd~VDUlix=qN zTl@S%jygfVAw=N#hZn{2dC;S8F6`oGbk82hiRY+p?BjGy;r=2VO2HtA(lf5VQ& zai0ng=9vICW;5vIAs3p#$~A}7I>M{@mp+NXiY&c7upejHqpK2l`WtqWwbpAxv-xB~XeSyq zxQxL|ksTfy?tS6Is(k&*()VWdJSVaBOS&)r3wo9?`^3sZz;~Z~@)>T)Rgl+jcuJ7A zn4CEt@ZEo6BlEG>PgcMq;`#oI-;U$|N-6ArrKEU@90vnIx^~Aoh=!xJ(l2q(uucEO zE%sfK8=6RF2|5s$G9O_+`^Na%BO;{_w%OyW02L9`ZlXqN8~3yIu)QX~;lF+U(5Cg0 zU99!8HEJxQ{qxb3jhR4hpPJ{Xx5p9-%OhA9_o=o}YV zxq&^|jSp2%ISKuc$KSZteKLRQ0=?Mkofwt~|4Iz@tkfK(TC74YzH}B+iwYA1(c0Jw zDO8-9OlOUn+mo*7imUZ!JsJ(e4K9cx2T>6N;0}=hj-E(?^Jv`2DK@+?_3a|zE;Jn~ z3a&*H?Q=pG^#b)(9!`*$6r(jCs(hfBUEUIm2rPe>#$YP$Db#kvg?#ejuj;M7SInIO z81dvGd&kW1RSg>A;tSGjPnfo;nFNw%cfX>yBFBz$B1eMe;6ZPO*H_#sp={q%kt5B7 zU5hwW=VrrHb~aqx9XzL7s_Z+zS|=F{hdwvX!-V^Dp>O)PN(rTk=arYjJcnyHdLuW> zy|bpm8u$+SOj74mG8vf2z8u7kcpPq3gl! ziu<>3dALD=I}8)w+=eL z-%M(2jn|)n`iF%_${+vNcQGwxdGA8NFHAi^uBxBh2Shh`R7fP=Bqh#1f+;{va7OGr z9v{H4vFSa?hTGc@iDORgpx4nwhymi1V8=URI5+=AJ+O`dhrFadS}6Vw7&!}?`Py?S~mCMNi z|Gj=JpS&4>m$t7prKcO|bJ)`Tx&26@2ao)*zp^7n!tuJH$aCdDWvEHu zSPM-cpZB=>zEs_6`3>{T_5Z(Fmx`D(Y=jfF$fRJp6*V>@2xpLxatc?9^+HsQpPFpb z_%%Ccx2rt%Ez(s(Pbk$879AXh^fOASQb=*YjP^pi#)B>)N_y*v*>qCpSzf~i+*Lmbc5 z`5UeJc%@k{2?LQc3><+>7jqOf1B|*7JydT6e-B$=inXqeo8%Yt+MMl7m zG{WFNFl+RZ7a44{Z=FBp5`5Ib2WNM-wVW_W%2@v%njwUae30#a_NmXYiYL(vCOd&c zMYmF%3-=4kjwj1$V5hA*h3uwW{$m%;>OMDmWs4%oIUoCb;^6Vt|5I9(|4NJ3@R~-o4MLPH zodw#MU+X_DLXB}Z;5vde@#-lINouL&x>-3!i&Ty1w33&j8j0LVuwB)#l~2OBSnh?O zz@CUFna)$jM8Vh{7f&V+G=fMVId)iFq5pfL(a%q(OxO1LkHq@~6F!et+MJM~_|7%J zWCih>({7}g9_$v_|G9K($2W(Xh|fY>K)DQ{J;;43a}R!~dW^pX+I3ISV06#Xb-g6% zP_@s=;5!YLFYU0FoPmV2qtL9?v#aVic$z|C{q{kV$XGZAaT-c6c<4+qfx+;K0He7Mk%8YgFZk?(vOzADf_>X&xK=Q z4LZ1S^;Wl1Idu9yPX$(MThvVd3pPbm- z)P_YOEc)MI&>uPbW zZh(rcF4Cp0Q>pRflVPoG<76TM<6iXzfP>_Izwjrnm1uT);i!_&}VK72wwxH&sV5P7O z&Nh;tBa|Ftw?XeJ%33X0acx5uFm5nw0#OFsUj&{DupwENIPL%zAtH>@(@sa8O!5{C z`?dAHa}(a^BN>KUgf#Q8;UhAj?T~?VH;~ylpnK zuTp8&eTw5t)P_e=K`S}PZQ}eEO|vowY2fBkz>3n1A0MTUS*mw6(bW&tIG?K3ZTWI$ zpk~tdcK+(bu`%kC-+P6w|A(qC4~H`T-u^tZ8T;6yFqTl(gk+l`6-j9mSt~-;l6{%6 zMUu6wSt?0o3t5X1MOrK+d!`6k$3B*sd7r-D-|u~|_j36M*M;XipYu8AKIgt~xb)k6 z)w`fsZ@yVsd0Zl>a2vN8f-Ra51W9G!UI$R}?%WSmEG#(G4Ih6qC0WQA-CQvnDH6%x zc##Jvv9qm0Yuo=pXir{cuMVj+ALslV`@_sp*p$LXQ>>pn(&@A$TubX|uVWCNPW5cC zwBsP6s!mqdyC+F<{P?xmW`r$rAxfn5p%D2xYwZvFr;&iMu!XO>i-qA0&h3d$-Dmy3 zy8v{!0JLyVgnQu0=r_a)O6Efsiu-RrBW&nF?gEPRy`9B_m;p+}5Zi0flgz8Lki90K zA%0C8L-aU++iJJMxN(7dXi5asw=>oVlKo(!x~7n$APPp(_OMZJwC@==pv_Iu=vqj~ z=iEu)1QEtQPg*L-py+{*?nOEiizOE}G6k{R7u?rmN*fXnLR;vn5Qj2Ox{EbRfb`&= zZ3s-kcuj^!fn4{qm@cKFpBoOOT`sYF*HAU(r)=3LdOoYnk#Mox>rEi@BK45FfYpl& zo}Of#nr}rr#xOa$S2SFBVK(S6iOCZU8lbtl4TFHzugQ9@uXbC9yc(u`J|T5jWU4_~ z*xv43Ghy`KPy_Cs;9mIKoxibtW(7uwTy`?yrkQ%XuXH}J3t{C(-}e1gBPh- zQ+Ml|BUN^`HL*N_%7xhPA~I$;Jd`Zq&>a4@k-0T%6UEZ!-r0x|dA8v(GA+Sdl-b}1 zE!xe%x&}wpiv6z-d(Cp-fsL{;YB1zmsDa8`4)_#ot4~D>DttdvNp$Ei8-;KhN62qR zffO$JuB<*yeo$^7e!1RF6LC`l9xL;{2XlN*dc1O!mPy z?pFwWs$*p0$OXRBlMoeSF>WlX;0G<@3X6&W?e9nO*`XXvcOAgfGZH{HntG>QV(Mo= zPnGNas}(=nz3UHG@cDBeBRimkZh!+)xcJ>S3&749ixa%}k>SFR(aajwE)(A~qIY}p zw!_uitrQ&)%$ODhB;EG(hh#D%G?)Jhu>>jh9+e#AhA8thJ$^1r&!(vIWZHrL2-Zm8 zS}GG8`90j-%}sg_=?@&U(M07ENbc-9!sqQa9=FRftsHgkxe)PQ3Gi&{3l+}p@a-6mIv2S-(ieN?c6`;_MqS7){QPp_RmqAgu36qm-1BfSuU`qKFoIu zKR25{qgD5ZcEaKWco3;$_JJ+ z;6&FUVnoznYH_ zLLbSvBlwFdi=mK&t3S#s!m#ak0w$+g{+j{Juut zzWp03o-34}-h6k^Sn?S>P$^D1a~f#a8Hd%|BM$x(1yO62E0J6%$pE(xl?7L|=n)h@ z6|TC~&PB<1{XH7Y46UFc^`qp$^q&u*!ujD1IV1}7bMX?ystH;dIexH>cmiuB65&nS z9`?$}bh`NEicKjAuEGPX-@d0Uukrv`I{(#^TbZw^)=sXwr_Uardg7^ze0lLNha+R@ zs&s*lbN+pE6Hud;k|Mu7Zvujk1n4@KUT)-?|GqJnIV6(;xd?tw`;Z$gZV~}BH1PN* z!Q89QE(Nb)Er?yE(Y%_9Vqs!QV;M}>&<4p5(at?+G8PnQXfyHw=?KcIl)wkUlIgM_ zT=feAOyz~YyR(qnyz^;fyJ5|1=ZB5+M@=cH?qu>g_5oE8P!nwNB|JsR6|r-)^ZfT3 zp(tj_-yPW~l6Psc6^eFakGr}(iM!rc^ya(rVfukSbn@(_*UFmEAG^+5&(lJ17x}<$ z!w}{hS#ig|t+uDCp|x9zr={6x_IR~C5Wyw1lX0D8hJ|Fgp6FP3O(A*f*lH3CI~)>` z6@dS){fvY``7xDUfASf{%fmX_ws}9FotkEB{%e%hGyhd{Nq3qS>oSP)>?h*@y~Kuj zC)exa)rzSYuUcK3EA;!;K*?a7>oicPlb=`3+$3>mD7j1Rb>vx{x`N!9K2Er~TgyY? z?X(IpyxUI&SIROJEhXvDeu(*nx}6luS}!E#!s4i}U6g?9ya=R%|sHE-MajQ>#uN9(+5`(u;BamJH$` zEAi=!mltfkS{gW{pAEJzS>)bVh-*10vM(}xugH$CG{gvPG1p-AhVOO5U*)HCk({o5 z3C{{5;W4YhNIo2#u>SO+OhS5QQZ?MW_Uso6cIwppTW*#m=Qn5N*BlQh>g9<3*Nqb! z`!2%HBu-GsT)Eq@gKc$(IM)sTo7tgzlQ_yi7KM~f+-3iEsC2mc+vu$Jk#~DC*J|ru zCqdpDjw_|15BY@%fV{U+e~k;;>WKMHdr`aabotbFp(+p9G;w*=JImPJ9^ zxu&+-=5$o@dcc#KJ?wy&ey@ly&OvmHtV8s?6R;+-j>_$%tI<&4hg5U8S6j}Guip^D z>dsa8ej${Zhv>Z$gV)i4qZvq`N!kLcZgrF;)PlyNSez=rV|`H>a+E{@80wtLyBQzy zDUHROwDv1@lREQj8irK^Kx*AL~(Dej$C zsNxz+%pPmDlWgBuf!xpBb(9_m-tQ0d9`ahIf8rN*%W4J(w_au_KWR0OsI57W^=Ed- zKunrN5)QvOg=n!yFVB`B)oA&p1TO2q8t;KQFJHExGhrV~gF1O%oMWt^hz%86i|~2j zXV(bo8Z}dS-JD>L2#G<_?%f`{zPx*A8cndDI)#K`Nzr`)`;n)kj1wdz0gppa+ddXHnMRp1BklKr4!p9A3nMHCxRsy=W7dz znmXOF&?_S5EAwg-B@j+SO4Mg(uGvCwgd$B;p~W*eqEH2g`Vs!=6Tbt&Nx3;05Ar>B zVtT`M!-7ofNa0}TgAIVHDfJwZls)v1sE4sgy!xGWLIg-d9%7Um8!w;bPX45~$@f0E zS8g}^!K=AdNv1wqkb3Udw3pTx62cLi&(r*>6FdpXolUvP#OWk{{-SW09^Y3jk*>Mj zD|aX(FKyjywuoro?lNnd6aL2k^T)a#gw%eK4}*d`B1O`<*%c-8(dZOm7v23L1ko?& zt4lI!z7>wXBb${eD{tuIi^tO1pkm~h+ip)dGIM4W#@vHsj89@6xS!1$Z}?aTCOTzA z)t0-DcsY&gY@EYxzr5M}zLfMw$ZNIeVZxsQIl8Xxjc}QVoP0MGIoAI>jko7_T~5?} zdu$}BD1$PWPC>DgAF!HQl?yZ?jvvv+pF|e(Mm!Xvr22l9LXac3?;N}Mr8-5B&f{v; z#LJBLgUKHRuI!_?klm#`na{qB)abnxyhvuXN7l1)v^;OdX0+{fUH0)n6wR6-_PAuI zpSz^$hgKkm^;AAJCPv`n48xv#H@l8Erh-gn*YU7)6z4kjACuz-Ke!_Rqu~&z&Fo;a#;GyhMVK2h@H?_5j~%=Mm7ChsS)kRPV$Tjw;GK2DvopI}9yk&ml0agg&aVO0Cy)bI|zB;tNrUarixgjIJ z9N?rM*}e7yf|QsW67#GKmdwvAAgN-3$6H7FbH6}aUojm+QP~*_PL&fUXV(5LN zPv>pLeH*7woZxkC_Kp~P=919{?Gm)=^3&_7VJ(X^`^e`86%D(Lg~2D@?juNGS3@*6 zn;=Gbq@r(&f8h6DFs$7SFqpgDWGB@<@8f0jezVkvhwwieMQOyefDuuDQ-%+W74`kY z9DHJQsf5^(&=Z)@v*s8Os;#8IsCS50gO387Mu9(hoz-RdsnYB$>p3Se5l1-*SWA+u zGcQwA-_ZJ8X}(o<=$$Sx3IFn+mG5&oNwYO_yd65UpE0tO8|IC{&n=rBi@bkBR*}GO z;l-EJp-eU}c4A}{AU6i26y-<9(L3u9j(opCo2^50$XPOvT42AaqvMrG5PvP-GzzC? zF?<#;Kz_^B=Dz)aOifAV7h|0{@Jftw=0uPrN(~-w^#WmKNQ0B2Rvkmh#{zcmt0s26<)7G7dx!Th^{EZ`92@eCCMbT}KiBhrdyY z%6>EUbz*k5{im6ucN*np9iC@AXKcUSe+U6k1?xWB^hqp;ivYgx=D(VfJ}dMPFJs3H z3qpG~Kf)Xwhji6vB87V(a@gM`@6hqx_tc^3r?30AG9IvPqc@yri!c&Wy3p41BJyO$n){Fo!^`bjh`{bDO`6UnQ>XUBu0~F%{kF!nYnW0m$UC) z@`Ki2i|B$Nl3?hmh_G2qGA~D*NXN_al)Zr4!tgNjkD@t}Ew$$EmP>Nd?ugs`V)$He3Z|>AHlj)SJX;U ziu}7`4H+_y`V#D;fY8rzn#c|U8t|G}9< z5odgq2iRd%!6U!N)_Bfxz0>+#!(8)joF`e|P==x2Lp}eh%!B6<=0Ck%rk}30F6^zi zc!CP*_7^R#3^RRpA<@@rPuE9<>&;AlJt{sPl#J+(B$XtmLfgz<)%tp|>8@men*O{& zx~+cb$i(mapg`;1EG3|M+pd<+-gu_Q5F*V@Sr6AX!iw#L5<`x6BX!w&vhtk8hoBYn z&gqe&{)?ImxoX}lF!6OPN_9Spf9lcSH{vMOGF6o44^G3$B;(Z+oQfHuoG|B|ilvJvgG>;(Y3Eur8Rxehp07QdESYo|N{A!x+F|Ya8YZ^IRLz|I za%}q1@K9J$ zLb48ZsZ@i|J)7=*qP zzbQVwvtiBkJnVGTMVK-EoIbZbeY~;$@n|i1tR1N)OSwS%eN!GBL+@-ohT*~4vc@tE z0r2+Cj9JiKw1wr=--0EUvf5fLrTG)G+%#7XAG-h3^qZn99w*M@r&&sG^vXs06JPu- z&Z**rCf7{`{z|mzMeE3VqAlO5oP1Pi+N~Nt}o9==dx| zXgBd)wzS_KavhJcrJm#G zLA~ITKOR0F$!R4=1PS{fijlevEU6B0PBShWWcIDgJD*jAHDC=J1Sg|fu8`xWGnYwK z5v%;L9UBMB4oNaV8h;F!-wUG+_>AY5f;(!0WJl!7*(#T%eI8_+r9){*Cg-{AQ?C;I z_qT)#IF1#b+&|4mx$<{!toV<>6GNYu3O?C$$Zd(VEcV&@*jQn$4|Bl6J$tSbF@?tD znkq9aS-F5v$2$UikhkO-DAkdsJ23s564jFm(vJPd$l1=uTPW>`ltJ!Kf1^y&Du^1Md4?XN81I=eN(f$4=QPOC-v&zrx z>f9fV9Fq&*9M$h$V_vWsu76jPczZKY)AcZky95!x54g2-U!X}^XTio6`I*d+=5YNF zoaQQdSGf~FkJ_YREh1dz9{)w4O1vYui>QYT9np4{Ok&@7ec@e_hFs6jPp0TKzQ9+W z_|F-OBez%67FyZWcF9*rK4l#oOW`8+1Jq`9z7+Vr>Tm~~gyLX}AL!nAIbkNW>BEJW zBlOXL9KN>3@cY)3)5`R_IJ%@t=R>oIH~hewdiu);(9A<`zr?CGNW%_&DD7U~2J|Oc zBp4R~9=%010f-UF#n=*Am)#ei8b;hWsN0i_>0=fQef;>66C(SmL=wrvyKz}VE;&Ks z=LcmR%LwdkyA4hYKMZ`F!*yNRwBuv14M=6S@Xl#C&n>})F)4`vUmy|xtnzdTP#m)N~ z%c1dkRfUwwc7q>ZVoX@}cO8CV3JNJCqfN;OjI~3ehXI7LPw1V;O>b3l<2-uz*KBYFroTnnW=KGV*BUgTp%^!_XJ^E6Ki6bekHV{A z`_j@Be}u3VzIcln`8M?VOitx$;hBv>Oj?fO_8GdJ9(rS8~DwJtLl`J3y-QNl-UQ>WzjzJF{l6L2otthv_F6G=gtFy}{YJOQ(m8R+y*X z?FqA1(mE?zlbfUEOZLo%eChVxp}8*)AO;2rH7JE-pdMVQ(!=oa;G(b;6az$=pGa|xfIqM)^9USFTuC!X}(B3_6CWyDp$RtAFhRowDFz7!PHayvw{(kE|Cb^dd zPtP8ny9ZN~ciR9gMAN~pZ@%;W`sY=I&@$_`K`n-q+ z%hZYGp+`QsO7e_V0~leM>Ui{e#GU&yT1kai^ab zuz*{9=c_Mdq|^xO52#~F5c9Ucmp-Q`=0j{gB=$8RY2jwL?Yxhkb>CQH=ykBGESEc7*EZ0P7z-fRnt)%7!g z;&x_)!TU!$@n_;GV*IUr(ihI74z$@nzOy&zoP&8nQD)|qkGs=$tsFDI@TA9;|J-Ln zlh{?R5BxS<3h{AhGz9%+D74=QV2yeEyW|fB70u5rjIL1T+S^0&Mzh8~Gq3q)Ee3=i z6hH5v4ks-}jM?END7=K2C4V{Q1h zpoxvQJYa=TzO#~O&)pEz{OFUEYT^9bubNN>xMS)Odx77sIRD=V7!Hm_T_(`&`Y3fl zL7&Er7H`b_kxIPr-&gq1?^p169%&y*?n}S+#9xhu_9CM*fk#;Vx(mpHC7BUZLffasp#qJ5k~nMqtd9%pxt#bEqcEl2t!8%9oC$&^&Cp4S&-I zz(dRPPlSrCd(ZUy2K5UxW~alxZ9iP6jyUQsndnHEb`IY!jST4PqO3}O^jx32$R%pD zAH!SJh31@mCJoWmAHOD+@KhgRCwHU4@H-_iEVY}s`)uv(*~&Fks~+5Trm~H~$`lF$ z+yf;ppC&9r552|kaB^!4fJ3SN`5tCEcDH#MfgRVr5Dl*1V`J=WtP98`YvGQHnlmmm z?tB&^+hsIx;5#@*(d!1w*ym&9!RqnY*-sr$^UTDH| z=Y;0*d{jZsLYCcW#VQe{^Ub`RzsIk7K{tI_9UtQK)7Gk(46i~R@1l-QEbrOwq>guZl!J5}`#9GzP8PY^F;M$HU+3|X`0z$F zCrIZm(B=e2cko#JoyV+$(quF!B(>g50Cn)4^Q~RtsCO}h09W>qwXaV^1Z%)on^Y=d zWDy~P%e$Y9JdX=#a-5O0vsjoLj%1Z~Q`%>CcjTPaF3J0)<@j}dv!Lr1f^;KmM1X>r zsdD62Q$ZzO$$A3?_t>W2>Mi2cqWmmMIC=ca+aSi)J|rY3npDzR6X8N5X?e*Wj|pcb zfMSH%!*pt;1!`+=5#x&Jg;kQ}0wrBp0|RPa$5gX5G7^bk&q%<&LG@NOl0K9?0c|+H z#dI1Y%Vw$S_4;GxkyeT*wfyNllb|8U403r2=IU5zY+l8+cWClS+I(NLUoc ze?TZmb7>n@>CpJ))={R#!Sj4E?gK)rDZKta$DO5#xOxQY z?cHb4MDR&=GVzx=j_*+hX!WS@B?D$j=p!%qMC_r7H(^W>Mt37=0T$)>oIZ*# zx5Xm`ZalxlBO8DSU(a=h3jVU*smH96qAvjpMNszvOID3|*|qQLhi(b25-dCLN2Xlz zFahsR5gxqn&mh$zn2(ffs7db+5r-vXa#?*NxW-U(1n``AmeMbomm=yY?uW9%tRlz* zoZ$g6&7;(BiQisx?@|?;@%edoBe_;7XWe6U|N2U={o9UN$FL%u)Py5Lm6^Yn8q9M# zWaI2we+pGM6TjE!E*TFUt$E^o1XaNW&U6!w?ja*xAplEs3=H(PSpDxVz|o=9*M*#y z!1o9=F`0EPED%At*1M^0k901Aa$b96jixS^^;4u?Lbfe!xuWB>@ja{qoa9xy=wDhX zruWywQx&U{B_>duP!OU(d_u-c_m(Rafm@*7)!k{Jk|7_?-l|tyZ1&+<-&GzDV(;?w zi=>+AXXu|Mcg*hkcH)*AUV$NY+{wIveD&&~OYXx~t7!)VK();!wtRVc9P zRP*csqwv5x(6)k!L-F+EmCH}K=8i7_Z08gAmJLcLUN5O2KTjzc^=OJ{A$Lm=+xgNK z#W7(7&9U8!mo;*Nz{NHzFfB|8WgY-m5`beFuu#1Q;nJU=fbI57wdlme85y@>ZpHK{ zG$s3;kFo0s@&yQyv}HqojM$1KgwRvE& zV^wgrTpo(lQg)U)Er+Zsyv1e_x9=?5JGQIO6K;2%VgJT;^NHZ-sq|=au}qf7ZvD=g zQP8xOgb=+BhcCix&|i&bESgLbI7;IccSo1`d$#;ej3nI^Cs}7@Dw6S0e4sd=fn$;O zD($5p3M7<3VykvcVS$(C)yS2G^q4jpkempdN<#-&uYjJBumW-t%DVBIJ^D`)Rjx@9Hk7M1N z`?fO`+x}TXTJwX+0M3ex`+YVjGE2*9qUk=8)|G9naB0q6iM{BZ(K;Kj#h?@(GF2&~ zEpM(&xdMvM@jOwUihmbYfj(uywm{!fwGuYoLG1EV0GrjPTMkQfuQ)bQrO8J*h0!Hk z=8RZIf-L@rCtn*N^CCij3W2Mw=MeZEYCw2@vvB7*5wbJ_*r81v;kvx+p;m6F&xFDd z3lnItTS`7?j%q^QMfu%wxG}73QLp>iGCkKV?I@XtzEX)&CwB<~cJ7bXY5_GdNz=;1xGRMIfim{MG2)w;t&lN`@z?{H;4BV4R{N4Kw@kQ%CaOV~ z^5g+eYWjqDuRtXDaVR{|!(gaD_;b4T!@HFh>{Y-~3y=c21G(>~U_5@ja=xY8^wJ&~ zTaAH@u5y_hc(+51BF`g~D4Hj_y9AJ)Y~0fGAMPolpb1m(Q<_+fqlOuwtLPvvoH8n=E25fiUUjSxh$oK;PlFP;SDiLdG;;`2)}Wd_De+$ zisUn--PQfW<+Nf9`Lg4}SZ+3gKE6-l3lXDkD&$SPr=w31Rm)=f`K~=`4R00XfTa^P zuS%t0c_JO?-JvJ}+zy*D5x$)rA$%Pmiq0V5uMu~xFp_NHZ++vHa>y~r{fvIhn}qS{ zGDH+$P3|~Kat=Sd8^J6-`=$)6%Y5m*LGiEqi6!TAaLlr0M2>C$8WvS^p?Vdb+s__& z?u+T*rqZ9H2hKKXjK#qE4xB>Tbp+&Md>ySE%85F@RnUG-T99)Em?CL*6lpvhk?*-W5yZav;D zEDBkyo;035LAI7=$L}N(4+|(DSI!@}k?f6O#^~&nkqRb`Eg!DH27OO;Rgb8XwzJCX zIN>FH)5G~Q%AL%*T^;pALj_So`FR_c{QN9>!uQVRvw%b=yKQ)Q#b;bf0sYeYC<#M^3RmJP#Spc&GjmknJ?l&uvlLm1wTuKi}F#v zzWzI!b1NELKCnXKSry4*!aEds!11R4Xt=K1Ba`{#BIgoYr_OdgJ(X$@zibB)+*b}@ zXEqc8bCH^{@tZ@q+TKtRr9pdSRknS4EGaQut8$3rV2?qXDgYP z07$60!JoFp&Ld~w^Zr{%uiXZN`cl=wG8~QL!Ujg~qCQi1eP(17$y6y6aRSFa%Tjs) z21TqG?;;ZzyHgP+D10}pdcP`g4Te-uf~@0M79qz9k7=ET7Y#oU_q8qCrIiEkqTv_ z853>rgEo-iqoH|K4ovaDQvZ8UjHWqo$^!QQ&1g;?c;wMU)CxvW{8KmKRw_0bGdJcD z+ikvWvm~Z)j=NA6=!{t(e~~C*RAaQN}H=A7NZ)|>+9 zlL?|uKT=x%&TA;yzzXiWlx?Bsva&=z>eQ-4g5hd2(HTaB_tG*R-23_}VtH)Uba(V9D5otPId8KHEo)l5!MEn4Fv%4iPRziBs{fBEr?d+$I|LKub za|W*+K@@lHG{4w?Qx!J$F)1q4tHk`Z{*;D5+|33hYw-1^%vfBSb zo|o1oHp12R7lA*M5%ozB7JI)yvD9{HAT_my4$43@-iR)%?oa0{(x*9`Ojke0Ch79h zFZAzE)Q&uFv~$nOk#G#aYA7$bPNPgdQMPZ4g)F8ZiIWuup0c?er+O22B0VzMO@lUE zF&_kgsl;`MAMOPwHK+{>%4x$ZqpM=tg&N7JU^;XRiu+5z@L!R-KM`Pjl-d2A1EKH%NxTbs`W+H>Q*6Lo$j*(k8Ge`ZY}uGTdYu5Lzx zUZQ${nty2e^#10(N~zne$7@=ljDQQ+L zIp3O(MkY*3<01B)GN};XGYI_1X8R~f7Sa7ZKa1IZ6~OR%3mglSV?ll`3cm3B_vohW zIdlu_S^29p8BmyRCgcH?@bA{pyE_xy2r+Bxk17O$|5>VJ3WPpOJqC1YZ>VXq*O_gp z9{o{Sm7H(C14XTmbcJdW()^FFuAloM-)k6Qt098X!M!dgI)LGPT?CO{szL>h|IEUbcCMU)g|*JFYKMziroJ`Sj!R=ClR1& zmS(8dGZfP_qI$vY<5|m|cVky#9r|nc7dzxe0!QMyNWo(GtpEu=y$=AJe6%7;GEt&7 zN0Jk<>-?kRp|Q2h*z24Gsv53C`4cm=;` zcC5+%*X1AL-gwiKa6MzUa@n@%cE?b2cCRA8&;JzH@6~nOAPHDO zddF(=m$1+6o@9;X0iUn~^k94B-V&(a?SFf4W-=nt$Ax}IeEwFyw~U>ntLY_f zu@f~`S*(MKtONp(&?iJ zd|e2xM7g`2boCBky&;l*+&qwf_!_&l|c&Nsy%79D3B!ICw%Akhe)|2GX zRz4K6l4C)17VF#|v%7450|wfPio@a)hQj8^U6Enc2uEYUy8{;wNETkfv4utGM1iR+ z7ReZJiJr*u2%Ig2`%%Jq*?V)Lfo0?=blkOp`Da^qIu%S1zjS7NB_4(-w!9>8oBYsB zdpc5G+50`C&y)KQbGFpxV~1K@ZZVtau3BgsZl`oqp9c3FJe}{oDId7bnXl!zHFm8d zc^AF*4*m2elV0XulW6_qpDTDL>gDu<(^%I(PEl?rF#k*KtEPr5!j(G7tcg zn-26-N6O9OqIA@bk8Hy|R>T48F*}E|PpwYRol~Bklv-Vi<;^Z=kg$%JDVb<{^<-9k z^we(s%Tgo4@y8mtTOM&u&ticR%hU&{{XpxJiy(F6t4s#@-QnzOYPR3cjYB)9AVNMy z?Fx8YX=oW);DO?Jck0}|nCq4MFL;tHP7&JiJ*U4^SKnm4Tr7`d%>6tcO^i)DC9O;yvlPv| z=!}Ys2o2TZG6t8B1tntm^_iC7s5NJTvAH!e3kau)=kI6^Hd{Rjg|kLpR#Rj@@vl3@ z@1E^ez+}j@rq8q+UI_A0{JXy4U)U5t!ZKRd4?C02ch&{k*@ zcKm`N4YHy%Vy9=5dKiC%fuj%}V#3(x{Emn^BnUfsK|WtzwWnr9v3;}L{WCC1qp7PN zsJ&k)G5)T*BOP(>_u;+MQvus(Yhj9HV-4Fvh}=%!l%VN4zbP{F!Qv~_--+eal*uM~ zgFG^MN2Lx)i`C}zFs$*ce;jbKQtIA`sU8W|$P0=AYwl`Rx?MxEfAVGd>_c|-+E7cfaMTyqGGhean`q+~GQyl$JQLFkoy#ey$CX_r?- za#H7as#f45^yyx7SHLd;rTa(H!n|DavY2deyPa#zRS_H-B+FCx>KcIV(ohgXqU`Hn z(6uwTHh`rNPNPu5;4^q090-~h#`0-?a=*O6{4 z|GRm7yu;xeDG80_Qsxc^@jK49eNPHLz-WAGc<|tj#!cJV<+X4QzCX_lQK5QH1w$Cu z;rnN}OWjBHX6_W^|2002IiDzYbpLH0UQsHr&vE z%`H<$9X1aPuH2vGGxBkGQlE%2#Mx3L|BXJJpFsU?uLVj~jC30lPOAzU!{Xip_~|~Mz07jEXQ&I_Voj-NopktMyQd0u}na8OX3+c_?Fr|ngZ*vD)1 zU?l~0@}rht9A2KR+>W#1!j-)pKBafpwxG;VoKn4Q3n6E!COGMBfbIU6t_Nlp#NBYD zg>eIBgS+_TSMJZO8%?dpr<%PR%3gjdn-Bi;U>Ci;sZ?4&0u1=}nLBk6G=rB=pwv8v3` zyZ8~hi*M32j4nwJE|hw#+B#T%vG(4*KtJNx+)Od*@@pSMMS5Nf7FM^?weIBKhIKs- z;gi*-kERQQfmnaqCVLxlTG(jWwV!}sNW()tRt%YdpeJZxz0=W5MJphPfayKZY&3z! z%ZZ3{%->{QrFO(&<5B>009OeA1iC_y{9PQBecJ^;S6Jl{w)YWz#7SiAPDVSsc_&rE``mcQ}lFD(@Gw-F6^RovJikjR?vGPJoIkL?kDbp)3B#)=f=eLq^3{tbvO;v%zEb~ z-JY)N+Uv=VZp?(it)u@S#;|aUANhG;kCnA8_(viN?PHsI^7gOkRP4X`mkeFOWqb| zyIKAVv5Dr~#!bf-c;Vkt`W$J*1QV>emxx2p}*gdB0yW36U=9RG68(F*no7fdeBZVGwR9}d}hWy zCjr~+lT}&+l4ZpHqL>n=XAjbf!h0L^|0z3^fGdj}oX+)#CD(AP>NBebw~DV`R}D`5 zn@~N*D2MIkTVu^ybeeO#7f zNKYK*QE%DWX-!$fJ6V)*b^4wzBB`!QGLy;X1WH9xp=u6>L=ndX#o)oQyKH|E;}oep zWq8~fco&x3!ZKJ(s%ORMqe(L z_7m&f|GmF2(}_T1uFQ)QXH+v=CXF7tx681bgS@pK8?iHuIJ0DDc`Tj7r;ETcBPdf(+2pJ697IifaIGZYsT?+Pc!@?(c+n)ea*3l3!i!iWfR63~Wr~D{IW}O4^4wPDyG8Z=lHY z$i+`V|B!Us{x;`ZcmIQ=C;UUwM-%oSY!Mtm?41@ZlOxQY9ccQkWiZhZ?)uKD7;Vr|exkC*5 z3aq0nI4zC(bNLr9Y4%qXAIlm?ltp#(<4suYPoe0W5oSaj8=2u8&Z1{LMRP?01f=G9 zy#)c-$*qTsn8Wxd+#W31`=SXEC-ZrRP%={P_| z?i@%~{RAjq5AYXsT)>aaPjwxKe75(3H$wT zC2aoxSHc%xdhu?&80#rVP@~ybSNavVH{|WEec?EH!ubZM^WE{Y^8m8E*Dr677I!w` zCw?fjOtq|J*6v&V!7K;szZ&0q$rBoW)-F}aDX~fq)Za5Lkx;jm(&tG<=6Q%IUEpmB z3O;{&G{@EU_nSF;8--)UmjMKc}sz!Qlpc!V=e)-cd}KyEKEO72nuv|0_= zI)S?ciX06;hN!S3{p28{p&hux*wbYL1kuohj__{$;3mDktswu=)66x)2(AI{sb@9U zh4xCEMtXQkjwlUx7+H#mmihUuB_e4oSWf3;Ec3PU=VbDYv<%eRh@# zoi`TFkO_QqvynTm5+HScGDI>^0W#qFArJgE<;C&24Ib>HhO!sSMc#A6lk8+c-_IA} zg@)tYb!D-imh8jw;g-%0>BdfG&7Nxxb9{+=7gu00OYDWTVKxnv7s8bVBh?I7Llv{k~cHY^F=naf+4B6u?lz>>FHK2rwrdex$o;)|4Y_xu)N-bzSbVPJPK+2a!=ncj<&1cK3pT$sh4?X&)lKp zubnxDrS+TSXcc~QdnWXm#offXl1w;2ahN;gE0b)6n2VhXVtu#==YU32A#0mH=4AQe z@Urf`>u=G-cR^Ek;X!B=rne;a(=TSIBs>_{`)@E1exZla#h9t#IRk|WNm)RRBcol~ zGB=?(G&p~BOGF9nw1nU(vp(X_Sg+-S$jH6x=8YWzd#w92@=AX8#&axxEVC;OzJjRM zk#d{qMY|rO5EKtrhL}q?A{L&BgwpN8hI3i2djzb2BT1gP`AKRI;k6Fce<#R+_3jGc z6vlXl_apQ(z5s}6r-F8+ojd*Tb(OutrUF`c2n{F;PBJZkmVsspAR&w)i{m+H#gi@A z#zE-SWhZra6Ij+37F(*a4i#Kn7a^yjx(_-VNAi=I#VTXhXkl7&%yJVPzDsC&NbkG# z$FD|-FB3{;?AZ4~T>@tj>f+hp_4=sGL0r6g+5F7R(?N#!K_C3{$UvQ=l+ky!4wJ%o zGxUM0jQ`yQ5ZLu`GLqx*CJTbK*4g9rX<9bln!bIxI6pW6zo;w!_eG8Qe=q8DEV*%i z$j&40`Sf4v2z;AH?UX(4c`Vm{b_5>XoA7b%W&U|d>2&qAf529?{S-A#E z3d+4a$lcbz%ED2-tsSgKgTW};k;etNjr>gp+oeaG%W;BO{gEZ7&+#BnNWppn0p52v z=g;rR941Et&YkuSa0^L%SD=6a4`F|S1f-7;Id!o0c8@_{GdqehvvH+W7`at4Udex8 zLY5!%zWHy~I~E5dd_@?MpzSKlNe$9%)|3msMX-g+pT94eD0(n{iQxH`&(Ck-&~S}u z-vme7({o=maKHTewanLyapL;zb3v2L`2Hcd%)hiTzZ+M%lla!D-@xCl1BY+?-~w>4 z7Hhhyyq|8s2P~KyHt4-wGTX~|<}mn*DRJ*oPc70x<^_^X+(DH|6QhYK$Z!{0b7u5- zZk569Fu$0|Q7|&xG&QQf;UQK&^f%-Gu=Un$QGQ?8_nx9t6c8jN4Fn{m2NeM+1*A(- zB$bw!0Spu+r6m+ZK?&)eK@gA@gdt^+?rw&;FTcO%{^LH5=Pm5xy4GI%T%Yq?D-Hc# z#q+DAjcB&eptI{v%~;VJAdy)C5|3wQ2hnC$&^A8(VOzVHZD zRk$-$pIeYwelCQrWRWurBLFKY0r-%Osdh@3J;{l1gnvm6Maix0tA-yuQrJEJoRMwp z*=v|0Z$vmkQ~^Do#|dJIiX2AS3o2nj9LLco3|J*C+P1$in{7;;l_!iU0#;SC~weGGMWn+a^Uu6aui$L;Iv9-=kmm(5Y~70oI4alomrQU%(@5nGk2q-pRp?IpI&i zSxV%7%~MYEb)Wd{>aCf2iaLr^V}$cyW7nwHL=6Ey`6+bQvm;%)^3CAS_9fiH*wHSx zf~v_=1 zbCmh{A2O=KEC^4&PL}N~{FR^014VT zD4x_XDF0?r8q0$AaKO(pSuuLPkD*{m+PK2E|!w!iz7Sa{)dcmbRT7aRw0|r*^>WgNiX>S{G<46x7ln!E#`M4#l zw>XVS`q>70AX(@gZdLlub4cw$Buy3^ibBP_p2Bly^oAE(#LJqz!U;nC#1+fRLInt{ zDE>-+{tju9F@6wu9~ffqzQxdDFd5fu24VYtqg}G`w@k!Ssi;=?Mjx5<*B$TIFos7q z@1)Dih0m4o%DDx<>VXFCrtNX`S5~|C;`i4gvDOK*JvZrkouGapgJqEC?IS*sL6tL! zmzU{YU4+p(pM=#sTe@%q#WA0JbC{wR&O5Zw>UNEeLgNa^dJJJ=IWYSTkW7o-W@{(< z3BdU-W7p5=AgCsAiud7iXZzwO?~(^4`l=o}sP5<+Hm9G~0Ed$D!k0h6Nfv6iQ=2=+ zsT;@>dd?@A)AuIf*z&bJIuS(Q_Rn(nxF?9krJo2J6jK^p^KEy6bm*gmp}NI08IKOU zT=)?AEz!bm_^#)0FWCH2Cn>-WcJ&Zf$Zdu!UQ#$erHgp2vvHJ*8XofNO;2jTZOU|s z2f*=<7>*u7%Jfv-Yuc zA}D$MjTn0Wb|Y8igf7E%Z(CDLbI3!;sgs>Pi)veJpv{^N>Of;{Wi1;^T$CC}BKG0| zrtK2+4uT@CfqX5~UArhhAmiss7J|0^+L5VE+Im7)0+dT|ujz=Y)V17902PVVpb$KB zy)u+ZOBOvD?wJ97%c^+2P?ex)bI|N}vLTNkS4~-KpxEEi_JLE6f9yY+zR{$9XF#^D z{(df?4{zVjKDTkaakFtnphd; zF4epwnk5a~ATLin+2$>$#=pF5<6F==fnkMAOHO7Ib6fj9Pkj}<6t?N=I(&D(QOe9& zQUj^#1K9G{9XeHo0a~dx-^K;oG?Jsj;iN~ILx9%=@KBLyw!+d4hdsN+w)dI#@tq z{4imQ@V-8QzG#7ab3m$x^Ysi|(FTcO{*%0NyX?uj$CIY0s2Clq`5B zwPU%Mx_Hb`0y}pRiI7^*ml$=;zv-aOYbC60Y%DP2idLy*Gh)>te}O83>|AuvcJ+9O zr6VvH1SyJ&#*_#f&_N z%h=%^8`|&8V7Zpcb?CZc78MOE9QA=${L5S^4bl|Ay)Ya*JP&Qn(5Vzm4cRWx(256B znvQWqVPU||9jtS{1kiq^?Y7wIrv0F%ySTrWP=sL`=Or%ctQuBfihZdF#o|wIdOz^R z-r=RI2-Ow7{_aiA3+f!91gA@xYJtuURf9b>f5ZXVFZ?p7SrU+oONi3)EpW;IG$N<> zxPb%>mVGjnH|NPwh2U5hbC(9K>$sA5#lELK0cuDyWCqFUEUnf%KcUySfut^UmJ%<6 zO^ZS$exXx+JW|F!tmQ(Y`hREH#L^O6@#Aull`W#;VEpd!pN08sP4$S=qB^A3G1&D} zyt7MUa%{0M@b@8i;OphUlK^?Nd~asu8Q?PEldFh2`Dv`mi{9`=5uuOu$a$$>&G!3` z+AphdWdk&aPo4MeYxlYwVAz$4@PX~7afwUWROf+t|Tg8hq3kf_>0CyI)zQN4jh9Xj6DFs2|6_)4y zamWbITB`Zs^cS}DS((i6YpAu)4|Lp%iwl2ssTgCIBtDfsy!_@Ip+ z7qM7MUHqX@JwGgJvbx?QsYlh^-GmFZ>{gPUt`?_vJPvow6g|W(awM=&u)NI$bedSw z5+Od=h0tE`W_^NaClo@OqGeOc{Z%9kEUb_XQ3Ui$7d#6PR7N)hS|Q&3E3S;MIRhV! z5+?Tbn^d>?Dk#THDw^hh<@CQkyKx$8!Gs=Qz_IU(@iATRvsvrci&xT9jBSJCxBDb4 zjk_)oo?P%?8SBX#uT}qG*;wkLR730p#ZKp&W?%ZaeWF=qF1L7y1b?ZH~J~GCl*B+5iz=P9&VZ9Q+_D{p%eCw^_A*u90 zSycqftHdiwcEV?#(8E+H=$hA_u>;h44;5AtB8!u-X^ho{6(|*pTE_Q0q2M~ zMtvt`irPc}hfLd;Qzp8du#TL3_NiWt1}cTOjH<)A@)p3Oui>K`zfLfrMsw-q%vB*g zBNoXHrbM`fcnkYItbE)WmH4SYI3+68j!kqkk#G*JQK)`$uaq^%q>9u6+c?tD8FnuR zo(cCP^nIKrD(4)$3Xk&rysFCTyR@0-l5_>5mhJS9)@ch4fU0UQcFC-g&b`a;mkTkb z|KC^psp&=6x!T}Xy@xVxqHyx(i_kcjqHkV@ex8+t&KMzR=qb zp|3U|rSOa63zvH9Cf%8W#M%UB?4#5gZ{}pl^&W!|YZKne=7ZA=+|oHOj_!NC=;BYq ziG(VNMRrlAeh_=hnLZ`-`V}3T4O$(a0k+#%9e_BDzbvGe>o)|Hn|7FzC4Eg|!66r>?vO`gZm+yu#{NyS zKE=!QfUMeutfmw3Za<^4^Lpij>L)#0^qHr(g;uo84j>8bw~qNIE=9eGu!wEpP;;QL z3183sCSKmLcJieSoD3V9(w1R~#p!w~N^o+1`bh*G6C5riy_dvHsQ{TUq&y2Xk4fGu zge1kmn!n#Ujf*HUd1k&z2X%d>ah0%;rGEjv!idtnbpNLAGB=Ea60UuOb;m(I2zEOA;w~5!c!{KLo$yWCpbC7^>Rt=vi2=kN@A&Px+ z2aU{>6slSxHMq0r)9u;kZUgA8bsY}OXsNJRI;tQtq7-usy&G)LK?^+|2ng@4s5i*? zTv~b5)bb`Lx&%FWOD3~{f1bn(Bg z4nF(t>flu>zN|ffiLQHZSK0ldigwIkXA{H zM4#Wcz6qYHlAF<3sH%waV^L^eCyBc2 zAU>=scmES#kMhUZ@!vl26|G6+v->%E&*z$V9?-zih!k!sF(C)&UfXuQRS<&E45*sgeRw;&mfPt z;#{_-eV+w>mkM-s?-QF0E~!qn|75L3b?3kt8r?r>f&5}H#LDCSMq#=FSo65tauT}+ z)7s@*0G)E34h&7cxGlmD+2lnIRXU)ZswcFefL0Ym!`L#HoVeBRjer~r62>ZqLG#>H z2VS}MM&T0^XQO*(Oj9IMG5WpEl0|)Qe|}MMp00te{yDQxD#ny~ zws0<8o$5I74ze3EG73;a+OuUA`d+ zMPV^mB^aXhuVaD?Chw@h@AmIkv#m^U+i%(hYFCy8f6e`k)O4ZpZVeAe7*?@le_RMx zq{b7Rkt{#n5G>UxJ1g``N@|$(w&JwR_8`~y_TKGnM_jXc+^pzP215f-#~ z4Xk*BjjT`XJXjfB-H)94Y@rUf;R9)xV*pj~7%k-fC(Wroqp`7hinv8rEJ44Hf~k42d|3K7?&5UO?Rdiy8KE4veZK zI-_#TTC2V6YRMATi)(hJ!uM+`-5U}BT%E%2Y6O$i#!_NkG2RpG}j|OEC+9wcRka>aLWQpjRwn1YuP7Euy{K@n@$Atd-emVl!zctP98FqhB87FNJv32nx zla4%(Tl?GqrE#^6shFm@9Z{~Dvn>}oE+(L&dN80$Oxak0RhfYoobafV?zf^*1-j46{DgElH%Pv!6VK&4~cj@9DRRROq$FV zMj=59cKw>l330cEq;>vvoGJf0&IGq{S-jd`JJwKeP|*igq{qVw}fw@rw< z;QmKlV8OUhL2*-=Y|DfXlX-Oo`Mu=+a|Co=!cbp+;XD+THc^KHkyNn(dbvZnY72gbGh_y$W!m+zeFAUbLPgC+D4@$P`eqxpbO`N9oaC#iP z>Sf)R8Y`mZQJ(^a1@ja%S*T*xtT~FI&&~T$xO21*;a zThw1ojsKMQ1>Ty!bu9U{1n{Dfexx>ay!R)jcRekc6K@$}7w9Uz(W7GEZ^^PUMzNm1dS1serSt2|uyTs#=n6&T@Fgaz+pHiq5boEX311I7 zH(inxN@+yrM-D(|o4wnD0`2bzzk-3VQ|1ttl7~Vkp9PkX`G_{t=zdH?jXL*Bg#Veg zX!rJInst_lHyd`Iu@@7XP`@32?R_2fNtn|YbLKKQh;Q6ndJ=((p}?oLG~eW&&rmV; z!@de3X}mBkjga_&Yy9t=gjx7#5upITHF`dcvYZ2Z!?B>3JWt&bZMjWz^O7NV8{6t{ zi#=A5OFjS3Ec%(kl~5))5OxJdYJpKR92-F-5nqvdxJ& z*!b(u!m$b`^PU|}2r9@LH5af&=3@g@g^@5U5})bRBc$|w|BR2Gl3H7a(nhG18GL?j z-T?h7xbLd+t&wV{d=Gej?x855)V=>+hE{!!kKxPkU$w~x za~uv8;h4*p)n8vI0iK$u&yanJ4~BFJr{LwU#2uGB-s3qM-b;Szac=K#2b=ThmtvtD zT|48R^eX?Gze)ZVJg}S?R!=eLA9X9-7Wc6G$onVSogycAA!X5y73*^TGY6(B z%$o3|(B)9=V4E8DtTW zx=--p_xHZ*xr$QFc`L;1ezFnD zY{-HoEc`JvuB`1*sTG}(50}=_Dn_#SZCX-elaH`e+bKVBo(&B8%d0`8ouNRDAVQE$ z`}7YA@Z}aAHUHt=RtOz3aR=Ck2%n2$Pv)2F|P>9aXIO zUml|u;@=|I1YXRw{O%Dcly$(BJnXoq+xNt|J)0KPz}MCL0tBOq@=pBM3`LmPKXr~MsAe)Ft8aa(wYUH}7tnKhNg&W%x(fhscKJ33!a*nj|*@+9+bSHE6`++x7GtE*{0*GZ%fF z`hlThgSHKL(N%R7^REO+4)|SN(6hbL8*=EaPkl3$q1fdL5K(PNW{5GRFW#z65)&>b zTD|8}81Y-WJ;HEybA61>x<*%$T-hicH2#@ko8=OnRUG9{sA*m2cwht}17sPO*5E1V z0gy-Gc|>RM;R{1&>41*UMy7#~<@o+;eyNKLhb7A+bV{1S&|lPGYa-ex@M(E85%Wv38H4L3z_&Hls+y!aJI| z%beD~-dT?=c<4eajr>`vVBU?EDyn(%z?Khq3U>FAQ~JQ>MX;gZaoE!^HOLjRzNbz> z&qr4B6q7y0qjEb_iQlq0%(5ZQrY@QF}79Q8<9md{M5PcjssA+aWv3@TT z!790|At|fv^J7kHN5s}T4ja|bFB7X*Nr!PG83Q;aQi8F^ug+jSP*uks#Pv^YLXYe~-mnk`p0P*el*ZZBwB|lkUsiW98M3txey31nB2x{DuYP)rlngO92AB9h(t`GDwS`gd z;5@(Rv6L4}e#kK@@2`oBV62e%yqKEh%PhkB1JZS5WjQK$xxyC=5$UiLU=@DttSPsq zs#Bs{|3TkPcUsf(DL!FmuF*{bR(fQ?=1S9S&C*aZUWF_&13O+&1bxPHWFA<^Pf^@r z>OEJi_#e=Mu$ifB+AALi z4cdbSadQ6+;yiyR(^8zf<~h?+`Ns(Eeuk-ai=;ww?&I)X-@gW0ME>1F)=|O;-^NvP z_X{4G8i8I^;`%qa*7kYF>mzLMO?db-dSFS^!?CF3?(5GNem;05;~k9O9c`ra`?U~6 z-I~Fp_o%R?r6vXX>1<*#<<0bX`>;;LnUlajNc=|qlUNVE4DA;=_xpt&dp5%RPd^&@ zNoF=VHBOUR$6!u0?Ot*6iPoH%zI$?s7j)BS#!J~jU!h^{dW8{8W~O)BdYe24tBV2= zVTT_h=$TN+r~CZoY@#`fhOw`C*cG1Fnfeh&GXs`Za1XZ|eu)b$H3{|6r57#zPbkx< zU3GgddL#t(11|JL$GT-T76m4HBH^mQlnz@b0-{#`q#siqCv7~y;$0~-1EWG@VVC=ZP8E?q z`M5k*t@YLXBA28`W0uZ&>XM8aq=--Q@jG)_9*C_B-%#do?D8C_caF*(-f-%{Mxkw1 z{Lp0b`x|z%WC=-(gE$VE2&WFtq7qLO5-+15mQww$+vjW42AZ(?PJP(UhtgBM3s+V;?tCRnd&2zT7O)aH1iHm8 z@ahfAr>Bh+JV#xnD8E1n$3|=7XD5a6NI#03*OF359avPNBs1j30p!#$u=xmDeQx#| zfyF_m#T-+4JUA7V-84Ye(Bxy25!zm)J~q)OrVfzi#@$&rE$s#JvM(K} zL8~t`RKXAyvtb&q6e#L&^9#1nDO1Pn!;&$aw@7PCI4tOF3cBm$+3xO*b=>%UR>lMr zetya+&c!N_H2tE|ZS-r(Tn1=c6eP|-dQ39(0|jU;`v&X^>S_mKz?4o>tv%pKrx%>Q8;n>Pz8wpAxCx2A>iE85COBS;aUk;p<_FUz%x~-X(sw z12F__omm}@m+G0fm{tDkkz$I|Icuu4O8l$0&(4sYCfhoTQ41jY?n4t4+d6Puzt$!Db5>!s*9;Ipr?rI& zICz&{Ska0du!cOFl1yTaJH-_34D{0%%?G)Xc1Q5nD*{xNjja9YE8Pem$dv_ z5{J3^9e42ukH=Xo5^KV309Gf?xh{VZ0WFzQPUK{aGWY&*8>1BONMUu7=+MQ_!06q-mb3- zzeC#r@lznQp;HijW+zkhr0TFd#w%#52cvuRaM2*q!Mk5@C6alG@IAO2dkxIv(x*LP z9Tl5fZ?Ej0cQaqP-#B(r{olQY?Y^qX3fSPlfpiW}A9r>c(!5M`SrMRe%H$l-rc5rU zMUO&^>>guCjBhFP80#W;u++Z0LiMS)fv6Jjj+T}3qd$Zanr^EU?Uux(89vuK@PNZS z9T||=f(aFJ-SA+nDaayYM(;O495Q&q%uI|qVRVS7Jr2U7Gw5tqR!nN%{(f{3IP6`U z3>PN6K4?A0y_!+9eq$2uS&@-BUjYg?0=`Y_fmFNWl$OtdmffG ztxx6G^Ju(UmXve6pjLHmnlj5k0_85;E!zY@n7cXY% z@R^IG8JnB7>0IL1guq)k@y7aV0XglQw_lu9cF*T^0GG>~loM2Z1AD_5Ze*%hP^@H1gti`(r!KyRP=WEaCrfqm$dnK~#L2^TWn zOp-}@4`D+1cOj>m;Wl1*hSY$LVt=h)H}d%&oL4`W8pw)00CS~&^q>{hrlfP{p zuQqhrVpZ5a9>iC%^x9w_T)|!h=bwFwYWobgtsz%s|H(L9_Tu?9A1r1u>Q>dzZ>uDyYv&TN>}Mr+sIfzYVAc13fsa|%-gb8AfS*q@a~-1vv#`gRG> zVRSve_=LP;{f!;a^|p=ysX<_CggtZ{Z5vq&Qg?=4T-pBJ+ve(Y^SL^yY||6?o&)*t z6fbVP9Ak*WR)e%nH564u(5*kj8LA3 z!LD$vZ|nl=)@vo&+(#WFYnd4Cd!q0J^>eE7suc7oa>{EHVsGeqsn^+lP~5SI|3POn z4RXO4L#U4f$+72Ql2yIN3snSJ(xvfxkMnjlW#PbsLVmd{ek$!7GmvNmmFjG0%#!W_TIz#wc6*Dk{$;^Ke^~`gV8k5riwi15{`&MexbiRWFP(D_Eo8 zJv@w@VQ20I3g|UUuQ$KoU`^2ZHTV{_Sm`cP-5?+q?tMoLYhAwFhESY4Uz&F)T46v< z#C-_^cS85|p-r`jb<8DjjuVa^bDn0~=p?I%u>k?V{em5=e3uI(erv^NX2~S61Al8h z+nCPNrV_4rhd<=oQffS#5BLi@b9Q`YFy&stXDNFk7K+cOx(4qb%(YiUbr)ncy}g*T z62Yw6Zrlw~MLuWO3AaeSRk|->#rmm;4|a@k9I%`+UX?sfY?oGVE*-R`5pN${ApmT+d6gJb z{*FC-C9^N1fQPfIxQY^O6N8<+jE>Fok$6QQ>*S~b#3wNKn9#H2o}-f~zdvLirNRx_ z5T^Z7T=ezSp6+7FX!Vt;=q9zg)bYmfDh~f!Eq2}8ucLCZq_Q2u{Vq_*bRD#1zbTW5 z|81N=YKr2rzmQz86s6b*dG3F&S%2-{+3wd|Dzvm?1>5O)niHRBSwF+~Yp~cM?m@eY z;D_?&%~rGqv`#|mkMCq3VeR)kmk!dW@vQH69|7v)|4iJ^Ioin91rmm@)=K^%JYdB! z@WElkX!p1w0i*O}`hb7L(5n0};m0jVE2ui7j8NG++sA>wvNi|F4xhWIz@~A?M&!#Q zzTGR46E%1T=^(KTc9{`T3wdLq?TqzFAM$MV|bjdDMD zSofhsoY#ffnAT0mc$LnovY?x+i>rcHvU7~gg;p{ulB(f(6xN{(nSIlPb6eLUZ=v}>$CWo@fRpY>*__N}9hDe92s zbbsdUt-Iupjo=0jGcJ9#&F1YHEI++C0(6N0*&;s9PnjWRQ~NmOAiAdqgpUV950c4i zP8&h~Xa9XrT~Lg1x$D+;XXPT)HD>ky*GwlQ^z3bx_Lyz|MUd5t(t=jiCYCa6gvJ8C zKmFhxa+L>jejqdXQ1AGSDqw^U^{zmnFu{og9Rt0+zZR5xP%w{a`(_)^QhjV?S%S2%-0h$jR+{?;_%kCFF zf{?1uwqPCF6-!CnWBM7y8)NK$&VSdmkLqODl$$Rrkj9s+lE%kmGtwXk`}%RrXmBsq zD8r4k3Jm8i&zd9>d0KTTBl=?y>G$_=^d>O&;1l{#(ta|q6<1BXbmtbs`UZS)(z4X& z*N2F*cvC>c)%WYF6il{J@}%aZ^s~Q(=^Xn&;c%ilULCU=~4@n8yM|1 zklY?K`G#4b>$JEGQ#4#7a5{F`PXbQ3e5H$6kI6bEh6Ej(;d`Q%OfH@7{cQ;YI(@9P zfSoh@9MqX-aU(Y0ySri=qM%i=+Z6sSH^s9hFX!5^gbeNFLXI`co*ibU2}CraM@5gL zQE_yq80NWj-y*t0oGHu2+70&QOVBqfs}+YUvTqdAYHr}AXZ6s>uU>SGU9Ik$_OT4| z{nALrp_okNfd?NYJDx36WqgB>Lp{B8f7mSIQ}I%lZ23335ty9P!;vzQ-E2>0VM$JVW(U&jWw zOJ@bJatk6IPdIHPg~)v?}w%ZA8I4uk#dGcR!xdsm53PoJG=5 z$jTZNS@NE#>s&N~d?M@oJK3r9TLsLeMoylA3R^E%)gcfpY7)RnT)brdH3@2{JA4wH z@@3CvyfJ@vjYVebC&k!S&)rn6kn|b!lKZ$urDS(R=r&(J#!?8-~2gIf2)JqiTK~dlbaXZib)ZLk>u;k+xQrC4K zaiSel18bJ%!2R7zH}#!fP)p@HUv?hPUDUE#vA-z$M&#aj029yDVQT6F6W7^Py5psc z_ab=Q?T~@C*c_-o+L$N5(gr2t!KSHbQ*r!-5YV9Y)C6|-9Jq!%8u$BoLeE*o3P`*m zdbJr!6H2=Wb3Fr!jL9e2;^IG0SLHv;U4PI=*`2iaPm7kI_lH(q7_M+`a()i6vi7& zXj&hMSDK9sJb&8q8d+y5;dJLxMnO)A{fq(^M$tL2nOPr>o~sl?oYc)=3k%x?!GwbX z6mLc8yP{7)ZVF_lfzj?&Jlq+hWV=#hk?pbFfhRm$Tt{M^OAeQy;aMRpZO859MA%ve z0e?RaCbhPfTfIdyzJt?CUSBCJFiSsK)e&5*G3Bzmix z|H|Y<(}(fk?q!v`H29~=WIksI_2<0Sch~XCRmT>(l81BM6B(7>3s+LBqZ?IX3=Fy0 zd6}x!{uia2N1+J2lZ%+E4IXlVC@hq@&*lv+FsAVEx0>CTFSz5Demzdr%FfP^p#dm}}$~tcZ%Gs~^$GF1! zblFNl5f{RgmK(D-?hzbtu&FNnq!nNuSmekn!faB@PHO&Nik^l(tKU^D-1jY^h+FEx zF)I7kIB64#NEw2N6pnDQ>ZgXFH{s)6(~op^-rk$QwPsDZgf=M3E%b^0DKzS|n+3(4 z){3jJK}5=dKKf}UaeMIf&%I+_%cfL!xJNQ~E z>-TTH@twNSF0+utO$&$n^$K{4V7Q`i4L|k1x|}@dwARx%xHOOp_1`lUR+meEp6OAN-CIkOS?D`X(Z&SP z;HAXqtO+hz$bUut6gzSPRRmL~-Y0SijUF4FZ=Hz0hTb*ki94c)ShTn0c_J*}u-7NbQ* z9DUyB3O+Vf#O7QDAwiv2?e*9G817K^zGIj0vJZ}U`rVg(^di$WNoWy$a_)k9hTH6U zh07_0kGY?o#*!vejJf33#gtWQCN$ee%wXO~mz4aw*%yG=tK%MxP^1w|q5~kwYWATw zWd_F?H{^Y)g^6{sS`H$Fcq*C?}{zI0&Lj8h(QA!ac zSxRu=6XgF24unqDnST)#O_%kUH1AO&Ji#PCzxa|IJkhTw%yk|vu?8i_p8OaONlCz( zIBr2^A}&3}Um~jDuF9qBp*W$Cqy-=PRmV$I*sqP=gPZ%1B3Gg9F=XYkPkj2u)ch?B zhW~nI=y)Cxxx4n%u}d*IVnOzJ(iRpY4D;N|(X@Y@sa)pwFMD314x0`}FzGMG@k;A;?T206K4B*eoGKv``>i~g?;(*}@Lwwx@~CvF zbP}4FL6ZOTe}#!-iWmqzhCjfXwL~0hzA6iCx0t~C?0$VU)oUQnrdj%8Gd-ZU-lA!# zhO9W7BN8h@&$~vGGy)fW{SYNmtb~GO%e9;9?-e0pUiWC!i^S(Cj!AKDV

?!HU{BEDqhk=P1tVm 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

ZXNPcgw6ar5c}nOPHY&^O62&!Mvt z66l8%U@lE1;+Zf=2CGcNS71ADv1bkW2;&lpsY(mf_Q70nmEg`vSM=X{AJZ4}TrGXa zZ1rt`0^bYG0@NFeWt_<4QZ4B``oxb)k$#VW)pMh2msbFaT>Mu#>HZVl-xJbn)^b88 z)y}1q#Bc)cLcPj~GjnvyO9k_u_XN9XP)LP^*I|s?ADtabWj?HK<+^O{1h!4i+2eCL@K{Bgg*mV$o4G{WsX)dLMhxa_MV`1(=Fgzt7W0^NuAoQvBMvbf}) zy6FhZ?)!DF0@)5_U_GOaF9D{|!O2!O_&kJA)Z$8YXhw!iB@!dk{W2Y$@89ify`qXx zn5`F_;;AS8ie+C*%DdgNuD}K69+q|u&#)b~F?x=Dr?^Wf-wT?MSN1$);8(~JBcXfS zt1jB};l>gfGCoDmd5rc#tgx(sK(Byvo75L>+p{&ZJ&mzF*o1#yENt?E6Cs=U#j9wJ z)?a?J^;L`*K6uCxOxA36iL^lWzKM#~S}bjatfgJvM@%ZUK>hq&?!B=rLCZl1K^v}u zG~eYoP*>Gsv%^pD{*cNfA(+QxB7gAtMgx~g!zEATjg?1u_DfU8Pw%5&2M}D5-6SP7 z!Wv{7cWHJs^^Hh9#4;AEktQJ{tr8J&_9cT!BqMgq9#GMFLc$41G_vm&^P{}4EfZxZ zV0LHo-Y7)MY#^cgaI)$EjNEQqx)fvOheCB&_-bASm$x)bniQ1L`oiMoov|%z0Duj~ zkpn+N$Xjl3llBV(ZHZ3|aWy7U3MK=J(cEaI?=3xmGzO#k)taj@`Q)uu zLv=MD7RFN`JZMXzrPG4$L6*9f@vcn5Uqhb$_jjIJQ5lc49Tk82>sDmehhKjYadd{h zr-}OX@~s%7LjII%v7>ag{ZNjYDrSes;dwDq;iUKch@T>8?H#m6ukfvdg;SZ~E4sJ& z#y?be9}lfhj{kFJMXoJ*zhW*4FEO87g71c=a<=1h9cz@y+kPFBQC7b!m7LnmhnM+% zT}=KN!CWh_Y+-&|Upsw$V!nViEV#w7ZP0gLF7;rWMO5J4N-*U9Yy@-7p%j}}7g&)u z@)aZpC<4bC~l_}@Pk4nRd9CcB}K@?lIAF)~?Yz?6`%W8n+)Z%4AdQ59&|VAtA|0)57z8{(?5}IF}QRIEGCh?Cjg4r(xP$jWGt+<>T-WL75q) zx&DSR$6rPI@G_B+r3pJ_nn|~mNtFnUvz6PK`%;)0&I)zq4+ASRDiGYiWsZK18pTfH zNx*5wMiE~^m$StLG>#NHy~i@VqO55BDF>c3zA*|ahju|5oe;xD`@g6RM$3spuO&?3CB^%9~KhDbM{sxAn;lPzR&W7h`nEJC`@+xF34H zgv{fDy|hi zZ~PP|2ZqA}w>i^#NPp?izK(!pyF#i2fQtGo&FR5S&JFy=*>O;zBu|b>CJ2!JiOcff z``M^gpP*k_ve)yTbDI(|^RMD)-Ic(=pU!t|A!z$r1C@1Q$Oh-m8^aX0omUbuT&14v zI4sSM$!202$}Nbtu4^16#4LyZK#qF%-3b{t!ngv=EJhZ7TlG=Du3^9{BQR|7UXLvz zA&zdYwBiK8UZfj~nQU3X`xFC8tZ=jl!9R#w{NJSGqwQGYnKcfm^3~UistKYmh`LWj z&9{aFeC*E;*O5$wD1#RB$uavReps+SlxXDc)8G{x<%lxy^6GJF+fF6SPUi*~e=z|0Y+*r?|6PvF#%AoM z^C(y&Vvb~_S>G}7Mj)Md{HF<<>B^*Tg$U&r#IEvY;56Oy2-$i2a@rL9(?>V(<1d_y z^O&WOMEzh)A)OB^E{waVqLGa~FJ-qFg!BMr~?R#vnNkoO0ynUg9 zA{AJi81@(V$c%iQaPlOoOsgPuu^!r=V`2Xa(k84k=szfs{E4NCJ!|&t@@IL$wP|B; z0as3kq=wu=``L8K!D}FO3V~^pv|k%}ARL6U)_Fpiidy4h zyU~K`1ih@e$P7Q+=d*rx?oa9R#gwLA_@aiJUv2+4qSms zDSC+OovAknS>a5N*kRmKj8SWyWoUz)p+Ikj#sU&zEH1<@@}9Y|o?6u4^SiY>W*?=M zYzNa=OIP&T?RBwD(tdIaNp$&1fr1tzCJ*Ia#Q&;0Y#ZuHpX}7|QQ1!WYh>)+l*I|= z9JY&wVe`S(bvE?-rVqr^1$Qrpp&lsH;_8F3gt!n`i2bHzNu!wGxXS$4sKv1q8<2sl z!dvV34Sk1aGuTCT)=o~PBcD+lyVwde(i7khP;15b(3>|xjM4os*g3{th?^B8DDjt43x-vb|O&aS1(tQ zo>oS2+P1kC`1i|mBh(MW+OT%a?Ms2uuiXl!z|i8-1&YjILNCKnl&4>mZlK!bAS*L6 zau737(0`OJkxbC`19lw((gzGU#4Mzg^v{O6sQJM)rn4l%vh3T%g9uF76*076^Mm)M zD=&oQu*G0)T5+~p{XW?tI9E1f7_A&0p`d(5UX-a*`CttN=d=}+;Vn2m_ypT_pJJ=X_SntKkw zD-acM`#COafaQt{Egsxyklqt^%GxfPw9ey_^*Myc+9(tf4;CJuoLDb^X?WsFoS;12 z(8#JNEwtdTVG7e8k}2w)PtBu=@yhTPDaH?nEkE_bj^hUokSk7|q(&av@Xes^|K#2GMvx^4>yJ z-@<&u@IYX9BLX#ZtDqWB%z#wZ7ZQh|B42TWpf_N_Nx{=iRDUyVJHVF#-`vKK{aA`R zm?5JtvgZu?L_yzm+q6BzIb+J~gf(GEqvbNPhLHmkm!^JodJ~S(`6{(i3wbLb4Sqio zxqLlQlBtDJKQg_QO^6(dzHeC+fes6@{$ds0Hvp@CZs}q_({qV|)}b00P7-|uM0Tbj zKgx4?5b6<-sINTnT_OoWo%zK4Inc2=l zb9lG6`$tsE(IH!ZCJ6A1|kq-W0)^A0Yzp^*qc5#hTf~zEK?ry*Mjz+#AT%(~_4hJ};c<-z-j6p3i?#gH!e6 z&+Fg+SO`==MEi=1xZuho)#eFzx(j21KzptI5hme1e)blv{hB(Gqf^8{NxsXxhy&n( z9^*y<-7k-L9w&5Q?dYeGN*YM4&C6!e-AAl7mmwAgF6_>$@Vo5UfVoB~CV5aGJBdLb z=h0wgiZ$d*z<(8I%K<_(!CHtV*7D;ReAM6}>Q~5zXGz^_ZCcr8*neOsy080pYM(cF zkZnR|Cku_A&7g-GL}mn4IHqdT9Q(}&mTs)q-2M|eG?a3Hblv8`~0aO!zZ)b9**UX?H!IfAT+v+-`p7sQ_ldevCFsqQD0ww zbud-$*uCBEddo3nDW)R&@;Z`Uch;@R;HYBXR7R61A=IKV zToJv|sZ}w)k$9x{0lu64jJPHIoVoN1_ObOVpc7IvRMP=XpB>#Rt$wEIopoA;r&eNx&P`Tfc;Eb@;%ydEQu4-mVo&^;M52^Vq zLeTxn;&KbbZl*gVLYdg-j{WHN+nU^ap!Gyi%2y-wxWK`t#|P^TBl9!22XCdT3N9DP zub!oz*Rl$^zS{g+1=bjD%FfG2f%km9<@wjw%y8V7rXJC@!3Yr3`HuCltnD9Q$Ld~t zS39Zn_?Jg4wro^P2#%r=P&zF zkp?mJ?}cIR9=64$tp_ekpI&GkDPAgvnCR^^hUT}z;h~}5H95OSxG~R2wElB>j20q2)~(!A)$viq z8W12t6O1)KOt%9*|D6 zY5G=?6ZKc}UN#}98J0(J=)3MP-&{yRtNNFVe^mx|G_Chop3oqzVcHcYg8Evw4XYoz zRzrT+e`_^EctqOEG(nFZgd22|o&Kr5Y}`^HUn3FgoHG#a14g$R)B1AWLU^+oQ0D{0 z603KFo-om?KAiQMb}4D~@}B8Z)(#^6&hs5N$HiK*!1VuI?a2oxJv_>cuqv7GYik@F z3a0BmK8}mQvPeFo78%8BKNqPIOs|vf>LxrhXx9h-_haQRtwcMw5flyo`ZEf(HQ9$S zf3v8wFfJ}&VuN1GHgjwJmIXuCP{$xC@76n&Vf5N4$}NUp+$6*JgCMUb4IVbe zo(#qECj{pI`ROAzBJCuh>^tSxS&*1zXN)KM$F)IyU7dEB5EM5~f<3;@f(&ic$WxZl z?WS#MZT&sxJRRH-U`VQzUN_Wn#>DP*(OwK2c%)HCpOf0L^%@uTJ#YF}r6?R@7N23eysk9_d8`Uf@&4^@y3n*1opz^eB2!@B1| z2Qb%m@3+DKGhryAL7i>LK-OXL?b(#z2g<{aDxomfh7&*y)MVB~trY zyNurUYMe$Vhdo#7i;q0k8dJKw#&b>`6sVsJ4N#_vWWox0R>IZq^KvBN*)$9Dhldp} zOOZia)oPN~F14$okHyYiizk1lFa6_O@0MvAt3YJBDo2K|TNYFgfNYYMn0tax%rxY5 zfI~2qptQXl&BRez5Zr;=JNodn-L{X)kg0W;NWAsK{y}Iu{P1#yap}#^H~5r4Sd=eb z+TAgQ1&h_lj%V+r4}29VQbnc}&~P7{d5e78-x;xbbOxj+*54_#pr>CUc9oj<>j(VW zW^f2$yZbYT78Og2jUBHo_!7S%YfJ;mMo4g$#L*MApl7jxpPPXzypz6>LE>qT8n?k* z7TGh%w(vPAX@m5j!6_poaYZYlL~ziMaV&nqL&>EO4*jAEVx;eWbmB%Vv-@|8f~b)gvSDvDkXf4*(!QDMEcdBFtb82m*tqJF-HoI;!5!FZj;V*A zUhV{5?sfAV9KLZQhII)2mv-s%4{Y7Y9@SkYl*a;Z83`XZ8EPIxABJ!TDV04i3BOmq zZ=s69D1L0uKwQ(>BlFd{%df4!h+ywc%Zd3CcEcEauRD8RFX-rXq-XJLwGCvw8$&g@ z48Qo2m&xa*lMn`p>vvXZmho^VmFRCq5n7s@eOOlLg}wg>NH?hB?`G5cr9mkJnu|NK zWGp<;)*tSdfDJ4N`##u04IE-;Mw697z1t?^)P;%U<+}D)=mv^Cim@g8MbPk>k>j{4 z+-A~&iTLoi&*~rUaQAc-9X2@tS&tC{CcqlQ4`28;%I(GwbNShN$msuEYS~UsEW(c; zAT>AoHHHjvxw;qhoxkH~_FRa0E=)&uvRi-bBx6%|A_*%qtVu)RvPU_gxtRw$Ti{v$ zaK0Mk*RVjjY^Ut{f$!ZX(5tc6sb67wrlgEHt>Jrn?5qG&;CaPD^wt~#3M~(hH5`A< zjR4RtaMEf|6MO_cI+~rdFD=36qje4=9hyA$#9IQ2VU-2R02d046BS9>A*pX{H1NVm z%Xxsgg-3j>iWPbZt2!w9EY-WEa1I{LKau(zX%-a)bBZ0sf!QYnK8Y$QVz}*Hl5Ooc zBvWGstGCFA;ymTokEzpaSiLXDFpCcJLuaAQ`#&dEYrw7%-r6{#=K5p$KR;eeqedED zG9KZ5%j60{LJ*L?iH?Td1To)kSUHb_f@?b?I?(LusB;_VS{;WMpP@4aphfUN1854P zFa!&7t)}-TakE_(#i3H{9F?wBfLGKlM8zH{i1Lf4m6gg7pVSZiz1aCydW0F(xjt1@ z8Go&DV`0{>^!fv)SR5m~aC^1ZX7T<5lez#40@=d8sZY4bOFCmw{~;A4klghETGr`7 zQ5PL^LlAQlgnBc&(!#zlK*!5Mv_?~eBg^8#O4LRwgbIQeoHL%_tW?|?C1DNV9!B2= zaq1YhctSLZ;m5<1oqHQHR9*ZG1qq-oqO!rI^zW?7P=FRwB0bNO>6giNLFvCD)uJ0z z6Wk=v_4TyruVU(sHIu?ebieQ^7rmM|{R0D+&k#`>Bt1kPQdK5iYN)_CAP%KWS}upZ zqN2-^lWH`GaK~TM=!RA(HR426(6Yb*+Y;;a9p~UH_+s`D#7TcEi7P7%cR+hgj@t!F zggkpc&9>}J!i;kzerop^H3Ihip2FP6jU4|hJ;sZNun*2$->n^;hoLjL4H{$56N^8j zS7tJy=_hPC?55@)ob)}gdHZpI>U{U~hHmYnw{mmpEh0~c?6}*Xtqh7}5(z_Du1oJ| zwS+Ws!VcAagcB~WB|_yxF%-sgM}Ot%sE_?s&s%Kj$_1@qRuzSn>D?q^OL5SHxL3$Y z&(4&6S(eK;F*SAG2$85v)|-ee(iu-#oB8o{1=4k(HDQQ379)dQmcir4Xz6@d%;Zk1 zJqW`;t!~-j$8`5iZj300U&pvPI-aWt57E*X3xu2>;zC1;P{CLjE24+U5_P#HUqen^ z`N9;B`)=a(lkOMiIjl1C)Pa&aydEX0j%b6@S8f^QH1pfE_uR1nK50!&Y>U;BCBC~8 z-3*Kc21Z`hI@FNpKbLDKK5U*<;sPvDt1Z;_YH-<;iKLcBBhYj84sWne|1kG~chfob zJ*-;OY%=^g4Jy1}R~ZIRH|$I<4m}vjh@ORHAS`cz2867H9Js@Xhnk-aZQXDt!GHUg zcKKV(;_v!&=^qVeajBb}e6|X<3QNKK^*EQFH`dRqG6)d>PeGDLmN>h6Z~OIK_(4Ij zH*(?H?V}qA3tl2Dq(UPnP%4-Ntl=xNc=6Mqv9r|3QM4C~`RA=C^m+@&+0+qprvO%1 zni9nm6fNMhjj`KNTi7o79%S%0PK=R-g@q9xU$qA-F&UxWBXLPPB}Yn5yyuMni>ddHhU<;G$M1|8ZHN-RmqhdyJ<1>nf)61= zbkSQ7Ey|ctqC`z4L<=DjC2B;Q5fVZeL>ER2qW3yxemC#;eb@T^XD!ySJkLG%oW1wi zI|Hc$b;+aA?SAt+f9Da>=F=SbsjjaE%hP8Q2ZQwd7xs z^v(utuAlpb;gEcOR|S731~6vZ%v{;Bob8|zF~KoFpdy5yO1`Ez7-!wU7Bk6C8sG%B zcQZdeEt0TWJO?GHAsZeo&!kLbH7HS8QcnIaMK1X?e|YvvELr-F$pi+O;tHHN4+Uy4 zew%iHQ+SkoU$#F(8o(o)?KK!e!a^MqkjMP2Mn`(r8iTzE?onljWUEguI+Lz;@T(Iq zoCE|{o@HHHtX%2?WI+eXJ-u{3RO|Iwe36rhZVXn2LqL*cOyhK=sGb9+D$MYXYcoAi zIIr`1mdXG@H3PtqG-)_})q=@_3w{?>vzdy=*YmzSm%yv1|D05q%iR*wd6cr9kpHq@_dHjnhPa|%mEK88*C(@%>v zvh{a$8M&I(rH(%|3#YM5Eb)mZc&wEMhzJflr^YWz@XbAmBFvFSr7`4er(Tmc)#)ld5@ab(Hbt~t3rbM;kYwDZ3qhCK&{^HC0=*tbrb~z!om6C0dadtB-10Zz#;R*vVfW7!A zjEg@8;CeK0pzqKSqB_=c1P4t0(|<@QQG_mCzr)2`XJ_&4#nXXT+U80E15-L$Cp;31 zIf2fv-r>>SvbqwNPt%M)zqhOE^_;BC6FDy|6Y`KMOtva~_jHsS*xvgj5^2%k#}N^w zQ3*)=^XEe;knkE_X`|FtjoIS3~l6GV%vr6!`dxsk5kxqvzC%f4s_Zimw@R*k<4G!>zP3(*tQ?Epcm{Uz2la>LA>zM zemi^?YQ=oa9Gze$dSEGa%cJ#{ImNU8nz75HC)SGlLq)^eScGEFSrB{$pfhb4z&E`E zqBq_7S`DB3#Q@TU+1PK^fQQo<%lw9>E4h*q@{_wLJySC;D+XH<2m!MHGh{8vsI1^p zFbS)9cNxQ5__JKgkH-yd>;0oLc7Lg>A1IIlg4BUS-}cAs9|oNz_zW3Ho1v@k)XEbt zt!eo2EYHV(35dU>Z997pQ(G~S_)im4g~Ql z*z&^yTE=ol60y7=C(GXL+W(~f#2|nyWeWYkCtDy{sAXcl$xJk!3Gm16rGv)xsOFw*42;}h2iPLPKDKc- z1~Ht~A>KAt(^(? z+?!n3LOFrH50HHh_cS=hSG%CA^IO)k#hWX~p$nxHzyNC3^RKZZM(5Z%QbGL+Y9c#$ zMaixJc}TR>;sOR7F`Oq3-ehk8ce4CZZbAIfsPNJ6BKyd+Tfb(S6H9W_(p$wwYoj|~ zy&N)J(NSO*YQ6F-pa^M=kb4`ySdsfGZ|> zk-Nt`3x55F+6gDvlZcfl->n~IUBTGXsrCfE@D(58#-8yDD=+^_>QTBn4&9hP+4|uB zG>>BFssZ6h;XojuK#oV?Egv&*Vy0w%{B-65N4I;@*J82I?Uc0<*>o<&RUO!Q5oOz9 z%^S7gRk(9kTC3W%6*;aj3yvRWIbe>x&$W{j-CPC$`N#xQuaLQ)>wF?FvzaiE74~wp zC+vAAg(K>XL)9d0e~z<5#m#f5HbmF^$z+9>8*rp*AU?~WfWS<)SD$|Jh*mSn!hd%M zfThkR@Zui0SlnT+_6*LD_-W$_81pT^ltU{k?xzIZfc3#tcI!@?@(aI7)C2x-$QyD^ zW?6MYpcnV7WMK#N;lVMaf^+mZSzy~u?N-lr5-TnS)6(Oo3MmR8Gp_y|iaYC47g zh=e$EY7nH$3T)0tH%l8(lWt4+;SVPyfBCgumph@=@~?{K%Ez;CcMW zcL|TBp=^vp(w>HBus{Rh+AuPAJf8Y^HZ=Kq3Jb;$D!Vs^;Sd)1V@Kc#;X^xw26a@_P#8@ZaE0M5fZrQTd9R5hQm%65}CK zRCSXAD0lEC`64hU%ekc&xbdIZZb^~Ow?+{7LxpY6VS^k7Jd}!J%~PhK{6(8&lNDsz zRyfRC>TaUDhM9KT?YfFb8lYd(Ltax}o2Y^i>;;l*=ch>at&8o?jM&+mc(Zkfa%YOm zl!4j$PvYh9!p_=AzdqOwv{LHF`vtObb-RM!3MTnut7pK5jKhK`Ax_Vez$ zeo1hk77)NV&WY))M@v8iB#-VOVR<@HGb4mP@yjiOrslVkPi3kYN~FUJy|mCJT!*X} zpk1Q@@s`UOn&m#uQ4vKab#|rjGd%Jao>1ArPJuhz2_5-G2%$8Z`RS*P*Q*TU4h`7}Xna6GVNYsUt0b~=?p+9 zaezZ^%DxuMSFOX2S)Dc0+z68SMmRlim2fTL;FE!MzJXCE6;$6Y6hOil+IW|rNPdYo zInMMr_2ox<8Ua=cr2H#+kKQf~tz`L^HOG5bY?o^b2*BvzWA}vgc|1Zc#Dy-nEt_(Y z9hWejM6@^uvOS}&GFfIrz5S`H_*{d;Lw0Q_ZMLND#P_=MCBh46XkYHNqvwy3gjpm0 z=wF6F0Kf;H%bQ`U5c&yuEOehSlAtU3Ehe>(3#!sS8=YC^wDsnFd=yfi zCNU^C8qno=B3WjEv3IFFyrhG@Cp`*9+Q7%5ZBoNy-eAe48HYAaze-o?6y^p7T7qd* zrDwmwfln9mtZAT(bfWj|9Me>%xIg5#2w~F~we#|UM+^$^KNS6r<{!X-)skoZj=_(&rOw7Jm>5 z3p(`{&}$zuzLZ3P!bgMtaOXV5eEYmqpmAlS5{Fna`rRISYfxi46PFC-=AhdDLvMb0VSm zrmga=dOJzaECC$p!)0U&j!(BYN`J*!3QzzX)1G5l>jBxvtK~8l2q6Cz;d0{IPBt{m zgUDIo1i`5t=j3jhq+1AePho)I&1quP2jAnRuEh#XMRo|Vl#6OmixkRK1u#^6rg&c( zjmc*tQ@P(YM=n0-9)1R|5;D^<;uNchlfG`=FyK(EY56+*an$*nEc4&c@(9regQTVW z!3U%7QRsk7wb|ZVMITGIuNa^^Bo!l-AD6tF?UAepwVH=ed?Ihq?py+YR4tyYReX|3 zd)7t=s$wNo+1Va5o1^-ZD4|bep9S`^h%h5EgbqsOlPF@dDQEzW;a@qF?6Uw|CDScm z#+vqUcz5abd94>X(LRLsW_{J%@ZFQ7ZVG0~CM#J1#0n{*dxC@s5((Qq6Y9}p-aPw1 zEdVL_DW_iRlqqQ>x~A}bD@A*!*;l^JB)$2IPpEKBm-f^aeZ3a3f>&{p1`9^?j|8q= z-!~CRHJ*wgYA;@JLIB5?Ae>`|d*i9XU4ZCXc6+ZLeEBe&2XbzPR(l@UdBy%m43& z_P-W4t7L*k**lEiMUc2@>1>6FwRsy}WJ`3(Af8Gq9;HKRAT=+b;J9C)J< zarjdBX3DX@ufxaie8nCv%*Hqp=x6@OE@d5b!{dJW*&F^CRptSl#et)0#Pu4fr&lfv zcY!x`xC?tH+%XWN(}sG@MAn56L|43}Wb-e35q-UZ>=$RqxCa};sd&|HU^|?|K>;my zz>+qczo(q2*>_7zOwCGe%(R{zP>VFS>v9Y=(#5L);m0uVOPefj0za6wo{6#zZ}*OL zI|yCHgi!7D>%n0woWpX(Um7GT655#9fDm-uW6b7fbH=fs!B#-lLU0VFMGPb6J_U>m z1oz#k0a!^?&?O|(K^Z$qbH|>w&#`$=CMALdc@SDK=ujE zRD0Bg{n*NnXnQZ7PS?J|m~)R6WkdNaBx^Iitrww!&r#mm1J@O*?!y}f2=B`Ql_DP` zU7;~AFYnp*$v2SJCSRN+zl~r!z4Z$R-(m%SGm++3aWFu1e{hj7 z{5v5IjFZ|+>D`m6pj=;0awk5is95&$_U6_EJJ+i>K^#Kbe)zRtSPEIo!{KZ4kvKrL z^}yG+RdP6QO>>AkiuNxIy(Bgz82~6Cs3*aapxqIyJMgVJlkcPTmHe{!bB{!?r;X9b zhzQ2gf+sqcF5$8R@1_sgR9x@FDY?nBtF@ovn(@^uQElQy~3x17xROUH; zGh;ffEWiMcVy&Q$ihc)`RJ+kogL-?KlmwAI0$1ai%u8HGG-0EKb?=%5H+*6zNup=W6V@&kB{JUxDPvQX}Z7q$+NmEsgE?FDMYFwY7$9s ziBw~`dF=HflV96k%K~61v7GBrwO{g5M{1jm3sLcV9V|)Tv7mS&j%a*`MPP^wX}pk@ zFfu3i-VpmsZF{O}F744jP&EI2tajuM6LrSV+6lVP+Sc)O#bH&k#$_J!NYuYp3`I12 zMS10n2Id$;V z8?4FsF>suoDc+zXp1!%E`$a(-8OejeB3{s|BFwLv52sY=f9AG}$7Gm94Q~8ejjfvj zvpJ%Acog?V4BUXb*9|eT-|v-3&e@xpw1Ohx7ETIW;?<;XlRwu`Q8RVYhf+eVgIQsj8 z18}S}-!IX5RV2jTJIs#lL*(4jjicqjj@k-_r}z<)@Hc{J)~SPn*YNvVfzHpbCrRs! zOFIXl(+P*98RHx!yRHao=)Oke0SCkC?+>hn3(KDd zFRS2TVVA5UZo6SDKNMOTnQi1JLLh3VINkz#1aN-8y6?1)V8?aV@=yf88Q^~%<{-XO za_nnV%~q}TLx4+&{z|Po-__sk`U!d52|QhQT!R2^`JmH@g0J;x#T9m$yEda0h+w%@ zP360kM8t{4%K)|X3gLKr9qzYf%TY|xS`k`{Kkss4&j6?wX-fDf=_x6Qyfv~pR`KCc zdz`dqCpmic2^%f&{w2$QntfSv3p!Rnlq!DT0ZNDTaG=p9(@<{;XK;G()SY#`tq!oB zJ)$|E$g_o08mYax_DZN@z_PHwiY0sY%^56yWr2UKM>{+QV0J8X95bu#jE?RDYd78c ziD9cuKjd`j7n73AIQL@@%M^e%Rz#izkLGA$nZo(|mm@pLazPi6hZ# zJ|aV&H>2maRA7;?2f*^APkZt?*D;m-lgz!xVUiumO%YV1=N?Df1`RI~J6DI6T|z@qnZMuDY-()o!i z`Q;)e`FX(sf1K0H`n*S3IPa#*uV17f^?mJn$@WfPSP-IC)j$4_`BdIRCpd2O=K3S< z%lozbZqvH6G-DTJR6KE7*NI!Um510hr{LM!?M)d=sr>p(y9lNsS3H_F=)|=`I@{gKnU|AcZ zB&8uB8``q(S8GQDWR%`}CyReBqb0(K4BuP0NUE0r_-E((YR=n9YV_mr!+nGOTI{I9{q5R4o+9|&vcwH zxR3wDB^_wZ-0AfMZs(yw^EvsJX&SMY8vxBV9uuJ3=qDz>7%t;dn?p9N_kV+_AqGx_ z5tbJ)Up>CC#nzuU7v(qHIg=1bK3*Up-*2MkkHkuB=W55MP_@zhx7FHEDtX!A?QfY} z9`^sPlJHmFzh(ni05M`tO*%CIWNY%u3@am#^O%`eRRy2TqO~o#pF%B16fkDv&EW=G(zzixlt3m*;^<^-r=K_a;I~ zJRa?t#dz&*J@c^+#hdBQ!R+u&!?7o=o3`rW2s%+AqTg*Q(vq?l*#zDjZJ3D(O!O`H66ngGQ|-)bO_(yS zJmZTy90l`g2vpg3(+|-EFfbC&v~9~9Jbatbp?W{68?QBW2HOF6f|{K?AA!GEeUG~sGWRy{6-7tO^m^Xgo;0&V z`{uab)PTp4EenMQWL?;4FRUdz4sEAdb)n$z)?-e7#jOeWz+515n-g=bc~)qf{S)&e zeKPk1rG2t)tldu;vC)KPn zf_2QEdR_ysc|d#a#($av2mZ^RCX(#Fwt=t=47Jy$dUtF~6Tq>}1UuLk#6Hl20*leq zr=e2oS8oE~DG*DFi?MyPXmz_i0#StC!YMEfjG;sQl)E=JDs5Auqah&swHyT_V|Js&QlMh?T>!!G?*H@Ga{+w6)+u*i&1ZPuWtYNwYUTU zbQx2R?o&_r;S8le5-vBc5uCTaNWvH$ms8(Yxe3x;B}+I4Y^SXb>y)5L4h^TbwWMSxm-I7cSq)pcU*R#3n29@U@5Gp zDDJ2{gEnw$#e2uM7qH7j=ns7L&x2#8yUH(60gklulFr|`KS6*@|2%iOjKT0Bz-@Xi z&@PvS9dL|=wJo=^E&Xh&MZLAoBAgaI4x7~TrpJa*$kvlZ{(=2V(zfs2`YRp(Tg#NO zt^zM{m{q*JOrC zRn$ugH;?)0FoE6VeCgyb6CR)+ZiFHWJwF`;V61aMxN3_&PmlRFpW}4-xLO{-^o-j5 z;L5P%mt-MdtcFzT67DZh1>~~)V38eA(@1+H+rS8pxuPR@BMT8%D|$TijvKQze{SUxRZy|MS zm3mBr1gcpTwK4tKe2rh5+eQ^ufLQBexI z?UCqO`WEOR%6X#3^>f_ma-&JCs2e}6#sjz8Zpj6Y4UAqx?F?^#h_(#XleUhttwb|C zGnF{c>|_bB)w}S3^5mCmV#C@v&@h7Ovl;rD-G+h z(%8?gY^2RyCegR}r*|42pxZSZ%bahUii{(g*0e+0+8A)N{{fU^FgB)hc@&g{RQ^Gy zp=)b1{BQR9=q(1jG&t6=OP=v98rhL%~m_qha8sHAW!5y0H6b(uiOuZgt56P_|8UC zU$U^ih7r%o4@lXoSEG%=T!OtkJw%;x602}pIKePKD~(!8qms|zp471?5T;BhNO$vcfu;;?yYcJWJxmhw;kfwP$N-m8pMk@py<|P?$fTxkG0Z zyAjGW&^E@rA^A9h*2b+99f-WX>?;wyt~s=knxo`9N#zut$-S5!#Ow{^NQ+1V3dGNX z@Y#(+Ndf9PNr976p;L0I>ecH~hQ`V1S@2~?$^7=@GwPN|LUZ8qe_p_@^sP6Mo=bM+ z*Ir0}B1pY~@;)(OSm|3RpVHK>m(D7UpQhcj_Ef5;gRS(uaAP?aw{*A%jDuQ8Il#+0 z90B-eZ!D*1xG>EwzN7&BY4)dfdK9c~Ul>hIVW9dLQkR4CIVSTbr~oYu;GafW z3{7d(SxBwGmsFLK#%uncq9E@KCAiN&X%eq}oY`W=(C3n{`kQ#Me)(bfwWMoSDcf3E z&@Ur@$P9*gp&h&1t%7s;M^B;KfgomD_00WZG*ERTrJ&a?rUm~(Yl?xFL>iJGRcX-W z4A=kzwNUBBcuIO!?z&4fU@fVS^)$XwBk@#O;vd&oepO4Qi?`_IWhznP9{f$_g^XRp z^nG>N^lJ;-9SrURyw;2?%)7dOirE0_$xsTTvnXfdEAahX6%L-%Gw+8bO?tyF08j+* zw6OVZL4fV6cxIYEZV%W1jO4Qts!J8rFcsbUd=1@tRIbzW;&d@V%##CWd~FDmYylXS zZ8P=2du4+IC=sY4zXGAKrL$~3X>CFV zk6e^|8AwKP9!YV2bjJEW}`_$HS+Y3+Y-U5v#3SaR}hgcHGs) zk0zE3xNMWa5ORWM(8@r1oWhA&dtNYGy$I&z0>j;Ms~uMZN(yJ6>m4*uecTm0NjTLD z$+YWNJyZlV9WP`vl}6LuyC!@P?P~BSS@r9H%H8X4Gyo zwwz6Ez=gEyZ=my!hJ3~*Ub6`mao^V=x> z-|YX|5$RgB5;lRY2e=aq%{+RVruAH&v2WdtnAt8T+%n3faeXPb#%J_ZtBVzy{)N@3 z$5|71%G3>}ba(k`^WKfriiXdA+&MFqD%Q;)&HRf$w3mOaFY!pe915y}0k^4C7zJaF z#|8*-X{C$)*8_dFX@JCV%;nd~K8;Y$K>hli7lXyC=i3!iYKd5-^bpJ2_R}|dx+HW1YX;yXd*x4sQz5f8K$fO+yxK|e^O_I& zHdyKQc$u~v^CwQt^j zZ?3nj!dGRUd)h{!+u?AArkMO3N|}AZVl#miX9?VobdaZv6$!Gh{~TVPuB3Phv)S^Q0%m?%Eu_iw@oCEpMz zil6f{ess^Mss`qgZ)qE3ziTmII3d^aKw>y*o1>a>Lf1wa8E1n=qLzX>VK_PIekSET zMuH6}GQ9l8ok}j!R`WD_W^~(|G$8_hF}%Nqt?!MdVF}^{Fm|PznJ3L+#mzXXK>G+Q z?mK*=DV+x~br4Lq;{2sd>QE4_y2dI%uBa!6aN&NKmwxzVa!qQj3z4Jz(IT1cr5wzU zSFl~VgyP2P6EWd;F9#lbtbWP2yj%0iCyHOZL&{Bb6!m*B1451}I>{+Qa^K zFla@-+`p$QfWyzP?^)Yn?>J@-8GycK2-kr9iPJ&%yfC-wAoUTcr?kwTwm=M^-pli( zl6%lf$e3qkizjjF&)J!W896(NLhh^9nCfG4izAxYI+e@@3V;*BrU~=#PJ!qN3mIxg z;q{!2KhXdH@M+WHuoWRDRIN0;A08FVCrf$6Y+4zW=WJ3Ivd@due0(MKBhBb7j8WrdgfX{w!29Oa08AxAhYl@_$8IVIDj$qP=+M% z9eo6l9`uF|1#ai3?E@QQhncJVhg%c&1xE#|ga!nX4v_WSbqQYLbazqQ9B;DFK#|X9 zoDP>_ZT)Pj|Ge9G9elhhCe)I%){)oUcsjXlMA`9t-%5g!G^TruHl&C{x0NRRM7iB9IDk!&ChX=%%~3)1~@>H*bKR4AC?bIxrsDvLDJ zmt8hYZ}U~iEt;Y*=eY&idKfZmFx+DpsC$m`TuNSx$35+g9!=4k`!iwi;Xnlg5Ynh1 zD#2&|jazw7raqsrgJdooRU0+7<;Km^Xk7Go$w;8T8t{hz4L1pJ&6UF-?l1te2=}C2 z4@1{RwQ_KQT}S(v06-q)x1_@)>G75IYwrE@sn{5AFxCjpmsxN>PY=*vN%buBu=~dr z{FB3J9PRadr#WZc40W|8>-#YMx$vRiBJ_Q$A8W~J@*t5Rm@%QFp^E(wE%bcS70uUr5km%7P<%1OkI1v3w>OfdUugP)^KK{Sr{jTMIIw`Fl7{5R86rgwpYo~kGv(bb4#;4p7O+!DdVmZ-K5XMOrMQ*LyU&xz zL=sDUYX7i?^Fkq-j(G05i?$CDm9N88PoirhHL}rJ8&Z1vGYN+qQ%9G6p5Me>@#AVM zT{v?gl7w6KqY@i97UN?+Z@o48G@`AyM#ZNTX}!WhR(<;?L1=*f^iS3`T-J*h_Zc0d zy)+K3Mo2O$@?i^i_t#nR>QsVpO|1Nt?Arg{Yl4-hgXp!kzl6PV3L~|vlo-{9n>K)c zT*SsVw9k~ee|Js?f`nwk2KYl!PxZ(Au^+=69KwrhT zv&haQ=SI)1-WNC6Ka_$xgfZip$hc@|Pa$)8O$36GWJ$cWaVA{2jenqD-ZkFs$WBuI zUKYLFl|%9aG1C0Uretdm5_#{(m3*RT-0bE`S|t6#;njKsfl_@=`u!FMf0($={&ieBckvrY^;WVR;e>`PQK z4adsBCgET{Stg1BJg~Z>MgU5u=b$(H%_J~lsVdk`jYl1;+Yb4{p5d-e&rCOqNT-s6 zQkfOM8HKprjB#`LOb>x9eHgvBXL08H36^2)$FVm6iH<}o7yec>0EcHBX+QC*X+5a4 zQ`DxAQkcp{vsb8&s)rAadjhqDWAi8uf6M+FuO@1Hmthz3{ za+FRUd_}DOAe>h^;mh=g5c+2o;wIby#@y~8wg7aJAliQN0DpKmA;A>Oky>};TB%Dx<9sly6ygW|b8)Mk!hH6t`Ry2i#82nyhMM$}NzUT2V@pg-U0 zw?9?Y(n5LEoR2~gm~~l?lV zOiR@HsYHR8gOt>j!dQu~kE}Q%StIsitO_+mM1V7)GW5~rG#TSVMR>a3dh6ozr*H3# zdce+@|04e&iJMxcdfBHM8LEuJ#E8J zwR6Iy8ejcmclNpi;4#@2l&Y~&w#R31>lhf*H+nUSc+Z*@9Nq7IS@bgdVGP$zMvOGi zQ}!ExHMBAF8}buS;NoOMn-$aKmbh`Bv4*94RrWX9C)|>IVz2+<$W1%0C_?XDA>0W7 zfEy7q2;BQ>=aC!i_E`r(Tm8AKNg#^+aHIRjW#+ea4R3?Ly?XvX;?HMc_yrUD3DaD= z*M*kFRJZFdp50m2kUb+~{QK+vyoRwn5NQ{O0sapk64+&X^B+?me~b$>6~1=9|I055 zTub)Dylz>HWU|mG2fpLDX5FkV_isuV0wGouF#p#?%f@Sw()Stv_k!_&I5BQEtE*l8 zNjwc1`Hi4{zcVpiM_}uarxOEIS3@GdYS~*8z9CKtu;*esCu31(3cZ=x+lcItX9Ki? zB3JwdLnt49VOk;1eT5`y11eAWqF!;4{JMWjRZOiwQ!xz4#1b1M-jF<+CPiEefMe&%;3}&O zI_&O7+jR5DkOSGeQIcDajrql8sjg8bU713uSdmLD%RAa;l{HbK5tPr(Ab)1pWF~v-{KjAVm z+3U5)R*&EEePnUJ1NX-cTxFn#KL8Y8gD6IFvP`XX!V+eVs)$2!Y zXnd2yO51gTw6aDIRP~?YfBN^->;CwK+o0cbVcU~!e*X0;M970{7G0k=&8SGXRUG7E zSU&nFt;x|IKZmvyBam{%-_3W*@71O`)O(GX&8gpf4szMCa*#?fq2KNifyD%@vENv9 z;D~c?rVLu^KE<)^<-UOL<hCl$#yd?;&8z(pL zsRFLxJF_*>c0aOiA^z`K6?uu~ZWo3BJBSarl2PlaMO9C>`lDG)_ zFNi;5e>>PF<60(~A0D7>UqD(*eu*r+N(O+Rib`M`4C99aO zrw)+6KMhxCR2g8L<8LD+2wL?WY@1)J4w*glAl2%I#+pI0gFE)gN?9brW|mHy?Mlu~ zskY$+ta2Rsa2XzXiK&2UtVhyaK~4ZT@;HsXWp?kvm+STDR=fJU4_j_Hq#%%)03}Vi zt#x90HSno-)EVLk1sZNdZ|3urOjL^LPeGfgIo@j?G0)8mk1wBZWYbPFm_J&h2ORYy znw$0&t9~@W=wDkC_wx0sUYJ6XWO+Vxv^bcc`o_(px5{P zBs+Ft2rFrpl=LP4;vxZYV`!h!xF(nyr5XIy)8|&r&1hit&xei4xept=_iZmz4@5Ie zL&Zx?GC@LxoyX=sBgcl2F+UW;K7%79ekE%rvS3{ghz;85vncm!TCMfg?vOw5%d$mAJ8^ch7@}ku=yV8XnJqz z9CNhK8MousQq!?MQHzC1^k#NZZ$6uio|svy7WW73e6N9qw|JD#-yx8}cKHtqiA%ZV zLh+BW;>g-lvWk!j6I-oLevH1}r{VOwF~Fg!ZCt!7Cng(R>!UFWX<8?ppP_*ba^L$* z_h8>@Z+w-e?O(#l?`$q7-o^CbxHxpr1#l!UVj6uH4YVB}^g19;%N;0KVuLphzBS$- zjYzqSQFExrhQ9C$V}G{H`g`G8HO_bfN+nm~P{rtfb*sD7U zIZ;Od{JCEMTTejj*58EhZ2PsSc8&yaYsj0}*X9ATXt1e>=Yt?S2Rx9^#F7r`t@%k4 z_&b)R!#Gi5mi}d&zj1=-lTl8a|Nck!D{@D3OEWj@c%RLJuVWca5D(t|aK)nQ)*R2b z>!$86`=p6$Qd{~&CLNDQ{hSh6M*WZlihz5j(Pjb~J|Q6k?U7-W11RTQH<1L#44pS$ z(zL)e0KW>K=}3e{NVCr>_-Sd?XB!$oQVmpek-k9b5Msqj3aFxJ>S z0$b_$&adRBprD~=yxJKXKUhn8V0oKS;L>p|lT$eEKY<}Mzz!@K^fy0}cSm9e!ZKvU zlQQxyIGnHDPP}(?TL7o}VMEHVKNg%0Yx=%I#28C^-PnsO!$A5=sVL4(+lVSd7Mj#kiS(*k9=7Rs3-TWy?l0R z*hNjYtqkIQ-ctI6-qG>pvnI8N9}DizuwKNZE5XsftTXn5_!I9@+iFgwY>0LKa7=-A zLK}G_e)1%3^E->-(!N1yno>wJtr16-e39GjbOnsIU>U2p)cF0^OO=7Ge{YW{tsBOJ z+6H%Q`>3n8wZe86Yr9f7e@Q(INEyIWI>(`&+cTf@w|j0**SPj7Mst?@(9jBFclaH7 zV)#(1G}D|M`G$)U`lfQyURAq3icha3qvXdA&NhJ+<^lUYX48|<(Av`)(C~2-3TDUj z0@L?ueb1D~uxp^xM&8cqvGc2Eb|sY1J8-mJCjEs0E*y-t2MXBlty+FOEMzr#+ZA;n z5$x!2{zFISt1lhaXfk{?RZ8Mr=D>*IMRC6w_u;g(pI6I|pH`X&KX52Mbb$PQ41Krw zgq6g(cy~ANS)B%lhsr%>2_R5n;a*ZDL^D}?sH>5|U+gvw(^cj5v+INy-J6)tvVHQ` zgzKU2*{S{O-=S-_1Xp%H^F5k{SjXRz-i?34@#AIxJ!a|4*_zA9ozFM&Lp6dOGyE|! z3MDO1sYUP4p;w2?meWBbE}T8xhY_O?7Spz6Pt8LOW_CgB&b}gKj-UA_)R7~_b{6^6 zBp!0y*#s<HNSBuWQcnZEI-)wGZ5j`vw#fJgfb z%VF~Yc9P#Nos$BCz0Ec7l-!5ikOYXzTA8*FlXRLY%agjI&b{l=%J}kDw1HBkY1NZq zCi9!OxZfhCmsL;-l@Y|$C6dBfmEN(|d$s%)%MC0SbE`s|Au~xAVIHF8M#vm_l=vh$ zW|Jc1r$8;AwX^I4;QQY;}ScOa(+!haLG-HnR%gEaOsA@9ojR z808h89`OF-g+g0Qq%4W!@4jkza?)!wx)q*iG05asxlxJtrrf=%0fUaa>VA#lYo&sX zGZ4sz41DS<=B+!I=X)G_1M}~!;VY~2*ngzet(+=S-DikZ|DA*c#$)-So_LY%lf4SR z8$|L#f#cc1#zx^lngP;w$z6N0uMI3q=S#13K~8}K3Kuaf_zB3MKICbf2z|~}vTe)` zjQyF6^Z_kdhOuKLyMx%i2}Y5#PG_H^#to=xYkN ztrO?{(0qUkvqb}i5_5V0TH>27jtZ>ZwY7y4siPQfoboKCzgvo%*7e@+KFKlT1O*MO zuoUDz60`W=UeLFahpk_u3uH$mTJ=*EgsB=2k9TJgVnN@i$;BAaJ zRk7jv;k?)P!=IKcCy$MmsC%-;juEde7u?U|1DhQ0ba=CZ+1 zYoNkpWP$#Q^rqUuM zuD&QaaTb(jDPur0linqB08;ul94lLT>C43 zCZD~dwkB$qLHt&#=f%w!2}+>I`|*AllR_@I!Pm0@iVk}gz)+YpibD0qCZ+1_^zK@B z?L9IYHJt%KS|Lz@Q?g|~nshRyf%-FHclPFQU<93Ym4n$0qqBK8`P-R&(Hy-T4N|Er zxd(f;Bd4Xpch(|`+abO{!P~!E59l*^OH$HL{QBOgdq)eg0Syg=@+lwu8;*g+ET4mRD!BKM5Q_Zoc| zfdk)fRMF49Y2Vx>M#$TnKHrmsfVzi+u4DDwT8`tPg3&w~PTK9l+P9VQOP>34m*(HV z$kFG;-(rCK1t~tNkh_512PLZ>IvPzIRxJ4zW7|6J&iNhEW?bxFZTBWzUY$lP)u|+~ zzwf$XEe$9E1yk~+zuBJJx(2CUjO$+Qh5-AqqD-f`3WwPWzo9jnRu7=lt2m7Zbh-dk z)|dZpy#!K}5|a!6|JZx$zoz2%fB0-;bV;WS1d#@j78oFifHcyfNC*;=g1|-y2oeS@ z-6AF30}+t!ZX~2rdb^MB&;7Z-_xpeN{^n!uu(RvD&d%$)p0x;W;tW=}DT56he)^vl zvd;KQ+z<23_xL$oyA!q2&X|{*mg94ZYq(zbFV~d$gxXOujI+JZ_i9mhyLJL;`g&8 zl&@9SZeI2%8chSu>?~e-jNb2Yj3P2{lhZ>r3w!=9iIB0c$7d#pKm+&f-wn&N=8lf~ ze7$Z^(kJPYlocY{WS#?%UWsi|>8~KuMSq}9k6TKNaQ#1>O=yoPn=7O#;FrP>kXYOB z<@)Z?^-SYB9zH36c=I<3JNf>5@I?|LFi)&UG~?-87XzYSP)P6Z8QL~t*{wlOY9K=l zsRJy2tLWFI=6>A0>F+Saanfe{V!UQI+odwZQ0uzf#(IORlAY;~zsi~2-TsRztY4A+Odtw9cniv$BJ`?hpoYip z71pPi4!eXsBkb2~2`%%NSVriad0VJ1`SE|2J_;pc_R@XxMyAJ3kje(sY&r&m+I+Kr z#_4P%b)9xf(D%9Pid};b#^3y70`471SLurwkT+?3mu*&~AJtUcPY)ar0PTuIbP*V9 zYzw_2&ii2af?eaMI8BlU8gdj$&U&GeG4pWw=V>dF^}&08!$SeSyF{>3(sam0nA$U& z2eLAp9q$~@jK5x|5R$+C5+jy3KO|l=fwO%Hj4&a+JE~cP;ob=bfr9y$)c<6Bw29Vk z5U60QT(&y^%x&I<9e-%Ncm*M;dNmmoP(`js`R$}rjiO+y%l-us2J7kZJR>I>JMd1H&Of38OA2K)0;R)I${72mL_qyA!TJf;N za%;c}B~;kIbyJ_9g{^fV@oN2>IFZziUfNhUnao_yfye9<0CA?@!_QBptQuS>d5%+m zHd_5W{xf1pz>Y1}j`!QDTTe3$aj`V9<~F5#Xm#f6M-+|De3x@73GP_uS5B;6yc2@5>cn$X7yFOf^)=p8 z6q(l0@o>kxOaa&`Ow0agjrB%}UE4{e56JJq$^(FceQvAoLVYmPJ+n_?$LYNdT}RJ& z8+_lycb~!uYPHm1wzlhxerKcxhmAxfNBUdbp@s2$4x6?`q;r5hJcF+WZcYZ(e+Cm) zwR$Ka&}_a-d7T+*=DF!M4M*xVg!oWv2*ckbgj&|5=TF(u0j7wN0#VDYn_mVuH!s%Z8mQYdr3{y*;R9 z=DpK;B9OpRVj{m4L85(Qn+rg$hA3jt<3MpJVfI_+xwLba3E5Bmv@p!`IQ1jvAX1Rj zgOBgQ%2-?tbPcu7IC;y&F+u=OX4$OE3Nk;9)^yKJ)QmIS_|3LDZ6d-PY%%~dNM(m* zFyuC6MitJMbBJ0~`VqUL_miDuF7?szv7$FSQ+!)^rj==lZn`F*CyUAePkM4hDe9X3 z`a0n&Y_Gkijo{7c79K$AY_o=a@CS~OViOjn`4+x9U%%IIZqWE7P}uz3() z|1Rew!JAn+`ei3*{to8(&j-V^++~nHT1>v0F)I*AzE;;r{K?ao_c%pm3HN!BRP`~= zIOZ{lUKK^_^BsvyaVFRFwx)sgySaZIfe`}LI8c?~E2+Z>wNev(742{-ZU%LDB3924 zb+t-#Sp6jR-|tPpY!V*%`MF(~q&4)VD=4uuYioRQn#?`9wP_v?t?y4>+#!V8mrzva z-#VDMa3aJS`;WKui6oy^jXuZOPB;$R2m&5(D}KNT(hdbkV&pCQlEwI#hwHi1v{Tai zBOgv56>bRdl%{z_e2xQ+hx$vx#6@jhfJkJEmgjsx`sSHpdvk}Nj^w*XqA9(}w-{!7 za!j(p!B9|M_07TQ*CtGEB}y+gjkJDR-oYK_!Cto4axX$D_aC>@CnoI_lP92k1MH0P zUk6_1sCB+i%Y7?K8V_;W35O^Gh3GnB-a<`^ir^CZmAzEd99qyPF$Wmc@o20&o;BP}KzoJvR%bXTOt6Zh1_Fwb zJ1YFr#1{mMF6~-=*T0yr`1+<9zY30amMQ8PY;Fj_PDgwXx=KQakT48p1WO5~2+sWY z%7!ft#@Wz82*{t~#H(-@E+7a}4Na`E3QU%@(;QzV(~>I`FEJY8qX^M|9q*C%zex0b zrv9-2%=5W)aw395`})65I;p5;Gf)1Ag173y3JW={Sfi?eFH%{59!LX{ND|F-s<2be zgH$zEUIoDD?p)B36~0jd9Gd94v^UlhFYtzYS^VIf(7e1vH1k67lxR^HU!nVgW?8>} zA^0CvJF@L(VC;b5l-Vum$4l~+H^VeirQ>ecSgvfM2e#9XbtwU?gXKcCQ_4NImr0F_ zj3PAJx6g42zYYRT4fzQ%c9975Ujfo+mO`jdkUD1l22P$&@7-TZP@4xaC&Brc_{Epc zLCw&C<~2O_Gj#aL@f1;H{E5L^?C1&{xv2kKJWu%6_bC^HB48(B!KvbV2=<$nbQfE9 z-8YECY0*_LUOVsLQu}QZ>^b5!lV`U9}Y%UMy$2c96#9{ZzZxgwz(vWOe`GeE1}kb zxw}f!4ld$o6*9_ljWvWd#=Gp{lX*Q)5aZg!$TcjNciwzMAB0qgGN_Hf;(t;(bLkWR zM0?v*rEW4}SO|&CV6W{9x8PEk@D(b{oS?DOI~`)Gza<$=fs`vt_KaS(M@)iDFfoX)nK>~kqr68 zKm-7opzW&BPva-8gCOGJv7U{*3=Pv`&<6Fvfe725N0y#dZny7fkNs^x22PuF-?;Fx zBthj>DLhH|K!h|E-8dNQ-l&Bch~m`BzvAS33~%A==pcjG^`F4tkg?}|z*$?L{@~5{ z_LKt%ts9S|7#8a~CI~v6-J+rjx0cqfEcuVURq7+9OdPU?kWnOi?~(c^-1XNZnbV?Z zp@-u%Mdcc8DxARtL{bZ4?DD7s%}i+2UV-W(?XP=6E17>*IV&(KSUdA)vK4mc$HlK_L(B5$$I zn82QI@{T9k`%T}RWi|tLKUy1yoea+E_d*As7o&Jd5B>|GL zqJ#*n$4_7%0*Ko7>vMl`ReUlxUgbEp`S2*%Ts>swE?c?p44V zlJfcLBkoiWzHdq~Lc~yg3xc@0nv}PR2Z9j7{gKPvhP{#2etiovc1KMGF&)lX3x*=( zcV%URFX05-;PggVa=ognfRoVWgRj5MgK50K5=r z?CxIr(+gCvRqU*l&+Ctb+ZVZA^o$U?5oxrL!uV3;nTr>{-p27I6a??#`La+~rO)wDZO8024r(*kN9(TocSE3^y7H(FJgW|-v^U3ezXZaW z97+t{(V4p}P$3oA5sQzsY5ZmQX#U_Y@_ssHrgFikJ{ zIC_R&`lpEO)fcPGE{Ed1C1@tC{pGm&X*@(-hBx92HTg8GPq}}>v`FaVY&{nm3E)lE zfhq&_!2QsW1CG{@IroW%MBKg||785iH^=7365dCN7#kmzgeRbXtQU>Dv=J{d;N#tc zUvU8Fl0^2wP+Q-jh{fW!SIL7fp#V9~f82UgzY0+t7?^Bj?j%gTTzu|LbU25>cS^S0 zNf(?i{^H~lyY9OP&{n3mIDyTHKv^IE$pZGeDgzBteIM!N6@UzJS?0DQ)9Qjaiu;2r zFE;x0il$yS?a=)P2`qYX=8q6P6&e_jH-(iM&J@)<`>1-V0*;@Wl; z*46hd)vME9s&{hd647XAR(t}fs8fbWn=9QBl=IbW_|Zoi_lj(QRqa+zUwqS+3n*O= zTe*O29<65Vhy=3>V?5b{aPm-~mZyxR7D8uqcQVHGiVFl z@?&%xKAx`;zCK9wTYMwf1JfI^T#5Pd7=7w7*z&$)v&ZtX;UcBTGTAa|N^C{fN)u zyy@+1S|pA2NY!<`#h24FP41VYv@)DIE>A@L;r-3_-^$DVpMORV6@=T3&5|f&t>1bw zdb)W7d(?|7UO07{tvyHuMTxy`t&0qj!Z}9ETW?!y3K`tZ{cK-Iwbm;R2-UpijU0}X zTwDd$>M)d@fU`hQB0pBBVJ&Zj;EnaEm8-akO?{3|?1qmhMxysZ` zp!xBj-eOBuWQJ4^tyIpRFYU|TZO?y)$~~!?9sO#OPXhc+M0cwEr+BgvUB_e4qn z8~5<;jQeo?ak_6n^X&Mz*Hc*py3TO@Ito2x`p;0=D~I5Bov_Dwe>BHiqbf+Dvch*L zVV`O}n?^&t(Y-S%%+cCsjPRcxWr3%?aRkutAZ+U%uC)AJOP{lF&YZWWFdiP}6lVzm z`~-z19ke)otgK&iX6A~)VYs*bY5zKqBUki*=Swpn{>Hf}(@Pe&JO`hJJ7T|Mo{|kx zIf4VW1zLD}|NZPvR(Vs`16xWx`x6a%h>Me}&$(spGaI_z&4cdRpH@R~p&4&C| z=I`saqV8L1dPQj4|HVg+|6ytA%Zf2?mHwK@jw^vd73`Z)Jt9^)=vy zQSx$qc2aeFHHOpup)S*-x~X=LXHHtwNA_hDvXvjGouclxmfn5OoaPkD38eyk@lJ{H zt}oy4tmI~Zvge?Bvnt04LCc%`(YZnzxVoXvHM{ul0z+a11kYBWv3yzUqt6%gb8T6& zO|UNt9Fp3*06_%CZz_=vMjJSN1LW4>G>#VFOX<=1sY#ppDHxZe8Bygltsj;Zq{1OP z$z?7VXX`nT?KLW^N~gcS@Bs%T@&x%6u0GHvP$VTEiXq_T>SmitT<*}pO;tk#1IeG$U39j@XN?0qod(rt<<*HNjw6av&Z${*zu7)uR2*z zEhmd!R3(!@Yj=}c9^pF957Oy`EUw5DI?u1}ilXbH+KtAOABWt1%_#x2hTM~R1bVai z!x07aMvJ05F?9H(@yRLrlyx?BL)i}p6y+xNmIv-z7JTz1JW}7cS-U@mgtta$Pnpo` z45*x8TLjBUa|n1+X*1>#QuinH_VdRS>Zu4eSCNFj-N&>l9++$;8uJBROPW`iiJFo5 z@M(pLgA)T_L(ORr-J*9kP`N5lCWMg)hTpXYWNgy1?qyhc4ZhSerI=g{Jl}J^E=ay~ zboXPH6n^3Ogf!2mrKeM#SsTvsXS{@GW5uGz2*R(_f}3k_Tp@!1f|Tfdg;Y2~Pl1f} z>p#rV?gyGqUG_NA5^r5R9-&eeKW?s-T5h9sd@0&$8CLu`8WCFJxc+49pw;fPFK3>< zr_WE7b31CDehtoSY9E1}^ikP+F*d%D%Tj{J{Y1REAbWBD#+?uIX?76psfY$sf<=B+ zvg?Yo-L=w~(V!6%6O-s~JLlC~3|KM=^l7nC`SNMdiIu}%q&?1)`ZxXMR)g-KJy>(m ze>L~{%V$lo>cxe`9Ql1vzvO!-E}eK46ZAJ7VwSfqhTl@erf7vTwX`ccU<({DdpS+A zpA>Ro|4fW=fy6_~i41+=tn|R}LjOY0xl(Q?a!2Ub0ab|AAQO~rIMPYDiv$ujc&=w+ zm!-~vAc$y(FG{a5-6p)#Yjt)%FtTso}`0Bjb1!n_Dz z1&_s7Lj!2F*>xcquP1Xdup%!@`P0&KZvnCUD)w7+Ro;1x8Swq4VIYu_M<9$;$0No8 zkmJeo)ODy;KJ<6!GBI=#VqxJmWmBayi~E!wzFwmfV)OF#zvZp>%etcgo7bl{#gaL0 zId`78c}7!-%|zFGt&|61kKNSJ?o%U^W+$(hqsVUe6u;YwgVy*oe>5ug8OSBYmwYjt zvu98}trR5J??1D6cuo@#KxfxdOhC?J1S7-hfkrn+_(#E}3N?#CwoOybvtuX2Uk!>% zW|kOg7S&vu3U0lcE^`wsc)wIK@xZn4vt(FzG&&=nVnMoxOXxZ7nN|u_&KSMx zTOk#d5-!R`#Tp+>_9`H;nR6gi+odaVgP?X{=CU8R!WC zk}zgfc*2=@o0c~5AAOjsAvs>rFMGxkX(&;!}n^`}df`0I@);YY{qkwVR?)K`r?Hk41Ym8VBd zUwPe^o5^N<0su5m+e|Z& z^&MPSTfSC0uANF0vd1C{4NR^*aH-piZ9WrrYiw#liDtfd)I+_TU?03TRhszs5_*gb&?#_&j#B_UC zYus_PUmm{3b_HfZz7YS!GtMxv5Hle=rxE|Gg9kMcr~wk*Am6@_1#gXB_Tqx#cyDAd zB<>8@DH!yiuu5{qpnuS}gc+cv97C;~gW+P{foK$}cwss>gZQR>0(#m%nrZq>(;mmi zBIyBoyEL*W05@E?imr5?Lnb3%0ybvK@B{6clTl{9_>Au6uZs(bn1D6air>*OCHkps zZIL1I&&v%}30=oJ*$aFCup&gGl|1#^o1V-T;gR97 zw)2CtJ*WEgxO`X51fWL%F`pF$Gxa$Rn^`oY`s+&x&-n;IaUMX*i z^u)^3;;X>c*u2`Fh#$Y?}6cwE6Jt`zet|BEeaMwcmW+eUeF!! zFS~Vty$6#k#|Ko`IL%ah%O3TLmbc0{n!i0nU5RwE9MvgD(uw@En$NXi8~vKgb@5%r+h}eKcrrNK0tYZ}_ltZjKLk9z#ftyw>)ta_+1vUz zu?8&J`zi=fp!uB(cdQvgN^v>0_A1lbMS&6lH* zGn)(D5rj*_1o>(8N8MC+p58+RNUy|~SI)?803xjIzpnB+_17h$UPKj^s%FTKr)vaR z*M70TOon~j7Rx%*wIRb(Jm;AdZ5i`-^lrwcDL+BiV4lU5NDa|{XE3|#wLSTtjczlk z0TB?s_L6tdY3H`rJuw2sfG787^(0*_CNr>51)cejI~PKHZOZoPa^%3$dA5ifQ zW!2k}3RkV&7S=xMEd5^WRa*SGijuJF>!zTG-92Lr(yN}EVeoO0wJE?bGNN>8U1Ly% zHz*>5H|^QDk-`%`(q~$SdqKuIDFo0FaO0 zJpvu`KEdW$JjWDjq`M;2+()=&HKN2n!ZNL_<|_p$7v;$ySh6>#yVpt&wn{2Od$Q=*k6S00ttWzJqZIK&uRNd?@<isw)&mv&h=4Tbm;BYW428Z~T~$M%l;=f_5j`NVe`sn7*>?d6S!w>W zief8?QpS1%{HA&1@+Tg2lCL-mvV&!4%pA*yU%nI#gIUSs-Pqge;scBhnh#E@VD5m6NsnUsJDySXft(lCw3=2N%&{zs6>TtsN?}X?W(Ly-ZVX!;EQn}5+pp}dBn)o6 zvA?!Mlo1FDK-e6@D^#JB{6=qZfFF8S7=2E_o3Pq!8L5OkMR()tRFMh?{WUPz2-qk( zX^keZlE~;Omm|cQ51x7J>+K~eHec%sLIL3mYBB7^=N^})Uw`%(JNpefdre~xp3UiL z6&my)@}Bre-iJXcC3VU?a+&iozUr#-wqEfjV^#9^dbWgZ6Gbokxb@csGBVLO-q4Dj z+R1#ua=d_8UnXu)6Zi(hGTpyU5jfAiH@A6~NOO2TbY@pDpimP;4Vn&^JTKY&@NpWB zkD*-*{258#!aZ8hi~vAf4V|2?v-){%EKRp-ynoine}n3dW)C12+aq-L35$#n53O&*3gSdyaEkPQ+0MWV1X&H zV$=K&#)b#6B$&KSKJ{UbtQU95kJnFqKRs4XCzbeY%Y_HbhAZtbZcAzR3$+>kxop-! z%q`#{Qg9F^V%jW{6N_Nqy<IeRs(P9#KI3@=L>tetQBY6u4Wh5)ml zHx^3Z0pK8Vsi1H(L{DoVd8+!wC*S4PurKaiA~7X-di?G>V|w)G+C;xQYw`V;MqEyt zi>NCuw(RsLaG`jDuh`wm(v`9R%l799HU(t-y47igsy6>wz{-IeS^g_U9d9(1Gny~U zPASOXuy0;gLY3z(p~yXhkB`4dzB+u*C>l^A(*zPVBwQxTmR<}X6ageL)W`#VPoWMp zP;TXX6Us2H4`x+Ag}BO!w;UCn2!$15Ib5qN`>H#Aked*4ArcY&U=5d zSL>47P}0xieh%pmAeVu-13V~a{LDLmly2%wmO6R|C1 zc{miSsN2(HoHpg>#M}OrkIsZ5KNY40_ClIPBRcRGYYUB+nsqnkU@<2}v0RI(!prjf zJ+gQ3p!G>Rrn5DH!Q0hJ_7ban6-O5?9L&bT3Vuh&M(#WX)rR5#lE$s{=`I@y@LqCT z*mDg|Z`(BU_BfaV`?qAVK1U40;Fskthwr$nnK8$BXWx*+i`GQ&LvhE4AG=lu_+uw8 zRIj&I)V#7wI%m_>GgM75()B43x5|%??^JZpj{{Jx_s5N8GU~LYW)rivj@Cg`WpwXN{ zdccw`eC@{M9mb#X-J}fRe~M+iw$CEM3hy69WEYK#Na=qcYP|cxXw&K$uXKSVk})={ zoP)`~*_-g6>AXju{$^IWL)T?kA$zAQwc~`&$$>XdJOJYPOvy*zt4{MWT$;6C99q?r zCfZV;fF?dc@>@eeD1Km2+OJl6@1voOwRA12rGA|}P1a^YyHd>&{T&SykL9z;<91`9 z@>0QKWo(P>rQ?MqrO4HOZ^faao9?LUM~0AkrW|kGAx70zd|rYLG8Oeo6-x^^L)Hl! z3XDCOP3FE@za~!y%@K+ge$awVH;w_Me$UR4o5$Qg>?#aY0G;!OimWInz)g|D{kovc zF*txMLUC1Iu!+x1HF)otqTS?du=s|7rO!l3l@IrZ%oDLFs36d?|2|mfl9};+jGa>| z${5rC&+1N}W$`lj4Izb^vLJ7_sB*xEHhvg$-yglvSPICa*NgzfXpN0{ZGCh*`+XN} z1|EQ^1`wd=_Y)PvpwA1whL;d4w5?|@AzKKdJ=@AnYgnE(R4R3B1O+kqD0PBLH&Cs> z2du-Ru7wGAWUuo4#({5y5yi@2?_8L4zSVt0Z_)0Kj53ayuB_;T>|zAcjv7Fu_C1*R zM+ULqRlWfQ7>*-NSbgTi8@PrGa9{~CR90YdKZ!L>;O}~QEq^TRSLMg zmPtnrWLlf`q#K$`eAC-(R(vm|AFik9j_Ro3$*R&?!+L5WdFAd%VRnX@i@HS|oQr3Z z@7>(8?)}i5qDpY4*XWq{QDqY!X;RxA5b!gCwq~pT=><-~sM{-&0)S7-swW6uqx1cn z>HL_*2g+ygSHI;e#I*$uGUk3((dD|1hw0D0KRxbeCWJGOKS=z$@H8Qe4>Rzge(zHm zIRhDAVES@EUp`ML^(3d|+gfNdMlF-2pkB)PBh4C5fxfPvH@*0oV!@qk^3}+wKMq-s zSyWjHSlaE=j5$>i=Y|rLXz5S{YB={3h+>jGTmur6(P;(uNg(qR0Mh#itK?=nA31WW zc=8i;Ced9)jyNE1b-;nAM2-qT-ITz6FsA7D==Rmuc%cA&Ckq0dnqNX|PJq_SVfh09 zU>JM!KtX>mC<*5&EL1e{^P5F$dc}v{>lDAoc}oOf!D5MvBGwH~X#!~eQv6ge*8d5h zs4&whL|e5h(U;^jW+diKBu3C}0B~ibF>3&wB`>|cWXnIB2&~wP0yVNNL!yV;z)KU7 z^JN0Oq+~*TiGL)8+I5M0(*3Bu-*>_i&kfxth8gc-j$B{Ba)lfI7J+K|ZNKk7yC|^;ALY-K3NV^>gtJg!z|4uThT}#~? zvEy(>1c5Q|9m^23Qt@9Q05B#P6njJp_*0GBuZwiD=10rfdWo0iC&*@w%dYlFaxGn8 zas#VTB>E2WuT=_XGNg~1yg7sUmG;4vL#4@`+zyc#b+e>+5ipnHEP=Pi{vY!eEtZBbA@!k2V! zhS6#|Drw3aK0!NA2iVb}m0)p+*A0G`T#-LPs%3zVw>Lx*512$Cwdm0iyrA?!D_zF0 zFD+)4xON=0Z1uIBy8DQpfQ+p2`Vv#I(E8!;mUdh*&r_@C0%Go!l6=oL2Lw6Yaa6ZC z3_eIME&~1+FL)45jl{x-yEGs~e)OD$y$8O-W z|GTDy++uF3H2Z&=le~RyWpYq<_b&Yi!##YS!r@j6nBj{SJihtN*%^;8hd|)s!B8$z zO=#1OLk(Q&U8}&`*YyDHFS-mq0TM#1hqwC-H~shZYF|&r#`mhV+7Kb>fftpicVcMs zgsGAy#=4N}#s!cDcO(FW`#OXfs^wAJ^>2T<>p0$=z1BPBJy438tA4ZIzhvFPI*(6j zxHHpW=w#GX&l7+58SQ(>f@?>cKV5$rzSW&3gRAVm_f4h+=Y-%}H+ zCpK1M!)k+y#>0he&#{1UJr%B0HA z#D5I{sRu(z<<|`;TtXP&4b+f~RpO)rNM0xbj-)*?V~fY;g*E;Zdqihe7;edhc~lVwC>FWh7R-4;?Uk8e@3y z^Y--2Oc)0VrpYT+`E5YOjA@PYHO$YC?olRar%xXS1XVss{_^jcatPO!)fnOK_C5$G zVcgvq${y&n-B98qdBqquh5{`p9@g-~(jxAR>cOb%hdUUSwd>O*2N}p|-c4?A5{Ze9 zuAT8GwBNGAhv1R>5qyIQvV~#-oiw;MdZDqX@1x%JbvKNDSMSf;1BY7r5D*MDmlDtz-cHvRg1pr$Wb9>ru{5OBR^Y3V&>lW;9=8$n6C z=m0=a#E6VEt7wtO@ha*q0sF2u{C+f(IN`2y4a^D)_*>KQnedABVoN@9-!Vu4Z*k zEE$$537BzH``a0{LlV)iftsM(pA8TZI%Uu^(r-Wjjn19tOh3OeWE2{;ZG}g34*&bP z79y~B?pHF@(7tW>b~c?5PuoafCp8oU3uDb5HSM&Chd2-q6OV7kCtj!Aqe=i|%d|-W zYQ&%jaMq`{)ZkM4v@b<&KQP;vUQe|NcfE%b1b}xopCe0)JNZtOUl!1d^mw8ydcZ3U zEqhJLTZ#&_{1Lq2IMrNwytAG^zYXB!^PK4^8G5l@9?|4BbCDm@5}c(G8Q0;PoA>o= zB?>?7$a(ZBAW{krfzYKLrNTVGEgE&rs(vgD*5m_!Hda^`lyg->yl6ai(PS2rH^ zV%Xh&MoG+{{h{y&@2Bp>yJ&>n(<@XA;n(YK0pKcQ5C$|?b~|Vgi=v<_4dXvgYTo2~ z5(9g=*H9u!UR(&2)qK-5vA$&l)5Yt@Q3T~eEkK(P>amX^Vl zypar!L_D3Goaj^9*<0m3*xGRU8Rxj94A^XfUtzhivKo%t>Ny9`xz%R|X^=Nw4y8O- z9byI2TRQW+b>Ff0+%$$@iKmMnzV!@s%NY(+V=_ffKIc#eA6s_UwTA(8Umkc6WMwv`4+Iq$tlE?6w83t+l9lF_Nd8 z`M2m&SM@SI$c%&`NtO3Mai}f$@&vVXoG*04^CEg3%b!|6L<{BHRl?n^r3}Fc^i%-a z++;V9wi?LvF6iQ(o;_;1 zj^>F$1mdUz^bJ=kjZFZR_f8VGS9xAepFTUn(uQEiMErS?>I&&hVizkDj?1MfU>ySA zse*3UOkIK6v5qD!Yt9YYwoys3t^>XLNOYv>r0_bF zMv@>{k~XTp72N@2VCWB=+FWalI@uV}e1p|$KF%e}H-Ei!z(t6y40z^>B#WT=9*s7| zG(cW9lEwXO_y6-CTPSl@8cnE`L4CT2cg3*1%UD-0A;{VrvtVQXX#YQxjP^h`DQ$X? zc(*xvbYo#NPTMy1Ek$e4&4dU0++_Wwr%FC2KTftL7H&vANV8E|H|VIIUVfeXKs>H1 zacJ5?lgR1nHU8qQ-XApqS5 z_qaUtSqEAoVQ%C+_Ah;C6iXe!bXL>?4tv^S`VcLj z>OEf_y&Rwsn;UD>?vbmhkW+lB7$rxYKUi+h>ArwL{PrsQJ6Ka|ASGzdYQzE1e86d8&OOPW0U9s{SDInq zu<`y_gijUg`WV?+)SGREc3b0!imfcWbbqgdKo~*9HuY=+=RxU514ox{c?zQ{Ak%l~ zdHg+^ceFrC1o0PL-G!Vn?ZOTOs+sR65bGv71U^=T$oHF+pT_C@>48Fz3Ezy}&7hy^ zmi=JD$%a;I{+NHwJB?Df`Kpno|9nZIQ6n?moFf+FVWv!e3=jJp@vohxSp;CZm&?Dx zpPACy*SzPT!4hSJE`N*_q+!c=kIj+BzwvLU9e%r_Ju^gHV^066I-2To^Q#&a5AC0< z;1q}8hNQ1r|GLLum>=pzpC=;(wo_83Ij*Ih5o_BRT^?urS&{eFL-n^-CWw7){^5wY zsUhjDW%fkt+!rXu8&^BKW!Uf5_@h$4_3ytzyh30CU)Aopv#kVlPCmKbS+-DV!Nu+m z1aQB7f=X$E45ovbJm^Q8!Gecn>XgVV^T=qT!NmjDik!O7du1scx#F*>bWgym&rb$nnF^w7D3gO`fd2Yp&^AmNG1S#Spc~zm@|T z+SNlQ0duk@l(At~qdh;Q!@fjtgI8m;6|(y@ymhllRf!MXTz$N3q2wsQGpXuoNX3>x z{x>%(IPRaP%r{Zf{#wO6Jukk|=o^F^kuVW4>;pxv=@Jm)?Y~g1;v_$|BfrOCpW)cZ zM7fBan4p77*Q2Ix@3YWXJ#TWlMh2!^FV!+F^||@1C z%b-+g6_S+*Rmj6#!{f_@dM~o*B#rDlQQ(K)iHZyU&QTI`>ucC_B4pSX9hl<_BV1hT zR7&w!$x#UlFaRyp!PLu}E#XC-TVzZ*{b$#J&qk2LSR#2ik0-;keRQR^8mGT+$HsGY#KKoa!{8^HtfDVL zz~q0YupziGNNydNAf)h9c5`(f84Cl0@+^ty;Q=>kBJD$T(?+RoBf!p57%hd7efQ?Z zX@v?Nd9O9u3>V@n1QIZDiCwiQ(3?4lFT29cG-R_%c-|q-z`3$r;L?4oif$4axoJ)=|kY^(Gz@5Lo=L!j<>az3>u79=%nCtt;0 z2JDV~Np0!*@~N(RtvTCUe5@M%a1ASU5ncZU4OrC)8rLfr-&u1;%QVkkpgJu^Y(dmy zb1CpdCs5>Js{7~J&E`1^UXn??MyU^|X}+p)qLzVfxnl1zrA5&|ytEd%%Q%|=X?Deu zE7YM*pbb+g`xX%)FuwI^%=Gh&95vLed~DpHO6I&pZTynzbsjPZeNjJRc3L{$Wsv>l zY(3=lUYxI;Z2QSKo!`7T2_X?gcs^gAao4P8bnWRxPY6vYBKUK%5XTQ%?3+Hqq$*u82Iu~p~NBz6~vLROgW zK>pK?hN=rGDBcCpH{s4WU(KtE+^Q4DmRFLE&mb7r!-s5T)uBxOMK3ikDullYl;4c{ zCJDuhrz=2YRA2X`-?FHZLDI$8$#Y4It=_UsnklV!s!oeHH*LH*cVZJ#XHALNCk>pv z5i8~F5o>zn<#p#5)A-vWyZ8C4MU(*lf^v=1V%(*Zi`JR>jrtwRAy=pXUbQz7CuxDz z;``;A$WDI5!^ZyC$Y*Q&(@cDk^V7?&4=!1Q0SHu{NT1SG8KLbs**lfJwv)bja@vQ7 z2lPq%J*!K4;MlU|mu~2sI@ge_Zy0nadfr@OJC^f@ZtxxJpFuprFC1lLutWc~HbeJk zYt6lH#Mqom2Bk=PRh!9&dTb_NEf8%Lw9Q%J9RrZJ5MJYbx&)5b!%5wYf(#40Khb=$OPZ~Lc*;8IYc_9%x z&GwNdJS%lKZ!i>ofedsfJl6a_dmH@$ zz-268jt7^441n?FV&kqrlS-@>P_hT6LlWjEvyxBJ$H!Vm(3%ON@!Y&?pmP@z{xYLG z|5@|1k>#Q@Go@jN^6QW{aw!N?DHm!pHv{pV`TQ$8Ii2^E7m9(&{e)WKnS>t5FkuPW zpH=`p+1NlO_6~iDL+U18ftbf^l|!k|ixrp&4(^4q8^m^Fg=SF`r;Y+8Bc!>d(k@5RUkR)P2*v5sl`? z$<*%}sM;(92x5)4+4Vf7^$#dy6?WR`R5?r6**Y{<4*F!4r1BL6;u7>PRHeH!N-m=o zRBFwWnF(unI(j{wJm1c+ApFyXb4fjz*fHGUWaAoaXkqdbHg`iTr)i3$vA5|-CT3*~VNl1AAyj(cE5TgrCh~?T zg=Tu{uXm@Dk&PmQ?cHD2lDZeMq?Nh2qL@|NVG>Xa3CY6ai1h~x0nh6PBO4Nrf>+{5 zD}M6c=Zaon-^llK53>E9{Xvi0SMMAYEF|?%T9eaAi)w20$v8*R$fnX>kzG&)mpTk^ z)s^~k9V@6xXSB(hX>-?aIn6n3A~hsttGFSEdAu>jzo3uv2ai9F&)rH&Au1M%N*wS` z2sfIlnXOr?5kKTZ^zIo2&hZS%YUGTNP&nyqdw8}g-xC%WC7L7`X%?cG)n9o_CAwCK}I)fx~& zv4r17z96F}uO{Ol_h3=^l3t3TO>~9<&bCU}KqUL(-q-AE`*PCnOD1AZiJT4cKTYXl zWPeG?vR{bFDrH&EeZGe(?8EnRU$J1=6mwP)PrFoh&ODSlgNR0_IiD?|I;wQNI zDpWK*BIn4NEjAF}^_ETC8FN^8N7+`ZF2D2p3pR<=H4WmT1jZEKt(aImF%679L=T=z zR-&B6ZEVCrwqoe%j(2rcmUNW#(J+9UIg5#aX6?DV3h9~o?%NRbqyeg&;+sKlzu~xI zdFso+v;|S4@oZo;4Gj_waPW6!5-v5$uh#qd%sh!~YKl8$D_;aX$a6`{ z;LNCHfjMGwmX57BDZ@iU{muxW1O<6#Rv+Wpu+HMOx~}@3lHX+VA@OQoyrM>Hp$6YK z+MZ1TKe3;q(}>Cz{`r?nSh^~!5Urpfzt^Z{c{Vjy^C+|eY{vxZN>@TtK%XL)DcM9QsTJ`Q*_HxCnjkT>pqej) zTqv|3b#E?;M~}`A`@Ef4I21@gn9qJ5!;6TT;t-1A`J#fIBFu@WsWOtH&B=6^gQyVK zEXI(XKZUf%xR6XHRGh6i`LKs(MRFwe2-wDf)FUZE;l01J6`H7+b)Psv?d5QZqa>J0 zP_6%a(FPceHy^H9&QO9i9T|U0NZj%`|^d$-d9c%45kT&ra^R;W*`t zaq4jTB_1c9?&LD*IMG-*TqJ@jDcgL{fXXV*Dri#h%CrBpW;XCFd#E0bc0mW$Q=%gu z9oA1c?M{M=$4id0->Q4Oi`09kN1axbmY4Q2Z8mM)kZ(MUQF{Q@SE5zoGd4PgKV~8x zlr5V8TOM8;E_EVBm!XSi7-vpq1a_TfI%kY#qR}9y(=Eph>5a$@cki_=!)@`+xv|a3 z@yX-w38Mp}kKeYyPd~bSV9^{>1ZgJ~o4oq~+eiOU4$@vQmREUG)}|NIa&2}cg!4A6 z0BaW5jT%{GiipPxdLHn6ndBiUF=-iT62nt^Yc(=87mk!+(SisysTV)!;q-Mc&bs0g zwiBut+r$~{C|$ltl?YEejJW;oN^@hh&vtJ9aul-}BsTYLTHv!w`m}vro&Bc*61B6W zpNXaPKJH`pyvF9H<%RRR?HG71nLApM%dC0^Za&`4rZF2`D>O-Ns^4&!NsS$O)1>b(L_q9mdHCH8GKGWX{AkMEj<+!Xu{^nl4{rRkZ|)Bbg3$AqIXnKNj66SoU@Gx95y#dA#5H$oYPxG1$q<46mVQDQB^S&9?j z9Jicopst@~q)Eq4%*&L>o{b)w6sc)i7onEKdAe0937#!&aV{`#fbx;9$e_qb=34AV z$NSha?jP~>Ni^Lou{a((jYb})%lDQYxtMKsIchj-BsArn0!H=<>3XSB zGq@ID9uM>j(S=Wnbu=rqg0g6{8$>H?MlvCS<+QB7vxSMpTauvFC z=g{~t0E2b;>Qdbd%Z$ISxelk+#O{q^!aBx1;+L6@?O=LtMw3UAtk**Ph|0z9 z9{((V7y{x^iP3Jg3%0(diH$Eb9}yWvu4QKhjW{}0kfgv~N%WB}z8R5>dij%B)j`op z(dToSSxFrk%@%DO@oZdfJS`oBj|}ufgz3#d-n;?gaV@IQi_Cd&qN!(&!3(}NJ;`8ZN+IRo4*?IRDbku+yt=(ZiOYq%;&exugn$VYP>^G|L){ZvnV1JK; zsm^V>wz5T|OTFIr<)wiwku59HRdTJ5PRB2b%(Os4hZYU~G9FESX9+Zl{7);!9-Dpq zy;K@o#%MfhVp4u>GjJraUe;ygYF5@{T+?9l_*-+=@zd>$s*o1N>ikauO2@4UYfN;= zz^2#_n_Fv(1IKWI217Uh!}?#_a5zVUdrIFbZp7O!x)l z4a^9Z(wm9v*@Wn0auabNsVZrZI<7jHag?b_Tvei8{6dQLdhn_xPrV|^m{m-!VW)BJ zpmup*m-@|yJg?03Gro(luj!kDzPh^LpeyY!S@8}9L$5EOmlvZIXIKP)a@xTd{? z!xsu$D;-80_CqXx&mEl&%6DB2kFYM>EUNy956hsb(ze_U7zms{=iVJC2s1-!A}6n8 z&WF}~hxhkB4L!TLULYjLAaqs`QMbn;ppAwB3;+n=zC9;J0s!Z?q0kKgz(*JW*s=lu zz?lF5oqKMxzTE8t7dul;dl(GBcZ&%CxLAyUJGThy)}UBS|6(<)`v9E3;@AK{v=adL zACAtg|Ff)a?N83%9vg-IFC!F%^WXTE0YED^2;MqEcMUTy0D!FHk72#yCu6#;VC!UP z>TL?s2HPNAg{*Cn2swg&pSy103P{UGdrF~GO@PZ-1w{EOo4 z3}H8g=>e6Io_0Vk5b|?`L2$b`*wFeuhsQr_Edk0~6^!9cKgFwE%zCylYLP$>s zkcfZsUx*?2m+dpjZBfPd;*BalAc5O(%I zjs9K#%<1a>ZzDIae^lM-1Nm9IgG7XcLH}01O_aOU2v+vAv-U=M8X}P{&_AO&xjB2; zSi9T(8Bh-NzZw6ulmq>l>(ATUayB=IK2!*Hm@RpU<>NIiM7t<)*#W;myrgW)hm(Z>)^0qXPR zq7@8?nBGHtSp;|G2J;51m%e?;YFd|FI9tofKb_;Z@lM8B0mvwj{U7ebJeIC2p5LdP z-?iLC7ueWWOFvE;@Sd{SP2N=QjTb7($q1*<2&Ua@DbY~K?R?sGW!hZc=$Vx$kA~e( z--zEc%7{+6ax9~N2|3yTUHuk5e`c8Z5W|t{KVDAT>(FrORnA4R<1+I6?LP5VJ^y-h z($?}D0i7dWgX|0LRGuwNG1!ENztHFKLEcxD{bO)GCZF=$JOuJ{CC@>J_qU)SjOqL> z?s}x9vqaYC9<;d<<7ARLrwp`!wKB+?vzyq3j6t8&)l%v;W!st)Q*%|85cvH+ri1D z*e=u{-xQHAfWX6OUBxPT>7iJ01QFHPUw$30X9$;Tow4^(bt%Ntmjj>|wj=Zt-gtW8 z(N}Jw#tO2w$gg!~EdD}uUnzNTlCeL!0v5Vce$!r6(&2XzIRcuQ6AwwO5(bHq`Qeu& z%PU5xWHNILh1U~_#>s|r4eeJlo}JHWfLOf0yQ98xI^3zgM?o<*gKQK7XEy^=C5TL3 zaNM)nPM+*7;ioTki^vm3>q}J1htgCNZ6|P#x@ias6F_N<$Kla~!L9VPnJ<`;1Q=%D zxYvpL2A`RC_^skYPhel#*18MHe>!5V+1d?1yRpjt;MU91G@e>RPf+dlpv}v)T)X*U zMq@*uxm&+}NY&du`?EUD=fq-*4{5pU-p<_*+$J)yY8IdUNs*Cm?_z3}3l$kD6Vi!J z0MsYy9<1|g3?0XIC9Pu8f~UgtT_X^R#mmG&ulniC=TKHfg4w<_aPd!sKJoUgKf=@{ z3h&PrK1i1=VbOhuhLy^EhVd;4IiJ^0(5;F&wZbNiX5Puh2ZuV1j@#Ip`QQH7uj-p< z@vpSfO`eo!flutu#wTGxih7^%M=lSzF=e*AIdA&dH5v#AOMubm?4*($2P**m*ePjRsAmF!ix19AL0BE40tYGA8 zu@i(BX!Kz2TITNWRtYGw^@P(3@|XhU{FW8}PdPMri?_Nq))|Kxha^H=`LlPnCHCHrdq+9TNKiHE~(>qgMxb^eLz z`u6ere8}L)`R>SKOCD~=2m{Ok;zkAJk7!S`mx9rnShrDjLN=Nbx)Q?fGfQSMBi3cKD&GE)1Trj5PT<;6~etc(bd&e&q%EH$kcR>VY@_2Q`4nuuJ`rzoPB+LOZ!(ROUa!9-z1*XRYG>n0tqO%yHz#B8FX{KRgdH(2twuj3b=KO9$}HcQ+KBiu&UJAad?;63<+AEgz-9mM z&*X1;eQWZMu1jqL;I6_b+4L6m=TJDXu#;7N)|bRs5dMt;uH+__Q2r>25raapuM3*a z{Ut_teqj^dbTUS?iV*2j;)`7p7yZgZh_`%bD_#{;fDBRVK zJornN(7BQ)P!Z0N0&DLcv`N@BV4V)_{SquF!kDol7??3VWUi9(`Z2pm&UB;sIvWmX zc#v9BQo=e7 zbq_uT1!GE%-y|W;vOB6T?i58Aqkr_dkUnyqx6yoS{7$Y*tX#<-S?y>1^y%+GIN;`h zJ|j>@b|8zHU*%Gl>Mz{&UwbZ*`B4>&RlfFeOiJYC z{t&I6tiY<}#BvSk(hBW(bt+3ib+bGvw;10f=$BM2gksb3pvaVk zBBf{*@mzsdY0d6ys-Gh5AFKH1$A67x{dPgXxs)~EJrmC45Lj}m*Np!R;i5%s;{Wvr z>kUc4m|SewjtK2}2z)YQd*QRs_DXm3?~|js+dmO$1XAMiHrE9oe@aj>w-RT4YHu0z zZweRQ*q20|oxv+Y3CoJqXDNxyv!e@l5HV4{V1NBV{W~RVywyQ(;O5G8LBbIyF^$kN zF7S~goNjjdUgh_#i4z_S^&qeV`*Rpujcn1U<1(XVujH;z*9e5_@|-`Je@%JUj;9&5 zr=y;A{hz^D<7t9)h9K>`!`BT+$4q5wZt6YVJnC_WdGQ%^?R5s7@@uoK|E|wA+Hppo zwA@@@WKvO4?FxecPK8-+D_jxaYy0$*G z__$i{&1avzHe&2!kENiK1*bqJA@`6Fb)*;IHUr_EEUm(2nn79xxjnJFcT$Puop?>W zVAlt;nlJp80($B*V6qvZY_A^wm7qSqdw!~|;Gpf@Sq@sXwCCo z-=-~LSS&9*n1&)Qa+)kL3pccHt8|k@a&Rf|D3lodnHM$r-e{xOE=}^WL$(59I=jyA zAyEb^3#H41!1}w1%f*`LBEkYx(LyFl042F@OqLa%H>~u=5J61#qP?UzkUEZVhg$^K zvLSr6sQBWx3Y<5ef24&XT!=37i>n~jGZ{Z^j+Cu_wXu-SR>g!S~_kH&Ckqe zVe)Wq$H?-DDVw2$proX)q~eEH?%K4$o|p8}-!Lf*CK^6#~U1(}BWuWWPWP2SDb`z1GR4UFRnPyTWzU zRwJld;_7`lZh=j)dE!-<@JdXniMfV`M!3SHR1C-ZTEN?)654{3&!GWF=r5i7B7gf| zu)R--$7YsKs!1fQ(7<2<0jf;iLkhGZF%Mg!fY*zmI&WDFJ7M9D>CBCmDlj#t!vos^Fdm)}o zsD5Cj^O!F1PqG3iyV1$>j_-W*>sUMJ%cQp}JQW?Cba#`g zCQzVogHBTJ0O)I};`H)_R~R^Um46bqicR7vz+RMNMA;zaJ(a{YhI2QY~IMfAgxZbb_!ibgVz`$As5oxpUw~> zu({VJRGsmxPO}xi?$6-Wr^~oJbwL7pS>JtWcbU?rQo}<0^>0XG+KC$dIFbhgM9I?j zTfzPjeXhy6Z!%;!xxUT~cY18$tPHIq&2>36fHb_Qt)p?%9tA{+y{|(zbZJE$M`2Cj zQu|;N7tx_f`8NRqcEs{TGV{OQf$@UP{IbU9KaOqO4rPge8;jJy&=s$E@{8J)2^K!5 zx4iZ%Mbx(IW#{~iN14ipX?cH{L$MU19fI3BE3(wUb?oO zsnXh)*Zj2zk7AG&UF0tF?@>Ri2N^~DwBPkK zDq&a%${Q4am?$_nl^QX`zIf?W`Tem+_m|jH{3A#@{I7XxG!gwVsS!pxp^pOqzHI%^ zWLYdLSYt>!sQt4MO1?uc?o1P>F&V{dyBF(3frm>{-xELioe(uK{en4CaQ@r*aUwCJ7x~BJ^Yb6#U!XKKj=P_F;Rg#LxS&JF zN#gljPb_=+*q@|}<9FYeytY>pwAFFO7HY2ufe~Q+Px9H%tV1p;pUiN|M;2f-M-3ZI z)tb3{ji?F5puC>_Og~|+&OjK2C^xbqgzTv#83EQLtklc0sim!HThB+{_>i?azG$^J z|6ahN66lCB2^;_K2`&=*tJ35dYUQ5-galNAKOq2H_TZ2IQV#jQftaKDtJJ%+I8cet zDE~;fn^hcNr^CzT-f83iUKa4;A!LyXZtCt*SAKNSh_i7OnWVMhh#G=0)3b|KT@6JV z9;kb}TYrK6Ig_!?81Gqb*U#PApVMvs?r`0W7;pA;$fz3z%fEJ)+Hb0e+%!}EGE^Dk zvxG^AN=f;#WJX)F-iSXL$_kKCYde~Y7~B19`oWub;s@@pnn+ZILc}7Z&-p4z=uyL` zl8BxYQeV=rXRJTJ7+j`p;t?~T+{(7+o9{bTJUysUY0KXKb)ym`{qyw;!RB+>7srfl zp+dOMC|D1uv0f5?)4VYpA12|tc&XQvFLwHB5CMP-mEc_~W9Zp9t3C&)NeeAxkD1uO z#1IP&h&;_+npxBNJu}B){5lQ`gxZ~#nE7wasd+426Vtou^*e0{e}8{RFJ*wDAe$kL zn_-um7YViI93A%99A&}9rjagPhFF+1io3~M;oB(+Tv{L2@mvW@7|2X6Z>1 zO1M#NgQz4V7CVH1i-675u@OBOq#er}!%BUpWdqxwd01+`LmaQ2lZKAgRDxA!`T)_c zQ10SFMYUQvJ^g$RH#Ymh41s#@E#nX6ycNl(ZK(?FCqE0Dec}`BdYH|4cwyDq3ktKe z`euX-!cP=#Zf=W%=p^!Zqd(WP%9*H$C5duvJ z6l-1+s|Ofvi)RE`M}hx-Ych=yl`KqcKTojsT)(jGE!s$bAQ%NHJ#do7Nsb7B0rq&ObfFZ(6c?gUYol|@n}4*Su+Zw`ryciN<87I` z3NL&S!3LV=PUiV*yKw5&S{LU5E+g<5-HeyZ=5ZH%gGJ=!;A7-;j2OK za2Tee8hPrf52;)vjtHWNXs(t6S;&#;U!8WSHNeh~qoBQ=$Bpl33~94+>rm@&AaW8b$~}rPI9Qi=imMYi zh%hZNkT)ei@MP=I^pSVLNpZLBw5nRnJyu1Xrp2M_mT!N5w{l7*|8xpLvfM?Tnz2{Q zC5gi%D#`NG0z5u1n#btbrO*DHOU3uet*mU$)SQyNOb%tHN)nRqc&RN4$9a7>rtKI5 zz5QG&C@#Q(YJ<$g<;W2Tt?Gj)_%hFZkexeB*gCR&zgOzAUJqFSEw z1TN>fBpKa??CkX9&Er~@uEIFe2S%C_37))_rD`9@`xQH|g~{Wk7M)>!&aI#_a3n+&;L(n-!yOa6?!H zV#u5yeAqFD5HSs2?XtW{y|y#sY5Z(|BuFx`bIk2bp&v~vu*npAVb*Gg*H&YA#A(;gZ7HvX7%YJ0* zYxZoR6B_XUucJpkm>ylRrS0PJXJ6eOwVKy77qmnT zwPkL`or<38_gm}WXKwCA$8tm=!$19a*T|6iF5r2kdBRVNs`IfQN+-SjGh_hFB|K0YT-AUVnCi(joEEBF~f;c3h-$xS!IuwZ}dx zw9_Mi6{QXJxNS$S^;XFTQr<>H4CgeKR!K7$g z2?EVUB3P3@=%mh9AYLX$P~ZRZNi{vrcDA^Z#PNVGGy@K=Ug zaB$%s9cQ<{+a0y&(=FY=_e~=HN}3lixDp}%0jx_Qk`UI(fC^bbuAb8QR#A;V!{8K= zub(f`1eKn0jZp&qM??4c5+12PgND}g>j$mFD^6driFFzpYQ;x3ZhOtovYgSv$X<6d z#fTWA?tB{=nMPZEE1acH7NrJ`zbCj>m$$fkHu0>dM((TUK1n0siKBJ%^BDe*;%v5R z{h9(XuHLY* zwDLm?;6?yuo5karJR*_@~Ah#=o2&rYZ<)Io^8{iwfxt6h?LDQIG=rpyMN_H zaivoc`ctns?cy!rt^@|-USo@c5A?ZQE;)B#xX_9h3t}sV7Q^*Nb8~OYeBA;y2i)fO zoZ03BQHIOU0@KK_Xsf^a67MQJbsScQv!Ng)bN<_u2zxN0@wNqV^&RYBHz2Fa#K{KT;SWTKi357cxNk{Cz0Zr9YNP-ZJ8B<^w_QRn@e|06MD zZ`>-0lWLU7@|xLl^^SlfyA-_y0$(hdtu|l#2>~r_smn1Q*8L;;?4A<|<$)N25ZQ5} z>Wp~{)lg3{#THWakk}+ZF7QzyW)HaMJLukcJ#6zXnJLiF(<|t-=z>8Gk@3RJm$8cK z5h1n~O~qNoT0YeBwk+$f;n-l^q}?h?LdIPDg*JpluC9TWu-+>Dj-^&dwx!J>wd_N4 zKZOY8g`}Q$Gi&qpXn_aEnDmd`>=udA*SlzaSx#B9JxW9tU_I$hB=|F@Q^gHFcyFRJ zKF-?cC?P}XSH1Tv`~v?yJ6nlnHj>I}PgLcK9nof!SXv*$sAoTPPSIc&?`>NRq>1_x z(EIb0iKkRVzDa_*x;J!7`%6cO;$+>~l=$pMZQT0-gL@Wnqphm*jzLGzZxV!_we*DJ z-x{pS5QUepN^9ck#C%2D&#$KVWOJb4lBjxu?SR(o0g0v}&wteDT$Y9FrcV8NM|?hi zOrXr-9o3J{Naeex``j<6&x?8j%{f?jF4n@={|(vI^uOLr!ikn6WD}r&*@{GGKtZNQ zWR!+}rl5@y5gKB(gWwr$YlT8=A-lt7b_a7}hr5*z>VvP&r48iovGLQaD3X@kjJg{x zd(dPhcc~y=OJj+$Ns= z$=`lM$`e2m-Dj=)$tJ(vjghevZR^&4AT8V*5UuP>Fx~gZ=08qrREfp~p(rjeX&x*y zH5X@uu_wba_3%3H{`KJ3?lRThCo^gLVfFXLCWY%zs(bJ29qGD%t>&xp^W@~L1atf4 zN%lJpyKO}O0@#WM??bTY?=Y)k0qYgAh)PR(jFk$KW(Q~))7P=v{)6uksuV>3=bRbP zermYiL)`uhm{*v0oI6J>b+%L;P>w@J_3&f@0CT;sdsH5;-TeleHCD&);m`Xpdzp||O^n-VWY=PkFRL9aEO zWyJb^OjlB_)&Q@ID`bKK=)Y>gE_Tv%D_m}Q=QC7%=x+T)91u07duJ z%XA5 zR#j@E>2Wf6e<_&n=JFcvgLsEeI9Hc+@Lt~&N)!>#(IcCBLm8%O2`#KGroX#+=G7si zdYYK1qE5UvQs`5oFxXwIRS`Fi)UtlDa3xN>#t4*b4kto-(>oQDaq&8q8}OXBw5_sR@kvUo`(T-mLtXQ=ORa%%8#Rr%D+oRDB2 zOH%V3kZr$!`lD4jQsL0I>5n8uo}ajA@O&KnLl#eAOfho(DqTdWKkYtiPAdo9sJnhi z_P$}%n$Jw&AH*+Pl&Ta$p81BEm@+QuU!v2?B@;ojl%yNCvM-#hY_zwg`QbVxrGv@)-Pb)Glc zeWWsb7H6`GB0!LY!!eJUGGaf+#g|eHb|j@|^eaxn+RTgve|{NvqfGh>!HD*p4x<)) zSa^BdRwVz>1c?ID%I}3(gJ^EH9{DfxL)tu|pu_1qrZ9f+19GD?shQ3%`oYg4L$4;c zs~y9kB>N{fmW?HBG!=A`?TbXm(A2EDJw@IQVUOU*eX+ac7ecx+1*0ZF->&SvdAuM| zOa@MPV)EcKQnrr0Dqg{t#H|5E0SF@= z%ox!Uy~b}yyCr!+aQifl&?J@^_7qiX$q3nZ=wTtIjpX>{uiW;!ZkFDlE1*tv2b>12 zg^nNcR4bVu>WVG@&`F;uwH}sQXL!#bhdXXXaolJV>`{c@1bbEa5=ichAANyhdC`xK6L;=MWlC2rly5WK;bX)}#$@4heY`HZAL&kFp$d$AT=-|0#^bH>4c zZ0mwXJjZr~ef>a+7;XGZ%#Riia>JX%;EK5819XHxm4FS*Ofz|)GS%?+1oalt+FB1( zzrz$~MYw{u3GakC9^?duq-k1-rcAPdMYT(74rk!O^@xr1B=|*;zveM+pDO zXRByQNBNu|-`?!e=N?x&I`npD(BP~x_cD|)*3WISia%ShwuN-Wk6M6mIST7JHtAU# z_3x0WU_eC^RIjD@%I}>A?&NBbUlj#7$!#B214d)tj`sOaf^pTPY6X7 zmEk`%#m8|AVFc)_&-m!g>9?8MabYvjbsM-Kw6LrFH4LTH;awd1C4h}Z*~)7=dHoAvcI{?ePJ4_2@8siCgFGcFA-1i zK?SpPq5zITG1Eh>uD-|hw(G)G>naGNS#S-6;05x8NPRAfAtRyYB`vS;Q&rVks_Ots z#5x3v2SdM^QCJ>6ZM{7{`6||YScw4y`$$QNY!e^t2_L;M4G%pCo^SF?i6dBvYJ<2; zM@$EVS&2+e-kDXXsy8)$erM*O-LS_Oqa+=(iJ^b|J56I6o>o!@3g`RE*=;-?*2$s1 zH{2O3g44oy)BYF7l{R@<|AafyNm-OtHSM92AOi1}^@b|5idvf=Au>gB8 zO%htFamtj45x|}2zZTW(Fu^Uw$Fz)hl;Bg)1RjE4rK2CbkcnLR*$_8KM`yJ`74p<2 zV5RfsjR3<_P^jXM_`nVFGh?Ft)py>Bb;Ad?VhV*#H%P!!bpHkDaG%A*2|YlqY6a>& z(5I9M3*G0RDkkdiFMei3`J;0umu~=q#c%etQ^66sI9Td~vHL7|iAW%NExPewpBMhk-d z#Q%>YHfO?&SCFLDG2#-*&@FbMp7?#`@;GZ7&ze4~dC3W9S&f1l_4dTAyjECX|AlGB5a&)e4JcTN5`tQGAT-22>VICCjU`0yf z8oOSaMY`LLERGZ8<4yFr(1oQ#VUUht?b#BgR!l`>)Z(2cS`sWaIQN%+v13_F&MK4br9w&>@UmPS?ZCym_r?7~;Y>!ia;_j+SueK~!!*8(^5v zLQ)8go_9Fk#87>(R{_C+S_=|_sU?;)z)@tYtq9?04QBGcl5J4FNdcsY zlDQfCfa^h<|6o{BU+jvBs0Jk*{q~ zGS(3jB6G_^bsTKONXPPCZRxgRjMtNj+s&%PId?m}?fgkD^Is~HBh#Du=mC`1*U!H9 z!sB6nj9eh}5pN+!*8G!6${dBlGRQ<>Mzu+4*abU~Oe$pZc(WjKHDY+2T)+YL)psr+ z*xYf|q;wOQoni+|ub#XB%jiqfbtm*CRmR=Jp*U_X91IMR<88k(L+%_{^Bjg7oY}*;-9`P`kl~Ua$o(h=lrhlQpwZ+6V`aqH(dskKVJ>NWc8q{e-6$wL9Tl zfhCU(dUs-jQU7J2;j0PZTeQU0Ph9KHYx2OerN2I5RK2tQr$z>APUwtvAT!bLg}+w7ersoIc=RE9c1(jJ*Yp zF>49V;`7*-+Nv;N9Z*C3Q@pg6_4u22_E6!n&x?0f^!~rl&gs65hl>$v))`P)=4kp2 zWOvZB1Ith^uTGyOq$%OCT10BmGSyR#iQAG>w0xml&6wcw_G{3E=6PA>Er$|Jsv^>l z91Yu0c%oM*|7R=Rq^wO`Jfy~Kh=82n8{PP zTs;@OnLx{BebeaRLkYY?#~|hJm^@?(;9`}%=?gQ+70o27n2XDR@&Ara- zX&SJ6abg{k=W=$5z0*WJ?^SlmnpSyX&-k(ucD=1qwexk~(nt z-~W)9cW!*oU)9URJ!Z&@J<1C8FaIlHWAl(r&vr-pc@7@fYvUv4|Fw_Xp$YBaZj>fP zaIf-k4Uu6#cD!635Du&;K?0|PPthcRvkQLu4YBC>bZvxO2&c0O-xvq*ZaEVED`j1W z72jzY9lR2S5-dX)Dl5tL2t_^0>{MQ-77;eJRj(37f5dv|&660LrcqMWBvK zh%)%^oxvxSE)=L*DIvlj-O7;Q&N4%YxOC#Hgwr;|Wz&i*8T2o)gMV`8mq)!(nuWYd zxT83dfX4?dxU$lx6}Kn^Mz@85_f>g#4(EB&0OFgDPVQ5I{`RVg(}Ps(YJwsE8n5Zc zZOK(9lv_I)>$$bv)DXWCCS{Xf^sg7yfiJ|whK&ROXR-2k9z;L@QY{3*&fhrM?i8QN z1$jc+dNwZFSv|9Hc`#kM3Mp9g=-huuLhtu<2m%AmkU74$wrgCMAJBVR42!J7*bF%K z5OzINIlNGWcf9Z_XiOzl&N<7d3rDiat;mP5`4PTjm&c7B7LA7%(t=jg*hKPjfOfUj z4eY5kKS6f@5ughVY$Ab-GLqd&d#-wl(7=w5!cm{zB%d1mV!sp5NBDktuRR5GmJc=}PX4W5pN)Kcv8CF8ly2OeE9b4TM^!5mmx z5O~GV^h^LDgfj2b=l2B4l<~?Y?$TY{vmSKwdcct_h1cp81Unc6+(5*JmEfGBb;*5?&&x}z& z3?K*U{F)#H>{Ca#E~UWPubqV0C2%OxSo{RBMt)G(RBEL1G?)koeiDv{lJKqdfIPxe zM{QfaSB)b}llQM&UM6JdGR0B_lxp8XKe!buRa#O<{TsjbTHHMbL&M3UrJY7CZu3@w zkjs)8wBfM-Qs%`K1QQt~ML4!Y0!*P<53n^bbd)GfM)f>`NqusL(H z1x^Pu%C}mG;V%BC3SynDy(hdyDdj$2Js0Y}ONG}HL)>(UB6CrTizyX#>{l0u5(%a)E zgjt%Q3EP=NQ+%A$FPa7l`X zxd0>h)4%c_jl5L?Zs=UrD#*DAQW!7yEfugdaU7 zR?Ts&?#OMh-BW{r>?oq;2eg+-1uF#5_jD%E;=6{wb6IyIBGHYtgn2+RD>-gszlNzw z`jC6OAY_H2$*AeYhYuqP_$?79!`JC`zhL0%+N;5SD=Lai%WrKtj)5O`MlV-xn&i(Z z;%#lOK8(HEc*RGr&w9;vX&_~kV& z7#&E)uh1iML^-bBl~5>2*&@GuK>QwexGcqyha2Mgi&25rbSI*B>X>;9v-VV|lO`B% zx{*4;EfNBjEFeVozb2%sr63(9I&_>c7ccIBb$6Cc3NO+#Si$`hj>k{qWkcNb`!8M3 zz3QcEtNz5_F%ftyabOGYp=h!WB0^2#fyF{vz>bEb#CPId>H;t-*rH3DSwrfW0iPou zz1UE=T#$>G_Z{|UQO9ej-Xf;17s$WQ@Q|s&N7eqi?9_+yoZK?r&sUL|KxUJ-0t)9s zVxJ5Wh;jiVdGL4851*++QqIR=;gh$&0ql3@u@#SA6uZDR|F`d*v#q&>eJPD0$$mu@ za0MFnPPP=-;_HZ0$a~nRM0s2M%~RVXF@M>WCkga*{^blLX-1g4L?P(?2M|8YX_@LD z6Y_cOz<=9{q6w1JDy*@Q_SN&xHgJxfX)S#b7K;h}8%Ad(TmdD>agxP-;>+loD|UBg z8#w;>xD!YddNo^gbTlF_A#axaXud&%%ul6$SbAe(_xS^*uR6KnQ*YstenvPC>Ebah zqzc{e{fxA1P0-Tbk6ITk&#pWu+Gt|-`pOF zd>LIAdC{~No(ctpri=P3mNIPmC;qvRU`qbn=vTjqTz1HddOt~l?l$~w&><2iF;R;n zUQ{wi1P3|8jL!doWrN+LvF(a>4cx4UUn-+jlGBzgz1~N(Uk00&M&7%b=^wLPLy$aQ z@ZbPKsG8QNR`O)nzGu5-VLvtatX}yj;WvKYWZvY%{X>8_IHClA>B)AZQ2OJ}@wyay z3dyCo-qJez?_p}C>o(yRe$OgDi>k*oDqOn2v6{!b3jsy$Plt_J=5Lp#GqN$E;5&ix zqejc=X68v~|62W`S+u^Sv0Z;9uJ(OkQkj3h;Z4%guSH1C>HPck3(otHm<-U;94<*> zQ*A+7li^64UZMCx)y#|0VCx3SDPJPzh|Uul!v7=H1NX zdw*c5Yb5RB(U-z)mLc4Q6{hpgzSthj40JvCTeL&uSK7tf8dO=v@G+Vc`hyWyC?aIYB2SIMl-jp&SHuYx_cNV68nB#RQrXXpa6QNMxQ+)sQsj!zx z`ti(Go0AcBg3x4#%Al1zv_LEKQ@s)yRA59Mzh`IY5b?bQd8U@%&dTSx&_wUG^bwLF_T9)+}lREZI%hR6Da`epm zd4QFIenDz}Vid3&3kX=}H2VeX z;m2ldb+jc}F-vSO|J)~Fh61BIIlk;@?+~iVuOQ~yQI@wz+1&L~hj(*}-0aC-zy1c>h=Qj6~$M)a|{t2Z}!u;sk>q;dlr$pXUyG)kHgxqC##+z4}^_qd8pO* zWAe%58X5j|T#PO`@WptVo9WPn(Y(7-2Oh_LUpAcW)DK4D4d8g}9aL=ajz(#NbX=RH z9-pQuJnRhsGTOk3pvjG7IcAw#szB|w75R@Q^pX_sy6<&wL*-whG&&bxRgMJgr|7lPoc({HRx;Hq zGN@!GTpfQwwXL3LU8Q4Xb>-pqQfG=oqAD4djogWrN-DCV<#-h0{B5uilibDAwJ(Ve zb3h^Reckx$HeA8&g*UJ5)S$o&Wj=d|x|)n$@iig0g?zHC{zVqF^miK(wjN7%Y$247 zG>mL!M`&g#3nMD0q5fJugp`G!R*;8(u6!n?WaJli1yA&XE&Z#-DOXK-sF8agPenb=) z^$?T@M5O{m@FkSAP0Q-?UG(Nwd<4p$&nPhN#-RQ}D>BtKl||98CT0Jp`l}u(1zn3K z&f4P}Z!~v52~cM$j^|>LAw%f;LVRt~M7W>c_39keQqrRYEjcKP#Fy&BZ?#IFWhexO zMA)r(Jj)b(oF6?bLugzvmRlZDKvI&}~pSrLv)+Hufzj;4J4{N-iA`K~qudo;3|EE&^$ z9Ow&62_|LEP+y%$RH8fOZIQ?35_bo@gX&Hm=jD+vkrMa@sMDB@N1LFL2xz{x1I|l2 zAwS3f{cSIIzf|D*nb#J+!f@!sruq}kmh4R~I4BoNNUmD~-wGU2`B4S(t9*O#>U~T_ z*BkwNOB=s_RVyz$z6l<{Ke0;1b#(u`2rI4<8~QN8E{GO?8#E`RKxJq6J=(1MoNTPd}5x zRH*i28rB3c=NlKB{3uP3yow0beB-xw_sy{IZs>_Y!KN`BJ+R`B`SY_Iq1~0%nSMZ! zyKG>G=rbUr8MmTu3F8q;^?ta27aDf!PMG%S(>V^UlOSmme~$&`xQe#-Wkq-+BAj6*<L0zdwT4p_FAn$(vP`EO-LMq7{Q zUN)WnRn*II(hY0(#a07zQ7etpbFa3&4_jyB5RDx6yhaKk9lfE4v_@TEsL21dh*3El z!ojIfSipyYKl7?8i z+|wFzbm%#z+**g#%_sJ$o3VA6tYn!!rYRomp=OpaVn;~1m82!{_Xb}Oe>V#P zR!bssE#G592^DtX?u+32)W}8AjH~uxl&avuW7ha~@~PWQ#nak?*L0z*?}&rDZYos! zB}GN`E)0avgL9#P(5cc_{0?FN&-+~;L)S7p3cq4HqFb{ZDctM(3;kENc}71iDoI&W zyz%u)w>zd9JX*2Yp-MQeE!uzAjgnedG>D4Q2t1!>ZzH!U*zVrA=(Xh-H?V_k;edk6 z$7P5bCZ(S}6NFE$xT_`HWet?{(@qnADTklhp~xgCkMEmB49kT3ePpqdr5s1&QyP(k zHLn(tZ8DCQJomb?N`nAL#?HR(RYeEvCN*a1?rv9NS~r1XFR#)%PziYQ)To5uy66Y>>Q?*5gx%*L4`8-0AV&I5*-nwhxV>I1G7DMz`~eQw^1 z!15+$yy>^5WNTdCCKZV_#Ve&$nCTL^%go}^PrZg*MOSg-58TtdXk2+gDafwD4mQnk z%uy`d|04e7Yt@CnV}K^}eGYv}gFMw0ML0(E9#&$9^!dY3TRroR`^^0W_}{+$N6F^t z)3h%%rx}oyl~*khXpHZ7Ns3Q(9hO^2!Pfb-fpkcx^B>nI1)J2uC(gWOx2Z<+{+=`0 zn3MytnTvqA86F)P5|>-iZ&E<{ySHk_9Y+5U>4er0I_g>Hf6u=xkb`xm-6@Rk302mQ z4I5P%#V@KKe@~B)E){i4=(SxP-?RF#Y0Ns(E%Y0i(j>%kR=CYud*)80k>oV7~zX0I2nJHBAYg?#jpsAAmvz z?mZz+>ZYM#;;x~if$~84{^^Zybk=e9a`ts|)bT|G=^#8k5t8)k zTsI)Oy7*~zlCx?AKoTSszRA;o(E~=%A+HmESC3^XhjYpa3&-BIVTw?=6KApBSwVZh z1UHHl1DCgClhT!KJ>2=#a<(0CIR{&rLHEN}UOXfT-`0Mn2~sBl1R)!xK}pY*g^C7id8wucW*_2OtD==;hBt-$s<77$8rKtmAgXj zBtB_@+8#M~Sq}LvnUjfX7}~{%mEmksiWqMFJ%;B>{eXFCE){7iq$^oJWA9YY;|3dx zM8;S346Sye@W zJ)Rh<#ZtLKoO^~cUw*0&3BB$oGV4T3QvR6EwQw>Q6MbQqo*2X9qMSy!Oy{l9RT?;E zi3S&LPdrGuMvM`Nq#4uY!Hd%#@_3sOxlPd}jJ`}uu(U3#k5G0E8^EQ7JQvGBG}6{b zt2VJRe!MxAh-#4NZ|nN7oX|B-B&Wg8AI+)NW+FdS zUlgRg>&gcUvwA4&aU52M5O+KhKF=Ab9Js92&@OjM3vA5Pl&q+C3WwaOijjP9rd0z$ zs+EX1suZNxQDuPa>6xA~kGk@K}750*$y%+7+)}-7urBlx|+%xu|$1us5RQKaKYiZvX2_oc| zAHhXa!%D7vSyFv*oiBN$-H-MPuW2~xzsc_ye99w}ysS%E6wjU%u#UlAm(o4D12ci= z(3EIo3Rsx)(kz)2PWRcg=abo( zT6uCxNa~!V`B)@xx05-QiX+ER!m2J6ufn524Jv+Z`j3d6d``hSg6^a*`1EZKp8sPj?7;6;~}H@3ms#fZS}y3#68NT-Z(YgmO@?O z&&=IxqJdK1N2ZXK%_6fY--wJgmtgv#p@A=OO)5Ovo;J~VRoD{|dr}<5j>GjfMk``? zU}>|Ot#TXpi#LcLPoI+Xh}Vwb{E1!T87n<0tN^`g{8lvM;nWV4lQiq9FgfyP;RV&g zVYJ}zPq$4uBSPiWWdL9llMj; zsWrw7B$7nkZBa-gA1=*PoVeG`v?x1_-3PYvqD-iYaMxd7TMvJxmU){pUOQ!|#8p;- zK}`G8V9}}q{`zd3EUtKv(p7OfLRsxpBHRt3oguUQM52aHl;%@gt+^Kk>up9c`Enj{Y+Rm{f&0 zTO6Bcn4xuB(Cp@YiWmwP#Mn0I9grTYEe;8BXenesW8Pr(G1-_?j3Cw$LyJAujyDtz6-nSq%GO@9;WEoJ3mR3?x%b{LTlYQ88mPnLJ@6rQ^!TW| zdv)V(TN4V!V-YU)JOz-glS|UE>GVJxUGu4azUY2{)hkeXisMO5oDbc??(x&dcd2{c50hRV~ z!uMyTtp47kx6q?AQ%XZ2^~bV%vU`sx_2bAXu^qv2Bh2s9wA21dlTRxXOMo9*t--I| zrKqrg=T-+BKnvfjf=(1p*n;!w8vP6FJ_MH?Xl)R2=wvfyn`Vc~{_>RZ%&fPrFY|tCPO8y8 zb|Uqs5cHLTkyuXfqpT_EA-v;AT`~FBEdK`m!xh7P;BRZt}vK9}jGp3fVWjZXPaspX>P zpyvNqnRUq<8m4vTSjsQ4IdRM^Fku?VR|$^1zT8<;q-`y(5KZIWKi=3q!-j@0PxXsr zPspesFDkEPQTno+S@qtwwLaWzeM-yrsRB@llY^ z?qtUXOKaJ@`I$-2>+;f&=BVbym@1`~yT8Uxi>&n_;61zgAbFq0z~gu(HIWAuqqf%X zets*(ma$umSX!1}IQH$!tdw<{ds>$@TGZ4#+J0{8Jb18?UKQ4?R-OMoSpA?Sewl*> z71D@(ySBbO-**5PtvB-u+N=Aq0f+O}%Z|#8ntAeiiA)}TIvm}qP|Q$Vzu$LX#Qkzc z=<8ICh|ff(LS9qT>~eGZmfEk|hx?nkwZDEs^XH_;o5xVIF8C^6mC35f%Oln02oACW z+ImhTSLx;W#h3V)16oUIAhi~Cs1B))0{aL@m9&;jo%AV``=bB6IajA5!Gc>#seZFz zdAD|9$Cxp1RYgdC>Y?!I=*P4*u>fOZh0t?@51DZ;1p_ZmmCsH`D$2(hR*3s=!FB># z*2tD=b-;o4uC9Cc70(JL5@I01_8tw3=#t&qouy@wg(}WgcE5wsx4 zmb820;n3ol&4!NVu~4|*=~Ql{B1yo?$GFgr(A-mV+RxmhXYTv0hyH;9oEubI9;ZTU z#m%&hw0UQur^lX_Q;<>nQZ8#)>Bixv@-)L&^zrUum#s0M@%7+OVPoh13x5_D&s{K| z!Qpd-EtM|AE<0iNKWFxj`&By6hlaW5F6Xtr#zmwvRTsI57Ya;|3u8 z%`qbQtK&ehtDJv4F^>3O1|5#%|1p69=+5}~n&2qBb*<3=08RUq5j_{7;UH9Sax=5? zvobVLa71~65l$$iGdR%G`zi~d6sSOep3Z&%BLB9JJ5KNtvf)#%^z zdrnX9e;awBf2$(sg9IYHArfG5$iI~diAn^G3L3u72tSms84Bg0d^MVzmpj@K;q81i zpc3SN8NXX9L9XWbuetsa{dbh0TKNVc^FJ@Be51Ap$wPQlO+8IDGgdZ72OQV@Qgl`a z`78N9oY}YnE>$z^Q#w8*n?Ii4>m#aJ`4OQjKo>#PMx4hLj#oM&O>RTnCLx)*>$J64 ZOT@)Vy}*8Qc(tJe^t6mLE7ct${|}UXKu-Vw delta 684 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 literal 41970 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

!T_l`QfK2`r6{hY{NOa~;^gL-W61j8c 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 L2I|$S_EG-_ui0Q5 delta 1916 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 YU8sQb_mr@cqS-3xJ&KzqQ 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 literal 110342 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 literal 5479 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 From 1dd7ea47e9ecfac6cc719061c2d26f21a87876fa Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 18:54:30 +0800 Subject: [PATCH 48/89] feat: use logo.png (1024px RGBA) as macOS app icon source Co-Authored-By: Claude --- .../AppIcon.appiconset/1024.png | Bin 28322 -> 24454 bytes .../AppIcon.appiconset/128.png | Bin 4931 -> 2581 bytes .../Assets.xcassets/AppIcon.appiconset/16.png | Bin 4022 -> 763 bytes .../AppIcon.appiconset/256.png | Bin 6468 -> 6194 bytes .../Assets.xcassets/AppIcon.appiconset/32.png | Bin 4113 -> 924 bytes .../AppIcon.appiconset/512.png | Bin 11978 -> 15627 bytes .../Assets.xcassets/AppIcon.appiconset/64.png | Bin 4345 -> 1414 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/macos/GostX/Assets.xcassets/AppIcon.appiconset/1024.png b/macos/GostX/Assets.xcassets/AppIcon.appiconset/1024.png index 2b9dd41a2da43486221803ddfe85a628e46074c2..f6a5a6742d95ce183afb33b9a942df5d11d941f3 100644 GIT binary patch literal 24454 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 literal 28322 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 diff --git a/macos/GostX/Assets.xcassets/AppIcon.appiconset/128.png b/macos/GostX/Assets.xcassets/AppIcon.appiconset/128.png index 90a8e1c4e5b99c38bd9b99c946e1be7c1ac8b0b0..4e8eaf9cf8249402ffe275f20b301777f68e9ae2 100644 GIT binary patch literal 2581 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 delta 4545 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 diff --git a/macos/GostX/Assets.xcassets/AppIcon.appiconset/16.png b/macos/GostX/Assets.xcassets/AppIcon.appiconset/16.png index 40ced6a5ec57ea3e33744671fef00b45fc76a1a7..ab5a6b2d19135081fd3c0dd3edef063882c9f7b1 100644 GIT binary patch delta 317 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 Bylemf delta 3721 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 Q2zaR~YATf6F@O4h0IT*@Pyhe` literal 11978 zcmeHt^;aFuvgqJ0!68`C;O-tgxVuAeci$wzgS)$1aM$4OZV4OL;INU$cka38z4iWt zyKb-U>6+^DA9_tyO}Mh6G#WAyG5`Q*vN95?008|4LIa3zPhbW+lI9H$Yb`FWY%4A; z?r7)eqUK~`W+83iVBunIChcP4Ep1|NZ^CZG4glK0MOp~@2}^k0Tf;#7&ypWM<19pj z0Coxv(8-6EFj?{-OjCI1Kc6CSEA@zZ>FFbM^>KsvRU$Qy2CFb^%5vvTnV2iPGGH+) zPJPesx}MHFU$=zz*Ig%t_OpFqKxb0X5)_}H0B_S)PKvlBVLG92ifstPJp@)Tp1Bjh zN2x0B?+J#X`yWh&@4?>AFyx~z=@~B7N{6ram|lK___wqs41Y#iyaDnZDDn(vYE?df zC}F$hl(AJJT#lT3SV0jr$cqo0hdN46R)N@+%7W{1_)b;vozT~Wl%YJ|Hs9Huq6eK% zGnSPvfjNS4rrJDqI64lhoF^N3&lZA;rC1O#zt}M(xv!~6&W=H>TzG$s0cC-3zfTnX zDwtO|t#piG1|O=Y;zLSAi4p&j1W~Al4?%&_a8Tms$hp7q((s7r(%Hf|CwocRW$NPJ zQfuMGL)BvUGeHaM5poEGk;z?QCkajh+nl5#oT#Q&2$MLYD)U9ZD&C3f_{a(~l)as-^88 zaj|yEUnMp5PG-$SbLQ=DhnpRiXzcD64wp%?bW)HxBevH5?xP?RtSV_5u_`_5(Zwst zCo=wS7A7{r2_OT1BfOY^Go3~{5QYDqD;it}g#hlA3dL*b!-Hz=|0(++6ct>~TbOH` zDomA^=nR7;GlrlOs|8CPI(P^|uvC_#w;Tyocqqzj)#Dl@nLj|+3)@A{1sOq4qZ~qq z(R@W;konNSigEkFNgc|11v6$oKRHHAr=lrH*eY-;H`ySK7^T{z^@eeOSB=GtO^R6!!+#P;(}3Ofp-rv@ zA+S00^Z^s*W)*e!`zkZ@SG2XBuj!KXHl%i+aAWLvNs+^LFS3f_Js@(76vBV4sDuKw zeYx!(0vk>f{&wJfG$WejE_1u3;z zwJCoy|89#v5%3Upv#%2Beb_~6LKgTz@+u=N z921d)`5!oK@2?8s#LeaE^9H7WeBg@TlR+tpB8u}o3JZtlkhxb8Qr62vFB4Cr(bS+s z2dld{!9PD YU8sQb_mr@cqS-3xJ&KzqQ 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 diff --git a/macos/GostX/Assets.xcassets/AppIcon.appiconset/64.png b/macos/GostX/Assets.xcassets/AppIcon.appiconset/64.png index dbad30937f7389d897f3f725f7f4a3075e12af20..5372a88fea434420a39ce7ef0fa2d07a4dea09f4 100644 GIT binary patch delta 974 zcmV;<12O#hA%+W(85sru0063Kaozv`00DDSM?wIu&K&6g004NCVGTu4zyJUM0RR91 zzyJUM0RRJ_0RRI4000310RRA?0ssU600031002Os0{{d700031002M$0001y7A4*Q z000SaNLh0L03C+_03C+`kLB@Yv+50U0e=HGNklgAFJt?%Oy=F6MA@fAR%^Np~H->KWArW;^yW?bUGdJ z{{AlG+%&XxyIs`>eRtv0=N#?^9}0y;xm*^r*=#f7Z~mIk=V}bbx=lpDVnP09x6{*8 zs#dF%&*yFI*Vk8)Dxl$TNKa2ss((EmkJI(_HD$9|ibkV$-&U(dg+hTOA$FS^A6#Ks z1}_GKfxXUeZ*SuE_Eub6Tzokn{#U6~1o~PA3}DH?-FoY_)z#IH z13+Jlu?$$ibcqCh0!pQl<-54MyAx94zjhvDoFoAAAP9it&(F__07yBNN`L*?@yp9g zdpj%^i$4+h1JZaEfTPYb?D-!!@NFMpSO#oeye;GT2nYs)qTlZ;f92&yBoYa2hk*es zSg+R;m}5&2I&FLe$lIM*tyap}Mx&wQ)W+Cmv#BtFjX5|8V9f^QF-GBVSo%lmoL3(h ze#oOxp#U3ma2hKC&P$AS$$#N+)`{hug9DqD01k(CjloI)vO3Ffz2!0|mU9jcd{zSP z@9#;zsFYusOoqe14J=?IZRR$%wB#bN^2R+#Qp7}sbkGd!59Fi7A^hLA{Eb}-2h2!B8&$JgmFNH{q; z`BOmz$&V#_tiIa?k-e3~MxzluK0ZpWh-AV;>2#WMxt#sNL;FFO#H9b~<%;cq{rj7u zDEgw1x{IPHh-N(nzQRLVi=f)=_LpUGl+2Oy@3JiJ>J|XI+YDcdIKLQqd3mAfbh?)> zM)pF$Hd6N0#(ERnS5pu=^BMwl*z>|zqW~|gqD~A(0Xpn?VXRSr7gkXx2BQES_Pj9G wD8LJ=s1t)xfDU_J7;6;Zg;msv!S@RI0O$PTOn{0WaR2}S07*qoM6N<$f;UX5WB>pF delta 3955 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 N002ovPDHLkV1fk Date: Tue, 14 Jul 2026 19:01:00 +0800 Subject: [PATCH 49/89] fix: add ConfigRepository and restore updateListen to MacExtrasConfigurator - New ConfigRepository.swift: multi-profile YAML config manager with UserDefaults persistence - MacExtrasConfigurator: restore statusListenItem and updateListen method - Xcode project: add ConfigRepository.swift to GostX target Co-Authored-By: Claude --- macos/GostX.xcodeproj/project.pbxproj | 39 ++++--- macos/GostX/ConfigRepository.swift | 131 ++++++++++++++++++++++++ macos/GostX/MacExtrasConfigurator.swift | 17 +++ 3 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 macos/GostX/ConfigRepository.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 8c0f41b..a01e170 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -7,14 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; + 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; - 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; - EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; B523FCA529E169180050578C /* HighlightedTextEditor in Frameworks */ = {isa = PBXBuildFile; productRef = B523FCA429E169180050578C /* HighlightedTextEditor */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; B532A66E285B528D00159BAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A66D285B528D00159BAC /* Assets.xcassets */; }; @@ -29,6 +28,8 @@ B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A429DD6E90008AA944 /* FileRepository.swift */; }; B5C1CF1429DD6E7C008AA934 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */; }; B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; + E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; + EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -70,12 +71,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 430349433004D3A000FD9EE9 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; - F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnManager.swift; sourceTree = ""; }; - 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; 4303495C3004D78100FD9EE9 /* Libgost.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libgost.xcframework; path = Frameworks/Libgost.xcframework; sourceTree = ""; }; + 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigRepository.swift; sourceTree = ""; }; B532A666285B528900159BAC /* GostX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GostX.app; sourceTree = BUILT_PRODUCTS_DIR; }; B532A669285B528900159BAC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; B532A66D285B528D00159BAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -94,10 +95,11 @@ B5C1CF1529DD6E7C008AA934 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; B5C1CF1729DD6E90008AA934 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; B5F6387829DEFF31009A1287 /* Arguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arguments.swift; sourceTree = ""; }; + F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, @@ -107,7 +109,18 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; + 430349453004D3A000FD9EE9 /* GostXTunnel */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */, + ); + explicitFileTypes = { + }; + explicitFolders = ( + ); + path = GostXTunnel; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -185,6 +198,7 @@ B5F6387829DEFF31009A1287 /* Arguments.swift */, B5B1C1A229DD6E90008AA942 /* FileManageView.swift */, B5B1C1A429DD6E90008AA944 /* FileRepository.swift */, + 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */, ); path = GostX; sourceTree = ""; @@ -242,8 +256,6 @@ 430349453004D3A000FD9EE9 /* GostXTunnel */, ); name = GostXTunnel; - packageProductDependencies = ( - ); productName = GostXTunnel; productReference = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; productType = "com.apple.product-type.app-extension"; @@ -414,6 +426,7 @@ B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */, 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */, B532A66A285B528900159BAC /* main.swift in Sources */, + E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -691,9 +704,7 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.0; - OTHER_LDFLAGS = ( - "-lresolv", - ); + OTHER_LDFLAGS = "-lresolv"; PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; @@ -732,9 +743,7 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.0; - OTHER_LDFLAGS = ( - "-lresolv", - ); + OTHER_LDFLAGS = "-lresolv"; PRODUCT_BUNDLE_IDENTIFIER = cn.liukebin.gostx; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; diff --git a/macos/GostX/ConfigRepository.swift b/macos/GostX/ConfigRepository.swift new file mode 100644 index 0000000..fc709a9 --- /dev/null +++ b/macos/GostX/ConfigRepository.swift @@ -0,0 +1,131 @@ +// macos/GostX/ConfigRepository.swift +import Foundation + +// MARK: - Profile Model + +struct Profile: Identifiable, Codable { + var id: String + var name: String + private var config: String + + init(id: String = UUID().uuidString, name: String, config: String = defaultGostYAML) { + self.id = id + self.name = name + self.config = config + } + + var yamlConfig: String { + get { config } + set { config = newValue } + } +} + +// MARK: - ConfigRepository + +class ConfigRepository: ObservableObject { + static let shared = ConfigRepository() + + private let defaults = UserDefaults.standard + private let profilesKey = "gost_profiles_v2" + private let activeProfileKey = "gost_active_profile_id" + + @Published var profiles: [Profile] = [] + @Published var activeProfileId: String? + + private init() { + load() + ensureDefaultProfile() + } + + // MARK: - Persistence + + private func load() { + guard let data = defaults.data(forKey: profilesKey), + let decoded = try? JSONDecoder().decode([Profile].self, from: data) + else { return } + profiles = decoded + activeProfileId = defaults.string(forKey: activeProfileKey) + } + + private func save() { + if let data = try? JSONEncoder().encode(profiles) { + defaults.set(data, forKey: profilesKey) + } + defaults.set(activeProfileId, forKey: activeProfileKey) + } + + private func ensureDefaultProfile() { + guard profiles.isEmpty else { return } + let defaultProfile = Profile(name: "Default", config: defaultGostYAML) + profiles = [defaultProfile] + activeProfileId = defaultProfile.id + save() + } + + // MARK: - Profile CRUD + + @discardableResult + func addProfile(name: String) -> String? { + let trimmed = name.trimmingCharacters(in: .whitespaces) + guard !trimmed.isEmpty else { return nil } + let profile = Profile(name: trimmed) + profiles.append(profile) + save() + return profile.id + } + + func deleteProfile(_ id: String) { + profiles.removeAll { $0.id == id } + if activeProfileId == id { + activeProfileId = profiles.first?.id + syncActiveToAppGroup() + } + save() + } + + func renameProfile(_ id: String, newName: String) -> Bool { + let trimmed = newName.trimmingCharacters(in: .whitespaces) + guard !trimmed.isEmpty, + let index = profiles.firstIndex(where: { $0.id == id }) + else { return false } + profiles[index].name = trimmed + save() + return true + } + + // MARK: - Config + + func getConfig(_ profileId: String) -> String { + profiles.first(where: { $0.id == profileId })?.yamlConfig ?? defaultGostYAML + } + + func saveConfig(_ profileId: String, yaml: String) { + guard let index = profiles.firstIndex(where: { $0.id == profileId }) else { return } + profiles[index].yamlConfig = yaml + save() + if profileId == activeProfileId { + AppGroupConfig.writeYaml(yaml) + } + } + + // MARK: - Active Profile + + func setActiveProfile(_ id: String) { + guard profiles.contains(where: { $0.id == id }) else { return } + activeProfileId = id + save() + syncActiveToAppGroup() + } + + func activeConfig() -> String { + guard let id = activeProfileId, + let profile = profiles.first(where: { $0.id == id }) + else { return defaultGostYAML } + return profile.yamlConfig + } + + private func syncActiveToAppGroup() { + let yaml = activeConfig() + AppGroupConfig.writeYaml(yaml) + } +} diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 296697b..f78ba15 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -16,6 +16,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { public var statusActionOnItem: NSMenuItem public var statusActionOffItem: NSMenuItem public var statusActionRestartItem: NSMenuItem + public var statusListenItem: NSMenuItem public var profileMenuItem: NSMenuItem = NSMenuItem() private var menu: NSMenu = NSMenu() @@ -40,6 +41,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { statusActionOnItem = NSMenuItem() statusActionOffItem = NSMenuItem() statusActionRestartItem = NSMenuItem() + statusListenItem = NSMenuItem() super.init() @@ -53,6 +55,12 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { menu.delegate = self menu.autoenablesItems = false + // Listening status + statusListenItem.isEnabled = false + menu.addItem(statusListenItem) + + menu.addItem(.separator()) + // Actions statusActionOnItem.title = NSLocalizedString("Start", comment: "start the service") statusActionOnItem.image = NSImage(systemSymbolName: "play.fill", accessibilityDescription: "") @@ -183,6 +191,15 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { delegate.start() } + public func updateListen(_ listen: String?) { + if listen == nil { + statusListenItem.isHidden = true + return + } + statusListenItem.isHidden = false + statusListenItem.attributedTitle = NSAttributedString(string: "\(listen!.replacingOccurrences(of: ";", with: "\n"))") + } + public func toOffState() { self.statusActionOnItem.isEnabled = true self.statusActionOffItem.isEnabled = false From eb73359cc979e64e50ee14f7de5d88da5b5eab07 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 19:02:56 +0800 Subject: [PATCH 50/89] Fix tun for macos --- .gitignore | 2 ++ libgost/tun_stack_darwin.go | 10 ++++++++++ libgost/tun_stack_default.go | 8 ++++++++ 3 files changed, 20 insertions(+) create mode 100644 libgost/tun_stack_darwin.go create mode 100644 libgost/tun_stack_default.go diff --git a/.gitignore b/.gitignore index 1b46bfb..1145d23 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ libgost/*.h libgost/*.a libgost/*.aar libgost/*.jar +libgost/*.xcframework libgost/build/ # 个人配置(含敏感信息,不提交) @@ -16,3 +17,4 @@ android/config-templates/*.yaml android/app/libs/libgost*.aar .pi-subagents/ macos/Frameworks/ +macos/build/ diff --git a/libgost/tun_stack_darwin.go b/libgost/tun_stack_darwin.go new file mode 100644 index 0000000..cfbd47e --- /dev/null +++ b/libgost/tun_stack_darwin.go @@ -0,0 +1,10 @@ +//go:build darwin + +package libgost + +// On macOS with NEPacketTunnelProvider, the "system" stack (kernel TCP +// with IP header rewriting) does not reliably deliver rewritten packets +// back to the kernel TCP stack on the point-to-point utun interface. +// sing-box also defaults to gvisor on macOS when includeAllNetworks is +// enabled. Use gvisor (userspace TCP via gVisor netstack) instead. +const tunStackType = "gvisor" diff --git a/libgost/tun_stack_default.go b/libgost/tun_stack_default.go new file mode 100644 index 0000000..17093bb --- /dev/null +++ b/libgost/tun_stack_default.go @@ -0,0 +1,8 @@ +//go:build !darwin + +package libgost + +// tunStackType selects the sing-tun stack implementation. +// The "system" stack is lightweight (kernel TCP + IP header rewriting) +// and works on Android, Linux, and Windows. +const tunStackType = "system" From 2fe77234b53a14ad8b7e6218299ffa0b8ebeb673 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:11:40 +0800 Subject: [PATCH 51/89] revert: remove statusListenItem and updateListen from MacExtrasConfigurator These were accidentally restored during compilation fix. User had intentionally removed the listen-status display from the menu bar. Co-Authored-By: Claude --- macos/GostX/AppDelegate.swift | 3 --- macos/GostX/MacExtrasConfigurator.swift | 17 ----------------- 2 files changed, 20 deletions(-) diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index 58c3afc..d7c8683 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -79,7 +79,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { } else { LibgostStopGost(nil) } - self.menu?.updateListen(nil) self.menu?.toOffState() } @@ -121,8 +120,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { return } - let statusJSON = LibgostGetStatus() - self.menu?.updateListen(statusJSON) self.menu?.toOnState() } } diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index f78ba15..296697b 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -16,7 +16,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { public var statusActionOnItem: NSMenuItem public var statusActionOffItem: NSMenuItem public var statusActionRestartItem: NSMenuItem - public var statusListenItem: NSMenuItem public var profileMenuItem: NSMenuItem = NSMenuItem() private var menu: NSMenu = NSMenu() @@ -41,7 +40,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { statusActionOnItem = NSMenuItem() statusActionOffItem = NSMenuItem() statusActionRestartItem = NSMenuItem() - statusListenItem = NSMenuItem() super.init() @@ -55,12 +53,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { menu.delegate = self menu.autoenablesItems = false - // Listening status - statusListenItem.isEnabled = false - menu.addItem(statusListenItem) - - menu.addItem(.separator()) - // Actions statusActionOnItem.title = NSLocalizedString("Start", comment: "start the service") statusActionOnItem.image = NSImage(systemSymbolName: "play.fill", accessibilityDescription: "") @@ -191,15 +183,6 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { delegate.start() } - public func updateListen(_ listen: String?) { - if listen == nil { - statusListenItem.isHidden = true - return - } - statusListenItem.isHidden = false - statusListenItem.attributedTitle = NSAttributedString(string: "\(listen!.replacingOccurrences(of: ";", with: "\n"))") - } - public func toOffState() { self.statusActionOnItem.isEnabled = true self.statusActionOffItem.isEnabled = false From 12c629bda155638a6459f7a90701eb48d3dee092 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:16:14 +0800 Subject: [PATCH 52/89] perf: build macOS target as arm64-only, halving binary size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gomobile: -target macos → macos/arm64 (72MB framework → 37MB) - xcodebuild: add ONLY_ACTIVE_ARCH=YES (strip x86_64 from app/ext) - App size: 130MB → 63MB Co-Authored-By: Claude --- Makefile | 2 +- libgost/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8e94f82..fa738f9 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ android-release: libgost/libgost.aar cd android && ./gradlew assembleRelease bundleRelease macos: macos/Frameworks/Libgost.xcframework - cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release -derivedDataPath build build + cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release -derivedDataPath build ONLY_ACTIVE_ARCH=YES build libgost/libgost.aar: cd libgost && $(MAKE) libgost.aar diff --git a/libgost/Makefile b/libgost/Makefile index 9e8cf73..4ebd4cf 100644 --- a/libgost/Makefile +++ b/libgost/Makefile @@ -53,7 +53,7 @@ debug-symbols: macos-xcframework: CGO_ENABLED=1 GOFLAGS="-buildvcs=false" $(GOMOBILE) bind \ -tags with_gvisor \ - -target macos \ + -target macos/arm64 \ -trimpath -ldflags="-s -w" \ -o Libgost.xcframework \ . From ded33f4071f145fb33567f38401421108e0f7b99 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:23:51 +0800 Subject: [PATCH 53/89] fix: set work dir in proxy mode so gost can find imported bypass files Proxy mode (non-VPN) was missing LibgostSetWorkDir, so gost looked for china_ip_list.txt and gfwlist_domains.txt in the wrong directory. Now points to App Group files/ like the extension does. Co-Authored-By: Claude --- macos/GostX/AppDelegate.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index d7c8683..6dfa693 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -113,6 +113,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { private func startProxyMode() { let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML + // Set work dir to App Group container so gost can find imported bypass files + if let containerURL = AppGroupConfig.containerURL { + let filesDir = containerURL.appendingPathComponent("files") + try? FileManager.default.createDirectory(at: filesDir, + withIntermediateDirectories: true, attributes: nil) + LibgostSetWorkDir(filesDir.path, nil) + } + var err: NSError? if !LibgostStartGost(yaml, "", &err), let err { logger.error("gost start failed: \(err.localizedDescription)") From 5fb82a43c26ea4a4c8f9d79d002a02bcc30b98d8 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:25:54 +0800 Subject: [PATCH 54/89] fix: use tunStackType (gvisor on macOS) instead of hardcoded system stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS NEPacketTunnelProvider requires gVisor userspace TCP stack — the kernel TCP header rewrite approach doesn't work on utun interfaces. Co-Authored-By: Claude --- libgost/tun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgost/tun.go b/libgost/tun.go index 1328166..c6d0244 100644 --- a/libgost/tun.go +++ b/libgost/tun.go @@ -163,7 +163,7 @@ func startVPNSingTun(fd, mtu int, chainName, dnsServiceAddr string) error { router: router, dnsServiceAddr: dnsServiceAddr, } - stack, err := singtun.NewStack("system", singtun.StackOptions{ + stack, err := singtun.NewStack(tunStackType, singtun.StackOptions{ Context: ctx, Tun: device, TunOptions: tunOptions, From a8b85b9d52253eae175b2c268738909a0517cc2e Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:35:08 +0800 Subject: [PATCH 55/89] fix: properly implement gvisor TUN stack for macOS - Use tunStackType constant instead of hardcoded 'system' (gvisor on darwin) - Add stack type and interface name to log messages - Fix double /24 in tcp_listener log output Co-Authored-By: Claude --- libgost/tun.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libgost/tun.go b/libgost/tun.go index c6d0244..c580c30 100644 --- a/libgost/tun.go +++ b/libgost/tun.go @@ -131,10 +131,10 @@ func startVPNSingTun(fd, mtu int, chainName, dnsServiceAddr string) error { if chainer == nil { logrus.Warnf("chain %q not found in registry – traffic will route directly", chainName) } else { - logrus.Infof("chain %q found, sing-tun system stack starting (fd=%d mtu=%d)", chainName, fd, mtu) + logrus.Infof("chain %q found, sing-tun stack starting (fd=%d mtu=%d type=%s)", chainName, fd, mtu, tunStackType) } } else { - logrus.Infof("no chain name – system stack will route directly (fd=%d mtu=%d)", fd, mtu) + logrus.Infof("no chain name – stack will route directly (fd=%d mtu=%d type=%s)", fd, mtu, tunStackType) } router := xchain.NewRouter( gostchain.ChainRouterOption(chainer), @@ -182,7 +182,7 @@ func startVPNSingTun(fd, mtu int, chainName, dnsServiceAddr string) error { device.Close() return fmt.Errorf("start sing-tun stack: %w", err) } - logrus.Infof("sing-tun system stack started (tcp_listener=%s/24, dns=%s)", + logrus.Infof("sing-tun stack started (type=%s iface=%s tcp_listener=%s, dns=%s)", tunStackType, device.Name(), tunVPNPrefix, dnsServiceAddr) tunStack = stack From 02be582929e29a6e36f3eb03bf6fa9501d3f6b8b Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 20:39:05 +0800 Subject: [PATCH 56/89] fix: handle device.Name() returning (string, error) Co-Authored-By: Claude --- libgost/tun.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libgost/tun.go b/libgost/tun.go index c580c30..dfa39e3 100644 --- a/libgost/tun.go +++ b/libgost/tun.go @@ -182,7 +182,8 @@ func startVPNSingTun(fd, mtu int, chainName, dnsServiceAddr string) error { device.Close() return fmt.Errorf("start sing-tun stack: %w", err) } - logrus.Infof("sing-tun stack started (type=%s iface=%s tcp_listener=%s, dns=%s)", tunStackType, device.Name(), + ifaceName, _ := device.Name() + logrus.Infof("sing-tun stack started (type=%s iface=%s tcp_listener=%s, dns=%s)", tunStackType, ifaceName, tunVPNPrefix, dnsServiceAddr) tunStack = stack From ae4eb5d91a237d0cc25a8286704ccc56ec2f1cd1 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 21:43:33 +0800 Subject: [PATCH 57/89] fix: restore vpnMode auto-detection and ConfigRepository - vpnMode now reads ConfigRepository.activeConfig instead of UserDefaults key - ConfigRepository.activeConfig changed to computed property - VpnManager.start adds diagnostic logging - Fix main thread checker warning in startVpnMode --- drawing.svg | 4 ++-- logo.png | Bin 0 -> 24454 bytes macos/GostX/AppDelegate.swift | 7 ++++--- macos/GostX/ConfigRepository.swift | 5 ++--- macos/GostX/VpnManager.swift | 8 +++++++- 5 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 logo.png diff --git a/drawing.svg b/drawing.svg index bb142e8..e21d2d0 100644 --- a/drawing.svg +++ b/drawing.svg @@ -8,8 +8,8 @@ version="1.1" id="svg1" inkscape:export-filename="../../../Desktop/logo.png" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96" + inkscape:export-xdpi="192" + inkscape:export-ydpi="192" inkscape:version="1.3 (0e150ed, 2023-07-21)" sodipodi:docname="drawing.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a5a6742d95ce183afb33b9a942df5d11d941f3 GIT binary patch literal 24454 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 literal 0 HcmV?d00001 diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index 6dfa693..5086e68 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -19,8 +19,9 @@ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "runtime class AppDelegate: NSObject, NSApplicationDelegate { private var menu: MacExtrasConfigurator? + /// 自动检测 YAML 是否包含 tungo 服务 private var vpnMode: Bool { - UserDefaults.standard.bool(forKey: defaultsVpnModeKey) + ConfigRepository.shared.activeConfig.contains("type: tungo") } func mainMenu() { @@ -100,10 +101,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { Task { do { try await VpnManager.shared.start() - self.menu?.toOnState() + DispatchQueue.main.async { self.menu?.toOnState() } } catch { logger.error("VPN start failed: \(error.localizedDescription)") - self.menu?.toOffState() + DispatchQueue.main.async { self.menu?.toOffState() } } } } diff --git a/macos/GostX/ConfigRepository.swift b/macos/GostX/ConfigRepository.swift index fc709a9..183a873 100644 --- a/macos/GostX/ConfigRepository.swift +++ b/macos/GostX/ConfigRepository.swift @@ -117,7 +117,7 @@ class ConfigRepository: ObservableObject { syncActiveToAppGroup() } - func activeConfig() -> String { + var activeConfig: String { guard let id = activeProfileId, let profile = profiles.first(where: { $0.id == id }) else { return defaultGostYAML } @@ -125,7 +125,6 @@ class ConfigRepository: ObservableObject { } private func syncActiveToAppGroup() { - let yaml = activeConfig() - AppGroupConfig.writeYaml(yaml) + AppGroupConfig.writeYaml(activeConfig) } } diff --git a/macos/GostX/VpnManager.swift b/macos/GostX/VpnManager.swift index 0c94ba9..8d95a20 100644 --- a/macos/GostX/VpnManager.swift +++ b/macos/GostX/VpnManager.swift @@ -1,6 +1,7 @@ // macos/GostX/VpnManager.swift import Foundation import NetworkExtension +import os /// 管理 NETunnelProviderManager 生命周期:加载/保存 VPN 配置、启停连接。 @MainActor @@ -47,8 +48,13 @@ class VpnManager: ObservableObject { // MARK: - Control func start() async throws { - guard let m = manager else { return } + guard let m = manager else { + os_log(.error, "[GostX] VpnManager.start: manager is nil") + return + } + os_log(.default, "[GostX] VpnManager.start: calling startVPNTunnel, status=%d", m.connection.status.rawValue) try m.connection.startVPNTunnel() + os_log(.default, "[GostX] VpnManager.start: startVPNTunnel returned, status=%d", m.connection.status.rawValue) } func stop() { From 7e58940a90f56039039f6aba572442c28f7d9786 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Tue, 14 Jul 2026 22:12:09 +0800 Subject: [PATCH 58/89] security: extract DEVELOPMENT_TEAM to xcconfig, add to .gitignore Move Apple Developer Team ID out of version-controlled project.pbxproj into a gitignored xcconfig file. Provide .example template for contributors. --- .gitignore | 2 ++ .../DevelopmentTeam.example.xcconfig | 4 ++++ macos/GostX.xcodeproj/project.pbxproj | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 macos/Configuration/DevelopmentTeam.example.xcconfig diff --git a/.gitignore b/.gitignore index 1145d23..448d046 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ libgost/build/ # 个人配置(含敏感信息,不提交) android/config-templates/*.yaml +macos/Configuration/*.xcconfig +!macos/Configuration/*.example.xcconfig android/app/libs/libgost*.aar .pi-subagents/ macos/Frameworks/ diff --git a/macos/Configuration/DevelopmentTeam.example.xcconfig b/macos/Configuration/DevelopmentTeam.example.xcconfig new file mode 100644 index 0000000..d142e46 --- /dev/null +++ b/macos/Configuration/DevelopmentTeam.example.xcconfig @@ -0,0 +1,4 @@ +// macOS signing configuration — copy to DevelopmentTeam.xcconfig and fill in your Team ID +// Go to https://developer.apple.com/account to find your Team ID + +DEVELOPMENT_TEAM = YOUR_TEAM_ID diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index a01e170..39942a6 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -72,6 +72,7 @@ /* Begin PBXFileReference section */ 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; + AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DevelopmentTeam.xcconfig; sourceTree = ""; }; 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 430349433004D3A000FD9EE9 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; @@ -163,6 +164,7 @@ isa = PBXGroup; children = ( 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */, + AD12003F3005000100FD9EE9 /* Configuration */, B532A668285B528900159BAC /* GostX */, B532A67A285B528D00159BAC /* GostXTests */, B532A684285B528D00159BAC /* GostXUITests */, @@ -228,6 +230,14 @@ path = GostXUITests; sourceTree = ""; }; + AD12003F3005000100FD9EE9 /* Configuration */ = { + isa = PBXGroup; + children = ( + AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; B5AF722B28634A6500BB5F98 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -482,13 +492,13 @@ /* Begin XCBuildConfiguration section */ 4303494E3004D3A000FD9EE9 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = GostXTunnel/GostXTunnel.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -521,13 +531,13 @@ }; 4303494F3004D3A000FD9EE9 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = GostXTunnel/GostXTunnel.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -678,6 +688,7 @@ }; B532A68C285B528D00159BAC /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -688,7 +699,6 @@ CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"GostX/Preview Content\""; - DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -716,6 +726,7 @@ }; B532A68D285B528D00159BAC /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -727,7 +738,6 @@ CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"GostX/Preview Content\""; - DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; From 3ed4a14cf71254448610fc980295559d114d5ca7 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 21:49:49 +0800 Subject: [PATCH 59/89] Enhanced settings window --- macos/GostX.xcodeproj/project.pbxproj | 77 +++--- .../xcshareddata/swiftpm/Package.resolved | 14 - macos/GostX/AppDelegate.swift | 14 + macos/GostX/AppGroupConfig.swift | 20 +- macos/GostX/AppLogger.swift | 50 ++++ macos/GostX/FileManageView.swift | 245 +++++++++++++----- macos/GostX/FileRepository.swift | 23 ++ macos/GostX/LogView.swift | 185 +++++++++++++ macos/GostX/MacExtrasConfigurator.swift | 18 ++ macos/GostX/SettingsView.swift | 129 +++++---- macos/GostX/YamlEditorView.swift | 136 ++++++++++ macos/GostXTunnel/PacketTunnelProvider.swift | 8 +- 12 files changed, 741 insertions(+), 178 deletions(-) delete mode 100644 macos/GostX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 macos/GostX/AppLogger.swift create mode 100644 macos/GostX/LogView.swift create mode 100644 macos/GostX/YamlEditorView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 39942a6..d6f4c93 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -8,13 +8,14 @@ /* Begin PBXBuildFile section */ 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; + 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B1478690C1481980B302A8 /* LogView.swift */; }; 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; - B523FCA529E169180050578C /* HighlightedTextEditor in Frameworks */ = {isa = PBXBuildFile; productRef = B523FCA429E169180050578C /* HighlightedTextEditor */; }; + 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; B532A66E285B528D00159BAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A66D285B528D00159BAC /* Assets.xcassets */; }; B532A671285B528D00159BAC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A670285B528D00159BAC /* Preview Assets.xcassets */; }; @@ -30,6 +31,7 @@ B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; + EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E9D6522B494B409C95F0FE /* AppLogger.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -71,13 +73,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; + 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YamlEditorView.swift; sourceTree = ""; }; 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; - AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DevelopmentTeam.xcconfig; sourceTree = ""; }; 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 430349433004D3A000FD9EE9 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; 4303495C3004D78100FD9EE9 /* Libgost.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libgost.xcframework; path = Frameworks/Libgost.xcframework; sourceTree = ""; }; 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigRepository.swift; sourceTree = ""; }; + 64B1478690C1481980B302A8 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = ""; }; + AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DevelopmentTeam.xcconfig; sourceTree = ""; }; B532A666285B528900159BAC /* GostX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GostX.app; sourceTree = BUILT_PRODUCTS_DIR; }; B532A669285B528900159BAC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; B532A66D285B528D00159BAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -100,7 +105,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */ = { + 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, @@ -110,18 +115,7 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - 430349453004D3A000FD9EE9 /* GostXTunnel */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */, - ); - explicitFileTypes = { - }; - explicitFolders = ( - ); - path = GostXTunnel; - sourceTree = ""; - }; + 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -138,7 +132,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B523FCA529E169180050578C /* HighlightedTextEditor in Frameworks */, EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -160,6 +153,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 43F701153007C22A00E70769 /* Recovered References */ = { + isa = PBXGroup; + children = ( + 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */, + ); + name = "Recovered References"; + sourceTree = ""; + }; + AD12003F3005000100FD9EE9 /* Configuration */ = { + isa = PBXGroup; + children = ( + AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; B532A65D285B528900159BAC = { isa = PBXGroup; children = ( @@ -171,6 +180,7 @@ 430349453004D3A000FD9EE9 /* GostXTunnel */, B532A667285B528900159BAC /* Products */, B5AF722B28634A6500BB5F98 /* Frameworks */, + 43F701153007C22A00E70769 /* Recovered References */, ); sourceTree = ""; }; @@ -196,6 +206,9 @@ B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */, F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */, B5B0E079285C63080035A6F2 /* SettingsView.swift */, + 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */, + 16E9D6522B494B409C95F0FE /* AppLogger.swift */, + 64B1478690C1481980B302A8 /* LogView.swift */, B5AEDDB02864920800C09ED6 /* AppDelegate.swift */, B5F6387829DEFF31009A1287 /* Arguments.swift */, B5B1C1A229DD6E90008AA942 /* FileManageView.swift */, @@ -230,14 +243,6 @@ path = GostXUITests; sourceTree = ""; }; - AD12003F3005000100FD9EE9 /* Configuration */ = { - isa = PBXGroup; - children = ( - AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */, - ); - path = Configuration; - sourceTree = ""; - }; B5AF722B28634A6500BB5F98 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -286,7 +291,6 @@ ); name = GostX; packageProductDependencies = ( - B523FCA429E169180050578C /* HighlightedTextEditor */, ); productName = GostX; productReference = B532A666285B528900159BAC /* GostX.app */; @@ -365,7 +369,6 @@ ); mainGroup = B532A65D285B528900159BAC; packageReferences = ( - B523FCA329E169180050578C /* XCRemoteSwiftPackageReference "HighlightedTextEditor" */, ); productRefGroup = B532A667285B528900159BAC /* Products */; projectDirPath = ""; @@ -431,6 +434,9 @@ B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */, B5AEDDB12864920800C09ED6 /* AppDelegate.swift in Sources */, B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */, + 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */, + EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */, + 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */, B5B1C1A129DD6E90008AA941 /* FileManageView.swift in Sources */, B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */, B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */, @@ -885,25 +891,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - B523FCA329E169180050578C /* XCRemoteSwiftPackageReference "HighlightedTextEditor" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kyle-n/HighlightedTextEditor"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - B523FCA429E169180050578C /* HighlightedTextEditor */ = { - isa = XCSwiftPackageProductDependency; - package = B523FCA329E169180050578C /* XCRemoteSwiftPackageReference "HighlightedTextEditor" */; - productName = HighlightedTextEditor; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = B532A65E285B528900159BAC /* Project object */; } diff --git a/macos/GostX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/GostX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index f8a3fd7..0000000 --- a/macos/GostX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "highlightedtexteditor", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kyle-n/HighlightedTextEditor", - "state" : { - "revision" : "7069ede002afc6bde867aef7035f45ff72dc839c", - "version" : "2.1.0" - } - } - ], - "version" : 2 -} diff --git a/macos/GostX/AppDelegate.swift b/macos/GostX/AppDelegate.swift index 5086e68..d6357e9 100644 --- a/macos/GostX/AppDelegate.swift +++ b/macos/GostX/AppDelegate.swift @@ -47,6 +47,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.mainMenu() self.menu = MacExtrasConfigurator(delegate: self) + // Configure unified logging for the main app + if let containerURL = AppGroupConfig.containerURL { + AppLogger.configure(containerURL: containerURL) + } + Task { await VpnManager.shared.setup() } @@ -75,15 +80,18 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func stop() { + AppLogger.log(.info, "Stopping...") if vpnMode { Task { @MainActor in VpnManager.shared.stop() } } else { LibgostStopGost(nil) } self.menu?.toOffState() + AppLogger.log(.info, "Stopped") } func start() { + AppLogger.log(.info, "Starting...") if vpnMode { startVpnMode() } else { @@ -94,6 +102,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // MARK: - VPN Mode private func startVpnMode() { + AppLogger.log(.info, "Starting VPN mode") // 同步 YAML 到 App Group let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML AppGroupConfig.writeYaml(yaml) @@ -102,8 +111,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { do { try await VpnManager.shared.start() DispatchQueue.main.async { self.menu?.toOnState() } + AppLogger.log(.info, "VPN started") } catch { logger.error("VPN start failed: \(error.localizedDescription)") + AppLogger.log(.error, "VPN start failed: \(error.localizedDescription)") DispatchQueue.main.async { self.menu?.toOffState() } } } @@ -112,6 +123,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // MARK: - Proxy Mode (non-VPN) private func startProxyMode() { + AppLogger.log(.info, "Starting proxy mode") let yaml = UserDefaults.standard.string(forKey: defaultsYamlKey) ?? defaultGostYAML // Set work dir to App Group container so gost can find imported bypass files @@ -125,10 +137,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { var err: NSError? if !LibgostStartGost(yaml, "", &err), let err { logger.error("gost start failed: \(err.localizedDescription)") + AppLogger.log(.error, "gost start failed: \(err.localizedDescription)") self.menu?.toOffState() return } self.menu?.toOnState() + AppLogger.log(.info, "Proxy started") } } diff --git a/macos/GostX/AppGroupConfig.swift b/macos/GostX/AppGroupConfig.swift index ede749b..9cbc37d 100644 --- a/macos/GostX/AppGroupConfig.swift +++ b/macos/GostX/AppGroupConfig.swift @@ -1,7 +1,7 @@ // macos/GostX/AppGroupConfig.swift import Foundation -/// 通过 App Group 容器在主 App 和 Extension 之间共享 YAML 配置。 +/// 通过 App Group 容器在主 App 和 Extension 之间共享 YAML 配置和日志设置。 struct AppGroupConfig { static let groupId = "group.cn.liukebin.gostx" static let yamlFileName = "gost.yaml" @@ -29,4 +29,22 @@ struct AppGroupConfig { guard let url = yamlFileURL else { return } try? yaml.write(to: url, atomically: true, encoding: .utf8) } + + // MARK: - Logging Settings + + private static var sharedDefaults: UserDefaults? { + UserDefaults(suiteName: groupId) + } + + static var loggingEnabled: Bool { + get { sharedDefaults?.bool(forKey: "logging_enabled") ?? true } + set { sharedDefaults?.set(newValue, forKey: "logging_enabled") } + } + + static var logLevel: String { + get { sharedDefaults?.string(forKey: "log_level") ?? "info" } + set { sharedDefaults?.set(newValue, forKey: "log_level") } + } + + static let logLevelOptions = ["error", "warn", "info", "debug", "trace"] } diff --git a/macos/GostX/AppLogger.swift b/macos/GostX/AppLogger.swift new file mode 100644 index 0000000..4cbccd7 --- /dev/null +++ b/macos/GostX/AppLogger.swift @@ -0,0 +1,50 @@ +// +// AppLogger.swift +// GostX +// +// Bridges main app os_log to gost.log for unified logging. +// + +import Foundation +import os + +/// Writes log messages from the main app into the shared gost.log file +/// alongside the tunnel extension logs. +enum AppLogger { + private static let queue = DispatchQueue(label: "cn.liukebin.gostx.applogger") + private static var logFileURL: URL? + + /// Must be called once on app startup with the container URL. + static func configure(containerURL: URL) { + logFileURL = containerURL.appendingPathComponent("gost.log") + } + + static func log(_ level: OSLogType = .default, _ message: String) { + os_log(level, "%{public}@", message) + guard AppGroupConfig.loggingEnabled else { return } + writeToFile("[APP] \(message)") + } + + private static func writeToFile(_ line: String) { + guard let url = logFileURL else { return } + queue.async { + let timestamp = ISO8601DateFormatter().string(from: Date()) + let entry = "\(formatTimestamp()) \(line)\n" + if let data = entry.data(using: .utf8) { + if let handle = try? FileHandle(forWritingTo: url) { + handle.seekToEndOfFile() + handle.write(data) + try? handle.close() + } else { + try? data.write(to: url, options: .atomic) + } + } + } + } + + private static func formatTimestamp() -> String { + let df = DateFormatter() + df.dateFormat = "HH:mm:ss.SSS" + return df.string(from: Date()) + } +} diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift index 2447d0b..8cfd657 100644 --- a/macos/GostX/FileManageView.swift +++ b/macos/GostX/FileManageView.swift @@ -25,6 +25,10 @@ class FileManageViewModel: ObservableObject { @Published var files: [FileInfo] = [] @Published var alertMessage: String? @Published var pendingOverwrite: (sourceURL: URL, fileName: String)? + @Published var selectedFileName: String? + @Published var fileContent: String = "" + @Published var isFileDirty: Bool = false + var originalContent: String = "" private let repo: FileRepository? @@ -39,6 +43,36 @@ class FileManageViewModel: ObservableObject { files = repo?.listFiles() ?? [] } + func selectFile(_ name: String) { + selectedFileName = name + let content = repo?.readFileContent(name) ?? "" + originalContent = content + fileContent = content + isFileDirty = false + } + + func saveFileContent() { + guard let repo, let name = selectedFileName else { return } + do { + try repo.writeFileContent(name, content: fileContent) + originalContent = fileContent + isFileDirty = false + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func createFile(_ name: String) { + guard let repo else { return } + do { + try repo.createFile(name) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + func importFile(from sourceURL: URL) { guard let repo else { return } let name = sourceURL.lastPathComponent @@ -75,6 +109,10 @@ class FileManageViewModel: ObservableObject { guard let repo else { return } do { try repo.renameFile(oldName, to: newName) + if selectedFileName == oldName { + selectedFileName = newName + fileContent = repo.readFileContent(newName) ?? "" + } refresh() } catch { alertMessage = error.localizedDescription @@ -85,6 +123,10 @@ class FileManageViewModel: ObservableObject { guard let repo else { return } do { try repo.deleteFile(name) + if selectedFileName == name { + selectedFileName = nil + fileContent = "" + } refresh() } catch { alertMessage = error.localizedDescription @@ -120,15 +162,19 @@ struct FileManageView: View { @State private var renameText: String = "" @State private var deleteTarget: FileInfo? @State private var showDeleteConfirm = false + @State private var showNewFileSheet = false + @State private var newFileName = "" var body: some View { - Group { + HStack(spacing: 0) { if !vm.isAvailable { unavailableView - } else if vm.files.isEmpty { - emptyView } else { - fileListView + fileSidebar + + Divider() + + fileDetail } } .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in @@ -136,82 +182,95 @@ struct FileManageView: View { vm.importFile(from: url) } } - } - - // MARK: - Unavailable - - private var unavailableView: some View { - VStack(spacing: 12) { - Image(systemName: "exclamationmark.triangle") - .font(.system(size: 32)) - .foregroundColor(.secondary) - Text(NSLocalizedString("App Group container not available.", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - - // MARK: - Empty - - private var emptyView: some View { - VStack(spacing: 16) { - Image(systemName: "folder") - .font(.system(size: 36)) - .foregroundColor(Color(nsColor: .tertiaryLabelColor)) - Text(NSLocalizedString("No files. Click + to import.", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) - Button(action: { showImporter = true }) { - Label(NSLocalizedString("Import File", comment: ""), systemImage: "plus") + .sheet(isPresented: $showNewFileSheet) { + VStack(spacing: 16) { + Text(NSLocalizedString("New File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $newFileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { + showNewFileSheet = false + newFileName = "" + } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Create", comment: "")) { + vm.createFile(newFileName) + newFileName = "" + showNewFileSheet = false + } + .keyboardShortcut(.defaultAction) + .disabled(newFileName.trimmingCharacters(in: .whitespaces).isEmpty) + } } + .padding() + .frame(width: 300, height: 140) } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .modifier(FileManageAlertModifier(vm: vm)) - .modifier(OverwriteSheetModifier(vm: vm)) } - // MARK: - List + // MARK: - Sidebar - private var fileListView: some View { + private var fileSidebar: some View { VStack(spacing: 0) { - List { - ForEach(vm.files) { file in - FileRowView( - file: file, - onExport: { - exportName = file.name - showExporter = true - }, - onRename: { - renameTarget = file - renameText = file.name - }, - onCopyPath: { vm.copyPath(file.name) }, - onDelete: { - deleteTarget = file - showDeleteConfirm = true + if vm.files.isEmpty { + VStack(spacing: 8) { + Spacer() + Text(NSLocalizedString("No files", comment: "")) + .font(.system(size: 12)) + .foregroundColor(.secondary) + Spacer() + } + } else { + List(selection: Binding( + get: { vm.selectedFileName }, + set: { name in + DispatchQueue.main.async { + vm.selectedFileName = name + if let name { vm.selectFile(name) } } - ) + } + )) { + ForEach(vm.files) { file in + FileRowView( + file: file, + onExport: { + exportName = file.name + showExporter = true + }, + onRename: { + renameTarget = file + renameText = file.name + }, + onCopyPath: { vm.copyPath(file.name) }, + onDelete: { + deleteTarget = file + showDeleteConfirm = true + } + ) + .tag(file.name) + } } + .listStyle(.plain) } - .listStyle(.plain) - .scrollContentBackground(.hidden) Divider() HStack { - Button(action: { showImporter = true }) { - Label(NSLocalizedString("Import File", comment: ""), systemImage: "plus") - .font(.system(size: 12)) + Button(action: { showNewFileSheet = true }) { + Label(NSLocalizedString("New File", comment: ""), systemImage: "doc.badge.plus") } .buttonStyle(.borderless) Spacer() + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import", comment: ""), systemImage: "square.and.arrow.up") + } + .buttonStyle(.borderless) } .padding(.horizontal, 12) .padding(.vertical, 6) + .frame(height: 32) } - .frame(minWidth: 300, minHeight: 200) + .frame(width: 200) .fileExporter( isPresented: $showExporter, item: exportName ?? "", @@ -233,6 +292,63 @@ struct FileManageView: View { vm: vm )) } + + // MARK: - Detail + + private var fileDetail: some View { + Group { + if let _ = vm.selectedFileName { + VStack(spacing: 0) { + TextEditor(text: $vm.fileContent) + .font(.system(size: 12, design: .monospaced)) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onChange(of: vm.fileContent) { _ in + DispatchQueue.main.async { + vm.isFileDirty = vm.fileContent != vm.originalContent + } + } + + Divider() + + HStack { + Spacer() + Button(action: { vm.saveFileContent() }) { + Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") + } + .buttonStyle(.borderless) + .disabled(!vm.isFileDirty) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + } else { + VStack { + Image(systemName: "doc.text.magnifyingglass") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Select a file to view", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } + + // MARK: - Unavailable + + private var unavailableView: some View { + VStack(spacing: 12) { + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 32)) + .foregroundColor(.secondary) + Text(NSLocalizedString("App Group container not available.", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } } // MARK: - FileRowView @@ -252,12 +368,15 @@ private struct FileRowView: View { .foregroundColor(.secondary) .frame(width: 20) - VStack(alignment: .leading, spacing: 2) { + VStack(alignment: .leading, spacing: 1) { Text(file.name) .font(.system(size: 13)) .lineLimit(1) - Text("\(formatFileSize(file.sizeBytes)) — \(formatFileDate(file.lastModified))") - .font(.system(size: 11)) + Text(formatFileSize(file.sizeBytes)) + .font(.system(size: 10)) + .foregroundColor(.secondary) + Text(formatFileDate(file.lastModified)) + .font(.system(size: 10)) .foregroundColor(.secondary) } diff --git a/macos/GostX/FileRepository.swift b/macos/GostX/FileRepository.swift index d0cc2ce..84d1717 100644 --- a/macos/GostX/FileRepository.swift +++ b/macos/GostX/FileRepository.swift @@ -141,6 +141,29 @@ class FileRepository { try FileManager.default.removeItem(at: url) } + // MARK: - Read / Write + + func createFile(_ name: String) throws { + let trimmed = try validateName(name) + let url = workDir.appendingPathComponent(trimmed) + if FileManager.default.fileExists(atPath: url.path) { + throw FileRepositoryError.fileAlreadyExists(trimmed) + } + try "".write(to: url, atomically: true, encoding: .utf8) + } + + func readFileContent(_ name: String) -> String? { + guard let trimmed = try? validateName(name) else { return nil } + let url = workDir.appendingPathComponent(trimmed) + return try? String(contentsOf: url, encoding: .utf8) + } + + func writeFileContent(_ name: String, content: String) throws { + let trimmed = try validateName(name) + let url = workDir.appendingPathComponent(trimmed) + try content.write(to: url, atomically: true, encoding: .utf8) + } + // MARK: - Path func filePath(_ name: String) -> String { diff --git a/macos/GostX/LogView.swift b/macos/GostX/LogView.swift new file mode 100644 index 0000000..7c40f29 --- /dev/null +++ b/macos/GostX/LogView.swift @@ -0,0 +1,185 @@ +// +// LogView.swift +// GostX +// +// Displays gost.log content with auto-scroll, copy, and clear. +// + +import SwiftUI + +@available(macOS 14.0, *) +struct LogView: View { + @State private var lines: [String] = [] + @State private var isFollowing = true + @State private var timer: Timer? + @State private var scrollProxy: ScrollViewProxy? + @State private var loggingEnabled = AppGroupConfig.loggingEnabled + @State private var logLevel = AppGroupConfig.logLevel + + private var logFileURL: URL? { + AppGroupConfig.containerURL?.appendingPathComponent("gost.log") + } + + var body: some View { + VStack(spacing: 0) { + // Toolbar — row 1: log controls + HStack(spacing: 8) { + Toggle(isOn: $loggingEnabled) { + Text(NSLocalizedString("Logging", comment: "")).font(.system(size: 11)) + } + .toggleStyle(.switch) + .controlSize(.mini) + .onChange(of: loggingEnabled) { newValue in + AppGroupConfig.loggingEnabled = newValue + AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") + } + + if loggingEnabled { + Picker("", selection: $logLevel) { + ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in + Text(level.capitalized).tag(level) + } + } + .pickerStyle(.menu) + .fixedSize() + .onChange(of: logLevel) { newValue in + AppGroupConfig.logLevel = newValue + AppLogger.log(.info, "Log level: \(newValue)") + } + } + + Spacer() + + Text(NSLocalizedString("Restart VPN to apply", comment: "")) + .font(.system(size: 9)) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 4) + + Divider() + + // Toolbar — row 2: view controls + HStack(spacing: 8) { + Button(action: { isFollowing.toggle() }) { + Image(systemName: isFollowing ? "pause.fill" : "play.fill") + } + .buttonStyle(.borderless) + .help(isFollowing + ? NSLocalizedString("Pause auto-scroll", comment: "") + : NSLocalizedString("Resume auto-scroll", comment: "")) + + Divider() + .frame(height: 16) + + Button(action: copyAll) { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Copy all", comment: "")) + + Button(action: clearLog) { + Image(systemName: "trash") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Clear log", comment: "")) + + Spacer() + + Text("\(lines.count) lines") + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + + Divider() + + // Log content + if lines.isEmpty { + VStack { + Image(systemName: "text.alignleft") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("No logs", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + ScrollViewReader { proxy in + ScrollView { + LazyVStack(alignment: .leading, spacing: 1) { + ForEach(Array(lines.enumerated()), id: \.offset) { _, line in + Text(line) + .font(.system(size: 11, design: .monospaced)) + .textSelection(.enabled) + } + } + .padding(8) + } + .background(Color(nsColor: .textBackgroundColor)) + .onAppear { scrollProxy = proxy } + } + } + } + .onAppear { + loadLog() + startPolling() + } + .onDisappear { + stopPolling() + } + .onChange(of: isFollowing) { _ in + if isFollowing, let proxy = scrollProxy, !lines.isEmpty { + proxy.scrollTo(lines.count - 1, anchor: .bottom) + } + } + } + + // MARK: - Actions + + private func loadLog() { + guard let url = logFileURL, FileManager.default.fileExists(atPath: url.path) else { + lines = [] + return + } + do { + let content = try String(contentsOf: url, encoding: .utf8) + let newLines = content.components(separatedBy: "\n").filter { !$0.isEmpty } + lines = newLines + } catch { + lines = [] + } + } + + private func startPolling() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in + DispatchQueue.main.async { + let oldCount = lines.count + loadLog() + if isFollowing, lines.count > oldCount, let proxy = scrollProxy { + proxy.scrollTo(lines.count - 1, anchor: .bottom) + } + } + } + } + + private func stopPolling() { + timer?.invalidate() + timer = nil + } + + private func copyAll() { + let text = lines.joined(separator: "\n") + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(text, forType: .string) + } + + private func clearLog() { + guard let url = logFileURL else { return } + try? "".write(to: url, atomically: true, encoding: .utf8) + lines = [] + } +} diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 296697b..0128b26 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -8,6 +8,7 @@ import Foundation import AppKit import SwiftUI +import NetworkExtension class MacExtrasConfigurator: NSObject, NSMenuDelegate { private var delegate: AppDelegate @@ -43,6 +44,23 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { super.init() + // Observe VPN status changes from system (covers toggling from System Settings) + NotificationCenter.default.addObserver( + forName: .NEVPNStatusDidChange, + object: nil, + queue: .main + ) { [weak self] notification in + guard let connection = notification.object as? NEVPNConnection else { return } + switch connection.status { + case .connected: + self?.toOnState() + case .disconnected, .invalid: + self?.toOffState() + default: + break + } + } + createMenu() toOffState() } diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index c12d8f6..f2ac6b8 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -1,12 +1,5 @@ // macos/GostX/SettingsView.swift import SwiftUI -import HighlightedTextEditor - -let reOpts = NSRegularExpression.Options([.anchorsMatchLines]) -let yamlKeyRule = try! NSRegularExpression( - pattern: "^(\\s*)(services|chains|hops|name|addr|handler|listener|connector|dialer|type|chain|auth|tls|metadata|bypass|resolver|hosts|retries|timeout)\\s*:", - options: reOpts) -let yamlCommentRule = try! NSRegularExpression(pattern: "^\\s*#.*", options: reOpts) @available(macOS 14.0, *) struct SettingsView: View { @@ -19,23 +12,51 @@ struct SettingsView: View { @State private var selectedTab = 0 var body: some View { - TabView(selection: $selectedTab) { - profilesTab - .tabItem { - Label(NSLocalizedString("Profiles", comment: ""), systemImage: "doc.text") - } - .tag(0) - FileManageView() - .tabItem { - Label(NSLocalizedString("Files", comment: ""), systemImage: "folder") + VStack(spacing: 0) { + VStack(spacing: 0) { + HStack(spacing: 0) { + tabItem(icon: "doc.text", title: NSLocalizedString("Profiles", comment: ""), index: 0) + tabItem(icon: "folder", title: NSLocalizedString("Files", comment: ""), index: 1) + tabItem(icon: "text.alignleft", title: NSLocalizedString("Logs", comment: ""), index: 2) } - .tag(1) + .padding(.horizontal, 20) + .padding(.vertical, 4) + } + + Divider() + + if selectedTab == 0 { + profilesTab + } else if selectedTab == 1 { + FileManageView() + } else { + LogView() + } } .frame(minWidth: 700, minHeight: 440) } + private func tabItem(icon: String, title: String, index: Int) -> some View { + let isSelected = selectedTab == index + return Button(action: { selectedTab = index }) { + VStack(spacing: 4) { + Image(systemName: icon) + .font(.system(size: 18, weight: .regular)) + Text(title) + .font(.system(size: 10)) + } + .frame(width: 68) + .padding(.vertical, 6) + .contentShape(Rectangle()) + .background(isSelected ? Color.accentColor.opacity(0.15) : Color.clear) + .clipShape(RoundedRectangle(cornerRadius: 6)) + .foregroundColor(isSelected ? Color.accentColor : .secondary) + } + .buttonStyle(.plain) + } + private var profilesTab: some View { - NavigationSplitView { + HStack(spacing: 0) { VStack(spacing: 0) { List(selection: $selectedProfileId) { ForEach(repo.profiles) { profile in @@ -58,17 +79,21 @@ struct SettingsView: View { Divider() - Button(action: { showAddSheet = true }) { - Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") - .frame(maxWidth: .infinity, alignment: .leading) + HStack { + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + } + .buttonStyle(.borderless) + Spacer() } - .buttonStyle(.borderless) .padding(.horizontal, 12) .padding(.vertical, 6) + .frame(height: 32) } - .frame(maxHeight: .infinity) - .navigationSplitViewColumnWidth(min: 180, ideal: 220) - } detail: { + .frame(width: 200) + + Divider() + if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) @@ -155,44 +180,40 @@ struct YamlEditorView: View { let profileId: String @StateObject private var repo = ConfigRepository.shared @State private var yamlText: String = "" - - private let rules: [HighlightRule] = [ - HighlightRule( - pattern: yamlCommentRule, - formattingRule: TextFormattingRule(key: .foregroundColor, value: NSColor.systemGray) - ), - HighlightRule( - pattern: yamlKeyRule, - formattingRules: [ - TextFormattingRule(fontTraits: .bold), - TextFormattingRule(key: .foregroundColor, value: NSColor.systemBlue), - ] - ), - ] + @State private var isDirty = false + @State private var originalText: String = "" var body: some View { VStack(spacing: 0) { - HighlightedTextEditor(text: $yamlText, highlightRules: rules) - .introspect { editor in - editor.textView.allowsUndo = true - editor.textView.breakUndoCoalescing() - editor.textView.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) - } + YamlTextView(text: $yamlText) .onChange(of: yamlText) { newValue in - save() + isDirty = newValue != originalText + } + .onAppear { + let text = repo.getConfig(profileId) + yamlText = text + originalText = text + isDirty = false } .frame(maxWidth: .infinity, maxHeight: .infinity) Divider() - Text("gost v3 YAML configuration — https://gost.run/docs/") - .font(.system(size: 10)) - .foregroundColor(Color(nsColor: .tertiaryLabelColor)) - .padding(.horizontal, 8) - .padding(.vertical, 4) - } - .onAppear { - yamlText = repo.getConfig(profileId) + HStack { + Spacer() + Button(action: { + save() + originalText = yamlText + isDirty = false + }) { + Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") + } + .buttonStyle(.borderless) + .disabled(!isDirty) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) } } diff --git a/macos/GostX/YamlEditorView.swift b/macos/GostX/YamlEditorView.swift new file mode 100644 index 0000000..0376e1e --- /dev/null +++ b/macos/GostX/YamlEditorView.swift @@ -0,0 +1,136 @@ +// +// YamlEditorView.swift +// GostX +// +// SwiftUI wrapper around NSTextView with real-time YAML syntax highlighting. +// Supports undo naturally because it never replaces the entire NSTextStorage. +// + +import SwiftUI +import AppKit + +/// A YAML text editor with syntax highlighting and native undo support. +struct YamlTextView: NSViewRepresentable { + @Binding var text: String + + // MARK: - YAML Syntax Highlighting + + private static let rules: [(NSRegularExpression, [NSAttributedString.Key: Any])] = { + let commentFont = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) + let keyFont = NSFont.monospacedSystemFont(ofSize: 12, weight: .bold) + let valueFont = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) + + let commentColor = NSColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) + let keyColor = NSColor(red: 0.2, green: 0.4, blue: 0.8, alpha: 1.0) + let stringColor = NSColor(red: 0.1, green: 0.6, blue: 0.1, alpha: 1.0) + let numberColor = NSColor(red: 0.9, green: 0.5, blue: 0.1, alpha: 1.0) + let boolColor = NSColor(red: 0.6, green: 0.2, blue: 0.8, alpha: 1.0) + + let patterns: [(String, [NSAttributedString.Key: Any])] = [ + // comment — must be first to avoid matching inside comments + ("#.*", [.foregroundColor: commentColor, .font: commentFont]), + // string (double/single-quoted) + ("\"[^\"]*\"|'[^']*'", [.foregroundColor: stringColor, .font: valueFont]), + // number (integer / float) + ("(? Coordinator { + Coordinator(self) + } + + func makeNSView(context: Context) -> NSScrollView { + let scrollView = NSTextView.scrollableTextView() + let textView = scrollView.documentView as! NSTextView + + textView.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular) + textView.isAutomaticTextReplacementEnabled = false + textView.allowsUndo = true + textView.isEditable = true + textView.isRichText = false + textView.textContainer?.widthTracksTextView = true + textView.textContainer?.containerSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) + textView.textContainerInset = NSSize(width: 4, height: 4) + + // Use textStorage delegate for highlighting — doesn't interfere with NSTextViewDelegate + textView.textStorage?.delegate = context.coordinator + + // Initial content + textView.string = text + YamlTextView.applyHighlighting(text, to: textView.textStorage!) + + return scrollView + } + + func updateNSView(_ scrollView: NSScrollView, context: Context) { + let textView = scrollView.documentView as! NSTextView + let coordinator = context.coordinator + + // Only update when binding changed externally (e.g., load profile) + guard textView.string != text else { return } + coordinator.needsBindingSync = false // suppress didProcessEditing binding sync + coordinator.updating = true + textView.string = text + YamlTextView.applyHighlighting(text, to: textView.textStorage!) + coordinator.updating = false + coordinator.needsBindingSync = true // enable user-editing binding sync + } +} diff --git a/macos/GostXTunnel/PacketTunnelProvider.swift b/macos/GostXTunnel/PacketTunnelProvider.swift index 6dfb260..927216c 100644 --- a/macos/GostXTunnel/PacketTunnelProvider.swift +++ b/macos/GostXTunnel/PacketTunnelProvider.swift @@ -107,8 +107,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { try? FileManager.default.createDirectory(at: filesDir, withIntermediateDirectories: true, attributes: nil) LibgostSetWorkDir(filesDir.path, nil) + + // Read logging preferences from shared UserDefaults + let level = AppGroupConfig.loggingEnabled ? AppGroupConfig.logLevel : "off" let logFile = containerURL.appendingPathComponent("gost.log").path + LibgostSetLogMaxSize(2 * 1024 * 1024) LibgostSetLogFile(logFile, nil) - LibgostSetLogLevel("info") + LibgostSetLogLevel(level) + os_log(.default, "[GostX] logging: enabled=%{public}@ level=%{public}@", + AppGroupConfig.loggingEnabled ? "yes" : "no", level) } } From bf6ec01e9ca65c829da6709ee3444609f100cc2a Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 21:55:48 +0800 Subject: [PATCH 60/89] spec: macOS Settings NavigationSplitView design --- ...cos-settings-navigationsplitview-design.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 docs/superpowers/specs/2025-07-16-macos-settings-navigationsplitview-design.md diff --git a/docs/superpowers/specs/2025-07-16-macos-settings-navigationsplitview-design.md b/docs/superpowers/specs/2025-07-16-macos-settings-navigationsplitview-design.md new file mode 100644 index 0000000..9487d6d --- /dev/null +++ b/docs/superpowers/specs/2025-07-16-macos-settings-navigationsplitview-design.md @@ -0,0 +1,146 @@ +# macOS Settings NavigationSplitView — Design + +**Date:** 2025-07-16 +**Status:** approved + +## Summary + +Replace the custom tab-bar layout in `SettingsView` with a standard macOS `NavigationSplitView` (3-column). The sidebar shows Profiles / Files / Logs categories. Selecting a category populates the Content column with that category's item list, and the Detail column shows the selected item's content. + +## Current vs. Target + +**Current**: Custom tab bar (Profiles / Files / Logs) at top; each tab is a self-contained view. Profiles has a left-right split, Files has a left-right split, Logs is a single scrollable view with inline controls. + +**Target**: `NavigationSplitView` with three columns. The tab bar is replaced by a sidebar. Each category's content is split into list + detail panes. Logs gains a proper split between options (toggle, level) and content (scrollable log lines). + +## Columns + +| Sidebar | Content | Detail | +|---------|---------|--------| +| Profiles | Profile List (+ Add/Rename/Delete) | YAML Editor (or placeholder) | +| Files | File List (+ Import/New/… actions) | File Content Editor (or placeholder) | +| Logs | Log options (toggle, level picker) | Log Content (scrollable lines or empty) | + +### Column behavior + +- Always show all 3 columns with a minimum window width that accommodates them. +- When window is resized narrow, the sidebar hides behind the system toolbar toggle (default NavigationSplitView behavior). + +## State Ownership + +### Category selection — new `@State` in SettingsView + +```swift +enum SettingsCategory: String, CaseIterable, Identifiable { + case profiles, files, logs + var id: Self { self } +} +@State private var selectedCategory: SettingsCategory = .profiles +``` + +### Profiles + +- State stays in `ConfigRepository.shared` (no change). +- `selectedProfileId` lifted from tab-local `@State` into `SettingsView` so sidebar + content + detail share one selection binding. + +### Files + +- `FileManageViewModel` promoted from being created inside `FileManageView` to a `@StateObject` in `SettingsView`. +- Passed down to `FileListView` and `FileContentView`. +- `selectedFileName` binding flows through `FileManageViewModel`. + +### Logs + +- New `LogViewModel: ObservableObject` owned by `SettingsView`. Holds `lines`, `isFollowing`, timer, scroll proxy, plus `copyAll()` / `clearLog()`. +- Log options (`loggingEnabled`, `logLevel`) backed by `AppGroupConfig`, passed as bindings to `LogOptionsView`. +- `LogContentView` observes `LogViewModel.$lines` and `LogViewModel.$isFollowing`. + +## Component Tree + +``` +SettingsView // NavigationSplitView +├─ Sidebar // List(selection: $selectedCategory) +│ ├─ Label("Profiles", "doc.text") +│ ├─ Label("Files", "folder") +│ └─ Label("Logs", "text.alignleft") +│ +├─ Content (switches on selectedCategory) +│ ├─ .profiles → ProfileListView(repo, $selectedProfileId) +│ │ // List of profile names + Add/Rename/Delete context menu +│ ├─ .files → FileListView(vm: FileManageViewModel) +│ │ // List of file names + Import/New/… + context menu +│ └─ .logs → LogOptionsView($loggingEnabled, $logLevel) +│ // Toggle + level picker + "Restart VPN to apply" hint +│ +└─ Detail (switches on selectedCategory) + ├─ .profiles → if let id → YamlEditorView(profileId: id) + │ else → placeholder "Select a profile to edit" + ├─ .files → if let name → FileContentView(vm: FileManageViewModel) + │ else → placeholder "Select a file to view" + └─ .logs → LogContentView(vm: LogViewModel) + // Scrollable log lines or "No logs" / "Logging is off" +``` + +## File Changes + +| File | Change | +|------|--------| +| `SettingsView.swift` | Rewrite as NavigationSplitView orchestrator (~100 lines) | +| `FileManageView.swift` | Delete (gutted into `FileListView` + `FileContentView`) | +| `LogView.swift` | Delete (gutted into `LogOptionsView` + `LogContentView`) | +| New: `ProfileListView.swift` | Extracted from SettingsView profiles tab sidebar | +| New: `FileListView.swift` | Extracted from FileManageView sidebar | +| New: `FileContentView.swift` | Extracted from FileManageView detail pane | +| New: `LogOptionsView.swift` | Logging toggle + level picker | +| New: `LogContentView.swift` | Scrollable log lines + toolbar | +| New: `LogViewModel.swift` | ObservableObject for log state (lines, polling, copy, clear) | + +## Data Flow + +### Profiles +``` +ConfigRepository.shared (@Published profiles, activeProfileId) + → ProfileListView reads repo.profiles + → tap sets selectedProfileId via Binding + → YamlEditorView(profileId:) reads/writes repo.getConfig()/repo.saveConfig() + → Add/Rename/Delete call repo methods, refresh via @Published +``` + +### Files +``` +FileManageViewModel (@StateObject in SettingsView, passes down) + → FileListView reads vm.$files, binds vm.$selectedFileName + → FileContentView read/write vm.fileContent via TextEditor binding + → dirty tracking + save in FileManageViewModel + → Import/Export/Rename/Delete stay in FileManageViewModel (methods + sheets) +``` + +### Logs +``` +LogViewModel (@StateObject in SettingsView) + → .lines, .isFollowing, timer, scrollProxy, .copyAll(), .clearLog() +LogOptionsView binds AppGroupConfig.loggingEnabled / logLevel +LogContentView observes logVM.$lines, logVM.$isFollowing + → if lines.isEmpty && loggingEnabled → polling active, "No logs" placeholder + → if lines.isEmpty && !loggingEnabled → "Logging is off" placeholder + → else → ScrollViewReader with LazyVStack of monospaced lines +``` + +## Edge Cases + +| Case | Handling | +|------|----------| +| No profiles exist | Profile list empty; "Add Profile" button visible; detail shows placeholder | +| Profile deleted while being edited | `selectedProfileId` set to next available (or nil); `YamlEditorView` guarded by `if let id` | +| Log file doesn't exist | `LogViewModel` returns empty array; detail shows "No logs" | +| Logging switched off | `LogViewModel` stops polling, clears lines, detail shows "Logging is off" | +| File deleted while being viewed | `selectedFileName` reset to nil by `deleteFile()`; detail falls to placeholder | +| Window resized narrow | NavigationSplitView collapses sidebar → system toggle button; Content + Detail remain | +| File save when unchanged | Save button disabled via `isFileDirty` (existing logic in ViewModel) | +| Rapid profile switching | `YamlEditorView.id(profileId)` forces full re-creation | + +## Testing + +- `LogViewModel` — new unit tests for: load from file, polling appends lines, clear empties file, copyAll joins lines +- `FileManageViewModel` — verify existing tests still pass; add coverage for `selectFile` / `saveFileContent` if missing +- SwiftUI views — manual QA confirms layout, navigation, and edge cases From cf11b6fb9bb950a9cd37b374796c17c05a5eb5d1 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:05:44 +0800 Subject: [PATCH 61/89] plan: macOS Settings NavigationSplitView implementation --- ...7-16-macos-settings-navigationsplitview.md | 1218 +++++++++++++++++ 1 file changed, 1218 insertions(+) create mode 100644 docs/superpowers/plans/2025-07-16-macos-settings-navigationsplitview.md diff --git a/docs/superpowers/plans/2025-07-16-macos-settings-navigationsplitview.md b/docs/superpowers/plans/2025-07-16-macos-settings-navigationsplitview.md new file mode 100644 index 0000000..de1d746 --- /dev/null +++ b/docs/superpowers/plans/2025-07-16-macos-settings-navigationsplitview.md @@ -0,0 +1,1218 @@ +# macOS Settings NavigationSplitView Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the custom tab-bar layout in `SettingsView` with a macOS-standard `NavigationSplitView` three-column layout (Sidebar → Content → Detail). + +**Architecture:** Extract the three tab contents into list/detail view pairs, lift shared state into `SettingsView`, and wire it through a `NavigationSplitView`. Delete `LogView.swift` and `FileManageView.swift` after extraction. + +**Tech Stack:** SwiftUI, macOS 14.0+, Combine + +## Global Constraints + +- macOS 14.0 minimum deployment target +- Always show 3 columns; sidebar hides via system toggle on narrow windows +- Follow existing patterns: `@StateObject` + `@ObservedObject` for view models, `@Published` for observable state +- Existing `YamlEditorView` (defined in SettingsView.swift) stays untouched +- New files registered in Xcode project via `project.pbxproj` + +--- + +## File Map + +| File | Action | Responsibility | +|------|--------|----------------| +| `macos/GostX/LogViewModel.swift` | Create | ObservableObject: log lines, polling, copy, clear | +| `macos/GostX/LogOptionsView.swift` | Create | Logging toggle + level picker | +| `macos/GostX/LogContentView.swift` | Create | Scrollable log lines + toolbar (pause, copy, clear) | +| `macos/GostX/ProfileListView.swift` | Create | Profile list with add/rename/delete | +| `macos/GostX/FileListView.swift` | Create | File list with import/new/rename/delete | +| `macos/GostX/FileContentView.swift` | Create | TextEditor for file content + save | +| `macos/GostX/SettingsView.swift` | Modify | Rewrite as NavigationSplitView orchestrator | +| `macos/GostX/LogView.swift` | Delete | Gutted into LogOptionsView + LogContentView | +| `macos/GostX/FileManageView.swift` | Delete | Gutted into FileListView + FileContentView | +| `macos/GostXTests/GostXTests.swift` | Modify | Add LogViewModel tests | +| `macos/GostX.xcodeproj/project.pbxproj` | Modify | Add/remove file references | + +--- + +### Task 1: Create LogViewModel + +**Files:** +- Create: `macos/GostX/LogViewModel.swift` +- Modify: `macos/GostXTests/GostXTests.swift` + +**Interfaces:** +- Produces: `class LogViewModel: ObservableObject` with `@Published var lines: [String]`, `@Published var isFollowing: Bool`, `func onAppear()`, `func onDisappear()`, `func copyAll()`, `func clearLog()` + +- [ ] **Step 1: Write LogViewModel** + +Create `macos/GostX/LogViewModel.swift`: + +```swift +// macos/GostX/LogViewModel.swift +import SwiftUI +import Combine + +@MainActor +class LogViewModel: ObservableObject { + @Published var lines: [String] = [] + @Published var isFollowing = true + + var scrollProxy: ScrollViewProxy? + private var timer: Timer? + + private var logFileURL: URL? { + AppGroupConfig.containerURL?.appendingPathComponent("gost.log") + } + + func onAppear() { + loadLog() + startPolling() + } + + func onDisappear() { + stopPolling() + } + + func copyAll() { + let text = lines.joined(separator: "\n") + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(text, forType: .string) + } + + func clearLog() { + guard let url = logFileURL else { return } + try? "".write(to: url, atomically: true, encoding: .utf8) + lines = [] + } + + // MARK: - Private + + private func loadLog() { + guard let url = logFileURL, + FileManager.default.fileExists(atPath: url.path), + let content = try? String(contentsOf: url, encoding: .utf8) + else { + lines = [] + return + } + lines = content.components(separatedBy: "\n").filter { !$0.isEmpty } + } + + private func startPolling() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + guard let self else { return } + DispatchQueue.main.async { + let oldCount = self.lines.count + self.loadLog() + if self.isFollowing, self.lines.count > oldCount, let proxy = self.scrollProxy { + proxy.scrollTo(self.lines.count - 1, anchor: .bottom) + } + } + } + } + + private func stopPolling() { + timer?.invalidate() + timer = nil + } +} +``` + +- [ ] **Step 2: Write LogViewModel tests** + +Add to `macos/GostXTests/GostXTests.swift` after the existing test class: + +```swift +@MainActor +class LogViewModelTests: XCTestCase { + var vm: LogViewModel! + var tempDir: URL! + + override func setUpWithError() throws { + tempDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) + try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + // Write a known log file + let log = tempDir.appendingPathComponent("gost.log") + try "line one\nline two\nline three\n".write(to: log, atomically: true, encoding: .utf8) + // Override containerURL to return our temp dir + // We test load/clear/copy independently by writing to a temp file + vm = LogViewModel() + } + + override func tearDownWithError() throws { + vm.onDisappear() + try? FileManager.default.removeItem(at: tempDir) + vm = nil + } + + func testInitialState() { + XCTAssertTrue(vm.isFollowing) + // lines depend on file existence; without a real log file they start empty + } + + func testClearLog() { + vm.clearLog() + // After clear, lines should be empty + XCTAssertEqual(vm.lines.count, 0) + } + + func testCopyAll() { + // lines is empty by default in unit test env (no App Group file) + vm.copyAll() + // Pasteboard should contain empty string + XCTAssertEqual(NSPasteboard.general.string(forType: .string), "") + } + + func testIsFollowingToggle() { + vm.isFollowing = false + XCTAssertFalse(vm.isFollowing) + vm.isFollowing = true + XCTAssertTrue(vm.isFollowing) + } +} +``` + +- [ ] **Step 3: Run tests** + +Run: `cd macos && xcodebuild test -project GostX.xcodeproj -scheme GostX -destination 'platform=macOS' ONLY_ACTIVE_ARCH=YES 2>&1 | tail -20` +Expected: All tests pass. + +- [ ] **Step 4: Commit** + +```bash +git add macos/GostX/LogViewModel.swift macos/GostXTests/GostXTests.swift +git commit -m "feat: add LogViewModel with polling, copy, and clear" +``` + +--- + +### Task 2: Create LogOptionsView + +**Files:** +- Create: `macos/GostX/LogOptionsView.swift` + +**Interfaces:** +- Consumes: `AppGroupConfig.loggingEnabled` (Bool), `AppGroupConfig.logLevel` (String), `AppGroupConfig.logLevelOptions` ([String]) +- Produces: `struct LogOptionsView: View` — toggle + picker + hint text + +- [ ] **Step 1: Write LogOptionsView** + +Create `macos/GostX/LogOptionsView.swift`: + +```swift +// macos/GostX/LogOptionsView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct LogOptionsView: View { + @State private var loggingEnabled = AppGroupConfig.loggingEnabled + @State private var logLevel = AppGroupConfig.logLevel + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Toggle(isOn: $loggingEnabled) { + Text(NSLocalizedString("Logging", comment: "")) + .font(.system(size: 12, weight: .medium)) + } + .toggleStyle(.switch) + .controlSize(.small) + .onChange(of: loggingEnabled) { newValue in + AppGroupConfig.loggingEnabled = newValue + AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") + } + } + + if loggingEnabled { + VStack(alignment: .leading, spacing: 6) { + Text(NSLocalizedString("Log Level", comment: "")) + .font(.system(size: 11)) + .foregroundColor(.secondary) + + Picker("", selection: $logLevel) { + ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in + Text(level.capitalized).tag(level) + } + } + .pickerStyle(.radioGroup) + .onChange(of: logLevel) { newValue in + AppGroupConfig.logLevel = newValue + AppLogger.log(.info, "Log level: \(newValue)") + } + } + } + + Text(NSLocalizedString("Restart VPN to apply", comment: "")) + .font(.system(size: 9)) + .foregroundColor(.secondary) + + Spacer() + } + .padding(16) + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/LogOptionsView.swift +git commit -m "feat: add LogOptionsView (toggle + level picker)" +``` + +--- + +### Task 3: Create LogContentView + +**Files:** +- Create: `macos/GostX/LogContentView.swift` + +**Interfaces:** +- Consumes: `LogViewModel` (lines, isFollowing, scrollProxy, onAppear, onDisappear, copyAll, clearLog), `loggingEnabled` Bool binding +- Produces: `struct LogContentView: View` + +- [ ] **Step 1: Write LogContentView** + +Create `macos/GostX/LogContentView.swift`: + +```swift +// macos/GostX/LogContentView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct LogContentView: View { + @ObservedObject var vm: LogViewModel + let loggingEnabled: Bool + + var body: some View { + VStack(spacing: 0) { + // Toolbar + HStack(spacing: 8) { + Button(action: { vm.isFollowing.toggle() }) { + Image(systemName: vm.isFollowing ? "pause.fill" : "play.fill") + } + .buttonStyle(.borderless) + .help(vm.isFollowing + ? NSLocalizedString("Pause auto-scroll", comment: "") + : NSLocalizedString("Resume auto-scroll", comment: "")) + + Divider() + .frame(height: 16) + + Button(action: { vm.copyAll() }) { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Copy all", comment: "")) + + Button(action: { vm.clearLog() }) { + Image(systemName: "trash") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Clear log", comment: "")) + + Spacer() + + Text("\(vm.lines.count) lines") + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + + Divider() + + // Content + if !loggingEnabled { + VStack { + Spacer() + Image(systemName: "text.alignleft") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Logging is off", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if vm.lines.isEmpty { + VStack { + Spacer() + Image(systemName: "text.alignleft") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("No logs", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + ScrollViewReader { proxy in + ScrollView { + LazyVStack(alignment: .leading, spacing: 1) { + ForEach(Array(vm.lines.enumerated()), id: \.offset) { _, line in + Text(line) + .font(.system(size: 11, design: .monospaced)) + .textSelection(.enabled) + } + } + .padding(8) + } + .background(Color(nsColor: .textBackgroundColor)) + .onAppear { vm.scrollProxy = proxy } + } + } + } + .onAppear { vm.onAppear() } + .onDisappear { vm.onDisappear() } + .onChange(of: vm.isFollowing) { _ in + if vm.isFollowing, let proxy = vm.scrollProxy, !vm.lines.isEmpty { + proxy.scrollTo(vm.lines.count - 1, anchor: .bottom) + } + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/LogContentView.swift +git commit -m "feat: add LogContentView (scrollable lines + toolbar)" +``` + +--- + +### Task 4: Create ProfileListView + +**Files:** +- Create: `macos/GostX/ProfileListView.swift` + +**Interfaces:** +- Consumes: `ConfigRepository.shared` (ObservedObject), `@Binding var selectedProfileId: String?` +- Produces: `struct ProfileListView: View` — profile list with add/rename/delete + +- [ ] **Step 1: Write ProfileListView** + +Extract the profile list sidebar from `SettingsView.swift` into `macos/GostX/ProfileListView.swift`: + +```swift +// macos/GostX/ProfileListView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct ProfileListView: View { + @ObservedObject var repo = ConfigRepository.shared + @Binding var selectedProfileId: String? + @State private var showAddSheet = false + @State private var showRenameSheet = false + @State private var newProfileName = "" + @State private var renameTargetId: String? = nil + + var body: some View { + VStack(spacing: 0) { + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + Label(profile.name, systemImage: "doc.text") + .tag(profile.id) + .contextMenu { + Button(NSLocalizedString("Rename...", comment: "")) { + renameTargetId = profile.id + newProfileName = profile.name + showRenameSheet = true + } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { + repo.deleteProfile(profile.id) + } + } + } + } + .listStyle(.plain) + + Divider() + + HStack { + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + } + .buttonStyle(.borderless) + Spacer() + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + .onAppear { + if selectedProfileId == nil, let first = repo.profiles.first { + selectedProfileId = first.id + } + } + .sheet(isPresented: $showAddSheet) { + addProfileSheet + } + .sheet(isPresented: $showRenameSheet) { + renameProfileSheet + } + } + + private var addProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("New Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showAddSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Add", comment: "")) { + if !newProfileName.isEmpty { + let newId = repo.addProfile(name: newProfileName) + if let id = newId { selectedProfileId = id } + newProfileName = "" + showAddSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } + + private var renameProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showRenameSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let id = renameTargetId, !newProfileName.isEmpty { + _ = repo.renameProfile(id, newName: newProfileName) + newProfileName = "" + renameTargetId = nil + showRenameSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/ProfileListView.swift +git commit -m "feat: add ProfileListView extracted from SettingsView" +``` + +--- + +### Task 5: Create FileListView + +**Files:** +- Create: `macos/GostX/FileListView.swift` + +**Interfaces:** +- Consumes: `@ObservedObject var vm: FileManageViewModel` +- Produces: `struct FileListView: View` — file list + context menu + import/new/rename/delete + +- [ ] **Step 1: Write FileListView** + +Extract the sidebar portion from `FileManageView` into `macos/GostX/FileListView.swift`. Keep all the dialogs, sheets, and modifiers that belong to the list. Note: `formatFileSize`, `formatFileDate`, `FileRowView`, `FileManageAlertModifier`, `OverwriteSheetModifier`, and `FileManageDialogsModifier` move from `FileManageView.swift` to here since they are all used by the list sidebar. + +```swift +// macos/GostX/FileListView.swift +import SwiftUI +import AppKit + +// MARK: - Helpers + +private func formatFileSize(_ bytes: Int64) -> String { + switch bytes { + case 0..<1024: return "\(bytes) B" + case 1024..<(1024 * 1024): return "\(bytes / 1024) KB" + default: return String(format: "%.1f MB", Double(bytes) / (1024.0 * 1024.0)) + } +} + +private func formatFileDate(_ date: Date) -> String { + let fmt = DateFormatter() + fmt.dateFormat = "yyyy-MM-dd HH:mm" + return fmt.string(from: date) +} + +// MARK: - FileListView + +@available(macOS 14.0, *) +struct FileListView: View { + @ObservedObject var vm: FileManageViewModel + @State private var showImporter = false + @State private var showExporter = false + @State private var exportName: String? + @State private var renameTarget: FileInfo? + @State private var renameText: String = "" + @State private var deleteTarget: FileInfo? + @State private var showDeleteConfirm = false + @State private var showNewFileSheet = false + @State private var newFileName = "" + + var body: some View { + VStack(spacing: 0) { + if vm.files.isEmpty { + VStack(spacing: 8) { + Spacer() + Text(NSLocalizedString("No files", comment: "")) + .font(.system(size: 12)) + .foregroundColor(.secondary) + Spacer() + } + } else { + List(selection: Binding( + get: { vm.selectedFileName }, + set: { name in + DispatchQueue.main.async { + if let name { + vm.selectFile(name) + } else { + vm.selectedFileName = nil + vm.fileContent = "" + } + } + } + )) { + ForEach(vm.files) { file in + FileRowView( + file: file, + onExport: { + exportName = file.name + showExporter = true + }, + onRename: { + renameTarget = file + renameText = file.name + }, + onCopyPath: { vm.copyPath(file.name) }, + onDelete: { + deleteTarget = file + showDeleteConfirm = true + } + ) + .tag(file.name) + } + } + .listStyle(.plain) + } + + Divider() + + HStack { + Button(action: { showNewFileSheet = true }) { + Label(NSLocalizedString("New File", comment: ""), systemImage: "doc.badge.plus") + } + .buttonStyle(.borderless) + Spacer() + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import", comment: ""), systemImage: "square.and.arrow.up") + } + .buttonStyle(.borderless) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .fileExporter( + isPresented: $showExporter, + item: exportName ?? "", + contentTypes: [.data], + defaultFilename: exportName ?? "file" + ) { result in + if case .success(let url) = result, let name = exportName { + vm.exportFile(name, to: url) + } + exportName = nil + } + .sheet(isPresented: $showNewFileSheet) { + VStack(spacing: 16) { + Text(NSLocalizedString("New File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $newFileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { + showNewFileSheet = false + newFileName = "" + } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Create", comment: "")) { + vm.createFile(newFileName) + newFileName = "" + showNewFileSheet = false + } + .keyboardShortcut(.defaultAction) + .disabled(newFileName.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + .modifier(FileManageDialogsModifier( + renameTarget: $renameTarget, + renameText: $renameText, + deleteTarget: $deleteTarget, + showDeleteConfirm: $showDeleteConfirm, + vm: vm + )) + } +} + +// MARK: - FileRowView + +@available(macOS 14.0, *) +private struct FileRowView: View { + let file: FileInfo + let onExport: () -> Void + let onRename: () -> Void + let onCopyPath: () -> Void + let onDelete: () -> Void + + var body: some View { + HStack(spacing: 12) { + Image(systemName: "doc.text") + .font(.system(size: 16)) + .foregroundColor(.secondary) + .frame(width: 20) + + VStack(alignment: .leading, spacing: 1) { + Text(file.name) + .font(.system(size: 13)) + .lineLimit(1) + Text(formatFileSize(file.sizeBytes)) + .font(.system(size: 10)) + .foregroundColor(.secondary) + Text(formatFileDate(file.lastModified)) + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + + Spacer() + } + .padding(.vertical, 4) + .contextMenu { + Button(NSLocalizedString("Export...", comment: "")) { onExport() } + Button(NSLocalizedString("Rename...", comment: "")) { onRename() } + Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } + } + } +} + +// MARK: - Alert Modifier + +@available(macOS 14.0, *) +private struct FileManageAlertModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.alert( + NSLocalizedString("Error", comment: ""), + isPresented: Binding( + get: { vm.alertMessage != nil }, + set: { if !$0 { vm.alertMessage = nil } } + ), + actions: { + Button(NSLocalizedString("OK", comment: "")) { vm.alertMessage = nil } + }, + message: { + Text(vm.alertMessage ?? "") + } + ) + } +} + +// MARK: - Overwrite Sheet Modifier + +@available(macOS 14.0, *) +private struct OverwriteSheetModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.sheet(isPresented: Binding( + get: { vm.pendingOverwrite != nil }, + set: { if !$0 { vm.cancelOverwrite() } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("File Already Exists", comment: "")) + .font(.headline) + Text(String.localizedStringWithFormat( + NSLocalizedString("A file named \"%@\" already exists. Do you want to replace it?", comment: ""), + vm.pendingOverwrite?.fileName ?? "")) + .font(.system(size: 13)) + .multilineTextAlignment(.center) + .frame(width: 300) + HStack(spacing: 12) { + Button(NSLocalizedString("Cancel", comment: "")) { vm.cancelOverwrite() } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Replace", comment: "")) { vm.confirmOverwrite() } + .keyboardShortcut(.defaultAction) + } + } + .padding() + .frame(width: 340, height: 160) + } + } +} + +// MARK: - Rename / Delete Dialogs Modifier + +@available(macOS 14.0, *) +private struct FileManageDialogsModifier: ViewModifier { + @Binding var renameTarget: FileInfo? + @Binding var renameText: String + @Binding var deleteTarget: FileInfo? + @Binding var showDeleteConfirm: Bool + let vm: FileManageViewModel + + func body(content: Content) -> some View { + content + .sheet(isPresented: Binding( + get: { renameTarget != nil }, + set: { if !$0 { renameTarget = nil } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $renameText) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { renameTarget = nil } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let target = renameTarget, !renameText.trimmingCharacters(in: .whitespaces).isEmpty { + vm.renameFile(target.name, to: renameText) + renameTarget = nil + } + } + .keyboardShortcut(.defaultAction) + .disabled(renameText.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 320, height: 150) + } + .alert( + NSLocalizedString("Delete File", comment: ""), + isPresented: $showDeleteConfirm, + presenting: deleteTarget + ) { file in + Button(NSLocalizedString("Cancel", comment: ""), role: .cancel) { + deleteTarget = nil + } + Button(NSLocalizedString("Delete", comment: ""), role: .destructive) { + vm.deleteFile(file.name) + deleteTarget = nil + } + } message: { file in + Text(String.localizedStringWithFormat( + NSLocalizedString("Are you sure you want to delete \"%@\"? This cannot be undone.", comment: ""), + file.name)) + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/FileListView.swift +git commit -m "feat: add FileListView extracted from FileManageView" +``` + +--- + +### Task 6: Create FileContentView + +**Files:** +- Create: `macos/GostX/FileContentView.swift` + +**Interfaces:** +- Consumes: `@ObservedObject var vm: FileManageViewModel` (reads `fileContent`, `isFileDirty`, `selectedFileName`; calls `saveFileContent()`) +- Produces: `struct FileContentView: View` + +- [ ] **Step 1: Write FileContentView** + +Extract the detail pane from `FileManageView` into `macos/GostX/FileContentView.swift`: + +```swift +// macos/GostX/FileContentView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct FileContentView: View { + @ObservedObject var vm: FileManageViewModel + + var body: some View { + Group { + if vm.selectedFileName != nil { + VStack(spacing: 0) { + TextEditor(text: $vm.fileContent) + .font(.system(size: 12, design: .monospaced)) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onChange(of: vm.fileContent) { _ in + DispatchQueue.main.async { + vm.isFileDirty = vm.fileContent != vm.originalContent + } + } + + Divider() + + HStack { + Spacer() + Button(action: { vm.saveFileContent() }) { + Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") + } + .buttonStyle(.borderless) + .disabled(!vm.isFileDirty) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + } else { + VStack { + Image(systemName: "doc.text.magnifyingglass") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Select a file to view", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/FileContentView.swift +git commit -m "feat: add FileContentView extracted from FileManageView" +``` + +--- + +### Task 7: Rewrite SettingsView as NavigationSplitView + +**Files:** +- Modify: `macos/GostX/SettingsView.swift` + +**Interfaces:** +- Consumes: All new views from Tasks 1-6, `ConfigRepository.shared`, `FileManageViewModel`, `LogViewModel` +- Produces: `struct SettingsView: View` using `NavigationSplitView` + +**Note:** The `YamlEditorView` struct stays in this file unchanged. Only `SettingsView` is rewritten. We add the `SettingsCategory` enum and `FileManageViewModel` / `LogViewModel` as `@StateObject`. + +- [ ] **Step 1: Rewrite SettingsView.swift** + +Replace the entire contents of `macos/GostX/SettingsView.swift`: + +```swift +// macos/GostX/SettingsView.swift +import SwiftUI + +// MARK: - Category + +enum SettingsCategory: String, CaseIterable, Identifiable { + case profiles + case files + case logs + + var id: Self { self } + + var icon: String { + switch self { + case .profiles: return "doc.text" + case .files: return "folder" + case .logs: return "text.alignleft" + } + } + + var label: String { + switch self { + case .profiles: return NSLocalizedString("Profiles", comment: "") + case .files: return NSLocalizedString("Files", comment: "") + case .logs: return NSLocalizedString("Logs", comment: "") + } + } +} + +// MARK: - SettingsView + +@available(macOS 14.0, *) +struct SettingsView: View { + @State private var selectedCategory: SettingsCategory = .profiles + + // Profiles + @State private var selectedProfileId: String? = nil + + // Files + @StateObject private var fileVM = FileManageViewModel() + + // Logs + @StateObject private var logVM = LogViewModel() + @State private var loggingEnabled = AppGroupConfig.loggingEnabled + + var body: some View { + NavigationSplitView { + // Sidebar + List(selection: $selectedCategory) { + ForEach(SettingsCategory.allCases) { category in + Label(category.label, systemImage: category.icon) + .tag(category) + } + } + .listStyle(.sidebar) + .navigationSplitViewColumnWidth(min: 140, ideal: 160, max: 200) + } content: { + // Content + switch selectedCategory { + case .profiles: + ProfileListView(selectedProfileId: $selectedProfileId) + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) + case .files: + FileListView(vm: fileVM) + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) + case .logs: + LogOptionsView() + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) + } + } detail: { + // Detail + switch selectedCategory { + case .profiles: + if let profileId = selectedProfileId { + YamlEditorView(profileId: profileId) + .id(profileId) + } else { + placeholderView( + icon: "doc.text.magnifyingglass", + text: NSLocalizedString("Select a profile to edit", comment: "") + ) + } + case .files: + if fileVM.selectedFileName != nil { + FileContentView(vm: fileVM) + } else { + placeholderView( + icon: "doc.text.magnifyingglass", + text: NSLocalizedString("Select a file to view", comment: "") + ) + } + case .logs: + LogContentView(vm: logVM, loggingEnabled: loggingEnabled) + .onAppear { + loggingEnabled = AppGroupConfig.loggingEnabled + } + } + } + .frame(minWidth: 700, minHeight: 440) + } + + private func placeholderView(icon: String, text: String) -> some View { + VStack { + Image(systemName: icon) + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(text) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} + +// MARK: - YAML Editor View + +@available(macOS 14.0, *) +struct YamlEditorView: View { + let profileId: String + @StateObject private var repo = ConfigRepository.shared + @State private var yamlText: String = "" + @State private var isDirty = false + @State private var originalText: String = "" + + var body: some View { + VStack(spacing: 0) { + YamlTextView(text: $yamlText) + .onChange(of: yamlText) { newValue in + isDirty = newValue != originalText + } + .onAppear { + let text = repo.getConfig(profileId) + yamlText = text + originalText = text + isDirty = false + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + Divider() + + HStack { + Spacer() + Button(action: { + save() + originalText = yamlText + isDirty = false + }) { + Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") + } + .buttonStyle(.borderless) + .disabled(!isDirty) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + } + + private func save() { + repo.saveConfig(profileId, yaml: yamlText) + if profileId == repo.activeProfileId { + AppGroupConfig.writeYaml(yamlText) + } + } +} + +@available(macOS 14.0, *) +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add macos/GostX/SettingsView.swift +git commit -m "refactor: rewrite SettingsView as NavigationSplitView" +``` + +--- + +### Task 8: Delete old files and update Xcode project + +**Files:** +- Delete: `macos/GostX/LogView.swift` +- Delete: `macos/GostX/FileManageView.swift` +- Modify: `macos/GostX.xcodeproj/project.pbxproj` + +- [ ] **Step 1: Delete old Swift files** + +```bash +rm macos/GostX/LogView.swift macos/GostX/FileManageView.swift +``` + +- [ ] **Step 2: Update Xcode project file** + +Use `ruby` to update `project.pbxproj` — remove the two old file references and add the six new ones: + +```bash +cd macos +ruby -e ' +require "xcodeproj" + +project = Xcodeproj::Project.open("GostX.xcodeproj") +target = project.targets.find { |t| t.name == "GostX" } +group = project.main_group.find_subpath("GostX", true) + +# Remove old files +["LogView.swift", "FileManageView.swift"].each do |name| + group.files.find { |f| f.path == name }&.remove_from_project +end + +# Add new files +["LogViewModel.swift", "LogOptionsView.swift", "LogContentView.swift", + "ProfileListView.swift", "FileListView.swift", "FileContentView.swift"].each do |name| + path = File.join(File.dirname(__FILE__), name) + File.write(path, "") unless File.exist?(path) # file already created by us + ref = group.new_file(name) + target.source_build_phase.add_file_reference(ref) if target +end + +project.save +puts "Updated project.pbxproj: removed 2 old files, added 6 new files" +' +``` + +If `xcodeproj` gem is not installed, install it first: `gem install xcodeproj` + +Alternatively, open the project in Xcode, remove `LogView.swift` and `FileManageView.swift` from the project navigator (choose "Remove Reference"), then drag the 6 new `.swift` files into the GostX group. + +- [ ] **Step 3: Verify the project builds** + +```bash +cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Debug -destination 'platform=macOS' ONLY_ACTIVE_ARCH=YES build 2>&1 | tail -30 +``` +Expected: **BUILD SUCCEEDED** + +- [ ] **Step 4: Commit** + +```bash +git add macos/GostX/LogView.swift macos/GostX/FileManageView.swift macos/GostX.xcodeproj/project.pbxproj +git commit -m "chore: remove old LogView/FileManageView, add new split views to Xcode project" +``` + +--- + +### Task 9: Build and run verification + +**Files:** None (verification only) + +- [ ] **Step 1: Clean build** + +```bash +cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Debug -destination 'platform=macOS' ONLY_ACTIVE_ARCH=YES clean build 2>&1 | tail -30 +``` +Expected: **BUILD SUCCEEDED** with zero warnings from our files. + +- [ ] **Step 2: Run tests** + +```bash +cd macos && xcodebuild test -project GostX.xcodeproj -scheme GostX -destination 'platform=macOS' ONLY_ACTIVE_ARCH=YES 2>&1 | tail -30 +``` +Expected: All tests pass. + +- [ ] **Step 3: Manual QA checklist** (run the app): + +1. Open app → Settings window shows three-column NavigationSplitView +2. Click "Profiles" in sidebar → Profile list in Content, YAML editor in Detail +3. Select a profile → YAML editor loads its config +4. Click "Files" → File list in Content, file content editor in Detail +5. Select a file → content loads in editor; edit + save works +6. Click "Logs" → Log options in Content (toggle + level), log content in Detail +7. Toggle logging off → Detail shows "Logging is off" placeholder +8. Resize window narrow → sidebar collapses; system toggle appears in toolbar From cbe18423f506c341b88906b78f11c04798f3b423 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:11:59 +0800 Subject: [PATCH 62/89] feat: add LogViewModel with polling, copy, and clear --- macos/GostX.xcodeproj/project.pbxproj | 23 ++++++--- macos/GostX/LogViewModel.swift | 68 +++++++++++++++++++++++++++ macos/GostXTests/GostXTests.swift | 48 +++++++++++++++++++ 3 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 macos/GostX/LogViewModel.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index d6f4c93..5a47658 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A429DD6E90008AA944 /* FileRepository.swift */; }; B5C1CF1429DD6E7C008AA934 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */; }; B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; + B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */; }; E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E9D6522B494B409C95F0FE /* AppLogger.swift */; }; @@ -82,6 +83,7 @@ 4303495C3004D78100FD9EE9 /* Libgost.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libgost.xcframework; path = Frameworks/Libgost.xcframework; sourceTree = ""; }; 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigRepository.swift; sourceTree = ""; }; 64B1478690C1481980B302A8 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = ""; }; + 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogViewModel.swift; sourceTree = ""; }; AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DevelopmentTeam.xcconfig; sourceTree = ""; }; B532A666285B528900159BAC /* GostX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GostX.app; sourceTree = BUILT_PRODUCTS_DIR; }; B532A669285B528900159BAC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; @@ -105,7 +107,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, @@ -115,7 +117,18 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; + 430349453004D3A000FD9EE9 /* GostXTunnel */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */, + ); + explicitFileTypes = { + }; + explicitFolders = ( + ); + path = GostXTunnel; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -214,6 +227,7 @@ B5B1C1A229DD6E90008AA942 /* FileManageView.swift */, B5B1C1A429DD6E90008AA944 /* FileRepository.swift */, 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */, + 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */, ); path = GostX; sourceTree = ""; @@ -290,8 +304,6 @@ 4303494B3004D3A000FD9EE9 /* PBXTargetDependency */, ); name = GostX; - packageProductDependencies = ( - ); productName = GostX; productReference = B532A666285B528900159BAC /* GostX.app */; productType = "com.apple.product-type.application"; @@ -368,8 +380,6 @@ "zh-Hans", ); mainGroup = B532A65D285B528900159BAC; - packageReferences = ( - ); productRefGroup = B532A667285B528900159BAC /* Products */; projectDirPath = ""; projectRoot = ""; @@ -443,6 +453,7 @@ 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */, B532A66A285B528900159BAC /* main.swift in Sources */, E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */, + B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/LogViewModel.swift b/macos/GostX/LogViewModel.swift new file mode 100644 index 0000000..35eb7da --- /dev/null +++ b/macos/GostX/LogViewModel.swift @@ -0,0 +1,68 @@ +// macos/GostX/LogViewModel.swift +import SwiftUI +import Combine + +@MainActor +class LogViewModel: ObservableObject { + @Published var lines: [String] = [] + @Published var isFollowing = true + + var scrollProxy: ScrollViewProxy? + private var timer: Timer? + + private var logFileURL: URL? { + AppGroupConfig.containerURL?.appendingPathComponent("gost.log") + } + + func onAppear() { + loadLog() + startPolling() + } + + func onDisappear() { + stopPolling() + } + + func copyAll() { + let text = lines.joined(separator: "\n") + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(text, forType: .string) + } + + func clearLog() { + guard let url = logFileURL else { return } + try? "".write(to: url, atomically: true, encoding: .utf8) + lines = [] + } + + // MARK: - Private + + private func loadLog() { + guard let url = logFileURL, + FileManager.default.fileExists(atPath: url.path), + let content = try? String(contentsOf: url, encoding: .utf8) + else { + lines = [] + return + } + lines = content.components(separatedBy: "\n").filter { !$0.isEmpty } + } + + private func startPolling() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + guard let self else { return } + DispatchQueue.main.async { + let oldCount = self.lines.count + self.loadLog() + if self.isFollowing, self.lines.count > oldCount, let proxy = self.scrollProxy { + proxy.scrollTo(self.lines.count - 1, anchor: .bottom) + } + } + } + } + + private func stopPolling() { + timer?.invalidate() + timer = nil + } +} diff --git a/macos/GostXTests/GostXTests.swift b/macos/GostXTests/GostXTests.swift index ff65854..0d4f864 100644 --- a/macos/GostXTests/GostXTests.swift +++ b/macos/GostXTests/GostXTests.swift @@ -34,3 +34,51 @@ class GostXTests: XCTestCase { } } + +@MainActor +class LogViewModelTests: XCTestCase { + var vm: LogViewModel! + var tempDir: URL! + + override func setUpWithError() throws { + tempDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) + try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + // Write a known log file + let log = tempDir.appendingPathComponent("gost.log") + try "line one\nline two\nline three\n".write(to: log, atomically: true, encoding: .utf8) + // Override containerURL to return our temp dir + // We test load/clear/copy independently by writing to a temp file + vm = LogViewModel() + } + + override func tearDownWithError() throws { + vm.onDisappear() + try? FileManager.default.removeItem(at: tempDir) + vm = nil + } + + func testInitialState() { + XCTAssertTrue(vm.isFollowing) + // lines depend on file existence; without a real log file they start empty + } + + func testClearLog() { + vm.clearLog() + // After clear, lines should be empty + XCTAssertEqual(vm.lines.count, 0) + } + + func testCopyAll() { + // lines is empty by default in unit test env (no App Group file) + vm.copyAll() + // Pasteboard should contain empty string + XCTAssertEqual(NSPasteboard.general.string(forType: .string), "") + } + + func testIsFollowingToggle() { + vm.isFollowing = false + XCTAssertFalse(vm.isFollowing) + vm.isFollowing = true + XCTAssertTrue(vm.isFollowing) + } +} From 612117159af5b6c573e8c52957b2fabdea923538 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:16:36 +0800 Subject: [PATCH 63/89] fix: make LogViewModel logFileURL injectable for testability --- macos/GostX/LogViewModel.swift | 5 +++-- macos/GostXTests/GostXTests.swift | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/macos/GostX/LogViewModel.swift b/macos/GostX/LogViewModel.swift index 35eb7da..f513429 100644 --- a/macos/GostX/LogViewModel.swift +++ b/macos/GostX/LogViewModel.swift @@ -9,9 +9,10 @@ class LogViewModel: ObservableObject { var scrollProxy: ScrollViewProxy? private var timer: Timer? + private let logFileURL: URL? - private var logFileURL: URL? { - AppGroupConfig.containerURL?.appendingPathComponent("gost.log") + init(logFileURL: URL? = nil) { + self.logFileURL = logFileURL ?? AppGroupConfig.containerURL?.appendingPathComponent("gost.log") } func onAppear() { diff --git a/macos/GostXTests/GostXTests.swift b/macos/GostXTests/GostXTests.swift index 0d4f864..d9bf242 100644 --- a/macos/GostXTests/GostXTests.swift +++ b/macos/GostXTests/GostXTests.swift @@ -39,16 +39,15 @@ class GostXTests: XCTestCase { class LogViewModelTests: XCTestCase { var vm: LogViewModel! var tempDir: URL! + var tempLogURL: URL! override func setUpWithError() throws { tempDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) - // Write a known log file - let log = tempDir.appendingPathComponent("gost.log") - try "line one\nline two\nline three\n".write(to: log, atomically: true, encoding: .utf8) - // Override containerURL to return our temp dir - // We test load/clear/copy independently by writing to a temp file - vm = LogViewModel() + tempLogURL = tempDir.appendingPathComponent("gost.log") + try "line one\nline two\nline three\n".write(to: tempLogURL, atomically: true, encoding: .utf8) + vm = LogViewModel(logFileURL: tempLogURL) + vm.onAppear() } override func tearDownWithError() throws { @@ -57,22 +56,30 @@ class LogViewModelTests: XCTestCase { vm = nil } + func testLoadsLinesFromFile() { + XCTAssertEqual(vm.lines.count, 3) + XCTAssertEqual(vm.lines[0], "line one") + XCTAssertEqual(vm.lines[1], "line two") + XCTAssertEqual(vm.lines[2], "line three") + } + func testInitialState() { XCTAssertTrue(vm.isFollowing) - // lines depend on file existence; without a real log file they start empty } - func testClearLog() { + func testClearLogTruncatesFile() { vm.clearLog() - // After clear, lines should be empty XCTAssertEqual(vm.lines.count, 0) + // Verify the temp file was truncated, not the real one + let content = try? String(contentsOf: tempLogURL, encoding: .utf8) + XCTAssertEqual(content, "") } func testCopyAll() { - // lines is empty by default in unit test env (no App Group file) vm.copyAll() - // Pasteboard should contain empty string - XCTAssertEqual(NSPasteboard.general.string(forType: .string), "") + let pb = NSPasteboard.general.string(forType: .string) + XCTAssertTrue(pb?.contains("line one") ?? false) + XCTAssertTrue(pb?.contains("line three") ?? false) } func testIsFollowingToggle() { From 3cb1085883a92e32dff8ebc2cbc4cf56f1b9439d Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:18:23 +0800 Subject: [PATCH 64/89] feat: add LogOptionsView (toggle + level picker) --- macos/GostX.xcodeproj/project.pbxproj | 4 +++ macos/GostX/LogOptionsView.swift | 51 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 macos/GostX/LogOptionsView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 5a47658..de742c2 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; + 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */; }; 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B1478690C1481980B302A8 /* LogView.swift */; }; 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; @@ -77,6 +78,7 @@ 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YamlEditorView.swift; sourceTree = ""; }; 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; + 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogOptionsView.swift; sourceTree = ""; }; 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 430349433004D3A000FD9EE9 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; @@ -228,6 +230,7 @@ B5B1C1A429DD6E90008AA944 /* FileRepository.swift */, 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */, 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */, + 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */, ); path = GostX; sourceTree = ""; @@ -454,6 +457,7 @@ B532A66A285B528900159BAC /* main.swift in Sources */, E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */, B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */, + 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/LogOptionsView.swift b/macos/GostX/LogOptionsView.swift new file mode 100644 index 0000000..6abd9b8 --- /dev/null +++ b/macos/GostX/LogOptionsView.swift @@ -0,0 +1,51 @@ +// macos/GostX/LogOptionsView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct LogOptionsView: View { + @State private var loggingEnabled = AppGroupConfig.loggingEnabled + @State private var logLevel = AppGroupConfig.logLevel + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Toggle(isOn: $loggingEnabled) { + Text(NSLocalizedString("Logging", comment: "")) + .font(.system(size: 12, weight: .medium)) + } + .toggleStyle(.switch) + .controlSize(.small) + .onChange(of: loggingEnabled) { newValue in + AppGroupConfig.loggingEnabled = newValue + AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") + } + } + + if loggingEnabled { + VStack(alignment: .leading, spacing: 6) { + Text(NSLocalizedString("Log Level", comment: "")) + .font(.system(size: 11)) + .foregroundColor(.secondary) + + Picker("", selection: $logLevel) { + ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in + Text(level.capitalized).tag(level) + } + } + .pickerStyle(.radioGroup) + .onChange(of: logLevel) { newValue in + AppGroupConfig.logLevel = newValue + AppLogger.log(.info, "Log level: \(newValue)") + } + } + } + + Text(NSLocalizedString("Restart VPN to apply", comment: "")) + .font(.system(size: 9)) + .foregroundColor(.secondary) + + Spacer() + } + .padding(16) + } +} From f024d83f50d80f267a90d26aa5855e54ca061c04 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:20:30 +0800 Subject: [PATCH 65/89] feat: add LogContentView (scrollable lines + toolbar) --- macos/GostX.xcodeproj/project.pbxproj | 4 ++ macos/GostX/LogContentView.swift | 98 +++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 macos/GostX/LogContentView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index de742c2..7d7d5d2 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */; }; + 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19495E70527B5668EC230C85 /* LogContentView.swift */; }; 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B1478690C1481980B302A8 /* LogView.swift */; }; 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; @@ -76,6 +77,7 @@ /* Begin PBXFileReference section */ 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; + 19495E70527B5668EC230C85 /* LogContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogContentView.swift; sourceTree = ""; }; 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YamlEditorView.swift; sourceTree = ""; }; 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogOptionsView.swift; sourceTree = ""; }; @@ -231,6 +233,7 @@ 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */, 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */, 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */, + 19495E70527B5668EC230C85 /* LogContentView.swift */, ); path = GostX; sourceTree = ""; @@ -458,6 +461,7 @@ E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */, B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */, 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */, + 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/LogContentView.swift b/macos/GostX/LogContentView.swift new file mode 100644 index 0000000..461f5ce --- /dev/null +++ b/macos/GostX/LogContentView.swift @@ -0,0 +1,98 @@ +// macos/GostX/LogContentView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct LogContentView: View { + @ObservedObject var vm: LogViewModel + let loggingEnabled: Bool + + var body: some View { + VStack(spacing: 0) { + // Toolbar + HStack(spacing: 8) { + Button(action: { vm.isFollowing.toggle() }) { + Image(systemName: vm.isFollowing ? "pause.fill" : "play.fill") + } + .buttonStyle(.borderless) + .help(vm.isFollowing + ? NSLocalizedString("Pause auto-scroll", comment: "") + : NSLocalizedString("Resume auto-scroll", comment: "")) + + Divider() + .frame(height: 16) + + Button(action: { vm.copyAll() }) { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Copy all", comment: "")) + + Button(action: { vm.clearLog() }) { + Image(systemName: "trash") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Clear log", comment: "")) + + Spacer() + + Text("\(vm.lines.count) lines") + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + + Divider() + + // Content + if !loggingEnabled { + VStack { + Spacer() + Image(systemName: "text.alignleft") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Logging is off", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if vm.lines.isEmpty { + VStack { + Spacer() + Image(systemName: "text.alignleft") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("No logs", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + ScrollViewReader { proxy in + ScrollView { + LazyVStack(alignment: .leading, spacing: 1) { + ForEach(Array(vm.lines.enumerated()), id: \.offset) { _, line in + Text(line) + .font(.system(size: 11, design: .monospaced)) + .textSelection(.enabled) + } + } + .padding(8) + } + .background(Color(nsColor: .textBackgroundColor)) + .onAppear { vm.scrollProxy = proxy } + } + } + } + .onAppear { vm.onAppear() } + .onDisappear { vm.onDisappear() } + .onChange(of: vm.isFollowing) { _ in + if vm.isFollowing, let proxy = vm.scrollProxy, !vm.lines.isEmpty { + proxy.scrollTo(vm.lines.count - 1, anchor: .bottom) + } + } + } +} From 92db74bec500b2f4d62df555b5a33240e8fdc99e Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:22:37 +0800 Subject: [PATCH 66/89] feat: add ProfileListView extracted from SettingsView --- macos/GostX.xcodeproj/project.pbxproj | 4 + macos/GostX/ProfileListView.swift | 109 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 macos/GostX/ProfileListView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 7d7d5d2..3c92112 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */; }; + 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; B532A66E285B528D00159BAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A66D285B528D00159BAC /* Assets.xcassets */; }; B532A671285B528D00159BAC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B532A670285B528D00159BAC /* Preview Assets.xcassets */; }; @@ -79,6 +80,7 @@ 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; 19495E70527B5668EC230C85 /* LogContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogContentView.swift; sourceTree = ""; }; 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YamlEditorView.swift; sourceTree = ""; }; + 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProfileListView.swift; sourceTree = ""; }; 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PacketTunnelProvider.swift; path = GostXTunnel/PacketTunnelProvider.swift; sourceTree = ""; }; 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogOptionsView.swift; sourceTree = ""; }; 430349423004D3A000FD9EE9 /* GostXTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GostXTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -234,6 +236,7 @@ 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */, 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */, 19495E70527B5668EC230C85 /* LogContentView.swift */, + 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */, ); path = GostX; sourceTree = ""; @@ -462,6 +465,7 @@ B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */, 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */, 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */, + 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/ProfileListView.swift b/macos/GostX/ProfileListView.swift new file mode 100644 index 0000000..99065e7 --- /dev/null +++ b/macos/GostX/ProfileListView.swift @@ -0,0 +1,109 @@ +// macos/GostX/ProfileListView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct ProfileListView: View { + @ObservedObject var repo = ConfigRepository.shared + @Binding var selectedProfileId: String? + @State private var showAddSheet = false + @State private var showRenameSheet = false + @State private var newProfileName = "" + @State private var renameTargetId: String? = nil + + var body: some View { + VStack(spacing: 0) { + List(selection: $selectedProfileId) { + ForEach(repo.profiles) { profile in + Label(profile.name, systemImage: "doc.text") + .tag(profile.id) + .contextMenu { + Button(NSLocalizedString("Rename...", comment: "")) { + renameTargetId = profile.id + newProfileName = profile.name + showRenameSheet = true + } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { + repo.deleteProfile(profile.id) + } + } + } + } + .listStyle(.plain) + + Divider() + + HStack { + Button(action: { showAddSheet = true }) { + Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") + } + .buttonStyle(.borderless) + Spacer() + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + .onAppear { + if selectedProfileId == nil, let first = repo.profiles.first { + selectedProfileId = first.id + } + } + .sheet(isPresented: $showAddSheet) { + addProfileSheet + } + .sheet(isPresented: $showRenameSheet) { + renameProfileSheet + } + } + + private var addProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("New Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showAddSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Add", comment: "")) { + if !newProfileName.isEmpty { + let newId = repo.addProfile(name: newProfileName) + if let id = newId { selectedProfileId = id } + newProfileName = "" + showAddSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } + + private var renameProfileSheet: some View { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename Profile", comment: "")).font(.headline) + TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { showRenameSheet = false } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let id = renameTargetId, !newProfileName.isEmpty { + _ = repo.renameProfile(id, newName: newProfileName) + newProfileName = "" + renameTargetId = nil + showRenameSheet = false + } + } + .keyboardShortcut(.defaultAction) + .disabled(newProfileName.isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } +} From 16a25b92007703394efb2c98577c995d5f332c68 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:25:08 +0800 Subject: [PATCH 67/89] feat: add FileListView extracted from FileManageView --- macos/GostX.xcodeproj/project.pbxproj | 4 + macos/GostX/FileListView.swift | 305 ++++++++++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 macos/GostX/FileListView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 3c92112..1bbf868 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; + 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1099D8C22BC993593DE940 /* FileListView.swift */; }; 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */; }; 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; @@ -109,6 +110,7 @@ B5C1CF1529DD6E7C008AA934 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; B5C1CF1729DD6E90008AA934 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; B5F6387829DEFF31009A1287 /* Arguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arguments.swift; sourceTree = ""; }; + CC1099D8C22BC993593DE940 /* FileListView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FileListView.swift; sourceTree = ""; }; F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -237,6 +239,7 @@ 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */, 19495E70527B5668EC230C85 /* LogContentView.swift */, 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */, + CC1099D8C22BC993593DE940 /* FileListView.swift */, ); path = GostX; sourceTree = ""; @@ -466,6 +469,7 @@ 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */, 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */, 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */, + 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/FileListView.swift b/macos/GostX/FileListView.swift new file mode 100644 index 0000000..c4a9440 --- /dev/null +++ b/macos/GostX/FileListView.swift @@ -0,0 +1,305 @@ +// macos/GostX/FileListView.swift +import SwiftUI +import AppKit + +// MARK: - Helpers + +private func formatFileSize(_ bytes: Int64) -> String { + switch bytes { + case 0..<1024: return "\(bytes) B" + case 1024..<(1024 * 1024): return "\(bytes / 1024) KB" + default: return String(format: "%.1f MB", Double(bytes) / (1024.0 * 1024.0)) + } +} + +private func formatFileDate(_ date: Date) -> String { + let fmt = DateFormatter() + fmt.dateFormat = "yyyy-MM-dd HH:mm" + return fmt.string(from: date) +} + +// MARK: - FileListView + +@available(macOS 14.0, *) +struct FileListView: View { + @ObservedObject var vm: FileManageViewModel + @State private var showImporter = false + @State private var showExporter = false + @State private var exportName: String? + @State private var renameTarget: FileInfo? + @State private var renameText: String = "" + @State private var deleteTarget: FileInfo? + @State private var showDeleteConfirm = false + @State private var showNewFileSheet = false + @State private var newFileName = "" + + var body: some View { + VStack(spacing: 0) { + if vm.files.isEmpty { + VStack(spacing: 8) { + Spacer() + Text(NSLocalizedString("No files", comment: "")) + .font(.system(size: 12)) + .foregroundColor(.secondary) + Spacer() + } + } else { + List(selection: Binding( + get: { vm.selectedFileName }, + set: { name in + DispatchQueue.main.async { + if let name { + vm.selectFile(name) + } else { + vm.selectedFileName = nil + vm.fileContent = "" + } + } + } + )) { + ForEach(vm.files) { file in + FileRowView( + file: file, + onExport: { + exportName = file.name + showExporter = true + }, + onRename: { + renameTarget = file + renameText = file.name + }, + onCopyPath: { vm.copyPath(file.name) }, + onDelete: { + deleteTarget = file + showDeleteConfirm = true + } + ) + .tag(file.name) + } + } + .listStyle(.plain) + } + + Divider() + + HStack { + Button(action: { showNewFileSheet = true }) { + Label(NSLocalizedString("New File", comment: ""), systemImage: "doc.badge.plus") + } + .buttonStyle(.borderless) + Spacer() + Button(action: { showImporter = true }) { + Label(NSLocalizedString("Import", comment: ""), systemImage: "square.and.arrow.up") + } + .buttonStyle(.borderless) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in + if case .success(let url) = result { + vm.importFile(from: url) + } + } + .fileExporter( + isPresented: $showExporter, + item: exportName ?? "", + contentTypes: [.data], + defaultFilename: exportName ?? "file" + ) { result in + if case .success(let url) = result, let name = exportName { + vm.exportFile(name, to: url) + } + exportName = nil + } + .sheet(isPresented: $showNewFileSheet) { + VStack(spacing: 16) { + Text(NSLocalizedString("New File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $newFileName) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { + showNewFileSheet = false + newFileName = "" + } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Create", comment: "")) { + vm.createFile(newFileName) + newFileName = "" + showNewFileSheet = false + } + .keyboardShortcut(.defaultAction) + .disabled(newFileName.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 300, height: 140) + } + .modifier(FileManageAlertModifier(vm: vm)) + .modifier(OverwriteSheetModifier(vm: vm)) + .modifier(FileManageDialogsModifier( + renameTarget: $renameTarget, + renameText: $renameText, + deleteTarget: $deleteTarget, + showDeleteConfirm: $showDeleteConfirm, + vm: vm + )) + } +} + +// MARK: - FileRowView + +@available(macOS 14.0, *) +private struct FileRowView: View { + let file: FileInfo + let onExport: () -> Void + let onRename: () -> Void + let onCopyPath: () -> Void + let onDelete: () -> Void + + var body: some View { + HStack(spacing: 12) { + Image(systemName: "doc.text") + .font(.system(size: 16)) + .foregroundColor(.secondary) + .frame(width: 20) + + VStack(alignment: .leading, spacing: 1) { + Text(file.name) + .font(.system(size: 13)) + .lineLimit(1) + Text(formatFileSize(file.sizeBytes)) + .font(.system(size: 10)) + .foregroundColor(.secondary) + Text(formatFileDate(file.lastModified)) + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + + Spacer() + } + .padding(.vertical, 4) + .contextMenu { + Button(NSLocalizedString("Export...", comment: "")) { onExport() } + Button(NSLocalizedString("Rename...", comment: "")) { onRename() } + Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } + Divider() + Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } + } + } +} + +// MARK: - Alert Modifier + +@available(macOS 14.0, *) +private struct FileManageAlertModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.alert( + NSLocalizedString("Error", comment: ""), + isPresented: Binding( + get: { vm.alertMessage != nil }, + set: { if !$0 { vm.alertMessage = nil } } + ), + actions: { + Button(NSLocalizedString("OK", comment: "")) { vm.alertMessage = nil } + }, + message: { + Text(vm.alertMessage ?? "") + } + ) + } +} + +// MARK: - Overwrite Sheet Modifier + +@available(macOS 14.0, *) +private struct OverwriteSheetModifier: ViewModifier { + @ObservedObject var vm: FileManageViewModel + + func body(content: Content) -> some View { + content.sheet(isPresented: Binding( + get: { vm.pendingOverwrite != nil }, + set: { if !$0 { vm.cancelOverwrite() } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("File Already Exists", comment: "")) + .font(.headline) + Text(String.localizedStringWithFormat( + NSLocalizedString("A file named \"%@\" already exists. Do you want to replace it?", comment: ""), + vm.pendingOverwrite?.fileName ?? "")) + .font(.system(size: 13)) + .multilineTextAlignment(.center) + .frame(width: 300) + HStack(spacing: 12) { + Button(NSLocalizedString("Cancel", comment: "")) { vm.cancelOverwrite() } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Replace", comment: "")) { vm.confirmOverwrite() } + .keyboardShortcut(.defaultAction) + } + } + .padding() + .frame(width: 340, height: 160) + } + } +} + +// MARK: - Rename / Delete Dialogs Modifier + +@available(macOS 14.0, *) +private struct FileManageDialogsModifier: ViewModifier { + @Binding var renameTarget: FileInfo? + @Binding var renameText: String + @Binding var deleteTarget: FileInfo? + @Binding var showDeleteConfirm: Bool + let vm: FileManageViewModel + + func body(content: Content) -> some View { + content + .sheet(isPresented: Binding( + get: { renameTarget != nil }, + set: { if !$0 { renameTarget = nil } } + )) { + VStack(spacing: 16) { + Text(NSLocalizedString("Rename File", comment: "")).font(.headline) + TextField(NSLocalizedString("File name", comment: ""), text: $renameText) + .textFieldStyle(.roundedBorder) + .frame(width: 250) + HStack { + Button(NSLocalizedString("Cancel", comment: "")) { renameTarget = nil } + .keyboardShortcut(.cancelAction) + Button(NSLocalizedString("Rename", comment: "")) { + if let target = renameTarget, !renameText.trimmingCharacters(in: .whitespaces).isEmpty { + vm.renameFile(target.name, to: renameText) + renameTarget = nil + } + } + .keyboardShortcut(.defaultAction) + .disabled(renameText.trimmingCharacters(in: .whitespaces).isEmpty) + } + } + .padding() + .frame(width: 320, height: 150) + } + .alert( + NSLocalizedString("Delete File", comment: ""), + isPresented: $showDeleteConfirm, + presenting: deleteTarget + ) { file in + Button(NSLocalizedString("Cancel", comment: ""), role: .cancel) { + deleteTarget = nil + } + Button(NSLocalizedString("Delete", comment: ""), role: .destructive) { + vm.deleteFile(file.name) + deleteTarget = nil + } + } message: { file in + Text(String.localizedStringWithFormat( + NSLocalizedString("Are you sure you want to delete \"%@\"? This cannot be undone.", comment: ""), + file.name)) + } + } +} From cca59a715bede4a006ff6c2674900b46a946d2f2 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:27:03 +0800 Subject: [PATCH 68/89] feat: add FileContentView extracted from FileManageView --- macos/GostX.xcodeproj/project.pbxproj | 4 +++ macos/GostX/FileContentView.swift | 48 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 macos/GostX/FileContentView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 1bbf868..bfe7742 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ B5C1CF1429DD6E7C008AA934 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */; }; B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */; }; + BA165896763DA2BA6B8AE650 /* FileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041916CF79F25CAF1E21B685 /* FileContentView.swift */; }; E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E9D6522B494B409C95F0FE /* AppLogger.swift */; }; @@ -78,6 +79,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 041916CF79F25CAF1E21B685 /* FileContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FileContentView.swift; sourceTree = ""; }; 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; 19495E70527B5668EC230C85 /* LogContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogContentView.swift; sourceTree = ""; }; 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YamlEditorView.swift; sourceTree = ""; }; @@ -240,6 +242,7 @@ 19495E70527B5668EC230C85 /* LogContentView.swift */, 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */, CC1099D8C22BC993593DE940 /* FileListView.swift */, + 041916CF79F25CAF1E21B685 /* FileContentView.swift */, ); path = GostX; sourceTree = ""; @@ -470,6 +473,7 @@ 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */, 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */, 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */, + BA165896763DA2BA6B8AE650 /* FileContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/FileContentView.swift b/macos/GostX/FileContentView.swift new file mode 100644 index 0000000..0d47d73 --- /dev/null +++ b/macos/GostX/FileContentView.swift @@ -0,0 +1,48 @@ +// macos/GostX/FileContentView.swift +import SwiftUI + +@available(macOS 14.0, *) +struct FileContentView: View { + @ObservedObject var vm: FileManageViewModel + + var body: some View { + Group { + if vm.selectedFileName != nil { + VStack(spacing: 0) { + TextEditor(text: $vm.fileContent) + .font(.system(size: 12, design: .monospaced)) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onChange(of: vm.fileContent) { _ in + DispatchQueue.main.async { + vm.isFileDirty = vm.fileContent != vm.originalContent + } + } + + Divider() + + HStack { + Spacer() + Button(action: { vm.saveFileContent() }) { + Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") + } + .buttonStyle(.borderless) + .disabled(!vm.isFileDirty) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } + } else { + VStack { + Image(systemName: "doc.text.magnifyingglass") + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(NSLocalizedString("Select a file to view", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } +} From a5561e74ce5866082e1bb350e6f7854f5c6c1e62 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:32:29 +0800 Subject: [PATCH 69/89] refactor: rewrite SettingsView as NavigationSplitView --- macos/GostX/SettingsView.swift | 233 ++++++++++++--------------------- 1 file changed, 86 insertions(+), 147 deletions(-) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index f2ac6b8..5e446c1 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -1,175 +1,114 @@ // macos/GostX/SettingsView.swift import SwiftUI -@available(macOS 14.0, *) -struct SettingsView: View { - @StateObject private var repo = ConfigRepository.shared - @State private var selectedProfileId: String? = nil - @State private var showAddSheet = false - @State private var showRenameSheet = false - @State private var newProfileName = "" - @State private var renameTargetId: String? = nil - @State private var selectedTab = 0 +// MARK: - Category - var body: some View { - VStack(spacing: 0) { - VStack(spacing: 0) { - HStack(spacing: 0) { - tabItem(icon: "doc.text", title: NSLocalizedString("Profiles", comment: ""), index: 0) - tabItem(icon: "folder", title: NSLocalizedString("Files", comment: ""), index: 1) - tabItem(icon: "text.alignleft", title: NSLocalizedString("Logs", comment: ""), index: 2) - } - .padding(.horizontal, 20) - .padding(.vertical, 4) - } +enum SettingsCategory: String, CaseIterable, Identifiable { + case profiles + case files + case logs - Divider() + var id: Self { self } - if selectedTab == 0 { - profilesTab - } else if selectedTab == 1 { - FileManageView() - } else { - LogView() - } + var icon: String { + switch self { + case .profiles: return "doc.text" + case .files: return "folder" + case .logs: return "text.alignleft" } - .frame(minWidth: 700, minHeight: 440) } - private func tabItem(icon: String, title: String, index: Int) -> some View { - let isSelected = selectedTab == index - return Button(action: { selectedTab = index }) { - VStack(spacing: 4) { - Image(systemName: icon) - .font(.system(size: 18, weight: .regular)) - Text(title) - .font(.system(size: 10)) - } - .frame(width: 68) - .padding(.vertical, 6) - .contentShape(Rectangle()) - .background(isSelected ? Color.accentColor.opacity(0.15) : Color.clear) - .clipShape(RoundedRectangle(cornerRadius: 6)) - .foregroundColor(isSelected ? Color.accentColor : .secondary) + var label: String { + switch self { + case .profiles: return NSLocalizedString("Profiles", comment: "") + case .files: return NSLocalizedString("Files", comment: "") + case .logs: return NSLocalizedString("Logs", comment: "") } - .buttonStyle(.plain) } +} - private var profilesTab: some View { - HStack(spacing: 0) { - VStack(spacing: 0) { - List(selection: $selectedProfileId) { - ForEach(repo.profiles) { profile in - Label(profile.name, systemImage: "doc.text") - .tag(profile.id) - .contextMenu { - Button(NSLocalizedString("Rename...", comment: "")) { - renameTargetId = profile.id - newProfileName = profile.name - showRenameSheet = true - } - Divider() - Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { - repo.deleteProfile(profile.id) - } - } - } - } - .listStyle(.plain) +// MARK: - SettingsView - Divider() +@available(macOS 14.0, *) +struct SettingsView: View { + @State private var selectedCategory: SettingsCategory = .profiles - HStack { - Button(action: { showAddSheet = true }) { - Label(NSLocalizedString("Add Profile", comment: ""), systemImage: "plus") - } - .buttonStyle(.borderless) - Spacer() - } - .padding(.horizontal, 12) - .padding(.vertical, 6) - .frame(height: 32) - } - .frame(width: 200) + // Profiles + @State private var selectedProfileId: String? = nil - Divider() + // Files + @StateObject private var fileVM = FileManageViewModel() + + // Logs + @StateObject private var logVM = LogViewModel() + @State private var loggingEnabled = AppGroupConfig.loggingEnabled - if let profileId = selectedProfileId { - YamlEditorView(profileId: profileId) - .id(profileId) - } else { - VStack { - Image(systemName: "doc.text.magnifyingglass") - .font(.system(size: 28)) - .foregroundColor(Color(nsColor: .tertiaryLabelColor)) - Text(NSLocalizedString("Select a profile to edit", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) + var body: some View { + NavigationSplitView { + // Sidebar + List(selection: $selectedCategory) { + ForEach(SettingsCategory.allCases) { category in + Label(category.label, systemImage: category.icon) + .tag(category) } - .frame(maxWidth: .infinity, maxHeight: .infinity) } - } - .onAppear { - if selectedProfileId == nil, let first = repo.profiles.first { - selectedProfileId = first.id + .listStyle(.sidebar) + .navigationSplitViewColumnWidth(min: 140, ideal: 160, max: 200) + } content: { + // Content + switch selectedCategory { + case .profiles: + ProfileListView(selectedProfileId: $selectedProfileId) + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) + case .files: + FileListView(vm: fileVM) + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) + case .logs: + LogOptionsView() + .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) } - } - .sheet(isPresented: $showAddSheet) { - addProfileSheet - } - .sheet(isPresented: $showRenameSheet) { - renameProfileSheet - } - } - - private var addProfileSheet: some View { - VStack(spacing: 16) { - Text(NSLocalizedString("New Profile", comment: "")).font(.headline) - TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) - .textFieldStyle(.roundedBorder) - .frame(width: 250) - HStack { - Button(NSLocalizedString("Cancel", comment: "")) { showAddSheet = false } - .keyboardShortcut(.cancelAction) - Button(NSLocalizedString("Add", comment: "")) { - if !newProfileName.isEmpty { - let newId = repo.addProfile(name: newProfileName) - if let id = newId { selectedProfileId = id } - newProfileName = "" - showAddSheet = false - } + } detail: { + // Detail + switch selectedCategory { + case .profiles: + if let profileId = selectedProfileId { + YamlEditorView(profileId: profileId) + .id(profileId) + } else { + placeholderView( + icon: "doc.text.magnifyingglass", + text: NSLocalizedString("Select a profile to edit", comment: "") + ) } - .keyboardShortcut(.defaultAction) - .disabled(newProfileName.isEmpty) + case .files: + if fileVM.selectedFileName != nil { + FileContentView(vm: fileVM) + } else { + placeholderView( + icon: "doc.text.magnifyingglass", + text: NSLocalizedString("Select a file to view", comment: "") + ) + } + case .logs: + LogContentView(vm: logVM, loggingEnabled: loggingEnabled) + .onAppear { + loggingEnabled = AppGroupConfig.loggingEnabled + } } } - .padding() - .frame(width: 300, height: 140) + .frame(minWidth: 700, minHeight: 440) } - private var renameProfileSheet: some View { - VStack(spacing: 16) { - Text(NSLocalizedString("Rename Profile", comment: "")).font(.headline) - TextField(NSLocalizedString("Profile name", comment: ""), text: $newProfileName) - .textFieldStyle(.roundedBorder) - .frame(width: 250) - HStack { - Button(NSLocalizedString("Cancel", comment: "")) { showRenameSheet = false } - .keyboardShortcut(.cancelAction) - Button(NSLocalizedString("Rename", comment: "")) { - if let id = renameTargetId, !newProfileName.isEmpty { - _ = repo.renameProfile(id, newName: newProfileName) - newProfileName = "" - renameTargetId = nil - showRenameSheet = false - } - } - .keyboardShortcut(.defaultAction) - .disabled(newProfileName.isEmpty) - } + private func placeholderView(icon: String, text: String) -> some View { + VStack { + Image(systemName: icon) + .font(.system(size: 28)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + Text(text) + .font(.system(size: 13)) + .foregroundColor(.secondary) } - .padding() - .frame(width: 300, height: 140) + .frame(maxWidth: .infinity, maxHeight: .infinity) } } From 9bc272519effddde3cbc4020fcd569b3e0bfa9b5 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:35:18 +0800 Subject: [PATCH 70/89] chore: extract FileManageViewModel, remove old LogView/FileManageView --- macos/GostX.xcodeproj/project.pbxproj | 12 +- macos/GostX/FileManageView.swift | 509 -------------------------- macos/GostX/FileManageViewModel.swift | 133 +++++++ macos/GostX/LogView.swift | 185 ---------- 4 files changed, 137 insertions(+), 702 deletions(-) delete mode 100644 macos/GostX/FileManageView.swift create mode 100644 macos/GostX/FileManageViewModel.swift delete mode 100644 macos/GostX/LogView.swift diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index bfe7742..0663114 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */; }; 112569258691CDCE54D5D03B /* LogContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19495E70527B5668EC230C85 /* LogContentView.swift */; }; - 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B1478690C1481980B302A8 /* LogView.swift */; }; 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */; }; 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430349433004D3A000FD9EE9 /* NetworkExtension.framework */; }; 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -18,6 +17,7 @@ 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1099D8C22BC993593DE940 /* FileListView.swift */; }; + 5B773BD6DC67E0481B3E9F01 /* FileManageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B40B99520AC17D0C83EEFF5 /* FileManageViewModel.swift */; }; 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */; }; 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */; }; B532A66A285B528900159BAC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532A669285B528900159BAC /* main.swift */; }; @@ -29,7 +29,6 @@ B5AEDDB12864920800C09ED6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEDDB02864920800C09ED6 /* AppDelegate.swift */; }; B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */; }; B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B0E079285C63080035A6F2 /* SettingsView.swift */; }; - B5B1C1A129DD6E90008AA941 /* FileManageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A229DD6E90008AA942 /* FileManageView.swift */; }; B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B1C1A429DD6E90008AA944 /* FileRepository.swift */; }; B5C1CF1429DD6E7C008AA934 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */; }; B5F6387929DEFF31009A1287 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F6387829DEFF31009A1287 /* Arguments.swift */; }; @@ -91,7 +90,7 @@ 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppGroupConfig.swift; path = GostX/AppGroupConfig.swift; sourceTree = ""; }; 4303495C3004D78100FD9EE9 /* Libgost.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Libgost.xcframework; path = Frameworks/Libgost.xcframework; sourceTree = ""; }; 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigRepository.swift; sourceTree = ""; }; - 64B1478690C1481980B302A8 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = ""; }; + 6B40B99520AC17D0C83EEFF5 /* FileManageViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FileManageViewModel.swift; sourceTree = ""; }; 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LogViewModel.swift; sourceTree = ""; }; AD12003E3005000000FD9EE9 /* DevelopmentTeam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DevelopmentTeam.xcconfig; sourceTree = ""; }; B532A666285B528900159BAC /* GostX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GostX.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -107,7 +106,6 @@ B5AEDDB02864920800C09ED6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B5B0E073285B62B80035A6F2 /* MacExtrasConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacExtrasConfigurator.swift; sourceTree = ""; }; B5B0E079285C63080035A6F2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - B5B1C1A229DD6E90008AA942 /* FileManageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManageView.swift; sourceTree = ""; }; B5B1C1A429DD6E90008AA944 /* FileRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRepository.swift; sourceTree = ""; }; B5C1CF1529DD6E7C008AA934 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; B5C1CF1729DD6E90008AA934 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; @@ -231,10 +229,8 @@ B5B0E079285C63080035A6F2 /* SettingsView.swift */, 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */, 16E9D6522B494B409C95F0FE /* AppLogger.swift */, - 64B1478690C1481980B302A8 /* LogView.swift */, B5AEDDB02864920800C09ED6 /* AppDelegate.swift */, B5F6387829DEFF31009A1287 /* Arguments.swift */, - B5B1C1A229DD6E90008AA942 /* FileManageView.swift */, B5B1C1A429DD6E90008AA944 /* FileRepository.swift */, 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */, 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */, @@ -243,6 +239,7 @@ 1D241A5E3ABD1C9E90BA745A /* ProfileListView.swift */, CC1099D8C22BC993593DE940 /* FileListView.swift */, 041916CF79F25CAF1E21B685 /* FileContentView.swift */, + 6B40B99520AC17D0C83EEFF5 /* FileManageViewModel.swift */, ); path = GostX; sourceTree = ""; @@ -461,8 +458,6 @@ B5B0E07A285C63080035A6F2 /* SettingsView.swift in Sources */, 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */, EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */, - 22929EC8453A4E408A3AEF79 /* LogView.swift in Sources */, - B5B1C1A129DD6E90008AA941 /* FileManageView.swift in Sources */, B5B1C1A329DD6E90008AA943 /* FileRepository.swift in Sources */, B5B0E074285B62B80035A6F2 /* MacExtrasConfigurator.swift in Sources */, 2FA7674F8D9746F99910289A /* VpnManager.swift in Sources */, @@ -474,6 +469,7 @@ 80502C1365AFE6E9D0EBCE67 /* ProfileListView.swift in Sources */, 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */, BA165896763DA2BA6B8AE650 /* FileContentView.swift in Sources */, + 5B773BD6DC67E0481B3E9F01 /* FileManageViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/GostX/FileManageView.swift b/macos/GostX/FileManageView.swift deleted file mode 100644 index 8cfd657..0000000 --- a/macos/GostX/FileManageView.swift +++ /dev/null @@ -1,509 +0,0 @@ -// macos/GostX/FileManageView.swift -import SwiftUI -import AppKit - -// MARK: - Helpers - -private func formatFileSize(_ bytes: Int64) -> String { - switch bytes { - case 0..<1024: return "\(bytes) B" - case 1024..<(1024 * 1024): return "\(bytes / 1024) KB" - default: return String(format: "%.1f MB", Double(bytes) / (1024.0 * 1024.0)) - } -} - -private func formatFileDate(_ date: Date) -> String { - let fmt = DateFormatter() - fmt.dateFormat = "yyyy-MM-dd HH:mm" - return fmt.string(from: date) -} - -// MARK: - ViewModel - -@MainActor -class FileManageViewModel: ObservableObject { - @Published var files: [FileInfo] = [] - @Published var alertMessage: String? - @Published var pendingOverwrite: (sourceURL: URL, fileName: String)? - @Published var selectedFileName: String? - @Published var fileContent: String = "" - @Published var isFileDirty: Bool = false - var originalContent: String = "" - - private let repo: FileRepository? - - init() { - repo = FileRepository() - refresh() - } - - var isAvailable: Bool { repo != nil } - - func refresh() { - files = repo?.listFiles() ?? [] - } - - func selectFile(_ name: String) { - selectedFileName = name - let content = repo?.readFileContent(name) ?? "" - originalContent = content - fileContent = content - isFileDirty = false - } - - func saveFileContent() { - guard let repo, let name = selectedFileName else { return } - do { - try repo.writeFileContent(name, content: fileContent) - originalContent = fileContent - isFileDirty = false - refresh() - } catch { - alertMessage = error.localizedDescription - } - } - - func createFile(_ name: String) { - guard let repo else { return } - do { - try repo.createFile(name) - refresh() - } catch { - alertMessage = error.localizedDescription - } - } - - func importFile(from sourceURL: URL) { - guard let repo else { return } - let name = sourceURL.lastPathComponent - if repo.exists(name) { - pendingOverwrite = (sourceURL, name) - return - } - doImport(sourceURL: sourceURL) - } - - func confirmOverwrite() { - guard let (url, _) = pendingOverwrite else { return } - pendingOverwrite = nil - doImport(sourceURL: url) - } - - func cancelOverwrite() { - pendingOverwrite = nil - } - - private func doImport(sourceURL: URL) { - guard let repo else { return } - do { - try repo.importFile(from: sourceURL) - refresh() - } catch { - alertMessage = String.localizedStringWithFormat( - NSLocalizedString("Import failed: %@", comment: ""), - error.localizedDescription) - } - } - - func renameFile(_ oldName: String, to newName: String) { - guard let repo else { return } - do { - try repo.renameFile(oldName, to: newName) - if selectedFileName == oldName { - selectedFileName = newName - fileContent = repo.readFileContent(newName) ?? "" - } - refresh() - } catch { - alertMessage = error.localizedDescription - } - } - - func deleteFile(_ name: String) { - guard let repo else { return } - do { - try repo.deleteFile(name) - if selectedFileName == name { - selectedFileName = nil - fileContent = "" - } - refresh() - } catch { - alertMessage = error.localizedDescription - } - } - - func exportFile(_ name: String, to destURL: URL) { - guard let repo else { return } - do { - try repo.exportFile(name, to: destURL) - } catch { - alertMessage = error.localizedDescription - } - } - - func copyPath(_ name: String) { - guard let repo else { return } - let path = repo.filePath(name) - NSPasteboard.general.clearContents() - NSPasteboard.general.setString(path, forType: .string) - } -} - -// MARK: - FileManageView - -@available(macOS 14.0, *) -struct FileManageView: View { - @StateObject private var vm = FileManageViewModel() - @State private var showImporter = false - @State private var showExporter = false - @State private var exportName: String? - @State private var renameTarget: FileInfo? - @State private var renameText: String = "" - @State private var deleteTarget: FileInfo? - @State private var showDeleteConfirm = false - @State private var showNewFileSheet = false - @State private var newFileName = "" - - var body: some View { - HStack(spacing: 0) { - if !vm.isAvailable { - unavailableView - } else { - fileSidebar - - Divider() - - fileDetail - } - } - .fileImporter(isPresented: $showImporter, allowedContentTypes: [.data, .plainText, .text]) { result in - if case .success(let url) = result { - vm.importFile(from: url) - } - } - .sheet(isPresented: $showNewFileSheet) { - VStack(spacing: 16) { - Text(NSLocalizedString("New File", comment: "")).font(.headline) - TextField(NSLocalizedString("File name", comment: ""), text: $newFileName) - .textFieldStyle(.roundedBorder) - .frame(width: 250) - HStack { - Button(NSLocalizedString("Cancel", comment: "")) { - showNewFileSheet = false - newFileName = "" - } - .keyboardShortcut(.cancelAction) - Button(NSLocalizedString("Create", comment: "")) { - vm.createFile(newFileName) - newFileName = "" - showNewFileSheet = false - } - .keyboardShortcut(.defaultAction) - .disabled(newFileName.trimmingCharacters(in: .whitespaces).isEmpty) - } - } - .padding() - .frame(width: 300, height: 140) - } - } - - // MARK: - Sidebar - - private var fileSidebar: some View { - VStack(spacing: 0) { - if vm.files.isEmpty { - VStack(spacing: 8) { - Spacer() - Text(NSLocalizedString("No files", comment: "")) - .font(.system(size: 12)) - .foregroundColor(.secondary) - Spacer() - } - } else { - List(selection: Binding( - get: { vm.selectedFileName }, - set: { name in - DispatchQueue.main.async { - vm.selectedFileName = name - if let name { vm.selectFile(name) } - } - } - )) { - ForEach(vm.files) { file in - FileRowView( - file: file, - onExport: { - exportName = file.name - showExporter = true - }, - onRename: { - renameTarget = file - renameText = file.name - }, - onCopyPath: { vm.copyPath(file.name) }, - onDelete: { - deleteTarget = file - showDeleteConfirm = true - } - ) - .tag(file.name) - } - } - .listStyle(.plain) - } - - Divider() - - HStack { - Button(action: { showNewFileSheet = true }) { - Label(NSLocalizedString("New File", comment: ""), systemImage: "doc.badge.plus") - } - .buttonStyle(.borderless) - Spacer() - Button(action: { showImporter = true }) { - Label(NSLocalizedString("Import", comment: ""), systemImage: "square.and.arrow.up") - } - .buttonStyle(.borderless) - } - .padding(.horizontal, 12) - .padding(.vertical, 6) - .frame(height: 32) - } - .frame(width: 200) - .fileExporter( - isPresented: $showExporter, - item: exportName ?? "", - contentTypes: [.data], - defaultFilename: exportName ?? "file" - ) { result in - if case .success(let url) = result, let name = exportName { - vm.exportFile(name, to: url) - } - exportName = nil - } - .modifier(FileManageAlertModifier(vm: vm)) - .modifier(OverwriteSheetModifier(vm: vm)) - .modifier(FileManageDialogsModifier( - renameTarget: $renameTarget, - renameText: $renameText, - deleteTarget: $deleteTarget, - showDeleteConfirm: $showDeleteConfirm, - vm: vm - )) - } - - // MARK: - Detail - - private var fileDetail: some View { - Group { - if let _ = vm.selectedFileName { - VStack(spacing: 0) { - TextEditor(text: $vm.fileContent) - .font(.system(size: 12, design: .monospaced)) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .onChange(of: vm.fileContent) { _ in - DispatchQueue.main.async { - vm.isFileDirty = vm.fileContent != vm.originalContent - } - } - - Divider() - - HStack { - Spacer() - Button(action: { vm.saveFileContent() }) { - Label(NSLocalizedString("Save", comment: ""), systemImage: "square.and.arrow.down") - } - .buttonStyle(.borderless) - .disabled(!vm.isFileDirty) - } - .padding(.horizontal, 12) - .padding(.vertical, 6) - .frame(height: 32) - } - } else { - VStack { - Image(systemName: "doc.text.magnifyingglass") - .font(.system(size: 28)) - .foregroundColor(Color(nsColor: .tertiaryLabelColor)) - Text(NSLocalizedString("Select a file to view", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - } - } - - // MARK: - Unavailable - - private var unavailableView: some View { - VStack(spacing: 12) { - Image(systemName: "exclamationmark.triangle") - .font(.system(size: 32)) - .foregroundColor(.secondary) - Text(NSLocalizedString("App Group container not available.", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } -} - -// MARK: - FileRowView - -@available(macOS 14.0, *) -private struct FileRowView: View { - let file: FileInfo - let onExport: () -> Void - let onRename: () -> Void - let onCopyPath: () -> Void - let onDelete: () -> Void - - var body: some View { - HStack(spacing: 12) { - Image(systemName: "doc.text") - .font(.system(size: 16)) - .foregroundColor(.secondary) - .frame(width: 20) - - VStack(alignment: .leading, spacing: 1) { - Text(file.name) - .font(.system(size: 13)) - .lineLimit(1) - Text(formatFileSize(file.sizeBytes)) - .font(.system(size: 10)) - .foregroundColor(.secondary) - Text(formatFileDate(file.lastModified)) - .font(.system(size: 10)) - .foregroundColor(.secondary) - } - - Spacer() - } - .padding(.vertical, 4) - .contextMenu { - Button(NSLocalizedString("Export...", comment: "")) { onExport() } - Button(NSLocalizedString("Rename...", comment: "")) { onRename() } - Button(NSLocalizedString("Copy Path", comment: "")) { onCopyPath() } - Divider() - Button(NSLocalizedString("Delete...", comment: ""), role: .destructive) { onDelete() } - } - } -} - -// MARK: - Alert Modifier - -@available(macOS 14.0, *) -private struct FileManageAlertModifier: ViewModifier { - @ObservedObject var vm: FileManageViewModel - - func body(content: Content) -> some View { - content.alert( - NSLocalizedString("Error", comment: ""), - isPresented: Binding( - get: { vm.alertMessage != nil }, - set: { if !$0 { vm.alertMessage = nil } } - ), - actions: { - Button(NSLocalizedString("OK", comment: "")) { vm.alertMessage = nil } - }, - message: { - Text(vm.alertMessage ?? "") - } - ) - } -} - -// MARK: - Overwrite Sheet Modifier - -@available(macOS 14.0, *) -private struct OverwriteSheetModifier: ViewModifier { - @ObservedObject var vm: FileManageViewModel - - func body(content: Content) -> some View { - content.sheet(isPresented: Binding( - get: { vm.pendingOverwrite != nil }, - set: { if !$0 { vm.cancelOverwrite() } } - )) { - VStack(spacing: 16) { - Text(NSLocalizedString("File Already Exists", comment: "")) - .font(.headline) - Text(String.localizedStringWithFormat( - NSLocalizedString("A file named \"%@\" already exists. Do you want to replace it?", comment: ""), - vm.pendingOverwrite?.fileName ?? "")) - .font(.system(size: 13)) - .multilineTextAlignment(.center) - .frame(width: 300) - HStack(spacing: 12) { - Button(NSLocalizedString("Cancel", comment: "")) { vm.cancelOverwrite() } - .keyboardShortcut(.cancelAction) - Button(NSLocalizedString("Replace", comment: "")) { vm.confirmOverwrite() } - .keyboardShortcut(.defaultAction) - } - } - .padding() - .frame(width: 340, height: 160) - } - } -} - -// MARK: - Rename / Delete Dialogs Modifier - -@available(macOS 14.0, *) -private struct FileManageDialogsModifier: ViewModifier { - @Binding var renameTarget: FileInfo? - @Binding var renameText: String - @Binding var deleteTarget: FileInfo? - @Binding var showDeleteConfirm: Bool - let vm: FileManageViewModel - - func body(content: Content) -> some View { - content - // Rename sheet - .sheet(isPresented: Binding( - get: { renameTarget != nil }, - set: { if !$0 { renameTarget = nil } } - )) { - VStack(spacing: 16) { - Text(NSLocalizedString("Rename File", comment: "")).font(.headline) - TextField(NSLocalizedString("File name", comment: ""), text: $renameText) - .textFieldStyle(.roundedBorder) - .frame(width: 250) - HStack { - Button(NSLocalizedString("Cancel", comment: "")) { renameTarget = nil } - .keyboardShortcut(.cancelAction) - Button(NSLocalizedString("Rename", comment: "")) { - if let target = renameTarget, !renameText.trimmingCharacters(in: .whitespaces).isEmpty { - vm.renameFile(target.name, to: renameText) - renameTarget = nil - } - } - .keyboardShortcut(.defaultAction) - .disabled(renameText.trimmingCharacters(in: .whitespaces).isEmpty) - } - } - .padding() - .frame(width: 320, height: 150) - } - // Delete confirmation alert - .alert( - NSLocalizedString("Delete File", comment: ""), - isPresented: $showDeleteConfirm, - presenting: deleteTarget - ) { file in - Button(NSLocalizedString("Cancel", comment: ""), role: .cancel) { - deleteTarget = nil - } - Button(NSLocalizedString("Delete", comment: ""), role: .destructive) { - vm.deleteFile(file.name) - deleteTarget = nil - } - } message: { file in - Text(String.localizedStringWithFormat( - NSLocalizedString("Are you sure you want to delete \"%@\"? This cannot be undone.", comment: ""), - file.name)) - } - } -} diff --git a/macos/GostX/FileManageViewModel.swift b/macos/GostX/FileManageViewModel.swift new file mode 100644 index 0000000..46fdd7c --- /dev/null +++ b/macos/GostX/FileManageViewModel.swift @@ -0,0 +1,133 @@ +// macos/GostX/FileManageViewModel.swift +import SwiftUI +import AppKit + +@MainActor +class FileManageViewModel: ObservableObject { + @Published var files: [FileInfo] = [] + @Published var alertMessage: String? + @Published var pendingOverwrite: (sourceURL: URL, fileName: String)? + @Published var selectedFileName: String? + @Published var fileContent: String = "" + @Published var isFileDirty: Bool = false + var originalContent: String = "" + + private let repo: FileRepository? + + init() { + repo = FileRepository() + refresh() + } + + var isAvailable: Bool { repo != nil } + + func refresh() { + files = repo?.listFiles() ?? [] + } + + func selectFile(_ name: String) { + selectedFileName = name + let content = repo?.readFileContent(name) ?? "" + originalContent = content + fileContent = content + isFileDirty = false + } + + func saveFileContent() { + guard let repo, let name = selectedFileName else { return } + do { + try repo.writeFileContent(name, content: fileContent) + originalContent = fileContent + isFileDirty = false + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func createFile(_ name: String) { + guard let repo else { return } + do { + try repo.createFile(name) + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func importFile(from sourceURL: URL) { + guard let repo else { return } + let name = sourceURL.lastPathComponent + if repo.exists(name) { + pendingOverwrite = (sourceURL, name) + return + } + doImport(sourceURL: sourceURL) + } + + func confirmOverwrite() { + guard let (url, _) = pendingOverwrite else { return } + pendingOverwrite = nil + doImport(sourceURL: url) + } + + func cancelOverwrite() { + pendingOverwrite = nil + } + + private func doImport(sourceURL: URL) { + guard let repo else { return } + do { + try repo.importFile(from: sourceURL) + refresh() + } catch { + alertMessage = String.localizedStringWithFormat( + NSLocalizedString("Import failed: %@", comment: ""), + error.localizedDescription) + } + } + + func renameFile(_ oldName: String, to newName: String) { + guard let repo else { return } + do { + try repo.renameFile(oldName, to: newName) + if selectedFileName == oldName { + selectedFileName = newName + fileContent = repo.readFileContent(newName) ?? "" + } + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func deleteFile(_ name: String) { + guard let repo else { return } + do { + try repo.deleteFile(name) + if selectedFileName == name { + selectedFileName = nil + fileContent = "" + } + refresh() + } catch { + alertMessage = error.localizedDescription + } + } + + func exportFile(_ name: String, to destURL: URL) { + guard let repo else { return } + do { + try repo.exportFile(name, to: destURL) + } catch { + alertMessage = error.localizedDescription + } + } + + func copyPath(_ name: String) { + guard let repo else { return } + let path = repo.filePath(name) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(path, forType: .string) + } +} diff --git a/macos/GostX/LogView.swift b/macos/GostX/LogView.swift deleted file mode 100644 index 7c40f29..0000000 --- a/macos/GostX/LogView.swift +++ /dev/null @@ -1,185 +0,0 @@ -// -// LogView.swift -// GostX -// -// Displays gost.log content with auto-scroll, copy, and clear. -// - -import SwiftUI - -@available(macOS 14.0, *) -struct LogView: View { - @State private var lines: [String] = [] - @State private var isFollowing = true - @State private var timer: Timer? - @State private var scrollProxy: ScrollViewProxy? - @State private var loggingEnabled = AppGroupConfig.loggingEnabled - @State private var logLevel = AppGroupConfig.logLevel - - private var logFileURL: URL? { - AppGroupConfig.containerURL?.appendingPathComponent("gost.log") - } - - var body: some View { - VStack(spacing: 0) { - // Toolbar — row 1: log controls - HStack(spacing: 8) { - Toggle(isOn: $loggingEnabled) { - Text(NSLocalizedString("Logging", comment: "")).font(.system(size: 11)) - } - .toggleStyle(.switch) - .controlSize(.mini) - .onChange(of: loggingEnabled) { newValue in - AppGroupConfig.loggingEnabled = newValue - AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") - } - - if loggingEnabled { - Picker("", selection: $logLevel) { - ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in - Text(level.capitalized).tag(level) - } - } - .pickerStyle(.menu) - .fixedSize() - .onChange(of: logLevel) { newValue in - AppGroupConfig.logLevel = newValue - AppLogger.log(.info, "Log level: \(newValue)") - } - } - - Spacer() - - Text(NSLocalizedString("Restart VPN to apply", comment: "")) - .font(.system(size: 9)) - .foregroundColor(.secondary) - } - .padding(.horizontal, 12) - .padding(.vertical, 4) - - Divider() - - // Toolbar — row 2: view controls - HStack(spacing: 8) { - Button(action: { isFollowing.toggle() }) { - Image(systemName: isFollowing ? "pause.fill" : "play.fill") - } - .buttonStyle(.borderless) - .help(isFollowing - ? NSLocalizedString("Pause auto-scroll", comment: "") - : NSLocalizedString("Resume auto-scroll", comment: "")) - - Divider() - .frame(height: 16) - - Button(action: copyAll) { - Image(systemName: "doc.on.doc") - } - .buttonStyle(.borderless) - .help(NSLocalizedString("Copy all", comment: "")) - - Button(action: clearLog) { - Image(systemName: "trash") - } - .buttonStyle(.borderless) - .help(NSLocalizedString("Clear log", comment: "")) - - Spacer() - - Text("\(lines.count) lines") - .font(.system(size: 10)) - .foregroundColor(.secondary) - } - .padding(.horizontal, 12) - .padding(.vertical, 6) - .frame(height: 32) - - Divider() - - // Log content - if lines.isEmpty { - VStack { - Image(systemName: "text.alignleft") - .font(.system(size: 28)) - .foregroundColor(Color(nsColor: .tertiaryLabelColor)) - Text(NSLocalizedString("No logs", comment: "")) - .font(.system(size: 13)) - .foregroundColor(.secondary) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } else { - ScrollViewReader { proxy in - ScrollView { - LazyVStack(alignment: .leading, spacing: 1) { - ForEach(Array(lines.enumerated()), id: \.offset) { _, line in - Text(line) - .font(.system(size: 11, design: .monospaced)) - .textSelection(.enabled) - } - } - .padding(8) - } - .background(Color(nsColor: .textBackgroundColor)) - .onAppear { scrollProxy = proxy } - } - } - } - .onAppear { - loadLog() - startPolling() - } - .onDisappear { - stopPolling() - } - .onChange(of: isFollowing) { _ in - if isFollowing, let proxy = scrollProxy, !lines.isEmpty { - proxy.scrollTo(lines.count - 1, anchor: .bottom) - } - } - } - - // MARK: - Actions - - private func loadLog() { - guard let url = logFileURL, FileManager.default.fileExists(atPath: url.path) else { - lines = [] - return - } - do { - let content = try String(contentsOf: url, encoding: .utf8) - let newLines = content.components(separatedBy: "\n").filter { !$0.isEmpty } - lines = newLines - } catch { - lines = [] - } - } - - private func startPolling() { - timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in - DispatchQueue.main.async { - let oldCount = lines.count - loadLog() - if isFollowing, lines.count > oldCount, let proxy = scrollProxy { - proxy.scrollTo(lines.count - 1, anchor: .bottom) - } - } - } - } - - private func stopPolling() { - timer?.invalidate() - timer = nil - } - - private func copyAll() { - let text = lines.joined(separator: "\n") - NSPasteboard.general.clearContents() - NSPasteboard.general.setString(text, forType: .string) - } - - private func clearLog() { - guard let url = logFileURL else { return } - try? "".write(to: url, atomically: true, encoding: .utf8) - lines = [] - } -} From f691968fae509ccf4d4f8a00a40c8cd9ef9f05f2 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 22:40:22 +0800 Subject: [PATCH 71/89] fix: sync loggingEnabled state and restore App Group unavailable guard - LogOptionsView: change @State to @Binding for loggingEnabled and logLevel so parent SettingsView state flows through to LogContentView - SettingsView: add logLevel @State, pass bindings to LogOptionsView - FileListView: restore isAvailable guard with unavailable view when App Group container is nil --- macos/GostX/FileListView.swift | 20 ++++++++++++++++++++ macos/GostX/LogOptionsView.swift | 4 ++-- macos/GostX/SettingsView.swift | 3 ++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/macos/GostX/FileListView.swift b/macos/GostX/FileListView.swift index c4a9440..64e23a0 100644 --- a/macos/GostX/FileListView.swift +++ b/macos/GostX/FileListView.swift @@ -34,6 +34,26 @@ struct FileListView: View { @State private var newFileName = "" var body: some View { + if !vm.isAvailable { + unavailableView + } else { + listContent + } + } + + private var unavailableView: some View { + VStack(spacing: 12) { + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 32)) + .foregroundColor(.secondary) + Text(NSLocalizedString("App Group container not available.", comment: "")) + .font(.system(size: 13)) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + private var listContent: some View { VStack(spacing: 0) { if vm.files.isEmpty { VStack(spacing: 8) { diff --git a/macos/GostX/LogOptionsView.swift b/macos/GostX/LogOptionsView.swift index 6abd9b8..a55b097 100644 --- a/macos/GostX/LogOptionsView.swift +++ b/macos/GostX/LogOptionsView.swift @@ -3,8 +3,8 @@ import SwiftUI @available(macOS 14.0, *) struct LogOptionsView: View { - @State private var loggingEnabled = AppGroupConfig.loggingEnabled - @State private var logLevel = AppGroupConfig.logLevel + @Binding var loggingEnabled: Bool + @Binding var logLevel: String var body: some View { VStack(alignment: .leading, spacing: 12) { diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 5e446c1..9bd7ac4 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -42,6 +42,7 @@ struct SettingsView: View { // Logs @StateObject private var logVM = LogViewModel() @State private var loggingEnabled = AppGroupConfig.loggingEnabled + @State private var logLevel = AppGroupConfig.logLevel var body: some View { NavigationSplitView { @@ -64,7 +65,7 @@ struct SettingsView: View { FileListView(vm: fileVM) .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) case .logs: - LogOptionsView() + LogOptionsView(loggingEnabled: $loggingEnabled, logLevel: $logLevel) .navigationSplitViewColumnWidth(min: 200, ideal: 220, max: 300) } } detail: { From bc8b9563757e42f2315b46f226edea125de7d4a6 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:10:34 +0800 Subject: [PATCH 72/89] style: Liquid Glass sidebar, log dropdown, content margins - LogOptionsView: use LabeledContent + .menu picker (dropdown) instead of radio group - ProfileListView/FileListView: add 8pt horizontal padding for content margins - MacExtrasConfigurator: unifiedTitleAndToolbar + transparent titlebar + .unified toolbarStyle for Liquid Glass sidebar-titlebar fusion --- macos/GostX/FileListView.swift | 1 + macos/GostX/LogOptionsView.swift | 9 +++------ macos/GostX/MacExtrasConfigurator.swift | 4 +++- macos/GostX/ProfileListView.swift | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/macos/GostX/FileListView.swift b/macos/GostX/FileListView.swift index 64e23a0..53b4da0 100644 --- a/macos/GostX/FileListView.swift +++ b/macos/GostX/FileListView.swift @@ -98,6 +98,7 @@ struct FileListView: View { } } .listStyle(.plain) + .padding(.horizontal, 8) } Divider() diff --git a/macos/GostX/LogOptionsView.swift b/macos/GostX/LogOptionsView.swift index a55b097..a9195ca 100644 --- a/macos/GostX/LogOptionsView.swift +++ b/macos/GostX/LogOptionsView.swift @@ -22,17 +22,14 @@ struct LogOptionsView: View { } if loggingEnabled { - VStack(alignment: .leading, spacing: 6) { - Text(NSLocalizedString("Log Level", comment: "")) - .font(.system(size: 11)) - .foregroundColor(.secondary) - + LabeledContent(NSLocalizedString("Log Level", comment: "")) { Picker("", selection: $logLevel) { ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in Text(level.capitalized).tag(level) } } - .pickerStyle(.radioGroup) + .pickerStyle(.menu) + .labelsHidden() .onChange(of: logLevel) { newValue in AppGroupConfig.logLevel = newValue AppLogger.log(.info, "Log level: \(newValue)") diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 0128b26..32b8384 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -174,7 +174,9 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.styleMask = [.titled, .closable, .miniaturizable, .resizable] + window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .unifiedTitleAndToolbar] + window.titlebarAppearsTransparent = true + window.toolbarStyle = .unified window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.setContentSize(NSSize(width: 780, height: 500)) window.center() diff --git a/macos/GostX/ProfileListView.swift b/macos/GostX/ProfileListView.swift index 99065e7..4657c6e 100644 --- a/macos/GostX/ProfileListView.swift +++ b/macos/GostX/ProfileListView.swift @@ -30,6 +30,7 @@ struct ProfileListView: View { } } .listStyle(.plain) + .padding(.horizontal, 8) Divider() From d7d102ec821860b46bf93957b88d1518d0708210 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:20:28 +0800 Subject: [PATCH 73/89] fix: use fullSizeContentView for sidebar-titlebar fusion Matches NetNewsWire's storyboard configuration. fullSizeContentView makes the NavigationSplitView content extend into the titlebar area, enabling the sidebar to fuse with the window titlebar like Liquid Glass. --- macos/GostX/MacExtrasConfigurator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 32b8384..7f1b329 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -174,7 +174,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .unifiedTitleAndToolbar] + window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] window.titlebarAppearsTransparent = true window.toolbarStyle = .unified window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" From 59e753c75f7f6a9fc4a2b1f5b9bd9fb817861468 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:22:54 +0800 Subject: [PATCH 74/89] fix: add ignoresSafeArea top for sidebar-titlebar extension Combined with fullSizeContentView + titlebarAppearsTransparent + .unified toolbarStyle, this allows the NavigationSplitView sidebar to extend into the titlebar area. --- macos/GostX/SettingsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 9bd7ac4..682b3b7 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -97,6 +97,7 @@ struct SettingsView: View { } } } + .ignoresSafeArea(.container, edges: .top) .frame(minWidth: 700, minHeight: 440) } From 643fa7f901aafaad94d493dd91ded5d519b9d72c Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:29:25 +0800 Subject: [PATCH 75/89] revert: remove custom titlebar config, rely on macOS 26 auto-adoption Per Apple docs, NavigationSplitView automatically adopts Liquid Glass on macOS 26. Custom fullSizeContentView/ignoresSafeArea may interfere. --- macos/GostX/MacExtrasConfigurator.swift | 4 +--- macos/GostX/SettingsView.swift | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 7f1b329..0128b26 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -174,9 +174,7 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) let window = NSWindow(contentViewController: settingsHostingController!) - window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] - window.titlebarAppearsTransparent = true - window.toolbarStyle = .unified + window.styleMask = [.titled, .closable, .miniaturizable, .resizable] window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.setContentSize(NSSize(width: 780, height: 500)) window.center() diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 682b3b7..9bd7ac4 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -97,7 +97,6 @@ struct SettingsView: View { } } } - .ignoresSafeArea(.container, edges: .top) .frame(minWidth: 700, minHeight: 440) } From a3d6e199252233d24292edf2fa648d310cd7d366 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:33:49 +0800 Subject: [PATCH 76/89] feat: add NSToolbar to Settings window Minimal toolbar delegate provides the toolbar area that NavigationSplitView sidebar can potentially fuse into on macOS 26. --- macos/GostX/MacExtrasConfigurator.swift | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 0128b26..25c2078 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -173,8 +173,13 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { return } settingsHostingController = NSHostingController(rootView: AnyView(SettingsView())) + let toolbar = NSToolbar(identifier: "SettingsToolbar") + toolbar.displayMode = .iconOnly + toolbar.delegate = ToolbarDelegate.shared + let window = NSWindow(contentViewController: settingsHostingController!) window.styleMask = [.titled, .closable, .miniaturizable, .resizable] + window.toolbar = toolbar window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.setContentSize(NSSize(width: 780, height: 500)) window.center() @@ -219,3 +224,21 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { rebuildProfileSubmenu() } } + +@available(macOS 14.0, *) +final class ToolbarDelegate: NSObject, NSToolbarDelegate { + static let shared = ToolbarDelegate() + + func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { + let item = NSToolbarItem(itemIdentifier: itemIdentifier) + return item + } + + func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + [.flexibleSpace] + } + + func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + [.flexibleSpace] + } +} From 65740358b42d7977b95d856d1c7a22bf64e2f39d Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:34:12 +0800 Subject: [PATCH 77/89] fix: combine fullSizeContentView + toolbar + ignoresSafeArea for sidebar fusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restores fullSizeContentView + titlebarAppearsTransparent + .unified toolbarStyle alongside NSToolbar and ignoresSafeArea — matching NetNewsWire's approach with NavigationSplitView. --- macos/GostX/MacExtrasConfigurator.swift | 4 +++- macos/GostX/SettingsView.swift | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/macos/GostX/MacExtrasConfigurator.swift b/macos/GostX/MacExtrasConfigurator.swift index 25c2078..b7f2f77 100644 --- a/macos/GostX/MacExtrasConfigurator.swift +++ b/macos/GostX/MacExtrasConfigurator.swift @@ -178,7 +178,9 @@ class MacExtrasConfigurator: NSObject, NSMenuDelegate { toolbar.delegate = ToolbarDelegate.shared let window = NSWindow(contentViewController: settingsHostingController!) - window.styleMask = [.titled, .closable, .miniaturizable, .resizable] + window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView] + window.titlebarAppearsTransparent = true + window.toolbarStyle = .unified window.toolbar = toolbar window.title = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "GostX" window.setContentSize(NSSize(width: 780, height: 500)) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 9bd7ac4..682b3b7 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -97,6 +97,7 @@ struct SettingsView: View { } } } + .ignoresSafeArea(.container, edges: .top) .frame(minWidth: 700, minHeight: 440) } From cbd9e7c9b7d45e5f9f3b18e50e0abd9b3204d1eb Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:47:16 +0800 Subject: [PATCH 78/89] fix: YamlEditorView ignores top safe area to remove toolbar-height gap NSScrollView (YamlTextView) respects safe area insets, leaving a gap matching the toolbar height. SwiftUI TextEditor (FileContentView) ignores it automatically. Add ignoresSafeArea(.container, edges: .top) to match. --- macos/GostX/SettingsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/macos/GostX/SettingsView.swift b/macos/GostX/SettingsView.swift index 682b3b7..39c2c09 100644 --- a/macos/GostX/SettingsView.swift +++ b/macos/GostX/SettingsView.swift @@ -75,6 +75,7 @@ struct SettingsView: View { if let profileId = selectedProfileId { YamlEditorView(profileId: profileId) .id(profileId) + .ignoresSafeArea(.container, edges: .top) } else { placeholderView( icon: "doc.text.magnifyingglass", From 66e413973386a76615e5b8c2495490e3e9580c76 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:48:19 +0800 Subject: [PATCH 79/89] fix: remove auto-select first profile on appear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now matches Files behavior — no profile is selected by default when switching to Profiles, showing the placeholder view instead. --- macos/GostX/ProfileListView.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/macos/GostX/ProfileListView.swift b/macos/GostX/ProfileListView.swift index 4657c6e..ac2c488 100644 --- a/macos/GostX/ProfileListView.swift +++ b/macos/GostX/ProfileListView.swift @@ -45,11 +45,6 @@ struct ProfileListView: View { .padding(.vertical, 6) .frame(height: 32) } - .onAppear { - if selectedProfileId == nil, let first = repo.profiles.first { - selectedProfileId = first.id - } - } .sheet(isPresented: $showAddSheet) { addProfileSheet } From 4ff16efffab01f9a3f825a932f7ffc42ddc4024e Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:53:55 +0800 Subject: [PATCH 80/89] Enhanced settings window --- macos/GostX/FileListView.swift | 4 ++-- macos/GostX/ProfileListView.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/macos/GostX/FileListView.swift b/macos/GostX/FileListView.swift index 53b4da0..8bc21db 100644 --- a/macos/GostX/FileListView.swift +++ b/macos/GostX/FileListView.swift @@ -97,8 +97,8 @@ struct FileListView: View { .tag(file.name) } } - .listStyle(.plain) - .padding(.horizontal, 8) + .listStyle(.automatic) + .padding(.horizontal, 4) } Divider() diff --git a/macos/GostX/ProfileListView.swift b/macos/GostX/ProfileListView.swift index ac2c488..4b3eba1 100644 --- a/macos/GostX/ProfileListView.swift +++ b/macos/GostX/ProfileListView.swift @@ -29,8 +29,8 @@ struct ProfileListView: View { } } } - .listStyle(.plain) - .padding(.horizontal, 8) + .listStyle(.automatic) + .padding(.horizontal, 4) Divider() From 1f35ecc6c487efa65aabe9c3786589ff29c2073c Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Wed, 15 Jul 2026 23:55:25 +0800 Subject: [PATCH 81/89] refactor: LogOptionsView uses macOS Settings grouped card style Matches the system Preferences pattern: section title, rounded card container with rows (label left, control right), thin dividers. --- macos/GostX/LogOptionsView.swift | 58 +++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/macos/GostX/LogOptionsView.swift b/macos/GostX/LogOptionsView.swift index a9195ca..5109510 100644 --- a/macos/GostX/LogOptionsView.swift +++ b/macos/GostX/LogOptionsView.swift @@ -7,22 +7,38 @@ struct LogOptionsView: View { @Binding var logLevel: String var body: some View { - VStack(alignment: .leading, spacing: 12) { - HStack { - Toggle(isOn: $loggingEnabled) { - Text(NSLocalizedString("Logging", comment: "")) - .font(.system(size: 12, weight: .medium)) - } - .toggleStyle(.switch) - .controlSize(.small) - .onChange(of: loggingEnabled) { newValue in - AppGroupConfig.loggingEnabled = newValue - AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") + VStack(alignment: .leading, spacing: 16) { + Text(NSLocalizedString("Logging", comment: "")) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.primary) + .padding(.horizontal, 12) + + VStack(spacing: 0) { + // Enable Logging toggle row + HStack { + Text(NSLocalizedString("Enable Logging", comment: "")) + .font(.system(size: 13)) + Spacer() + Toggle("", isOn: $loggingEnabled) + .toggleStyle(.switch) + .controlSize(.small) + .labelsHidden() + .onChange(of: loggingEnabled) { newValue in + AppGroupConfig.loggingEnabled = newValue + AppLogger.log(.info, "Logging \(newValue ? "enabled" : "disabled")") + } } - } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + Divider() + .padding(.leading, 12) - if loggingEnabled { - LabeledContent(NSLocalizedString("Log Level", comment: "")) { + // Log Level picker row + HStack { + Text(NSLocalizedString("Log Level", comment: "")) + .font(.system(size: 13)) + Spacer() Picker("", selection: $logLevel) { ForEach(AppGroupConfig.logLevelOptions, id: \.self) { level in Text(level.capitalized).tag(level) @@ -30,16 +46,28 @@ struct LogOptionsView: View { } .pickerStyle(.menu) .labelsHidden() + .font(.system(size: 13)) .onChange(of: logLevel) { newValue in AppGroupConfig.logLevel = newValue AppLogger.log(.info, "Log level: \(newValue)") } } + .padding(.horizontal, 12) + .padding(.vertical, 8) } + .background( + RoundedRectangle(cornerRadius: 8) + .fill(Color(nsColor: .quaternaryLabelColor).opacity(0.3)) + ) + .overlay( + RoundedRectangle(cornerRadius: 8) + .strokeBorder(Color(nsColor: .separatorColor), lineWidth: 0.5) + ) Text(NSLocalizedString("Restart VPN to apply", comment: "")) - .font(.system(size: 9)) + .font(.system(size: 11)) .foregroundColor(.secondary) + .padding(.horizontal, 12) Spacer() } From ffc3806c6fa9cf5a55ff95e02c0bb26a969a6770 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 00:01:21 +0800 Subject: [PATCH 82/89] =?UTF-8?q?fix:=20LogContentView=20=E2=80=94=20toolb?= =?UTF-8?q?ar=20at=20bottom,=20hidden=20when=20off,=20ignoresSafeArea=20to?= =?UTF-8?q?p?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matches FileContentView pattern: content then Divider then action buttons. Toolbar hidden when logging off. Added ignoresSafeArea(.container, .top) for toolbar-height gap fix. --- macos/GostX/LogContentView.swift | 77 +++++++++++++++++--------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/macos/GostX/LogContentView.swift b/macos/GostX/LogContentView.swift index 461f5ce..1168440 100644 --- a/macos/GostX/LogContentView.swift +++ b/macos/GostX/LogContentView.swift @@ -8,43 +8,6 @@ struct LogContentView: View { var body: some View { VStack(spacing: 0) { - // Toolbar - HStack(spacing: 8) { - Button(action: { vm.isFollowing.toggle() }) { - Image(systemName: vm.isFollowing ? "pause.fill" : "play.fill") - } - .buttonStyle(.borderless) - .help(vm.isFollowing - ? NSLocalizedString("Pause auto-scroll", comment: "") - : NSLocalizedString("Resume auto-scroll", comment: "")) - - Divider() - .frame(height: 16) - - Button(action: { vm.copyAll() }) { - Image(systemName: "doc.on.doc") - } - .buttonStyle(.borderless) - .help(NSLocalizedString("Copy all", comment: "")) - - Button(action: { vm.clearLog() }) { - Image(systemName: "trash") - } - .buttonStyle(.borderless) - .help(NSLocalizedString("Clear log", comment: "")) - - Spacer() - - Text("\(vm.lines.count) lines") - .font(.system(size: 10)) - .foregroundColor(.secondary) - } - .padding(.horizontal, 12) - .padding(.vertical, 6) - .frame(height: 32) - - Divider() - // Content if !loggingEnabled { VStack { @@ -86,7 +49,47 @@ struct LogContentView: View { .onAppear { vm.scrollProxy = proxy } } } + + // Bottom toolbar (only when logging is on) + if loggingEnabled { + Divider() + + HStack(spacing: 8) { + Button(action: { vm.isFollowing.toggle() }) { + Image(systemName: vm.isFollowing ? "pause.fill" : "play.fill") + } + .buttonStyle(.borderless) + .help(vm.isFollowing + ? NSLocalizedString("Pause auto-scroll", comment: "") + : NSLocalizedString("Resume auto-scroll", comment: "")) + + Divider() + .frame(height: 16) + + Button(action: { vm.copyAll() }) { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Copy all", comment: "")) + + Button(action: { vm.clearLog() }) { + Image(systemName: "trash") + } + .buttonStyle(.borderless) + .help(NSLocalizedString("Clear log", comment: "")) + + Spacer() + + Text("\(vm.lines.count) lines") + .font(.system(size: 10)) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(height: 32) + } } + .ignoresSafeArea(.container, edges: .top) .onAppear { vm.onAppear() } .onDisappear { vm.onDisappear() } .onChange(of: vm.isFollowing) { _ in From fba9d3c922b41501962554d9242ef93455bd08a3 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 00:10:45 +0800 Subject: [PATCH 83/89] fix: auto-scroll uses onChange(of: lines.count) instead of timer callback Timer's scrollTo ran before SwiftUI re-rendered, targeting IDs that didn't exist yet. onChange(of: lines.count) fires after SwiftUI updates the view, guaranteeing the target is in the view hierarchy. --- macos/GostX/LogContentView.swift | 5 +++++ macos/GostX/LogOptionsView.swift | 8 ++++---- macos/GostX/LogViewModel.swift | 4 ---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/macos/GostX/LogContentView.swift b/macos/GostX/LogContentView.swift index 1168440..1a25de0 100644 --- a/macos/GostX/LogContentView.swift +++ b/macos/GostX/LogContentView.swift @@ -97,5 +97,10 @@ struct LogContentView: View { proxy.scrollTo(vm.lines.count - 1, anchor: .bottom) } } + .onChange(of: vm.lines.count) { _ in + if vm.isFollowing, let proxy = vm.scrollProxy, !vm.lines.isEmpty { + proxy.scrollTo(vm.lines.count - 1, anchor: .bottom) + } + } } } diff --git a/macos/GostX/LogOptionsView.swift b/macos/GostX/LogOptionsView.swift index 5109510..475ae84 100644 --- a/macos/GostX/LogOptionsView.swift +++ b/macos/GostX/LogOptionsView.swift @@ -8,10 +8,10 @@ struct LogOptionsView: View { var body: some View { VStack(alignment: .leading, spacing: 16) { - Text(NSLocalizedString("Logging", comment: "")) - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.primary) - .padding(.horizontal, 12) +// Text(NSLocalizedString("Logging", comment: "")) +// .font(.system(size: 13, weight: .semibold)) +// .foregroundColor(.primary) +// .padding(.horizontal, 12) VStack(spacing: 0) { // Enable Logging toggle row diff --git a/macos/GostX/LogViewModel.swift b/macos/GostX/LogViewModel.swift index f513429..96ebc50 100644 --- a/macos/GostX/LogViewModel.swift +++ b/macos/GostX/LogViewModel.swift @@ -53,11 +53,7 @@ class LogViewModel: ObservableObject { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in guard let self else { return } DispatchQueue.main.async { - let oldCount = self.lines.count self.loadLog() - if self.isFollowing, self.lines.count > oldCount, let proxy = self.scrollProxy { - proxy.scrollTo(self.lines.count - 1, anchor: .bottom) - } } } } From f47434f63f1f3894477c992fbacaee6c67fe6db8 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 00:20:58 +0800 Subject: [PATCH 84/89] fix: isFollowing defaults to false, timer only starts when logging enabled - isFollowing default changed from true to false - onAppear(loggingEnabled:) checks state before starting poll timer - LogViewModelTests updated --- macos/GostX/LogContentView.swift | 2 +- macos/GostX/LogViewModel.swift | 8 +++++--- macos/GostXTests/GostXTests.swift | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/macos/GostX/LogContentView.swift b/macos/GostX/LogContentView.swift index 1a25de0..6400f17 100644 --- a/macos/GostX/LogContentView.swift +++ b/macos/GostX/LogContentView.swift @@ -90,7 +90,7 @@ struct LogContentView: View { } } .ignoresSafeArea(.container, edges: .top) - .onAppear { vm.onAppear() } + .onAppear { vm.onAppear(loggingEnabled: loggingEnabled) } .onDisappear { vm.onDisappear() } .onChange(of: vm.isFollowing) { _ in if vm.isFollowing, let proxy = vm.scrollProxy, !vm.lines.isEmpty { diff --git a/macos/GostX/LogViewModel.swift b/macos/GostX/LogViewModel.swift index 96ebc50..5dfdce9 100644 --- a/macos/GostX/LogViewModel.swift +++ b/macos/GostX/LogViewModel.swift @@ -5,7 +5,7 @@ import Combine @MainActor class LogViewModel: ObservableObject { @Published var lines: [String] = [] - @Published var isFollowing = true + @Published var isFollowing = false var scrollProxy: ScrollViewProxy? private var timer: Timer? @@ -15,9 +15,11 @@ class LogViewModel: ObservableObject { self.logFileURL = logFileURL ?? AppGroupConfig.containerURL?.appendingPathComponent("gost.log") } - func onAppear() { + func onAppear(loggingEnabled: Bool) { loadLog() - startPolling() + if loggingEnabled { + startPolling() + } } func onDisappear() { diff --git a/macos/GostXTests/GostXTests.swift b/macos/GostXTests/GostXTests.swift index d9bf242..8c33973 100644 --- a/macos/GostXTests/GostXTests.swift +++ b/macos/GostXTests/GostXTests.swift @@ -47,7 +47,7 @@ class LogViewModelTests: XCTestCase { tempLogURL = tempDir.appendingPathComponent("gost.log") try "line one\nline two\nline three\n".write(to: tempLogURL, atomically: true, encoding: .utf8) vm = LogViewModel(logFileURL: tempLogURL) - vm.onAppear() + vm.onAppear(loggingEnabled: true) } override func tearDownWithError() throws { @@ -64,7 +64,7 @@ class LogViewModelTests: XCTestCase { } func testInitialState() { - XCTAssertTrue(vm.isFollowing) + XCTAssertFalse(vm.isFollowing) } func testClearLogTruncatesFile() { From e268873d32615d531132db18c484ae400412ec72 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 08:47:39 +0800 Subject: [PATCH 85/89] fix: exclude x86_64 from Release to fix archive with arm64-only Libgost macOS 26 is Apple Silicon only. Libgost.xcframework only ships arm64. Archiving was failing because Xcode builds for x86_64+arm64 by default. --- macos/GostX.xcodeproj/project.pbxproj | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index 0663114..ad862e6 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -15,7 +15,8 @@ 4303494C3004D3A000FD9EE9 /* GostXTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 430349423004D3A000FD9EE9 /* GostXTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 4303495A3004D6B700FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; 4303495B3004D6D500FD9EE9 /* AppGroupConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430349593004D6B700FD9EE9 /* AppGroupConfig.swift */; }; - 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; + 43F7013E300838C000E70769 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; + 43F70141300838DC00E70769 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; 539C284B9A34F4EEB926B517 /* FileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1099D8C22BC993593DE940 /* FileListView.swift */; }; 5B773BD6DC67E0481B3E9F01 /* FileManageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B40B99520AC17D0C83EEFF5 /* FileManageViewModel.swift */; }; 661952039FA5490CAD5BBB39 /* YamlEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA10536E0954A81A09F6F24 /* YamlEditorView.swift */; }; @@ -35,7 +36,6 @@ B949D5D298F19829F1C9EBAB /* LogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFCA548038B9C1B568E78C0 /* LogViewModel.swift */; }; BA165896763DA2BA6B8AE650 /* FileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041916CF79F25CAF1E21B685 /* FileContentView.swift */; }; E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; - EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4303495C3004D78100FD9EE9 /* Libgost.xcframework */; }; EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E9D6522B494B409C95F0FE /* AppLogger.swift */; }; /* End PBXBuildFile section */ @@ -115,7 +115,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */ = { + 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, @@ -125,18 +125,7 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - 430349453004D3A000FD9EE9 /* GostXTunnel */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 430349503004D3A000FD9EE9 /* Exceptions for "GostXTunnel" folder in "GostXTunnel" target */, - ); - explicitFileTypes = { - }; - explicitFolders = ( - ); - path = GostXTunnel; - sourceTree = ""; - }; + 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,7 +133,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4303495D3004D78100FD9EE9 /* Libgost.xcframework in Frameworks */, + 43F7013E300838C000E70769 /* Libgost.xcframework in Frameworks */, 430349443004D3A000FD9EE9 /* NetworkExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -153,7 +142,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EA7823795D8442489C72D878 /* Libgost.xcframework in Frameworks */, + 43F70141300838DC00E70769 /* Libgost.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -709,6 +698,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + EXCLUDED_ARCHS = x86_64; MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; From 65d6efc0530b56f9668efae982ab2b4194053709 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 08:53:12 +0800 Subject: [PATCH 86/89] =?UTF-8?q?fix:=20GostX=20Release=20=E2=80=94=20remo?= =?UTF-8?q?ve=20hardcoded=20Apple=20Development,=20enable=20hardened=20run?= =?UTF-8?q?time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed CODE_SIGN_IDENTITY=Apple Development from Release (let Automatic pick Developer ID) - ENABLE_HARDENED_RUNTIME = YES for notarization compatibility --- macos/GostX.xcodeproj/project.pbxproj | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/macos/GostX.xcodeproj/project.pbxproj b/macos/GostX.xcodeproj/project.pbxproj index ad862e6..36952da 100644 --- a/macos/GostX.xcodeproj/project.pbxproj +++ b/macos/GostX.xcodeproj/project.pbxproj @@ -5,7 +5,6 @@ }; objectVersion = 70; objects = { - /* Begin PBXBuildFile section */ 0874FE9F0ADC49BBB6CD3626 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296DECB2BD4347C19DFD902E /* PacketTunnelProvider.swift */; }; 110C2C030865F24C8C297E45 /* LogOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0072E6B1705600E6B4FA5B /* LogOptionsView.swift */; }; @@ -38,7 +37,6 @@ E086218C337C33C8BB4052CA /* ConfigRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D840A91D1C37F07D57AD8A1 /* ConfigRepository.swift */; }; EBCBCEB114E34B4BB5D68150 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E9D6522B494B409C95F0FE /* AppLogger.swift */; }; /* End PBXBuildFile section */ - /* Begin PBXContainerItemProxy section */ 4303494A3004D3A000FD9EE9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -62,7 +60,6 @@ remoteInfo = GostX; }; /* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 4303494D3004D3A000FD9EE9 /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; @@ -76,7 +73,6 @@ runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 041916CF79F25CAF1E21B685 /* FileContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FileContentView.swift; sourceTree = ""; }; 16E9D6522B494B409C95F0FE /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = ""; }; @@ -113,7 +109,6 @@ CC1099D8C22BC993593DE940 /* FileListView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FileListView.swift; sourceTree = ""; }; F188A4B6A6F14F1D867F7D30 /* VpnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VpnManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ - /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ 430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; @@ -123,11 +118,9 @@ target = 430349413004D3A000FD9EE9 /* GostXTunnel */; }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - /* Begin PBXFileSystemSynchronizedRootGroup section */ 430349453004D3A000FD9EE9 /* GostXTunnel */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (430349503004D3A000FD9EE9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = GostXTunnel; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ - /* Begin PBXFrameworksBuildPhase section */ 4303493F3004D3A000FD9EE9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -161,7 +154,6 @@ runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ - /* Begin PBXGroup section */ 43F701153007C22A00E70769 /* Recovered References */ = { isa = PBXGroup; @@ -268,7 +260,6 @@ sourceTree = ""; }; /* End PBXGroup section */ - /* Begin PBXNativeTarget section */ 430349413004D3A000FD9EE9 /* GostXTunnel */ = { isa = PBXNativeTarget; @@ -346,7 +337,6 @@ productType = "com.apple.product-type.bundle.ui-testing"; }; /* End PBXNativeTarget section */ - /* Begin PBXProject section */ B532A65E285B528900159BAC /* Project object */ = { isa = PBXProject; @@ -392,7 +382,6 @@ ); }; /* End PBXProject section */ - /* Begin PBXResourcesBuildPhase section */ 430349403004D3A000FD9EE9 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -426,7 +415,6 @@ runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 4303493E3004D3A000FD9EE9 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -480,7 +468,6 @@ runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ - /* Begin PBXTargetDependency section */ 4303494B3004D3A000FD9EE9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -498,7 +485,6 @@ targetProxy = B532A682285B528D00159BAC /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ B5C1CF1629DD6E7C008AA934 /* Localizable.stringsdict */ = { isa = PBXVariantGroup; @@ -510,7 +496,6 @@ sourceTree = ""; }; /* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 4303494E3004D3A000FD9EE9 /* Debug */ = { isa = XCBuildConfiguration; @@ -754,14 +739,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = GostX/GostX.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"GostX/Preview Content\""; - ENABLE_HARDENED_RUNTIME = NO; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = GostX/Info.plist; @@ -860,7 +844,6 @@ name = Release; }; /* End XCBuildConfiguration section */ - /* Begin XCConfigurationList section */ 430349513004D3A000FD9EE9 /* Build configuration list for PBXNativeTarget "GostXTunnel" */ = { isa = XCConfigurationList; From 20246f03192d4ac3748b2644d9eea7e29c979fb4 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 08:57:03 +0800 Subject: [PATCH 87/89] feat: add macos-release Makefile target for Developer ID DMG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automates: archive → export → DMG → optional notarization Requires Developer ID Application certificate. Notarization enabled via APPLE_ID, APPLE_PASSWORD, APPLE_TEAM environment variables. --- Makefile | 37 ++++++++++++++++++++++++- macos/Configuration/ExportOptions.plist | 8 ++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 macos/Configuration/ExportOptions.plist diff --git a/Makefile b/Makefile index fa738f9..0248c8c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all android android-release macos clean +.PHONY: all android android-release macos macos-release clean all: android @@ -11,6 +11,41 @@ android-release: libgost/libgost.aar macos: macos/Frameworks/Libgost.xcframework cd macos && xcodebuild -project GostX.xcodeproj -scheme GostX -configuration Release -derivedDataPath build ONLY_ACTIVE_ARCH=YES build +# Developer ID distribution: signed, notarized DMG for direct sharing +# Prerequisites: +# 1. Developer ID Application certificate in Keychain +# 2. (Optional) APPLE_ID, APPLE_PASSWORD, APPLE_TEAM env vars for notarization +# APPLE_PASSWORD should be an app-specific password from appleid.apple.com +macos-release: macos/Frameworks/Libgost.xcframework + cd macos && \ + xcodebuild archive \ + -project GostX.xcodeproj \ + -scheme GostX \ + -configuration Release \ + -archivePath build/GostX.xcarchive \ + -destination 'platform=macOS' && \ + xcodebuild -exportArchive \ + -archivePath build/GostX.xcarchive \ + -exportOptionsPlist Configuration/ExportOptions.plist \ + -exportPath build/Release && \ + hdiutil create -volname GostX \ + -srcfolder build/Release/GostX.app \ + -ov -format UDZO \ + build/GostX.dmg + @echo "✅ DMG ready: macos/build/GostX.dmg" + @if [ -n "$$APPLE_ID" ] && [ -n "$$APPLE_PASSWORD" ] && [ -n "$$APPLE_TEAM" ]; then \ + echo "📤 Submitting for notarization..."; \ + xcrun notarytool submit macos/build/GostX.dmg \ + --apple-id "$$APPLE_ID" \ + --password "$$APPLE_PASSWORD" \ + --team-id "$$APPLE_TEAM" \ + --wait && \ + xcrun stapler staple macos/build/GostX.dmg && \ + echo "✅ Notarized: macos/build/GostX.dmg"; \ + else \ + echo "⚠️ Skipping notarization (set APPLE_ID, APPLE_PASSWORD, APPLE_TEAM to enable)"; \ + fi + libgost/libgost.aar: cd libgost && $(MAKE) libgost.aar diff --git a/macos/Configuration/ExportOptions.plist b/macos/Configuration/ExportOptions.plist new file mode 100644 index 0000000..055f67c --- /dev/null +++ b/macos/Configuration/ExportOptions.plist @@ -0,0 +1,8 @@ + + + + + method + developer-id + + From 2ec63ad940458064f1e120ff18152ed878fb0018 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 09:04:19 +0800 Subject: [PATCH 88/89] fix: GostXTunnel entitlement matches automatic profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed packet-tunnel-provider → packet-tunnel-provider-systemextension to match the provisioning profile generated by Automatic Signing. --- macos/GostXTunnel/GostXTunnel.entitlements | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/GostXTunnel/GostXTunnel.entitlements b/macos/GostXTunnel/GostXTunnel.entitlements index 9d122bc..07aa1b9 100644 --- a/macos/GostXTunnel/GostXTunnel.entitlements +++ b/macos/GostXTunnel/GostXTunnel.entitlements @@ -14,7 +14,7 @@ com.apple.developer.networking.networkextension - packet-tunnel-provider + packet-tunnel-provider-systemextension From f05d9066f7417b2ce228b284ee7d6e9023049d70 Mon Sep 17 00:00:00 2001 From: Liu Kebin Date: Thu, 16 Jul 2026 09:24:34 +0800 Subject: [PATCH 89/89] Fix entitlements --- macos/GostXTunnel/GostXTunnel.entitlements | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/GostXTunnel/GostXTunnel.entitlements b/macos/GostXTunnel/GostXTunnel.entitlements index 07aa1b9..9d122bc 100644 --- a/macos/GostXTunnel/GostXTunnel.entitlements +++ b/macos/GostXTunnel/GostXTunnel.entitlements @@ -14,7 +14,7 @@ com.apple.developer.networking.networkextension - packet-tunnel-provider-systemextension + packet-tunnel-provider