{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "mentions",
  "title": "Mentions",
  "type": "registry:ui",
  "description": "Textarea with trigger-character autocomplete and companion MentionTag with Twitter/X-style hover profile card popup.",
  "categories": [
    "form"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/mentions/mentions.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { styleMap } from 'lit/directives/style-map.js'\nimport { Calendar } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { computePosition } from '../../lib/position'\n\nexport interface MentionOption {\n  value: string\n  label: string\n  description?: string\n  avatar?: string\n  email?: string\n  handle?: string\n  bio?: string\n  joined?: string\n  following?: number | string\n  followers?: number | string\n  verified?: boolean\n  disabled?: boolean\n  [key: string]: unknown\n}\n\nconst PROPERTIES_TO_COPY = [\n  'direction',\n  'boxSizing',\n  'width',\n  'height',\n  'overflowX',\n  'overflowY',\n  'borderTopWidth',\n  'borderRightWidth',\n  'borderBottomWidth',\n  'borderLeftWidth',\n  'borderStyle',\n  'paddingTop',\n  'paddingRight',\n  'paddingBottom',\n  'paddingLeft',\n  'fontStyle',\n  'fontVariant',\n  'fontWeight',\n  'fontStretch',\n  'fontSize',\n  'fontSizeAdjust',\n  'lineHeight',\n  'fontFamily',\n  'textAlign',\n  'textTransform',\n  'textIndent',\n  'textDecoration',\n  'letterSpacing',\n  'wordSpacing',\n  'tabSize',\n  'whiteSpace',\n  'wordBreak',\n  'wordWrap',\n] as const\n\ninterface CaretRect {\n  top: number\n  left: number\n  height: number\n}\n\nfunction getCaretRect(textarea: HTMLTextAreaElement, position: number): CaretRect {\n  if (typeof document === 'undefined') return { top: 0, left: 0, height: 16 }\n  const div = document.createElement('div')\n  document.body.appendChild(div)\n\n  const style = div.style\n  const computed = window.getComputedStyle(textarea)\n\n  style.position = 'absolute'\n  style.visibility = 'hidden'\n  style.whiteSpace = 'pre-wrap'\n  style.wordWrap = 'break-word'\n  style.top = '0'\n  style.left = '0'\n\n  for (const prop of PROPERTIES_TO_COPY) {\n    ;(style as any)[prop] = (computed as any)[prop]\n  }\n\n  style.overflow = 'hidden'\n\n  const text = textarea.value.substring(0, position)\n  div.textContent = text\n\n  const span = document.createElement('span')\n  span.textContent = textarea.value.substring(position) || '.'\n  div.appendChild(span)\n\n  const spanRect = span.getBoundingClientRect()\n  const divRect = div.getBoundingClientRect()\n  const taRect = textarea.getBoundingClientRect()\n\n  const result: CaretRect = {\n    top: taRect.top + (spanRect.top - divRect.top) - textarea.scrollTop,\n    left: taRect.left + (spanRect.left - divRect.left) - textarea.scrollLeft,\n    height: spanRect.height,\n  }\n\n  document.body.removeChild(div)\n  return result\n}\n\nconst jsonConverter = {\n  fromAttribute: (v: string | null) => {\n    if (!v) return undefined\n    try {\n      return JSON.parse(v)\n    } catch {\n      return undefined\n    }\n  },\n  toAttribute: (v: unknown) => (v ? JSON.stringify(v) : null),\n}\n\nconst stringListConverter = {\n  fromAttribute: (v: string | null): string[] => {\n    if (!v) return ['@']\n    try {\n      const parsed = JSON.parse(v)\n      if (Array.isArray(parsed)) return parsed\n    } catch {}\n    return v.split(',').map((s) => s.trim()).filter(Boolean)\n  },\n  toAttribute: (v: string[] | undefined): string | null => (v ? JSON.stringify(v) : null),\n}\n\nconst trueUnlessFalse = {\n  fromAttribute: (v: string | null) => v !== 'false',\n}\n\nlet uid = 0\n\n/**\n * <uip-mentions> — the registry Mentions textarea as a web component.\n *\n * Form-associated: submits `name=value` with its <form>.\n */\nexport class UipMentions extends LitElement {\n  static formAssociated = true\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  static styles = [tailwind, css`:host { display: block; position: relative; width: 100%; }`]\n\n  static properties = {\n    value: { reflect: true },\n    defaultValue: { attribute: 'default-value' },\n    options: { converter: jsonConverter },\n    triggers: { converter: stringListConverter },\n    triggerPrefixes: { attribute: 'trigger-prefixes', converter: jsonConverter },\n    prefixText: { attribute: 'prefix' },\n    rows: { type: Number },\n    loading: { type: Boolean, reflect: true },\n    placeholder: {},\n    disabled: { type: Boolean, reflect: true },\n    readOnly: { type: Boolean, attribute: 'readonly', reflect: true },\n    name: { reflect: true },\n    accessibleLabel: { attribute: 'aria-label' },\n    open: { state: true },\n    activeTrigger: { state: true },\n    query: { state: true },\n    highlightedIndex: { state: true },\n    pos: { state: true },\n  }\n\n  value = ''\n  defaultValue?: string\n  options: MentionOption[] | Record<string, MentionOption[]> = []\n  triggers: string[] = ['@']\n  triggerPrefixes?: Record<string, string>\n  prefixText = '@'\n  rows = 4\n  loading = false\n  placeholder = ''\n  disabled = false\n  readOnly = false\n  name?: string\n  accessibleLabel?: string\n\n  loadOptions?: (query: string, trigger: string) => Promise<MentionOption[]>\n\n  private open = false\n  private activeTrigger = '@'\n  private query = ''\n  private triggerIndex = -1\n  private highlightedIndex = 0\n  private asyncResults: MentionOption[] = []\n  private isAsyncLoading = false\n  private pos: Record<string, string> = {}\n  private initialValue = ''\n  private listboxId = `mentions-list-${++uid}`\n  private internals = this.attachInternals()\n  private debounceTimer?: ReturnType<typeof setTimeout>\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'mentions')\n    if (this.defaultValue !== undefined && this.getAttribute('value') === null) {\n      this.value = this.defaultValue\n    }\n    this.initialValue = this.value\n    document.addEventListener('pointerdown', this.onDocumentPointerDown)\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    document.removeEventListener('pointerdown', this.onDocumentPointerDown)\n    if (this.debounceTimer) clearTimeout(this.debounceTimer)\n  }\n\n  private onDocumentPointerDown = (e: PointerEvent) => {\n    if (!this.open) return\n    const path = e.composedPath()\n    if (!path.includes(this)) {\n      this.open = false\n    }\n  }\n\n  protected willUpdate(changed: Map<string, unknown>) {\n    if (changed.has('value') || changed.has('name')) {\n      this.internals.setFormValue(this.value ?? '')\n    }\n  }\n\n  formResetCallback() {\n    this.value = this.initialValue\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n  }\n\n  formDisabledCallback(disabled: boolean) {\n    this.disabled = disabled\n  }\n\n  private get textarea(): HTMLTextAreaElement | null {\n    return this.renderRoot?.querySelector('textarea')\n  }\n\n  private get popoverEl(): HTMLElement | null {\n    return this.renderRoot?.querySelector('[data-slot=mentions-popover]')\n  }\n\n  private get currentOptionsList(): MentionOption[] {\n    if (!this.options) return []\n    if (Array.isArray(this.options)) return this.options\n    if (typeof this.options === 'object') {\n      return (this.options as Record<string, MentionOption[]>)[this.activeTrigger] ?? []\n    }\n    return []\n  }\n\n  private get filtered(): MentionOption[] {\n    if (this.loadOptions) return this.asyncResults\n    const source = this.currentOptionsList\n    if (!this.query) return source\n    const q = this.query.toLowerCase()\n    return source.filter(\n      (o) =>\n        o.label.toLowerCase().includes(q) ||\n        o.value.toLowerCase().includes(q) ||\n        (o.email && o.email.toLowerCase().includes(q)),\n    )\n  }\n\n  private findActiveMention(val: string, caret: number): { trigger: string; index: number; query: string } | null {\n    const triggers = this.triggers ?? ['@']\n    for (let i = caret - 1; i >= 0; i--) {\n      const ch = val[i]!\n      if (triggers.includes(ch)) {\n        const before = i === 0 ? '' : val[i - 1]!\n        if (i === 0 || /\\s/.test(before)) {\n          return { trigger: ch, index: i, query: val.substring(i + 1, caret) }\n        }\n        return null\n      }\n      if (/\\s/.test(ch)) return null\n    }\n    return null\n  }\n\n  private updatePosition() {\n    const ta = this.textarea\n    if (!ta) return\n    const rect = getCaretRect(ta, ta.selectionStart ?? 0)\n    this.pos = {\n      position: 'fixed',\n      top: `${rect.top + rect.height + 4}px`,\n      left: `${rect.left}px`,\n      zIndex: '50',\n    }\n  }\n\n  private scheduleAsync(trigger: string, q: string) {\n    if (this.debounceTimer) clearTimeout(this.debounceTimer)\n    this.debounceTimer = setTimeout(async () => {\n      if (!this.loadOptions) return\n      this.isAsyncLoading = true\n      try {\n        const res = await this.loadOptions(q, trigger)\n        this.asyncResults = res\n      } finally {\n        this.isAsyncLoading = false\n      }\n    }, 200)\n  }\n\n  private onInput(e: Event) {\n    const target = e.target as HTMLTextAreaElement\n    const next = target.value\n    this.value = next\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new CustomEvent('value-change', { detail: { value: next }, bubbles: true, composed: true }))\n\n    const match = this.findActiveMention(next, target.selectionStart ?? 0)\n    if (match) {\n      this.open = true\n      this.activeTrigger = match.trigger\n      this.triggerIndex = match.index\n      this.query = match.query\n      this.highlightedIndex = 0\n      this.dispatchEvent(\n        new CustomEvent('search', {\n          detail: { trigger: match.trigger, query: match.query },\n          bubbles: true,\n          composed: true,\n        }),\n      )\n      if (this.loadOptions) this.scheduleAsync(match.trigger, match.query)\n      requestAnimationFrame(() => this.updatePosition())\n    } else {\n      this.open = false\n    }\n  }\n\n  private insert(option: MentionOption) {\n    const ta = this.textarea\n    if (!ta) return\n    const caret = ta.selectionStart ?? 0\n    const before = this.value.substring(0, this.triggerIndex)\n    const after = this.value.substring(caret)\n    const prefix = this.triggerPrefixes?.[this.activeTrigger] ?? this.activeTrigger ?? this.prefixText ?? '@'\n    const token = `${prefix}${option.value} `\n    const next = before + token + after\n    this.value = next\n    this.open = false\n\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n    this.dispatchEvent(new CustomEvent('value-change', { detail: { value: next }, bubbles: true, composed: true }))\n    this.dispatchEvent(new CustomEvent('select', { detail: { option }, bubbles: true, composed: true }))\n\n    requestAnimationFrame(() => {\n      const pos = before.length + token.length\n      ta.focus()\n      ta.setSelectionRange(pos, pos)\n    })\n  }\n\n  private onKeyDown(e: KeyboardEvent) {\n    if (!this.open) return\n    if (e.key === 'Escape') {\n      e.preventDefault()\n      this.open = false\n      return\n    }\n    const filtered = this.filtered\n    if (filtered.length === 0) return\n\n    if (e.key === 'ArrowDown') {\n      e.preventDefault()\n      this.highlightedIndex = (this.highlightedIndex + 1) % filtered.length\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault()\n      this.highlightedIndex = (this.highlightedIndex - 1 + filtered.length) % filtered.length\n    } else if (e.key === 'Enter' || e.key === 'Tab') {\n      e.preventDefault()\n      const opt = filtered[this.highlightedIndex]\n      if (opt && !opt.disabled) this.insert(opt)\n    }\n  }\n\n  render() {\n    const filtered = this.filtered\n    const isLoading = this.loading || this.isAsyncLoading\n\n    return html`\n      <div part=\"base\" class=\"relative w-full\" data-uipkge=\"\" data-slot=\"mentions\">\n        <textarea\n          part=\"textarea\"\n          .value=${this.value}\n          rows=${this.rows}\n          placeholder=${this.placeholder}\n          ?disabled=${this.disabled}\n          ?readonly=${this.readOnly}\n          name=${this.name ?? nothing}\n          role=\"combobox\"\n          aria-autocomplete=\"list\"\n          aria-haspopup=\"listbox\"\n          aria-expanded=${this.open ? 'true' : 'false'}\n          aria-controls=${this.listboxId}\n          aria-label=${this.accessibleLabel ?? nothing}\n          class=\"border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-16 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50\"\n          @input=${this.onInput}\n          @keydown=${this.onKeyDown}\n          @scroll=${() => this.updatePosition()}\n        ></textarea>\n\n        ${this.open\n          ? html`\n              <div\n                part=\"popover\"\n                data-slot=\"mentions-popover\"\n                style=${styleMap(this.pos)}\n                class=\"border-border/80 bg-popover text-popover-foreground w-64 rounded-lg p-1 shadow-md border fixed\"\n              >\n                <div id=${this.listboxId}>\n                  ${isLoading\n                    ? html`<div class=\"text-muted-foreground px-2 py-3 text-sm\" role=\"status\">Loading...</div>`\n                    : filtered.length === 0\n                      ? html`<div class=\"text-muted-foreground px-2 py-3 text-sm\" role=\"status\">No matches</div>`\n                      : html`\n                          <ul class=\"max-h-64 overflow-auto\" role=\"listbox\" aria-label=\"Mentions\">\n                            ${filtered.map((opt, i) => {\n                              const active = i === this.highlightedIndex\n                              return html`\n                                <li\n                                  role=\"option\"\n                                  aria-selected=${active ? 'true' : 'false'}\n                                  aria-disabled=${opt.disabled ? 'true' : nothing}\n                                  class=${cn(\n                                    'flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors',\n                                    active && !opt.disabled ? 'bg-accent text-accent-foreground' : '',\n                                    opt.disabled ? 'cursor-not-allowed opacity-50' : '',\n                                  )}\n                                  @mouseenter=${() => !opt.disabled && (this.highlightedIndex = i)}\n                                  @mousedown=${(e: MouseEvent) => {\n                                    e.preventDefault()\n                                    if (!opt.disabled) this.insert(opt)\n                                  }}\n                                >\n                                  ${opt.avatar\n                                    ? html`<img src=${opt.avatar} alt=\"\" class=\"size-6 rounded-full object-cover\" />`\n                                    : nothing}\n                                  <div class=\"min-w-0 flex-1\">\n                                    <div class=\"truncate font-medium\">${opt.label}</div>\n                                    ${opt.description || opt.email\n                                      ? html`<div class=\"text-muted-foreground truncate text-xs\">\n                                          ${opt.description || opt.email}\n                                        </div>`\n                                      : nothing}\n                                  </div>\n                                </li>\n                              `\n                            })}\n                          </ul>\n                        `}\n                </div>\n              </div>\n            `\n          : nothing}\n      </div>\n    `\n  }\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                UipMentionTag                               */\n/* -------------------------------------------------------------------------- */\n\nexport class UipMentionTag extends LitElement {\n  static styles = [tailwind, css`:host { display: inline-block; vertical-align: baseline; }`]\n\n  static properties = {\n    trigger: {},\n    name: {},\n    handle: {},\n    email: {},\n    avatar: {},\n    bio: {},\n    joined: {},\n    location: {},\n    following: {},\n    followers: {},\n    verified: { type: Boolean, reflect: true },\n    href: {},\n    popoverEnabled: { converter: trueUnlessFalse, attribute: 'popover' },\n    isHovered: { state: true },\n    isFollowing: { state: true },\n    pos: { state: true },\n  }\n\n  trigger = '@'\n  name?: string\n  handle?: string\n  email?: string\n  avatar?: string\n  bio?: string\n  joined?: string\n  location?: string\n  following?: number | string\n  followers?: number | string\n  verified = false\n  href?: string\n  popoverEnabled = true\n\n  private isHovered = false\n  private isFollowing = false\n  private pos: Record<string, string> = {}\n  private hoverTimer?: ReturnType<typeof setTimeout>\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'mention-tag')\n    this.addEventListener('mouseenter', this.onMouseEnter)\n    this.addEventListener('mouseleave', this.onMouseLeave)\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.removeEventListener('mouseenter', this.onMouseEnter)\n    this.removeEventListener('mouseleave', this.onMouseLeave)\n    if (this.hoverTimer) clearTimeout(this.hoverTimer)\n  }\n\n  private onMouseEnter = () => {\n    if (!this.popoverEnabled) return\n    if (this.hoverTimer) clearTimeout(this.hoverTimer)\n    this.hoverTimer = setTimeout(() => {\n      this.isHovered = true\n      this.updateComplete.then(() => {\n        const trigger = this.renderRoot?.querySelector('[data-slot=mention-tag-anchor]')\n        const popover = this.renderRoot?.querySelector('[data-slot=mention-card]')\n        if (trigger && popover) {\n          const { style } = computePosition(trigger, popover as HTMLElement, { side: 'bottom', align: 'start', sideOffset: 6 })\n          this.pos = style\n        }\n      })\n    }, 150)\n  }\n\n  private onMouseLeave = () => {\n    if (this.hoverTimer) clearTimeout(this.hoverTimer)\n    this.hoverTimer = setTimeout(() => {\n      this.isHovered = false\n    }, 100)\n  }\n\n  render() {\n    const formattedHandle = this.handle || this.name || ''\n    const fullHandle = formattedHandle.startsWith(this.trigger) ? formattedHandle : `${this.trigger}${formattedHandle}`\n    const initials = (this.name || this.handle || 'U').slice(0, 2).toUpperCase()\n\n    const triggerContent = html`\n      <span\n        data-slot=\"mention-tag-anchor\"\n        class=\"inline-flex cursor-pointer items-center gap-0.5 rounded px-1.5 py-0.5 text-sm font-medium transition-colors select-none bg-muted/70 text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none\"\n      >\n        <span class=\"text-primary font-semibold\">${this.trigger}</span>\n        <span>${this.name || this.handle || this.email}</span>\n      </span>\n    `\n\n    return html`\n      <span class=\"relative inline-block\">\n        ${this.href\n          ? html`<a href=${this.href} class=\"no-underline text-inherit\">${triggerContent}</a>`\n          : triggerContent}\n\n        ${this.popoverEnabled && this.isHovered\n          ? html`\n              <div\n                data-slot=\"mention-card\"\n                style=${styleMap(this.pos)}\n                class=\"border-border/80 bg-popover text-popover-foreground fixed inset-auto m-0 z-50 w-80 rounded-xl p-4 shadow-lg border\"\n                @mouseenter=${() => {\n                  if (this.hoverTimer) clearTimeout(this.hoverTimer)\n                }}\n                @mouseleave=${this.onMouseLeave}\n              >\n                <div class=\"space-y-3\">\n                  <div class=\"flex items-start justify-between gap-3\">\n                    <div class=\"ring-border/50 size-12 ring-2 relative rounded-full overflow-hidden flex items-center justify-center bg-muted text-foreground text-sm font-semibold\">\n                      ${this.avatar\n                        ? html`<img src=${this.avatar} alt=${this.name || this.handle || ''} class=\"size-full object-cover\" />`\n                        : initials}\n                    </div>\n                    <button\n                      type=\"button\"\n                      class=${cn(\n                        'h-8 rounded-full px-3.5 text-xs font-semibold transition-[transform,background-color] duration-150 active:scale-95',\n                        this.isFollowing\n                          ? 'border-border text-foreground hover:bg-muted border bg-transparent'\n                          : 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs',\n                      )}\n                      @click=${(e: Event) => {\n                        e.stopPropagation()\n                        this.isFollowing = !this.isFollowing\n                      }}\n                    >\n                      ${this.isFollowing ? 'Following' : 'Follow'}\n                    </button>\n                  </div>\n\n                  <div>\n                    <div class=\"flex items-center gap-1\">\n                      <span class=\"text-foreground text-sm font-bold tracking-tight\">${this.name || this.handle}</span>\n                      ${this.verified\n                        ? html`\n                            <span class=\"text-primary inline-flex\" title=\"Verified\">\n                              <svg class=\"size-3.5 fill-current\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n                                <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z\" />\n                              </svg>\n                            </span>\n                          `\n                        : nothing}\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">${fullHandle}</p>\n                    ${this.email ? html`<p class=\"text-muted-foreground mt-0.5 text-xs\">${this.email}</p>` : nothing}\n                  </div>\n\n                  ${this.bio ? html`<p class=\"text-foreground/90 text-xs leading-relaxed\">${this.bio}</p>` : nothing}\n\n                  ${this.joined || this.location\n                    ? html`\n                        <div class=\"text-muted-foreground flex items-center gap-4 text-xs\">\n                          ${this.joined\n                            ? html`\n                                <div class=\"flex items-center gap-1\">\n                                  ${icon(Calendar, 'calendar', 'size-3.5 opacity-70')}\n                                  <span>Joined ${this.joined}</span>\n                                </div>\n                              `\n                            : nothing}\n                        </div>\n                      `\n                    : nothing}\n\n                  ${this.following !== undefined || this.followers !== undefined\n                    ? html`\n                        <div class=\"border-border/50 flex items-center gap-4 border-t pt-1 text-xs\">\n                          ${this.following !== undefined\n                            ? html`\n                                <div>\n                                  <span class=\"text-foreground font-bold\">${this.following}</span>\n                                  <span class=\"text-muted-foreground ml-1\">Following</span>\n                                </div>\n                              `\n                            : nothing}\n                          ${this.followers !== undefined\n                            ? html`\n                                <div>\n                                  <span class=\"text-foreground font-bold\">${this.followers}</span>\n                                  <span class=\"text-muted-foreground ml-1\">Followers</span>\n                                </div>\n                              `\n                            : nothing}\n                        </div>\n                      `\n                    : nothing}\n                </div>\n              </div>\n            `\n          : nothing}\n      </span>\n    `\n  }\n}\n\ncustomElements.get('uip-mentions') || customElements.define('uip-mentions', UipMentions)\ncustomElements.get('uip-mention-tag') || customElements.define('uip-mention-tag', UipMentionTag)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-mentions': UipMentions\n    'uip-mention-tag': UipMentionTag\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/mentions/mentions.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
