2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.3> Use this file to discover all available pages before exploring further.
4 4
5# Dibujar en la interfaz con un mod5# Dibuja en la interfaz con un mod
6 6
7> Dibuje paneles, una banda sobre el símbolo del sistema, botones y campos de texto desde un mod de Claude Code, maneje pulsaciones e entrada, y mantenga el estado entre redibujos y sesiones.7> Dibuja paneles, una franja sobre el prompt, botones y campos de texto desde un mod de Claude Code, gestiona pulsaciones y entradas, y conserva el estado entre redibujados y sesiones.
8 8
9Un mod puede dibujar su propia interfaz en Claude Code y cambiar partes de la interfaz que Claude Code ya dibuja. Cada lugar donde un mod puede dibujar se llama [sitio de renderizado](/docs/es/plugins/mods/reference#render-sites), como un panel, la banda sobre el símbolo del sistema o el spinner. Claude Code genera el evento [`ui.render`](/docs/es/plugins/mods/reference#interface) cada vez que está a punto de dibujar un sitio de renderizado, y su hook para ese evento devuelve lo que se debe dibujar allí.9Un mod puede dibujar su propia interfaz en Claude Code y cambiar partes de la interfaz que Claude Code ya dibuja. Cada lugar donde un mod puede dibujar se llama [punto de renderizado](/docs/es/plugins/mods/reference#render-sites), como un panel, la franja sobre el prompt o el spinner. Claude Code dispara el evento [`ui.render`](/docs/es/plugins/mods/reference#interface) cada vez que está a punto de dibujar un punto de renderizado, y tu hook para ese evento devuelve lo que se debe dibujar allí.
10 10
11Este mapa muestra dónde un mod puede dibujar en una sesión de terminal:11Este mapa muestra dónde puede dibujar un mod en una sesión de terminal:
12 12
13<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5fda26b6609c62b68c6f9e528c1590ea" className="dark:hidden" alt="Mapa de una sesión de terminal de Claude Code. Un mod puede agregar un panel como barra lateral a la derecha, una notificación en la esquina superior derecha de la transcripción, una línea de registro en la transcripción, una banda sobre el símbolo del sistema y una línea de estado bajo el símbolo del sistema. Un mod puede redibujar mensajes, filas de llamadas de herramientas y el spinner. El símbolo del sistema es propio de Claude Code." width="600" height="336" data-path="images/mods-screen-map.svg" />13<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5fda26b6609c62b68c6f9e528c1590ea" className="dark:hidden" alt="Mapa de una sesión de terminal de Claude Code. Un mod puede agregar un panel como barra lateral a la derecha, una notificación emergente en la parte superior derecha de la transcripción, una línea de registro en la transcripción, una franja sobre el prompt y una línea de estado debajo del prompt. Un mod puede redibujar mensajes, filas de llamadas a herramientas y el spinner. El prompt es propio de Claude Code." width="600" height="336" data-path="images/mods-screen-map.svg" />
14 14
15<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map-dark.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5b4161581a1bd2c0450b0c8b57bc1225" className="hidden dark:block" alt="Mapa de una sesión de terminal de Claude Code. Un mod puede agregar un panel como barra lateral a la derecha, una notificación en la esquina superior derecha de la transcripción, una línea de registro en la transcripción, una banda sobre el símbolo del sistema y una línea de estado bajo el símbolo del sistema. Un mod puede redibujar mensajes, filas de llamadas de herramientas y el spinner. El símbolo del sistema es propio de Claude Code." width="600" height="336" data-path="images/mods-screen-map-dark.svg" />15<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map-dark.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5b4161581a1bd2c0450b0c8b57bc1225" className="hidden dark:block" alt="Mapa de una sesión de terminal de Claude Code. Un mod puede agregar un panel como barra lateral a la derecha, una notificación emergente en la parte superior derecha de la transcripción, una línea de registro en la transcripción, una franja sobre el prompt y una línea de estado debajo del prompt. Un mod puede redibujar mensajes, filas de llamadas a herramientas y el spinner. El prompt es propio de Claude Code." width="600" height="336" data-path="images/mods-screen-map-dark.svg" />
16 16
17En una terminal más estrecha, el panel se sitúa sobre el símbolo del sistema en lugar de junto a la transcripción.17En una terminal más angosta, el panel se ubica sobre el prompt en lugar de junto a la transcripción.
18 18
19Construya su [primer mod](/docs/es/plugins/mods/create) antes de comenzar aquí. Comience con el ejemplo trabajado, que construye un panel con dos pestañas y un contador, luego lea la sección para cada parte que desee cambiar.19Crea tu [primer mod](/docs/es/plugins/mods/create) antes de empezar aquí. Comienza con el ejemplo práctico, que crea un panel con dos pestañas y un contador, y luego lee la sección de cada parte que quieras cambiar.
20 20
21<Note>21<Note>
22 Para buscar una propiedad o límite, consulte la [referencia](/docs/es/plugins/mods/reference#render-sites).22 Para consultar una prop o un límite específico, consulta la [referencia](/docs/es/plugins/mods/reference#render-sites).
23</Note>23</Note>
24 24
25<h2 id="build-a-pane-with-tabs">25<h2 id="build-a-pane-with-tabs">
26 Construir un panel con pestañas26 Crea un panel con pestañas
27</h2>27</h2>
28 28
29En esta sección construye un mod que agrega un comando `/hello-tabs` y el comando abre un panel. Un panel es una barra lateral junto a la transcripción en una terminal de pantalla completa ancha, o una región enmarcada sobre el símbolo del sistema de otra manera. Este panel muestra dos pestañas, y la segunda pestaña tiene un botón que suma uno a un contador. El recuento sigue ahí después de reiniciar Claude Code.29En esta sección creas un mod que agrega un comando `/hello-tabs`, y el comando abre un panel. Un panel es una barra lateral junto a la transcripción en una terminal de pantalla completa ancha, o una región enmarcada sobre el prompt en otros casos. Este panel muestra dos pestañas, y la segunda pestaña tiene un botón que suma uno a un contador. El conteo sigue ahí después de reiniciar Claude Code.
30 30
31El mod terminado se ve así. La grabación abre el panel, cambia a la segunda pestaña, presiona el botón varias veces y vuelve a la primera pestaña:31El mod terminado se ve así. La grabación abre el panel, cambia a la segunda pestaña, presiona el botón algunas veces y vuelve a la primera pestaña:
32 32
33<Frame>33<Frame>
34 <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=49d520094d87b5b44bfe50fa49677f06" aria-label="El comando /hello-tabs se escribe en el símbolo del sistema de Claude Code y se abre un panel enmarcado sobre él, con '1: One' y '2: Two' en la parte superior y el texto 'This is the first tab.' La segunda pestaña muestra un botón 'Add one' junto a 'Count: 1', y el recuento sube a 3. El panel luego vuelve a la primera pestaña." data-path="images/mods-hello-tabs-light.mp4" />34 <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=49d520094d87b5b44bfe50fa49677f06" aria-label="Se escribe el comando /hello-tabs en el prompt de Claude Code y se abre un panel enmarcado sobre él, con '1: One' y '2: Two' en la parte superior y el texto 'This is the first tab.' La segunda pestaña muestra un botón 'Add one' junto a 'Count: 1', y el conteo sube a 3. Luego el panel vuelve a la primera pestaña." data-path="images/mods-hello-tabs-light.mp4" />
35 35
36 <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=ff7a14d713d6e5d3b0000efa8522ea4b" aria-label="El comando /hello-tabs se escribe en el símbolo del sistema de Claude Code y se abre un panel enmarcado sobre él, con '1: One' y '2: Two' en la parte superior y el texto 'This is the first tab.' La segunda pestaña muestra un botón 'Add one' junto a 'Count: 1', y el recuento sube a 3. El panel luego vuelve a la primera pestaña." data-path="images/mods-hello-tabs-dark.mp4" />36 <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=ff7a14d713d6e5d3b0000efa8522ea4b" aria-label="Se escribe el comando /hello-tabs en el prompt de Claude Code y se abre un panel enmarcado sobre él, con '1: One' y '2: Two' en la parte superior y el texto 'This is the first tab.' La segunda pestaña muestra un botón 'Add one' junto a 'Count: 1', y el conteo sube a 3. Luego el panel vuelve a la primera pestaña." data-path="images/mods-hello-tabs-dark.mp4" />
37</Frame>37</Frame>
38 38
39Claude Code no tiene un elemento de pestañas integrado, por lo que las pestañas son dos botones en una fila. El mod realiza un seguimiento de cuál está activo y dibuja el contenido de esa pestaña bajo la fila.39Las pestañas son dos botones en una fila. El mod registra cuál está activa y dibuja el contenido de esa pestaña debajo de la fila.
40 40
41<Steps>41<Steps>
42 <Step title="Crear el plugin">42 <Step title="Crea el plugin">
43 Un mod es un plugin con un manifiesto, un `hooks.json` que apunta a su código y el archivo de código. [Crear un mod](/docs/es/plugins/mods/create#write-a-mod-yourself) explica cada uno. Cree un directorio llamado `hello-tabs` con directorios `.claude-plugin` y `hooks` dentro, luego guarde los dos primeros archivos.43 Un mod es un plugin con un manifiesto, un `hooks.json` que apunta a tu código y el archivo de código. [Crear un mod](/docs/es/plugins/mods/create#write-a-mod-yourself) explica cada uno. Crea un directorio llamado `hello-tabs` con los directorios `.claude-plugin` y `hooks` dentro, y luego guarda los dos primeros archivos.
44 44
45 Guarde el manifiesto como `hello-tabs/.claude-plugin/plugin.json`:45 Guarda el manifiesto como `hello-tabs/.claude-plugin/plugin.json`:
46 46
47 ```json hello-tabs/.claude-plugin/plugin.json theme={null}47 ```json hello-tabs/.claude-plugin/plugin.json theme={null}
48 {48 {
53 }53 }
54 ```54 ```
55 55
56 Nombre su punto de entrada en `hello-tabs/hooks/hooks.json`:56 Indica tu punto de entrada en `hello-tabs/hooks/hooks.json`:
57 57
58 ```json hello-tabs/hooks/hooks.json theme={null}58 ```json hello-tabs/hooks/hooks.json theme={null}
59 {59 {
62 ```62 ```
63 </Step>63 </Step>
64 64
65 <Step title="Escribir el código">65 <Step title="Escribe el código">
66 El código realiza tres trabajos, uno en cada hook:66 Esta lista indica qué hace cada hook, en el orden en que aparecen en el código:
67 67
68 * Agrega el comando `/hello-tabs`68 * Agrega el comando `/hello-tabs` y carga el conteo que guardó una sesión anterior
69 * Abre el panel cuando ejecuta ese comando69 * Abre el panel cuando ejecutas ese comando
70 * Dibuja el contenido del panel: la fila de pestañas y el cuerpo de la pestaña abierta70 * Dibuja el contenido del panel: la fila de pestañas y el cuerpo de la pestaña abierta
71 71
72 Dos variables a nivel de módulo, `tab` y `count`, mantienen el estado del panel.72 Dos variables a nivel de módulo, `tab` y `count`, contienen el estado del panel.
73 73
74 Guarde esto como `hello-tabs/hooks/register.js`:74 Guarda esto como `hello-tabs/hooks/register.js`:
75 75
76 ```javascript hello-tabs/hooks/register.js theme={null}76 ```javascript hello-tabs/hooks/register.js theme={null}
77 // The pane's id, used to open the pane and to recognize it when drawing77 // The pane's id, used to open the pane and to recognize it when drawing
167 167
168 Cada hook también hace algo que el código no deja claro:168 Cada hook también hace algo que el código no deja claro:
169 169
170 * **[`session.start`](/docs/es/plugins/mods/reference#session)** también lee el recuento guardado de [`$.store`](#keep-state), un almacén de clave-valor que persiste entre sesiones.170 * **[`session.start`](/docs/es/plugins/mods/reference#session)** también lee el conteo guardado desde [`$.store`](#keep-state), un almacén de clave-valor que persiste entre sesiones.
171 * **[`command.run`](/docs/es/plugins/mods/api#add-a-command)** solo le dice a Claude Code que el panel existe. Abrir un panel no dibuja nada por sí solo: Claude Code luego genera `ui.render` para preguntar qué va en él.171 * **[`command.run`](/docs/es/plugins/mods/api#add-a-command)** solo le indica a Claude Code que el panel existe. Abrir un panel no dibuja nada por sí solo: Claude Code luego dispara `ui.render` para preguntar qué va dentro.
172 * **`ui.render`** devuelve el árbol de elementos, un `Box` que contiene otros cuadros, texto y botones, y lo construye nuevamente desde `tab` y `count` cada vez que se ejecuta.172 * **`ui.render`** devuelve el árbol de elementos, un `Box` que contiene otras cajas, texto y botones, y lo vuelve a construir a partir de `tab` y `count` cada vez que se ejecuta.
173 173
174 Presionar un botón ejecuta su callback `onPress`, que cambia una variable y llama a `redraw`. Claude Code luego ejecuta el hook `ui.render` nuevamente, y el hook construye un nuevo árbol a partir de los nuevos valores. Cada dibujo interactivo utiliza ese ciclo de renderizado: un callback cambia el estado y el hook se renderiza nuevamente desde el nuevo estado.174 Presionar un botón ejecuta su callback `onPress`, que cambia una variable y llama a `redraw`. Claude Code luego vuelve a ejecutar el hook `ui.render`, y el hook construye un árbol nuevo a partir de los valores nuevos. Todo dibujo interactivo usa ese ciclo de renderizado: un callback cambia el estado y el hook vuelve a renderizar a partir del nuevo estado.
175 </Step>175 </Step>
176 176
177 <Step title="Abrir el panel">177 <Step title="Abre el panel">
178 En su shell, inicie Claude Code con `claude --plugin-dir ./hello-tabs`. En el símbolo del sistema de Claude Code, ejecute `/hello-tabs`. Se abre un panel con `1: One` y `2: Two` en la parte superior. Presione `2`, luego presione `a`, el atajo de teclado para **Add one**, varias veces. El recuento sube.178 En tu shell, inicia Claude Code con `claude --plugin-dir ./hello-tabs`. En el prompt de Claude Code, ejecuta `/hello-tabs`. Se abre un panel con `1: One` y `2: Two` en la parte superior. Presiona `2` y luego presiona `a`, la tecla de acceso rápido de **Add one**, algunas veces. El conteo sube.
179 </Step>179 </Step>
180 180
181 <Step title="Verificar que el recuento fue guardado">181 <Step title="Comprueba que el conteo se guardó">
182 Presione Esc para cerrar el panel, luego salga de la sesión. En su shell, inicie Claude Code nuevamente con el mismo comando `claude --plugin-dir ./hello-tabs` y en el símbolo del sistema de Claude Code ejecute `/hello-tabs`. El recuento está donde lo dejó.182 Presiona Esc para cerrar el panel y luego sal de la sesión. En tu shell, vuelve a iniciar Claude Code con el mismo comando `claude --plugin-dir ./hello-tabs` y, en el prompt de Claude Code, ejecuta `/hello-tabs`. El conteo está donde lo dejaste.
183 183
184 Para borrar el recuento, haga que el mod llame a `$.store.delete('count')`. [Mantener estado](#keep-state) cubre cuánto tiempo dura cada tipo de valor.184 Para borrar el conteo, haz que el mod llame a `$.store.delete('count')`. [Conservar el estado](#keep-state) explica cuánto dura cada tipo de valor.
185 </Step>185 </Step>
186</Steps>186</Steps>
187 187
188<h2 id="pick-where-to-draw">188<h2 id="pick-where-to-draw">
189 Elegir dónde dibujar189 Elige dónde dibujar
190</h2>190</h2>
191 191
192Un hook `ui.render` se ejecuta para cada sitio de renderizado a menos que lo reduzca al que desea dibujar. Para elegir el sitio de renderizado, pase un filtro, llamado [matcher](/docs/es/plugins/mods/events#filter-which-events-a-hook-handles), como segundo argumento a `on`. `{ component: 'Pane' }` ejecuta el hook solo para paneles. En el hook, `e.component` nombra el sitio, `e.surface` dice qué aplicación está dibujando, y `e.props` contiene los datos propios del sitio. Para un panel, `e.requestId` es el `id` con el que lo abrió.192Un hook `ui.render` se ejecuta para cada punto de renderizado, a menos que lo restrinjas al punto en el que quieres dibujar. Para elegir el punto de renderizado, pasa un filtro, llamado [matcher](/docs/es/plugins/mods/events#filter-which-events-a-hook-handles), como segundo argumento de `on`. `{ component: 'Pane' }` ejecuta el hook solo para paneles. En el hook, `e.component` indica el punto, `e.surface` indica qué aplicación está dibujando y `e.props` contiene los datos propios del punto. Para un panel, `e.requestId` es el `id` con el que lo abriste.
193 193
194Dos sitios están vacíos hasta que un mod los llena, el panel y la banda. Seleccione una pestaña para ver qué es cada uno y cómo dibujar en él:194El panel y la franja están vacíos hasta que un mod los llena. Selecciona una pestaña para ver qué es cada uno y cómo dibujar en él:
195 195
196<Tabs>196<Tabs>
197 <Tab title="Panel">197 <Tab title="Panel">
198 Un panel es una barra lateral junto a la transcripción en una terminal de pantalla completa ancha, o una región enmarcada sobre el símbolo del sistema de otra manera. Con varios paneles abiertos, cada uno obtiene una pestaña que muestra su título.198 Un panel es una barra lateral junto a la transcripción en una terminal de pantalla completa ancha o, en caso contrario, una región enmarcada encima del prompt. Con varios paneles abiertos, cada uno tiene una pestaña que muestra su título.
199 199
200 Un panel aparece cuando su mod llama a `$.ui.open` con un `id` que elige, como en `$.ui.open({ id: 'hello-tabs' })`. [Abrir un panel en el momento adecuado](#open-a-pane-at-the-right-time) cubre los otros campos y cuándo un panel espera una terminal más ancha.200 Un panel aparece cuando tu mod llama a `$.ui.open` con un `id` que eliges, como en `$.ui.open({ id: 'hello-tabs' })`. [Abre un panel en el momento adecuado](#open-a-pane-at-the-right-time) describe los demás campos y cuándo un panel espera a que la terminal sea más ancha.
201 201
202 Para dibujar en su panel, filtre en `{ component: 'Pane' }` y verifique que `e.requestId` sea su `id`.202 Para dibujar en tu panel, filtra por `{ component: 'Pane' }` y comprueba que `e.requestId` sea tu `id`.
203 </Tab>203 </Tab>
204 204
205 <Tab title="Banda sobre el símbolo del sistema">205 <Tab title="Franja encima del prompt">
206 La banda es una franja directamente sobre la entrada del símbolo del sistema. Siempre está ahí, y cada mod la comparte.206 La franja es una tira directamente encima de la entrada del prompt. Siempre está ahí y todos los mods la comparten.
207 207
208 Su hook devuelve un árbol para mostrar algo en la banda, o `next(e)` para no mostrar nada. Un árbol reemplaza lo que los mods [después del suyo](/docs/es/plugins/mods/events#the-order-mods-run-in) dibujan allí. Para mantener el suyo, ponga el resultado de `await next(e)` entre los hijos de un [`Box`](#build-a-tree-from-elements) en su árbol.208 Tu hook devuelve un árbol para mostrar algo en la franja, o `next(e)` para no mostrar nada. Un árbol reemplaza lo que los mods [posteriores al tuyo](/docs/es/plugins/mods/events#the-order-mods-run-in) dibujan ahí. Para conservar lo suyo, coloca el resultado de `await next(e)` entre los hijos de un [`Box`](#build-a-tree-from-elements) en tu árbol.
209 209
210 Para dibujar en la banda, filtre en `{ component: 'AbovePrompt' }`.210 Para dibujar en la franja, filtra por `{ component: 'AbovePrompt' }`.
211 </Tab>211 </Tab>
212</Tabs>212</Tabs>
213 213
214<h3 id="change-what-claude-code-already-draws">214<h3 id="change-what-claude-code-already-draws">
215 Cambiar lo que Claude Code ya dibuja215 Cambia lo que Claude Code ya dibuja
216</h3>216</h3>
217 217
218Claude Code dibuja la mayoría de su interfaz a sí mismo: mensajes, filas de llamadas de herramientas, el spinner y más. Cada una de esas partes es un sitio de renderizado también, por lo que un mod puede cambiar el estilo o reemplazarlo. Para cambiar uno, filtre su hook `ui.render` en su nombre de esta tabla:218Claude Code dibuja por sí mismo la mayor parte de su interfaz: mensajes, filas de llamadas a herramientas, el spinner y más. Cada una de esas partes también es un punto de renderizado, así que un mod puede cambiar su estilo o reemplazarla. Para cambiar una, filtra tu hook `ui.render` por su nombre de esta tabla:
219 219
220| Sitio | Qué es |220| Punto | Qué es |
221| :- | :- |221| :- | :- |
222| `UserMessage`, `AssistantMessage` | Un mensaje en la transcripción |222| `UserMessage`, `AssistantMessage` | Un mensaje en la transcripción |
223| `ToolUse`, `ToolResult`, `ToolGroup` | La fila de una llamada de herramienta, su resultado y una ejecución plegada de llamadas |223| `ToolUse`, `ToolResult`, `ToolGroup` | La fila de una llamada a herramienta, su resultado y un grupo contraído de llamadas |
224| `CommandOutput` | La fila que un comando imprimió |224| `CommandOutput` | La fila que imprimió un comando |
225| `AskUserQuestion` | El diálogo que Claude abre para hacerle una pregunta |225| `AskUserQuestion` | El diálogo que Claude abre para hacerte una pregunta |
226| `Spinner`, `ToolProgress`, `TurnDuration` | Líneas de estado para un turno: la línea que se anima mientras Claude trabaja, la línea de progreso en vivo de una herramienta en ejecución y la línea que cierra un turno |226| `Spinner`, `ToolProgress`, `TurnDuration` | Líneas de estado de un turno: la línea que se anima mientras Claude trabaja, la línea de progreso en vivo de una herramienta en ejecución y la línea que cierra un turno |
227| `InfoNotice`, `SessionMode`, `PromptHint` | Líneas de estado bajo el logo, las etiquetas de modo en el pie de página y la línea de sugerencia bajo el símbolo del sistema |227| `InfoNotice`, `SessionMode`, `PromptHint` | Líneas de estado bajo el logotipo, las etiquetas de modo en el pie y la línea de sugerencia bajo el prompt |
228 228
229En un sitio que Claude Code ya dibuja, su hook tiene tres opciones: cambiar un detalle, reemplazar el dibujo o dejarlo solo. Seleccione una pestaña para ver cada una aplicada al spinner. Los ejemplos leen una variable `calls` que otro hook cuenta, como en el [mod de tutorial](/docs/es/plugins/mods/create#write-a-mod-yourself).229En un punto que Claude Code ya dibuja, tu hook puede cambiar un detalle, reemplazar el dibujo o dejarlo como está. Selecciona una pestaña para ver cada opción aplicada al spinner. Los ejemplos leen una variable `calls` que otro hook cuenta, como en el [mod del tutorial](/docs/es/plugins/mods/create#write-a-mod-yourself).
230 230
231<Tabs>231<Tabs>
232 <Tab title="Cambiar un detalle">232 <Tab title="Cambiar un detalle">
233 Para mantener el dibujo de Claude Code y cambiar una parte de él, pase a `next` una copia del evento con `props` cambiados. Este hook cambia el texto después de la palabra del spinner:233 Para conservar el dibujo de Claude Code y cambiar una parte, pasa a `next` una copia del evento con las `props` modificadas. Este hook cambia el texto que sigue a la palabra del spinner:
234 234
235 ```javascript theme={null}235 ```javascript theme={null}
236 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {236 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
239 })239 })
240 ```240 ```
241 241
242 El spinner mantiene su animación y su palabra, y su texto sigue a la palabra:242 El spinner conserva su animación y su palabra, y tu texto va después de la palabra:
243 243
244 ```text theme={null}244 ```text theme={null}
245 Thinking · tool calls: 2…245 Thinking · tool calls: 2…
247 </Tab>247 </Tab>
248 248
249 <Tab title="Reemplazar el dibujo">249 <Tab title="Reemplazar el dibujo">
250 Para dibujar algo propio en el lugar del sitio, devuelva un árbol y no llame a `next`. Este hook dibuja una línea de texto donde estaría el spinner:250 Para dibujar algo propio en lugar del punto, devuelve un árbol y no llames a `next`. Este hook dibuja una línea de texto donde estaría el spinner:
251 251
252 ```javascript theme={null}252 ```javascript theme={null}
253 on('ui.render', { component: 'Spinner' }, async ($, e) => {253 on('ui.render', { component: 'Spinner' }, async ($, e) => {
257 })257 })
258 ```258 ```
259 259
260 Mientras Claude trabaja, su línea se muestra y el spinner de Claude Code no:260 Mientras Claude trabaja, se muestra tu línea y no el spinner de Claude Code:
261 261
262 ```text theme={null}262 ```text theme={null}
263 Claude has made 2 tool calls263 Claude has made 2 tool calls
264 ```264 ```
265 </Tab>265 </Tab>
266 266
267 <Tab title="Dejarlo solo">267 <Tab title="Dejarlo como está">
268 Para dejar el sitio como Claude Code lo dibuja, devuelva `next(e)`. Un hook a menudo hace eso para algunos eventos y no para otros. Este hook deja el spinner solo hasta que hay una llamada para contar:268 Para dejar el punto tal como lo dibuja Claude Code, devuelve `next(e)`. Un hook suele hacerlo para algunos eventos y no para otros. Este hook deja el spinner como está hasta que haya una llamada que contar:
269 269
270 ```javascript theme={null}270 ```javascript theme={null}
271 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {271 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
275 })275 })
276 ```276 ```
277 277
278 Antes de la primera llamada de herramienta, el spinner se ve de la manera que lo hace sin el mod:278 Antes de la primera llamada a herramienta, el spinner se ve igual que sin el mod:
279 279
280 ```text theme={null}280 ```text theme={null}
281 Thinking…281 Thinking…
283 </Tab>283 </Tab>
284</Tabs>284</Tabs>
285 285
286El símbolo del sistema de permiso no es un sitio de renderizado, por lo que un mod no puede cambiar lo que muestra. El diálogo de pregunta, `AskUserQuestion`, es uno, por lo que un mod puede cambiar eso.286En estos puntos, `next(e)` devuelve una referencia al dibujo de Claude Code, `{ type: 'engine', ref }`, a menos que un mod que se ejecuta después del tuyo haya devuelto un árbol propio. Para cambiar lo que hay en ese dibujo, pasa a `next` una copia del evento con props diferentes, como hace la pestaña **Cambiar un detalle**. Puedes devolver la referencia tal cual o colocarla en un `Box` junto a elementos propios:
287 287
288La terminal y la aplicación de escritorio no generan todos los mismos sitios. `Pane`, `AbovePrompt`, `Spinner` y los sitios de transcripción funcionan en ambos. Algunas otras líneas de estado se generan solo en la terminal. La [tabla de sitios de renderizado](/docs/es/plugins/mods/reference#render-sites) enumera dónde se genera cada uno.288```javascript theme={null}
289on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
290 const { Box, Text } = $.ui.resolve(e)
291 const theirs = await next(e)
292 return Box({ flexDirection: 'column', children: [theirs, Text({ children: ['under the spinner'] })] })
293})
294```
295
296Mientras Claude trabaja, el spinner se anima como antes y `under the spinner` aparece debajo.
297
298La solicitud de permiso no es un punto de renderizado, así que un mod no puede cambiar lo que muestra. El diálogo de preguntas, `AskUserQuestion`, sí lo es, así que un mod puede cambiarlo. Un árbol para el diálogo debe contener la referencia exactamente una vez, con tus elementos encima. De lo contrario, Claude Code dibuja su propio diálogo.
299
300La terminal y la aplicación Desktop no generan los mismos puntos. `Pane`, `AbovePrompt`, `Spinner` y los puntos de la transcripción funcionan en ambas. Algunas otras líneas de estado solo se generan en la terminal. La [tabla de puntos de renderizado](/docs/es/plugins/mods/reference#render-sites) indica dónde se genera cada uno.
289 301
290<h3 id="open-a-pane-at-the-right-time">302<h3 id="open-a-pane-at-the-right-time">
291 Abrir un panel en el momento adecuado303 Abre un panel en el momento adecuado
292</h3>304</h3>
293 305
294Un panel aparece solo cuando su mod lo abre. Cómo y cuándo lo abre decide si toma el enfoque del teclado, cuánto espacio solicita y si aparece en absoluto en una terminal estrecha.306Un panel solo aparece cuando tu mod lo abre. Cómo y cuándo lo abres determina si toma el foco del teclado, cuánto espacio solicita y si llega a mostrarse en una terminal estrecha.
295 307
296Para abrir un panel, llame a [`$.ui.open`](/docs/es/plugins/mods/reference#mods-api-methods) con un `id` que elija. El `id` es el nombre del panel: su hook `ui.render` lo verifica y lo pasa nuevamente para cerrar el panel.308Para abrir un panel, llama a [`$.ui.open`](/docs/es/plugins/mods/reference#mods-api-methods) con un `id` que elijas. El `id` es el nombre del panel: tu hook `ui.render` lo comprueba y lo vuelves a pasar para cerrar el panel.
297 309
298```javascript theme={null}310```javascript theme={null}
299await $.ui.open({ id: 'hello-tabs', title: 'Hello tabs', focus: true })311await $.ui.open({ id: 'hello-tabs', title: 'Hello tabs', focus: true })
300```312```
301 313
302Para cerrar el panel, llame a `$.ui.close` con el `id` con el que lo abrió:314Para cerrar el panel, llama a `$.ui.close` con el `id` con el que lo abriste:
303 315
304```javascript theme={null}316```javascript theme={null}
305await $.ui.close({ id: 'hello-tabs' })317await $.ui.close({ id: 'hello-tabs' })
306```318```
307 319
308Además de `id`, `$.ui.open` toma estos campos opcionales:320Además de `id`, `$.ui.open` acepta estos campos opcionales:
309 321
310| Campo | Qué hace |322| Campo | Qué hace |
311| :- | :- |323| :- | :- |
312| `title` | La etiqueta de pestaña del panel cuando hay más de un panel abierto |324| `title` | La etiqueta de la pestaña del panel cuando hay más de un panel abierto |
313| `focus` | Solicita [enfoque del teclado](#know-which-keys-your-mod-can-receive) |325| `focus` | Solicita el [foco del teclado](#know-which-keys-your-mod-can-receive) |
314| `closeOnEscape` | Hace que Esc cierre el panel |326| `closeOnEscape` | Hace que Esc cierre el panel |
315| `holdToasts` | Mantiene las notificaciones, los pequeños avisos de [`$.ui.toast`](/docs/es/plugins/mods/api#show-something-without-starting-a-turn), hasta que se cierre el panel |327| `holdToasts` | Retiene los toasts, los pequeños avisos de [`$.ui.toast`](/docs/es/plugins/mods/api#show-something-without-starting-a-turn), hasta que el panel se cierre |
316| `rows` | La altura a solicitar cuando el panel se sitúa sobre el símbolo del sistema. El valor predeterminado es un tercio del espacio. |328| `rows` | La altura que se solicita cuando el panel está encima del prompt. El valor predeterminado es un tercio del espacio. |
317| `columns` | El ancho a solicitar cuando el panel se sitúa junto a la transcripción |329| `columns` | El ancho que se solicita cuando el panel está junto a la transcripción |
318 330
319`focus`, `closeOnEscape` y `holdToasts` son opcionales y aceptan solo `true`. Para omitir uno, déjelo fuera. Pasar `false` lanza un error como `ui.open: focus is true or left out`. Para establecer uno de ellos condicionalmente, agregue el campo solo cuando la condición se cumple. Esta llamada solicita enfoque del teclado solo cuando `items` no está vacío:331`focus`, `closeOnEscape` y `holdToasts` son opcionales y solo aceptan `true`. Para no usar uno, omítelo. Pasar `false` genera un error como `ui.open: focus is true or left out`. Para establecer uno de ellos de forma condicional, agrega el campo solo cuando se cumpla la condición. Esta llamada solicita el foco del teclado solo cuando `items` no está vacío:
320 332
321```javascript theme={null}333```javascript theme={null}
322const pane = { id: 'hello-tabs', title: 'Hello tabs' }334const pane = { id: 'hello-tabs', title: 'Hello tabs' }
323await $.ui.open(items.length > 0 ? { ...pane, focus: true } : pane)335await $.ui.open(items.length > 0 ? { ...pane, focus: true } : pane)
324```336```
325 337
326Para permitir que un comando abra el panel mientras Claude está trabajando, agregue `immediate: true` cuando [registre el comando](/docs/es/plugins/mods/api#add-a-command). Sin él, un comando escrito durante un turno espera a que el turno termine.338Para permitir que un comando abra el panel mientras Claude está trabajando, agrega `immediate: true` cuando [registres el comando](/docs/es/plugins/mods/api#add-a-command). Sin eso, un comando escrito durante un turno espera a que el turno termine.
327 339
328<h4 id="when-a-pane-waits-for-a-wider-terminal">340<h4 id="when-a-pane-waits-for-a-wider-terminal">
329 Cuando un panel espera una terminal más ancha341 Cuándo un panel espera a que la terminal sea más ancha
330</h4>342</h4>
331 343
332Un panel que su mod abre sin ser solicitado no aparece en una terminal estrecha, por lo que no puede ocupar una pantalla pequeña. Si aparece depende de lo que lo abrió:344Un panel que tu mod abre sin que se lo pidan no aparece en una terminal estrecha, para que no pueda ocupar una pantalla pequeña. Que aparezca o no depende de qué lo abrió:
333 345
334* **Abierto por algo que hizo el usuario**, como un comando que ejecutó o un botón que presionó, el panel aparece en cualquier ancho346* **Si lo abrió algo que hizo el usuario**, como un comando que ejecutó o un botón que presionó, el panel aparece con cualquier ancho
335* **Abierto por su mod actuando por sí solo**, como desde un temporizador o un hook [`turn.start`](/docs/es/plugins/mods/events#follow-a-turn), el panel aparece solo en una terminal de al menos 144 columnas de ancho. Después de que el usuario haya abierto ese panel una vez por sí mismo, 110 columnas es suficiente.347* **Si lo abrió tu mod por su cuenta**, por ejemplo desde un temporizador o un hook [`turn.start`](/docs/es/plugins/mods/events#follow-a-turn), el panel solo aparece en una terminal de al menos 144 columnas de ancho. Después de que el usuario haya abierto ese panel por sí mismo una vez, bastan 110 columnas.
336 348
337Cuando aparece el panel, `$.ui.open` se resuelve en `{ isPlaced: true }`. Cuando el panel está esperando, `isPlaced` es `false` y `reason` es una cadena que dice por qué. Un panel en espera aparece cuando el usuario lo abre o amplía la terminal. Para decir que algo está disponible sin abrir un panel, llame a `$.ui.toast('Your message')`, que muestra un pequeño aviso que desaparece después de unos segundos.349Cuando el panel aparece, `$.ui.open` se resuelve como `{ isPlaced: true }`. Cuando el panel está en espera, `isPlaced` es `false` y `reason` es una cadena que explica el motivo. Un panel en espera aparece cuando el usuario lo abre o amplía la terminal. Para avisar que algo está disponible sin abrir un panel, llama a `$.ui.toast('Your message')`, que muestra una notificación toast.
338 350
339<h2 id="build-a-tree-from-elements">351<h2 id="build-a-tree-from-elements">
340 Construir un árbol a partir de elementos352 Construye un árbol a partir de elementos
341</h2>353</h2>
342 354
343Lo que devuelve un hook `ui.render` es un árbol de elementos: una descripción de qué dibujar, hecha de cuadros, texto y controles anidados entre sí. Describe el dibujo y Claude Code lo renderiza en la terminal o en la aplicación de escritorio.355Lo que devuelve un hook `ui.render` es un árbol de elementos: una descripción de lo que se debe dibujar, hecha de cajas, texto y controles anidados unos dentro de otros. Tú describes el dibujo, y Claude Code lo renderiza en la terminal o en la aplicación Desktop.
344 356
345Para obtener los elementos, llame a `$.ui.resolve(e)` en su hook, como en `const { Box, Text, Button } = $.ui.resolve(e)`. Cada elemento es una función. Pasa sus propiedades y pone los elementos y cadenas que van dentro en `children`.357Para obtener los elementos, llama a `$.ui.resolve(e)` en tu hook, como en `const { Box, Text, Button } = $.ui.resolve(e)`. Cada elemento es una función. Le pasas props, y pones los elementos y cadenas que van dentro de él en `children`.
346 358
347La mayoría de los dibujos utilizan cuatro elementos. Seleccione una pestaña para ver cada uno y cómo la terminal lo dibuja:359Selecciona una pestaña para ver cada uno de los elementos más comunes y cómo los dibuja la terminal:
348 360
349<Tabs>361<Tabs>
350 <Tab title="Texto">362 <Tab title="Text">
351 `Text` dibuja una cadena, con estilo opcional como `bold` y `color`:363 `Text` dibuja una cadena, con estilos opcionales como `bold` y `color`:
352 364
353 ```javascript theme={null}365 ```javascript theme={null}
354 Text({ children: ['This is the first tab.'] })366 Text({ children: ['This is the first tab.'] })
359 ```371 ```
360 </Tab>372 </Tab>
361 373
362 <Tab title="Cuadro">374 <Tab title="Box">
363 `Box` organiza lo que hay dentro, en una fila o una columna. Este pone un botón y una línea de texto uno al lado del otro, dos columnas separadas:375 `Box` organiza lo que contiene, en una fila o una columna. Este coloca un botón y una línea de texto uno al lado del otro, separados por dos columnas:
364 376
365 ```javascript theme={null}377 ```javascript theme={null}
366 Box({378 Box({
378 ```390 ```
379 </Tab>391 </Tab>
380 392
381 <Tab title="Botón">393 <Tab title="Button">
382 `Button` es un control que el usuario puede presionar. Ejecuta su callback `onPress`. Con `plain: true` no tiene corchetes y muestra su atajo de teclado:394 `Button` es un control que el usuario puede presionar. Ejecuta tu callback `onPress`. Con `plain: true` no tiene corchetes y muestra su tecla de acceso rápido:
383 395
384 ```javascript theme={null}396 ```javascript theme={null}
385 Button({ key: 'more', label: 'Add one', onPress: addOne })397 Button({ key: 'more', label: 'Add one', onPress: addOne })
392 ```404 ```
393 </Tab>405 </Tab>
394 406
395 <Tab title="Entrada">407 <Tab title="Input">
396 `Input` es un campo de texto. Ejecuta su callback `onSubmit` con el texto cuando el usuario presiona Enter:408 `Input` es un campo de texto. Ejecuta tu callback `onSubmit` con el texto cuando el usuario presiona Enter:
397 409
398 ```javascript theme={null}410 ```javascript theme={null}
399 Input({411 Input({
407 ```419 ```
408 420
409 ```text theme={null}421 ```text theme={null}
410 Note: Type a note and press Enter ⏎ add422 Note: Type a note and press Enter
411 ```423 ```
412 </Tab>424 </Tab>
413</Tabs>425</Tabs>
414 426
415Esta tabla enumera cada elemento:427La [galería de interfaces](/docs/es/plugins/mods/gallery) tiene ejemplos y capturas de pantalla de la mayoría de los elementos. Esta tabla enumera todos los elementos:
416 428
417| Elemento | Qué dibuja | Dónde |429| Elemento | Qué dibuja | Dónde |
418| :- | :- | :- |430| :- | :- | :- |
419| `Box` | Un contenedor flex. Toma propiedades de diseño como `flexDirection`, `columnGap`, `padding`, `borderStyle` y `width`. | En todas partes |431| `Box` | Un contenedor flex. Acepta props de diseño como `flexDirection`, `columnGap`, `padding`, `borderStyle` y `width`. | En todas partes |
420| `Text` | Texto con estilo. Toma `color`, `bold`, `dimColor`, `italic` y `wrap`. Un `color` es una clave de tema o un color como `'red'`. Un `wrap` es `'wrap'`, `'truncate'`, `'truncate-start'`, `'truncate-middle'` o `'truncate-end'`. | En todas partes |432| `Text` | Texto con estilo. Acepta `color`, `bold`, `dimColor`, `italic` y `wrap`. Un `color` es una clave del tema o un color como `'red'`. Un `wrap` es `'wrap'`, `'truncate'`, `'truncate-start'`, `'truncate-middle'` o `'truncate-end'`. | En todas partes |
421| `Button` | Un control que llama a `onPress` | En todas partes |433| `Button` | Un control que llama a `onPress` | En todas partes |
422| `Link`, `Code`, `Markdown` | Un enlace con `href` y una `label` opcional, un bloque de código y texto formateado de la manera que lo son las respuestas de Claude. `Markdown` toma su contenido en una propiedad `text`, no en `children`, y necesita una `key` cuando pasa `onLinkPress`. | En todas partes |434| `Link`, `Code`, `Markdown` | Un enlace con `href` y una `label` opcional, un bloque de código y texto con el mismo formato que las respuestas de Claude. `Markdown` recibe su contenido en una prop `text`, no en `children`, y necesita una `key` cuando pasas `onLinkPress`. | En todas partes |
423| `Input`, `Select` | Un campo de texto y un selector | Terminal, Escritorio |435| `Input`, `Select` | Un campo de texto y una lista desplegable | Terminal, Desktop |
424| `Svg` | Un documento SVG | Escritorio |436| `Svg` | Un documento SVG | Desktop |
425| `Client` | Una región dibujada por un segundo archivo suyo, para animación e entrada de puntero. Ese archivo no obtiene API de mods. Solo llega a sus hooks publicando datos, que llegan como un evento `ui.message`. | Terminal, Escritorio |437| `Client` | Una región dibujada por un segundo archivo tuyo, para animación y entrada del puntero. Ese archivo no recibe la API de mods. Solo llega a tus hooks publicando datos, que llegan como un evento `ui.message`. | Terminal, Desktop |
426| `Raster`, `Image` | Una [cuadrícula de celdas coloreadas](#draw-a-grid-of-colored-cells) y una imagen | Terminal |438| `Raster`, `Image` | Una [cuadrícula de celdas de colores](#draw-a-grid-of-colored-cells) y una imagen | Terminal |
427 439
428Si su módulo es un archivo `.tsx` o `.jsx`, puede escribir el árbol como JSX. Desestructure los elementos de `$.ui.resolve(e)` primero, porque un módulo de hooks no tiene globales de elementos.440Si tu módulo es un archivo `.tsx` o `.jsx`, puedes escribir el árbol como JSX. Primero desestructura los elementos de `$.ui.resolve(e)`.
429 441
430Si un árbol utiliza un elemento que la aplicación no tiene, una propiedad que un elemento no toma o un hijo donde no va ninguno, Claude Code dibuja su propia versión del sitio.442Si un árbol usa un elemento que la aplicación no tiene, una prop que un elemento no acepta o un hijo donde no va ninguno, Claude Code dibuja su propia versión del sitio.
431 443
432En una sesión iniciada con `--plugin-dir`, una línea de transcripción lo dice, como `ui.render (Pane) refused: Text prop "bogusProp" is not allowed; the engine drew its own`. El [registro de depuración](/docs/es/plugins/mods/troubleshoot#read-the-debug-log) lo registra como `ui.render (Pane): a hook returned a tree that does not validate` con la misma razón. Nada más aparece en la sesión, por lo que cuando un dibujo no aparece, verifique esa línea o el registro.444En una sesión iniciada con `--plugin-dir`, una línea de la transcripción lo indica, por ejemplo `ui.render (Pane) refused: Text prop "bogusProp" is not allowed; the engine drew its own`. El [registro de depuración](/docs/es/plugins/mods/troubleshoot#read-the-debug-log) lo registra como `ui.render (Pane): a hook returned a tree that does not validate` con el mismo motivo. No aparece nada más en la sesión, así que cuando un dibujo no se muestre, revisa esa línea o el registro.
433 445
434<h3 id="draw-a-grid-of-colored-cells">446<h3 id="draw-a-grid-of-colored-cells">
435 Dibujar una cuadrícula de celdas coloreadas447 Dibuja una cuadrícula de celdas de colores
436</h3>448</h3>
437 449
438Para un mapa de calor, un gráfico de chispa o un tablero de juego en la terminal, dibuje un `Raster` y no un `Box` para cada celda. Un `Raster` toma una `key`, su tamaño en `columns` y `rows`, y `cells`, que empaqueta cada celda en una cadena. Cada celda son tres números: el punto de código del carácter, su color y su color de fondo. Un color es un número hexadecimal con dos dígitos cada uno para rojo, verde y azul, como `0xc62828` para un rojo, o `0x01000000` para el predeterminado de la terminal.450Para un mapa de calor, un minigráfico o un tablero de juego en la terminal, dibuja un solo `Raster` y no un `Box` por cada celda. Un `Raster` recibe una `key`, su tamaño en `columns` y `rows`, y `cells`, una cadena base64 que empaqueta todas las celdas. Cada celda son tres números: el punto de código del carácter, su color y su color de fondo. Un color es un valor RGB de 24 bits en hexadecimal, como `0xc62828` para un rojo. El valor `0x01000000`, uno por encima de ese rango, significa el predeterminado de la terminal.
439 451
440La aplicación de escritorio no tiene `Raster`, por lo que verifique `e.surface` y dibuje texto allí. Este cuerpo de panel dibuja un mapa de calor de tres por dos:452La aplicación Desktop no tiene `Raster`, así que comprueba `e.surface` y dibuja texto allí. Este cuerpo de panel dibuja un mapa de calor de tres por dos:
441 453
442```javascript theme={null}454```javascript theme={null}
443// The value that means "use the terminal's default color"455// The value that means "use the terminal's default color"
471 483
472En la terminal, el panel muestra la cuadrícula:484En la terminal, el panel muestra la cuadrícula:
473 485
474<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-heat-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=b91bcce3bad74bc851149133d4acc5d5" alt="Un panel en la terminal que contiene una pequeña cuadrícula de bloques coloreados, dos filas de tres. La fila superior es verde, ámbar y roja. La fila inferior es verde, verde y ámbar." width="360" height="132" data-path="images/mods-heat-map.svg" />486<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-heat-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=b91bcce3bad74bc851149133d4acc5d5" alt="Un panel en la terminal que contiene una pequeña cuadrícula de bloques de colores, dos filas de tres. La fila superior es verde, ámbar y roja. La fila inferior es verde, verde y ámbar." width="360" height="132" data-path="images/mods-heat-map.svg" />
475 487
476La matriz `rows` es la parte que cambiaría, y `cellsOf` la convierte en la cadena empaquetada. El hook dibuja solo en un panel cuyo `id` es `heat`, por lo que abra uno con `$.ui.open({ id: 'heat' })` desde un comando, como el ejemplo [`hello-tabs`](#build-a-pane-with-tabs) abre su panel.488El arreglo `rows` es la parte que cambiarías, y `cellsOf` lo convierte en la cadena empaquetada. El hook dibuja solo en un panel cuyo `id` es `heat`, así que abre uno con `$.ui.open({ id: 'heat' })` desde un comando, como el [ejemplo `hello-tabs`](#build-a-pane-with-tabs) abre su panel.
477 489
478Cada carácter tiene que tener un ancho de una celda. Para animar un `Raster` que ya está en pantalla, llame a `$.ui.blit` con el `id` del panel como `requestId`, la `key` del `Raster`, el mismo tamaño y celdas nuevas. Para este ejemplo, eso es `$.ui.blit({ requestId: 'heat', key: 'grid', columns: 3, rows: 2, cells: cellsOf(newRows) })`. Repinta ese elemento sin ejecutar su hook `ui.render` nuevamente.490Cada carácter tiene que ocupar una celda de ancho. Para animar un `Raster` que ya está en pantalla, llama a `$.ui.blit` con el `id` del panel como `requestId`, la `key` del `Raster`, el mismo tamaño y celdas nuevas. Para este ejemplo, eso es `$.ui.blit({ requestId: 'heat', key: 'grid', columns: 3, rows: 2, cells: cellsOf(newRows) })`. Vuelve a pintar ese único elemento sin ejecutar de nuevo tu hook `ui.render`.
479 491
480<h2 id="respond-to-presses-and-typing">492<h2 id="respond-to-presses-and-typing">
481 Responder a pulsaciones y escritura493 Responder a pulsaciones y escritura
482</h2>494</h2>
483 495
484Cuando el usuario presiona un botón, escribe en un campo o elige de una lista que su mod dibujó, Claude Code llama a la función que le dio a ese control, y se ejecuta en su módulo. Cada control toma sus propios callbacks:496Cuando el usuario pulsa un botón, escribe en un campo o elige de una lista que tu mod dibujó, Claude Code llama al callback de ese control, que se ejecuta en tu módulo. Cada control acepta sus propios callbacks:
485 497
486* **`Button`**: toma `onPress(e)`, donde `e.surface` es la aplicación de la que proviene la pulsación498* **`Button`**: acepta `onPress(e)`, donde `e.surface` es la app de la que vino la pulsación
487* **`Input`**: toma `onSubmit(value)` y `onInput(value)`499* **`Input`**: acepta `onSubmit(value)` y `onInput(value)`
488* **`Select`**: toma `onSelect(value)` con sus opciones en `options`, una lista de al menos una opción con valores únicos, como `[{ value: 'sm', label: 'Small' }, { value: 'lg', label: 'Large' }]`500* **`Select`**: acepta `onSelect(value)` con sus opciones en `options`, una lista de al menos una opción con valores únicos, como `[{ value: 'sm', label: 'Small' }, { value: 'lg', label: 'Large' }]`
489 501
490Una prueba presiona o escribe en un control por su `key`, así que dé a cada control uno. Cada uso de un control también genera [`ui.press`, `ui.input` o `ui.select`](/docs/es/plugins/mods/reference#interface) con la `key` en `e.element`, y otro mod puede enganchar esos eventos. Su hook se ejecuta antes de su callback, por lo que ve lo que el usuario escribe en su `Input` y puede cambiarlo o responder en lugar de su callback. La API de mods no tiene método que presione el botón de otro mod.502Una prueba pulsa o escribe en un control por su `key`, así que dale una a cada control. Cada uso de un control también dispara [`ui.press`, `ui.input` o `ui.select`](/docs/es/plugins/mods/reference#interface) con la `key` en `e.element`, y otro mod puede manejar esos eventos. Su hook se ejecuta antes que tu callback, así que ve lo que el usuario escribe en tu `Input` y puede cambiarlo o responder en lugar de tu callback. La API de mods no tiene ningún método que pulse el botón de otro mod.
491 503
492<h3 id="know-which-keys-your-mod-can-receive">504<h3 id="know-which-keys-your-mod-can-receive">
493 Enfoque del teclado y atajos de teclado505 Foco del teclado y teclas de acceso rápido
494</h3>506</h3>
495 507
496Su mod nunca lee el teclado a sí mismo. El usuario presiona una tecla, Claude Code decide cuál de sus controles es para, y se ejecuta el callback de ese control. Aparte de un [atajo de teclado de dígito en la banda](/docs/es/plugins/mods/reference#elements), eso sucede solo mientras su panel o banda tiene enfoque del teclado. El resto del tiempo, las teclas van al símbolo del sistema.508Tu mod nunca lee el teclado por sí mismo. El usuario pulsa una tecla, Claude Code decide a cuál de tus controles va dirigida y se ejecuta el callback de ese control. Aparte de una [tecla de acceso rápido numérica en la banda](/docs/es/plugins/mods/reference#elements), eso solo ocurre mientras tu panel o banda tiene el foco del teclado. El resto del tiempo, las teclas van al prompt.
497 509
498<h4 id="how-a-pane-gets-keyboard-focus">510<h4 id="how-a-pane-gets-keyboard-focus">
499 Cómo un panel obtiene enfoque del teclado511 Cómo obtiene un panel el foco del teclado
500</h4>512</h4>
501 513
502Un panel obtiene enfoque del teclado de una de tres maneras:514Un panel obtiene el foco del teclado cuando:
503 515
504* Su mod lo abre con `focus: true` desde un comando o una pulsación516* Tu mod lo abre con `focus: true` desde un comando o una pulsación
505* El usuario presiona Ctrl+X y luego Tab517* El usuario pulsa Ctrl+X y luego Tab
506* El usuario hace clic en él518* El usuario hace clic en él
507 519
508Claude Code otorga `focus: true` solo mientras el símbolo del sistema está vacío y nada más tiene enfoque del teclado. Un panel que se abre mientras el usuario está escribiendo no toma sus pulsaciones de teclas.520Claude Code concede `focus: true` solo mientras el prompt está vacío y nada más tiene el foco del teclado. Un panel que se abre mientras el usuario está escribiendo no se queda con sus pulsaciones de teclas.
509 521
510<h4 id="what-each-key-does">522<h4 id="what-each-key-does">
511 Qué hace cada tecla523 Qué hace cada tecla
512</h4>524</h4>
513 525
514Esta tabla enumera qué hace una tecla mientras su panel o banda tiene enfoque del teclado:526Esta tabla indica qué hace una tecla mientras tu panel o banda tiene el foco del teclado:
515 527
516| Tecla | Qué hace |528| Tecla | Qué hace |
517| :- | :- |529| :- | :- |
518| Tab | Se mueve al siguiente control |530| Tab | Pasa al siguiente control |
519| Arriba y Abajo | Se mueven entre controles mientras su dibujo cabe. Cuando el panel o banda tiene más filas de las que puede mostrar, los desplazan. |531| Arriba y Abajo | Se mueven entre controles mientras tu dibujo cabe. Cuando el panel o la banda tiene más filas de las que puede mostrar, lo desplazan. |
520| Enter | Presiona el `Button` enfocado, envía el `Input` enfocado o elige en un `Select` |532| Enter | Pulsa el `Button` con el foco, envía el `Input` con el foco o elige en un `Select` |
521| Atajo de teclado de un botón | Presiona ese botón. Mientras un `Input` tiene el enfoque, cada tecla imprimible va al campo. |533| La tecla de acceso rápido de un botón | Pulsa ese botón. Mientras un `Input` tiene el foco, toda tecla imprimible va al campo. |
522| Esc | Devuelve el enfoque del teclado al símbolo del sistema. Con `closeOnEscape: true`, también cierra el panel. |534| Esc | Devuelve el foco del teclado al prompt. Con `closeOnEscape: true`, también cierra el panel. |
523 535
524Un mod no puede vincular Tab o las teclas de flecha a nada más, por lo que un juego se dirige con `w`, `a`, `s` y `d`.536Un mod no puede asignar Tab ni las flechas a ninguna otra cosa, así que un juego se controla con `w`, `a`, `s` y `d`.
525 537
526<h4 id="set-a-hotkey-and-the-first-focus">538<h4 id="set-a-hotkey-and-the-first-focus">
527 Establecer un atajo de teclado y el primer enfoque539 Establecer una tecla de acceso rápido y el primer foco
528</h4>540</h4>
529 541
530Dos propiedades en un control deciden cómo el teclado lo alcanza:542Estas props de un control deciden cómo llega el teclado a él:
531 543
532* **`hotkey`**: para permitir que el usuario presione un `Button` con una tecla, dé un `hotkey` de un dígito o una letra minúscula, como en `hotkey: 'a'`544* **`hotkey`**: para que el usuario pueda pulsar un `Button` con una sola tecla, dale una `hotkey` de un dígito o una letra minúscula, como en `hotkey: 'a'`
533* **`autoFocus`**: para elegir qué control tiene el enfoque cuando se abre el panel, agregue `autoFocus: true` a él. Deje la propiedad fuera de los otros, porque Claude Code rechaza `autoFocus: false`.545* **`autoFocus`**: para elegir qué control tiene el foco cuando se abre el panel, añádele `autoFocus: true`. La prop solo acepta `true`, así que omítela en los demás controles.
534 546
535Cómo se muestra un atajo de teclado depende del botón y la aplicación:547Cómo se muestra una tecla de acceso rápido depende del botón y de la app:
536 548
537| Botón | En la terminal | En la aplicación de escritorio |549| Botón | En la terminal | En la app de escritorio |
538| :- | :- | :- |550| :- | :- | :- |
539| Con corchetes, el predeterminado | `[ Add one ]`, sin atajo de teclado mostrado | La etiqueta con una pequeña tecla al lado |551| Con corchetes, el predeterminado | `[ Add one ]`, sin mostrar la tecla de acceso rápido | La etiqueta con una pequeña tecla al lado |
540| Con `plain: true` | `1: One` | La etiqueta con una pequeña tecla al lado |552| Con `plain: true` | `1: One` | La etiqueta con una pequeña tecla al lado |
541 553
542En la terminal, nombre la tecla en la etiqueta de un botón entre corchetes, o use `plain: true`, para que el usuario pueda ver qué presionar. La [referencia de elementos](/docs/es/plugins/mods/reference#elements) tiene las otras reglas de `Button`: `action`, atajos de teclado de dígitos en la banda y dos botones en un atajo de teclado.554En la terminal, nombra la tecla en la etiqueta de un botón con corchetes, o usa `plain: true`, para que el usuario vea qué pulsar. La [referencia de elementos](/docs/es/plugins/mods/reference#elements) tiene las demás reglas de `Button`: `action`, las teclas de acceso rápido numéricas en la banda y dos botones con la misma tecla de acceso rápido.
543 555
544<h3 id="take-typed-input-and-draw-a-row-for-each-item">556<h3 id="take-typed-input-and-draw-a-row-for-each-item">
545 Tomar entrada escrita y dibujar una fila para cada elemento557 Recibir texto escrito y dibujar una fila por elemento
546</h3>558</h3>
547 559
548Muchos paneles son un campo de texto con una lista debajo. El ejemplo en esta sección es un panel de notas: escribe una nota y presiona Enter para agregarla, y cada nota tiene un botón `x` que la elimina. Con dos notas agregadas, la terminal dibuja el panel de esta manera:560Muchos paneles son un campo de texto con una lista debajo. El ejemplo de esta sección es un panel de notas: escribes una nota y pulsas Enter para añadirla, y cada nota tiene un botón `x` que la elimina. Con dos notas añadidas, la terminal dibuja el panel así:
549 561
550```text theme={null}562```text theme={null}
551╭──────────────────────────────────────────────────────────╮563╭──────────────────────────────────────────────────────────╮
555╰──────────────────────────────────────────────────────────╯567╰──────────────────────────────────────────────────────────╯
556```568```
557 569
558El ejemplo utiliza dos técnicas:570El ejemplo usa estas técnicas:
559 571
560* **Tomar entrada escrita**: un `Input` llama a `onSubmit(value)` con el texto del campo cuando el usuario presiona Enter, y `onInput(value)` en cada cambio572* **Recibir texto escrito**: un `Input` llama a `onSubmit(value)` con el texto del campo cuando el usuario pulsa Enter, y a `onInput(value)` en cada cambio
561* **Dibujar una lista**: asigne sus datos a una fila cada uno, y dé a cada botón de fila su propia `key`573* **Dibujar una lista**: asigna a cada dato una fila y dale al botón de cada fila su propia `key`
562 574
563Este hook dibuja el contenido del panel:575Este hook dibuja el contenido del panel:
564 576
620 632
621Para probar el panel:633Para probar el panel:
622 634
623* **Agregar una nota**: escriba una línea y presione Enter. La línea aparece como una nueva fila y el campo se vacía.635* **Añadir una nota**: escribe una línea y pulsa Enter. La línea aparece como una fila nueva y el campo se vacía.
624* **Eliminar una nota**: presione Tab hasta que el botón `x` de la nota tenga el enfoque, luego presione Enter. La `x` es la etiqueta del botón y no un atajo de teclado, por lo que escribir la letra no lo presiona.636* **Eliminar una nota**: pulsa Tab hasta que el botón `x` de la nota tenga el foco y luego pulsa Enter. La `x` es la etiqueta del botón y no una tecla de acceso rápido, así que escribir la letra no lo pulsa.
625 637
626Cada cambio sigue el mismo ciclo de renderizado que `hello-tabs`: el callback cambia `notes`, llama a `redraw` y guarda la lista en `$.store`.638Cada cambio sigue el mismo ciclo de renderizado que `hello-tabs`: el callback cambia `notes`, llama a `redraw` y guarda la lista en `$.store`.
627 639
628El campo se vacía después de cada envío debido a su propiedad `value`. `value` es el texto que el campo contiene cuando se dibuja, y la escritura del usuario lo reemplaza hasta que su hook dibuja el campo nuevamente. El ejemplo siempre dibuja el campo con `''`.640El campo se vacía después de cada envío debido a su prop `value`. `value` es el texto que contiene el campo cuando se dibuja, y lo que escribe el usuario lo reemplaza hasta que tu hook vuelve a dibujar el campo. El ejemplo siempre dibuja el campo con `''`.
629 641
630El ejemplo guarda las notas y no las carga. Para traerlas de vuelta en la siguiente sesión, léalas en un hook `session.start`, de la manera que `hello-tabs` lee `count`.642El ejemplo guarda las notas pero no las carga. Para recuperarlas en la siguiente sesión, léelas en un hook `session.start`, de la misma forma en que `hello-tabs` lee `count`.
631 643
632Tres propiedades componen la línea del campo, `Note: Type a note and press Enter ⏎ add`:644Estas props forman la línea del campo, `Note: Type a note and press Enter ⏎ add`:
633 645
634| Propiedad | En el ejemplo | Qué es |646| Prop | En el ejemplo | Qué es |
635| :- | :- | :- |647| :- | :- | :- |
636| `label` | `Note` | El texto antes del campo. La terminal dibuja `: ` después. |648| `label` | `Note` | El texto antes del campo. La terminal dibuja `: ` después de él. |
637| `placeholder` | `Type a note and press Enter` | Texto atenuado que se muestra mientras el campo está vacío |649| `placeholder` | `Type a note and press Enter` | Texto atenuado que se muestra mientras el campo está vacío |
638| `submitLabel` | `add` | La palabra después de `⏎` que dice qué hace Enter |650| `submitLabel` | `add` | La palabra después de `⏎` que indica qué hace Enter |
639 651
640Enviar un `Input` no inicia un turno a menos que su callback llame a [`$.prompt.submit`](/docs/es/plugins/mods/api#start-a-turn-from-a-background-job).652Enviar un `Input` no inicia un turno a menos que tu callback llame a [`$.prompt.submit`](/docs/es/plugins/mods/api#start-a-turn-from-a-background-job).
641 653
642<h2 id="redraw-when-something-changes">654<h2 id="redraw-when-something-changes">
643 Redibujar un sitio655 Volver a dibujar un sitio
644</h2>656</h2>
645 657
646Un dibujo es una instantánea: muestra lo que devolvió su hook `ui.render` la última vez que se ejecutó. Para mostrar algo nuevo, el hook tiene que ejecutarse de nuevo. Claude Code lo ejecuta de nuevo para algunos cambios, y su mod solicita el resto.658Un dibujo es una instantánea: muestra lo que devolvió tu hook `ui.render` la última vez que se ejecutó. Para mostrar algo nuevo, el hook tiene que ejecutarse de nuevo. Claude Code lo vuelve a ejecutar ante algunos cambios, y tu mod lo solicita para el resto.
647 659
648<h3 id="when-claude-code-redraws-without-being-asked">660<h3 id="when-claude-code-redraws-without-being-asked">
649 Cuándo Claude Code redibuja sin ser solicitado661 Cuándo Claude Code vuelve a dibujar sin que se lo pidas
650</h3>662</h3>
651 663
652Claude Code ejecuta su hook `ui.render` de nuevo cuando cambian los props del sitio o cambia el ancho de la terminal. No ejecuta el hook en un temporizador y no puede saber cuándo cambia una variable en su módulo.664Claude Code vuelve a ejecutar tu hook `ui.render` cuando cambian las props del sitio o cuando cambia el ancho de la terminal. No ejecuta el hook con un temporizador, y no puede saber cuándo cambia una variable de tu módulo.
653 665
654<h3 id="redraw-when-your-data-changes">666<h3 id="redraw-when-your-data-changes">
655 Redibujar cuando sus datos cambian667 Volver a dibujar cuando cambian tus datos
656</h3>668</h3>
657 669
658Para que sus sitios se redibjen después de que sus propios datos cambien, llame a `$.ui.invalidate('ui.render')`. Este panel cuenta pulsaciones. La devolución de llamada del botón cambia `count` y luego solicita un redibujado:670Para que tus sitios se vuelvan a dibujar después de que cambien tus propios datos, llama a `$.ui.invalidate('ui.render')`. Este panel cuenta pulsaciones. El callback del botón cambia `count` y luego solicita que se vuelva a dibujar:
659 671
660```javascript theme={null}672```javascript theme={null}
661let count = 0673let count = 0
682})694})
683```695```
684 696
685Cada pulsación aumenta el número en el panel. El ejemplo [`hello-tabs`](#build-a-pane-with-tabs) envuelve la misma llamada en su función `redraw`.697Cada pulsación aumenta el número en el panel. El [ejemplo `hello-tabs`](#build-a-pane-with-tabs) envuelve la misma llamada en su función `redraw`.
686 698
687Un valor que mantiene en [`$.state`](#keep-a-value-in-\$-state) no necesita la llamada, porque escribir el valor redibuja los sitios que lo leen.699Un valor que guardas en [`$.state`](#keep-a-value-in-\$-state) no necesita la llamada, porque al escribir el valor se vuelven a dibujar los sitios que lo leen.
688 700
689<h3 id="redraw-on-a-timer">701<h3 id="redraw-on-a-timer">
690 Redibujar en un temporizador702 Volver a dibujar con un temporizador
691</h3>703</h3>
692 704
693Para mantener un reloj, una cuenta atrás o un valor de fuera de la sesión actual, redibuje según un cronograma. Inicie un temporizador en el hook `session.start` del módulo. Si el módulo ya tiene uno, como lo hace `hello-tabs`, agregue la línea [`$.clock.every`](/docs/es/plugins/mods/api#run-work-in-the-background) a él:705Para mantener actualizado un reloj, una cuenta regresiva o un valor externo a la sesión, vuelve a dibujar de forma programada. Inicia un temporizador en el hook `session.start` del módulo. Si el módulo ya tiene uno, como ocurre con `hello-tabs`, agrégale la línea de [`$.clock.every`](/docs/es/plugins/mods/api#run-work-in-the-background):
694 706
695```javascript theme={null}707```javascript theme={null}
696on('session.start', async ($, e, next) => {708on('session.start', async ($, e, next) => {
700})712})
701```713```
702 714
703Claude Code ahora ejecuta su hook `ui.render` una vez por segundo. El temporizador se detiene cuando el módulo se recarga, y la nueva copia del módulo inicia el suyo propio.715Ahora Claude Code ejecuta tu hook `ui.render` una vez por segundo. El temporizador se detiene cuando el módulo se recarga, y la nueva instancia del módulo inicia el suyo.
704 716
705<h3 id="how-often-a-site-can-redraw">717<h3 id="how-often-a-site-can-redraw">
706 Con qué frecuencia se puede redibujar un sitio718 Con qué frecuencia puede volver a dibujarse un sitio
707</h3>719</h3>
708 720
709Claude Code limita la frecuencia con la que redibuja un sitio, por lo que su mod puede llamar a `$.ui.invalidate` con la frecuencia que cambien sus datos. El panel visible y la banda tienen un límite más alto que otros sitios, y la [tabla de límites](/docs/es/plugins/mods/reference#limits) tiene los números.721Claude Code limita la frecuencia con la que se vuelve a dibujar un sitio, así que tu mod puede llamar a `$.ui.invalidate` tan seguido como cambien sus datos. Para saber con qué frecuencia puede volver a dibujarse cada sitio, consulta la [tabla de límites](/docs/es/plugins/mods/reference#limits).
710 722
711Las llamadas que llegan más rápido que el límite se combinan en un redibujado. Ese redibujado ejecuta su hook una vez, y el hook lee sus datos tal como están en ese momento, por lo que se muestra el valor más reciente y los valores intermedios no se muestran. Una animación no puede ejecutarse más rápido que el límite.723Las llamadas que llegan más rápido que el límite se agrupan en un solo redibujado. Ese redibujado ejecuta tu hook una vez, y el hook lee tus datos tal como están en ese momento, así que se muestra el valor más reciente y no los intermedios. Una animación no puede ejecutarse más rápido que el límite.
712 724
713<h2 id="keep-state">725<h2 id="keep-state">
714 Mantener estado726 Conservar el estado
715</h2>727</h2>
716 728
717Un mod tiene tres lugares para mantener un valor, y difieren en cuánto tiempo dura el valor: hasta que el módulo se recarga, hasta que termina la sesión o de una sesión a la siguiente. Elija según cuánto tiempo tenga que durar el valor:729El lugar donde un mod guarda un valor decide cuánto dura ese valor: hasta que el módulo se recarga, hasta que termina la sesión o de una sesión a la siguiente. Elige según cuánto tiempo tiene que durar el valor:
718 730
719| Mantenerlo en | Dura hasta | Úselo para |731| Guárdalo en | Dura hasta que | Úsalo para |
720| :- | :- | :- |732| :- | :- | :- |
721| Una variable a nivel de módulo | El módulo se recarga, lo que sucede cada vez que guarda un archivo durante el desarrollo | Valores que puede perder, como `tab` en `hello-tabs` |733| Una variable a nivel de módulo | El módulo se recarga, lo que ocurre cada vez que guardas un archivo durante el desarrollo | Valores que puedes perder, como `tab` en `hello-tabs` |
722| `$.state` | La sesión termina, o el usuario ejecuta `/clear`, `/resume` o `/branch` | Valores de los que depende un dibujo que deben sobrevivir a una recarga |734| `$.state` | La sesión termina, o el usuario ejecuta `/clear`, `/resume` o `/branch` | Valores de los que depende un dibujo y que deben sobrevivir a una recarga |
723| `$.store` | Su mod lo elimina, o ninguna sesión lee o escribe el almacén durante [`cleanupPeriodDays`](/docs/es/settings-reference#cleanupperioddays). El almacén es un almacén de clave-valor, guardado como un archivo JSON de su propio plugin bajo `~/.claude/plugins/store/`. | Configuración, historial, cualquier cosa que el usuario espera encontrar la próxima vez |735| `$.store` | Tu mod lo elimina, o ninguna sesión lee ni escribe el almacén durante [`cleanupPeriodDays`](/docs/es/settings-reference#cleanupperioddays). El almacén es un almacén de clave-valor, guardado como un archivo JSON propio de tu plugin en `~/.claude/plugins/store/`. | Ajustes, historial, cualquier cosa que el usuario espera encontrar la próxima vez |
724 736
725`$.store.get(key)` se resuelve en el valor o `undefined`, y `$.store.set(key, value)` toma cualquier valor JSON.737`$.store.get(key)` se resuelve con el valor o `undefined`, y `$.store.set(key, value)` acepta cualquier valor JSON.
726 738
727<h3 id="keep-a-value-in-state">739<h3 id="keep-a-value-in-$-state">
728 Mantener un valor en `$.state`740 Guardar un valor en `$.state`
729</h3>741</h3>
730 742
731`$.state` mantiene valores durante la duración de una sesión, y se redibuja por usted. Es estado reactivo: un hook `ui.render` que lee un valor se suscribe a él, por lo que Claude Code redibuja ese sitio cada vez que escribe el valor, y no llama a `$.ui.invalidate`. Un valor en `$.state` también sobrevive a una recarga del módulo, lo que una variable no.743`$.state` conserva valores durante una sesión y vuelve a dibujar por ti. Es estado reactivo: un hook `ui.render` que lee un valor se suscribe a él, así que Claude Code vuelve a dibujar ese sitio cada vez que escribes el valor, y no llamas a `$.ui.invalidate`. Un valor en `$.state` también sobrevive a una recarga del módulo, cosa que una variable no hace.
732 744
733Para configurarlo, declare sus valores, apunte su manifiesto a la declaración, luego defina y use cada valor. Los ejemplos mueven el `count` de `hello-tabs` a `$.state`.745Para configurarlo, declara tus valores, apunta tu manifiesto a la declaración y luego define y usa cada valor. Los ejemplos mueven el `count` de `hello-tabs` a `$.state`.
734 746
735<h4 id="declare-the-values">747<h4 id="declare-the-values">
736 Declarar los valores748 Declarar los valores
737</h4>749</h4>
738 750
739Declare los valores en un archivo de tipos. La clave externa es el nombre de su plugin, y cada entrada bajo ella es un valor y su tipo. Guarde esto como `hello-tabs/types/index.d.ts`:751Declara los valores en un archivo de declaración de tipos. La clave exterior es el nombre de tu plugin, y cada entrada debajo de ella es un valor y su tipo. Guarda esto como `hello-tabs/types/index.d.ts`:
740 752
741```typescript hello-tabs/types/index.d.ts theme={null}753```typescript hello-tabs/types/index.d.ts theme={null}
742declare module 'claude-code' {754declare module 'claude-code' {
753 Apuntar el manifiesto a la declaración765 Apuntar el manifiesto a la declaración
754</h4>766</h4>
755 767
756Para permitir que `claude plugin validate` verifique su código contra ese archivo, agregue un campo `types` al manifiesto con su ruta:768Para que `claude plugin validate` compruebe tu código con ese archivo, agrega al manifiesto un campo `types` con su ruta:
757 769
758```json hello-tabs/.claude-plugin/plugin.json theme={null}770```json hello-tabs/.claude-plugin/plugin.json theme={null}
759{771{
769 Definir, leer y escribir un valor781 Definir, leer y escribir un valor
770</h4>782</h4>
771 783
772En su módulo, defina cada valor con un predeterminado, léalo mientras dibuja y escríbalo desde un callback. `atom` nombra un valor y su predeterminado, `read` lo devuelve y `update` lo escribe. Los tres ayudantes llaman a `$.state.get` y `$.state.set` por usted:784En tu módulo, define cada valor con un valor predeterminado, léelo mientras dibujas y escríbelo desde un callback. `atom` nombra un valor y su valor predeterminado, `read` lo devuelve y `update` lo escribe. Los tres helpers llaman a `$.state.get` y `$.state.set` por ti:
773 785
774```javascript theme={null}786```javascript theme={null}
775import { atom, read, update } from 'claude-code'787import { atom, read, update } from 'claude-code'
784onPress: () => update($, count, (value) => value + 1)796onPress: () => update($, count, (value) => value + 1)
785```797```
786 798
787Porque el hook `ui.render` leyó `count`, Claude Code ejecuta el hook nuevamente cada vez que el botón lo escribe.799Como el hook `ui.render` leyó `count`, Claude Code vuelve a ejecutar el hook cada vez que el botón lo escribe.
788 800
789Tres reglas se aplican al código:801Estas reglas se aplican al código:
790 802
791* **Escriba `plugin` y `key` como cadenas literales**: `claude plugin validate` las lee de su fuente803* **Escribe `plugin` y `key` como literales de cadena**: `claude plugin validate` los lee de tu código fuente
792* **Declare cada valor en el archivo de tipos**: de lo contrario, la validación falla con `hello-tabs.count is not declared`804* **Declara cada valor en el archivo de declaración de tipos**: de lo contrario, la validación falla con `hello-tabs.count is not declared`
793* **Escriba desde un callback u otro hook de evento**: un hook `ui.render` puede leer estado y no puede escribirlo, así que escriba desde `onPress`, `onSubmit` u otro hook de evento805* **Escribe desde un callback o desde el hook de otro evento**: un hook `ui.render` puede leer el estado pero no puede escribirlo, así que escribe desde `onPress`, `onSubmit` o un hook para otro evento
794 806
795<h4 id="change-hello-tabs-to-use-state">807<h4 id="change-hello-tabs-to-use-$-state">
796 Cambiar `hello-tabs` para usar `$.state`808 Cambiar `hello-tabs` para usar `$.state`
797</h4>809</h4>
798 810
799Para mover `count` en `hello-tabs` a `$.state`, cambie cada línea que lo use:811Para mover `count` de `hello-tabs` a `$.state`, cambia cada línea que lo usa:
800 812
801* **En la parte superior del módulo**: agregue la línea `import` y reemplace `let count = 0` con la línea `atom`813* **Al principio del módulo**: agrega la línea `import` y reemplaza `let count = 0` por la línea `atom`
802* **En el hook `ui.render`**: agregue la línea `read` antes de `tabButton` y dibuje `'Count: ' + n` en el `Text`814* **En el hook `ui.render`**: agrega la línea `read` antes de `tabButton` y dibuja `'Count: ' + n` en el `Text`
803* **En el botón Add one**: reemplace `onPress` con el de [Guardar desde más de una sesión](#save-from-more-than-one-session), que guarda el recuento además de escribirlo815* **En el botón Add one**: reemplaza `onPress` por el de [Guardar desde más de una sesión](#save-from-more-than-one-session), que guarda el conteo además de escribirlo
804* **En el hook `session.start`**: reemplace las dos líneas que leen `saved` con la llamada `loadCount` de [Cargar un valor guardado nuevamente después de `/clear`](#load-a-saved-value-again-after-clear)816* **En el hook `session.start`**: reemplaza las dos líneas que leen `saved` por la llamada a `loadCount` de [Volver a cargar un valor guardado después de `/clear`](#load-a-saved-value-again-after-clear)
805 817
806Mantenga `redraw` para los botones de pestaña, porque `tab` sigue siendo una variable.818Conserva `redraw` para los botones de pestaña, porque `tab` sigue siendo una variable.
807 819
808<h3 id="load-a-saved-value-again-after-clear">820<h3 id="load-a-saved-value-again-after-clear">
809 Cargar un valor guardado nuevamente después de `/clear`821 Volver a cargar un valor guardado después de `/clear`
810</h3>822</h3>
811 823
812Si su mod copia un valor guardado de `$.store` a `$.state` en `session.start`, tiene que copiarlo nuevamente después de `/clear`, `/resume` o `/branch`. Esos comandos devuelven cada valor de `$.state` a su predeterminado, y `session.start` no se dispara nuevamente. [`classic.SessionStart`](/docs/es/plugins/mods/events#hook-the-settings-hook-events) se dispara después de cada uno de ellos, con `e.source` establecido en `clear`, `resume` o `fork`, así que copie el valor nuevamente en un hook en él. De lo contrario, su dibujo muestra el predeterminado, y un callback que guarda el valor de `$.state` escribe el predeterminado sobre lo que almacenó.824Si tu mod copia un valor guardado de `$.store` a `$.state` en `session.start`, tiene que volver a copiarlo después de `/clear`, `/resume` o `/branch`. Esos comandos restablecen cada valor de `$.state` a su valor predeterminado, y `session.start` no se vuelve a disparar. [`classic.SessionStart`](/docs/es/plugins/mods/events#hook-the-settings-hook-events) sí se dispara después de cada uno de ellos, con `e.source` establecido en `clear`, `resume` o `fork`, así que vuelve a copiar el valor en un hook sobre él. De lo contrario, tu dibujo muestra el valor predeterminado, y un callback que guarda el valor de `$.state` escribe el valor predeterminado encima de lo que almacenaste.
813 825
814Este código carga `count` de ambos hooks. Se basa en la versión de `$.state` de `hello-tabs`, donde `count` es un átomo y `update` se importa. Ponga `loadCount` arriba de `register` y agregue la llamada `loadCount` al hook `session.start` que ya tiene. `classic.SessionStart` también se dispara al inicio y después de la compactación, que no reinicia `$.state`, por lo que el filtro en `source` mantiene el hook a los tres reiniciados:826Este código carga `count` desde ambos hooks. Se basa en la versión de `hello-tabs` con `$.state`, donde `count` es un atom y `update` está importado. Coloca `loadCount` encima de `register` y agrega la llamada a `loadCount` al hook `session.start` que ya tienes. `classic.SessionStart` también se dispara al inicio y después de la compactación, que no restablece `$.state`, así que el filtro sobre `source` limita el hook a los tres restablecimientos:
815 827
816```javascript theme={null}828```javascript theme={null}
817// Copy the saved count from $.store into $.state, or 0 if nothing is saved829// Copy the saved count from $.store into $.state, or 0 if nothing is saved
833})845})
834```846```
835 847
836Con ambos hooks en su lugar, el panel muestra el recuento guardado después de `/clear` y no `0`, y la siguiente pulsación de **Add one** suma al recuento guardado.848Con ambos hooks en su lugar, el panel muestra el conteo guardado después de `/clear` y no `0`, y la siguiente pulsación de **Add one** suma al conteo guardado.
837 849
838`loadCount` escribe el valor almacenado sobre el que está en `$.state`, y `session.start` se dispara nuevamente cada vez que el módulo se recarga. Para mantener el almacén actualizado, guarde en cada cambio, como lo hace el botón **Add one**.850`loadCount` escribe el valor almacenado encima del que está en `$.state`, y `session.start` se vuelve a disparar cada vez que el módulo se recarga. Para evitar que el almacén se quede atrás, guarda en cada cambio, como hace el botón **Add one**.
839 851
840Para verificar la recarga sin una sesión, [pruebe el dibujo después de `/clear`](/docs/es/plugins/mods/test#test-a-drawing-after-clear).852Para comprobar la recarga sin una sesión, [prueba el dibujo después de `/clear`](/docs/es/plugins/mods/test#test-a-drawing-after-clear).
841 853
842<h3 id="save-from-more-than-one-session">854<h3 id="save-from-more-than-one-session">
843 Guardar desde más de una sesión855 Guardar desde más de una sesión
844</h3>856</h3>
845 857
846Cada sesión en su máquina que ejecuta su mod comparte un `$.store`. Un `get` seguido de un `set` no es atómico. Cuando dos sesiones leen un valor, lo cambian y lo escriben de vuelta, compiten, y la segunda escritura reemplaza la primera.858Todas las sesiones de tu máquina que ejecutan tu mod comparten un mismo `$.store`. Un `get` seguido de un `set` no es atómico. Cuando dos sesiones leen cada una un valor, lo cambian y lo vuelven a escribir, compiten entre sí, y la segunda escritura reemplaza a la primera.
847 859
848Dos opciones hacen que sea menos probable:860Para que eso sea menos probable:
849 861
850* **Dé a cada elemento su propia clave**: un `set` cambia solo su propia clave, por lo que las sesiones que escriben claves diferentes no se sobrescriben entre sí862* **Dale a cada elemento su propia clave**: un `set` cambia solo su propia clave, así que las sesiones que escriben claves distintas no se sobrescriben entre sí
851* **Lea nuevamente justo antes de escribir**: para un valor que varias sesiones cambian, `get` la clave en el callback y construya el nuevo valor a partir de eso, no de una copia que cargó en `session.start`. La escritura de otra sesión se pierde si llega entre su `get` y su `set`.863* **Vuelve a leer justo antes de escribir**: para un valor que cambian varias sesiones, haz `get` de la clave en el callback y construye el nuevo valor a partir de eso, no de una copia que cargaste en `session.start`. La escritura de otra sesión igual se pierde si ocurre entre tu `get` y tu `set`.
852 864
853Este botón suma uno a lo que el almacén contiene ahora, luego actualiza el dibujo:865Este botón suma uno a lo que el almacén contenga en ese momento y luego actualiza el dibujo:
854 866
855```javascript theme={null}867```javascript theme={null}
856onPress: async () => {868onPress: async () => {
862}874}
863```875```
864 876
865Si una segunda sesión ha presionado su propio botón tres veces desde que esta sesión comenzó, esta pulsación muestra y guarda un recuento que incluye esos tres.877Si una segunda sesión ha pulsado su propio botón tres veces desde que comenzó esta sesión, esta pulsación muestra y guarda un conteo que incluye esas tres.
866 878
867<h2 id="next-steps">879<h2 id="next-steps">
868 Próximos pasos880 Próximos pasos
869</h2>881</h2>
870 882
871* [Reaccionar a eventos](/docs/es/plugins/mods/events): alimente su dibujo desde llamadas de herramientas y turnos883* [Reacciona a eventos](/docs/es/plugins/mods/events): alimenta tu dibujo a partir de llamadas a herramientas y turnos
872* [Usar la API de mods](/docs/es/plugins/mods/api): alimente su dibujo desde temporizadores y llamadas de modelo884* [Usa la API de mods](/docs/es/plugins/mods/api): alimenta tu dibujo a partir de temporizadores y llamadas al modelo
873* [Probar un dibujo](/docs/es/plugins/mods/test#test-a-drawing): presione sus botones desde una prueba, en más de una superficie885* [Prueba un dibujo](/docs/es/plugins/mods/test#test-a-drawing): presiona tus botones desde una prueba, en más de una superficie
874* [Sitios de renderizado](/docs/es/plugins/mods/reference#render-sites) y [elementos](/docs/es/plugins/mods/reference#elements): propiedades de cada sitio y propiedades de cada elemento886* [Puntos de renderizado](/docs/es/plugins/mods/reference#render-sites) y [elementos](/docs/es/plugins/mods/reference#elements): las props de cada punto de renderizado y las props de cada elemento