Help Widget — Atajos FAQ, búsqueda local y whitelist product (I4)
Help Widget — Atajos FAQ, búsqueda local y whitelist product (I4)
Iniciativa I4 del plan post-audit Supercontexto (sesión 27, tag supercontext/sesion-27-i4-done). Completa el refactor del Help Widget contextual de CreaRack Pro añadiendo tres capacidades: atajos rápidos (FAQ chips), búsqueda local de títulos cliente-side, y filtrado por producto mediante whitelist server-side.
Contexto y motivación
El Help Widget existente sólo ofrecía el modo “Ask” (IA) y el Browse por categorías. Problemas detectados en el audit Supercontexto:
- Latencia percibida: cada consulta invocaba al asistente aunque el usuario sólo quisiera abrir un artículo concreto.
- Coste innecesario: queries triviales (“¿qué es el Map Editor?”) consumían tokens de IA.
- Contaminación del corpus: el endpoint
/api/help/wikiservía también documentación interna (workspace-tech,crearack-tech) al usuario final. - Ruido en UI: se mostraba la latencia del asistente (
123ms) — dato técnico irrelevante para el usuario.
Cambios implementados
1. Atajos rápidos (FAQ chips)
Archivo: static/js/alpine-components.js — componente helpWidget
Se añade el array faqs[] con 7 accesos directos preseleccionados:
| Label | Slug |
|---|---|
| Primeros pasos | crearack/conceptos/primeros-pasos |
| Map Editor | crearack/blueprints/que-es-map-editor |
| Terminal SSH | crearack/terminal/que-es-terminal |
| Observatory | crearack/monitoring/que-es-observatory |
| Wireless | crearack/monitoring/que-es-wireless |
| UPS | crearack/monitoring/que-es-ups |
| Digital Signage | crearack/signage/que-es-signage |
Flujo openFaq(slug):
- Si
total === 0, carga el catálogo wiki (loadWiki()). - Busca el artículo por
slugdentro decategories. - Si encontrado y tiene
path→ abre el artículo inline (openArticle(path)). Latencia 0, coste 0. - Fallback: si el slug aún no está indexado en D1, cambia a tab “buscar” y lanza
this.ask()con el label del FAQ como pregunta.
Los chips sólo se muestran cuando no hay categoría activa, no hay artículo abierto y no hay texto en el buscador (showFaqs getter).
CSP-safe: el handler global openHelpFaq(el) en base.js despacha el evento help-open-faq al componente Alpine, evitando onclick inline prohibidos por la Content Security Policy.
// base.js
function openHelpFaq(el) {
var slug = el && el.dataset ? el.dataset.slug : '';
if (slug) window.dispatchEvent(new CustomEvent('help-open-faq', { detail: { slug: slug } }));
}
<!-- base.html — Alpine listener -->
@help-open-faq.window="openFaq($event.detail.slug)"
2. Búsqueda local de títulos (cliente-side)
Archivos: alpine-components.js, templates/base.html, help.css
- Estado
search: ''en el componente Alpine. - Getter
filteredCategoryList: filtracategoryListportitle.toLowerCase().indexOf(q). Agrupa los resultados por categoría. - Getter
hasSearchResults:filteredCategoryList.length > 0. - Método
clearSearch(): reseteasearcha''.
Comportamiento UI:
- Mientras
searchestá vacío → se muestra el grid de categorías normal. - Con texto en el buscador → se oculta el grid de categorías y se muestra
help-search-results(lista plana agrupada por categoría). - Si no hay coincidencias → mensaje “No articles match this search.”
Sin latencia ni coste: el filtrado opera sobre el catálogo ya cargado en memoria del navegador.
3. Whitelist por producto (server-side)
Archivo: core/api_help.py
Antes: filtrado por blacklist de categorías (_PRIVATE_CATEGORIES = {"workspace"}), eliminando categorías completas del response.
Después: whitelist por product:
_USER_FACING_PRODUCT = "crearack"
# En help_wiki():
resp = requests.get(
f"{WORKSPACE_URL}/biblioteca/wiki",
headers=_workspace_headers(),
params={"product": _USER_FACING_PRODUCT}, # ← nuevo
timeout=TIMEOUT,
)
El workspace filtra server-side: sólo devuelve pages con product == "crearack". Pages con product = workspace-tech, product = crearack-tech o sin product quedan excluidas del Help de usuario.
Companion changes en workspace (commit 96d5238):
- 3 pages nuevas:
crearack--conceptos--primeros-pasos,crearack--monitoring--que-es-wireless,crearack--monitoring--que-es-ups. - 2 pages editadas con sección “Como empezar”.
- Endpoint
functions/api/biblioteca/wiki.tsacepta?product=.
4. UI más limpia en respuestas de IA
// Antes
get answerMeta() { return ... + ' sources · ' + (this.answer.duration_ms || 0) + 'ms'; }
// Después
get answerMeta() { return ... + ' sources'; }
Se elimina la latencia (duration_ms) del texto visible al usuario. Sólo se muestra el número de fuentes consultadas.
Arquitectura de componentes
base.html (template)
└─ Alpine helpWidget (alpine-components.js)
├─ faqs[] → FAQ chips (openFaq)
├─ search → estado búsqueda local
├─ filteredCategoryList → getter computed
└─ openFaq(slug) → resolve path o fallback ask
base.js (global handlers)
└─ openHelpFaq(el) → dispara CustomEvent 'help-open-faq'
core/api_help.py
└─ GET /api/help/wiki → proxy con ?product=crearack
└─ workspace /biblioteca/wiki?product=crearack (CF Workers D1)
Trade-offs y deuda técnica
| Item | Estado |
|---|---|
Whitelist depende de product correctamente etiquetado en corpus | ⚠️ Deuda — parte de I5 |
Re-tagging masivo de pages sin product | Pendiente en I5 |
| Fallback FAQ → modo Ask si slug no indexado | Funcional pero subóptimo hasta que D1 tenga cobertura completa |
Clases CSS añadidas
| Clase | Uso |
|---|---|
.help-faqs | Contenedor de la sección FAQ chips |
.help-faqs-grid | Grid flex-wrap para los chips |
.help-faq | Chip individual (pill border-radius 999px) |
.help-search-wrap | Wrapper del input de búsqueda |
.help-search-input | Input text de búsqueda |
.help-search-clear | Botón × para limpiar búsqueda |
.help-search-results | Contenedor de resultados en búsqueda activa |
.help-search-group-label | Label de categoría en resultados (Oswald, uppercase) |
Véase también
- [[entity—core—endpoint—api-help-wiki]]