mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-14 12:46:05 +00:00
Remove environment variable features and add 4K resolution limit
- Remove VIBETUNNEL_FLIP_Y and VIBETUNNEL_USE_WARP environment variables - Simplify coordinate transformation configuration - Add automatic 4K resolution capping for displays above 4K (like 5K displays) - Scale down proportionally to maintain aspect ratio (e.g., 5120×2880 → 3840×2160) - Prevent web interface clipping on high-resolution displays - Remove MOUSE_CLICK_DEBUG.md and test_display_coordinates.sh - Fix linting issues in various files
This commit is contained in:
parent
53b4aff571
commit
4fc7a5da5b
8 changed files with 39 additions and 118 deletions
|
|
@ -1,82 +0,0 @@
|
|||
# Mouse Click Debug Guide for VibeTunnel Screen Capture
|
||||
|
||||
## Problem
|
||||
Mouse clicks are not working correctly in the screen capture feature. The coordinate system conversion might be incorrect.
|
||||
|
||||
## Changes Made
|
||||
|
||||
1. **Added Environment Variable Controls**:
|
||||
- `VIBETUNNEL_FLIP_Y` - Set to "false" to disable Y-coordinate flipping
|
||||
- `VIBETUNNEL_USE_WARP` - Set to "true" to use CGWarpMouseCursorPosition instead of CGEvent
|
||||
|
||||
2. **Enhanced Debug Logging**:
|
||||
- Extensive coordinate transformation logging
|
||||
- Screen information for all displays
|
||||
- Mouse position before and after moves
|
||||
- Capture mode and filter information
|
||||
|
||||
3. **Improved Mouse Movement**:
|
||||
- Added mouse move before click (already implemented)
|
||||
- Option to use CGWarpMouseCursorPosition for more direct cursor control
|
||||
- Proper multi-monitor support with screen-relative Y-flipping
|
||||
|
||||
## Testing Instructions
|
||||
|
||||
### 1. Test Default Behavior (Y-flipping enabled)
|
||||
```bash
|
||||
./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
|
||||
```
|
||||
|
||||
### 2. Test Without Y-Coordinate Flipping
|
||||
```bash
|
||||
VIBETUNNEL_FLIP_Y=false ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
|
||||
```
|
||||
|
||||
### 3. Test With CGWarpMouseCursorPosition
|
||||
```bash
|
||||
VIBETUNNEL_USE_WARP=true ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
|
||||
```
|
||||
|
||||
### 4. Test Both Options
|
||||
```bash
|
||||
VIBETUNNEL_FLIP_Y=false VIBETUNNEL_USE_WARP=true ./build/Build/Products/Debug/VibeTunnel.app/Contents/MacOS/VibeTunnel
|
||||
```
|
||||
|
||||
## What to Look For in Logs
|
||||
|
||||
Use `./scripts/vtlog.sh -f -c ScreencapService` to monitor logs and look for:
|
||||
|
||||
1. **Coordinate Transformation**:
|
||||
- `🔍 [DEBUG] calculateClickLocation - Input: x=XXX, y=YYY`
|
||||
- `🔍 [DEBUG] Configuration: shouldFlipY=true/false, useWarpCursor=true/false`
|
||||
- `🔍 [DEBUG] Y-coordinate flipping DISABLED` (when VIBETUNNEL_FLIP_Y=false)
|
||||
|
||||
2. **Mouse Position**:
|
||||
- `🖱️ Current mouse position: (XXX, YYY)`
|
||||
- `🖱️ [DEBUG] Mouse position after move: (XXX, YYY)`
|
||||
|
||||
3. **Final Coordinates**:
|
||||
- `🎯 [DEBUG] Final coordinates: x=XXX, y=YYY`
|
||||
- `🔍 [DEBUG] Direct pixel coordinates (no Y-flip): x=XXX, y=YYY`
|
||||
|
||||
4. **Errors**:
|
||||
- `❌ [DEBUG] CGWarpMouseCursorPosition failed`
|
||||
- `🔐 [DEBUG] Accessibility permission status: false`
|
||||
|
||||
## Possible Issues
|
||||
|
||||
1. **Accessibility Permission**: Ensure VibeTunnel has accessibility permission in System Settings > Privacy & Security > Accessibility
|
||||
|
||||
2. **Coordinate Systems**:
|
||||
- SCDisplay uses top-left origin (0,0 at top-left)
|
||||
- NSScreen uses bottom-left origin (0,0 at bottom-left)
|
||||
- The Y-flipping might not be needed if SCDisplay coordinates are already converted
|
||||
|
||||
3. **Multi-Monitor**: The code now properly handles multi-monitor setups by finding which screen contains the click point
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Test each configuration and observe where the mouse actually clicks
|
||||
2. Check if clicks work better with `VIBETUNNEL_FLIP_Y=false`
|
||||
3. Compare logged coordinates with expected click positions
|
||||
4. If still not working, the debug logs will show exactly what coordinates are being used
|
||||
|
|
@ -278,26 +278,29 @@ public final class CaptureConfigurationBuilder {
|
|||
|
||||
return limitedDimensions
|
||||
}
|
||||
|
||||
|
||||
private func limitTo4K(width: Int, height: Int) -> (width: Int, height: Int) {
|
||||
let max4KWidth = 3840
|
||||
let max4KHeight = 2160
|
||||
|
||||
let max4KWidth = 3_840
|
||||
let max4KHeight = 2_160
|
||||
|
||||
// If already within 4K bounds, return as is
|
||||
if width <= max4KWidth && height <= max4KHeight {
|
||||
return (width, height)
|
||||
}
|
||||
|
||||
|
||||
// Calculate scale factor to fit within 4K bounds while maintaining aspect ratio
|
||||
let widthScale = Double(max4KWidth) / Double(width)
|
||||
let heightScale = Double(max4KHeight) / Double(height)
|
||||
let scale = min(widthScale, heightScale)
|
||||
|
||||
|
||||
let scaledWidth = Int(Double(width) * scale)
|
||||
let scaledHeight = Int(Double(height) * scale)
|
||||
|
||||
logger.info("🔽 Scaling down from \(width)x\(height) to \(scaledWidth)x\(scaledHeight) (scale: \(String(format: "%.2f", scale)))")
|
||||
|
||||
|
||||
logger
|
||||
.info(
|
||||
"🔽 Scaling down from \(width)x\(height) to \(scaledWidth)x\(scaledHeight) (scale: \(String(format: "%.2f", scale)))"
|
||||
)
|
||||
|
||||
return (width: scaledWidth, height: scaledHeight)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -288,7 +288,6 @@ public final class CoordinateTransformer {
|
|||
|
||||
return CGPoint(x: clampedX, y: clampedY)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helper Extensions
|
||||
|
|
|
|||
|
|
@ -124,20 +124,20 @@ final class SystemPermissionManager {
|
|||
requestPermission(permission)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Force a permission recheck (useful when user manually changes settings)
|
||||
func forcePermissionRecheck() {
|
||||
logger.info("Force permission recheck requested")
|
||||
|
||||
|
||||
// Clear any cached values
|
||||
permissions[.accessibility] = false
|
||||
permissions[.screenRecording] = false
|
||||
permissions[.appleScript] = false
|
||||
|
||||
|
||||
// Immediate check
|
||||
Task { @MainActor in
|
||||
await checkAllPermissions()
|
||||
|
||||
|
||||
// Double-check after a delay to catch any async updates
|
||||
try? await Task.sleep(for: .milliseconds(500))
|
||||
await checkAllPermissions()
|
||||
|
|
@ -282,7 +282,7 @@ final class SystemPermissionManager {
|
|||
// First check the API
|
||||
let apiResult = AXIsProcessTrusted()
|
||||
logger.debug("AXIsProcessTrusted returned: \(apiResult)")
|
||||
|
||||
|
||||
// More comprehensive test - try to get focused application and its windows
|
||||
// This definitely requires accessibility permission
|
||||
let systemElement = AXUIElementCreateSystemWide()
|
||||
|
|
@ -292,7 +292,7 @@ final class SystemPermissionManager {
|
|||
kAXFocusedApplicationAttribute as CFString,
|
||||
&focusedApp
|
||||
)
|
||||
|
||||
|
||||
if appResult == .success, let app = focusedApp {
|
||||
// Try to get windows from the app - this definitely needs accessibility
|
||||
var windows: CFTypeRef?
|
||||
|
|
@ -301,10 +301,10 @@ final class SystemPermissionManager {
|
|||
kAXWindowsAttribute as CFString,
|
||||
&windows
|
||||
)
|
||||
|
||||
|
||||
let hasAccess = windowResult == .success
|
||||
logger.debug("Comprehensive accessibility test result: \(hasAccess), can get windows: \(windows != nil)")
|
||||
|
||||
|
||||
if hasAccess {
|
||||
logger.debug("Accessibility permission verified through comprehensive test")
|
||||
return true
|
||||
|
|
@ -320,7 +320,7 @@ final class SystemPermissionManager {
|
|||
logger.debug("API reports true but cannot access UI elements")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ struct AdvancedSettingsView: View {
|
|||
.foregroundColor(.orange)
|
||||
Text("VT update available")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
|
||||
Button("Update") {
|
||||
Task {
|
||||
await cliInstaller.install()
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ struct VTCommandPageView: View {
|
|||
Text("CLI tool is outdated")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
|
||||
Button("Update VT Command Line Tool") {
|
||||
Task {
|
||||
await cliInstaller.install()
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ final class CLIInstaller {
|
|||
isInstalled = isCorrectlyInstalled
|
||||
|
||||
logger.info("CLIInstaller: vt script installed: \(self.isInstalled)")
|
||||
|
||||
|
||||
// If installed, check if it's outdated
|
||||
if isInstalled {
|
||||
checkScriptVersion()
|
||||
|
|
@ -265,29 +265,29 @@ final class CLIInstaller {
|
|||
alert.alertStyle = .critical
|
||||
alert.runModal()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Script Version Detection
|
||||
|
||||
|
||||
/// Calculates SHA256 hash of a file
|
||||
private func calculateHash(for filePath: String) -> String? {
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let hash = SHA256.hash(data: data)
|
||||
return hash.compactMap { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
|
||||
|
||||
/// Gets the hash of the bundled vt script
|
||||
private func getBundledScriptHash() -> String? {
|
||||
guard let scriptPath = Bundle.main.path(forResource: "vt", ofType: nil) else {
|
||||
logger.error("CLIInstaller: Bundled vt script not found")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
return calculateHash(for: scriptPath)
|
||||
}
|
||||
|
||||
|
||||
/// Checks if the installed script is outdated compared to bundled version
|
||||
func checkScriptVersion() {
|
||||
Task { @MainActor in
|
||||
|
|
@ -295,13 +295,13 @@ final class CLIInstaller {
|
|||
logger.error("CLIInstaller: Failed to get bundled script hash")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Check both possible installation paths
|
||||
let pathsToCheck = [
|
||||
vtTargetPath,
|
||||
"/opt/homebrew/bin/vt"
|
||||
]
|
||||
|
||||
|
||||
var installedHash: String?
|
||||
for path in pathsToCheck where FileManager.default.fileExists(atPath: path) {
|
||||
if let hash = calculateHash(for: path) {
|
||||
|
|
@ -309,9 +309,9 @@ final class CLIInstaller {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update outdated status
|
||||
if let installedHash = installedHash {
|
||||
if let installedHash {
|
||||
self.isOutdated = (installedHash != bundledHash)
|
||||
logger.info("CLIInstaller: Script version check - outdated: \(self.isOutdated)")
|
||||
logger.debug("CLIInstaller: Bundled hash: \(bundledHash), Installed hash: \(installedHash)")
|
||||
|
|
|
|||
|
|
@ -197,15 +197,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser
|
|||
// Check CLI installation status
|
||||
let cliInstaller = CLIInstaller()
|
||||
cliInstaller.checkInstallationStatus()
|
||||
|
||||
|
||||
// Show welcome screen when version changes OR when vt script is outdated
|
||||
let storedWelcomeVersion = UserDefaults.standard.integer(forKey: AppConstants.UserDefaultsKeys.welcomeVersion)
|
||||
|
||||
|
||||
// Small delay to allow CLI check to complete
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
||||
// Show welcome if version is different from current OR if vt script is outdated
|
||||
if (storedWelcomeVersion < AppConstants.currentWelcomeVersion || cliInstaller.isOutdated)
|
||||
&& !isRunningInTests && !isRunningInPreview {
|
||||
if (storedWelcomeVersion < AppConstants.currentWelcomeVersion || cliInstaller.isOutdated)
|
||||
&& !isRunningInTests && !isRunningInPreview
|
||||
{
|
||||
self?.showWelcomeScreen()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue