Newer
Older
navi-1 / webclient / src / components / ui / registry.js
/**
 * Auto-discovered registry of UI components rendered by the navi_ui MCP server.
 *
 * Add a new Vue SFC named in PascalCase matching the snake_case component name:
 *   card_grid  -> CardGrid.vue
 *   code_block -> CodeBlock.vue
 *
 * The registry exports:
 *   - resolve(name): look up a component constructor by snake_case name
 *   - isBare(name): true when the component draws its own card frame
 *   - list(): array of registered names
 */

import { defineAsyncComponent, markRaw } from 'vue'

const modules = import.meta.glob('./*.vue', { eager: true })

function pascalCaseFromSnake(name) {
  return name
    .split('_')
    .map(part => part.charAt(0).toUpperCase() + part.slice(1))
    .join('')
}

const registry = new Map()

// Components that render their own frame (a GnCard, a table, …) opt out of the
// generic ui-component wrapper by exporting `bare` from a plain <script> block;
// without it they would sit inside a second border with a developer-facing
// header.
const bare = new Set()

for (const path in modules) {
  const fileName = path.replace(/^.*\//, '').replace(/\.vue$/, '')
  const module = modules[path]
  const component = module.default
  if (!component) continue

  // Register under PascalCase file name (e.g. CardGrid)
  registry.set(fileName, markRaw(component))

  // Also register under snake_case alias (e.g. card_grid)
  const snakeAlias = fileName
    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    .toLowerCase()
  registry.set(snakeAlias, markRaw(component))

  if (module.bare === true) {
    bare.add(fileName)
    bare.add(snakeAlias)
  }
}

export function resolve(name) {
  if (!name || typeof name !== 'string') return null
  if (registry.has(name)) return registry.get(name)

  const pascal = pascalCaseFromSnake(name)
  return registry.get(pascal) || null
}

export function isBare(name) {
  if (!name || typeof name !== 'string') return false
  return bare.has(name) || bare.has(pascalCaseFromSnake(name))
}

export function list() {
  return Array.from(registry.keys())
}