fix(pipeline): Use config display value in auto-injection

- Change line 477 in controller.py to use self.config.display or "terminal"
- Previously hardcoded "terminal" ignored config and CLI arguments
- Now auto-injection respects the validated display configuration

fix(app): Add warnings for auto-selected display

- Add warning in pipeline_runner.py when --display not specified
- Add warning in main.py when --pipeline-display not specified
- Both warnings suggest using null display for headless mode

feat(completion): Add bash/zsh/fish completion scripts

- completion/mainline-completion.bash - bash completion
- completion/mainline-completion.zsh - zsh completion
- completion/mainline-completion.fish - fish completion
- Provides completions for --display, --pipeline-source, --pipeline-effects,
  --pipeline-camera, --preset, --theme, --viewport, and other flags
This commit is contained in:
2026-03-21 17:05:03 -07:00
parent f568cc1a73
commit 860bab6550
6 changed files with 251 additions and 3 deletions

View File

@@ -0,0 +1,99 @@
# Mainline bash completion script
#
# To install:
# source /path/to/completion/mainline-completion.bash
#
# Or add to ~/.bashrc:
# source /path/to/completion/mainline-completion.bash
_mainline_completion() {
local cur prev words cword
_init_completion || return
# Get current word and previous word
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Completion options based on previous word
case "${prev}" in
--display)
# Display backends
COMPREPLY=($(compgen -W "terminal null replay websocket pygame moderngl" -- "${cur}"))
return
;;
--pipeline-source)
# Available sources
COMPREPLY=($(compgen -W "headlines poetry empty fixture pipeline-inspect" -- "${cur}"))
return
;;
--pipeline-effects)
# Available effects (comma-separated)
local effects="afterimage border crop fade firehose glitch hud motionblur noise tint"
COMPREPLY=($(compgen -W "${effects}" -- "${cur}"))
return
;;
--pipeline-camera)
# Camera modes
COMPREPLY=($(compgen -W "feed scroll horizontal omni floating bounce radial" -- "${cur}"))
return
;;
--pipeline-border)
# Border modes
COMPREPLY=($(compgen -W "off simple ui" -- "${cur}"))
return
;;
--pipeline-display)
# Display backends (same as --display)
COMPREPLY=($(compgen -W "terminal null replay websocket pygame moderngl" -- "${cur}"))
return
;;
--theme)
# Theme colors
COMPREPLY=($(compgen -W "green orange purple blue red" -- "${cur}"))
return
;;
--viewport)
# Viewport size suggestions
COMPREPLY=($(compgen -W "80x24 100x30 120x40 60x20" -- "${cur}"))
return
;;
--preset)
# Presets (would need to query available presets)
COMPREPLY=($(compgen -W "demo demo-base demo-pygame demo-camera-showcase poetry headlines empty test-basic test-border test-scroll-camera" -- "${cur}"))
return
;;
esac
# Flag completion (start with --)
if [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W "
--display
--pipeline-source
--pipeline-effects
--pipeline-camera
--pipeline-display
--pipeline-ui
--pipeline-border
--viewport
--preset
--theme
--websocket
--websocket-port
--allow-unsafe
--help
" -- "${cur}"))
return
fi
}
complete -F _mainline_completion mainline.py
complete -F _mainline_completion python\ -m\ engine.app
complete -F _mainline_completion python\ -m\ mainline

View File

@@ -0,0 +1,81 @@
# Fish completion script for Mainline
#
# To install:
# source /path/to/completion/mainline-completion.fish
#
# Or copy to ~/.config/fish/completions/mainline.fish
# Define display backends
set -l display_backends terminal null replay websocket pygame moderngl
# Define sources
set -l sources headlines poetry empty fixture pipeline-inspect
# Define effects
set -l effects afterimage border crop fade firehose glitch hud motionblur noise tint
# Define camera modes
set -l cameras feed scroll horizontal omni floating bounce radial
# Define border modes
set -l borders off simple ui
# Define themes
set -l themes green orange purple blue red
# Define presets
set -l presets demo demo-base demo-pygame demo-camera-showcase poetry headlines empty test-basic test-border test-scroll-camera test-figment test-message-overlay
# Main completion function
function __mainline_complete
set -l cmd (commandline -po)
set -l token (commandline -t)
# Complete display backends
complete -c mainline.py -n '__fish_seen_argument --display' -a "$display_backends" -d 'Display backend'
# Complete sources
complete -c mainline.py -n '__fish_seen_argument --pipeline-source' -a "$sources" -d 'Data source'
# Complete effects
complete -c mainline.py -n '__fish_seen_argument --pipeline-effects' -a "$effects" -d 'Effect plugin'
# Complete camera modes
complete -c mainline.py -n '__fish_seen_argument --pipeline-camera' -a "$cameras" -d 'Camera mode'
# Complete display backends (pipeline)
complete -c mainline.py -n '__fish_seen_argument --pipeline-display' -a "$display_backends" -d 'Display backend'
# Complete border modes
complete -c mainline.py -n '__fish_seen_argument --pipeline-border' -a "$borders" -d 'Border mode'
# Complete themes
complete -c mainline.py -n '__fish_seen_argument --theme' -a "$themes" -d 'Color theme'
# Complete presets
complete -c mainline.py -n '__fish_seen_argument --preset' -a "$presets" -d 'Preset name'
# Complete viewport sizes
complete -c mainline.py -n '__fish_seen_argument --viewport' -a '80x24 100x30 120x40 60x20' -d 'Viewport size (WxH)'
# Complete flag options
complete -c mainline.py -n 'not __fish_seen_argument --display' -l display -d 'Display backend' -a "$display_backends"
complete -c mainline.py -n 'not __fish_seen_argument --preset' -l preset -d 'Preset to use' -a "$presets"
complete -c mainline.py -n 'not __fish_seen_argument --viewport' -l viewport -d 'Viewport size (WxH)' -a '80x24 100x30 120x40 60x20'
complete -c mainline.py -n 'not __fish_seen_argument --theme' -l theme -d 'Color theme' -a "$themes"
complete -c mainline.py -l websocket -d 'Enable WebSocket server'
complete -c mainline.py -n 'not __fish_seen_argument --websocket-port' -l websocket-port -d 'WebSocket port' -a '8765'
complete -c mainline.py -l allow-unsafe -d 'Allow unsafe pipeline configuration'
complete -c mainline.py -n 'not __fish_seen_argument --help' -l help -d 'Show help'
# Pipeline-specific flags
complete -c mainline.py -n 'not __fish_seen_argument --pipeline-source' -l pipeline-source -d 'Data source' -a "$sources"
complete -c mainline.py -n 'not __fish_seen_argument --pipeline-effects' -l pipeline-effects -d 'Effect plugins (comma-separated)' -a "$effects"
complete -c mainline.py -n 'not __fish_seen_argument --pipeline-camera' -l pipeline-camera -d 'Camera mode' -a "$cameras"
complete -c mainline.py -n 'not __fish_seen_argument --pipeline-display' -l pipeline-display -d 'Display backend' -a "$display_backends"
complete -c mainline.py -l pipeline-ui -d 'Enable UI panel'
complete -c mainline.py -n 'not __fish_seen_argument --pipeline-border' -l pipeline-border -d 'Border mode' -a "$borders"
end
# Register the completion function
__mainline_complete

View File

@@ -0,0 +1,48 @@
#compdef mainline.py
# Mainline zsh completion script
#
# To install:
# source /path/to/completion/mainline-completion.zsh
#
# Or add to ~/.zshrc:
# source /path/to/completion/mainline-completion.zsh
# Define completion function
_mainline() {
local -a commands
local curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'(-h --help)'{-h,--help}'[Show help]' \
'--display=[Display backend]:backend:(terminal null replay websocket pygame moderngl)' \
'--preset=[Preset to use]:preset:(demo demo-base demo-pygame demo-camera-showcase poetry headlines empty test-basic test-border test-scroll-camera test-figment test-message-overlay)' \
'--viewport=[Viewport size]:size:(80x24 100x30 120x40 60x20)' \
'--theme=[Color theme]:theme:(green orange purple blue red)' \
'--websocket[Enable WebSocket server]' \
'--websocket-port=[WebSocket port]:port:' \
'--allow-unsafe[Allow unsafe pipeline configuration]' \
'(-)*: :{_files}' \
&& ret=0
# Handle --pipeline-* arguments
if [[ -n ${words[*]} ]]; then
_arguments -C \
'--pipeline-source=[Data source]:source:(headlines poetry empty fixture pipeline-inspect)' \
'--pipeline-effects=[Effect plugins]:effects:(afterimage border crop fade firehose glitch hud motionblur noise tint)' \
'--pipeline-camera=[Camera mode]:camera:(feed scroll horizontal omni floating bounce radial)' \
'--pipeline-display=[Display backend]:backend:(terminal null replay websocket pygame moderngl)' \
'--pipeline-ui[Enable UI panel]' \
'--pipeline-border=[Border mode]:mode:(off simple ui)' \
'--viewport=[Viewport size]:size:(80x24 100x30 120x40 60x20)' \
&& ret=0
fi
return ret
}
# Register completion function
compdef _mainline mainline.py
compdef _mainline "python -m engine.app"
compdef _mainline "python -m mainline"

View File

@@ -254,6 +254,16 @@ def run_pipeline_mode_direct():
# Create display using validated display name # Create display using validated display name
display_name = result.config.display or "terminal" # Default to terminal if empty display_name = result.config.display or "terminal" # Default to terminal if empty
# Warn if display was auto-selected (not explicitly specified)
if not display_name:
print(
" \033[38;5;226mWarning: No --pipeline-display specified, using default: terminal\033[0m"
)
print(
" \033[38;5;245mTip: Use --pipeline-display null for headless mode (useful for testing)\033[0m"
)
display = DisplayRegistry.create(display_name) display = DisplayRegistry.create(display_name)
if not display: if not display:
print(f" \033[38;5;196mFailed to create display: {display_name}\033[0m") print(f" \033[38;5;196mFailed to create display: {display_name}\033[0m")

View File

@@ -189,10 +189,19 @@ def run_pipeline_mode(preset_name: str = "demo"):
# CLI --display flag takes priority over preset # CLI --display flag takes priority over preset
# Check if --display was explicitly provided # Check if --display was explicitly provided
display_name = preset.display display_name = preset.display
if "--display" in sys.argv: display_explicitly_specified = "--display" in sys.argv
if display_explicitly_specified:
idx = sys.argv.index("--display") idx = sys.argv.index("--display")
if idx + 1 < len(sys.argv): if idx + 1 < len(sys.argv):
display_name = sys.argv[idx + 1] display_name = sys.argv[idx + 1]
else:
# Warn user that display is falling back to preset default
print(
f" \033[38;5;226mWarning: No --display specified, using preset default: {display_name}\033[0m"
)
print(
" \033[38;5;245mTip: Use --display null for headless mode (useful for testing/capture)\033[0m"
)
display = DisplayRegistry.create(display_name) display = DisplayRegistry.create(display_name)
if not display and not display_name.startswith("multi"): if not display and not display_name.startswith("multi"):

View File

@@ -474,9 +474,10 @@ class Pipeline:
not self._find_stage_with_capability("display.output") not self._find_stage_with_capability("display.output")
and "display" not in self._stages and "display" not in self._stages
): ):
display = DisplayRegistry.create("terminal") display_name = self.config.display or "terminal"
display = DisplayRegistry.create(display_name)
if display: if display:
self.add_stage("display", DisplayStage(display, name="terminal")) self.add_stage("display", DisplayStage(display, name=display_name))
injected.append("display") injected.append("display")
# Rebuild pipeline if stages were injected # Rebuild pipeline if stages were injected