Skip to content

feat(packages): shared ModelSwitcher + credits modal, fetchLLM - #893

Merged
AbhinRustagi merged 35 commits into
mainfrom
abhin/pkg-updates
Jul 29, 2026
Merged

feat(packages): shared ModelSwitcher + credits modal, fetchLLM#893
AbhinRustagi merged 35 commits into
mainfrom
abhin/pkg-updates

Conversation

@AbhinRustagi

@AbhinRustagi AbhinRustagi commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Package updates that the openui-cloud / openui-self-hosted template slimming depends on. Merge + publish before the template PR (#894) so the template deps resolve on the registry.

  • react-ui (v0.14.0): ModelSwitcher block (/blocks + main entry) with theme-aware { light, dark } logo API and Tag-based badges; useSystemThemeMode hook; thread-header defaults (title hides when :empty, actions flex) replacing per-app CSS overrides.
  • react-headless (v0.10.0): fetchLLM gains buildBody; useFetchLLM hook (stable instance, live options); getResponseErrorMessage — the chat store now surfaces server error messages instead of generic status lines.
  • devtools (v0.0.2): OpenUICreditsModal (429 → credits notice via observability); dev-only gating.

Minor (not patch) bumps for react-ui/react-headless: both ship new API + behavior changes, so existing ^0.13.x / ^0.9.x consumers opt in rather than silently receiving them.

- react-ui: ModelSwitcher promoted to /blocks + main entry; theme-aware {light,dark} logo API (ModelLogo); v0.13.3
- react-headless: fetchLLM gains buildBody for custom request bodies (default AG-UI shape unchanged)
- devtools: OpenUICreditsModal (429 -> credits notice via observability) + CreditsModal; dev-only widget

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
AbhinRustagi and others added 15 commits July 28, 2026 19:15
Wraps fetchLLM in a render-tolerant hook — consumers get a stable ChatLLM from one call, no useState(() => fetchLLM(...)) or ref boilerplate. Options are read fresh per send, so a buildBody closing over current state stays live without extra refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
Drops the custom pill markup + SCSS; the trigger keeps its compact dot (Tag has no icon-only mode).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
… blocks scss

- thread.scss: title self-hides when :empty; actions flex to fill (space-between) — replaces per-app overrides
- modelSwitcher.scss now @forward'ed from components/index.scss like every other component; drop the TS scss import, scss.d.ts, and the cp-css .mjs/.cjs rewrite hack

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
…esponses

Adds getResponseErrorMessage (parses error.message / error / message from the body, falls back to status text); the chat store now throws that instead of a generic 'Request failed: <status>'. Restores the behavior the template's deleted chat-error.ts provided, at the framework level — e.g. the cloud route's 429 billing message reaches the thread error UI again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
System prefers-color-scheme via useSyncExternalStore; replaces per-app theme providers for system-driven apps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
Minor (not patch) bumps: both ship new public API and behavior changes (thread-header layout defaults; store error messages). A patch would flow silently into existing ^0.13.x / ^0.9.x consumers — minor makes it opt-in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
- adapters-and-formats: correct the default body (AG-UI shape, was documented as {threadId, messages}), document buildBody, add useFetchLLM example
- self-hosting/openui-cloud/skills: drop OPENUI_MODEL, hand-rolled ChatLLM example -> useFetchLLM, ModelSwitcher note

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
Raw SSE forwarded untouched instead of NDJSON re-encoding via toReadableStream; signal propagated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR
Comment thread skills/openui/SKILL.md Outdated
Comment thread skills/openui/references/cloud-integration.md Outdated
Comment thread skills/openui/references/cloud-integration.md Outdated
Comment thread packages/react-ui/cp-css.js Outdated
Comment thread packages/react-ui/tsdown.config.ts Outdated
Comment thread packages/react-ui/src/components/AgentInterface/thread.scss Outdated
Comment thread packages/react-headless/src/adapters/useFetchLLM.ts Outdated
Comment thread packages/react-headless/src/adapters/fetchLLM.ts Outdated
Comment thread packages/devtools/package.json Outdated
Comment thread docs/content/docs/agent/reference/self-hosting.mdx Outdated
@AbhinRustagi AbhinRustagi changed the title feat(packages): shared ModelSwitcher + credits modal, fetchLLM buildBody feat(packages): shared ModelSwitcher + credits modal, fetchLLM Jul 29, 2026
Comment thread packages/devtools/src/OpenUICreditsModal.tsx Outdated
Comment thread docs/content/docs/agent/reference/adapters-and-formats.mdx Outdated
Comment thread packages/react-headless/src/store/createChatStore.ts Outdated
abhithesys
abhithesys previously approved these changes Jul 29, 2026
@AbhinRustagi
AbhinRustagi enabled auto-merge (squash) July 29, 2026 12:24
@AbhinRustagi
AbhinRustagi merged commit 19617a2 into main Jul 29, 2026
1 of 2 checks passed
@AbhinRustagi
AbhinRustagi deleted the abhin/pkg-updates branch July 29, 2026 12:47
AbhinRustagi added a commit that referenced this pull request Jul 30, 2026
* refactor(openui-cloud): slim template onto shared @openuidev packages

- ModelSwitcher: use @openuidev/react-ui (drop local model-switcher/); pass provider logos as {light,dark}
- Billing: replace local BillingCreditsDialog with dev-only OpenUICreditsModal from @openuidev/devtools
- LLM: swap createCloudChatLLM for fetchLLM + buildBody (429 flows via observability); hold LLM in a ref
- Cleanup: remove unused deps, cloud-chat-llm, chat-error, cloud-chat-constants, orphaned CSS/logos

Depends on #893 (packages) being merged + published first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* Update package.json

* refactor(openui-cloud): use the useFetchLLM hook for the chat LLM

Drops the local llmRef + selectedModelRef plumbing for useFetchLLM, which holds a stable instance and reads options fresh per send — buildBody closes over selectedModel directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): use fetchLLM; drop react-headless/react-lang/tailwind deps

- page.tsx: replace hand-rolled ChatLLM with fetchLLM (imported from @openuidev/react-ui), buildBody sends { messages }
- drop Tailwind (trivial usage): minimal globals.css reset, inline layout style, delete postcss.config.mjs
- package.json: remove @openuidev/react-headless, @openuidev/react-lang, tailwindcss, @tailwindcss/postcss; react-ui -> 0.13.3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): drop thread-header CSS overrides now in react-ui

Title :empty-hide and actions flex are library defaults after the thread.scss change; the openui-cloud-thread-header hook class is unreferenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): adopt ref-commit simplifications

- route.ts: badRequest helper, NextResponse, model allowlist (reject unknown), propagate upstream errors with status — billing.ts deleted (store now surfaces server messages)
- theme: useSystemThemeMode from react-ui; delete use-system-theme.tsx; unwrap ThemeProvider in layout
- globals.css: trim to page shell + artifact compat (~18 lines); drop Tailwind + postcss.config.mjs + tailwind deps + vitest
- frontend-token: NextResponse, propagate upstream status

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(templates): align to react-ui 0.14 / react-headless 0.10; add devtools to self-hosted

- cloud: ^0.14.0 / ^0.10.0
- self-hosted: react-ui ^0.14.0; OpenUIDevtools rendered dev-only in layout (devDependency)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* docs(templates): refresh READMEs for the slimmed starters

- cloud: drop OPENUI_MODEL, point at src/lib/models.tsx + /api/chat validation, list useFetchLLM/ModelSwitcher/devtools, pnpm-only
- self-hosted: add setup section (OPENAI_API_KEY), fix route path, pnpm-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): forward raw SSE via .asResponse() + openAIAdapter

Drops the NDJSON re-encode (toReadableStream); propagates browser aborts via req.signal; model overridable with OPENAI_MODEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(cli): bump to 0.3.0 for the slimmed templates

Scaffolded output changes substantially (react-ui 0.14 / react-headless 0.10, fetchLLM/useFetchLLM wiring, devtools, removed billing/tailwind) — minor bump, matching the package versioning rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): consolidate react-ui imports, extract starters, trim comments

- import everything from @openuidev/react-ui (headless re-export); starters/prompt templates moved to src/lib/starters
- drop the now-trivial handleModelChange wrapper; use the logoPath const
- shorten route/model/component comments to the essentials (tool-loop's safety-rule docs kept)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix(openui-self-hosted): base sans font on body (Tailwind preflight used to provide it)

react-ui sets fonts per component, not on body — text that merely inherits fell back to the browser serif once Tailwind's preflight was removed. Same latent gap exists in the reference commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* feat(openui-self-hosted): load Inter via next/font like the cloud template

Replaces the globals.css font-family fallback — next/font self-hosts Inter with built-in fallbacks, matching react-ui's --openui-font-body token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix: update docs and match latest changes

* fix: update readme

* fix: add favicon

* fix: version match

* fix: format

* fix: styles

* fix: remove credits modal

* fix: update lockfiles

* update lock files

* Update self-hosting.mdx

* Update SKILL.md

* fix: update minimumReleaseAge rule

* fix: lockfiles

* fix: naming refactor

* rm pnpm-lock

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
AbhinRustagi added a commit that referenced this pull request Aug 4, 2026
* refactor(openui-cloud): slim template onto shared @openuidev packages

- ModelSwitcher: use @openuidev/react-ui (drop local model-switcher/); pass provider logos as {light,dark}
- Billing: replace local BillingCreditsDialog with dev-only OpenUICreditsModal from @openuidev/devtools
- LLM: swap createCloudChatLLM for fetchLLM + buildBody (429 flows via observability); hold LLM in a ref
- Cleanup: remove unused deps, cloud-chat-llm, chat-error, cloud-chat-constants, orphaned CSS/logos

Depends on #893 (packages) being merged + published first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* Update package.json

* refactor(openui-cloud): use the useFetchLLM hook for the chat LLM

Drops the local llmRef + selectedModelRef plumbing for useFetchLLM, which holds a stable instance and reads options fresh per send — buildBody closes over selectedModel directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): use fetchLLM; drop react-headless/react-lang/tailwind deps

- page.tsx: replace hand-rolled ChatLLM with fetchLLM (imported from @openuidev/react-ui), buildBody sends { messages }
- drop Tailwind (trivial usage): minimal globals.css reset, inline layout style, delete postcss.config.mjs
- package.json: remove @openuidev/react-headless, @openuidev/react-lang, tailwindcss, @tailwindcss/postcss; react-ui -> 0.13.3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): drop thread-header CSS overrides now in react-ui

Title :empty-hide and actions flex are library defaults after the thread.scss change; the openui-cloud-thread-header hook class is unreferenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): adopt ref-commit simplifications

- route.ts: badRequest helper, NextResponse, model allowlist (reject unknown), propagate upstream errors with status — billing.ts deleted (store now surfaces server messages)
- theme: useSystemThemeMode from react-ui; delete use-system-theme.tsx; unwrap ThemeProvider in layout
- globals.css: trim to page shell + artifact compat (~18 lines); drop Tailwind + postcss.config.mjs + tailwind deps + vitest
- frontend-token: NextResponse, propagate upstream status

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(templates): align to react-ui 0.14 / react-headless 0.10; add devtools to self-hosted

- cloud: ^0.14.0 / ^0.10.0
- self-hosted: react-ui ^0.14.0; OpenUIDevtools rendered dev-only in layout (devDependency)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* docs(templates): refresh READMEs for the slimmed starters

- cloud: drop OPENUI_MODEL, point at src/lib/models.tsx + /api/chat validation, list useFetchLLM/ModelSwitcher/devtools, pnpm-only
- self-hosted: add setup section (OPENAI_API_KEY), fix route path, pnpm-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): forward raw SSE via .asResponse() + openAIAdapter

Drops the NDJSON re-encode (toReadableStream); propagates browser aborts via req.signal; model overridable with OPENAI_MODEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(cli): bump to 0.3.0 for the slimmed templates

Scaffolded output changes substantially (react-ui 0.14 / react-headless 0.10, fetchLLM/useFetchLLM wiring, devtools, removed billing/tailwind) — minor bump, matching the package versioning rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): consolidate react-ui imports, extract starters, trim comments

- import everything from @openuidev/react-ui (headless re-export); starters/prompt templates moved to src/lib/starters
- drop the now-trivial handleModelChange wrapper; use the logoPath const
- shorten route/model/component comments to the essentials (tool-loop's safety-rule docs kept)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix(openui-self-hosted): base sans font on body (Tailwind preflight used to provide it)

react-ui sets fonts per component, not on body — text that merely inherits fell back to the browser serif once Tailwind's preflight was removed. Same latent gap exists in the reference commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* feat(openui-self-hosted): load Inter via next/font like the cloud template

Replaces the globals.css font-family fallback — next/font self-hosts Inter with built-in fallbacks, matching react-ui's --openui-font-body token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix: update docs and match latest changes

* fix: update readme

* fix: add favicon

* fix: version match

* fix: format

* fix: styles

* fix: remove credits modal

* fix: update lockfiles

* update lock files

* Update self-hosting.mdx

* Update SKILL.md

* fix: update minimumReleaseAge rule

* fix: lockfiles

* fix: add interleaving for persisted data

* fix: add streaming fix

* fix: messageGroup

* fix: format

* fix: scroll issues

* fix: revert pnpm-lock

* fix: add url fetch

* fix: format

* fix: keep renderMessage clear

* fix: update css

* feat(react-ui): restyle the behind-the-scenes tool timeline

Rework the timeline so a live run reads as one quiet, accumulating list
rather than a stack of bordered cards:

- Every revealed step stays on screen; only the newest shimmers + blinks,
  and the tray header reads "Working" while the model is still thinking.
- Each tool row is now the collapsible trigger, expanding into either the
  result's links or a single block holding the request and response.
- Glyphs are per tool family (web search, image search, artifact) with a
  generic fallback for app-defined tools, drawn in primary at 30% so they
  sit behind the copy. A failure is the only status that keeps a colour.
- The height-capped list scrolls silently, fading each edge only while a
  row is actually hidden behind it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): make the tool-call shimmer actually render

The effect paints text with gradients clipped to the glyphs, but every
copy also set an opaque -webkit-text-fill-color, which paints over the
clipped background — the animation ran invisibly. The fill has to be
transparent, as the reduced-motion override already assumed.

Its geometry mixed absolute and relative lengths: a 100px band inside an
image sized to 250% of the label came out wider than a short label like
"Working", lighting the whole word at once, and was still on screen when
the loop restarted, which read as a jump. Sizing the band as a fraction
of the label fixes both and keeps the sweep proportional at any length.

Folds the three near-identical copies into one mixin so the geometry
lives in a single place, and eases the duration to 3s.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(openui-cloud): declare a remote MCP server on the chat route

Gives the example a source of app-defined tools to exercise: Cloud
connects to the server and runs the calls itself, so the stream carries
mcp_list_tools/mcp_call items the adapter already turns into ordinary
timeline entries — no client-side tool loop.

Defaults to DeepWiki (public, no auth) so a fresh clone can smoke-test
it, with env overrides for pointing at another server.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: trim the tool-timeline comments, drop the MCP env indirection

The MCP server is a fixed demo default now — the env overrides only
existed to be documented in .env.example, so both go.

Co-authored-by: Cursor <cursoragent@cursor.com>

* revert(openui-cloud): drop the demo MCP server from the chat route

It was only there to exercise the timeline locally; the example keeps to
Cloud's built-in tools.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style(react-ui): unify the tool-row frames and hover states

Both toggles lift their label to primary on hover, so the rows read as
expandable without a hover fill. Raw request/response containers take the
same border and radius as the sources list — a row expands into one or the
other, so they can't look like different kinds of container — and a subtle
tint separates the payloads from the sources, which keeps the plain
surface. Thinking prose moves to primary as the model's own voice, the
running icon pulses 0.6 -> 0.2, and the tray holds 24rem before scrolling.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): only fade a timeline edge with real content behind it

Two ways the mask lied. scrollHeight and clientHeight are rounded while
rows are not, so a list that fits could report a pixel of overflow and
fade an edge it never scrolled past; overflow now has to clear 8px, and
each edge needs 4px of actual scroll offset.

The measurement also ran only when this component rendered, but whether a
row is expanded is the child's state — so the list could grow past its cap
and drop back under it with no render here, leaving the fade stuck on. A
ResizeObserver on the list catches both transitions, since its box is
content-sized right up to the cap. That also covers width changes, so the
window resize listener goes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): stop the thread shifting when a message grows scrollable

Styling ::-webkit-scrollbar opts out of overlay scrollbars, so the bar
takes real width: the centered column jumped sideways the moment a message
got tall enough to scroll, e.g. on expanding a tool row. Reserve the
gutter up front.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: refactor

* fix: hoist interleaved turn tray + tray-open behaviour

* refactor: extract focus-ring mixin, tighten comments

* fix: format

* refactor: make smaller fns

* Update thread.scss

* fix: comments

* fix: openai responses adapter

* fix: rename to web_search

* fix: remove tool calls for deduplication

* fix: styles

* refactor: remove duplications

* fix: version bump

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pr3khar <prakhar19376@iiitd.ac.in>
Co-authored-by: Cursor <cursoragent@cursor.com>
AbhinRustagi added a commit that referenced this pull request Aug 4, 2026
* refactor(openui-cloud): slim template onto shared @openuidev packages

- ModelSwitcher: use @openuidev/react-ui (drop local model-switcher/); pass provider logos as {light,dark}
- Billing: replace local BillingCreditsDialog with dev-only OpenUICreditsModal from @openuidev/devtools
- LLM: swap createCloudChatLLM for fetchLLM + buildBody (429 flows via observability); hold LLM in a ref
- Cleanup: remove unused deps, cloud-chat-llm, chat-error, cloud-chat-constants, orphaned CSS/logos

Depends on #893 (packages) being merged + published first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* Update package.json

* refactor(openui-cloud): use the useFetchLLM hook for the chat LLM

Drops the local llmRef + selectedModelRef plumbing for useFetchLLM, which holds a stable instance and reads options fresh per send — buildBody closes over selectedModel directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): use fetchLLM; drop react-headless/react-lang/tailwind deps

- page.tsx: replace hand-rolled ChatLLM with fetchLLM (imported from @openuidev/react-ui), buildBody sends { messages }
- drop Tailwind (trivial usage): minimal globals.css reset, inline layout style, delete postcss.config.mjs
- package.json: remove @openuidev/react-headless, @openuidev/react-lang, tailwindcss, @tailwindcss/postcss; react-ui -> 0.13.3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): drop thread-header CSS overrides now in react-ui

Title :empty-hide and actions flex are library defaults after the thread.scss change; the openui-cloud-thread-header hook class is unreferenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): adopt ref-commit simplifications

- route.ts: badRequest helper, NextResponse, model allowlist (reject unknown), propagate upstream errors with status — billing.ts deleted (store now surfaces server messages)
- theme: useSystemThemeMode from react-ui; delete use-system-theme.tsx; unwrap ThemeProvider in layout
- globals.css: trim to page shell + artifact compat (~18 lines); drop Tailwind + postcss.config.mjs + tailwind deps + vitest
- frontend-token: NextResponse, propagate upstream status

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(templates): align to react-ui 0.14 / react-headless 0.10; add devtools to self-hosted

- cloud: ^0.14.0 / ^0.10.0
- self-hosted: react-ui ^0.14.0; OpenUIDevtools rendered dev-only in layout (devDependency)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* docs(templates): refresh READMEs for the slimmed starters

- cloud: drop OPENUI_MODEL, point at src/lib/models.tsx + /api/chat validation, list useFetchLLM/ModelSwitcher/devtools, pnpm-only
- self-hosted: add setup section (OPENAI_API_KEY), fix route path, pnpm-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-self-hosted): forward raw SSE via .asResponse() + openAIAdapter

Drops the NDJSON re-encode (toReadableStream); propagates browser aborts via req.signal; model overridable with OPENAI_MODEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* chore(cli): bump to 0.3.0 for the slimmed templates

Scaffolded output changes substantially (react-ui 0.14 / react-headless 0.10, fetchLLM/useFetchLLM wiring, devtools, removed billing/tailwind) — minor bump, matching the package versioning rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* refactor(openui-cloud): consolidate react-ui imports, extract starters, trim comments

- import everything from @openuidev/react-ui (headless re-export); starters/prompt templates moved to src/lib/starters
- drop the now-trivial handleModelChange wrapper; use the logoPath const
- shorten route/model/component comments to the essentials (tool-loop's safety-rule docs kept)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix(openui-self-hosted): base sans font on body (Tailwind preflight used to provide it)

react-ui sets fonts per component, not on body — text that merely inherits fell back to the browser serif once Tailwind's preflight was removed. Same latent gap exists in the reference commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* feat(openui-self-hosted): load Inter via next/font like the cloud template

Replaces the globals.css font-family fallback — next/font self-hosts Inter with built-in fallbacks, matching react-ui's --openui-font-body token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpABGCgS6C4HRdQSrwhPMR

* fix: update docs and match latest changes

* fix: update readme

* fix: add favicon

* fix: version match

* fix: format

* fix: styles

* fix: remove credits modal

* fix: update lockfiles

* update lock files

* Update self-hosting.mdx

* Update SKILL.md

* fix: update minimumReleaseAge rule

* fix: lockfiles

* fix: add interleaving for persisted data

* fix: add streaming fix

* fix: messageGroup

* fix: format

* fix: scroll issues

* fix: revert pnpm-lock

* fix: add url fetch

* fix: format

* fix: keep renderMessage clear

* fix: update css

* feat(react-ui): restyle the behind-the-scenes tool timeline

Rework the timeline so a live run reads as one quiet, accumulating list
rather than a stack of bordered cards:

- Every revealed step stays on screen; only the newest shimmers + blinks,
  and the tray header reads "Working" while the model is still thinking.
- Each tool row is now the collapsible trigger, expanding into either the
  result's links or a single block holding the request and response.
- Glyphs are per tool family (web search, image search, artifact) with a
  generic fallback for app-defined tools, drawn in primary at 30% so they
  sit behind the copy. A failure is the only status that keeps a colour.
- The height-capped list scrolls silently, fading each edge only while a
  row is actually hidden behind it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): make the tool-call shimmer actually render

The effect paints text with gradients clipped to the glyphs, but every
copy also set an opaque -webkit-text-fill-color, which paints over the
clipped background — the animation ran invisibly. The fill has to be
transparent, as the reduced-motion override already assumed.

Its geometry mixed absolute and relative lengths: a 100px band inside an
image sized to 250% of the label came out wider than a short label like
"Working", lighting the whole word at once, and was still on screen when
the loop restarted, which read as a jump. Sizing the band as a fraction
of the label fixes both and keeps the sweep proportional at any length.

Folds the three near-identical copies into one mixin so the geometry
lives in a single place, and eases the duration to 3s.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(openui-cloud): declare a remote MCP server on the chat route

Gives the example a source of app-defined tools to exercise: Cloud
connects to the server and runs the calls itself, so the stream carries
mcp_list_tools/mcp_call items the adapter already turns into ordinary
timeline entries — no client-side tool loop.

Defaults to DeepWiki (public, no auth) so a fresh clone can smoke-test
it, with env overrides for pointing at another server.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: trim the tool-timeline comments, drop the MCP env indirection

The MCP server is a fixed demo default now — the env overrides only
existed to be documented in .env.example, so both go.

Co-authored-by: Cursor <cursoragent@cursor.com>

* revert(openui-cloud): drop the demo MCP server from the chat route

It was only there to exercise the timeline locally; the example keeps to
Cloud's built-in tools.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style(react-ui): unify the tool-row frames and hover states

Both toggles lift their label to primary on hover, so the rows read as
expandable without a hover fill. Raw request/response containers take the
same border and radius as the sources list — a row expands into one or the
other, so they can't look like different kinds of container — and a subtle
tint separates the payloads from the sources, which keeps the plain
surface. Thinking prose moves to primary as the model's own voice, the
running icon pulses 0.6 -> 0.2, and the tray holds 24rem before scrolling.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): only fade a timeline edge with real content behind it

Two ways the mask lied. scrollHeight and clientHeight are rounded while
rows are not, so a list that fits could report a pixel of overflow and
fade an edge it never scrolled past; overflow now has to clear 8px, and
each edge needs 4px of actual scroll offset.

The measurement also ran only when this component rendered, but whether a
row is expanded is the child's state — so the list could grow past its cap
and drop back under it with no render here, leaving the fade stuck on. A
ResizeObserver on the list catches both transitions, since its box is
content-sized right up to the cap. That also covers width changes, so the
window resize listener goes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(react-ui): stop the thread shifting when a message grows scrollable

Styling ::-webkit-scrollbar opts out of overlay scrollbars, so the bar
takes real width: the centered column jumped sideways the moment a message
got tall enough to scroll, e.g. on expanding a tool row. Reserve the
gutter up front.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: refactor

* fix: hoist interleaved turn tray + tray-open behaviour

* refactor: extract focus-ring mixin, tighten comments

* fix: format

* refactor: make smaller fns

* Update thread.scss

* fix: comments

* fix: openai responses adapter

* fix: rename to web_search

* fix: remove tool calls for deduplication

* fix: styles

* refactor: remove duplications

* fix: version bump

* chore: version bump

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pr3khar <prakhar19376@iiitd.ac.in>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants