Finish the mobile performance pass

- Cache the model catalog once per user and each conversation's setting
  separately; clear both on logout and after model saves
- Compress hashed assets asynchronously with a byte budget and set Vary
- Memoize message parsing and throttle selection reads
- Debounce draft writes to localStorage
- Keep the composer editor on the page and focused, as before
- Add a bundle budget check and run it in CI
- Show a loading block while sessions load and a workspace avatar in the
  mobile conversation header
This commit is contained in:
2026-10-20 14:37:11 +01:00
parent c1a94fc9a5
commit 4f7e0a6bf0
38 changed files with 1320 additions and 334 deletions
@@ -6,6 +6,12 @@ import linkComputerRefs from "@/src/utils/link-computer-refs";
import { UserMarkdownComponents } from "../(data)/user-markdown-components";
import { QuotableAttribute } from "../(hooks)/use-message-selection";
/**
* Kept module-level: a new array on every render makes `react-markdown`
* parse the message again.
*/
const RemarkPlugins = [remarkGfm, remarkBreaks];
/**
* Ink bubble with paper text; inverts in dark mode. The text is markdown
* (GFM; raw HTML is never rendered) as the composer writes it, a single
@@ -18,7 +24,7 @@ export default function UserBubble({ text }: { text: string }) {
{...{ [QuotableAttribute]: "" }}
className="max-w-[80%] min-w-0 gap-2.5! items-stretch rounded-default-lg bg-ink text-on-ink px-4 py-3 text-sm leading-5 break-words"
>
<Markdown remarkPlugins={[remarkGfm, remarkBreaks]} components={UserMarkdownComponents}>
<Markdown remarkPlugins={RemarkPlugins} components={UserMarkdownComponents}>
{linkComputerRefs({ text })}
</Markdown>
</Stack>