ATProto-based AI Assistant Chat interface.

Version history of apps/web/src/features/models/components/ModelSelect.tsx

October 2026

pouriade.com
committed
pouriade.com
committed
pouriade.com
committed
pouriade.com
Content on its way shows a skeleton in its own shape, pulsing gently on a new --color-skeleton, in place of every "Loading…" and every empty space that filled in later. Loading reads its shapes out once and fades them in after the quiet wait, while a route's pending page shows at once, since the router now holds it back 400ms and keeps it at least 300ms. The chat frame has the sidebar's logo and toggle over New chat, the chats, and the account card; the chat list keeps its rows until the local copy answers or, with no chats yet, until the index first syncs; a search shows rows until it first answers rather than saying nothing matched; the account card holds its avatar and name; a conversation holds its title and first turn, so a titled chat never flashes New chat; the model selects hold a line for the name, so a chosen model no longer flashes as unavailable; and settings and admin pages, the users list, the Sync page's switch, the Share panel, and the shared page each have theirs. The store hooks report loading as React Query's isLoading, or the store still opening in this tab, so no component checks the handover. The shared page's loader fetches the chat for a signed-in viewer and the page reads it with useSuspenseQuery, under the route's skeleton and a centered error page with Retry. A busy button keeps its label with a turning spinner before it, a little stronger than a disabled one, for Save, every submit, Load models, Rebuild, Sign in's Continue, and the rest.
committed
hadsie.com
committed