# @codeatlasdev/ui 4.2.0: @codeatlasdev/ui 4.2.0 Page: https://ui.codeatlas.com.br/changelog/4.2.0/. Index: https://ui.codeatlas.com.br/llms/changelog.txt. 22 changesets. 22 changes. This release has no written post; the list below is generated from the changelog. ## In short 1. AI feedback loop.. AI feedback loop. 2. Avatar renders the photo as a real (no detached Image() p.... Avatar renders the photo as a real `` (no detached `Image()` probe, so no double download) and passes `loading`, `referrerpolicy` and `crossorigin` through, so `loading="lazy"` keeps a 200-row list from firing 200 requests and `referrerpolicy="no-referrer"` works with CDNs that reject a cross-origin `Referer`. 3. Card has a padding prop ('none' | 'sm' | 'md' | 'lg', any CSS.... `Card` has a `padding` prop (`'none' | 'sm' | 'md' | 'lg'`, any CSS length, or a number of pixels) so a one-off density no longer needs an inner wrapper; `flush` stays as the alias of `'none'`. ## Upgrade ```bash bun add @codeatlasdev/ui@4.2.0 ``` Across majors: `bunx @codeatlasdev/ui migrate --from --to `, or the MCP tool `get_migration`. ## Changesets ### Minor Changes - b65d56d: AI feedback loop. The `ca-ui-mcp` server has a new `report_issue` tool: an agent that hits a library problem (bug, missing prop or component, migration gap, wrong docs) gets a sanitized issue draft (paths, foreign URLs, emails, IPs, tokens, env assignments and foreign package scopes are redacted, and the redactions are listed) with a prefilled link to the new "AI report" issue form and a `gh issue create` command. It never posts. `bunx @codeatlasdev/ui workarounds` lists the `// ca-ui-workaround(#): ` markers an agent left in your code and which issues are already closed (read-only, offline-safe, exit 0). - 81910d1: Avatar renders the photo as a real `` (no detached `Image()` probe, so no double download) and passes `loading`, `referrerpolicy` and `crossorigin` through, so `loading="lazy"` keeps a 200-row list from firing 200 requests and `referrerpolicy="no-referrer"` works with CDNs that reject a cross-origin `Referer`. `fallback` now also takes a snippet (an icon for groups, channels and bots), and `onerror` fires when the image fails so you can refetch a signed URL. The fallback is always rendered beneath the image, so the box never changes size. The `data-status` attribute on `.ca-avatar` is gone. - 80c0336: `Card` has a `padding` prop (`'none' | 'sm' | 'md' | 'lg'`, any CSS length, or a number of pixels) so a one-off density no longer needs an inner wrapper; `flush` stays as the alias of `'none'`. The docs now say that Card's content is already padded, and in development a `[ca-ui]` warning (`card-double-padding`) fires once when the first child adds its own padding on top of the card's (the 20px + 20px = 40px "oversized card"). - cd655bb: Chat primitives: `MessageBubble` (direction, grouped tails, sender and avatar slot, quote, reactions, delivery ticks), `ConversationItem` (inbox row with unread, pinned, muted, typing, active), `AudioPlayer` (lazy src, waveform from `peaks`, slider semantics, playback rate), `MediaGrid` (image, video and document tiles opening a `Lightbox` gallery) and `VirtualList` (windowing plus load-older at the top that keeps the scroll position). New `chat`, `audioPlayer`, `mediaGrid` and `virtualList` messages in en and pt-BR. - 80c0336: `CopyButton` takes `aria-label` for the icon-only form (`label=""`), so each row of a list or table can announce its own name ("Copy client_id of Acme") instead of the same localized "Copy". The default stays the i18n `copyButton.label`. The label is ignored when `label` is visible, so the accessible name keeps containing the visible text. Remaining attributes (`data-*`, `id`, `aria-describedby`, …) are now forwarded to the underlying button. - 6e08799: Every numbered demo now has a reviewed `.meta.json` sidecar (`summary`, `use`, `spec`, `tags`, `shape`), so `get_demo`, `search_demos` and `/api/v1/en/{e,p}/NNN.txt` return what the demo shows, when to use it and a Spec list of its literal values (props, snippets, state, counts). The catalog check (`manifest --check`, run by every build) now fails on a demo without a sidecar, a sidecar without a demo, an unknown key, a bad `shape`, or a spec line that quotes a token that is not in the demo source. New `codeatlas-ui pick [ids...]` opens the gallery, listens on `127.0.0.1` with a random per-run token and prints the ids you send with "Send to AI" (`#005 #012`). - d99cead: `codeatlas-ui init` scaffolds SEO and PWA on SvelteKit (#205): prerendered `manifest.webmanifest`, `robots.txt` and `sitemap.xml` routes built with the `seo` helpers, a service worker (`src/service-worker/index.ts` on `$app/service-worker`, `$app/manifest` and `$app/env` for Kit 3; `src/service-worker.ts` on `$service-worker` for Kit 2) with a prerendered `/offline` page, and a default `` with canonical and manifest link in the root layout. Existing files are never overwritten; after creating them `init` reminds the app to set `paths.origin` (otherwise Kit's prerender placeholder origin reaches the sitemap and canonical URLs), add the icons and exclude `src/service-worker` from `tsconfig.json` on Kit 3. - d99cead: New `InstallPrompt` (#208): a dismissible banner that offers to install the app. On Chromium it keeps `beforeinstallprompt` (no browser mini-infobar) and shows an Install button that opens the native dialog and reports the choice with `onchoice`; on iOS Safari and iPadOS, which have no install API, it shows the Share, Add to Home Screen steps. It renders nothing when the app already runs standalone, and stays hidden for `dismissDays` (default 14) after the user closes it. Strings are in the `installPrompt` i18n group (`en`, `pt-BR`); the `root: Button only` size budget rises by 240 B gzip because both catalogs ship with every root import. - 81910d1: Lightbox opens videos and galleries. `items` (`{ type?: 'image' | 'video', src, alt?, caption?, meta?, poster?, download? }`, exported as `LightboxItem`) plus a bindable `index` add previous/next buttons, ArrowLeft/ArrowRight and swipe (no wrap; the end buttons are `aria-disabled` so focus stays put), a counter, caption and sender/time line, video with controls (paused when you move on), and a Download button (`ondownload` or a plain ``). `src`/`alt` still work as a single-item shortcut. New `lightbox.previous`, `next`, `download` and `counter` labels in en and pt-BR; the close button moved into a `.ca-lightbox__tools` bar. - 3180023: Leading icon slot for `DropdownMenuItem` and `TabsTrigger`, and a documented pattern for brand logos (#194). `icon` is a snippet rendered in a fixed 14px box with the standard gap and `aria-hidden` (the label stays the accessible name), so icon size and alignment no longer drift between menus. `brandIcon` marks the icon as a fixed-color brand mark: no highlighted, disabled, active or destructive color reaches it (trademark guidelines forbid recoloring). New `SchemeAsset` takes `light` and `dark` snippets and shows the one for the kit's color scheme (`.dark` on `` or a `data-theme-scope` island) with CSS only, so it is right in SSR and on first paint and apps no longer read the theme store for logo variants. New classes: `.ca-dropdown-item__icon`, `.ca-tabs-trigger__icon`, `.ca-scheme-asset`, `.ca-scheme-asset__light`, `.ca-scheme-asset__dark`. - 7a112eb: Navigation: `LoadingBoundary` wraps `` with a 200 ms delay and 500 ms minimum so a loading UI never flashes (default placeholder is `Skeleton` rows, `aria-busy`, named region). `@codeatlasdev/ui/kit` gains `NavigationProgress` (top bar driven by `navigating` from `$app/state`) and the `pendingLink()` attachment that sets `data-pending` on the link being navigated to. A test now proves AppShell stays mounted across 10 client-side navigations. - df50d86: New `RecordList`, `RecordRow` and `RecordCell`: records as dense rows (identity, a few columns, a row menu) that re-flow to a compact two-line row on a phone. The list owns the flush container, the separators, one `columns` template shared by the header and every row, the re-flow (a container query on the list's own width, 36rem), the 44px actions cell and an `aria-hidden` header whose labels become per-cell `dt` when stacked. It replaces the hand-rolled flush `Card` + grid + `:global` row CSS, and is much shorter on a phone than `Table responsive="cards"`. - d99cead: `` completes the social and language tags (#207): `og.defaultImage` is the site-wide card used when a page has no `og.image` (also feeds `twitter:image` and the `summary_large_image` card), `twitter:title` and `twitter:description` mirror the page title and description, and the new `lang` prop sets `og:locale` and keeps `` in sync on the client. - 81910d1: `skeleton` prop on `Avatar`, `KpiCard`, `TextField`, `Table`, `Card` and `GlassCard`: the component keeps its real DOM, so the placeholder has exactly the loaded size (no layout shift). Content is laid out but hidden, the host paints the same shimmer as ``, and it is announced with `aria-busy="true"`; a skeleton `TextField` is disabled and an `Avatar` makes no image request. Reduced motion stops the sweep and forced colors draw an outline. New global `[data-skeleton]` rule and `ca-shimmer` keyframes in `utilities.css`. - f889334: Every backdrop blur now comes from one place. Components no longer declare `backdrop-filter`: they set `data-ca-blur=""` (or are a `.ca-surface`) and `styles/surface.css` paints it from the tokens `` rewrites, so one attribute turns every blur off or down together. The look is unchanged (guarded by a computed-style test over 9 styles, light and dark). `FloatingLayer` gains a `blur` prop (`overlay` or `tooltip`). `lint:tokens` rejects `backdrop-filter` outside `surface.css` and `materials.css`. The MCP server gains `list_surfaces`, generated from the `[data-style]` selectors in `materials.css`, and `dist/surfaces.json` ships with the package. - 3180023: `TabsList` handles its own overflow (#190). It keeps the width of its tabs inside `Tabs` (no more full-column pill on desktop), and when the tabs do not fit it scrolls sideways on its own with the scrollbar hidden and scroll snapping, instead of wrapping or widening the page on a phone. The selected tab is scrolled into view on mount and when it changes (only the strip scrolls, never the page). New optional `fade` prop masks the edge that still hides tabs, like the `AppSettings` nav. The wrapper `div` with `overflow-x: auto` and the `:global(.ca-tabs-list) { width: max-content }` workaround can be deleted. ### Patch Changes - 526fee2: A menu, Select list, popover or tooltip that is already open when a Dialog, Sheet or Drawer opens (a shortcut or an async action opening the modal over it) now moves into that modal and stays clickable and above it. Before, it stayed outside the modal, so it went inert and could not be used. When a modal closes before the popovers hosted in it, they are handed to the next modal or put back where they were declared instead of being removed with it. The layer stack and the stacking contract are covered by new tests and documented on the Dialog page (#225). - 3afd417: Overlays return focus to the control that opened them on iPhone/iPad and Safari too. WebKit does not focus a tapped button (it focuses a focusable ancestor such as `
`, or nothing), so focus used to land there on close and VoiceOver users lost their place. CommandPalette: Mod+K focuses the search field on touch devices with a keyboard (covered by a test now). - c76c837: New guide `getting-started/migrate-react`, readable offline through the MCP `get_guide("migrate-react")` and on the docs site: decision flow (incremental or rewrite), React hook to Svelte 5 rune map with the effect pitfalls, MUI / shadcn-Radix / Chakra to `@codeatlasdev/ui` component maps, layout templates, theme mapping, TanStack Table to `@codeatlasdev/ui/table`, and how to report a gap (`report_issue` kind `migration-gap` plus a `ca-ui-workaround` marker). A unit test fails when a component named in the maps stops being an export. - d99cead: The manifest `generateBrand` / `caBrandPlugin` write is installable: `display: "standalone"` (was `"browser"`), `id` and `scope` set to `"/"`, and `theme_color` / `background_color` taken from the theme's default scheme instead of always the light one (#206). The icon hash now includes the default scheme, so switching it renames the files. - 81910d1: Skeleton's shimmer is a translated `::after` band instead of an animated `background-position`, so it no longer repaints on every frame. Reduced motion still gets a static, dimmed placeholder. - 9c9df3b: `@codeatlasdev/ui/vite` is safe in the SSR graph: importing `designStyle`, `mergeTheme` or `ThemeOptions` from a `theme.ts` that `hooks.server.ts` shares no longer fails the SvelteKit server build with `UNLOADABLE_DEPENDENCY` on `@resvg/resvg-js` (#184). The native peers are loaded by name at run time and `caBrandPlugin` marks them `ssr.external`, so the `ssr.external` workaround is no longer needed. `runBrand` and `brandUrls` are now exported from `@codeatlasdev/ui/vite`, as the 4.0.0 changelog announced (#185).