Novedades
Lo que llegó en cada versión, sacado del registro de cambios (CHANGELOG.md) que viaja en el paquete de la 0.35.0. Una entrada marcada Rompe cambia algo que tu código tiene que tocar al actualizar: npx amset-ui migrate se encarga de casi todo.
0.35.0, del 3 de noviembre de 2026
Table: varios rangos de celdas sueltos a la vez (AMS-2351). ConcellSelection, Ctrl/Cmd+clic añade otro rango sin soltar los que hay, y sobre una celda ya elegida la quita (su rango queda partido en lo que la rodeaba); Mayús+F8 empieza otro rango desde la celda enfocada, y Mayús+flechas o Mayús+clic estiran el último. Los rangos que se tocan se ven como uno, con el contorno por fuera. Ctrl/Cmd+C copia varios rangos solo si comparten filas (van lado a lado) o columnas (uno debajo de otro), como en Excel; si no cuadran, no se toca el portapapeles y se dice por qué. Un valor suelto pegado rellena todas las celdas de todos los rangos, y varias celdas no se pegan sobre varios rangos.onCellRangeChangesigue avisando del rango activo (el último) yonCellRangesChangeda la lista entera. Sin Ctrl ni F8, todo queda como antes. Y el contorno del rango se ve por fin: el borde a cero de la regla general ganaba por especificidad a los 2 px de cada lado y solo se pintaba el fondo (medido en Chromium; una prueba de navegador lo fija). Y se ve bien también en oscuro: el relleno es ahora--amset-accent-soft, el mismo de la fila marcada (antes, un 12 % de acento que en oscuro casi no se distinguía de la fila), y el contorno--amset-accent-text(el acento a secas daba 2,9:1 frente a la fila en oscuro; ahora 7,1:1 en claro y 8,6:1 en oscuro).Table: salir de una edición que no se puede guardar, rango sin grupos plegados, cuenta de filas paginadas, error de carga, elegir todo lo filtrado, pegado por lotes y ajustar el ancho al contenido (AMS-2351). Si se sale de una celda en edición con algo que no vale (o el guardado falla), la edición se descarta y la tabla dice por qué; antes el campo se quedaba abierto con el error y el foco en otro sitio, donde ni Intro ni Escape le llegaban. Con el foco dentro, el error se sigue enseñando para corregirlo. Mayús+flechas ya no cae en las filas de un grupo plegado. Paginada, la rejilla llevaaria-rowcountcon el total y cada fila suaria-rowindexreal (conpagination.total, el del servidor).error(contrue, el texto de serie) sustituye a las filas por un aviso yonRetryle pone «Reintentar». Con la página entera marcada y más filas en otras páginas sale «Elegir las N filas», como en el correo.onCellsEditrecibe en una sola llamada todo lo que se pega. Doble clic en el tirador de una columna la ajusta a lo más ancho de su contenido.DataImportWizard: importar por tandas con avance y cancelar, CSV en UTF-16, hojas ocultas, celdas combinadas y errores de fórmula (AMS-2352). ConbatchSize,onImportse llama una vez por tanda, con una barra de avance y «Cancelar la importación»; su segundo argumento traebatch,batchesy unasignalque se dispara al cancelar. Si falla o se cancela una tanda que no es la primera, el resultado dice cuántas filas entraron y cuántas quedan. Cambio:onImportrecibe siempre ese segundo argumento, también sin tandas ({ batch: 1, batches: 1, signal }); una prueba que compare la llamada con un solo argumento tiene que añadirlo. Los CSV en UTF-16 (el «Texto Unicode» de Excel), con o sin marca, se leen bien. Una hoja oculta del libro se marca «(oculta)» y no es la que se abre. El valor de una celda combinada se copia a las filas con datos que cubre. Una celda con#DIV/0!,#N/Ay demás errores de Excel es un error de la fila, no un dato.FileManager: subir de uno en uno con cancelar y reintentar, resultado por elemento al borrar o mover, tamaño máximo y nombres repetidos (AMS-2353).onUploadFilesube cada fichero por separado: sale en una lista con su barra, se puede cancelar (se dispara lasignal) y, si falla, se queda con el motivo para reintentarlo o quitarlo.onDeleteyonMovepueden devolver{ failed: [{ id, message }] }: lo que falló se nombra y se queda elegido, y lo demás se da por hecho.maxSize(en bytes) yrejectDuplicatesdejan fuera lo que pasa del tamaño o se llama igual que algo de la carpeta, también con el botón, y se dice cuántos y por qué.screenUploadshace ese filtro fuera del componente.SchemaForm: listas de posiciones fijas de draft-07, objetos sin campos, valores que llegan tarde, «sin cambios» tras guardar, claves con punto o numéricas, y error y foco de una lista (AMS-2354).items: [a, b]conadditionalItems(draft-07, OpenAPI 3.0) se lee comoprefixItemsy se pinta un campo por posición. Un objeto sin nada que pedir ya no pinta un grupo vacío. SiinitialValuescambia (datos que llegan de una petición), el formulario los toma sin pisar lo que ya se cambió a mano. Un envío que sale bien cuenta como guardado:onChangevuelve adirty: false. Una propiedad llamadaversion.mayoro2024se edita y se envía en su sitio; antes react-hook-form la leía como ruta. El error de una lista describe su grupo (aria-describedby) y al quitar una fila el foco pasa a la que ocupa su sitio, o a «Añadir» si no queda ninguna.DataImportWizard: la fila de cada error es la del fichero, tope de descompresión y avisos que dicen el motivo (AMS-2352). Los errores citan la línea real del CSV (contando las líneas en blanco y las de dentro de un campo entre comillas) y la fila de Excel, y una corrección se escribe en la fila que toca aunque haya huecos antes. Un.xlsxcon una celda en la última fila de Excel ya no crea un millón de filas vacías en memoria, el ZIP tiene un tope de 256 MB descomprimidos y un fichero de más de 64 MB dentro del libro rompe la lectura en vez de saltarse en silencio (antes unsharedStringsenorme dejaba las celdas vacías). SionImportfalla, el aviso dice su motivo; la revisión deja ver más filas, de 50 en 50; avisa si una misma columna alimenta a dos campos; con dos ficheros elegidos seguidos gana el último, y al terminar el foco pasa al resultado.FileManageryMediaManager: lo soltado pasa poraccept, barra de varias subidas, atajos y «Cargar más» (AMS-2353). Lo que se suelta encima pasa el mismo filtro que lo que se elige con el botón y se dice cuántos ficheros se quedan fuera. Con dos subidas a la vez, la primera que acaba ya no esconde la barra de la otra: la barra da la media de las que siguen.nameProblemrechaza los nombres reservados de Windows (CON,NUL,COM1…), los acabados en punto y los de más de 255 caracteres. EnFileManager, Ctrl+A elige lo que se ve, Supr pide borrar con su confirmación, F2 renombra la entrada con el foco y Mayús+clic elige un rango; la confirmación dice qué se borra, no solo cuántos, yhasMorecononLoadMorepone «Cargar más». ElonKeyDownde fuera se sigue llamando.Table: vacíos al final en los dos sentidos, selección por página y sin filas ocultas, búsqueda en árbol y fechas locales al exportar (AMS-2351). Los vacíos (y unNaN) van al final también en orden descendente, en la tabla y en el árbol. La casilla de la cabecera marca o desmarca solo la página, sin borrar lo elegido en otras, y lo que el filtro, la búsqueda o los datos esconden deja de contar como elegido: las acciones nunca reciben filas que no se ven (con datos del servidor no se recorta).searchablebusca también en las filas de un árbol, y conserverSideo la paginación del servidor el cuadro solo sale cononSearchChange, que avisa del texto. Una columna que llega después de montar sale a la vista (salvohiddenByDefault) ypagination.sizese aplica también después de montar. Un guardado de celda que rechaza con algo que no es unErrorya no deja el editor ocupado. Cambio de formato: el CSV exporta las fechas con hora en la hora local y con su desfase (2026-02-01T11:30:00+01:00), como se ven en pantalla, en vez de en UTC.SchemaFormy su validador: patrones que cuelgan,multipleOfcon importes, palabras que se ignoraban y envíos que fallaban en silencio (AMS-2354). Unpatterncon una repetición anidada sin separador ((a+)+,(\w+\s?)*) se rechaza comoSchemaFormError: con 26 letras tardaba 27 segundos en cada tecla.multipleOf0,01 vale ya para importes grandes. Una propiedad heredada (constructor) no cuenta como presente. Se validanpatternProperties(yadditionalProperties: falseya no rechaza lo que casa),propertyNames,minProperties,maxProperties,prefixItems,contains,minContains,maxContainsynullable. Un$refconpropertiesorequiredal lado suma los dos. Cambio de clave: los errores generales del esquema van aroot(ROOT_FIELD), la misma que los del servidor; antes iban araiz. Un envío que lanza se dice en el formulario, cambiar de rama de unoneOfconserva los campos comunes y una fila nueva de una lista de objetos llega con susdefault.
0.34.0, del 2 de noviembre de 2026
MediaManager: soltar ficheros encima, «Cargar más» y barra de avance;FileManagercon barra de avance (AMS-2328).onUploadrecibe ahora un segundo argumento,onProgress: llámalo con un valor de 0 a 100 y el gestor enseña una barra con el avance hasta que la subida acabe o falle (las funciones que no lo usan siguen funcionando igual).MediaManageracepta ficheros soltados sobre la biblioteca, con el borde marcado mientras se arrastra, y conhasMoreyonLoadMorepone el botón «Cargar más» al final de la cuadrícula para pedir la siguiente tanda al servidor.SchemaForm: elegir la rama de unoneOfoanyOfde objetos sin campo que las distinga (AMS-2327). Cuando las ramas son objetos con campos propios y ninguna se reconoce por un valorconst, el formulario pone un desplegable «Tipo» (con eltitlede cada rama) y enseña solo los campos de la elegida; al cambiar de rama se vacían los de la anterior para que no choquen al validar ni se envíen.
0.33.0, del 2 de noviembre de 2026
- Selectores con miles de opciones: solo se pintan las que se ven, y la barra sigue a la opción activa con el teclado (AMS-2326).
Select,MultiSelectyComboBoxcomparten la lista, que con más de 300 opciones de una sola línea (sin grupos, sin segunda línea y sinrenderOption) pinta unas veinte filas y deja el sitio de las demás con el relleno de la propia lista. Las flechas, Re Pag, Av Pag, Inicio, Fin y escribir para saltar llevan la barra hasta la opción activa aunque no esté pintada, y al reabrir la elegida se ve. Cada fila llevaaria-posinsetyaria-setsizepara que el lector diga «1.234 de 5.000». Con grupos, descripciones orenderOptionno se recorta nada, porque el hueco supone filas del mismo alto. Nueva historia «Lista larga» ye2e/select-long-list.spec.ts, medido en Chromium con 5.000 opciones: de 15 a 22 filas pintadas y la activa siempre a la vista.
0.32.0, del 1 de noviembre de 2026
Calendar: saltar de mes y de año desde el título conmonthJump(AMS-2325). ConmonthJumpel título del mes es un botón que abre una vista con los doce meses del año; pulsando el año se ve un bloque de doce años. Los botones de los lados cambian de año o de bloque, los meses fuera deminValueymaxValuesalen apagados y elegir uno devuelve la rejilla en ese mes (y avisa cononFocusChange). Solo con un mes a la vista y en calendario gregoriano; en otro, o con varios meses, el título sigue siendo texto.MultiSelect: modo casillas y tope de opciones pintadas;TreeSelect: búsqueda (AMS-2326).checklistdeja la lista abierta mientras se marca, enseña lo elegido con su marca (aria-multiselectable), quita una ya elegida al pulsarla y, al llegar amax, apaga las que faltan en vez de esconderlas.maxVisibleOptionspinta como mucho esas opciones en listas de miles y avisa de cuántas se ven.TreeSelectconsearchablepone un cuadro de búsqueda que deja lo que coincide con el camino hasta ello abierto. El texto libre delComboBoxya existía (allowsCustomValue).VirtualListno se ha metido dentro de las listas desplegables: el recorte opcional demaxVisibleOptionses lo que hay.Table: editores de celda de fecha, casilla y propio, y validación con el servidor (AMS-2324).editor.typesumadate(un selector de fecha que guarda AAAA-MM-DD),checkbox(guarda un booleano) ycustom, con unrenderque recibecommit,cancel,errorysaving.validateAsynccomprueba lo escrito contra el servidor después devalidate, con la celda ocupada y «Comprobando…», y si devuelve un motivo la edición se queda abierta con él; también se aplica a cada celda que se pega.CellEdit.valueahora puede ser un booleano (antesstring | number | null): quien lo asigne a una variable de ese tipo tiene que ampliarla. Pegar entiende «sí», «no», «true» y «false» en una casilla, AAAA-MM-DD en una fecha yparseen un editor propio.Boardtáctil,Timelineeditable yDockLayoutcon el centro dividido (AMS-2333). EnBoard, el asa de cada tarjeta se arrastra con el dedo (eventos de puntero,touch-action: none, 44 px en pantallas táctiles) y suelta sobre otra tarjeta o sobre el hueco de una columna; el arrastre con ratón y el teclado no cambian.TimelinecononTaskChangehace las barras editables: se arrastra el cuerpo para mover la tarea, sus extremos para cambiar el principio o el final, y con el teclado las flechas mueven un día (Mayús, el final; Alt, el inicio), con aviso de lo que quedó; entrega las fechas nuevas y quien lo usa decide si las acepta.DockLayoutsuma una zona opcionalsplitjunto al centro (con su separador, plegado y la opción «Dividir el centro» en el menú del panel); una disposición escrita sin ella sigue valiendo. UnDockLayoutdentro de un panel ya funcionaba y queda probado.ButtonconpreventFocusOnPressyBreadcrumbscondisabled(AMS-1401).preventFocusOnPresshace que pulsar con el ratón no le quite el foco al campo o a la selección que lo tuviera, para los botones de una barra de formato; el teclado no cambia.Breadcrumbscondisabledapaga toda la ruta: las migas dejan de ser enlaces, salen del tabulador y se leen como no disponibles.
0.31.0, del 1 de noviembre de 2026
WeekAgenda: citas de todo el día, de varios días y que cruzan la medianoche, y elegir un tramo arrastrando (AMS-2333). Una cita conallDaysale en una franja fija sobre la cuadrícula (no en la de horas) y, conendDate, ocupa varias columnas; las que se pisan se apilan en filas, y en la lista del móvil salen con «Todo el día». Una cita con horas yendDate(por ejemplo una guardia de 22:00 a 06:00 del día siguiente) sale partida en un trozo por día, bloqueados, y las que se repiten conservan su duración.onRangeSelectentrega el día y las horas de inicio y fin de un tramo elegido arrastrando por los huecos vacíos de una columna (con ratón o lápiz; con el dedo, arrastrar sigue desplazando la página) o con el teclado: Mayús y las flechas de arriba y abajo estiran el cursor de hueco, Intro entrega y Escape suelta. Un clic suelto sigue yendo aonSlotSelect. Todo es opcional y, sin las props nuevas, el DOM es el de antes.- Gráficas: leyenda que oculta series, valor pulsable, barras agrupadas y líneas de referencia (AMS-2330). En
LineChart,AreaChartyStackedBarChart:toggleSeriesconvierte la leyenda en botones que ocultan o devuelven cada serie (el eje y los totales se recalculan; la tabla para el lector de pantalla conserva todas), conhiddenSeriesyonHiddenSeriesChangepara llevar el estado desde fuera;onPointPresshace pulsable cada valor del globo de una categoría, con teclado y dedo, y entrega serie, categoría y valor;referenceLinesyreferenceBandspintan objetivos, medias y rangos aceptables (el eje los incluye y el resumen para el lector los dice); yStackedBarChart groupedpone las barras una al lado de otra desde el cero. Todo es opcional y, sin las props nuevas, el dibujo y el DOM son los de antes. - Menús y pestañas: opciones con casilla y de opción única, y paneles que conservan su estado (AMS-2332).
MenuActionsumatype(checkboxoradio, con papelmenuitemcheckboxomenuitemradio),checkedycloseOnSelect(confalseelegirla no cierra el menú, ni el submenú ni el de fuera); valen enActionMenu,Menubar,ContextMenuy el desplegable deSplitButton, y el estado lo lleva quien los usa (para «Mostrar columnas» y «Ordenar por»).TabssumakeepMounted: los paneles de las pestañas no elegidas siguen montados y ocultos (hidden), así que lo escrito en un formulario no se pierde al cambiar de pestaña;useTabs().panelProps(key, keepMounted)devuelve entonces los atributos del panel oculto. Por defecto no cambia nada. Pruebas enmenu-checkable.test.tsxytabs-keep-mounted.test.tsx. Queda fuera:NavigationMenusin versión móvil. - Avisos y paleta de comandos: aviso ligado a una promesa, búsqueda aproximada, primer resultado marcado y búsqueda en el servidor (AMS-2331).
useNotifications()sumapromise(promise, { loading, success, error }), que enseña «cargando» y lo convierte en éxito o en error (el error interrumpe como cualquiera) y devuelve la misma promesa, yupdate(id, changes)para cambiar un aviso que sigue a la vista.CommandPalettesumasearchMode(containspor defecto,fuzzycon letras salteadas y orden por parecido,nonepara no filtrar),onSearchChange(lo escrito en cada pulsación y''al cerrar) yloading(lista ocupada y «Buscando…»). Cambio de comportamiento: al escribir queda marcado el primer resultado, así que Intro lo ejecuta sin pulsar antes una flecha; con el campo vacío no se marca nada. Pruebas encommand-palette-search.test.tsxynotifications-promise.test.tsx. ImageCropperyFileManager: zoom, giro, imagen recortada, árbol de carpetas y panel de detalles (AMS-2334).ImageCroppercononCropañade «Recortar», que dibuja el rectángulo en un<canvas>y entrega la imagen recortada (unBlob, conoutputTypeyquality;crossOriginpara una imagen de otro origen, y si el lienzo no se puede leer lo dice en vez de fallar callado);rotatablegira la imagen un cuarto de vuelta a cada lado y recorta sobre la imagen girada (entrega también la rotación);zoomableacerca hasta cuatro veces con un visor que se desplaza y el arrastre sigue contando en píxeles de la imagen.FileManagercontreepinta el árbol de carpetas a la izquierda (elegir una navega a ella;loadFolderspara cargarlo a demanda) y condetailsel panel de detalles a la derecha con nombre, tipo, tamaño, fecha y miniatura de lo elegido (renderPreviewpone tu propio visor); en una caja estrecha se apilan. Sin estas props, ambos son los de antes.QueryBuildery campos: valor según el tipo, exportar a SQL, MongoDB y texto, paginación sin total, céntimos y requisitos de contraseña (AMS-2329). EnQueryBuilder, un campo conoptionselige el valor de un desplegable con «es» y «no es» y un campo de fecha usa el selector de fecha;exportsenseña la consulta escrita, yqueryToSql(con parámetros,?o$1, y los nombres de columna solo salen defields, rechazando lo que no sea un identificador),queryToMongoyqueryToTextla dan para usarla en el servidor, ignorando lo quematchesQueryignora.PaginationconhasNextsirve a una API con cursor que no sabe cuántas páginas hay: solo «Página N» entre las flechas.MoneyFieldconunit="cents"recibe y entrega céntimos enteros (según los decimales de la moneda).PasswordFieldconrulesenseña la lista de requisitos con su estado escrito («cumplido» / «pendiente»);commontrae los de siempre. No entra todavía la multiselección de valores enQueryBuilder.- Subida e importación:
PUTa URL firmada, miniaturas, ficheros ya subidos, corregir celdas al importar y listas de valores (AMS-2328).httpUploadconPUTy sindatanifieldNamemanda ya el fichero tal cual como cuerpo (antes mandaba un formulariomultipart, que una URL firmada de S3 rechaza);bodyfijamultipartorawa mano.Uploaderganapreviews(miniatura de cada imagen elegida, liberada al quitarla) yexistingFilescononRemoveExistingpara enseñar los ficheros que ya estaban subidos, con su descarga;FileListadmitepreviewyremovablepor fila. EnDataImportWizard, las celdas con error se corrigen en la propia revisión y la fila se valida de nuevo, las filas con error se descargan en un CSV con su línea y el motivo, y el nuevo tipolistconoptionstraduce el texto del fichero («Alta», «activo») al valor de la aplicación. No entran todavía la subida por partes con progreso enFileManageryMediaManager, soltar ficheros ni paginación enMediaManager. SchemaForm: widgets propios,onChangecon estado de cambios, validación propia y asíncrona por campo y listas reordenables (AMS-2327).widgetsregistra tus propios campos por nombre (ui={{ color: { widget: 'swatch' } }}); el widget recibe su valor, su error, el esquema y los datos de todo el formulario consetValuepara escribir en otro campo.onChange(values, { dirty })avisa en cada cambio (no al montar) y dice si hay cambios sin guardar, comparando con los valores de partida.validateañade una regla propia, también asíncrona (comprobar en el servidor que un código está libre), que solo se llama cuando el esquema ya da el campo por bueno y que admitelines.*.skupara cada elemento de una lista; si lanza, el error sale en el campo. En una lista,reorderableañade «Subir» y «Bajar» yduplicableañade «Duplicar» (respetamaxItems). Todo es opcional: sin estas props el formulario es el de antes. No entra todavía elegir la rama de unoneOf/anyOfsin discriminante.
0.30.0, del 30 de octubre de 2026
- Cuatro bloques nuevos:
sign-up,invoice-list,team-membersypricing(AMS-2301). El alta de cuenta (nombre, correo, contraseña escrita dos veces con su fuerza, condiciones de uso opcionales y el error del servidor), el listado de facturas (lo pendiente y lo vencido arriba, buscar, filtrar por estado, ordenar, paginar, marcar varias y exportar), el equipo (roles, quitar acceso con confirmación e invitar) y la comparativa de planes (pagar cada mes o cada año, ahorro, plan recomendado y plan actual). Se copian connpx amset-ui add. Con ellos son once; el tope de la prueba de bloques pasa de diez a catorce. MultiSelectyPeoplePickerya marcan el campo como no válido coninvalidsolo (AMS-2301). Calculabaninvalidpero después miraban soloerror, así queinvalidsin mensaje no ponía nidata-invalidniaria-invalid.invalid={false}ahora también gana a unerror, como enSelectySearchField.- El
stylede fuera ya llega aImageCompare,ReadingProgress,Thumbnail,WeekAgenda,LineChart,AreaChartyStackedBarChart(AMS-2301). Los tipos lo admitían, pero cada uno ponía su propiostyledespués del reenvío y lo borraba. Se mezcla con el del componente; enWeekAgenday los gráficos de series las variables propias del componente ganan, porque sin ellas se descuadra el dibujo. - Un
Calendarde solo lectura se anuncia como tal (AMS-2301). La rejilla llevaaria-readonlycuandoreadOnlyestá puesto; antes solo dejaba de dejar elegir y un lector de pantalla no lo sabía. src/components/props-contract.test.tsx: el contrato de las props sobre las 200 y pico fichas (AMS-2301). Enumera con el compilador cada prop cerrada de cada componente (unas 1.100 props y 3.100 valores), las pinta una a una y exige que ninguna haga caer al componente; que las del vocabulario común (size,tone,variant,invalid,loading...) cambien algo de lo pintado o estén justificadas con su motivo; y queclassNamese una a las clases del componente ystylellegue al DOM. Comprobado que muerde: revertido el arreglo deinvalid, falla señalandomulti-select.invalid.
0.29.0, del 29 de octubre de 2026
Table: elegir las columnas al exportar y exportar por tandas del servidor (AMS-2274). EnexportCsvyexportXlsx,columnsfija qué columnas salen y en qué orden, ychooseColumnsabre un panel con una casilla por columna antes de bajar el fichero. ConloadRowsla tabla pide las filas al servidor tanda a tanda (empezando en 0, hasta quehasMoresea falso), enseña cuántas lleva cargadas con un botón para cancelar y baja todas juntas; si el servidor falla o se cancela, se dice y no se baja nada. Salen sueltos los tiposExportPageyLoadExportPage.
0.28.0, del 29 de octubre de 2026
DockLayout: espacio de trabajo con paneles acoplables (AMS-2259). Cuatro zonas con pestañas; cada panel se mueve a otra zona desde su menú o arrastrando su pestaña y se cierra desde el mismo menú; los lados y la zona de abajo cambian de tamaño con un separador que también se mueve con las flechas, y se pliegan. La disposición se crea concreateDockLayout, se cambia con funciones puras y se puede controlar para guardarla.MediaManagereImageCropper: biblioteca multimedia y recorte de imágenes (AMS-2259). La biblioteca busca, filtra por tipo, sube, elige uno o varios, marca las imágenes sin texto alternativo y, del elegido, edita título, alternativo, pie y etiquetas, recorta y borra con confirmación. El recortador mueve y redimensiona la caja con ratón o teclado, con proporción libre o fija, y entrega el recorte en píxeles de la imagen original.FileManager: explorador de ficheros (AMS-2258). Ruta con migas, búsqueda sin acentos, lista ordenable o cuadrícula con las carpetas delante, elegir varios, crear carpeta y renombrar con el nombre comprobado, mover con «Mover a» o arrastrando a una carpeta, borrar con confirmación y subir con el botón o soltando ficheros encima. Cada acción es una función de la aplicación; si falla, se dice y no se pierde lo elegido.DashboardBuilder: panel de mando que cada persona ordena (AMS-2258). Piezas en una rejilla de 12 columnas que, en modo edición, se mueven arrastrando su asa o con las flechas, cambian de tamaño desde la esquina o con Mayúsculas y las flechas, se quitan y se añaden del catálogo; al mover una, las demás se apartan y todo sube. Cada movimiento anuncia dónde queda la pieza y cuánto mide.WorkflowBuilderyAgentWorkflowEditor: editor de flujos de nodos y conexiones (AMS-2258, AMS-2259). Un lienzo con desplazamiento y zoom, nodos que se arrastran o se mueven con las flechas, conexiones que se validan (tipos de dato, entradas ocupadas, círculos), problemas del flujo escritos, «Ordenar» por capas, el estado de cada nodo en una ejecución y un panel para conectar y quitar conexiones sin ratón.AgentWorkflowEditortrae los pasos de un agente de IA (mensaje, modelo, herramienta, condición, aprobación humana y respuesta) traducidos.- Subidas por partes, reanudables, con reintentos y pausa (AMS-2273).
chunkedUploadsube un fichero por partes (5 MiB por defecto), pregunta al servidor por dónde iba para reanudar tras un corte o una pausa y reintenta cada parte con espera creciente;retryingañade reintentos automáticos a cualquier subida.useFileUploadsganapause,resume,pauseAll,resumeAll, el estadopaused,retriesy el avance total (progress), yUploadertienepausablecon la barra de todos juntos y los botones de pausar y reanudar. NotificationCenter,useNotificationCenteryonShowenNotificationProvider(AMS-2275). El centro de avisos con el historial agrupado por día o por tipo, filtro de no leídos, marcar leído o no leído, borrar uno o todos con confirmación y acciones propias de cada aviso; el gancho que lo guarda en el navegador; yonShowpara que los avisos efímeros queden en el historial.AddressFieldpropone direcciones mientras se escribe (AMS-2276). Consuggest(el buscador de tu proveedor de mapas o de tu servidor), la calle pide propuestas a partir de tres letras y tras 300 ms sin escribir, cancela la petición vieja al seguir escribiendo y, al elegir una, rellena todos los campos. Si el buscador falla, se dice y se sigue a mano. Sinsuggest, la calle es la de siempre.RecentSearchListyuseRecentSearches: búsquedas recientes y ámbitos (AMS-2278). Las últimas búsquedas guardadas en el navegador y compartidas entre componentes, una lista para repetirlas, quitarlas o borrarlas bajo un buscador vacío, yparseScopedQuerypara buscar en un ámbito con#pedidos 1042.ShortcutsProvider,useShortcutyKeyCombo: atajos de teclado de toda la aplicación (AMS-2277). Un solo escuchador con registro de atajos, ámbitos que ganan a los globales, detección de choques, ayuda con Mayús+? y atajos que cada persona cambia desde la ayuda pulsando la combinación nueva;KeyCombopinta un atajo como teclas de Mac o de PC.AgentApprovalDialog,AIUsageDashboardy Excel enTable(AMS-2279, AMS-2280, AMS-2274). El diálogo para aprobar o rechazar la acción de un agente de IA (herramienta, datos, cambios, riesgo, comentario al rechazar, datos editables, «permitir siempre» y caducidad); el panel de consumo de IA (coste, tokens de entrada y salida, coste por día y modelo, desglose por modelo y topes con aviso); yexportXlsxenTable, que baja un .xlsx de verdad con números y fechas con su tipo, másselectedOnlypara exportar solo las filas elegidas en CSV o en Excel.PromptTemplateEditor,DragSelectArea,UserRoleEditor,OrganizationTreeyDataMappingEditor(AMS-2259). Editor de plantillas con variables{{name}}(insertar, valores de prueba, vista previa y estimación de tokens); selección de varios elementos arrastrando un rectángulo, con Control+A y Escape; personas y roles (cambiar, buscar, quitar con confirmación); organigrama en cajas o en lista con teclado de árbol; y emparejado de campos entre un origen y un destino con transformaciones, avisos yapplyMapping.SmartFilterBar,SavedViewsManageryBulkEditPanel(AMS-2259). La barra de filtros de un listado (búsqueda, filtros rápidos, consulta avanzada con elQueryBuilderen un panel y quitar todo), consmartFilterpara aplicarla en el cliente; las vistas guardadas (elegir, guardar nueva o encima de la activa, renombrar, por defecto y borrar con confirmación); y la edición masiva de los registros elegidos (no cambiar, poner un valor o vaciar, con resumen antes de aplicar).SLAIndicator,useAutosaveyApprovalFlow(AMS-2259).SLAIndicatordice cuánto queda de un plazo de servicio con la barra de lo gastado y la fase escrita (en plazo, por vencer, vencido, en pausa, cumplido, incumplido).useAutosaveguarda un valor cuando deja de cambiar, de uno en uno y sin reintentos en bucle, yAutosaveIndicatorcuenta en qué punto está.ApprovalFlowpinta una cadena de aprobación con sus pasos, sus aprobadores y lo que decidieron, y recoge aprobar o rechazar con motivo de quien le toca.DataImportWizard: importar un CSV o un Excel (AMS-2256). Un asistente de tres pasos (fichero, columnas y revisión) que lee el fichero en el navegador, sin subirlo, y emparejado con los campos de la aplicación entrega las filas ya convertidas y validadas. Detecta el separador, la codificación y las fechas de Excel; lee .xlsx con un lector propio (un ZIP de XML) que solo se carga al elegir un libro. Valida tipo, obligatorios, duplicados (unique) y reglas propias (validate); las filas con errores se saltan o impiden importar (allowPartial), y el resultado cuenta las importadas, las saltadas y las que rechazó el servidor. La validación es de formato: el servidor vuelve a validar.QueryBuilder: consultas con grupos anidados (AMS-2255). Condiciones «campo, operador, valor» unidas por «y» o por «o», con grupos dentro de grupos (hasta cinco niveles) y la posibilidad de negar un grupo. Entrega un árbol de datos (QueryGroup) yfilterByQuery/matchesQuerylo aplican a una lista de registros; una condición a medias no filtra nada. La lógica de cada condición (operadores por tipo, comparación de texto sin acentos, números y fechas) se sacó de los filtros avanzados deTablea un módulo compartido, así que los dos se comportan igual.DiffVieweryAuditLogViewer: comparar y auditar (AMS-2254).DiffViewercompara dos textos o dos JSON, uno tras otro o lado a lado, línea a línea y palabra a palabra; las líneas iguales lejos de un cambio se pliegan en un botón y cada línea cambiada lleva un signo y un texto escondido, así que el cambio no depende del color.AuditLogVieweres el historial de cambios de algo: quién hizo qué, sobre qué y cuándo, agrupado por día, con búsqueda, carga por tandas y el cambio de cada campo desplegable con su comparación. CononRevertcada entrada lleva un botón de revertir con confirmación; la biblioteca no deshace nada, el servidor tiene que comprobarlo de nuevo.PermissionGateyPermissionMatrix: permisos en la interfaz (AMS-2253).PermissionProviderreparte lo concedido (una lista con comodines comoinvoices.*, o una función con tu política) yPermissionGatemuestra, oculta o apaga un trozo de interfaz según ello; apagar es un<fieldset disabled>de verdad, así que el navegador desactiva cada control de dentro, y el motivo se lee.usePermissionyusePermissionsdan la misma respuesta en código. Mientras los permisos cargan no decide nada, y sin proveedor no concede nada y lo avisa en consola.PermissionMatrixes la matriz de un rol: módulos en filas, acciones en columnas, casillas de fila, columna y esquina con estado intermedio, celdas bloqueadas que no cambian ni al marcar su fila, una sola parada del tabulador con flechas y cada cambio anunciado. Ocultar algo en pantalla no lo protege: el servidor sigue comprobando.
0.27.0, del 28 de octubre de 2026
- Cinco componentes que los fronts de la casa se habían tenido que escribir (AMS-736).
AnimatedNumber, una cifra que recorre el camino de la anterior a la nueva (el lector recibe solo la final; conprefers-reduced-motioncambia de golpe).LiveClock, la hora en vivo de la zona que se pida, que avanza en punto leyendo el reloj del sistema.PeoplePicker, elegir a varias personas con avatar, presencia y una línea de debajo; los conectados primero y sin ofrecer a quien ya está (exclude).VideoPlayer, un vídeo con los controles propios del reproductor de audio más sonido, subtítulos WebVTT, velocidad, pantalla completa y teclado (Espacio, flechas, M, C, F). YEmojiPicker, con buscador, categorías, recientes y tono de piel: el catálogo (casi dos mil emoji con nombre y palabras clave en español e inglés) no viaja con el componente, se pide al abrirlo (unos 37 KB comprimidos) o antes conpreloadEmoji();EmojiPanelva suelto para un panel propio. SideNavdentro de unDrawerconserva sus etiquetas en pantalla estrecha (AMS-2022). Por debajo de 40 rem el menú se plegaba a baldosas de iconos aunque fuera dentro de un panel desplegable, donde hay sitio de sobra, y el menú móvil se quedaba sin nombres. Dentro de unDrawerya no se pliega, yresponsive={false}lo pide a mano en cualquier otro sitio.- El bloque
SignInpuede abrirse con un error (AMS-2217). Nueva properror: el motivo con el que empieza la pantalla (la sesión caducó, el enlace de recuperar ya se usó), arriba del formulario, que se quita al volver a enviar. La historia «Credenciales que no valen» lo usa y ya enseña el error en su captura. amset-ui themerepara el contraste que rompe un color forzado (AMS-2154). Un token escrito tal cual concolors(el rojo exacto del asistente comoerroren oscuro) dejaba su derivado de al lado (error-soft) calculado para el valor de la biblioteca, y el texto se quedaba en 3,48:1 con solo un aviso. Ahora el derivado de la misma familia se mueve hasta que el par llega a su mínimo y la CLI dice de dónde a dónde; lo que no se puede arreglar sin tocar otra familia sigue avisando. Nueva funciónrepairContrasten@amset/ui/styles/palette.- El reproductor de audio entra en el editor con un sonido que existe. Su valor de partida apuntaba a un fichero de ejemplo (
ejemplo.mp3) que no existe en ningún sitio y daba 404, y el reproductor entraba roto. Ahora apunta aejemplo.wav, un tintineo corto sin derechos de nadie que aloja la web de la biblioteca. Una direccióndata:no vale para esto: el editor descarta cualquiersrcque empiece pordata:(salvo en imágenes).scripts/audio-default.test.tsfija la dirección. DateTimePickercontrolado ya no pierde la fecha si empieza vacío. Convalue={null}(o"", como empieza un formulario de react-hook-form), al elegir el día se juntaba con una hora que aún no había, salía «sin valor» y, como el campo estaba controlado, no se guardaba en ningún sitio: la fecha se perdía antes de poner la hora y no avisaba nunca, así que el formulario se quedaba vacío. Ahora la mitad que ya se ha elegido se guarda dentro mientras falta la otra y avisa en cuanto están las dos. Si desde fuera lo vacían después de completarlo (un reset), se queda vacío.src/components/date-time-picker.test.tsxlo fija.
0.26.0, del 27 de octubre de 2026
- La barra que sale sobre un texto elegido ya no tapa la barra de formato. Con la primera línea elegida salía encima de la selección, porque cabía dentro del marco del editor y el marco incluye la barra de formato: en un móvil tapaba justo la negrita y no se podía pulsar. Ahora solo sale encima si cabe dentro de la zona del texto; si no, debajo.
e2e/rich-text-mobile.spec.tslo fija. - El editor ya no se rinde al escribir deprisa. Teclear más de 50 teclas seguidas de golpe (una expansión de texto, un dictado, una tecla repetida) hacía que React lanzara «Maximum update depth exceeded»: lo que quedaba por hacer con esa tecla, entre ello colocar el cursor, no se hacía y el resto del texto salía por el principio del párrafo. Venía de dos efectos que programaban un
setStateen cada cambio del documento (la limpieza de bloques propios y el cierre del menú de ortografía) aunque no hubiera nada que cambiar; ahora solo lo programan si hay algo. Medido: 1.000 letras de golpe, sin errores y todas en su sitio.e2e/rich-text-ux.spec.tslo fija. @amset/ui/rich-text: los componentes con contenido (tarjeta, aviso, rejilla, pila…) se ven de verdad dentro del editor. Hasta ahora eran un marco punteado con su nombre y el texto de dentro: el título, el tono, el icono o el borde que se ponían en la ficha no se veían hasta leer el documento, y parecía que el componente no hacía caso. Ahora el editor pinta el componente real, con su aspecto, y su contenido es texto del documento escrito dentro de él, con párrafos, listas, el menú «/», borrar y deshacer como en cualquier otro sitio. Cambiar una propiedad se ve al momento. Un clic en su aspecto (no en el texto) lo elige entero para moverlo, borrarlo o abrir sus propiedades, y el doble clic o el botón derecho abren la ficha. En solo lectura el contenido no se puede escribir. Una rejilla o una pila reparten lo que se escribe en columnas al editar igual que al leer, y si el componente acaba de descargarse cuando ya se estaba escribiendo dentro, el cursor se queda donde estaba. Lo que se escribe en el contenido sigue siendo un documento, igual que antes: lo único que cambia es cómo se ve al editar.e2e/rich-text-components.spec.tsysrc/rich-text/view/framed-component.test.tslo fijan.- La ficha de propiedades de un componente ya no tiene campos en inglés ni los que solo sirven programando. Medido sobre los 157 componentes: 81 propiedades y 146 valores de listas cerradas salían con el nombre en inglés («Close label», «Raised», «Min height», «Secondary») y también salían las opciones de formato de
Intl, las funciones y los nombres de campo de un formulario. Ahora todo lo que se enseña tiene su texto en castellano y en inglés, y lo técnico se esconde. Una prueba (src/rich-text/amset/prop-labels.test.ts) recorre todos los esquemas y falla si una propiedad o un valor nuevos salen sin nombre. SchemaForm: un grupo opcional que nadie ha rellenado ya no pide sus campos obligatorios. Un objeto opcional con campos obligatorios (la fecha mínima de un selector de fechas: año, mes y día) se trataba como un objeto al que le faltaban tres campos, así que el formulario no se podía enviar sin inventarse una fecha. Ahora un grupo opcional con todos sus campos en blanco (o hecho solo de grupos vacíos y listas sin filas, como el filtro avanzado de una tabla) es «no hay valor» y no sale en el resultado; uno a medias sigue pidiendo lo que falta y uno obligatorio vacío sigue diciendo que faltan sus campos. Así «Aplicar» funciona en la ficha de propiedades deDatePicker,Calendar,DateRangePicker,TimeField,Rating,Steps,Timeline,QuickRangeyTable.- Una función obligatoria que el documento no trae ya no hace fallar al componente.
ColorPanelllama a suonChangeal montarse y en un documento no hay quien lo escuche: salíat is not a function. Una función obligatoria sin acción elegida es ahora una que no hace nada, y la ficha tampoco la exige (al pintar ya no contaba como falta).VisuallyHiddensale del catálogo de componentes (157): no se ve, así que en un documento no se podría ni elegir. - El menú «/» del editor (y el de menciones y el de ortografía) ya no se queda fuera de la pantalla. Con el cursor cerca del borde de la ventana, o fuera de ella (la página se desplazó con la lista abierta), la lista se colocaba por debajo del borde: la opción activa bajaba con las flechas, pero dentro de un trozo de lista que no se veía, y al llegar al final no se veía nada. Ahora la lista cabe siempre entera en la ventana: se pega al borde más cercano y, si la ventana es más baja que ella, se encoge. Medido en el resto de listas con foco virtual (selector, combo, selector múltiple, buscador, prefijo de teléfono, selector en árbol, cascada, paleta de comandos): siguen bien la opción activa.
e2e/rich-text-ux.spec.tslo fija, con la ventana baja y con el cursor fuera. CookieBannerya no roba el foco a quien está escribiendo. Al aparecer mandaba el foco dentro del aviso aunque el foco estuviera en un campo: en una tienda que lo pinta tras hidratar, quien ya escribía en el buscador perdía las teclas siguientes (escribía «zzz» y quedaba «z»). Ahora, si el foco está en un campo de texto, un área, un selector o un contenido editable, se queda ahí y el aviso se anuncia conaria-live; en cualquier otro caso entra como antes.isTyping()enutils/focuses lo que lo decide.
0.25.0, del 25 de octubre de 2026
- Modos nuevos: radio de las esquinas, relieve y densidad amplia.
data-radius(none,sm,mdolg) mueve los cuatro radios a la vez, también las píldoras de un interruptor, una barra de progreso o una ficha, que llevaban999pxescrito a mano y ningún tema alcanzaba: ahora salen de un token nuevo,--amset-radius-full.data-elevation="flat"quita la sombra de reposo de tarjetas, campos y botones y deja la de lo que flota.data-density="spacious"sube los controles a 36, 44 y 52 px para pantallas que se usan con el dedo, y la tabla, la lista de datos y el historial abren sus huecos con ella. Los tres van en la raíz o en un contenedor,AmsetProviderlos acepta comoradius,elevationydensity, y la barra de Storybook tiene los dos selectores nuevos. Lo que es redondo por lo que significa (el punto de un botón de opción, un avatar, un girador) no cambia, y unshape="pill"pedido en la prop sigue siendo píldora en cualquier modo. - Las 693 variables de los componentes, probadas una a una en un navegador. Cada una se puso como la pondría un proyecto y se miró si cambiaba algo: todas lo hacen. 674 se vieron solas recorriendo las 1.052 historias; el resto solo actúa en un estado concreto (arrastrando, pulsando, al cerrar un panel, en pantalla estrecha) y se comprobó montando ese estado. Una prueba nueva falla si una hoja declara una variable que no lee nadie.
TabsyAppliedFiltersalineados con el resto. Las pestañas medían 30, 38 y 46 px y no seguían a la densidad; ahora miden lo que un control de su tamaño (30, 36 y 44 px, y lo que pida la densidad), con--amset-tabs-height, y--amset-tabs-padding-blockpasa a 0. Las fichas de los filtros aplicados salían de 23,2, 26,8 y 32,4 px y ahora miden 24, 28 y 36, comoToggleChip, con--amset-filters-height. Medido en los 119 componentes con tamaño, en sus tres tamaños y sus tres densidades: el resto de controles ya casaba.e2e/modes.spec.tslo fija.
0.24.0, del 25 de octubre de 2026
- Repaso de móvil y teclado de todas las fichas.
ButtonGrouppegado que no cabe en una fila ya se apila a todo el ancho (antes pasaba a una segunda fila desalineada); lo decide el componente midiendo, no un ancho fijo. La leyenda deDonutChartdeja de pisar el nombre con el valor,Paginationesconde los números intermedios por debajo de 30 rem y la barra deRichTextEditorpasa a una sola fila que se desplaza. En el editor,Escapesobre un menú de la barra lo cierra y deja el foco en su botón en vez de saltar al texto. Las páginas de demostración deAffixyTableOfContentsya no desbordan en un móvil. Medido con una sonda de teclado sobre las 208 fichas; los avisos restantes eran de la propia sonda (radios nativos, acordeones que no se cierran conEscape). WeekAgenda: el hueco para crear una cita se elige también con el teclado. Lo encontróamset-ui a11y lintal pasarlo sobre la propia biblioteca: cononSlotSelectel hueco solo se podía elegir con el ratón (WCAG 2.1.1). Ahora la cuadrícula es una parada del tabulador con un cuadro de hueco que se mueve con las flechas (Re Pág y Av Pág de hora en hora, Inicio y Fin al primer y al último hueco del día), anuncia el día y la hora y se elige con Intro o Espacio, igual que un clic. El cuadro solo se pinta con el foco del teclado, no al llegar con un clic, y en un idioma de derecha a izquierda las flechas laterales van al revés. SinonSlotSelectla cuadrícula sigue sin meterse en el tabulador. Prueba en navegador ene2e/week-agenda-keyboard.spec.ts.MenubaryButtonGroupya no empujan la página a 320 px de ancho (WCAG 1.4.10). Medido poramset-ui a11y audit: la barra de menús llegaba a 338 px y el grupo de botones a 323 px de 320, y las tarjetas del ejemplo deAppShella 325 px porque la rejilla pedía 14 rem de mínimo. Las entradas de la barra y los botones del grupo pasan a otra línea cuando no caben (no se desplazan dentro de su caja: el aro de foco de un botón se sale 4 px y se recortaría), y la rejilla del ejemplo se encoge.e2e/reflow-320.spec.tslo fija.amset-ui a11y auditno da por inalcanzable lo que desaparece mientras se tabula. Un botón que solo existe tras bajar la página (comoBackToTop) se desmonta al subir el foco al primer enlace, y se contaba como un control que el tabulador no alcanzaba. Ahora se vuelve a mirar la página al acabar el recorrido y lo que ya no está no cuenta. La pagina limpia dee2e/a11y-cli.spec.tslleva un botón que se quita al enfocar otro, para que no vuelva.amset-ui a11y: pasado contra las 208 fichas del propio Storybook, ya no da falsos fallos. La primera pasada daba 112 hallazgos propios y casi todos eran de la herramienta, no de la biblioteca.a11y auditya no cuenta como inalcanzables los controles de un widget compuesto (toolbar,tablist,listbox...), que tiene una parada de tabulador y se recorre con las flechas; no tabula los controles contabindex="-1"; mide un<input>escondido por la etiqueta que se ve; compara el foco por elemento y no por selector (dos filas de una tabla daban el mismo); no da por cerrada la vuelta en la primera salida del documento, porque una capa superior (un banner) la cruza antes del final; mide el tamaño de un objetivo con su caja entera aunque el scroll de su lista lo recorte; y suelta el foco previo (unautofocus) antes de medir, o un campo con el aro puesto de salida parecía no tenerlo.a11y lintreconoce el foco pintado con[data-focus-visible]y da por apagado el movimiento infinito si cualquier hoja respetaprefers-reduced-motion. Resultado: 202 de 208 fichas sin hallazgos propios; el resto son candidatos a revisar, no fallos confirmados.
0.23.0, del 24 de octubre de 2026
amset-ui a11y: la accesibilidad de tu proyecto, fuera de la biblioteca. Tres comandos nuevos que comparten una tabla de reglas, cada una con su criterio de WCAG.a11y audit <url>abre las páginas en un navegador con el Playwright del proyecto y mide lo que un escáner estático no ve porque exige pulsar teclas o cambiar la página: el foco visible al tabular, las trampas de teclado, los controles que el teclado no alcanza, el foco tapado por una cabecera fija (2.4.11), los objetivos de menos de 24 px (2.5.8), la página a 320 px de ancho (1.4.10), el espaciado del texto (1.4.12) y el movimiento reducido. Si el proyecto tieneaxe-core, pasa además sus reglas de WCAG 2.2 sin contarlas dos veces; si no lo tiene, lo dice en el informe y no lo da por pasado. Solo lee: tabula y mide, nunca hace clic. Escribea11y-report/index.htmlyreport.json, y sale con código 1 si hay errores.a11y lintlee el código con el TypeScript del proyecto y avisa de controles hechos condiv, imágenes sinalt,IconButtonsin nombre, campos sin etiqueta, el foco quitado, colores sin contraste y componentes hechos a mano donde la biblioteca ya trae uno; un hallazgo concreto se calla con// amset-a11y-ignore image-without-alt.a11y statementredacta el borrador de la declaración de accesibilidad (Ley 11/2023, Anexo V de la Directiva europea) con lo medido y las fichas de los componentes que usa el proyecto: sin auditoría el estado es «no evaluado», con una auditoría limpia es «sin incumplimientos detectados» y nunca «conforme», lo que nadie ha comprobado sale como pendiente, y lo que no puede saber (empresa, contacto, vía de reclamación) queda como un hueco marcado para completar. Es un borrador, no asesoría jurídica. Las medidas del navegador se prueban contra una página limpia y otra con cada fallo (e2e/a11y-cli.spec.ts), ynpm run consumerspasa los tres comandos contra cada starter recién creado y compilado.
0.22.0, del 24 de octubre de 2026
@amset/ui/rich-text: repaso entero del editor (AMS-2072). Botón Insertar en la barra con todo lo que se puede meter, por apartados y con buscador (antes los componentes solo salían escribiendo «/»). Un componente recién insertado sale con sus propiedades abiertas, que se vuelven a abrir con doble clic, con el botón derecho o desde la barra; la ficha va en castellano, por orden y sin lo técnico (el selector pasaba de 86 campos en inglés a los suyos). El botón derecho da un menú según lo de debajo: componente (propiedades, duplicar, subir, bajar, eliminar), imagen, enlace, tabla, y cortar, copiar, pegar y formato. Los 158 componentes entran con contenido de verdad, sacado de sus fichas (antes unos 80 entraban con «Etiqueta» y listas vacías). La pantalla completa es la del navegador o la capa superior: con un antepasado contransform(la documentación de Storybook) se quedaba en la barra sin el texto. El menú «/» va en la capa superior y se abre hacia arriba si abajo no cabe. Pegar, soltar o elegir del equipo una imagen funciona sinonImageUpload(se incrusta en base64; también en Firefox, que la deja enitems). El panel del enlace pide el texto, la dirección y una descripción (title, que se guarda y se exporta), y avisa de textos como «aquí».CommandPalette: con las flechas, la opción activa se queda a la vista. El foco de la paleta no sale del campo de búsqueda (la activa se dicta conaria-activedescendant), así que el navegador no desplazaba nada y, en cuanto la lista no cabía, se bajaba a ciegas por debajo del borde y se subía sin que la lista volviera. Ahora la fila activa se lleva a la vista cuando la mueve el teclado (no con el ratón, para que la lista no dé un tirón bajo el puntero) y la primera sube hasta el título de su grupo. Los selectores ya lo hacían bien;e2e/keyboard-scroll.spec.tslo fija en los dos y mide la paleta antes y después del arreglo (22 filas fuera de la vista bajando, 0 ahora).
0.21.0, del 23 de octubre de 2026
- Se retiran las recetas con editores de terceros. Con el editor propio de
@amset/ui/rich-textya no hacen falta: se quitan las historias de recetas con otros editores deRichTextToolbar, sus recetas y demos, la prueba e2e que las ejercitaba y las once dependencias de desarrollo de esos editores (elpackage-lock.jsonbaja unas 900 líneas).RichTextToolbarsigue siendo API pública y se puede usar con cualquier motor: la guíadocs/rich-text-editor.mdse centra ahora en el editor propio y en el contrato de la barra, y conserva la lista de comprobación con lector de pantalla, que pasa a hacerse sobreRichTextEditor. llms.txtyAGENTS.mdcuentan lo que un asistente de IA necesita saber: que el código se copia, el servidor MCP y Figma.npx amset-ui addya copiaba el código fuente de los componentes,@amset/mcpya daba ocho herramientas a un asistente de código y los tokens ya salían como Variables de Figma, pero esos ficheros, que son lo que lee un asistente, no decían nada de eso y la biblioteca parecía solo instalable y sin integraciones. Ahora tienen las secciones «Instalar o copiar el código» y «Servidor MCP y Figma», yllms.txtenlaza a las guías. El editor de texto enriquecido (RichTextEditor,RichTextView) y el visor de HTML (HtmlViewer) son de la subruta@amset/ui/rich-texty no se pueden copiar conadd: se instalan, y una prueba lo vigila.llms.txtlleva enlaces. Google avisaba de que el fichero no los traía. Ahora tiene una sección «Documentación» con enlaces en Markdown allms-full.txt, al catálogo de componentes, a «Empezar», a la página de agentes y a la del editor, y una prueba comprueba que haya un H1 y al menos tres enlaces.@amset/ui/rich-text: corrector ortográfico propio, comentarios y sugerencias, historial de versiones, columnas y fórmulas (AMS-2055).spellchecker(concreateWordSpellcheckerycreateLanguageToolSpellchecker): los fallos se subrayan con una onda, el botón derecho o la tecla de menú da sugerencias, ignorar y añadir al diccionario, y la palabra que se escribe no se marca; el editor no trae diccionario, lo pones tú.reviewañade comentarios (hilos que guardas fuera del documento, con respuesta, resolver y eliminar) y el modo «Sugerir cambios» (lo escrito se subraya, lo borrado se tacha, y se acepta o rechaza uno a uno o todo);RichTextViewganasuggestions="show | accepted | rejected", y Word recibe las sugerencias como revisiones y los comentarios como comentarios, y los lee de vuelta.versionsañade el historial: lista, comparación con el documento de ahora (lo añadido subrayado, lo quitado tachado) y restaurar con deshacer;diffDocuments(before, after)lo da fuera del editor. Nuevos bloques de serie: columnas (de 2 a 4, el texto fluye de una a otra, una sola en pantalla estrecha) y fórmulas en TeX pintadas como MathML con un conversor propio sin dependencias (se guardan como$$ ... $$en Markdown). Además, el resaltado de buscar y el de ortografía ya no se pisan entre dos editores de la misma página. Sin cambios de API en lo que ya existía.
0.20.0, del 22 de octubre de 2026
- Markdown: los dos espacios al final de una línea vuelven a ser un salto de línea, y nada se pierde por anidar de más.
fromMarkdownrecortaba cada línea del párrafo y el salto de CommonMark desaparecía. Un enlace entre<>con espacios ahora se lee (los espacios pasan a%20), y una cita anidada más allá de 20 niveles se queda como texto en vez de vaciarse al guardarla. @amset/ui/rich-text: visor de HTML seguro y los componentes de AMSET UI dentro de un documento (AMS-2028).HtmlViewerpinta HTML ajeno: en modo documento lo vuelve a escribir desde el árbol con una lista de lo permitido (etiquetas, atributos, esquemas de URL y propiedades de CSS), y en modo página lo aísla en un<iframe sandbox>sinallow-same-originy con una CSP que no deja red, formularios ni scripts ajenos.amsetBlocks({ actions, data })registra 159 componentes de la biblioteca como bloques: el catálogo sale del compilador de TypeScript, cada uno se descarga la primera vez que se usa, las props se validan con su esquema antes de pintarlo, y las acciones y los datos son identificadores que pone la aplicación (nunca viaja una función). Tarjeta, Alerta, Rejilla, Pila y compañía llevan bloques del documento dentro (component), con marco y nombre en el editor, ficha de propiedades y «Quitar y dejar el contenido».toTsx(doc)lo lleva a un componente de React, ylistComponents,describeComponentycreateComponentdejan a un agente montar pantallas validadas. Además, en Firefox la primera pulsación en un editor sin foco ponía el cursor al principio del documento en vez de donde se pulsaba, ynpm run e2e:enginesno activaba Firefox ni WebKit (el nombre de la variable no coincidía con el deplaywright.config.ts). Sin cambios de API en lo que ya existía.@amset/ui/rich-text: Markdown y Word, pegado fiel, bloques nuevos, avisos de accesibilidad y circuito de IA (AMS-2011).toMarkdown/fromMarkdown(CommonMark y GFM, lo que Markdown no dice va como HTML y vuelve) ytoDocx/fromDocx(un .docx con titulos, formato, listas con niveles, tablas con celdas combinadas, enlaces e imagenes); el pegado de Word, Google Docs, Excel y LibreOffice se prueba con un corpus. Celdas combinadas, menciones con@, cuadros destacados (> [!NOTE]), desplegables y bloques propios (blocks: tu componente en el texto, con surenderyedit). Barra flotante sobre el texto seleccionado, asa para arrastrar bloques y Alt+Mayus+flechas. Avisos de accesibilidad mientras se escribe (checkAccessibility). Un circuito de IA sin proveedor:aiCommands,createRichTextAgent(leer, buscar, proponer cambios que una persona acepta o rechaza, escribir en directo, herramientas para un modelo).@amset/ui/rich-text: un editor de texto enriquecido propio, sin dependencias (AMS-2011).RichTextEditores un campo completo (etiqueta, ayuda, error, contador, solo lectura) con barra de herramientas -formato, títulos, listas y tareas, citas, código, alineación, color y resaltado, índices, enlaces, imágenes con sus propiedades, tablas, línea horizontal, buscar y reemplazar, código HTML y pantalla completa-, atajos de Markdown al escribir, pegado desde Word y Google Docs sin su basura, arrastrar y soltar, y deshacer agrupado. El valor es el documento en JSON (format="json", lo exacto) o HTML saneado (format="html"). Por dentro hay un modelo inmutable con pasos invertibles, un esquema que descarta todo lo que no conoce (el HTML exportado se genera desde el modelo: ni<script>, ni manejadores, nijavascript:) y una vista sobrecontenteditableque cancelabeforeinput, y cuando el navegador no deja (una composición de IME, un teclado de Android) lee el DOM y aplica la diferencia.RichTextViewpinta lo guardado en el servidor, sindangerouslySetInnerHTML.RichTextToolbaradmitechildrenpara grupos propios. Vive en su subruta y el índice no lo importa: no pesa nada en quien no lo usa.
0.19.0, del 22 de octubre de 2026
Form: el clic en Guardar ya no se pierde al corregir el último error (AMS-2036). Un campo que valida al salir (el número confirma su valor en elblur) corregía su error justo al apretar Guardar: desaparecían el resumen y el mensaje del campo, el formulario subía hasta 111 px entre elpointerdowny elpointerupy el clic caía fuera del botón, así que había que pulsar dos veces. Ahora lo corregido se queda tachado en el resumen hasta el siguiente envío (con «Todo corregido: ya puedes enviar» cuando no queda nada y el tono de éxito), y mientras se aprieta dentro del formulario ningún error cambia de aspecto: se pone al día en cuanto llega el clic. Además, al enviar con errores el foco va a la cifra del número y no a su botón «−». Prueba nuevae2e/form-summary.spec.tsen Chromium, Firefox y WebKit, con axe en claro y oscuro.- El mensaje de chat separa lo que lleva dentro. Un razonamiento, una llamada a herramienta y la respuesta dentro de la misma burbuja de
ChatMessagese pegaban sin un píxel de aire (medido: 0 px entre la herramienta y el texto). Ahora cada bloque que no es el último deja--amset-chat-message-block-gapdebajo (--amset-space-2de serie), también con un párrafo demargin: 0en línea. Historia nueva «Con razonamiento y herramienta». Dialog,Drawery la paleta de comandos ya no se cierran con un clic que empezó dentro (AMS-2027). Pulsar fuera se detectaba por el destino delclick, y el navegador lo manda al ancestro común de donde se pulsa y donde se suelta. Si el contenido cambia de tamaño entre las dos cosas (un aviso de error que desaparece al salir del campo) el botón se mueve, se suelta sobre el fondo y el diálogo se cerraba sin guardar: medido en Facturación al imputar horas tras un primer error. Ahora solo cierra si el gesto empezó también en el fondo. Lo mismo al arrastrar seleccionando el texto de un campo y soltar fuera.npm run figmalo hace todo, y en cada subida aprese genera solo. Construye el Storybook si falta, lo sirve en local y mide las historias con varias páginas a la vez, y dejafigma-export/con los componentes, los tokens y el plugin. Un job nuevo del CI (figma, no bloquea la web) lo ejecuta en cada subida aprey ui.amset.tech lo publica en/figma/, igual que el resto de lo que se genera en cada versión.- Una app de gestión sale bien maquetada sin CSS propio (AMS-1972). Cinco huecos medidos en una app de gestión real con
getComputedStyle, que la app tapaba con reglas suyas: unaTabledentro de unaCardconwithoutPaddingya no pinta un segundo marco y alinea sus celdas extremas y la paginación con el título de la tarjeta; las celdas numéricas no parten un importe en dos líneas;PageHeadercentramediacon el título y estrenamediaTilepara el icono de sección en su baldosa; unSideNavdentro de unDrawerocupa el cajón entero, sin la raya lateral ni la doble barra de desplazamiento;AppShellquita el margen de 8 px delbody,SiteFootercon solo la línea legal no duplica la raya, y unaCardpulsable sin contenido ya no corta el subtítulo. - Un campo de texto ya no trae el CSS de color, fecha, hora, selector ni teléfono (AMS-2014). El generador del CSS por componente importaba cualquier hoja que tuviera una regla empezando por una clase que el módulo mencionaba, y casi todas las hojas de campos tienen unas pocas reglas de
.amset-field(cómo se pinta un campo que contiene un color o una fecha).TextFieldyTextAreaarrastraban así 64 KB de CSS sin comprimir cuando usaban 29. Ahora una hoja solo se importa por las clases que le pertenecen: las que dominan en ella o aquella donde la clase tiene más reglas.TextFieldbaja a 29 KB,Selectde 71 a 40 y los imports de todo el paquete de 710 a 489. Una prueba fija los componentes que no pueden quedarse sin su propia hoja. MultiSelectyTagsInput: lo elegido va dentro de la caja (AMS-1971). Las fichas iban en un bloque aparte encima del campo y cada valor nuevo empujaba hacia abajo todo el formulario. Ahora fluyen dentro de la caja, delante de donde se escribe: con una línea mide lo mismo que cualquier campo y solo crece si no caben. La etiqueta se queda siempre en su sitio. Pulsar el hueco libre de la caja lleva al campo, ylimitTagsdeja la caja en una línea aunque se elijan muchas: en reposo enseña ese número de fichas y un «+N», y al entrar salen todas.- La exportación a Figma trae también el estado pulsado y el deshabilitado, y la portada del Storybook la explica. En la historia «Interactivo» de cada componente se mide el pulsado (puntero pulsado sobre el primer elemento interactivo) y el deshabilitado (el atributo se pone en el elemento vivo y dibuja las reglas
:disabledde la biblioteca). La portada gana un apartado «En Figma», en castellano y en inglés. - La exportación a Figma trae también el hover y el foco. Cada componente con historia «Interactivo» añade las variantes
Interactivo · hovereInteractivo · foco, medidas con un puntero y un Tab reales; el anillo de foco se dibuja como un trazo por fuera. Solo cuentan si cambian el aspecto en reposo. doctorya no da error condata-theme={variable}(AMS-1962). El valor de una expresión no se puede leer sin ejecutar el código y se tomaba la propia variable como si fuera el tema, así quedata-theme={tema}salía como error. Ahora es un aviso (data-theme-dynamic) que pide comprobar que solo devuelva "light" o "dark". Los literales ({'oscuro'}) siguen siendo error, y lo que dice un comentario (/* */,//,<!-- -->) ya no cuenta como código.- La página de documentación de Notificaciones ya no repite el demo cuatro veces. Con las historias en línea, cada una montaba su propia cola de posición fija y las cuatro se apilaban en la misma esquina; ahora cada historia va en su marco, como en
AmsetProvider. Además, el error deuseNotificationsy el deuseConfirmationfuera de su proveedor nombrabanuseNotificacionesyProveedorNotificaciones, que ya no existen: ahora dicen el nombre real y en inglés. @amset/ui/icons.json: la geometría de cada icono como dato (AMS-1373). Para pintar los iconos sin importar los componentes (la página de iconos de amset.tech se llevaría 900 KB de JS si los importara). Sale de llamar a cada icono y recorrer el elemento que devuelve, no de leer el código generado, así que no se rompe cuando cambia el formato del build. Cada entrada dice si es de la casa, si trae su propio<svg>(la estrella rellena), lo que le pasa a su envoltorio (elstrokeWidthpropio de 35 iconos) y sus elementos con sus atributos.
0.18.0, del 20 de octubre de 2026
VatField: el IVA intracomunitario, validado país por país.DocumentFieldsolo entiende NIF, NIE y CIF, y una tienda que vende a empresas de la UE no podía validar un IVA alemán o francés sin una expresión regular que deja pasar el dígito cambiado, que es el error real. El campo cubre los 27 Estados de la UE y Irlanda del Norte (XI): cada uno con su forma y, en los 22 donde el algoritmo es público, su cifra de control (AT, BE, CZ, DE, DK, EE, EL, ES, FI, FR, HR, HU, IE, IT, LU, NL, PL, PT, RO, SE, SI, SK); Bulgaria, Chipre, Lituania, Letonia, Malta e Irlanda del Norte se validan por su forma y el campo lo dice. Se escribe con espacios, puntos o guiones y sale poronChangelimpio, en mayúsculas y conELpara Grecia (GRse acepta). La ayuda dice de qué país es y si la cifra se ha comprobado;countrieslimita los Estados admitidos. No consulta VIES: dice si el número puede existir, no si está dado de alta (la consulta necesita un servidor). Nuevas utilidadesvalidateVat,vatProblem,vatIsChecked,vatCountry,clearVatyVAT_COUNTRIES, yvatSchemapara los esquemas. 88 pruebas, con números reales de cada país publicados como válidos y el mismo número con un dígito cambiado, que tiene que fallar en cada país con algoritmo.Fieldset: un grupo de campos con su rótulo, un<fieldset>de verdad. La biblioteca no tenía cómo agrupar controles que van juntos para que un lector de pantalla diga el grupo antes de cada uno.legendes la<legend>;disabledapaga los campos de dentro a la vez;columnslos reparte en 2, 3 o 4 columnas (una por debajo de 40 rem);hintpone ayuda bajo el rótulo yhiddenLegendlo esconde a la vista. Quita elmin-width: min-contentdel navegador, que hacía desbordar una página estrecha. Se puede usar desde un Server Component.
0.17.2, del 20 de octubre de 2026
Table: filas jerárquicas, rango de celdas y totales por grupo.tree(getChildren,expanded,onExpandedChange) pasa la rejilla atreegridcon nivel, posición y estado de cada fila y teclado de árbol;cellSelectionselecciona un rango con el ratón o conMayúsy flechas, lo copia como TSV y HTML y lo pega;groupTotalssuma una fila de totales por grupo. Todo se anuncia en una región viva. TiposTableTree,CellRangeyCellRef.SchemaForm(@amset/ui/form): un formulario entero desde un JSON Schema. Campos por tipo yformat, objetos comofieldset, listas que se añaden y se quitan, pasos conWizardy condiciones (if/then/else,dependentRequired,dependentSchemas,oneOfporconst). Validador propio, sin dependencias, que dice lo que no entiende en vez de ignorarlo; los campos apagados por una condición no se envían.WeekAgenda: citas que se repiten, zonas horarias y columnas por recurso.repeatcon las reglas de iCalendar (freq,interval,weekdays,until,count,except),timeZonepor cita y por agenda con el horario de verano de cada fecha (lo que cruza la medianoche sale partido y bloqueado) yresourcespara repartir la vista de día por sala o persona.onEventChangeentrega ademásseries,occurrence,timeZoneyresource. SalenoccurrencesBetween,occurrenceKeyyparseOccurrenceKey.- Ficha de conformidad de accesibilidad por componente (
ACCESSIBILITY-CONFORMANCE.md,npm run a11y:sheet). Generada desde las fichas: qué documenta cada componente, si pasa axe en el navegador, si tiene guion de prueba con lector de pantalla y el resultado de cada lector. Los guiones y los resultados manuales viven endocs/accessibility/protocol.json; lo que nadie ha ejecutado figura como «pendiente», no como «cumple». Una prueba exige la sección de accesibilidad en cada ficha. npx amset-ui generate: una aplicación entera desde su plano JSON. Valida el plano entero antes de escribir (bin/blueprint.mjs), parte del starter decreate, pone una página por entrada (vacía, o condashboard,customer-list,settingsosign-in), rehace el menú, copia los bloques y pasadoctorcon un solo intento de reparación;--checkañade el compilador. Las pantallas salen con datos vacíos y el servicio sin conectar fallando a las claras. La skillamset-uiexplica el plano a un asistente.- Cobertura de las capturas de referencia (
npm run visual:coverage). Dice qué historias de repaso y sistema no tienen sus cuatro capturas, cuáles las tienen a medias y qué capturas sobran; una prueba falla con las huérfanas y los juegos a medias. Las que faltan las genera el jobe2e:capturas. Límites de peso de la tabla y de la agenda subidos (44,6 y 20,8 KB) por lo anterior. - Lo que amset.tech copiaba a mano viaja ahora en el paquete (AMS-1922). El CHANGELOG en inglés sale como
@amset/ui/changelog.en.json(el texto castellano normalizado como clave, igual queprops.en.json) y una prueba falla si una entrada nueva no lo trae;SECURITY.md,benchmark/RESULTADOS.md,docs/rich-text-editor.mdydocs/adr/entran en el paquete;api.jsonda los tipos enteros (antes recortaba con «…» los demessagesdeAmsetProviderytriggerdeTooltip);EditableColoryColorFormatse exportan, yToneIconcuenta como componente y no entre los iconos. - Los mandos de la etiqueta del campo llegan también a la valoración, la subida de ficheros y el grupo de radios (AMS-737).
components.field['label-weight']y el resto delabel-*se escribían solo sobre.amset-field, así que esas tres piezas, que reutilizan la etiqueta del campo, se quedaban con otra tipografía.amset-ui themelos emite ahora también sobre ellas (el tamaño no: cada una lo ajusta con susize). - Las pruebas del comando
themevolvían a ejecutarse: se saltaban en silencio desde el pase a inglés porque buscabandist/styles/paleta.js. amset-ui theme --format hex: el tema con los colores en hexadecimal. Por defecto el CSS sigue saliendo enoklch(...), que es como se deriva el tema; con--format hexse convierte a#rrggbb(#rrggbbaacon transparencia). Un formato que no existe da un error claro.ImageViewer: «ajustada a la ventana» ya ajusta (AMS-1947). El escenario del visor era una rejilla con una filaauto, que crecía hasta el alto natural de la imagen, y elmax-height: 100%de la imagen no se resolvía contra nada: una captura de 1440 × 900 en una ventana de 1280 × 900 salía 41 px más alta que su escenario, y una vertical de 600 × 1600 en un móvil, 225 px. Medido en un navegador de verdad. Ahora la rejilla tiene una fila y una columna definidas (minmax(0, 1fr)) y a tamaño real vuelve a crecer con la imagen, para que el desplazamiento llegue hasta el final. Prueba nuevae2e/image-viewer.spec.ts: apaisada, cuadrada y vertical en tres tamaños de ventana, por los cuatro lados del escenario.- Los componentes se exportan a Figma (
npm run export:figma-components). Cada historia deComponentes/se renderiza en un navegador real y se vuelca lo que mide (cajas, rellenos, bordes, radios, sombras, textos, SVG); el plugin defigma/dibuja un conjunto de componentes por componente, con una variante por historia, y engancha a las variables deAMSET · Colorlos colores que coinciden con una sola. Las cajas son fijas (sin auto layout) y los estados solo salen si una historia los muestra. knobs.jsonvuelve a traer los mandos del campo de código y del encabezado de página (AMS-1903). Al renombrar sus clases (AMS-814) el catálogo dejó de verlos, y con ellos el probador de tema, el MCP y la tabla de variables de sus fichas. Los grupos siguen siendocodeyheading, y el selector es el nuevo. Una prueba nueva (scripts/knobs-groups.test.ts) falla si se pierden.- La tabla de datos de los gráficos de series ya tiene estilo (AMS-1902). Con
tableVisible,LineChart,AreaChartyStackedBarChartla enseñaban sin una sola regla: cifras pegadas y sin bordes. Ahora lleva las mismas que la deBarChart, con los encabezados alineados a sus cifras. - Las dos primeras series de un gráfico ya no salen del mismo color con una marca azul (AMS-1909). El orden de tonos por defecto era acento, información, éxito...; con una marca azul, acento e información eran dos azules. Ahora es acento, aviso, éxito, error, información, neutro, en
LineChart,AreaChart,StackedBarChartyRadarChart. Quien no pasabatoneverá la segunda serie en ámbar en vez de azul. - Los bloques salen enteros en inglés en el Storybook, y el manifiesto lleva su inglés (AMS-1913, AMS-1922). Los textos que un bloque arma con un dato dentro («8 en total», «Factura F-…», «¿Emitir la factura …?», «Facturas de …», «Historial de …») tienen su traducción, y la prueba de las demos ya ve los trozos fijos de esas plantillas, así que el siguiente no se escapa. El mensaje de longitud del alta de cliente pasa a «Máximo de caracteres: 500».
dist/sources/manifest.jsonpublica elende cada bloque.
0.17.1, del 19 de octubre de 2026
La 0.17.0 se publicó desde un pipeline depreanterior al arreglo deFlag, y salió con ese fallo: con SSR, la portada y cualquier página con una bandera daban el error 418 al hidratar. Si usasFlagcon un servidor, sube a esta.
Flagya no rompe la hidratación cuando el servidor ya tenía su región (AMS-1894). Un build estático pinta cientos de páginas en el mismo proceso, y desde la segunda la región de banderas ya estaba en la caché del servidor:Flagla pintaba sin frontera deSuspense, el navegador (que empieza de cero) la pintaba con ella, y React daba el error 418 al hidratar. Pasaba en la portada de amset.tech y en cada página con el selector de idioma. Ahora la frontera va siempre y el navegador conserva lo del servidor hasta que llega la región; lo prueba una hidratación real con el servidor caliente y el navegador frío.migrateydoctorven las hojas de antes importadas desde CSS (AMS-1912).migratereescribe también los@import '@amset/ui/estilos.css'yestilos/<hoja>.cssde tus .css, ydoctorda error si el CSS global importa una hoja que ya no existe (antes decía que no importabas ninguna).doctorcuenta ahora los mismos nombres quemigratecambia, también los de la 0.16, y señaladata-tema, que la biblioteca ya no lee.npx amset-ui create: una aplicación nueva con la biblioteca ya puesta (AMS-1786). Dos plantillas enstarters/: Next con App Router (la de por defecto) y Vite con--template vite. TypeScript estricto y ESLint de serie, sin Tailwind, el armazón (AppShell,TopBar,ThemeButtonySideNavmarcando la página actual) y cada página en su propio fichero: en Next, páginas de servidor; en Vite,React.lazyy la carga adelantada al pasar por el enlace. Después pasainit, así que salen con el.npmrc, el tema del color de--brandy el proveedor, ydoctorno tiene nada que decir. No escribe en una carpeta con algo dentro.npm run consumerscrea los dos con el CLI del paquete empaquetado y comprueba que se instalan, pasan sus tipos y su lint, compilan, navegan sin recargar y caben en su tope de JS (medido: 150 KB gzip en Next y 85 KB en Vite).- Bloques: siete pantallas enteras que
npx amset-ui addcopia al proyecto (AMS-1786). Panel de gestión (dashboard), listado de clientes (customer-list), alta de cliente (customer-form, con su esquemacustomerSchemapara validar también en el servidor), ficha de cliente (customer-detail), emisión de factura (invoice-form), ajustes (settings) e inicio de sesión (sign-in). Hechos solo con componentes de la biblioteca: importan@amset/uicomo tu proyecto y no copian código de ningún componente, así que no pesan nada.npx amset-ui list --blockslos enseña;addavisa de lo que necesitan y tu proyecto no tiene. En Storybook, en Bloques, con sus estados en claro, oscuro y móvil, y traducidos al inglés.scripts/blocks.test.tsvigila que solo importan la biblioteca, que no escriben ni un color ni una medida, y que los siete copiados compilan en estricto. useLocale()se exporta: el idioma del proveedor ({ locale, direction }) para formatear una fecha o un importe propio igual que la biblioteca. Lo necesitaban los bloques y cualquier aplicación que escribe sus propias cifras.TablecononRowPress: la fila se abre también con Intro. La fila ya se enfocaba con las flechas, pero solo respondía al clic: con el teclado no había forma de abrir una factura del panel. Intro sobre un control de dentro de la fila sigue siendo de ese control.
0.17.0, del 19 de octubre de 2026
La 0.15.0 y la 0.16.0 no llegaron a publicarse: esta es la primera versión publicada desde la 0.14.0, así que quien venga de la 0.14 recibe de golpe lo de las tres secciones (la 0.15.0, la 0.16.0 y esta).
- Los componentes de IA, configurables para meterlos en el chat del asistente. Lo que el chat real de AMSET hace y las piezas no dejaban configurar:
PromptInputadmitebusyModes(seguir escribiendo mientras responde -añadir al turno, parar y corregir, encolar-: radios de verdad sobre el campo yonSubmitrecibe la forma elegida en{ mode }; «Parar» pasa a estar junto a «Enviar»),inputRef,actionsa la derecha,onPasteText(un texto enorme se adjunta como fichero en vez de pegarse),onInputKeyDownpara atajos propios,inputProps(corrector, idioma, tope de longitud) yattachDisabled.ChatMessageganaactions(acciones propias junto a copiar, regenerar y editar),actionsVisibility,editor(editar en la misma burbuja) yhighlighted(coincidencia de una búsqueda, con un aro fijo y no una animación).ActivityLogganagrouppor evento (un historial de varias tareas, con título por sección y sin juntar lo igual de dos secciones),renderTextyloading. Y un componente nuevo,FindBar: la barra de buscar dentro de una conversación larga, con contador, siguiente, anterior y cerrar (Intro, Mayús+Intro, Escape). El segundo argumento deonSubmites nuevo pero no rompe a nadie; 36 pruebas con teclado y foco reales. - Las piezas de IA igualan al chat real del asistente (AMS-1773). Se leyó el chat de AMSET pieza a pieza y se añadió lo que hacía y la biblioteca no:
PromptInputganapastedFileName, un freno contra el rebote del doble envío (el mismo texto dos veces en 0,3 s),busyModeScope,history(flechas arriba y abajo, con el cursor al final) ysubmitOn="auto";Attachmentganaicon,detail,onPreview,compacty el anuncio de «Subido»;ThumbnailganaonPress,loading,errorycaption; nuevosImageViewer(el visor a pantalla casi completa, un diálogo de verdad) y los ganchosuseBlobUrlyuseElapsed;ArtifactCardganaonRetry;ModelSelector,maxFixes;Conversationganaloading,loadingOlder,onReachStart(mantiene el sitio de lectura al llegar mensajes arriba),stickToBottom,revealKey,highlightedyemptycomo nodo;ReasoningyActivityLogcuentan el tiempo en vivo (startedAt,endedAt,label,emptyIcon);Suggestions,allowPartial;UsageBadge,cachedTokens;FindBar,hasMore. Todo opcional: nadie que ya los use cambia. Dejado fuera a propósito: la vista previa de PDF (uniframesinsandboxabriría la puerta a ejecutar HTML ajeno; falta una pieza que solo admita PDF) y el markdown (sin dependencias en el núcleo). - Dos fichas ya no comparten título (AMS-1783): el campo de importe pasa de «Importe» a «Campo de importe» y el de tarjeta de «Tarjeta» a «Campo de tarjeta», como «Campo de texto» o «Campo numérico». Los ids de las fichas no cambian, y
scripts/docs.test.tsfalla si dos fichas vuelven a repetir título. - Los umbrales de cobertura suben a lo medido (AMS-1763): de 80/80/75/80 a 95/94/86/92 (líneas, funciones, ramas, sentencias). Pruebas nuevas para el arrastre de filas, el tirador de ancho de columna, el teclado de la rejilla y los estados de interacción, que eran lo más flojo (entre un 12 % y un 42 %).
- Rompe Rompe: tres clases raíz con el nombre del componente que de verdad las usa (AMS-814).
.amset-code(el campo de código de un solo uso) pasa a.amset-code-fieldcon todos sus hijos y modificadores,.amset-heading(el encabezado de página) a.amset-page-header, y.amset-list(la lista del panel de los desplegables) a.amset-listbox. Las variables--amset-code-*y--amset-heading-*no cambian. Quien estilara esas clases desde fuera tiene que usar los nombres nuevos. - El menú de acciones busca escribiendo con el buscador de la biblioteca (AMS-1200):
useTypeaheadya acepta una función para leer las claves del DOM y el menú dejó de llevar su copia. Sigue encontrando «Éxito» con «e» y dejando el espacio como parte de lo escrito. - La campana de la bandeja y el botón de abrir del buscador y del selector múltiple usan
amset-trigger-open(AMS-1293): se borran las dos copias de las reglas de «disparador con el panel abierto». Flagya no mete las 244 banderas en el JavaScript de la página (AMS-1893). Los dibujos van por regiones (Europa, América, África, Asia, Oceanía), cada una en un fichero que se pide cuando se pinta una de sus banderas: lo que cuenta deFlagpasa de unos 17,6 KB comprimidos de JS a unos 1,5 KB. Mientras llega el dibujo se enseñan las dos letras, y un servidor que espera a la suspensión (Next,renderToPipeableStream) ya trae la bandera en el HTML. NuevopreloadFlags()para tenerla en el primer pintado de un cliente. El presupuesto del paquete gana el escenarioFlagconstaticOnly, que solo cuenta lo que se descarga por imports estáticos.Table: la cabecera de fila (rowHeader) ya no sale centrada (AMS-1884). El<th>heredaba el centrado del navegador; ahora alinea al inicio y en negrita como el resto de celdas, yalign="end"/"center"siguen mandando.Pagination: unsizeque no está en la lista de tamaños ya se ve en el selector (AMS-1883). Antes el selector salía vacío o cortado; ahora el tamaño se añade a las opciones, sin duplicados y ordenado.RadarChart: la marca del máximo ya no pisa el nombre del eje de arriba (AMS-1895). Cuelga por debajo del contorno exterior.- La prueba de hidratación de
RelativeTimeya no depende de la zona horaria de la máquina (AMS-1896): fija eltimeZonepor defecto deIntl.DateTimeFormat, porque cambiarprocess.env.TZno vale en los hilos de Vitest. - Todo el código está en inglés: ni una variable, función, parámetro, propiedad ni tipo en castellano (AMS-1887). Se renombraron unos 3.400 nombres distintos (más de 37.000 usos en 800 ficheros) con el LanguageService de TypeScript, no con sustituciones de texto:
claveDeFila→rowKey,esClicDeEnrutador→isRouterClick,alPulsarFila→onPressRow,ordenable→sortable. Lo que un consumidor puede notar:bin/proyecto.mjspasa abin/project.mjs;dist/fuentes/(lo que leeamset-ui add) pasa adist/sources/; los ids de los hallazgos deamset-ui doctorvan en inglés (registry,layer-order,legacy-names,provider,router,project,installed,theme-not-imported); el estado de cada fichero deaddy deinitesnew/equal/overridden/kept/modified;AppShellusacontentcomoidpor defecto del<main>; el atributodata-photodeAvatarvaleloading/loaded.amset-ui migratesigue buscando los nombres antiguos (escritos como cadenas enbin/migrations.mjs); su lista teníainitialsValuesen vez devaloresInicialesy no encontraba esa prop, ya corregido. Lo vigilascripts/identifiers-english.test.ts, que recorre el árbol de sintaxis de todo el repositorio. LocaleProviderya no arrastra el selector de fechas (AMS-1885). Estaba definido dentro dedate-picker.tsx, así que cualquier aplicación que lo montara -que es todas, en todas sus páginas- se llevaba la hojadate.css(4,6 KB gzip, medido con Vite) y, en un build de Next con Turbopack, además el calendario, los trozos editables y el selector entero: ~45 KB sin comprimir en una portada que no pintaba ni una fecha. Pasa a su propio módulo,components/locale-provider.tsx. No cambia la API:LocaleProvider,LocaleProviderPropsyDEFAULT_LOCALEsalen del mismo índice y de las mismas subrutas. Medido en amset.tech: el JavaScript inicial baja 10 KB gzip y el de la biblioteca de 144 a 116 KB sin comprimir. Lo vigilansrc/locale-provider-weight.test.ts(el grafo de imports del proveedor no puede tocar fechas ni capas flotantes) y un escenario nuevo del presupuesto de peso,LocaleProvidercon 2,2 KB, que cuenta solo lo estático: el catálogo de claves que el proveedor pide conimport()en desarrollo son 20 KB que nadie descarga en producción y taparían un cambio de unos pocos KB.RichTextToolbaryToggleButton: la barra de un editor de texto enriquecido, sin meter el editor en la biblioteca (AMS-1787). La barra habla con el editor porstate(lo que hay puesto donde está el cursor) yonCommand, así que vale con cualquier motor de edición y@amset/uisigue sin dependencias. Es unaToolbar(una parada del tabulador, flechas, memoria del foco) con unToggleButtonpor formato (aria-pressed, atajo enaria-keyshortcutsy en la ayuda, Control o Comando según el sistema), grupos con nombre, enlace e imagen con paneles que solo se montan al abrirlos y que no son un<form>(vale dentro del formulario de la aplicación), y pulsar con el ratón no le quita el foco al editor. Las direcciones se comprueban connormalizeLinkHrefynormalizeImageSrc, que se exportan: lista blanca de esquemas, unjavascript:o undata:no pasan. La imagen pide texto alternativo o marcarla como decorativa (alt=""). Hojas nuevastoggle-button.cssyrich-text.css, con el marco del editor (.amset-richtext__frame) y la tipografía de lo escrito (.amset-richtext__content). Guía en inglés endocs/rich-text-editor.mdcon las recetas enteras, comprobadas por el compilador y atadas a la guía por una prueba, y un checklist para la pasada manual con NVDA y VoiceOver deTableeditable,Combobox,InvoiceLinesEditory la propia barra.WeekAgendaya no rompe la hidratación en inglés (AMS-1894). La cabecera de la semana sale deformatRange(«Oct 11 – 17, 2026»), y el ICU de Node escribe espacios finos (U+2009) alrededor de la raya donde Chrome pone espacios normales: pintada en el servidor, la agenda no casaba al hidratar y React tiraba el HTML de la página entera (error 418, medido en amset.tech, solo en inglés porque en castellano ese formato no lleva espacios). El calendario, los trozos de fecha y el cronograma ya lo normalizaban, cada uno con su copia; ahora los cuatro pasan porutils/plain-spaces.ts. Lo fija una prueba deweek-agenda.test.tsxque pinta la agenda enen-US(comprobado que falla sin el arreglo).- Decidido:
onPresssigue siendo el evento de pulsar, eisDateUnavailableeisLeafconservan su «is» (AMS-1762). Primer registro de decisiones del repositorio, endocs/adr/(en inglés):onPresses el nombre que ya escriben todos los fronts y une ratón, teclado y dedo en un solo evento;onClickse queda solo como puerta de migración en la familia del botón y enTextButton, siempre junto aonPressy con elMouseEventnativo (AMS-735).isDateUnavailablees un predicado, no un estado, eisLeafes la marca de tres estados de la carga diferida. No cambia ni una prop.prop-names.test.tsdeja de tolerarlos en silencio: los lista como excepciones con enlace al registro y falla con cualquier otro «is» o con unonClickpropio sinonPress. npx amset-ui migratecubre también lo que la 0.16 pasó al inglés (AMS-1894). Se escribió para lo que retiró la 0.14 y no sabía nada de la tabla de la 0.16; migrando amset.tech salieron los huecos. Ahora reescribe las 133 hojas por componente (@amset/ui/estilos/boton.css→@amset/ui/styles/button.css, también importadas sueltas),estilos.css,fichas.jsonymandos.json,Tooltip para→trigger,PasswordField nueva→newPasswordyerrorDeImporte→amountError. La tabla de hojas se emparejó por CONTENIDO, comparando las reglas de cada hoja de las dos versiones publicadas: las 133 tienen destino y ninguna cae en la misma que otra. Arreglado de paso: el codemod solo miraba las props deTableyFormescritas en el código, así que una entrada nueva enPROP_RENAMESse ignoraba sin avisar; ahora manda la tabla. Lo que sigue siendo a mano: las claveslinea1/linea2de una dirección (el compilador ya dice «¿quisiste decirline1?») y los atributosdata-tema/data-densidaddel HTML, que pueden ser de la propia aplicación.- Storybook: las descripciones de los controles dejan de usar nombres viejos y la guía de tallas ya enseña la columna «Largo» (AMS-1667).
SlotPickerdecíahoradonde la prop estime,WeeklyScheduleyRangeFilterdecían{ desde, to }donde es{ from, to }yclosedSchedule()salía comohorarioCerrado(); y las historias deSizeGuidedeclaraban la medidalargopero daban los valores bajo la clavelong, así que la columna salía vacía. Solo documentación e historias: sin cambios de API. BarChart: marcas de referencia y una segunda cifra por barra (AMS-1818).markersdibuja líneas que cruzan todas las barras a su valor (la media del grupo, un objetivo), en columnas y en filas: entran en el techo del eje, salen en la tabla con su nombre y su valor y no cuentan en el resumen del dibujo; la capa mide lo mismo que el sitio donde crecen las barras, así que la línea al 62 % cae exactamente donde acabaría una barra de 62 (medido a 0 px).DataBar.detailpone una segunda cifra visible detrás del número de la barra («+12 pts»), solo en filas, donde hay sitio; en columnas se queda en la tabla, junto a la nota. Nuevas variables--amset-bars-namey--amset-bars-row-gap. Lo que sigue pendiente de ese ticket (barra apilada acierto/blanco/fallo y el detalle por punto enLineChart) es API nueva y queda aparte.MonthAgenda: los eventos se pueden pulsar (AMS-1817).AgendaEventadmitehref(el evento de la lista del día es un enlace, que pasa por elRouterProviderigual queLink) yMonthAgendaadmiteonEventPress(key): sinhref, el evento pasa a ser un botón que avisa de su clave; conhref, avisa además al seguir el enlace. La tarjeta se ve igual y entera es pulsable (foco de 2 px, borde del color del evento al posar el ratón). Sin ninguno de los dos, el evento sigue siendo texto. Lo que sigue pendiente de ese ticket (rejilla de mes con títulos dentro de cada día y vista de agenda por rango) es API nueva y queda aparte.Table: el selector de filas por página ya no se corta, y la columnarowHeaderse alinea al inicio (AMS-1819). El selector medía 4,5 rem fijos y con una cifra más ancha salía «5…»: ahora es un mínimo y crece con su valor. Unth scope="row"lo centra el navegador, así que una columnarowHeaderquedaba centrada bajo una cabecera a la izquierda; la biblioteca dice ahoratext-align: startpara todas las celdasthque no sonendnicenter. Y la barra libre del toolbar (.amset-table__bar-free) ya no se encoge sin suelo hasta cortar un toolbar propio en 390 px: tiene un mínimo y baja de línea.PhoneField: el número cabe con prefijos largos en un campo estrecho (AMS-869). Con +351 en un campolgde 224 px el último dígito se salía 7,4 px; el disparador del prefijo gasta ahora 12 px menos (menos aire entre la bandera, el prefijo y la flecha: 97,2 → 85,2 px medidos) y el número tienemin-width: 0.- Ficha propia para 14 componentes que solo salían dentro de la de su grupo (AMS-1898).
AreaChart,StackedBarChart,Grid,Container,Center,ScrollArea,AspectRatio,Kbd,Mark,Highlight,Blockquote,ColorPanel,ToggleChipGroupyUploadertenían API pública y no tenían página, ni entrada en el índice, ni búsqueda por su nombre: ahora cada uno tiene la suya en Storybook y endocs.json(202 fichas), con su historia «Interactivo», su texto en inglés y su teclado y su accesibilidad medidos en un navegador.Uploaderno salía descrito en ningún sitio. Las fichas deStack,Code(ahora «Código en línea»),LineChart,ColorPicker,ToggleChipyFileUploaddejan de contar lo de las otras y las enlazan. De paso:Blockquoteconciteperdía los atributos que se le pasaban (ahora van al<figure>); enUploaderlos atributos,classNameystylecaían en la zona de dentro y no en la caja, y al quitar el último fichero el foco se iba albodyy el aviso se perdía (ahora vuelve al botón de elegir y se anuncia); el tirador del área de color se colocaba contra el flotante y no contra el área (52 px desviado enColorPicker, fuera del panel conColorPanelsuelto), y ahora cae donde dice su valor; en la tira de tono, Fin (o arrastrar hasta el borde) llevaba el tirador al principio porque 360° es 0°, y ahora acaba en 359°;ToggleChipGroupyUploaderestrenan--amset-chip-group-gapy--amset-uploader-gap; y el Storybook vuelve a construir, que importabaLocaleProviderde su sitio viejo. - Las capas flotantes se recolocan al cargar la tipografía (AMS-1176). Un reflujo que mueve el disparador sin cambiarle el tamaño (la fuente web que acaba de llegar) no disparaba ni el scroll, ni el resize, ni el observador del disparador, y la capa se quedaba con la medida vieja hasta el primer resize. Ahora se vuelve a colocar cuando resuelve
document.fonts.readyy cuando cambia el tamaño delbody. - Hidratar una pantalla con
DatePickercuesta un tercio: el panel cerrado ya no monta su calendario (AMS-1886).FloatingDialog-el panel del selector de fecha, del rango, del color, de los menús, delDropdowny de los filtros de la tabla- pintaba su contenido también cerrado, así que cadaDatePickermontaba un mes entero que nadie veía, y cada día creaba sus formateadores deIntl(calendarOf, la fecha larga, el número del día). Ahora el contenido se monta al abrir y se desmonta al acabar la salida, y los formateadores se crean una vez por idioma (utils/intl-cache.ts). Medido con el benchmark de apps (CPU ×4, mediana de 15): la pantalla de pedidos hidrata en 150 ms en vez de 264; el selector de fecha cerrado pasa de sumar ~250 ms a ~40. Aviso: quien buscase en una prueba algo de dentro de un panel cerrado ya no lo encuentra hasta abrirlo. - Rompe Rompe: TODA la API que se toca desde fuera pasa al inglés. AMS-550 tradujo componentes y props, pero quedaban en castellano funciones, atributos, ficheros, subrutas y la CLI, y los ejemplos de la referencia seguían con la API de antes (
<Boton variante="solido">). Qué cambia al subir de versión:Antes Ahora @amset/ui/estilos.css,@amset/ui/estilos/<hoja>.css@amset/ui/styles.css,@amset/ui/styles/<sheet>.css(las hojas, también en inglés:boton.css→button.css)@amset/ui/formulario,@amset/ui/esquemas@amset/ui/form,@amset/ui/schemasfichas.json,mandos.json,ejemplos.json,idiomas/*.jsondocs.json,knobs.json,examples.json,locales/*.json(y sus claves:component,module,title,family,description,stories,name,knobs,story,code)npx amset-ui tema,npx amset-ui mandos,npx amset-ui migrar,amset-tema.cssnpx amset-ui theme,npx amset-ui knobs,npx amset-ui migrate,amset-theme.css;addescribestyles/ystyles.cssdata-tema,data-densidaddata-theme,data-densitydata-cargando,data-seleccionada,data-deshabilitada(Card) y el resto dedata-*en castellanodata-loading,data-selected,data-disabled… (data-actual→data-current,data-sin-leer→data-unread,data-nivel→data-level…)@layer amset-ui.componentes@layer amset-ui.componentsClases amset-no-imprimir,amset-only-impresion,amset-for-imprimir,amset-card-pago*,amset-motion-esencial,amset-order-state__hitoamset-print-hidden,amset-print-only,amset-printable,amset-payment-card*,amset-motion-essential,amset-order-state__milestone(y--amset-order-state-milestone-gap)Animaciones amset-*-entra,-entrar,-salir,-aparece,amset-progress-va-y-viene,amset-rating-marcar-enter,-exit,-appear,amset-progress-back-and-forth,amset-rating-markcsvDeTabla,descargarCsv,separadorPorDefecto,filtrarFilas,ordenarFilas,paginarFilas,opcionesDeColumna,valorDeCeldatableToCsv,downloadCsv,defaultSeparator,filterRows,sortRows,paginateRows,columnOptions,cellValuecalcularFactura,errorDeImportecalculateInvoice,amountError; yrejectionReason/FileLimitsdeFileUpload, que el README daba por exportados y no lo estabanTooltip para,AddressFieldlinea1/linea2,parseHotkey(...).teclaTooltip trigger,line1/line2,.keyPasswordField nuevanewPasswordAvatar state="inline"(era «en línea» mal traducido)state="online"TaxZone:peninsula,baleares,canariasmainland,balearic-islands,canary-islands(ceutaymelillase quedan)TransferListmoveText(..., 'elegidos' | 'disponibles'),ROOT_FIELD = 'raiz''chosen' | 'available','root'Claves de textos CabeceraDeAgenda.*,FilaDeFranja.*,TablaDeDias.*,NumerosDePagina.*,TiraDeDias.*,HorasDelDia.*AgendaHeader.*,SlotRow.*,DayTable.*,PageNumbers.*,DayStrip.*,DayHours.*Ids de Storybook componente-boton--jugar,componentes-*,sistema-*,fundamentos-*component-button--playground,review-*,system-*,foundations-*; los exports de las historias también en inglés (Cargando→Loading) y el nombre que se ve, en castellanoTambién los ficheros y las carpetas del repositorio pasan al inglés (
src/components,src/utils,src/styles,src/form,src/schemas,src/table,src/messages/texts,boton.tsx→button.tsx,DISENO.md→DESIGN.md...). Lo que se nota desde fuera:npx amset-ui addcopia acomponents/,utils/,messages/texts/ystyles/en vez de los nombres de antes;DESIGN.mdyACCESSIBILITY.mdviajan en el paquete con su nombre nuevo; y los ficheros dedist/cuelgan de las mismas carpetas (dist/components/button.js), aunque desde fuera solo se llega a ellos por el indice y las subrutas deexports.Los scripts de
package.jsontambién:build,build:css,build:css-components,build:docs,build:examples,build:sources,build:locales,build:storybook,typecheck,verify,test:watch,e2e:engines,e2e:engines:gate,consumers,budget,audityexport:figma(antesconstruir*,tipos,verificar,test:mirar,e2e:motores*,consumidores,presupuesto,auditaryexportar:figma). Quien tenga unnpm run construiren su CI de una rama, lo cambia.Los parámetros de las funciones exportadas también se llaman en inglés, y
scripts/docs-api.test.tscomprueba con el compilador cada ejemplo de README, REFERENCE y Primeros pasos: imports, componentes, props y valores. - Storybook sin ruido de accesibilidad y con el código en inglés. Pasado axe sobre las 1.036 páginas (historias y documentación, claro y oscuro): las reglas de PÁGINA (
region,landmark-one-main,page-has-heading-one) saltaban en casi todas las historias por construcción y se apagan en el banco (src/bench/a11y.ts); las de orden de títulos y puntos de referencia repetidos se apagan solo en las páginas de repaso y de sistema, que apilan piezas sueltas. Lo que sí era del componente: la cabecera del calendario y de la agenda era un<header>que axe contaba comobanner, ahora es un<div>; la columna vacía deBoardySortableListvacía no tenía fila válida dentro delgrid(aria-required-children); el nombre accesible de cada columna deBoardllevaba «tarjetas» escrito en castellano y ahora sale del paquete de textos (Board.column, con plural, también en inglés);RangeFilterdeshabilitado llevaaria-disabledy deja de medirle el contraste a lo apagado; y la barra neutra deTimelineno llegaba a 4,5:1 en el tema oscuro. Por otro lado, 504 historias se llamaban en castellano (Cargando,ConError,SoloLectura…): ahora el export y el id de Storybook van en inglés, el nombre que se ve sigue en castellano, y los identificadores de las historias, el banco y.storybook/también (fotoDeMuestra→samplePhoto). - El panel lateral y el diálogo vuelven a parar la página de detrás y a decir en su disparador si están abiertos (AMS-1284). Al pasar los dos a
<dialog>se perdieron, sin un solo error, dos cosas que hacía la biblioteca anterior: la página de detrás se seguía desplazando con el modal abierto (medido en el banco: la rueda encima del velo la llevaba de 200 a 1.000 px) y el disparador dejó de llevararia-expandedyaria-controls, así que un lector no decía «expandido» ni se quedaba hundido con su capa abierta. Ahora, mientras hay unDialog, unDrawero unaCommandPaletteabiertos, la raíz llevaoverflow: hiddeny, si la barra de desplazamiento ocupa sitio,scrollbar-gutter: stable, para que nada salte de lado; lo que la página tenía en línea se devuelve al cerrar, y un diálogo abierto desde un panel no suelta la página hasta que se cierra también el panel. El disparador que se pasa portriggerrecibearia-expanded,aria-controls(solo con la capa abierta, apuntando aliddel<dialog>, el tuyo si le das uno) yamset-trigger-open. Cuesta 0,34 KB comprimidos en cada uno; el presupuesto de peso suma un escenarioButton, Drawer. benchmark/: la pantalla de referencia, medida en un navegador real (AMS-1784). Una pantalla (formulario con validación, tabla con orden y paginación, diálogo de confirmación) pintada en el servidor e hidratada en Chromium: se mide lo que descarga la página, el JS que se parsea y la hidratación, y pasa el mismo recorrido por roles y axe sin fallos.npm run benchmarkconstruye, mide y reescribebenchmark/RESULTADOS.mdcon las versiones, la fecha y las salvedades. Medido hoy: la pantalla carga 127 KB gzip.- El botón se queda con el foco al pulsarlo con el ratón también en Safari (AMS-1881). Safari no enfoca un
<button>nativo al pulsarlo, ni con el ratón ni con el dedo, así que al abrir unDialog, unDrawero un menú con un clic y cerrarlo, el navegador no tenía a quién devolver el foco y caía en elbody(quien seguía con el teclado volvía al principio de la página).ButtoneIconButtonenfocan ahora en elclick-no en elmousedown, donde Safari lo deshace y Firefox saca el aro de foco con el ratón- y antes deonPress, que es cuando la capa mira quién tiene el foco. Con el teclado no cambia nada. Cuesta 70 bytes comprimidos.
0.16.0, del 18 de octubre de 2026
npx amset-ui migrar: reescribe el código que usa los nombres que la 0.14 retiró (AMS-1831). Las props y las claves de columna deTable(enServidor,paginacion,ordenable,numerica...), elsentidodel orden,Formulariocon sus props (esquema,onEnviar...), los campos*Form,BotonEnviar,resolverDeEsquema,PropsCalendarRange,TableSortInputy la llamada aariaGranularity. Lo hace con el árbol de sintaxis del TypeScript del propio proyecto, no con expresiones regulares: distingue una prop de la misma palabra en un comentario, una cadena u otro componente, respeta tu sangría y tus comentarios, sigue imports con alias y con espacio de nombres, y las columnas declaradas en una variable. Lo que no puede decidir solo (un...props, un.sentidoleído en un manejador,PropsForm,ERROR_NETWORK_COUPON) sale como aviso con fichero y línea.--dry-runcuenta antes,--checksale con 1 si queda algo (para la CI), y es idempotente.doctoravisa (error) cuando quedan esos nombres. Probado con 19 pruebas, entre ellas una que compila el código de antes (falla) y el migrado (compila) contra los tipos reales de la biblioteca, y ejecutado en seco contra cuatro fronts de la casa sin ningún falso positivo.
0.15.0, del 17 de octubre de 2026
InvoiceLinesEditor: la fila ya no se sale de su caja (AMS-1812). Con la columna de retención (la de serie) la fila de una sola línea suma unos 65 rem de columnas, pero el umbral para pasar de tarjeta a fila era 52 rem, el mismo con y sin retención: entre 832 y 1040 px de contenedor sobresalía hasta 210 px y el importe y el botón de quitar quedaban fuera, recortados (sin retención, 38 px a 832). Ahora hay dos umbrales, 55 rem sin retención y 66 con ella; por debajo, cada línea es la tarjeta de dos columnas.e2e/factura-ancho.spec.tsbarre doce anchos y falla si la fila sobresale (comprobado que muerde). Lo destapó un barrido de axe sobre todas las historias del Storybook, que contaba el importe recortado como un contraste de 1,12:1.- Rompe ROMPE (arregla un fallo):
NumberField,QuantityPickeryMoneyFieldmandan en un formulario el numero, no el texto que se ve. Elnameiba en el campo visible, que lleva el numero con el formato del idioma: en un formulario nativo,10,5llegaba como"10,5"(un servidor lo lee comoNaNo como10),12.000como"12.000"(un12) y un importe como"1.234,56 €". Ahora elnameva en un<input type="hidden">con el numero canonico —10.5,0.21,12000,1234.56—, siempre con punto, sin separador de miles ni simbolo, ya cuadrado a minimo, maximo y paso como lo deja el campo al salir, y vacio si no hay cifra o no se entiende. Un campo apagado no se envia, como un<input disabled>. Aviso: el campo visible ya no llevaname; si una prueba o un script lo buscaba por[name=...], ahora encuentra el oculto. NuevocanonicalNumber()enutilidades/numero. 12 pruebas nuevas (numero-formulario.test.tsx); 9 fallan contra el codigo anterior. - Recargo de equivalencia en las facturas (AMS-1785).
calcularFacturayInvoiceLinesEditorsuman el recargo al IVA según su tipo -5,2 % con el 21 %, 1,4 % con el 10 %, 0,5 % con el 4 %, art. 161 de la Ley 37/1992- con su fila propia en el desglose (surchargeBreakdown) y en el total. Se pide para toda la factura (equivalenceSurcharge) o línea a línea (surchargePerLinetrae una casilla por línea y la de la línea manda). Se redondea una vez por tipo, como el IVA, no línea a línea (diez líneas de 0,10 dan 0,05 y no 0,10).surchargeRatescambia los tipos; el 0,62 % transitorio del 5 % no va por defecto. Sin pedirlo, los totales de siempre no cambian. Sale por el índiceSPANISH_SURCHARGE_RATESy los tipos nuevos. Item,ItemGroup,InputGroupyRadarChart. La fila genérica (conhrefoonPressel TÍTULO es el enlace o el botón y su zona se estira por CSS hasta cubrir la fila: un solo elemento interactivo, y las acciones por encima como botones aparte); el campo con segmentos pegados dentro de una sola caja, con el anillo de foco, la ayuda y el error deTextField; y el gráfico de radar (escala desde cero, unnulles un hueco y no un cero, resumen para el lector con lo más alto y lo más bajo de cada serie, y la tabla entera).radarPoint,ringPathyseriesPathsalen deutilidades/grafico-radar.ts.TextButton, la acción de texto (AMS-1792). Un<button>con el aspecto y los tonos deLinkpara una acción que va dentro de una frase (reintentar, dejarlo): el lector dice «botón», responde a Intro y Espacio, estype="button"por defecto y no envía un formulario dos veces mientrasloading. Sin la caja ni el relleno deButton: hereda tamaño y altura de línea, y con el dedo la zona de pulsación crece a 44 px con un pseudo-elemento, sin mover la línea (un<button>es siempre inline-block: el relleno sí la estiraría). Sustituye el botón escrito a mano que cada front montaba para esto.HoverCard,MenubaryNavigationMenu. La tarjeta que sale al pasar el ratón por un enlace (su contenido se puede tocar:closeDelaydeja llegar hasta ella, Tab entra porque va justo después y Escape la descarta, WCAG 1.4.13); la barra de menús de escritorio (un solo tabulador, flechas laterales con vuelta y al revés en RTL, y con un menú abierto el ratón abre el vecino, que unActionMenumodal no puede); y la navegación principal con despliegues de ARIA (enlaces de verdad, el panel justo tras su botón,aria-current, Escape y salir con Tab la cierran, y pulsar un botón que el ratón acaba de abrir lo FIJA en vez de cerrarlo).useMenuListsale deMenuPanelpara compartir el teclado de la lista con la barra de menús.- El prefijo del teléfono sale completo en la lista (AMS-1703). Dominica, Bahamas, Jamaica y el resto del +1 se veían todos como «+1»; ahora la lista y el nombre accesible dicen «Dominica (+1 767)», y con varios rangos «+1 809 · 829 · 849» en el nombre accesible y el título, y «+1 809…» en la fila (el completo partía el nombre del país en dos líneas: 43 px la fila de República Dominicana frente a 36). El disparador sigue en «+1» (las cifras del reparto ya van en el número escrito, y repetirlas daría «+1 767 767 555…»); Estados Unidos, Canadá (demasiados rangos) y los repartos de una o dos cifras se quedan en el indicativo. El E.164 no cambia.
dialLabel()sale por el índice. ModelSelectoryArtifactCard(+ArtifactFrame), los dos que faltaban para migrar el chat del asistente. El selector de motor y modelo es de presentación pura (el catálogo y la elección los pone quien lo usa): dos filas de radios nativos —las flechas saltan los motores apagados—, un motor apagado dice su motivo con texto, y si lo elegido se ha apagado el botón se marca y el panel avisa;fixLabel+onFixpone el enlace para arreglarlo. La tarjeta de artefacto trae la miniatura o el icono según el tipo, abrir y descargar como botones HERMANOS de la cabecera, y la vista previa solo se monta abierta;onVisiblepide el contenido de lo que entra en pantalla.ArtifactFrameaísla el HTML consandbox="allow-scripts"y nuncaallow-same-origin, y no se deja quitar desde fuera. Salen sueltosselectionLabelyartifactKind.npx amset-ui initynpx amset-ui doctor(AMS-1778).initdeja un proyecto con la biblioteca puesta como toca: la línea@amset:registrydel.npmrc(nunca un token),@amset/ui/estilos/base.cssarriba del CSS global —lo que fija el orden de las capas antes que Tailwind 4 sin pagar el CSS dos veces—, tu tema generado con el comandotema(contraste comprobado),AmsetProvidery el enrutador de Next montados en el layout,_appo<App />. Es idempotente, no pisa un fichero tuyo (solo le añade líneas),--dry-runcuenta lo que haría y--installinstala con tu gestor.doctordiagnostica un proyecto que ya la usa y sale con código 1 si hay errores: Tailwind 3 sin orden de capas,data-temacon otros valores, token como${VARIABLE}en el.npmrc, Pages Router sintranspilePackages, y avisos por el tema sin generar, los enlaces que recargan la página, Jest sin mapa de CSS. Probado con proyectos de Next y de Vite sin configurar que pasan porinit, compilan, se abren en Chromium y llevan el color de marca pedido (npm run consumidores,scripts/init-doctor.test.ts).reffunciona con React 18 (AMS-1782). En React 18 un componente de función no reciberefcomo prop y lo descarta:<Button ref={nodo}>lo dejaba anullyDropdownno abría su panel desde unButton(medido con React 18 real).Button,IconButtonyFloatingButton-los únicos tres que exponenrefen su tipo público- van ahora envueltos conwithRef, unforwardRefque entrega elrefal render; en React 19 todo sigue igual. Nuevonpm run test:react18(y su paso en la CI): instalareact@18aparte y pinta cada ficha y los botones conrefsin que salga ni una excepción ni un aviso de React, así que «React 18 y 19» deja de ser una promesa sin vigilar.
0.14.0, del 17 de octubre de 2026
- Rompe Rompe: fuera los nombres de antes que quedaban como alias (AMS-1761). Un alias obsoleto es código muerto: se van los de
Table(enServidor,seleccionadas,alSeleccionar,paginacion,columnasConfigurables,alCambiarColumnas,redimensionables,alPulsarFila,barraSuperior; en la columnanumerica,ordenable,filtrable,ocultaPorDefecto,pegada,cabeceraDeFila; ysentidoen el orden, con su tipoTableSortInput), los nombres en español del formulario (Formulario,CampoTextoForm,CampoAreaForm,SelectorForm,SelectorFechaForm,CasillaForm,InterruptorForm,GrupoRadioForm,BuscadorForm,BotonEnviar,resolverDeEsquema,PropsForm,EsquemaEstandar,IncidenciaEstandar,ShippingAnswer),PropsCalendarRange,ariaGranularityyERROR_NETWORK_COUPON. La tabla de equivalencias está en el README. amset-ui-web y amset-assistant suben a esta versión a la vez. - El calendario del
DatePickerya no vuelve a aparecer al cerrarse (AMS-1766). El cierre de las capas esperaba a TODAS las animaciones y transiciones del panel, también a las que pone el CSS de quien usa la biblioteca: en amset-assistant, una transición de 200 ms enscrollbar-colorhacía que la salida de 100 ms acabara, el panel volviera a verse entero unos 80 ms y solo entonces se cerrara. Ahora se esperan las animaciones de@keyframesy las transiciones que mueven la capa (opacity,transform,translate,scale,display,overlay), y la salida de las capas flotantes y delTooltipse queda en su último fotograma (forwards) hasta elclose(). - README en inglés (AMS-1716).
README.mdpasa a ser la portada en inglés —lo que muestran npm y GitLab—: instalación, uso, estilos, temas, Next.js, idiomas bajo demanda, formularios, peso medido, pruebas, herramientas para asistentes de IA y licencia. La referencia completa por componente sigue en castellano enREADME.es.md, que ahora viaja en el paquete.src/readme-ingles.test.tsata la portada a la realidad: cadanpm runy cada subruta@amset/ui/...que cita tiene que existir, y la prueba de documentación vigila tambiénREADME.es.md. - Referencia en inglés (AMS-1774).
REFERENCE.mdtraduce al inglés la referencia completa por componente (README.es.md, 2.674 líneas) y viaja en el paquete, enlazada desde la portada.src/referencia-ingles.test.tsimpide que se separen: mismos encabezados en el mismo orden, mismos bloques de código y mismas tablas, y las mismas variables--amset-*y componentes citados en las dos. - Componentes para interfaces de IA:
PromptInput,ChatMessage,Reasoning,ToolCall,ActivityLog,Attachment,SuggestionsyUsageBadge. Salen de lo que ya hacía el chat del asistente de AMSET, sin perder nada: la caja crece con el texto hasta un tope, envía con Intro y baja de línea con Mayús+Intro (sin enviar mientras se compone con un IME), recupera lo último con la flecha arriba, admite ficheros pegados, arrastrados o elegidos y pasa de «Enviar» a «Parar» mientras responde; el registro sigue el final solo si ya se estaba abajo (useStickToBottom, exportado) y junta los eventos repetidos; una llamada a herramienta que falla se abre sola. Todo dicho con texto además de con color, sin dependencias y con un CSS por componente. Salen sueltosformatDuration,formatBytes,collapseRunsyisNearBottom. Un@keyframesnuevo,amset-caret-blink, compartido por los tres cursores. FormTreeSelect, el campo conectado que faltaba (AMS-1675).@amset/ui/formularioya tiene un campo conectado por cada campo con valor:FormTreeSelectguarda la clave del nodo elegido, o la lista de claves conselection="multiple"(en el orden del árbol, no en el del clic), y enseña el error del esquema en el campo.FormCascaderyFormTreeSelectentran también en las tablas de campos conectados de la referencia, de donde faltaba la primera.- El orden del CSS ya no decide como se ve un campo (AMS-1775). La caja y la entrada de todos los campos (
.amset-field__controly.amset-field__entry) pesaban una clase, lo mismo que sus variantes (el area de texto, el campo de numero, el buscador...), y mandaba la que el empaquetador dejara la ultima: en la biblioteca sola el orden lo fijaindex.css, pero en un consumidor no. Medido en un navegador con las hojas en el orden malo: elTextAreasalia con una caja de 36 px alrededor de un textarea de 56 px (en Next 16 con Turbopack, 46 alrededor de 67), y con el interlineado ennormalen vez de 21 px. Ahora las dos bases van en:where()y pesan cero: cualquier variante, y cualquier CSS de quien usa la biblioteca, las gana siempre. En los dos ordenes la caja mide lo mismo (80,3 px) y no desborda. Una prueba nueva (orden-del-css.test.ts) impide volver a escribir la base con una clase suelta. Sin cambios en la API. - Firefox y WebKit en el e2e, y lo que encontraron (AMS-1716).
npm run e2e:motorespasa el e2e de comportamiento en los dos motores (opt-in conAMSET_MOTORES=1, sin axe ni capturas), y la CI lleva una puerta rápida,e2e:motores. Dos fallos reales que Chromium no veía: en Safari, al cerrar un desplegable o una confirmación el foco se perdía en el<body>(Safari no da el foco a un botón al pulsarlo con el ratón, y el navegador devuelve el foco a «lo que lo tenía»); ahora la capa lo devuelve a su disparador si se ha perdido. Y en Firefox y Safari el diálogo, el panel lateral y la paleta de comandos desaparecían de golpe al cerrarse (solo Chromium animadisplayyoverlay); ahora la salida va pordata-exitingyclose()espera a que acabe, igual en los tres motores. Sin cambios de API. - La marca ya admite un segundo color:
secondary. Hasta ahora solo existíabrand(el acento), así que un diseño con dos colores de marca no se podía expresar. Nueva familia de tokens--amset-secondary,-hover,-active,-text,-softe-ink(violeta por defecto, en claro y en oscuro, con sus pares de contraste AA medidos), clavepalette.secondaryenamset-ui.config.js, ytone="secondary"en todo lo que ya aceptabaaccent(botones, etiquetas, avisos, tarjetas, avatares, gráficos, texto, enlaces…). Sin cambios para quien no la use. - Y un tercero:
tertiary. Misma receta quesecondary: tokens--amset-tertiary*(verde azulado por defecto, claro y oscuro, contraste AA medido),palette.tertiaryytone="tertiary"en todo lo que aceptabaaccent. - El segundo y el tercer color llegan a TODOS los componentes con
data-tone. Casillas, interruptores, deslizadores, pestañas, pasos, el día elegido del calendario, el aro de foco… pintaban siempreaccenty no teníantone. Ahoradata-tone="secondary"odata-tone="tertiary"en el propio componente o en cualquier contenedor repinta todo lo de dentro (remapea--amset-accent*y--amset-focus), sin tocar cada componente. - La documentación cuenta los tres colores de marca. Fundamentos → Color lista las familias
secondaryytertiary(53 roles) y estrena «Los tres colores de marca», una demo en vivo detoneydata-tone; Primeros pasos explica las claves nuevas depalettey las fichas que hablaban de «seis tonos» ya nombran los ocho. npx amset-ui add: copiar el código de un componente a tu proyecto (AMS-1771). Para quien no quiere depender de una versión publicada:add button iban-field(o--all) copia el código con todo lo que necesita, sus hojas de estilo y un catálogo de textos generado solo con lo copiado;listenseña los 190 que hay. Quita el.jsde los imports (--keep-jslo deja), no pisa un fichero que ya habías cambiado (--overwritesí) y apunta lo copiado enamset-ui.json. El paquete publica el código endist/sources/(npm run construir:fuentes). Medido: lo copiado compila en estricto yadd --alltambién.@amset/ui/esquemas: las comprobaciones de los campos españoles, sueltas (AMS-1771).documentSchema,ibanSchema,cnaeSchema,socialSecuritySchema,plateSchema,cardNumberSchemaypostalCodeSchema: la letra del NIF, el módulo 97 del IBAN, la división del CNAE... como esquemas Standard Schema que además traencheckymessagepara engancharlas a Zod o Valibot sin que la biblioteca dependa de ninguno. Mismos mensajes que el campo, enesyen. Subruta de servidor: sin'use client'.ExportButtonyexportElement: gráficos y tablas a PNG, SVG y PDF (AMS-1771). En el navegador y sin librerías: el elemento se copia con los estilos ya calculados, se mete en un SVG conforeignObjecty de ahí salen los tres formatos (elementToPng,elementToSvg,elementToPdf,downloadBlob).scalehasta 4 para imprimir; abarca lo que se sale de la caja; incrusta tipografías e imágenes. El PDF lleva la imagen, no texto. Un elemento oculto falla conExportError. Medido en Chromium conLineChart,BarChartyDonutChart. Nuevos textosExportButton.*.Tablecon 100.000 filas, medido (AMS-1771). ConvirtualizedymaxHeight: primer pintado 121 ms con 16 filas en el DOM, 61 fotogramas por segundo al desplazar y 42 ms para ordenar. No cambia código: solo se documenta con la medida.ACCESIBILIDAD.md: lo que se mide (axe con WCAG 2.0, 2.1 y 2.2 AA), lo que se vigila aparte y lo que no se ha medido todavía, sin declarar un nivel que no se ha comprobado (AMS-1771).
0.12.0, del 16 de octubre de 2026
- La biblioteca se prueba en proyectos reales (AMS-1716).
npm run consumidoresempaqueta el paquete, lo instala en un proyecto Next (App Router) y en otro Vite, los compila, los sirve y los abre en Chromium: tipado de un proyecto ajeno, HTML de servidor traducido, hidratación sin errores, interacción, idiomas perezosos que no se descargan si nadie los pide y tope de JS de primera carga. Jobconsumidoresen la CI. Sin cambios para quien usa la biblioteca. - Los avisos para quien programa ya no hablan ni cargan nada en producción (AMS-1716). Con
process.env.NODE_ENVdetrás de una guardatypeof process, en el navegador la producción se tomaba por desarrollo: concatalogsel proveedor descargaba el catálogo entero de la biblioteca (18 KB gzip, medido en un Vite real) para comprobar las claves, y los avisos de props obsoletas de la tabla salían por consola. Ahora lo decideisDevelopment(), que deja la expresión que sustituye el empaquetador sin guarda. - Elegir un día en un
DatePicker(o cualquier capa) dentro de unDialogya no cierra el modal. Elclosey elcanceldel<dialog>hijo subían por el árbol de React hasta elonClose/onCanceldel modal que lo contiene y lo cerraban (también con Escape dentro del calendario).Dialog,Drawer,CommandPaletteyFloatingDialogsolo atienden el cierre que es suyo. - El texto sobre un relleno de color es claro en los dos temas (AMS-1737). En el tema oscuro los rellenos sólidos (
accent,error-solid,success-solid,warning-solid,info-solid) eran claros y llevaban texto casi negro: con una marca roja o verde salía letra negra sobre rojo o verde. Ahora el relleno tiene la misma luminosidad oscura en los dos temas y su tinta (*-ink) es casi blanca, teñida del tono de la familia, no blanco ni negro puros. Medido: con cualquier tono y croma de marca la tinta llega a 4,5:1 sobre su relleno en claro y en oscuro (prueba nueva enpaleta.test.ts).highlightpasa a negro también en oscuro, así que el pasar el ratón oscurece siempre. Aviso: en oscuro los botones primarios y de estado se ven más profundos que antes; si forzabascolorscon un relleno claro, el generador se negará a escribir el tema. - El texto de un
ToggleChipactivado se lee con cualquier color (AMS-1737). Llevaba el 75 % del color del chip y el resto de la tinta del tema: un rosa intenso en claro o el acento en oscuro quedaban por debajo de 4,5:1. Ahora lleva el 30 % (--amset-chip-ink-share), y una prueba barre el espacio de color en los dos temas. Markdeja de pintar texto oscuro sobre negro. Usaba el rolhighlight(hacia dónde se oscurece un relleno) como fondo; ahora va sobregold-softcon la tinta principal, y el par está en las pruebas de contraste.- Lo hecho dentro de una barra del cronograma oscurece la barra en vez de aclararla, para que las fechas sigan leyéndose sobre ella.
- Los componentes de bloque ocupan el ancho de su hueco sin pedirlo. Campos y
Select, tabla, acordeón, tarjetas, alertas, pestañas, paginación, gráficos, listas y el resto de componentes de bloque llevaninline-size: 100%desdebase.css: metidos en una fila flexible se encogían hasta su contenido (en la web, 80 componentes; elSelectcambiaba de ancho al elegir). Los que miden lo que su contenido (botones, etiquetas, avatares, interruptores,SegmentedGroup, texto) no cambian, y una prueba obliga a clasificar cada componente nuevo. Dentro de otros componentes se mantienen compactos el prefijo del teléfono, las horas del horario semanal y el selector de filas de la paginación. Calendarsuelto se mide contra su caja y sale centrado: los días encogen con el hueco en vez de con la ventana (en una tarjeta de 238 px el mes medía 274 y se salía) y el calendario ocupa su hueco hasta lo que mide el mes.Gridiguala el alto de lo que va en cada celda (equalHeight, por defectotrue): una tarjeta dentro de un envoltorio llena la fila igual que si fuera hija directa.Linkva subrayado por defecto (underline="always"): dentro de un párrafo el color no basta (axelink-in-text-block, 1,12:1 medido).underline="none"lo quita para menús y barras.SegmentedGroup: el relleno ya no sale volando tras desplazar la página. El punto de partida se medía al elegir la opción anterior; con la página movida entre medias arrancaba a miles de píxeles y el marco enseñaba la barra de desplazamiento.benchmark/: peso de las piezas más usadas (P1.6).npm run benchmarkempaqueta con Vite cada una (Button;Button,SelectyDatePicker;Table) y cuenta JS y CSS en gzip, con las versiones fijadas y las salvedades escritas enbenchmark/README.md.- Pedir un componente ya no trae los textos de todos (AMS-1718). El catalogo
es/enentero (65 KB sin comprimir) viajaba con cualquier componente que dijera algo: unButtonpesaba 23,4 KB gzip y ahora 5,9;Tablepasa de 54 a 38 KB y la agenda de semana de 35 a 18. Cada componente trae solo su paquete (src/mensajes/textos/);ESyENsiguen siendo el catalogo entero.translatorFor(locale, paquetes, mensajes?)yuseMessages(paquetes)reciben ahora los paquetes del componente, ycatalogForse sustituye porcatalogEntry. Un texto pedido a un paquete que no lo trae sale en la consola y hace fallar las pruebas. - Seguridad:
LinkyButtonLinkcontarget="_blank"llevanrel="noopener noreferrer"(AMS-1716). Antes no llevabanrely la pagina de destino recibia la ventana de origen y la cabeceraReferer. Elrelque pasa quien lo usa se respeta y se completa. La direccion de seguimiento deOrderStatustambien llevanoopener. SECURITY.md, auditoria y presupuesto de peso en CI (AMS-1716).SECURITY.mdcon como informar y que hace y no hace la biblioteca;npm run auditar(altas y criticas de lo que viaja) ynpm run presupuesto(peso gzip de lo que se importa, medido con Vite) en el jobverificar;src/seguridad-urls.test.tsxfija quejavascript:queda bloqueado.- axe pasa tambien
wcag22aa(AMS-1716): en axe-core 4.13 es la reglatarget-size; 568 casos sin violaciones. - Todas las descripciones de props, también en inglés (AMS-1687). Las 300 que faltaban, de componentes antiguos y de los nuevos, ya tienen su versión en
src/mensajes/props.en.json; una prueba nueva enscripts/api.test.tsfalla si una prop nueva se queda con su descripción solo en castellano. LineChart,AreaChartyStackedBarChart(AMS-1698). Gráficos de varias series con hueco (null), eje con números redondos, leyenda, valores al pasar el ratón, resumen y tabla para el lector de pantalla. Hojaseries.css, textosSeriesChart.*.- El panel del
Selectse topa a 20 rem. Con muchas opciones el panel llegaba a medir toda la ventana, con una barra de desplazamiento kilométrica. Ahora mide como mucho 20 rem (unas ocho opciones y media) y el resto se recorre con scroll; se cambia con--amset-select-panel-max-height. Cualquier capa flotante puede pedir su propio tope con--amset-floating-cap; si la ventana es más baja, el panel sigue encogiéndose. WorkCalendarView,createWorkCalendary lectores de festivos (AMS-1700). Calendario laboral sin festivos incrustados: se cargan de la fuente oficial conparseIcsHolidays/parseCsvHolidays. Resumen de días laborables por mes,isDateUnavailablepara los selectores de fecha. Hojacalendario-laboral.css, textosWorkCalendar.*.WeekAgendaen móvil: en pantalla estrecha pasa a la lista de días con la franja de la semana arriba, como Teams (layoutauto|full|compact). En táctil las citas se pulsan y no se arrastran.WeekAgenda(AMS-1700). Agenda de semana y de día con citas que se arrastran y se estiran (puntero y teclado), reparto de las que coinciden, línea de «ahora» y avisos al lector de pantalla. Hoja propia,agenda-semana.css; textosWeekAgenda.*en español e inglés;placeBlocks,moveBlockyresizeBlocksalen sueltos.- Rompe ROMPE: un solo nombre para cada prop, el del HTML y sin «is» (P0.1, AMS-1727). La biblioteca tenia dos dialectos: 44 controles decian
isDisabled,isReadOnly,isRequired,isInvalideisSelectedy 24 piezasdisabled,readOnly,selectedyopen, asi que quien montaba un formulario tenia que acordarse de cual iba en cada uno. Ahora sondisabled,readOnly,required,invalid,selected,open,indeterminate,dismissableykeyboardDismissDisableden todos, y no queda ningun alias de los nombres de antes: hay que cambiarlos al actualizar (en JSX,isDisabledpasa adisabledy asi con cada uno).MultiSelectse queda condisabled,readOnlyyrequiredcomo todos, y desaparecen sus alias de «is». Se quedanisDateUnavailable(es una función, no un estado),onPressy elonClicknativo, yuseDisclosuresigue devolviendoisOpenjunto a sus verbosopen()yclose(). Una prueba (nombres-de-props.test.ts) impide que vuelva un nombre con «is». InvoiceLinesEditor(AMS-1700). Editor de las líneas de una factura con IVA y retención de IRPF por línea y el desglose de totales;calcularFacturaylineAmountsalen sueltos. Hoja propia,factura.css. TextosInvoiceLinesEditor.*en español e inglés.Tableavanzada (AMS-1699). Agrupación por columna, filtros con varias condiciones, exportar a CSV, columnas guardadas entre visitas, edición en celda y virtualización de filas. Todo opcional: sin las props nuevas la tabla se comporta como antes. Nuevos textosTable.*en español e inglés;csvDeTablaydescargarCsvsalen sueltos.ContextMenuy submenús enActionMenu(AMS-1698). El menú del clic derecho, con teclado, yMenuAction.childrenpara listas anidadas.CodeField: rellenar o vaciar una casilla del medio ya no corre las cifras de la derecha (AMS-1747). En modo controlado el hueco no sobrevivía a que el padre devolviera el valor sin huecos, y teclear en la casilla 5 con "123" puesto dejaba el 7 en la 4. Ahora las casillas conservan sus huecos mientras el padre devuelva lo queonChangeemitió, yonChange/onCompleteentregan siempre el código sin huecos (antesonCompletepodía llegar con un espacio dentro). Escribir sobre una casilla que ya tiene cifra la sustituye en vez de repartir las dos, y una casilla vacía ya no se pinta con un espacio.- Firefox y WebKit en el e2e, y lo que encontraron (AMS-1716).
npm run e2e:motorespasa el e2e de comportamiento en los dos motores (opt-in conAMSET_MOTORES=1, sin axe ni capturas), y la CI lleva una puerta rápida,e2e:motores. Dos fallos reales que Chromium no veía: en Safari, al cerrar un desplegable o una confirmación el foco se perdía en el<body>(Safari no da el foco a un botón al pulsarlo con el ratón, y el navegador devuelve el foco a «lo que lo tenía»); ahora la capa lo devuelve a su disparador si se ha perdido. Y en Firefox y Safari el diálogo, el panel lateral y la paleta de comandos desaparecían de golpe al cerrarse (solo Chromium animadisplayyoverlay); ahora la salida va pordata-exitingyclose()espera a que acabe, igual en los tres motores. Sin cambios de API. - Campos sin caja:
variant="bare"y--amset-field-touch-height. Un campo que va dentro de otra caja (la barra de un chat, un buscador dentro de una barra de herramientas) tenia que quitarse la suya a base de CSS propio, y al quitarla perdia tambien el relleno y el alto, asi que el texto quedaba pegado al borde y el campo medía mas o menos que los botones de al lado.bareenTextField,TextAreay el resto de campos no pinta borde, fondo ni raya, conserva el alto y el relleno del tamaño y marca el foco con un anillo (y el error con la raya de abajo). Y--amset-field-touch-heightfija el alto de la caja con el dedo (por defecto 48 px, como siempre) para el front que usa controles mas bajos en el movil. Aditivo.
0.11.0, del 16 de octubre de 2026
- Los enlaces precargan el destino (como
next/link).RouterProvideraceptaprefetch(router.prefetchde Next):LinkyButtonLinklo llaman una vez por destino al posar el raton o recibir el foco. No lo hacen con un destino de fuera, contargetpropio ni apagados. Opcional: sinprefetchno cambia nada. Next sin cablear nada:@amset/ui/nextexportaNextRouterProvider(App Router) y@amset/ui/next/pagesexportaNextPagesRouterProvider(Pages Router); se ponen una vez arriba del arbol y todos los enlaces navegan y precargan por el enrutador de Next.nextes peer opcional: quien no importe esos subpaths no lo necesita. Nueva ficha Enrutado en Storybook (y, con ella, en la web) que explica el proveedor genérico y los dos de Next. PageHeader,ButtonGroupyComboboxya no llevan'use client'. No usan estado, efectos ni manejadores propios: en Next se renderizan como componentes de servidor y su codigo no viaja al navegador. Medido en una app de Next 16 con una pagina de servidor que los usa:PageHeaderyButtonGroupdesaparecen del JS de cliente (-1,4 kB, -469 B gzip). El resto de componentes con la directiva si usan hooks, contexto o manejadores.- Textos que la biblioteca seguía escribiendo a mano en castellano (AMS-1720). Con el idioma en inglés salían en castellano: la calculadora de cuotas («al mes durante», «La última…», el mínimo y «TIN / TAE»), el selector de envío («Te faltan… para que salga gratis»), el aviso del tablero al mover una tarjeta, el «de 5» de la valoración, el contador de la visita guiada, la pregunta de después de la encuesta NPS (su función por defecto se llamaba sin traductor), los errores de dirección y de tarjeta y los nombres de color y de canal del selector de color. Ahora salen del catálogo de mensajes.
addressErrors,postalCodeError,cardNumberError,expiryError,cvvError,cardErrors,moveAnnouncementycolorNameaceptan el traductor como último argumento opcional y, sin él, devuelven lo mismo que antes; nuevochannelName. La prueba del catálogo ya no se conforma con buscar tildes: recorre el JSX y las plantillas de cada componente. - Storybook: las demos, la documentación y los controles, de verdad en el idioma elegido (AMS-1720).
- Los textos de las demos (botones, tablas, avisos, importes) salen en inglés: los args llegan traducidos al componente y el resto se cambia en la vista previa con
src/banco/demos.en.json(3.400 textos; los nombres propios y los códigos se quedan). Los importes y fechas que las historias formateaban con'es-ES'fijo usansrc/banco/formatos.ts. En castellano todo queda igual que antes, también lo que fotografían las capturas de referencia. - Veinticinco historias de Repaso y Sistema envolvían la demo en un
LocaleProvidersin idioma que forzaba el castellano: fuera. - Las fichas cuyas historias van en su propio marco (
inline: false) salían en castellano: cada marco recibe el idioma y la página lo lee de su propio estado. - «Jugar» pasa a llamarse «Interactivo» (Playground en inglés). Los ids no cambian.
- Pulsar un enlace de una demo ya no navega la ventana entera (Storybook pone
<base target="_parent">): un ancla desplaza, uno de fuera abre otra pestaña. - Los controles que el componente solo lee al montarse (
default…,initial…, el estado de la historia) ya funcionan: la demo se vuelve a montar al cambiarlos, salvo en Capas, donde cambiar el título de un diálogo abierto no lo cierra.currencyylocalese eligen de una lista: escritos a mano tumbaban la historia. - La documentación de 198 ficheros usaba los nombres de la API anteriores a AMS-550 (
alCambiar,valorInicial,plana,fantasma,clave…): ahora usa los reales (onChange,initialValue,flat,ghost,key…), comprobado contra las props y tipos de cada componente. - Siete historias pedían iconos con su nombre viejo («Subir», «Carrito») y pintaban la palabra: arreglado, con una prueba.
- Los textos de las demos (botones, tablas, avisos, importes) salen en inglés: los args llegan traducidos al componente y el resto se cambia en la vista previa con
- Render en el servidor con idiomas perezosos (AMS-1721).
loadMessages(locale, catalogs)resuelve el catálogo del idioma antes de renderizar (componente de servidor,loader,getServerSideProps) y su resultado viaja comomessagesenAmsetProvider/LocaleProvider: el HTML sale traducido y se hidrata sin avisos. Comparte la caché depreloadCatalog, no lleva'use client'y rechaza si la carga falla. UnAmsetProviderrecibe ahora tambiénmessages. Sin cambios para quien no lo usa. - Los huecos que quedaban para migrar un front (AMS-1730).
Selectgananative(un<select>de verdad: el selector del sistema en el movil, cononChangeEvent) ygroupen la opcion, que agrupa bajo un titulo tanto en el<select>(optgroup) como en el panel propio.ButtonganaloadingText, que sustituye al contenido mientras envia, sin girador.IconButtonganafullWidth.NumberFieldganaonChangeEvent. Y--amset-field-required-display: noneesconde el asterisco de «obligatorio» (el lector lo sigue anunciando). Todo es aditivo. - Los idiomas de
catalogsse cargan bajo demanda (AMS-1721). Cada etiqueta puede ser el JSON o una función (fr: () => import('./fr.json')): solo se pide el idioma dellocaleactivo, una sola vez, sin repetir la carga aunque la función se escriba en línea, conStrictModeni al volver a un idioma ya visto. Mientras llega salen los textos de la biblioteca; si falla se avisa por consola, se llama aonCatalogErrory se reintenta al volver a pedirlo; si se cambia de idioma con otro en camino, el tardío no pisa al nuevo.preloadCatalog(cargador)precarga (selector de idioma, servidor). Nuevos tiposCatalogLoader,CatalogSourceyCatalogErrorHandler. Compatible: un objeto sigue saliendo en el primer pintado. - API para migrar un front que ya existe (AMS-735). Tres cosas que antes obligaban a reescribir cientos de manejadores:
onClickdeButton,IconButtonyButtonLinkentrega ahora elMouseEventnativo (conpreventDefault,stopPropagationycurrentTarget; antes recibia la pulsacion unificada);onChangeEvententrega el evento del<input>enTextField,PasswordField,TextArea,CheckboxySwitch(e.target.value,e.target.checked), sin quitaronChange; einputRefllega aNumberFieldyFileUpload, asi queinputRef.current?.click()abre el selector de ficheros desde un boton propio. Aviso: si pasabasonClickleyendopointerType, usaonPress, que sigue dando la pulsacion unificada. - Idiomas enteros desde el proveedor.
AmsetProvideryLocaleProvideraceptancatalogs({ fr, 'pt-BR': ptBR }, un JSON deComponente.clave-> texto por etiqueta BCP-47) yAmsetProvideracepta ademasmessages. Se usa el catalogo dellocaleactivo (exacto, idioma o regional) y lo que falte cae en ingles clave a clave;messagesmanda sobrecatalogs. La plantilla con todas las claves se publica en@amset/ui/idiomas/es.jsonyen.json. En desarrollo el proveedor avisa una vez de un idioma sin catalogo y de claves que no existen, yvalidateCatalog(json)(con los tiposMessageCatalogs,MessageOverrides,MessageKeyyCatalogReport) lo comprueba en una prueba. Sin cambios para quien no pasacatalogs. - La raya de color de cada evento de la agenda vuelve a medir 3 px (AMS-1665).
--amset-agenda-slotestaba declarada dos veces en la misma regla y la segunda (el hueco entre el mes y la lista, 20 px) pisaba el grosor de la raya. El grosor pasa a su propia variable,--amset-agenda-stripe;--amset-agenda-slotqueda solo para el hueco. - Los campos agrupados dicen que estan apagados (AMS-1268).
NumberField,QuantityPicker,CodeFieldySignatureFieldponenaria-disableden surole="group"cuando estan desactivados: antes solo se apagaban los botones de dentro y un lector de pantalla no decia que el campo entero no se podia usar. - Licencia MIT (AMS-1688). El paquete declara
"license": "MIT"y trae el ficheroLICENSE(antesUNLICENSED). Sin cambios de código. - Textos que la biblioteca escribia en castellano aunque el idioma fuera ingles (AMS-1714). Ahora salen del catalogo de mensajes: «Antes» y «Ahorras un» del importe, «sube» y «baja» de la variacion del dato, «de 5» del resumen de valoraciones, el contador de la galeria, «con subniveles» del selector en cascada, «sin leer» de la bandeja, «Eventos del...» de la agenda, «Trabajando» de confirmar accion y «(se abre en otra pestaña)» del estado del pedido. 12 claves nuevas; en castellano no cambia nada.
- El
Selectanuncia que es obligatorio y que no es valido (AMS-1155). El disparador pasa derole="button"arole="combobox"(patron «select-only combobox» de la APG) y llevaaria-requiredyaria-invalidcuando toca. Un lector de pantalla dice ahora «cuadro combinado» en vez de «boton». Afecta a cada campo construido sobre el: telefono, selector de idioma y moneda, tipo de IVA, mes y filas por pagina. Si una prueba tuya busca ese control congetByRole('button'), pasa agetByRole('combobox'). Visualmente no cambia nada. - El Storybook entero sigue el idioma de la barra (AMS-1720). Antes iba a medias: con English elegido la barra lateral, la explicación de cada historia, *Primeros pasos* y los rótulos de Repaso y Fundamentos seguían en castellano, y la interfaz de Storybook (pestañas, ajustes, tabla de props, atajos) salía en inglés aunque se eligiera castellano. Ahora: la barra lateral usa
.storybook/menus.en.json(con los nombres que Storybook saca del export sin acentos corregidos enmenus.es.json); las 672 explicaciones de historias van en.storybook/historias.en.json; *Primeros pasos* son dos ficheros markdown; los rótulos del banco (Canvas,Matrixy las tablas de Fundamentos) van ensrc/banco/banco.en.jsonyCanvaspasa a seguir el idioma elegido (sin barra sigue siendo el castellano, así que las capturas de referencia no cambian); y lo que escribe Storybook por su cuenta se pone en castellano con.storybook/traductor-de-interfaz.ts. Una prueba (scripts/historias.test.ts) obliga a que cada nombre, explicación y rótulo tenga su versión en inglés y a que no sobre ninguna. Se apaga la guía de bienvenida y el aviso de novedades de Storybook, que son su publicidad y no se pueden traducir. Las descripciones de la pestaña *Controles*, que Storybook fija al preparar la historia y no repinta, se traducen en el propio DOM al elegir inglés (solo si la traducción lleva el mismo código en el mismo orden). SiteFootery@amset/ui/seo(AMS-1750).SiteFooteres el pie de página semántico: un<footer>(contentinfodentro deAppShell) con marca, columnas de enlaces —cada una un<nav>con su título como nombre—, redes solo con icono, franja legal y controles; los enlaces externos se abren en otra pestaña conrel="noopener noreferrer"y avisándolo al lector de pantalla. Hojapie-sitio.css, textosSiteFooter.*. La subruta nueva@amset/ui/seo(servidor, sin dependencias ni CSS) traepageMetadata(compatible con la Metadata API de Next y con validación que lanzaSeoError),checkSeo, los constructores de JSON-LD (organization,webSite,webPage,breadcrumbList,article,product,faqPage,localBusiness),<JsonLd>con escape seguro dentro de<script>,sitemapEntries/sitemapXmlyrobotsRules/robotsTxt(blockAllparapre). Aditivo.Tabsadmite pestañas sin panel (AMS-1634). Una pestaña sincontentno pinta panel ni llevaaria-controls: es un filtro o un modo de la misma pantalla. Sigue siendo una pestaña (flechas, foco, elegida) y se puede mezclar con las que si tienen panel.Tab.contentpasa a ser opcional; es aditivo. ElDialogya cubria el resto de huecos que el ticket media en la 0.8.0 (icono y color de cabecera, acciones en cabecera, incrustado, arriba, ancho numerico, sin relleno y en columna).- Un ejemplo de uso real por cada historia de Storybook, publicado en
@amset/ui/ejemplos.json. Sacado del propio codigo: si la historia traerender, es su cuerpo tal cual; si no, se sintetiza la etiqueta JSX con los args reales demetay de la historia. Un ejemplo al que le falte una prop obligatoria (porque su valor dependia de algo externo al fichero, o delmappingde un control de Storybook comoiconControl) se descarta en vez de publicarse incompleto.scripts/ejemplos.test.tscompila cada ejemplo sintetizado contra los tipos reales del componente.
0.10.2, del 15 de octubre de 2026
ToggleChip,ToggleChipGroupyOptionCard(AMS-1534). Los dos componentes que faltaban para migrar el asistente sin perder diseño.ToggleChipes un chip que se activa y se apaga (<button aria-pressed>) con color propio por chip (coloro--amset-chip-color),count,icon, tres tamaños y marca de verificación al activarse;ToggleChipGrouplos agrupa con nombre y salto de línea.OptionCardes una opción deRadioGrouppintada como tarjeta, conmedia,descriptiony la marca de la elegida: dentro del grupo se reparten en una rejilla (--amset-option-card-min). Teclado y foco son los de un radio nativo. Hojas propiaschip-alternable.cssytarjeta-opcion.css, ficha en Storybook en español e inglés y pruebas de comportamiento.
0.10.1, del 15 de octubre de 2026
- El campo de hora ya hidrata igual en inglés (AMS-1687). Con un reloj de 12 horas, el servidor (ICU de Node) escribía un espacio fino antes del «AM»/«PM» y Chrome un espacio normal: React tiraba el HTML y daba el error 418 en las fichas
TimeFieldyDateTimePickerde amset.tech/en/. Los separadores del campo se normalizan al espacio normal, y una prueba lo fija.
0.10.0, del 15 de octubre de 2026
- La documentación también en inglés (AMS-1687).
@amset/ui/fichas.jsonlleva en cada fichaen: { titulo, descripcion }y nuevo export@amset/ui/props.en.json(descripción de cada prop en inglés, por el texto en español). El extractor rompe el build si una ficha no tiene su versión ensrc/mensajes/fichas.en.json, y una prueba vigila la cobertura y que el castellano no haya cambiado desde que se tradujo (cada traducción guarda la huella de su origen). En Storybook, las páginas de documentación siguen el selector de idioma: con English, título, texto y descripciones de las props salen en inglés (1.758 descripciones, todas traducidas). Las historias sueltas (sus nombres y comentarios) y las páginas de Empezar siguen en castellano. TagsInputyCombobox(AMS-1698). Campo de etiquetas que se crean al escribir (Intro, coma, pegado o al salir), sin repetidas, conmax,validate,transformyname; yCombobox, el buscador con texto libre por defecto. TextosTagsInput.*en es y en.DateTimePickeryMonthPicker(AMS-1698). La fecha y la hora en un solo campo (2026-10-15T09:30, conmin,max,isDateUnavailableyname) y el selector de mes y año (2026-10, con los meses en el idioma activo yminYear/maxYear). Solo avisan con el valor entero. Hoja propia,fecha-hora.css, con--amset-datetime-gapy--amset-monthpicker-gap.- Cinco campos de gestión y cinco ganchos (AMS-1677). Nuevos:
MoneyField(dinero que entiende coma decimal, separador de miles y símbolo según el idioma y entrega un número),SocialSecurityField(NUSS con los dos dígitos de control comprobados),LicensePlateField(matrícula actual, provincial y de ciclomotor),CnaeField(CNAE-2009, con catálogo opcional) yVatRateSelect(21, 10, 4 y 0 %). Salen sueltos los validadores (validateSocialSecurity,validatePlate,validateCnae...) y las funciones de dinero (parseMoney,formatMoney,formatMoneyForEdit). Ganchos:useMediaQuery,useDisclosure,useClipboard,useHotkeysyuseAnnouncer. Textos en español e inglés, ficha en Storybook y pruebas de comportamiento (teclado y errores al salir del campo) para cada uno. Ver el roadmap endocs/roadmap-amset-ui.md. - Los textos de la biblioteca, en español e inglés (AMS-1687). Cada texto que un componente decía por su cuenta (etiquetas accesibles, avisos, errores, anuncios, resúmenes, plurales...) sale ahora de un catálogo por idioma (unas 560 claves, cada una en
esy enen), según elLocaleProvider. Un idioma sin catálogo cae en inglés; sin proveedor sigue el español, así que nada cambia para quien ya usa la biblioteca. Nuevos: la propmessagesdel proveedor,translatorFor,useMessages, y un último parámetro opcionalmsgen las funciones que escriben un texto. Los textos con cantidades usanIntl.PluralRules. Storybook trae un selector de idioma (es, en, ar). Tres pruebas vigilan que no haya claves sin usar, que el inglés no lleve acentos y que ningún componente vuelva a escribir un texto en español a mano. - El CSS entra por componente, no entero (AMS-1677). Cada módulo de
dist/importa ahora SU hoja (ybase.css, con los tokens y el reset), así que el empaquetador se lleva solo el CSS de lo que usas: unButtonen Vite pasa de 392 kB a 23 kB de CSS (4 kB gzip), y una pantalla de 20 componentes de 395 a 133 kB, con el mismo resultado al píxel que la hoja única. Nuevos exports:@amset/ui/estilos/base.cssy@amset/ui/estilos/<hoja>.css. Cambia al actualizar: (1) si sigues importando@amset/ui/estilos.csspagas el CSS dos veces: quítalo; (2) con Next Pages Router hace faltatranspilePackages: ['@amset/ui'](sin él,ERR_UNKNOWN_FILE_EXTENSIONal construir); (3) en Jest, unmoduleNameMapperpara.css$. Probado en Vite 8, webpack 5, y Next 16 con App Router y Pages Router. Los imports van en la misma línea que"use client"para no mover los sourcemaps. - Once componentes se pueden usar desde un Server Component (AMS-1677). Antes TODOS los componentes llevaban
"use client", así que en Next un layout de solo servidor se llevaba igualmente el JS deText,Title,Badge,Separator,EmptyState,VisuallyHidden,CompactGroup,DataList,List,Stack/Grid/Center/ContaineryBlockquote. Ya no lo llevan: no usan hooks, manejadores de eventos ni APIs del navegador. Comprobado importándolos y llamándolos con la condiciónreact-serverde Node, y una prueba nueva (componentes-servidor.test.ts) impide que un fichero sin la directiva empiece a necesitar el navegador. Sin cambios de API. Los componentes que dependen de un proveedor, de texto por idioma o de otro componente de cliente (PageHeader,ButtonGroup,Result) siguen marcados, a propósito.
0.9.6, del 15 de octubre de 2026
- Un tema claro dentro de una página oscura ya se ve claro. Los valores claros solo existían en
:root: un<div data-tema="light">dentro de una página en oscuro seguía oscuro, sin ningún error (la muestra de tema de amset.tech salía oscura con «Claro» marcado). Ahora[data-tema='light']existe en cualquier elemento, igual que el oscuro, y donde se anidan gana el de más dentro. Para que el texto y el fondo de la zona cambien, la zona lleva además la claseamset-canvas(o su propio color y fondo). - Los enlaces ya no van subrayados.
Linksale sin subrayar, en el color de acento y en negrita (el peso es la pista que no depende del color: axe sigue limpio con el enlace dentro de un párrafo).underline="always"lo subraya siempre yunderline="hover"al posar el ratón. Tampoco se subrayan ya al posar el ratón los títulos deCard, las migas, la descarga deFileList, el enlace deStat, el de los filtros, el deProductCardni los deGlobalAlert.baresigue existiendo y ahora solo le quita el peso.
0.9.5, del 15 de octubre de 2026
Repaso de calidad de doce componentes que Aitor señaló en amset.tech (AMS-1701). Todo medido en el navegador antes y después, con prueba que falla sin el arreglo.
DateRangePickerya deja elegir el segundo extremo en el otro mes. Con dos meses a la vista, el primer mes seguía al día con el foco, y un día recibe el foco al pulsar el ratón, antes de soltarlo: las rejillas se corrían un mes bajo el puntero y el clic caía en otro sitio. Ahora la vista solo se mueve cuando el foco sale de ella. De paso: cada mes lleva su nombre encima, «Mes siguiente» mira el último mes visible yDatePicker/DateRangePickersiguen suvalue(un atajo deQuickRangeya escribe en el campo).Countdowncon variantes:variant="roll"(por defecto, las cifras que cambian ruedan),"ring"(un anillo que se vacía de forma continua) y"text"(el aspecto de antes). Los anuncios para lector no cambian.ActivityMapcuadrado: el título de la tabla se metía como celda de la primera columna; ahora la columna de días mide lo que mide, las casillas son cuadradas, los meses van encima y la leyenda cuadra con la rejilla.LocalePickercon banderas (withFlags,LocaleOption.flag,localeFlag), y el hueco de unDatePickervacío sale con las letras de cada idioma (yyyy, jj/mm/aaaa, tt.mm.jjjj).PhoneFieldcon los 244 países y territorios de la UIT, cada uno con su bandera (SVG propios, geometría real) y su nombre en el idioma activo; se busca escribiendo el nombre («Fra»), el espacio sigue buscando, Re Pág/Av Pág saltan diez y la rueda del ratón ya baja la lista. Nueva propinitialCountry. Las opciones se anuncian «España (+34)».- La rueda no bajaba en NINGUNA lista flotante larga: el panel escuchaba también el desplazamiento de su propia lista y, al volver a medirse, la devolvía arriba. Arreglado en
flotante.tsx, para todas. ListOption.textValue(texto de búsqueda aparte de la etiqueta),CountryPrefix.areaCodes.
- La rueda no bajaba en NINGUNA lista flotante larga: el panel escuchaba también el desplazamiento de su propia lista y, al volver a medirse, la devolvía arriba. Arreglado en
Amountalineado: altura de línea propia, «/mes» pegado al precio, la insignia de descuento centrada con las cifras; tres tamaños en fila comparten línea base.Table: ordenar y filtrar son dos cajas iguales de 22 px; el filtro activo se marca con el color del acento y el embudo relleno, sin fondo de bloque. La cabecera de fila pegada ya no coge el gris de la cabecera.Tabssin barra de desplazamiento: un píxel de margen negativo desbordaba la lista y Windows enseñaba una barra con flechas. Si las pestañas no caben, la rueda las mueve de lado sin barra.orientation="vertical"ya se pinta en columna.SideNavcon el diseño del menú de AMSET (el del assistant): baldosa de icono, nombre y descripción, marca de la página actual, grupos desplegables con cuenta, plegado a carril con tooltip. API nueva retrocompatible:description, grupos (NavGroup,NavItem),onCollapsedChange,header/footercomo función de{ collapsed }. Anchos 13/16/19 rem.AppShellya no apila el menú encima del contenido en una pantalla estrecha: queda un carril al lado.CollapsibleyAccordioncerrados sin hueco fantasma: el panel cerrado seguía teniendo caja y se comía elgap.
0.9.4, del 15 de octubre de 2026
- Dos hidrataciones mas que se rompian con una web de verdad (AMS-1666). Salieron al barrer amset.tech en produccion, donde el HTML lo pinta un runner de CI en UTC y el navegador esta en otra zona y otro dia:
QuickRangecon un periodo puesto marcaba el atajo que cuadra («este mes») segun el dia que fuese al pintar: el servidor decia uno y el navegador otro. Ahora no marca ninguno hasta que se sabe el dia.RelativeTimeconwithFullDateescribia la fecha completa en la zona del servidor («15:45») y el navegador en la suya («17:45»). Ahora la primera vez se acepta lo del servidor y en cuanto se hidrata el nodo se vuelve a montar con la hora de quien mira (useInBrowser):suppressHydrationWarningcalla el aviso pero deja el texto del servidor.hidratacion.test.tsxpinta ahora el servidor en UTC y el navegador en Europe/Madrid, y comprueba que el texto acaba bien.
- La API de
Tablepasa a inglés, con los nombres de antes funcionando (AMS-1677). Era el único componente que mezclaba idiomas (onSortjunto aalSeleccionar,paginacion,enServidor...). Ahora:serverSide,selectedKeys,onSelectionChange,pagination,configurableColumns,onVisibleColumnsChange,resizableColumns,onRowPress,toolbar; en las columnassortable,filterable,numeric,hiddenByDefault,sticky,rowHeader; y el orden es{ column, direction }. Los nombres antiguos siguen funcionando con un aviso en la consola (una vez por nombre, nunca en producción) y se retiran en la próxima versión mayor.onSortentregadirectiony tambiénsentidomientras tanto, para no romper a quien lo lea. Tabla de equivalencias en el README, que además tenía la sección de Tabla escrita con una API anterior que ya no existía. - La API de la biblioteca, legible por agentes de IA (AMS-1677). El build escribe ahora
dist/api.jsoncon las props reales de cada componente (tipo, obligatoria o no, comentario), sacadas del compilador y no de una lista a mano; los atributos HTML nativos no se enumeran, se anota que se aceptan. De ahí salendist/AGENTS.md,dist/llms.txtydist/llms-full.txt, y el paquete lleva una skill,skills/amset-ui/SKILL.md, para quien construye con la biblioteca desde Claude Code, OpenCode u otro agente. Nuevos exports:@amset/ui/api.json,/AGENTS.md,/llms.txty/llms-full.txt. Sin cambios en la API de los componentes. - El job
verificarconstruye antes de probar. Los 8 tests del CLI de temas se saltaban siempre en CI porquedist/aún no existía; ahora corren.
0.9.3, del 14 de octubre de 2026
Timelineya no rompe la hidratacion por los espacios de la fecha (AMS-1666). El tramo de una barra («24 oct – 4 nov») lo escribeIntl.DateTimeFormat.formatRange, y con ICU 78 -el de Node 22.23- pone espacios finos (U+2009) a cada lado de la raya, mientras que Chrome pone espacios normales. Un HTML pintado en Node y hidratado en el navegador no casaba y React lanzaba el error 418. Ahora se normalizan al espacio normal. Se vio al quitar de la web de amset.tech el parche que esquivaba el cronograma: la prueba de hidratacion de la biblioteca no lo cogia porque corre en un solo ICU.
0.9.2, del 14 de octubre de 2026
useTabssale por el índice (AMS-1670). Es la lógica de las pestañas sin su ropa: para una pantalla que ya pinta su panel por su cuenta y solo necesita la lista con su teclado (las flechas dan la vuelta, Inicio y Fin, solo la activa entra en el tabulador, las apagadas se saltan, activación automática o manual). Salen tambiénTabSlot,TabsSetupyTabsPlumbing. Antes solo lo usabanTabsy la galería. Para filtrar una misma lista no son pestañas: esSegmentedGroup.- Ya no se rompe la hidratacion en una web con SSR o exportada a estatico (AMS-1666). Un componente que lee «hoy» o «ahora» mientras se pinta sale distinto en el servidor y en el navegador, y React tira el HTML y repinta (error 418 en la consola, medido en produccion en amset.tech). Pasaba en
Calendar,DatePicker,DateRangePicker,QuickRange,MonthAgenda,ShippingPickeryTimeline. Ahora el primer pintado -el del servidor y el de la hidratacion- no sabe el dia, y el navegador lo conoce justo despues, sin un fotograma de mas en una app que solo pinta en cliente:CalendaryMonthAgendasin un dia que los ancle (nivalue, nidefaultFocusedValue, niinitialDay) pintan solo su sitio, conaria-busy, hasta que llega «hoy»; con un dia dado se pintan enteros desde el servidor.- La marca del dia de hoy (
data-today) y la linea de hoy deTimelinesalen al llegar al navegador. ShippingPickerescribe el plazo de entrega al llegar al navegador, salvo que se le pasenow.- Hay una prueba que lo vigila en TODAS las fichas (
hidratacion.test.tsx): pinta en servidor un dia, hidrata otro y cuenta los desajustes. Una ficha nueva entra sola.
0.9.1, del 14 de octubre de 2026
- El texto de las 149 fichas, al día con el código (AMS-1659). Las fichas del banco (y desde ahora las de amset.tech) seguían hablando de una biblioteca que ya no está, y usaban los nombres en castellano de la API de antes (
etiqueta,tamano,alNavegar,ProveedorIdioma…). Revisadas una a una contra el código: props y valores reales, teclado y accesibilidad como funcionan hoy, y fuera lo que ya no existe. @amset/ui/fichas.json: la ficha de cada componente, como dato (AMS-1659). Una entrada por componente con su nombre de importación, su módulo, su título en español, su familia, el texto de su ficha (qué es, cuándo usarlo y cuándo no, teclado, accesibilidad, aspecto fino; en markdown) y los ids de sus historias en el banco. Sale de las historias de Storybook al construir, así que la web de amset.tech deja de llevar una copia a mano que se quedaba vieja.- La
descriptiondelDialogadmite un nodo (una etiqueta de estado junto a la frase) y se sigue anunciando como descripción. Sin descripción ya no apunta conaria-describedbya un elemento que no existe. Dialogcubre ya lo que pedía un modal con cabecera de marca (AMS-1534, a petición de amset-assistant, que tenía 42 modales propios). Todo es opcional y lo que ya existía no cambia:titlepuede ser un nodo;iconpone un icono propio en lugar del del tono yaccentle da color (--amset-dialog-accent);headerActionslleva botones entre el título y el aspa.width(píxeles si es número, medida CSS si es texto) manda sobresize;placement="top"lo pega arriba, para una paleta que crece hacia abajo;hideHeaderno pinta la cabecera pero el título sigue siendo el nombre accesible.flushquita el relleno del cuerpo ybodyColumnlo hace columna flex, para contenido con su propio desplazamiento.inline: la misma caja EN la página, como una región con su título. Sin<dialog>, fondo, Escape ni trampa de foco, sin aspa y sin inventar un «Cerrar»: sirve para reutilizar como panel de pantalla lo que en otra es un diálogo.
0.9.0, del 14 de octubre de 2026
QRCode(AMS-1638). Un código QR en SVG sin dependencias:<QRCode value label level size margin onError />. Detrás hay un codificador propio (encodeQr, ISO 18004, modo bytes/UTF-8, versiones 1–40, niveles L/M/Q/H) comprobado módulo a módulo contra el paqueteqrcode(2.032 matrices idénticas con máscara forzada) y el SVG renderizado se decodifica con un lector independiente. Sale siempre negro sobre blanco, también en el tema oscuro: es lo que lee un escáner. No se hacenMentions,Watermark,Masonryni un visor de imágenes: sin demanda medida.TreeSelect: un desplegable con el árbol dentro (AMS-1636). El campo es el delSelecty el panel es elTree: elegir una categoría o un departamento de una jerarquía sin tener el árbol siempre a la vista. Una (selection="single", cierra al elegir) o varias ("multiple", se queda abierto y lo elegido sale en el orden del árbol); cualquier nodo se puede elegir, rama u hoja, salvo los apagados. Las ramas que llevan a lo elegido se abren solas y el foco entra en la fila elegida.displayRender,name(un campo oculto por clave). Teclado el delTreemás Abajo/Arriba para abrir y Escape para cerrar. Sin búsqueda dentro del panel (elTreeno la tiene) y sin versión conectada propia: va conFormField.Tree: carga diferida y marcado en cascada (AMS-1637).loadData(clave)pide los hijos de una ramaisLeaf: falsela primera vez que se abre; el chevron deja paso a un anillo (aria-busy) y se anuncia «Cargando…». Si falla, la rama se queda como estaba y el chevron reintenta.cascade(conselection="multiple"): marcar una rama marca todo lo de dentro y una rama con solo una parte marcada se ve «a medias» (y el lector dice «elegida en parte»). El valor sale en el orden del árbol; lo apagado no se toca. Con carga diferida, una rama ya marcada hereda la marca en lo que llega.- Medido contra el ticket: el
Carouselya tenía autoplay (automatic, con botón de pausa, pausa al pasar el ratón o enfocar y respeto a «menos movimiento») y control desde fuera (index+onChange); no se tocó. Arrastrar nodos para moverlos queda fuera (pide una alternativa de teclado diseñada aparte).
Cascader: selector en cascada (AMS-1635). País > provincia > ciudad en un solo campo, con una columna por nivel, en vez de variosSelectencadenados que no comparten estado ni teclado. El valor es la ruta (['es', 'madrid', 'alcala']), controlada o no.changeOnSelectdeja elegir una rama sin bajar a una hoja;loadData(ruta)pide los hijos de una opciónisLeaf: falsela primera vez que se abre (la columna dice «Cargando…»);displayRender,levelLabels(el nombre de cada nivel para el lector de pantalla),name(viaja la clave de la hoja). Teclado: flechas arriba/abajo por la columna, derecha entra, izquierda vuelve, Intro elige, Escape cierra. Las ramas se anuncian «con subniveles».FormCascaderlo conecta al formulario. Sin búsqueda ni selección múltiple todavía.- Subir ficheros de verdad (AMS-1555).
FileUploadelegía y validaba, pero no subía nada: el progreso, el fallo y el reintento los reescribía cada proyecto. Ahora:Uploader: la zona de soltar, una barra por cada fichero que sube, el motivo y «Reintentar» si falla, y quitar uno que sube cancela la petición. Sube por HTTP conhttp={{ action, method, headers, data, fieldName, withCredentials }}o por donde digas concustomRequest({ file, onProgress, signal })(una URL firmada, un SDK). Cuántos suben a la vez:concurrency(3). Avisa cononChange,onUploadedyonFailed, y anuncia «factura.pdf subido» o el fallo.useFileUploads: el motor sin pintar nada (cola, progreso, fallo, reintento, cancelar,pending, yautoStart: false+start()para subir con un botón de «Enviar»), para montar otra pantalla.httpUpload,UploadError,isAbort: la petición por defecto, conXMLHttpRequest(es lo único que da progreso de subida;fetchno).FileUploadganamaxCount(los que no caben se rechazan con su motivo),directory(elegir una carpeta; soltar una carpeta encima no la recorre), pegar un fichero o una captura con Ctrl+V yhideList.- Sigue siendo cierto: el filtro de tipo y tamaño es comodidad, el servidor tiene que volver a comprobarlo.
- Campos coherentes entre sí y
NumberFieldvaciable (AMS-1625, AMS-1626).NumberFieldse puede vaciar: borrar el texto y salir del campo avisa cononChange(NaN)—el «sin valor» que ya escribían los consumidores— y deja el campo en blanco. Antes volvía el número anterior y se enviaba. Lo que no es un número (abc) sigue descartándose.QuantityPickerno cambia: una cantidad no puede estar vacía.FormNumberFieldlo traduce aundefined.DocumentFieldeIbanFieldadmitenonBlur.PhoneFieldadmitename(envía el número entero en E.164, no el trozo visible),QuantityPickeradmitenameySlideradmitename(cada tirador lo lleva); ya viajan en un formulario nativo.- El error de
MultiSelectlleva el icono de aviso, como el de los demás campos. Sliderya aceptabaonBlur(es un atributo de su<div>); el ticket decía que no.
- Armazón, pegado al desplazar y controles pegados (AMS-1558).
AppShell: cabecera, columna lateral, contenido y pie en una rejilla de CSS. El contenido es un<main id="contenido" tabindex="-1">, el destino de «saltar al contenido» delTopBar; la cabecera se queda fija y la columna se queda fija justo debajo de ella (mide su alto) con su propio desplazamiento. Por debajo de 48 rem la columna pasa encima del contenido y deja de ser fija.siderWidth,mainId,stickyHeader.Affix: un bloque que se queda pegado al desplazar, por arriba (offsetTop) o por abajo (offsetBottom), cononChange(pegado). Esposition: sticky: el contenedor tiene que ser más alto que el bloque y ningún ancestro puede teneroverflow: hiddenniauto.TableOfContentsganaoffset: con una cabecera fija de 64 px, pulsar una entrada deja la sección debajo de ella y la raya de lectura se cuenta desde su borde de abajo. Sinoffsetes lo de siempre.CompactGroup: un campo con su botón (o varios campos) pegados sin huecos y con las esquinas de unión cuadradas. Los campos de dentro llevanhiddenLabel. Vale con cualquier campo (texto, número, selector...) y conButton,IconButtonyButtonLink.- No hecho, con su ticket:
Cascader(AMS-1635),TreeSelect(AMS-1636), carga diferida y arrastre delTreey autoplay delCarousel(AMS-1637), y las piezas menores (AMS-1638).
@amset/ui/formulario: todos los campos conectados y la API en inglés (AMS-1557). Antes conectaba 8 campos y tenía los nombres en español (Formulario,CampoTextoForm,BotonEnviar,resolverDeEsquema), lo que dejaba dos idiomas en una misma importación.- Nombres en inglés:
Form(propsschema,initialValues,onSubmit,validateOn),SubmitButton,schemaResolvery los tiposFormProps,SubmitResponse,StandardSchema,StandardIssue. Los nombres de siempre siguen funcionando con sus mismas props —son alias obsoletos y se retirarán en una versión mayor—: no hay que migrar nada hoy. - Campos nuevos:
FormPasswordField,FormPhoneField,FormDocumentField,FormIbanField,FormCodeField,FormNumberField,FormQuantityPicker,FormSlider(un valor),FormRating,FormMultiSelect,FormSegmentedGroup(una opción),FormDateRangePicker,FormTimeField,FormColorPicker,FormCardField,FormAddressField,FormSignatureFieldyFormFileUpload. Los de valor compuesto (tarjeta, dirección) reparten el error del esquema a cada subcampo:tarjeta.numberfalla en el número, no en el bloque. FormField: conecta CUALQUIER otro campo con una función que recibevalue,onChange,onBluryerror. Cubre lo que no tenga versión propia o un componente tuyo.- Los campos conectados antiguos se llaman ahora
FormTextField,FormTextArea,FormSelect,FormSearchField,FormDatePicker,FormCheckbox,FormSwitchyFormRadioGroup. - Aviso:
ROOT_FIELDsigue valiendo'raiz'y no'root'; es la clave que ya leen los proyectos enformState.errors.
- Nombres en inglés:
- Tono de la barra, forma de los botones, tamaño del grupo y aviso de foto rota (AMS-1556).
Progressganatone(success,error,warning,info,neutral; sin tono, acento): una subida que ha fallado se pinta en rojo y una terminada, en verde. El color no es lo único que lo dice: ponlo también en el texto convalueLabel.Button,ButtonLinkeIconButtongananshape:'default'o'pill'(esquinas del todo redondeadas) y, solo enIconButton,'circle'. Dentro de unButtonGroupsolo se redondean las esquinas de fuera del grupo. Se declara el vocabularioShapeySHAPESenvariantes.ButtonGroupganasize: se escribe una vez y lo heredan losButton,IconButtonyButtonLinkde dentro que no dicen el suyo (uno con su propiosizelo conserva).AvatarganaonError(src): avisa de que la foto no ha cargado. El avatar ya se quedaba en iniciales por su cuenta; esto es para reintentar con otra dirección o dejar constancia.- No hacía falta:
ButtonGroupya teníaorientation="vertical".
- Tabla: filas desplegables, clase por fila, alto máximo y más (AMS-1554).
renderExpandedRow(row)convierte cada fila en desplegable: gana un botón conaria-expanded(que apunta a su detalle conaria-controlscuando está abierto) y el detalle sale debajo, de ancho completo.rowExpandable(row)deja sin botón a las filas que no tienen detalle. Se llega con el teclado (flecha derecha desde la fila, Intro) y desplegar no cuenta como pulsar la fila (alPulsarFila). Controlable conexpandedKeys/onExpandedChange, oinitialExpandedKeys.rowClassName(row, index)suma una clase a una fila concreta (una factura vencida en rojo).maxHeight('24rem','60vh'): el cuerpo se desplaza y la cabecera y el pie se quedan fijos. Era la variable--amset-table-height; ahora es prop.TableColumn.pegadaadmite'end'(la columna de acciones se queda al final al desplazar en horizontal);truey'start'siguen pegando al principio.TableColumn.ellipsiscorta el texto largo con «…» en una línea y deja el texto entero en eltitle.- Arreglado: con la tabla desplazada, la cabecera de fila pegada (
th) se pintaba encima de la cabecera de columna «Número», y el pie fijo quedaba por debajo de las columnas pegadas. - No hacía falta: los totales al pie (
TableColumn.footer) y la cabecera pegada ya existían. - Sigue sin existir:
colSpanpor celda,bordered,tableLayout,showHeader,caption.
- Funciones de pintado (AMS-1550). Hasta ahora una opción, una celda o un día eran texto más un icono: en cuanto hacía falta un avatar, dos líneas o un precio, el componente se descartaba. Ahora se puede pintar la pieza. La regla es la de
Chip:labeles lo que se lee y la función pinta lo que se ve, así que el nombre accesible no cambia y la pieza sigue siendo elegible con ratón y teclado.renderOption(option, { selected, disabled })enSelect,MultiSelect,SearchField,SelectableListyVirtualList(esta última solo la llama para las filas pintadas).renderValue(option)enSelect: lo elegido dentro del campo cerrado.renderTag(option)enMultiSelect: cada ficha de lo elegido.renderItem(item, { selected, disabled })enTransferList.renderDay(date, number)enCalendar: lo que va dentro de cada día. El nombre del día no cambia.renderPage(page, { current })enPagination: el contenido del botón de cada página (solo conshape="full").renderCrumb(crumb, { current })enBreadcrumbs.TableColumn.renderrecibe ahora tambiénindex, la posición de la fila en la página que se ve.- Se exportan los tipos
OptionRenderyOptionRenderState. - Todo es aditivo. Falta el detalle desplegable de fila y el pie de resumen de la tabla: son de AMS-1554.
0.8.0, del 13 de octubre de 2026
- Quién cierra una capa y con qué se abre (AMS-1553).
DialogganaisDismissable(pulsar fuera cierra; enfalse, solo se va por sus botones, Escape oopen) eisKeyboardDismissDisabled, las mismas dos que ya teníaDrawer.kind="alert"sigue sin cerrarse al pulsar fuera.TooltipganaopenOn('hover' | 'focus' | 'press', por defecto['hover', 'focus']). Con'press'se alterna al pulsar y se cierra al pulsar fuera: es lo que la hace visible en una pantalla táctil. UnonClickpropio del elemento se respeta.DropdownganaopenOn('press' | 'contextmenu'). Con'contextmenu'se abre con el clic derecho y el menú del navegador no sale. No hay'hover'a propósito: el panel es modal.- No hay
zIndexni contenedor de portal, y no es un olvido: las capas van en la capa superior del navegador, que queda por encima de cualquierz-index(medido con 2147483647) y funciona dentro de un shadow DOM. Un panel que se abre desde dentro del modal de otra librería vive en ese modal, así que sigue siendo pulsable.
- Estado de carga (AMS-1552).
loading(yloadingText, para lo que dice) llega a los componentes que no lo tenian:Switch,Select,MultiSelect,SearchField,SelectableList,VirtualList,DialogyDrawer.Switch: mientras espera la confirmacion NO cambia de valor ni avisa, pero sigue en el orden del tabulador (apagarlo sacaria el foco), con un girador en el tirador.Select,MultiSelect,SearchField: el campo se marca ocupado (aria-busy), lleva un anillo y el panel enseña una filaCargando…. Las opciones que ya hubiera se quedan.SelectableList,VirtualList: ocupadas, con la fila debajo (scroll infinito).Dialog,Drawer: el contenido se cambia por un esqueleto y se anuncia; el titulo y los botones se quedan.
Spinnerganadelay(no sale hasta pasado ese tiempo, para que una carga de 100 ms no parpadee) y puede envolver un bloque conchildren: lo deja en gris, inerte y con el girador encima.inputRefenTextField,PasswordFieldyTextArea: el nodo del<input>o del<textarea>de verdad, no el de la caja. Para enfocar un campo al abrir una edicion en linea, seleccionar un token que se va a copiar o devolver el foco a un buscador. Acepta objeto y funcion, y convive conautoFocus(el atributo sigue saliendo al DOM). Era lo que impedia migrar los campos de amset-assistant que se enfocan a mano.- El
idde un campo llega a su control (AMS-1488).TextArea,TimeField,DatePickeryDateRangePickerlo aceptaban y lo tiraban;MultiSelectySearchFieldlo dejaban en el<div>de fuera. Ahora, en un campo con<input>elforde su etiqueta lo apunta, y en uno cuyo control es una fila de segmentos (fecha, hora, rango) cae en el propio grupo. PhoneField,QuantityPicker,TimeField,DatePickeryDateRangePickeradmiteniden el tipo: en ejecucion ya lo reenviaban, pero el compilador no dejaba escribirlo.
0.7.0, del 13 de octubre de 2026
Todos los componentes dejan pasar los atributos de su elemento (AMS-1549). Antes lo hacían 14 de 158; ahora lo hacen todos los que pintan DOM. Es un cambio aditivo: medido contra la 0.6.2, no se ha quitado ni cambiado el tipo de ninguna prop, así que actualizar no debería romper la compilación de nadie.
### Qué se puede hacer ahora
<Tag id="estado-pago" title="Pagada el 12/10" data-testid="etiqueta" style={{ marginInlineStart: 8 }}>
Pagada
</Tag>Cualquier atributo del elemento que el componente pinta en su raíz: id, title, lang, data-*, aria-*, style y los manejadores de ratón y teclado. Dos reglas que conviene saber:
classNamese suma a las clases de la biblioteca ystylese mezcla con el del componente. Ninguno de los dos lo sustituye.- En un campo (
TextField,Select,NumberField…) los atributos van al control —ahí los espera quien escribe undata-testido unautoComplete—, yclassNameystylevan a la caja, que es lo que se ve.
También se exporta DomProps<'div'>, para tipar un envoltorio propio sin enumerar atributos a mano.
### Lo que NO se deja pasar, y por qué
Hay props del elemento que un componente pone él mismo, y una de fuera se aceptaría y se descartaría en silencio. En esos casos el tipo no las admite, así que te enteras al compilar en vez de comprobar que no pasa nada:
| Componente | No admite | Usa |
|---|---|---|
Dialog, Drawer, IdleWarning, CommandPalette | onCancel, onClose, onClick, role, aria-labelledby, aria-describedby | onOpenChange, title, description |
Dropdown, ConfirmAction, ShareButton, NotificationTray, ActionMenu | id, role, tabIndex, onKeyDown, onClick, aria-label, aria-labelledby, aria-describedby | title, onOpenChange |
Tooltip | id, role | — |
OverlayCloseButton | onClick | onPress |
Checkbox, Switch | onKeyDown, onKeyUp, onPointerEnter, onPointerLeave, onPointerDown | envolver en tu propio elemento |
Carousel | onFocus, onBlur | — |
Tree, SelectableList, VirtualList, SortableList | onKeyDown, onFocus, onBlur, role, tabIndex | — |
DocumentField, IbanField | onBlur | — |
### Otros cambios
- En un campo de texto, un
aria-describedbyde fuera se suma a la ayuda y al error en vez de pisarlos (TextField,TextArea,PasswordField). Cardsepara en el tipo lo que gobierna cada forma (estática, pulsable, enlace); no cambia lo que aceptaba.FieldRootPassThroughyFieldInputPassThroughaceptan ahorastyle.- Tres pruebas vigilan el resultado en la propia biblioteca: que el atributo llegue, que caiga en la raíz y no en una pieza interna, y que un manejador de fuera se llame.
Antes de 0.7.0
No había registro de cambios. Las versiones anteriores están en el historial de GitLab.
