From 7bffb2ae7b9bada55ed3e1433c9bc011635d7cb0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Jun 2025 03:19:17 +0200 Subject: [PATCH] Add tty-fwd universal binary build step to release process - Integrate tty-fwd universal binary build into build.sh - Automatically build and copy tty-fwd-universal to Resources folder - Ensure binary is executable and included in app bundle - Update release process to build universal binary for Intel and Apple Silicon --- .../UserInterfaceState.xcuserstate | Bin 48921 -> 52943 bytes .../Core/Services/TTYForwardManager.swift | 2 +- VibeTunnel/Core/Services/TunnelServer.swift | 2 +- VibeTunnel/Presentation/Views/AboutView.swift | 4 +- VibeTunnel/SettingsView.swift | 35 +++++-- .../Utilities/AnimatedSettingsWindow.swift | 86 ++++++++++++++++++ .../Utilities/SettingsWindowDelegate.swift | 71 --------------- VibeTunnel/VibeTunnel.entitlements | 8 +- scripts/build.sh | 23 ++++- scripts/release.sh | 7 +- 10 files changed, 142 insertions(+), 96 deletions(-) create mode 100644 VibeTunnel/Utilities/AnimatedSettingsWindow.swift delete mode 100644 VibeTunnel/Utilities/SettingsWindowDelegate.swift diff --git a/VibeTunnel.xcodeproj/project.xcworkspace/xcuserdata/steipete.xcuserdatad/UserInterfaceState.xcuserstate b/VibeTunnel.xcodeproj/project.xcworkspace/xcuserdata/steipete.xcuserdatad/UserInterfaceState.xcuserstate index e9d4f057c4da2c15f6f17d258944ae74b2cf4732..dce1332909a56404406b52a9b98169e2971a6f22 100644 GIT binary patch delta 23262 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

VibeTunnel Server

-

✓ Server is running on port \(portNumber)

+

Server is running on port \(portNumber)

Available endpoints:

  • /health - Health check
  • diff --git a/VibeTunnel/Presentation/Views/AboutView.swift b/VibeTunnel/Presentation/Views/AboutView.swift index ede872f9..f2556260 100644 --- a/VibeTunnel/Presentation/Views/AboutView.swift +++ b/VibeTunnel/Presentation/Views/AboutView.swift @@ -64,12 +64,12 @@ struct AboutView: View { title: "Report an Issue", icon: "exclamationmark.bubble" ) - HoverableLink(url: "https://x.com/steipete", title: "Follow @steipete on Twitter", icon: "bird") + HoverableLink(url: "https://x.com/VibeTunnel", title: "Follow @VibeTunnel", icon: "bird") } } private var copyrightSection: some View { - Text("© 2025 Amantus AI • MIT Licensed") + Text("© 2025 VibeTunnel Team • MIT Licensed") .font(.footnote) .foregroundStyle(.secondary) .padding(.bottom, 32) diff --git a/VibeTunnel/SettingsView.swift b/VibeTunnel/SettingsView.swift index 6e1abf9a..72122318 100644 --- a/VibeTunnel/SettingsView.swift +++ b/VibeTunnel/SettingsView.swift @@ -32,6 +32,7 @@ extension Notification.Name { struct SettingsView: View { @State private var selectedTab: SettingsTab = .general @State private var contentSize: CGSize = .zero + @AppStorage("debugMode") private var debugMode = false // Define ideal sizes for each tab private let tabSizes: [SettingsTab: CGSize] = [ @@ -55,11 +56,13 @@ struct SettingsView: View { } .tag(SettingsTab.advanced) - DebugSettingsView() - .tabItem { - Label(SettingsTab.debug.displayName, systemImage: SettingsTab.debug.icon) - } - .tag(SettingsTab.debug) + if debugMode { + DebugSettingsView() + .tabItem { + Label(SettingsTab.debug.displayName, systemImage: SettingsTab.debug.icon) + } + .tag(SettingsTab.debug) + } AboutView() .tabItem { @@ -68,7 +71,7 @@ struct SettingsView: View { .tag(SettingsTab.about) } .frame(width: contentSize.width, height: contentSize.height) - .animatedWindowResizing(size: contentSize) + .animatedWindowContainer(size: contentSize) .onReceive(NotificationCenter.default.publisher(for: .openSettingsTab)) { notification in if let tab = notification.object as? SettingsTab { selectedTab = tab @@ -80,6 +83,12 @@ struct SettingsView: View { .onAppear { contentSize = tabSizes[selectedTab] ?? CGSize(width: 500, height: 400) } + .onChange(of: debugMode) { _, _ in + // If debug mode is disabled and we're on the debug tab, switch to general + if !debugMode && selectedTab == .debug { + selectedTab = .general + } + } } } @@ -214,7 +223,6 @@ struct AdvancedSettingsView: View { .buttonStyle(.bordered) .disabled(isCheckingForUpdates) } - .padding(.top, 8) } header: { Text("Updates") .font(.headline) @@ -421,7 +429,7 @@ struct DebugSettingsView: View { Section { // API Endpoints with test functionality VStack(alignment: .leading, spacing: 12) { - ForEach(apiEndpoints, id: \.path) { endpoint in + ForEach(apiEndpoints) { endpoint in VStack(alignment: .leading, spacing: 4) { HStack { Text(endpoint.method) @@ -614,11 +622,20 @@ struct DebugSettingsView: View { } // API Endpoint data -struct APIEndpoint { +struct APIEndpoint: Identifiable { + let id: String let method: String let path: String let description: String let isTestable: Bool + + init(method: String, path: String, description: String, isTestable: Bool) { + self.id = "\(method)_\(path)" + self.method = method + self.path = path + self.description = description + self.isTestable = isTestable + } } let apiEndpoints = [ diff --git a/VibeTunnel/Utilities/AnimatedSettingsWindow.swift b/VibeTunnel/Utilities/AnimatedSettingsWindow.swift new file mode 100644 index 00000000..1c0cec48 --- /dev/null +++ b/VibeTunnel/Utilities/AnimatedSettingsWindow.swift @@ -0,0 +1,86 @@ +import SwiftUI +import AppKit + +/// A view that enables animated window resizing for Settings windows +struct AnimatedWindowContainer: NSViewRepresentable { + let content: Content + let targetSize: CGSize + + class Coordinator: NSObject { + var lastSize: CGSize = .zero + weak var window: NSWindow? + var isAnimating = false + + func animateWindowResize(to newSize: CGSize) { + guard let window = window, + newSize != lastSize, + !isAnimating else { return } + + lastSize = newSize + isAnimating = true + + // Calculate the new frame maintaining the window's top-left position + var newFrame = window.frame + let heightDifference = newSize.height - newFrame.height + newFrame.size = newSize + newFrame.origin.y -= heightDifference // Keep top edge in place + + // Animate the window frame change + NSAnimationContext.runAnimationGroup({ context in + context.duration = 0.25 + context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + window.animator().setFrame(newFrame, display: true) + }, completionHandler: { + self.isAnimating = false + }) + } + } + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + func makeNSView(context: Context) -> NSView { + let view = NSView() + view.wantsLayer = true + view.layer?.backgroundColor = NSColor.clear.cgColor + + // Use async to ensure window is available + DispatchQueue.main.async { + if let window = view.window { + context.coordinator.window = window + // Disable automatic window resizing + window.styleMask.remove(.resizable) + // Set initial size + context.coordinator.lastSize = window.frame.size + } + } + + return view + } + + func updateNSView(_ nsView: NSView, context: Context) { + // Ensure we have a window + if context.coordinator.window == nil, let window = nsView.window { + context.coordinator.window = window + window.styleMask.remove(.resizable) + context.coordinator.lastSize = window.frame.size + } + + // Animate to the new size + context.coordinator.animateWindowResize(to: targetSize) + } +} + +/// Extension to make it easy to use with any SwiftUI view +extension View { + func animatedWindowContainer(size: CGSize) -> some View { + background( + AnimatedWindowContainer( + content: Color.clear, + targetSize: size + ) + .allowsHitTesting(false) + ) + } +} diff --git a/VibeTunnel/Utilities/SettingsWindowDelegate.swift b/VibeTunnel/Utilities/SettingsWindowDelegate.swift deleted file mode 100644 index 1bca565a..00000000 --- a/VibeTunnel/Utilities/SettingsWindowDelegate.swift +++ /dev/null @@ -1,71 +0,0 @@ -import AppKit -import SwiftUI - -/// A window delegate that handles animated resizing of the settings window -class SettingsWindowDelegate: NSObject, NSWindowDelegate { - static let shared = SettingsWindowDelegate() - - private override init() { - super.init() - } - - /// Animates the window to a new size - func animateWindowResize(to newSize: CGSize, duration: TimeInterval = 0.3) { - guard let window = NSApp.windows.first(where: { $0.title.contains("Settings") || $0.title.contains("Preferences") }) else { - return - } - - // Calculate the new frame maintaining the window's top-left position - var newFrame = window.frame - let heightDifference = newSize.height - newFrame.height - newFrame.size = newSize - newFrame.origin.y -= heightDifference // Keep top edge in place - - // Animate the frame change - NSAnimationContext.runAnimationGroup { context in - context.duration = duration - context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - window.animator().setFrame(newFrame, display: true) - } - } - - func windowWillClose(_ notification: Notification) { - // Clean up if needed - } -} - -/// A view modifier that sets up the window delegate for animated resizing -struct AnimatedWindowResizing: ViewModifier { - let size: CGSize - - func body(content: Content) -> some View { - content - .onAppear { - setupWindowDelegate() - // Initial resize without animation - SettingsWindowDelegate.shared.animateWindowResize(to: size, duration: 0) - } - .onChange(of: size) { _, newSize in - SettingsWindowDelegate.shared.animateWindowResize(to: newSize) - } - } - - private func setupWindowDelegate() { - Task { @MainActor in - // Small delay to ensure window is created - try? await Task.sleep(for: .milliseconds(100)) - - if let window = NSApp.windows.first(where: { $0.title.contains("Settings") || $0.title.contains("Preferences") }) { - window.delegate = SettingsWindowDelegate.shared - // Disable window resizing by user - window.styleMask.remove(.resizable) - } - } - } -} - -extension View { - func animatedWindowResizing(size: CGSize) -> some View { - modifier(AnimatedWindowResizing(size: size)) - } -} \ No newline at end of file diff --git a/VibeTunnel/VibeTunnel.entitlements b/VibeTunnel/VibeTunnel.entitlements index c1756644..85a5438d 100644 --- a/VibeTunnel/VibeTunnel.entitlements +++ b/VibeTunnel/VibeTunnel.entitlements @@ -3,12 +3,6 @@ com.apple.security.app-sandbox - - com.apple.security.network.client - - com.apple.security.network.server - - com.apple.security.files.user-selected.read-write - + \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index 876bbe60..a8854d9f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -27,7 +27,6 @@ # DEPENDENCIES: # - Xcode and command line tools # - xcbeautify (optional, for prettier output) -# - Generated Xcode project (run generate-xcproj.sh first) # # EXAMPLES: # ./scripts/build.sh # Release build @@ -73,6 +72,28 @@ echo "Code signing: $SIGN_APP" # Clean build directory only if it doesn't exist mkdir -p "$BUILD_DIR" +# Build tty-fwd universal binary +echo "🔨 Building tty-fwd universal binary..." +if [[ -x "$PROJECT_DIR/tty-fwd/build-universal.sh" ]]; then + cd "$PROJECT_DIR/tty-fwd" + ./build-universal.sh + + # Verify the binary was built + if [[ -f "$PROJECT_DIR/tty-fwd/target/release/tty-fwd-universal" ]]; then + echo "✓ tty-fwd universal binary built successfully" + # Copy to Resources folder for inclusion in app bundle + cp "$PROJECT_DIR/tty-fwd/target/release/tty-fwd-universal" "$PROJECT_DIR/VibeTunnel/Resources/tty-fwd" + chmod +x "$PROJECT_DIR/VibeTunnel/Resources/tty-fwd" + echo "✓ Copied tty-fwd universal binary to Resources folder" + else + echo "Error: Failed to build tty-fwd universal binary" + exit 1 + fi +else + echo "Error: tty-fwd build script not found at $PROJECT_DIR/tty-fwd/build-universal.sh" + exit 1 +fi + # Build the app cd "$PROJECT_DIR" diff --git a/scripts/release.sh b/scripts/release.sh index 69ae0eba..7c421fa6 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -32,7 +32,7 @@ # # DEPENDENCIES: # - preflight-check.sh (validates release readiness) -# - generate-xcproj.sh (Tuist project generation) +# - Xcode workspace and project files # - build.sh (application building) # - sign-and-notarize.sh (code signing and notarization) # - create-dmg.sh (DMG creation) @@ -142,10 +142,9 @@ echo " Build: $BUILD_NUMBER" echo " Tag: $TAG_NAME" echo "" -# Step 2: Clean and generate project -echo -e "${BLUE}📋 Step 2/7: Generating Xcode project...${NC}" +# Step 2: Clean build directory +echo -e "${BLUE}📋 Step 2/7: Cleaning build directory...${NC}" rm -rf "$PROJECT_ROOT/build" -"$SCRIPT_DIR/generate-xcproj.sh" # Check if Xcode project was modified and commit if needed if ! git diff --quiet "$PROJECT_ROOT/VibeTunnel.xcodeproj/project.pbxproj"; then