CreaRack-SL

Búsqueda global del Workspace (Fuse.js + índice estático)

Sustituida el 11-09-2026 por [[feature—workspace—busqueda-texto-completo]]: la cajita ya no usa Fuse.js ni el índice estático de 1,7 MB (que solo llevaba los primeros 1.500 caracteres de cada página); busca en el cuerpo entero con FTS5 en D1. Esta página queda como registro de cómo funcionaba.

Descripción

La búsqueda global del Workspace permite a los usuarios encontrar páginas wiki, secciones estáticas y entradas del worklog directamente desde el header de la aplicación. Antes del commit f13bf8a (2026-04-27), el componente buscaba sobre datos mock estáticos (tasks, news, worklog hardcodeados) y devolvía resultados incorrectos o vacíos.

Tras la corrección, la búsqueda opera sobre un índice real de 311 entradas generado en build-time, cubriendo:

FuenteEntradasNotas
Wiki Supercontexto~285Páginas .md de src/content/wiki/
Páginas Astro estáticas25Dashboard, Tareas, Biblioteca, Worklog, etc.
Worklog~1 por entradasrc/content/worklog/*.md

Arquitectura

build-search-index.mjs
      │
      ▼
public/search-index.json   ←  311 entradas, ~484 KB
      │
      ▼  lazy fetch al primer focus
SearchInline.tsx
      │
      ├── Fuse.js (threshold 0.35, ignoreLocation: true)
      │     ├── title   (weight: 3)
      │     ├── content (weight: 2)
      │     └── section (weight: 1)
      │
      ├── Historial en localStorage (clave: ws-search-history, máx: 6)
      └── Keyboard nav: ↑ ↓ ⏎ Esc

Componente: SearchInline.tsx

Ubicación: src/components/shell/SearchInline.tsx
Tipo: React (forwardRef), montado en el shell del header global.

Estado

EstadoTipoDescripción
qstringQuery de búsqueda en curso
resultsSearchEntry[]Resultados devueltos por Fuse
historystring[]Búsquedas recientes de localStorage
selnumberÍndice del resultado seleccionado con teclado
openbooleanVisibilidad del dropdown

Singletons de módulo

let fuseInstance: Fuse<SearchEntry> | null = null;
let indexLoaded = false;

El índice se carga una sola vez (lazy, al primer onFocus) y se reutiliza en toda la sesión. No hay re-fetch entre búsquedas.

Flujo de interacción

  1. Usuario hace focus → loadIndex() fetcha /search-index.json si aún no está cargado.
  2. Usuario escribe → useEffect([q]) llama a fuseInstance.search(term, { limit: 12 }).
  3. Usuario selecciona con ↑↓/⏎ o click → navigate(href) guarda en historial y redirige.
  4. Sin query + historial disponible → dropdown muestra sección “Recientes” con últimas 6 búsquedas.
  5. Esc → cierra dropdown y elimina el focus del input.

Script: build-search-index.mjs

Ubicación: scripts/build-search-index.mjs
Ejecución: node scripts/build-search-index.mjs (manual o en CI pre-deploy)
Output: public/search-index.json

Estructura de cada entrada del índice

{
  "title": "Modelo Rack — entity page",
  "href": "/wiki/entity--racks--model--rack",
  "section": "Wiki · Supercontexto",
  "content": "<texto plano, primeros 1500 chars del body>"
}

Mapeo product → section

Valor product en frontmatterSección mostrada en UI
crearackWiki · CreaRack Help
crearack-techWiki · CreaRack Tech
workspaceWiki · Workspace
workspace-techWiki · Workspace Tech
ia-techWiki · IA Tech
supercontextoWiki · Supercontexto

Proceso de indexación

  1. Carga las 25 páginas estáticas de STATIC_PAGES (hardcodeadas en el script).
  2. Itera src/content/wiki/*.md — parsea frontmatter con regex propio, extrae title, slug, product.
  3. Itera src/content/worklog/*.md — agrupa todos bajo /worklog.
  4. Serializa el array completo a public/search-index.json (sin pretty-print para minimizar tamaño).
  5. Imprime resumen: [search-index] N entradas → OUTPUT (estáticas: X, wiki: Y, worklog: Z).

Función extractFrontmatter

Mejorada respecto a la versión anterior: parsea todas las claves YAML de primer nivel (no solo title y slug) usando una regex de línea genérica, respetando comillas simples y dobles. Permite extraer product, status, type, etc.

Función stripMarkdown

Limpia el cuerpo del markdown antes de indexar: elimina bloques de código, imágenes, links, headers, énfasis, listas, tablas y blockquotes. El texto resultante se trunca a 1500 chars (wiki) o 2000 chars (worklog).


Estilos: globals.css

Cambios en la versión f13bf8a:

/* Dropdown: dimensiones ajustadas */
.search-inline-dropdown {
  min-width: 360px;   /* antes: sin min-width */
  max-width: 520px;   /* nuevo */
  max-height: 480px;  /* antes: 420px */
}

/* Nuevo: cabecera de sección dentro del dropdown */
.search-inline-section { ... }

/* Nuevo: botón "Limpiar" historial */
.search-inline-clear { ... }

Limitaciones conocidas

  • El índice es estático (build-time). Páginas creadas o modificadas no aparecen en búsqueda hasta el próximo build + deploy.
  • El fichero search-index.json pesa ~484 KB — se carga completo en memoria del cliente en el primer focus. Aceptable para un tool interno de equipo pequeño; considerar particionado si crece >1 MB.
  • El worklog se indexa por entrada de fichero .md, pero todas comparten la misma href: /worklog (no deep-link a entrada individual).
  • Las páginas Astro estáticas están hardcodeadas en STATIC_PAGES — añadir nuevas rutas requiere actualizar el script manualmente.

Historial de versiones

VersiónFechaDescripción
v0 (pre-fix)< 2026-04-27Búsqueda sobre mock estático, 1 entrada en índice, colecciones inexistentes
v1 (f13bf8a)2026-04-27Fuse.js real, 311 entradas, historial, keyboard nav

Véase también

  • [[workspace—que-es-workspace]]
  • [[crearack—conceptos—interfaz-general]]