CH 22 · Plugins de UI: Cambiar el Aspecto, Añadir Paneles, Inyectar Contenido en dsh
Objetivo del capítulo
Los plugins de los capítulos anteriores trabajaban en sitios "que no puedes ver": registran herramientas, interceptan llamadas, proveen dependencias. Este capítulo cambia a la capa visible — los plugins pueden cambiar directamente el aspecto y las capacidades de la Web UI. Este capítulo aclara qué pueden hacer los plugins de UI, cómo funcionan, y práctica para ver un flujo de eventos real.
Ya estás usando plugins de UI
Mirando atrás a CH 17, instalamos tres plugins, ¡y todos eran plugins de UI!
| Plugin | Qué hace |
|---|---|
| dsh-theme | Cambia el aspecto de la Web UI (aparece una tarjeta de tema en Settings → Appearance) |
| dsh-oil-sticky-prompt | Fija el último mensaje del usuario en la parte superior al desplazarse |
| dsh-better-sidebar | Banco de trabajo de la barra lateral, ve el directorio directamente desde el panel de archivos |
Puede que simplemente "hayas instalado un plugin" en aquel momento, pero ahora entiendes: todos ellos modifican la UI. Ese es el primer nivel de entendimiento de los plugins de UI — son responsables de toda la personalización que "puedes ver".
Qué pueden hacer los plugins de UI
Aproximadamente tres categorías de capacidades:
- Cambiar el aspecto: cambiar colores, fuentes, estilo de layout. La categoría más ligera, dsh-theme es uno.
- Modificar layout / añadir paneles / añadir características: mover la barra lateral, la barra superior, el área de entrada, y también añadir directamente un módulo de características completo o un panel de visualización a la interfaz. dsh-better-sidebar, dsh-oil-sticky-prompt están en esta categoría; el DSH Skill & MCP Panel que instalaste en CH 12 también lo es — añade directamente una entrada "MCP Management" a Settings, con el estado de cada servidor y el conteo de herramientas visible de un vistazo, equivalente a añadir un panel de visualización de O&M a la Web UI.
- Contribuir contenido a la conversación: renderizar filas personalizadas en la sesión (p. ej. dibujar una tarjeta para cierto tipo de resultado de herramienta, insertar nodos especiales). Esta es la categoría más profunda, llamada oficialmente "contribute business rows to the Web Client".
Las dos primeras categorías son principalmente trabajo puramente de front-end; la tercera realmente necesita "engancharse" al pipeline de renderizado de la conversación.
Cómo funcionan los plugins de UI: todo empieza con el flujo de eventos
Los plugins de UI no manipulan directamente el DOM en la página, se suscriben al flujo de eventos. Este es el punto más crítico para entender los plugins de UI.
A lo largo de una sesión, cada evento se convierte en un evento estándar: el usuario envió un mensaje, el modelo empezó a emitir en streaming, un step empezó, una llamada a herramienta ocurrió... estos eventos fluyen por el canal session/event como agua. La interfaz integrada renderiza burbujas de conversación, trayectoria y estado a partir de ellos; los plugins de UI hacen lo mismo, suscribiéndose al mismo flujo vía ctx.on('session/event', ...).
El ejemplo mínimo oficial de un plugin de UI hace exactamente esto:
export const name = 'my-ui'
export const inject = ['agents']
export function apply(ctx) {
ctx.on('session/event', (_session, event) => {
if (event.type === 'assistant/chunk' && event.data.chunk.type === 'text-delta') {
console.log(event.data.chunk.text) // Cada pieza del output en streaming del modelo
}
})
}El esqueleto de todo el mecanismo:
Tú introduces, el modelo responde, el controlador de sesión los traduce todos en un flujo de eventos; los plugins de UI y la interfaz integrada se suscriben al mismo flujo, cada uno toma lo que necesita. La interfaz es un "consumer" del flujo de eventos, no un manipulador directo del DOM — esto también explica por qué "todo es un plugin" puede cubrir la UI: incluso la interfaz es "escuchada" por plugins desde el flujo de eventos.
Práctica: instala un "Observador de Eventos" para ver el flujo de eventos
Solo mirar el mecanismo no es lo suficientemente divertido, escribamos un plugin minimalista que imprima cada evento de sesión — veamos cuánto pasa realmente tras una sola conversación.
Paso 1: crea directorio, escribe plugin
En un workspace donde quieras poner plugins:
New-Item -ItemType Directory -Path "ui-demo\src" -ForceCrea ui-demo\src\event-watch.js:
export const name = 'event-watch'
export function apply(ctx) {
ctx.on('session/event', (_session, event) => {
console.log(`[event] ${event.type}${event.data?.type ? ' / ' + event.data.type : ''}`)
})
}Solo suscríbete a session/event e imprime el tipo de cada evento.
Paso 2: declara el plugin
Crea ui-demo\cordis.yml (sustituye la ruta por la tuya, recuerda %20 para los espacios):
- insert:
- id: event-watch
name: 'file:///E:/tu-workspace/ui-demo/src/event-watch.js'Paso 3: ejecuta headless una vez
cd tu-directorio-de-workspace
dsh --profile headless --patch "./ui-demo/cordis.yml" "Just reply: hi"Mi salida real de terminal:

Solo respondiendo "hi", entre bambalinas pasaron 22 eventos: inicialización de la sesión (permission, sandbox, approval), turn/start, step/start, tres user/messages, cabeceras de la request, assistant/chunk emitiendo texto uno a uno en streaming, assistant/message asentando, step/end, turn/end...
La conversación que ves en la Web UI es esta cadena de eventos siendo renderizada tras el consumo. El observador de eventos nos dejó "ver" la materia prima tras la UI por primera vez.
Deja que dsh lo haga: desarrolla un plugin de UI visible
El observador de eventos de arriba solo "observa eventos" y aún no ha cambiado realmente la interfaz. Pídele a dsh que desarrolle directamente un plugin de UI donde el efecto sea visible en la página de ajustes, puede hacerlo.
Envía este prompt directamente en el cuadro de entrada de la Web UI:
In my current workspace, help me develop a simple UI plugin visible in the Web UI: add a custom tab to the settings page (e.g. called "My Plugins", with some placeholder content on the page, no complex functionality needed). First read the dsh official docs to understand the UI plugin mechanism and how to add tabs to the settings page, implement per the official spec, tell me which profile to install to, how to see it in the interface (and tell me if you need to restart dsh or change config).Irá a leer los docs por sí mismo, juzgará el stack tecnológico (tales plugins de interfaz usualmente necesitan TypeScript y build de front-end, lo manejará él mismo), implementará, y te dirá cómo verificar. Sigue sus instrucciones para reiniciar dsh, y verás la nueva pestaña en la página de ajustes.
Esto es yo ejecutándolo realmente una vez — fue desde leer los docs, escribir el plugin, instalar en el profile, hasta decirme cómo verlo en la interfaz, cómo salir, todo hecho por sí mismo, incluso destacando las rutas clave:

Tras reiniciar, ve a Settings → Plugins, y arriba hay una nueva pestaña "My Plugins":

Un detalle: este plugin es puramente UI, sin comportamiento del lado Host, así que sin cambios en settings.yaml / cordis.yml — todo se carga a través de los bundles del profile. Revertir también es simple: solo elimínalo del array de bundles. Esta es otra encarnación de "todo es un plugin": incluso añadir una pestaña en la interfaz es "instalar un plugin".
Errores comunes
| Problema | Qué está pasando | Cómo manejarlo |
|---|---|---|
| El observador de eventos no imprime | session/event suscrito incorrectamente, o plugin no cargado | Confirma la ruta de cordis.yml, la sintaxis de ctx.on |
| Tantos eventos que no puedes leerlos | Una conversación naturalmente tiene docenas de eventos | Primero mira entre turn/start y turn/end, ese es el tronco principal |
| Quieres dibujar contenido directamente en la conversación | Eso requiere un renderer de React, avanzado | Primero familiarízate con el flujo de eventos y los ajustes, avanzado más tarde |
Reporta Cannot find package | El plugin usa dependencias externas que no están instaladas | npm install el paquete correspondiente en el directorio del plugin |
Lo que aprendiste en este capítulo
- [ ] Enuncias las tres categorías de capacidades de los plugins de UI: cambio de aspecto / layout y panel / contribución de contenido a la sesión
- [ ] Entiendes el mecanismo central de que "la interfaz es un consumer del flujo de eventos"
- [ ] Usas
ctx.on('session/event', ...)para suscribirte a eventos de sesión - [ ] Usas el observador de eventos para ver el flujo de eventos tras una sola conversación
- [ ] Sabes que los plugins de UI pueden añadir pestañas personalizadas a la página de ajustes
