cap macro command palette list at 200 and scope highlight observer

This commit is contained in:
Abhinav Raut
2026-04-22 15:54:05 +05:30
parent 3ba8ab0a8d
commit 8ebd7d7e2b
2 changed files with 62 additions and 24 deletions
@@ -1,5 +1,11 @@
<template>
<CommandDialog :open="open" @update:open="toggleOpen" class="transform-gpu z-[51] !min-w-[50vw]">
<CommandDialog
:open="open"
v-model:search-term="searchTerm"
:filter-function="isMacroMode ? passThroughFilter : undefined"
@update:open="toggleOpen"
class="transform-gpu z-[51] !min-w-[50vw]"
>
<CommandInput :placeholder="t('command.typeCmdOrSearch')" @keydown="onInputKeydown" />
<CommandList
class="!min-h-[50vh] h-[50vh] !min-w-[50vw]"
@@ -41,19 +47,14 @@
</CommandGroup>
<!-- Macros -->
<div
v-if="
nestedCommand === 'apply-macro-to-existing-conversation' ||
nestedCommand === 'apply-macro-to-new-conversation'
"
>
<div v-if="isMacroMode">
<CommandGroup :heading="$t('actions.applyMacro')">
<div class="min-h-[400px]">
<div class="h-[60vh] grid grid-cols-12">
<!-- Left Column: Macro List (30%) -->
<div class="col-span-4 pr-2 border-r overflow-y-auto h-full">
<div ref="macroListRef" class="col-span-4 pr-2 border-r overflow-y-auto h-full">
<CommandItem
v-for="(macro, index) in macroStore.macroOptions"
v-for="(macro, index) in visibleMacros"
:key="macro.value"
:value="macro.label + '|' + index"
:data-index="index"
@@ -247,6 +248,8 @@ import { Label } from '@shared-ui/components/ui/label'
import { useI18n } from 'vue-i18n'
import { Letter } from 'vue-letter'
const RENDER_CAP = 200
const conversationStore = useConversationStore()
const macroStore = useMacroStore()
const { t } = useI18n()
@@ -257,6 +260,34 @@ const showDatePicker = ref(false)
const datePickerOpen = ref(false)
const selectedDate = ref(null)
const selectedTime = ref('12:00')
const searchTerm = ref('')
const macroListRef = ref(null)
const passThroughFilter = (items) => items
const isMacroMode = computed(
() =>
nestedCommand.value === 'apply-macro-to-existing-conversation' ||
nestedCommand.value === 'apply-macro-to-new-conversation'
)
const macroSearchIndex = computed(() =>
macroStore.macroOptions.map((m) => ({ macro: m, labelLower: String(m.label).toLowerCase() }))
)
const visibleMacros = computed(() => {
const term = searchTerm.value?.trim().toLowerCase()
const index = macroSearchIndex.value
if (!term) {
const all = macroStore.macroOptions
return all.length > RENDER_CAP ? all.slice(0, RENDER_CAP) : all
}
const matched = []
for (let i = 0; i < index.length && matched.length < RENDER_CAP; i++) {
if (index[i].labelLower.includes(term)) matched.push(index[i].macro)
}
return matched
})
const { Meta_K, Ctrl_K } = useMagicKeys({
passive: false,
@@ -370,24 +401,26 @@ const nestedCommandHandler = (data) => {
open.value = data.open
}
let highlightObserver = null
watch(macroListRef, (el) => {
highlightObserver?.disconnect()
highlightObserver = null
highlightedMacro.value = null
if (!el) return
highlightObserver = new MutationObserver(() => {
const idx = el.querySelector('[data-highlighted]')?.getAttribute('data-index')
highlightedMacro.value = idx != null ? visibleMacros.value[idx] : null
})
highlightObserver.observe(el, { attributes: true, attributeFilter: ['data-highlighted'], subtree: true })
})
onMounted(() => {
emitter.on(EMITTER_EVENTS.SET_NESTED_COMMAND, nestedCommandHandler)
watchHighlightedMacro()
})
onUnmounted(() => {
emitter.off(EMITTER_EVENTS.SET_NESTED_COMMAND, nestedCommandHandler)
highlightObserver?.disconnect()
})
const watchHighlightedMacro = () => {
const observer = new MutationObserver(() => {
const highlightedEl = document.querySelector('[data-highlighted]')?.getAttribute('data-index')
highlightedMacro.value = highlightedEl ? macroStore.macroOptions[highlightedEl] : null
})
observer.observe(document.body, {
attributes: true,
subtree: true
})
}
</script>
@@ -7,17 +7,22 @@ const props = defineProps({
open: { type: Boolean, required: false },
defaultOpen: { type: Boolean, required: false },
modal: { type: Boolean, required: false },
searchTerm: { type: String, required: false },
filterFunction: { type: Function, required: false },
class: { type: String, required: false }
})
const emits = defineEmits(['update:open'])
const emits = defineEmits(['update:open', 'update:searchTerm'])
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<Dialog v-bind="forwarded">
<DialogContent :class="['overflow-hidden p-0 shadow-lg', props.class]">
<DialogContent :class="['overflow-hidden p-0 shadow-lg', props.class]">
<Command
:search-term="props.searchTerm"
:filter-function="props.filterFunction"
@update:search-term="$emit('update:searchTerm', $event)"
class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-2 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"
>
<slot />