diff --git a/README.md b/README.md index 845b051..8ad7c01 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The installer will build, install, and set up everything automatically with a gu **After installation:** ```bash -source ~/.bashrc # (or ~/.zshrc for zsh) +source ~/.bashrc # (or ~/.zshrc, or ~/.config/fish/config.fish) VideoTools ``` @@ -91,6 +91,7 @@ Output is professional quality, ready for: - **DVD_IMPLEMENTATION_SUMMARY.md** - Technical specifications - **INTEGRATION_GUIDE.md** - System architecture and integration - **QUEUE_SYSTEM_GUIDE.md** - Queue system reference +- **localization-policy.md** - Localization strategy and implementation guide ## Requirements @@ -112,7 +113,9 @@ VideoTools has a modular architecture: ### Build & Run ```bash # One-time setup -source scripts/alias.sh +source scripts/alias.sh # bash +# source scripts/alias.zsh # zsh +# source scripts/alias.fish # fish # Run the application VideoTools diff --git a/docs/BUILD_AND_RUN.md b/docs/BUILD_AND_RUN.md index d65dfb4..2c6c2be 100644 --- a/docs/BUILD_AND_RUN.md +++ b/docs/BUILD_AND_RUN.md @@ -6,7 +6,9 @@ ```bash cd /home/stu/Projects/VideoTools -source scripts/alias.sh +source scripts/alias.sh # bash +# source scripts/alias.zsh # zsh +# source scripts/alias.fish # fish VideoTools ``` @@ -15,7 +17,7 @@ This will: 2. Build the application (if needed) 3. Run VideoTools GUI -**Available commands after sourcing alias.sh:** +**Available commands after sourcing the alias script:** - `VideoTools` - Run the application - `VideoToolsRebuild` - Force a clean rebuild - `VideoToolsClean` - Clean all build artifacts @@ -67,7 +69,7 @@ source ~/.bashrc ### For Zsh users: Add this line to `~/.zshrc`: ```bash -source /home/stu/Projects/VideoTools/scripts/alias.sh +source /home/stu/Projects/VideoTools/scripts/alias.zsh ``` Then reload: @@ -75,6 +77,17 @@ Then reload: source ~/.zshrc ``` +### For Fish users: +Add this line to `~/.config/fish/config.fish`: +```fish +source /home/stu/Projects/VideoTools/scripts/alias.fish +``` + +Reload fish: +```fish +source ~/.config/fish/config.fish +``` + ### After setting up: From any directory, you can simply type: ```bash @@ -133,10 +146,16 @@ bash scripts/run.sh - No manual steps needed - Always runs the latest code -### alias.sh +### alias.sh / alias.zsh / alias.fish ```bash source scripts/alias.sh ``` +```bash +source scripts/alias.zsh +``` +```fish +source scripts/alias.fish +``` **Purpose:** Creates convenient shell commands @@ -440,4 +459,3 @@ VideoTools ``` **That's it!** The scripts handle everything else automatically. - diff --git a/docs/INSTALL_LINUX.md b/docs/INSTALL_LINUX.md index b66e4d1..64d3793 100644 --- a/docs/INSTALL_LINUX.md +++ b/docs/INSTALL_LINUX.md @@ -24,7 +24,7 @@ This single command automates the entire setup process. 4. **Install Binary:** Copies the compiled binary to the selected location and makes it executable. 5. **Configure Shell:** Detects your shell (`bash` or `zsh`) and updates the corresponding resource file (`~/.bashrc` or `~/.zshrc`) to: * Add the installation directory to your `PATH`. - * Source the `alias.sh` script for convenience commands. + * Source the matching alias script (`alias.sh` for bash, `alias.zsh` for zsh). ### After Installation @@ -36,6 +36,9 @@ source ~/.bashrc # For zsh users: source ~/.zshrc + +# For fish users (manual setup required): +source ~/.config/fish/config.fish ``` You can now run the application from anywhere by simply typing `VideoTools`. @@ -80,7 +83,9 @@ If you prefer to perform the steps manually: export PATH="$HOME/.local/bin:$PATH" # Source VideoTools aliases - source /path/to/VideoTools/scripts/alias.sh + source /path/to/VideoTools/scripts/alias.sh # bash + # source /path/to/VideoTools/scripts/alias.zsh # zsh + # source /path/to/VideoTools/scripts/alias.fish # fish ``` 4. **Reload Your Shell:** diff --git a/main.go b/main.go index a59d14c..ffd8d43 100644 --- a/main.go +++ b/main.go @@ -48,11 +48,20 @@ import ( "git.leaktechnologies.dev/stu/VideoTools/internal/sysinfo" "git.leaktechnologies.dev/stu/VideoTools/internal/ui" "git.leaktechnologies.dev/stu/VideoTools/internal/utils" + guitutils "git.leaktechnologies.dev/stu/VideoTools/internal/utils" "github.com/ebitengine/oto/v3" "github.com/skip2/go-qrcode" ) +// abs returns the absolute value of an int32 +func abs(x int32) int32 { + if x < 0 { + return -x + } + return x +} + // Module describes a high level tool surface that gets a tile on the menu. type Module struct { ID string @@ -1156,49 +1165,49 @@ type appState struct { historyTabIdx int // Author module state - authorFile *videoSource - authorChapters []authorChapter - authorSceneThreshold float64 - authorDetecting bool - authorClips []authorClip // Multiple video clips for compilation - authorOutputType string // "dvd" or "iso" - authorRegion string // "NTSC", "PAL", "AUTO" - authorAspectRatio string // "4:3", "16:9", "AUTO" - authorCreateMenu bool // Whether to create DVD menu - authorMenuTemplate string // "Simple", "Dark", "Poster" - authorMenuBackgroundImage string // Path to a user-selected background image - authorMenuTheme string // "VideoTools" - authorMenuTitleLogoEnabled bool // Enable title logo (main logo above menu) - authorMenuTitleLogoPath string // Path to title logo image - authorMenuTitleLogoPosition string // Position for title logo - authorMenuTitleLogoScale float64 // Scale for title logo - authorMenuTitleLogoMargin int // Margin for title logo - authorMenuStudioLogoEnabled bool // Enable studio logo (corner logo) - authorMenuStudioLogoPath string // Path to studio logo image - authorMenuStudioLogoPosition string // "Top Left", "Top Right", "Bottom Left", "Bottom Right" - authorMenuStudioLogoScale float64 // Scale for studio logo - authorMenuStudioLogoMargin int // Margin for studio logo - authorMenuStructure string // Feature only, Chapters, Extras - authorMenuExtrasEnabled bool // Show extras menu - authorMenuChapterThumbSrc string // Auto, First Frame, Midpoint, Custom - authorTitle string // DVD title - authorSubtitles []string // Subtitle file paths - authorAudioTracks []string // Additional audio tracks - authorSummaryLabel *widget.Label - authorTreatAsChapters bool // Treat multiple clips as chapters - authorChapterSource string // embedded, scenes, clips, manual - authorChaptersRefresh func() // Refresh hook for chapter list UI - authorDiscSize string // "DVD5" or "DVD9" - authorLogText string - authorLogLines []string // Circular buffer for last N lines - authorLogFilePath string // Path to log file for full viewing - authorLogEntry *widget.Entry - authorLogScroll *container.Scroll - authorProgress float64 - authorProgressBar *widget.ProgressBar - authorStatusLabel *widget.Label - authorCancelBtn *widget.Button - authorVideoTSPath string + authorFile *videoSource + authorChapters []authorChapter + authorSceneThreshold float64 + authorDetecting bool + authorClips []authorClip // Multiple video clips for compilation + authorOutputType string // "dvd" or "iso" + authorRegion string // "NTSC", "PAL", "AUTO" + authorAspectRatio string // "4:3", "16:9", "AUTO" + authorCreateMenu bool // Whether to create DVD menu + authorMenuTemplate string // "Simple", "Dark", "Poster" + authorMenuBackgroundImage string // Path to a user-selected background image + authorMenuTheme string // "VideoTools" + authorMenuTitleLogoEnabled bool // Enable title logo (main logo above menu) + authorMenuTitleLogoPath string // Path to title logo image + authorMenuTitleLogoPosition string // Position for title logo + authorMenuTitleLogoScale float64 // Scale for title logo + authorMenuTitleLogoMargin int // Margin for title logo + authorMenuStudioLogoEnabled bool // Enable studio logo (corner logo) + authorMenuStudioLogoPath string // Path to studio logo image + authorMenuStudioLogoPosition string // "Top Left", "Top Right", "Bottom Left", "Bottom Right" + authorMenuStudioLogoScale float64 // Scale for studio logo + authorMenuStudioLogoMargin int // Margin for studio logo + authorMenuStructure string // Feature only, Chapters, Extras + authorMenuExtrasEnabled bool // Show extras menu + authorMenuChapterThumbSrc string // Auto, First Frame, Midpoint, Custom + authorTitle string // DVD title + authorSubtitles []string // Subtitle file paths + authorAudioTracks []string // Additional audio tracks + authorSummaryLabel *widget.Label + authorTreatAsChapters bool // Treat multiple clips as chapters + authorChapterSource string // embedded, scenes, clips, manual + authorChaptersRefresh func() // Refresh hook for chapter list UI + authorDiscSize string // "DVD5" or "DVD9" + authorLogText string + authorLogLines []string // Circular buffer for last N lines + authorLogFilePath string // Path to log file for full viewing + authorLogEntry *widget.Entry + authorLogScroll *container.Scroll + authorProgress float64 + authorProgressBar *widget.ProgressBar + authorStatusLabel *widget.Label + authorCancelBtn *widget.Button + authorVideoTSPath string // Rip module state ripSourcePath string @@ -1851,25 +1860,21 @@ func (r *mouseButtonRenderer) BackgroundColor() color.Color { func (s *appState) setContent(body fyne.CanvasObject) { update := func() { - // Preserve current window size to prevent auto-resizing when content changes - // This ensures the window maintains the size the user set, even when content - // like progress bars or queue items change dynamically currentSize := s.window.Canvas().Size() - bg := canvas.NewRectangle(backgroundColor) sizeGuard := canvas.NewRectangle(color.Transparent) - sizeGuard.SetMinSize(fyne.NewSize(800, 600)) + if currentSize.Width > 0 && currentSize.Height > 0 { + sizeGuard.SetMinSize(currentSize) + } else { + sizeGuard.SetMinSize(fyne.NewSize(800, 600)) + } if body == nil { s.window.SetContent(container.NewMax(bg, sizeGuard)) - // Restore window size after setting content - s.window.Resize(currentSize) return } // Wrap content with mouse button handler wrapped := newMouseButtonHandler(container.NewMax(bg, sizeGuard, body), s) s.window.SetContent(wrapped) - // Restore window size after setting content - s.window.Resize(currentSize) } // Use async Do() instead of DoAndWait() to avoid deadlock when called from main goroutine @@ -1905,6 +1910,7 @@ func (s *appState) showErrorWithCopy(title string, err error) { } func (s *appState) showMainMenu() { + _ = guitutils.DetectGUIEnvironment() // Use GUI utilities to ensure import is used s.stopPreview() s.stopPlayer() s.stopQueueAutoRefresh() @@ -6673,15 +6679,26 @@ func runGUI() { } else { logging.Debug(logging.CatUI, "app icon not found; continuing without custom icon") } + // Enhanced cross-platform GUI detection and window sizing + guiEnv := guitutils.DetectGUIEnvironment() + logging.Debug(logging.CatUI, "detected GUI environment: %s", guiEnv.String()) + // Adaptive window sizing for professional cross-resolution support w.SetFixedSize(false) // Allow manual resizing and maximizing - // Use compact default size (800x600) that fits on any screen - // Window can be resized or maximized by user using window manager controls - w.Resize(fyne.NewSize(800, 600)) + // Use GUI environment to determine optimal window size + optimalSize := guiEnv.GetOptimalWindowSize(800, 600) + w.Resize(optimalSize) w.CenterOnScreen() - logging.Debug(logging.CatUI, "window initialized at 800x600 (compact default), manual resizing enabled") + // Log GPU acceleration support + if guiEnv.SupportsGPUAcceleration() { + logging.Debug(logging.CatUI, "GPU acceleration should be available on %s %s", guiEnv.GPUInfo.Vendor, guiEnv.GPUInfo.Model) + } else { + logging.Debug(logging.CatUI, "GPU acceleration may not be available - using software rendering") + } + + logging.Debug(logging.CatUI, "window initialized at %v (auto-detected environment), manual resizing enabled", optimalSize) // Initialize audio module - load persisted config or use defaults audioDefaults, err := loadAudioConfig() @@ -11299,12 +11316,12 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe } sess := &playSession{ - path: path, - fps: fps, - width: w, - height: h, - targetW: targetW, - targetH: targetH, + path: path, + fps: fps, + width: w, + height: h, + targetW: targetW, + targetH: targetH, volume: 100, duration: duration, stop: make(chan struct{}), diff --git a/scripts/alias.fish b/scripts/alias.fish new file mode 100644 index 0000000..0cea4d8 --- /dev/null +++ b/scripts/alias.fish @@ -0,0 +1,26 @@ +#!/usr/bin/env fish +# VideoTools Convenience Script (fish) +# Source this file in fish to add the 'VideoTools' command + +if not set -q FISH_VERSION + echo "This script is for fish. Use scripts/alias.sh or scripts/alias.zsh instead." + return 1 +end + +set -l script_path (status -f) +set -l project_root (dirname (dirname $script_path)) + +alias VideoTools="bash $project_root/scripts/run.sh" + +function VideoToolsRebuild + echo "Rebuilding VideoTools..." + bash "$project_root/scripts/build.sh" +end + +function VideoToolsClean + echo "Cleaning VideoTools build artifacts..." + cd "$project_root" + go clean -cache -modcache -testcache + rm -f "$project_root/VideoTools" + echo "Clean complete" +end diff --git a/scripts/alias.sh b/scripts/alias.sh index 8163b24..75e7795 100755 --- a/scripts/alias.sh +++ b/scripts/alias.sh @@ -1,6 +1,11 @@ #!/bin/bash -# VideoTools Convenience Script -# Source this file in your shell to add the 'VideoTools' command +# VideoTools Convenience Script (bash) +# Source this file in bash to add the 'VideoTools' command + +if [ -z "$BASH_VERSION" ]; then + echo "This script is for bash. Use scripts/alias.zsh or scripts/alias.fish instead." + return 1 +fi PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" diff --git a/scripts/alias.zsh b/scripts/alias.zsh new file mode 100644 index 0000000..85a6943 --- /dev/null +++ b/scripts/alias.zsh @@ -0,0 +1,26 @@ +#!/usr/bin/env zsh +# VideoTools Convenience Script (zsh) +# Source this file in zsh to add the 'VideoTools' command + +if [[ -z "$ZSH_VERSION" ]]; then + echo "This script is for zsh. Use scripts/alias.sh or scripts/alias.fish instead." + return 1 +fi + +SCRIPT_PATH="${(%):-%N}" +PROJECT_ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)" + +alias VideoTools="bash $PROJECT_ROOT/scripts/run.sh" + +VideoToolsRebuild() { + echo "Rebuilding VideoTools..." + bash "$PROJECT_ROOT/scripts/build.sh" +} + +VideoToolsClean() { + echo "Cleaning VideoTools build artifacts..." + cd "$PROJECT_ROOT" || return 1 + go clean -cache -modcache -testcache + rm -f "$PROJECT_ROOT/VideoTools" + echo "Clean complete" +}