{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "highlight",
  "title": "Highlight",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/highlight/Highlight.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onMounted, watch, type HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  /** Text to search within. */\n  text: string\n  /** Query string or RegExp to highlight. */\n  query: string | RegExp\n  /** HTML tag used to wrap matched substrings. Default 'mark'. */\n  highlightTag?: 'mark' | 'span'\n  /** Class applied to each highlight wrapper. */\n  highlightClass?: HTMLAttributes['class']\n  /** Inline style applied to each highlight wrapper. */\n  highlightStyle?: HTMLAttributes['style']\n  /** Case-sensitive matching. Default false. */\n  caseSensitive?: boolean\n  /** Match whole words only. Default false. */\n  wholeWord?: boolean\n  /** Cap the number of highlights rendered. 0 = unlimited. Default 0. */\n  maxHighlights?: number\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  highlightTag: 'mark',\n  caseSensitive: false,\n  wholeWord: false,\n  maxHighlights: 0,\n})\n\nconst emit = defineEmits<{\n  /** Emitted when the query changes. Provides the total match count (before maxHighlights cap). */\n  matchCount: [count: number]\n}>()\n\ninterface Segment {\n  text: string\n  match: boolean\n}\n\nconst segments = computed<Segment[]>(() => {\n  const text = props.text\n  const query = props.query\n  if (!text) return []\n  if (!query) return [{ text, match: false }]\n\n  let pattern: RegExp\n  if (query instanceof RegExp) {\n    const flags = query.flags.includes('g') ? query.flags : query.flags + 'g'\n    pattern = new RegExp(query.source, flags)\n  } else {\n    if (!query) return [{ text, match: false }]\n    const escaped = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n    const body = props.wholeWord ? `\\\\b${escaped}\\\\b` : escaped\n    const flags = props.caseSensitive ? 'g' : 'gi'\n    pattern = new RegExp(body, flags)\n  }\n\n  const out: Segment[] = []\n  let last = 0\n  let count = 0\n  let m: RegExpExecArray | null\n  while ((m = pattern.exec(text)) !== null) {\n    if (m.index > last) out.push({ text: text.slice(last, m.index), match: false })\n    out.push({ text: m[0], match: true })\n    last = m.index + m[0].length\n    count++\n    if (props.maxHighlights > 0 && count >= props.maxHighlights) break\n    if (m[0] === '') pattern.lastIndex++\n  }\n  if (last < text.length) out.push({ text: text.slice(last), match: false })\n\n  return out\n})\n\n// Total match count (uncapped) — emitted via watch to avoid side-effects in computed\nconst totalMatchCount = computed(() => {\n  const text = props.text\n  const query = props.query\n  if (!text || !query) return 0\n\n  let pattern: RegExp\n  if (query instanceof RegExp) {\n    const flags = query.flags.includes('g') ? query.flags : query.flags + 'g'\n    pattern = new RegExp(query.source, flags)\n  } else {\n    const escaped = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n    const body = props.wholeWord ? `\\\\b${escaped}\\\\b` : escaped\n    const flags = props.caseSensitive ? 'g' : 'gi'\n    pattern = new RegExp(body, flags)\n  }\n\n  let total = 0\n  let m: RegExpExecArray | null\n  while ((m = pattern.exec(text)) !== null) {\n    total++\n    if (m[0] === '') pattern.lastIndex++\n  }\n  return total\n})\n\nwatch(totalMatchCount, (n) => emit('matchCount', n))\nonMounted(() => emit('matchCount', totalMatchCount.value))\n</script>\n\n<template>\n  <span data-uipkge data-slot=\"highlight\" :class=\"cn(props.class)\">\n    <template v-for=\"(seg, i) in segments\" :key=\"i\">\n      <component\n        :is=\"highlightTag\"\n        v-if=\"seg.match\"\n        data-slot=\"highlight-match\"\n        :class=\"cn('bg-yellow-200 text-yellow-950 dark:bg-yellow-500/30 dark:text-yellow-100 rounded px-0.5 font-medium', highlightClass)\"\n        :style=\"highlightStyle\"\n        >{{ seg.text }}</component\n      >\n      <template v-else>{{ seg.text }}</template>\n    </template>\n  </span>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/highlight/Highlight.vue"
    },
    {
      "path": "packages/registry-vue/components/highlight/index.ts",
      "content": "export { default as Highlight } from './Highlight.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/highlight/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Highlights matching substrings in text for search results. Supports string or regex queries, case-sensitive and whole-word matching, custom highlight tag, and a max-highlight cap.",
  "categories": [
    "display",
    "utility"
  ]
}