diff --git a/app/upgrader.py b/app/upgrader.py index 30a36a71..e02eb632 100644 --- a/app/upgrader.py +++ b/app/upgrader.py @@ -35,18 +35,19 @@ class Upgrader: load_dotenv(str(envFile)) user_site: Path = config_path / f"python{sys.version_info.major}.{sys.version_info.minor}-packages" + user_site_ver: Path = user_site / ".version" + + from app.library.version import APP_VERSION if not user_site.exists(): user_site.mkdir(parents=True, exist_ok=True) + user_site_ver.write_text(APP_VERSION) if user_site.is_dir(): if str(user_site) not in sys.path: sys.path.insert(0, str(user_site)) - from app.library.version import APP_VERSION - - user_site_ver: Path = user_site / ".version" - if not user_site_ver.exists() or user_site_ver.read_text().strip() != APP_VERSION: + if not user_site_ver.exists() or APP_VERSION != user_site_ver.read_text().strip(): self.clean_up(user_site, user_site_ver, APP_VERSION) self.check(config_path, user_site) diff --git a/ui/app/components/InputAutocomplete.vue b/ui/app/components/InputAutocomplete.vue index 3e1d4ce1..6f5191f2 100644 --- a/ui/app/components/InputAutocomplete.vue +++ b/ui/app/components/InputAutocomplete.vue @@ -34,21 +34,38 @@ const props = defineProps<{ const model = defineModel() const onInput = () => { - if (false === showList.value) { - showList.value = true - } - highlightedIndex.value = filteredOptions.value.length ? 0 : -1 + showList.value = isFlagTrigger.value && filteredOptions.value.length > 0 + highlightedIndex.value = showList.value ? 0 : -1 } const showList = ref(false) const highlightedIndex = ref(-1) const dropdownItemRefs = ref<(HTMLElement | null)[]>([]) +// Extract the last non-space token and its bounds +const getLastToken = (value: string) => { + const m = (value || '').match(/(\S+)$/) + const token: string = m?.[1] ?? '' + const start = m ? (m.index as number) : value.length + const end = m ? start + token.length : value.length + return { token, start, end } +} + const filteredOptions = computed(() => { - if (!model.value) { + const value = model.value || '' + if (!value) { return props.options } - const val = model.value.toLowerCase() + const { token } = getLastToken(value) + // Hide suggestions when token has '=' or doesn't start with '--' + if (!token || token.includes('=') || !token.startsWith('--')) { + return [] + } + // Hide suggestions if token exactly matches an option value + if (props.options.some(opt => opt.value === token)) { + return [] + } + const val = token.toLowerCase() const startsWithFlag = [] const includesFlag = [] const includesDesc = [] @@ -67,7 +84,16 @@ const filteredOptions = computed(() => { }) const selectOption = (val: string) => { - model.value = val + const value = model.value || '' + const { token, start, end } = getLastToken(value) + if (token) { + // Preserve any '=value' suffix already typed for this token + const eqPos = token.indexOf('=') + const after = eqPos !== -1 ? token.slice(eqPos) : '' + model.value = value.slice(0, start) + val + after + value.slice(end) + } else { + model.value = val + } showList.value = false highlightedIndex.value = -1 } @@ -81,8 +107,8 @@ const hideList = () => { } const onFocus = () => { - showList.value = true - highlightedIndex.value = filteredOptions.value.length ? 0 : -1 + showList.value = isFlagTrigger.value && filteredOptions.value.length > 0 + highlightedIndex.value = showList.value ? 0 : -1 } const setDropdownItemRef = (el: Element | ComponentPublicInstance | null, idx: number) => { @@ -100,8 +126,21 @@ watch(filteredOptions, () => { }) }) +const isFlagTrigger = computed(() => { + const { token } = getLastToken(model.value || '') + if (!token || !token.startsWith('--') || token.includes('=')) return false + // Suppress if exact match + return !props.options.some(opt => opt.value === token) +}) + const handleKeydown = (e: KeyboardEvent) => { - if (!filteredOptions.value.length) { + // Escape closes dropdown and lets arrows navigate the input + if (e.key === 'Escape') { + showList.value = false + highlightedIndex.value = -1 + return + } + if (!showList.value || !filteredOptions.value.length) { return } @@ -126,7 +165,7 @@ const handleKeydown = (e: KeyboardEvent) => { } else if (e.key === 'Enter' || e.key === 'Tab') { const selected = highlightedIndex.value >= 0 && highlightedIndex.value < filteredOptions.value.length ? filteredOptions.value[highlightedIndex.value] : undefined - if (selected) { + if (selected && isFlagTrigger.value) { e.preventDefault() selectOption(selected.value) } diff --git a/ui/app/components/TextareaAutocomplete.vue b/ui/app/components/TextareaAutocomplete.vue index 15a9d02e..30034a86 100644 --- a/ui/app/components/TextareaAutocomplete.vue +++ b/ui/app/components/TextareaAutocomplete.vue @@ -1,9 +1,10 @@