chore: add Apache License 2.0 and ignore build directory

This commit is contained in:
2026-07-16 17:24:50 +03:00
parent 45e4bce2a0
commit 7affa0dbc8
23 changed files with 1814 additions and 157 deletions
+91
View File
@@ -37,6 +37,66 @@ func Parse() Settings {
return settings
}
type OutputMode string
const (
OutputTerminal OutputMode = "terminal"
OutputOverlay OutputMode = "overlay"
OutputBoth OutputMode = "both"
)
type OverlayBackend string
const (
BackendAuto OverlayBackend = "auto"
BackendWayland OverlayBackend = "wayland"
BackendX11 OverlayBackend = "x11"
)
type OverlaySettings struct {
Backend OverlayBackend
Opacity float64
FontSize int
BottomMargin int
MaxWidth int
Monitor string
FinalTimeout time.Duration
ClickThrough bool
}
type DesktopSettings struct {
Settings
Output OutputMode
Overlay OverlaySettings
}
func ParseDesktop() DesktopSettings {
flags := flag.CommandLine
var desktop DesktopSettings
var mode string
var output string
var backend string
flags.StringVar(&mode, "mode", "", "caption mode: vad or fixed (required)")
flags.DurationVar(&desktop.ChunkDuration, "chunk-duration", time.Second, "fixed chunk size or VAD preview refresh interval")
flags.StringVar(&desktop.ModelsDir, "models-dir", DefaultModelsDir, "directory containing downloaded Sherpa models")
flags.IntVar(&desktop.Threads, "threads", 2, "CPU threads used by recognition and VAD")
flags.Float64Var(&desktop.PreviewThresholdDB, "preview-threshold-dbfs", -45, "RMS dBFS threshold used to begin VAD previews")
flags.StringVar(&output, "output", "", "caption output: terminal, overlay, or both (required)")
flags.StringVar(&backend, "overlay-backend", "auto", "display backend: auto, wayland, or x11")
flags.Float64Var(&desktop.Overlay.Opacity, "overlay-opacity", 0.90, "caption background opacity from 0 to 1")
flags.IntVar(&desktop.Overlay.FontSize, "overlay-font-size", 28, "caption font size in logical pixels")
flags.IntVar(&desktop.Overlay.BottomMargin, "overlay-bottom-margin", 100, "distance from the monitor bottom in logical pixels")
flags.IntVar(&desktop.Overlay.MaxWidth, "overlay-max-width", 1200, "maximum caption width in logical pixels")
flags.StringVar(&desktop.Overlay.Monitor, "overlay-monitor", "auto", "monitor selection: auto, numeric index, or connector name")
flags.DurationVar(&desktop.Overlay.FinalTimeout, "overlay-final-timeout", 4*time.Second, "how long a final caption remains; zero waits for replacement")
flags.BoolVar(&desktop.Overlay.ClickThrough, "overlay-click-through", true, "pass pointer input through the caption window")
flag.Parse()
desktop.Mode = Mode(mode)
desktop.Output = OutputMode(output)
desktop.Overlay.Backend = OverlayBackend(backend)
return desktop
}
func (s Settings) Validate() error {
if s.Mode != ModeFixed && s.Mode != ModeVAD {
return errors.New("--mode is required and must be either fixed or vad")
@@ -49,3 +109,34 @@ func (s Settings) Validate() error {
}
return nil
}
func (s DesktopSettings) Validate() error {
if err := s.Settings.Validate(); err != nil {
return err
}
if s.Output != OutputTerminal && s.Output != OutputOverlay && s.Output != OutputBoth {
return errors.New("--output is required and must be terminal, overlay, or both")
}
if s.Overlay.Backend != BackendAuto && s.Overlay.Backend != BackendWayland && s.Overlay.Backend != BackendX11 {
return errors.New("--overlay-backend must be auto, wayland, or x11")
}
if s.Overlay.Opacity < 0 || s.Overlay.Opacity > 1 {
return errors.New("--overlay-opacity must be between 0 and 1")
}
if s.Overlay.FontSize <= 0 {
return errors.New("--overlay-font-size must be positive")
}
if s.Overlay.BottomMargin < 0 {
return errors.New("--overlay-bottom-margin cannot be negative")
}
if s.Overlay.MaxWidth <= 0 {
return errors.New("--overlay-max-width must be positive")
}
if s.Overlay.FinalTimeout < 0 {
return errors.New("--overlay-final-timeout cannot be negative")
}
if s.Overlay.Monitor == "" {
return errors.New("--overlay-monitor cannot be empty")
}
return nil
}
+41
View File
@@ -14,3 +14,44 @@ func TestSettingsValidate(t *testing.T) {
t.Fatal("missing mode unexpectedly passed")
}
}
func validDesktopSettings() DesktopSettings {
return DesktopSettings{
Settings: Settings{Mode: ModeVAD, ChunkDuration: time.Second, Threads: 2},
Output: OutputOverlay,
Overlay: OverlaySettings{
Backend: BackendAuto, Opacity: 0.9, FontSize: 28, BottomMargin: 100,
MaxWidth: 1200, Monitor: "auto", FinalTimeout: 4 * time.Second, ClickThrough: true,
},
}
}
func TestDesktopSettingsValidate(t *testing.T) {
if err := validDesktopSettings().Validate(); err != nil {
t.Fatalf("valid desktop settings failed: %v", err)
}
tests := []struct {
name string
change func(*DesktopSettings)
}{
{"missing output", func(s *DesktopSettings) { s.Output = "" }},
{"invalid backend", func(s *DesktopSettings) { s.Overlay.Backend = "other" }},
{"negative opacity", func(s *DesktopSettings) { s.Overlay.Opacity = -0.1 }},
{"large opacity", func(s *DesktopSettings) { s.Overlay.Opacity = 1.1 }},
{"zero font", func(s *DesktopSettings) { s.Overlay.FontSize = 0 }},
{"negative margin", func(s *DesktopSettings) { s.Overlay.BottomMargin = -1 }},
{"zero width", func(s *DesktopSettings) { s.Overlay.MaxWidth = 0 }},
{"empty monitor", func(s *DesktopSettings) { s.Overlay.Monitor = "" }},
{"negative timeout", func(s *DesktopSettings) { s.Overlay.FinalTimeout = -time.Second }},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
settings := validDesktopSettings()
test.change(&settings)
if err := settings.Validate(); err == nil {
t.Fatal("invalid settings unexpectedly passed")
}
})
}
}