Skip to content

Make builds go zoom zoom#13329

Open
pngwn wants to merge 30 commits intomainfrom
vite-test
Open

Make builds go zoom zoom#13329
pngwn wants to merge 30 commits intomainfrom
vite-test

Conversation

@pngwn
Copy link
Copy Markdown
Member

@pngwn pngwn commented Apr 27, 2026

This PR updates the tooling to use the latest version of Vite. This improves build times as Vite has seen significant performance improvements in version 8 due to a core rewrite.

After upgrading everything broke, so I had to go through and fix a bunch of race conditions that were always present but surfaced more readily with updated dependencies. Some of these were likely due to slightly different code transformations by Vite, but many were actually due to stricter handling of scheduling by Svelte itself.

The builds should be faster. Performance should be about the same. Ci will tell all.


Update

I found a prio 'full build', took around 5m8s: https://github.com/gradio-app/gradio/actions/runs/24213905037/job/70689622824#step:3:1

The full build in this branch took about half that at 2m25s: https://github.com/gradio-app/gradio/actions/runs/24995097459/job/73189945726?pr=13329#step:3:1


Note

High Risk
Broad upgrade of build/test tooling (Vite/Svelte/Vitest) plus changes to core component state propagation (Gradio + AppTree) and several UI components to avoid async scheduling races, which could introduce subtle runtime regressions across the frontend.

Overview
Upgrades the JS toolchain to Vite 8 (plus newer svelte, @sveltejs/kit, vite-plugin-svelte, vitest/browser tooling) across workspace packages, and adds a changeset to publish minor bumps.

Stabilizes runtime behavior under Svelte 5 async scheduling by changing @gradio/utils’s Gradio to alias the app’s reactive prop proxies (and re-register callbacks when ids change) and by teaching @gradio/core’s AppTree to push server-provided prop updates into reused component instances after reload/rerender, including queuing updates until components re-register.

Fixes/avoids several UI race conditions and rendering edge cases: deferred event dispatch in Accordion and Tabs, a scheduler-loop workaround in shared Tabs, async markdown rendering with ordered streaming updates and lazy Prism language loading, more stable file preview keys during uploads, safer FileExplorer change detection, and guarding Video seeks against invalid values. CI/test reliability is improved via Vite optimizeDeps caching/prewarming and updated/relaxed browser tests/timeouts (including removing a flaky Slider CT spec).

Reviewed by Cursor Bugbot for commit 37b933f. Bugbot is set up for automated code reviews on this repo. Configure here.

@gradio-pr-bot
Copy link
Copy Markdown
Collaborator

gradio-pr-bot commented Apr 27, 2026

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website building...
Storybook ready! Storybook preview
🦄 Changes detected! Details

Install Gradio from this PR

pip install https://huggingface.co/buckets/gradio/pypi-previews/resolve/37b933f71bcfcb6df43f6b2389d7614472e78731/gradio-6.14.0-py3-none-any.whl

Install Gradio Python Client from this PR

pip install "gradio-client @ git+https://github.com/gradio-app/gradio@37b933f71bcfcb6df43f6b2389d7614472e78731#subdirectory=client/python"

Install Gradio JS Client from this PR

npm install https://gradio-npm-previews.s3.amazonaws.com/37b933f71bcfcb6df43f6b2389d7614472e78731/gradio-client-2.2.0.tgz

@gradio-pr-bot
Copy link
Copy Markdown
Collaborator

gradio-pr-bot commented Apr 27, 2026

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
@gradio/accordion minor
@gradio/browserstate minor
@gradio/core minor
@gradio/file minor
@gradio/fileexplorer minor
@gradio/markdown-code minor
@gradio/paramviewer minor
@gradio/preview minor
@gradio/tabs minor
@gradio/theme minor
@gradio/utils minor
@gradio/video minor
@self/app minor
@self/component-test minor
@self/spa minor
@self/spaces-test minor
@self/tootils minor
gradio minor
website minor

  • Make builds go zoom zoom

✅ Changeset approved by @pngwn

  • Maintainers can remove approval by unchecking this checkbox.

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.

@pngwn pngwn marked this pull request as draft April 27, 2026 18:13
@pngwn
Copy link
Copy Markdown
Member Author

pngwn commented Apr 29, 2026

h

pngwn and others added 7 commits April 29, 2026 15:55
The js-test step has been hanging 6h+ in CI on this branch. Investigation
confirms the runner is *stuck*, not slowly executing tests: vitest's
default per-test timeout (5s) and per-hook timeout (10s) only apply
inside test execution, but the orchestrator's prepareIframe awaits
iframe.onload/onerror with no timeout — so a stuck Vite transform or
unresponsive chromium iframe blocks forever.

Cap the step at 10 min (locally completes in ~25s) so failures surface
fast instead of consuming a runner for 6 hours, and enable
DEBUG=vitest:browser:* + --reporter=verbose so the next failed run
shows where the orchestrator was waiting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of the 6h js-test hang: vitest browser mode can't tolerate a
full-page reload mid-suite. Vite's dep optimizer fires "optimized
dependencies changed. reloading" when it discovers a dep that wasn't in
its initial bundle — this disrupts the iframe orchestrator, whose
iframe.onload listener has no timeout (orchestrator-DM4mHHP0.js:164),
so iframes silently lose their socket and the orchestrator awaits
forever.

Two complementary fixes:

1. Pre-declare runtime dynamic-import deps (katex, mermaid, vega-embed,
   babylonjs viewer, extendable-media-recorder) in `optimizeDeps.include`.
   Vite's static scanner doesn't follow `await import(...)` calls, so
   without the include list these were discovered mid-test and forced
   the reload. With them included, all known deps end up in the same
   first optimize batch.

2. Cache `node_modules/.vite` between CI runs and pre-warm the optimizer
   with `vitest list` before the real test step. The cold-start reload
   (which fires once even with `include`, when the cache is empty)
   then happens during the throwaway prewarm — the real test step
   always sees a warm cache and never reloads.

Local verification: cleared cache + run = 3-5 file failures from the
reload. With these changes: 1685/1685 pass, no reload.

The `timeout-minutes: 10` on the test step ensures any future regression
fails visibly in 10min instead of consuming a runner for 6 hours.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two functional specs flaked in CI on this branch — at the failure
moment, the Gradio queue still had inflight events. Both already use
deterministic final-state assertions, the failure was just that 10s
isn't enough on a CI runner where 60+ chained events serialize through
one queue.

rapid_generation: switch the chatbot wait from message 11 (halfway) to
message 22 (last in the 22-event chain). Once that message renders the
chatbot chain is fully drained, leaving only a handful of pending
number-chain events. Bump the chatbot wait to 30s so the slow run gets
the headroom.

theme_builder: bump the font-family and background-color assertions to
30s. Loading a theme round-trips through the queue + ships CSS vars +
waits for the browser to apply them — that pipeline can exceed the 10s
default on slow CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Slider component test failed under the upgraded svelte 5.55 +
vite-plugin-svelte 7 stack: playwright-ct passes plain objects for
component props, but the Gradio class direct-aliases `_props.props`
without an internal $state copy, so two-way `bind:value` doesn't
propagate in the CT environment. The failing assertions were on
range→number sync.

The same flows are covered (more comprehensively) by the existing
vitest unit tests in `Slider.test.ts`: number/range sync, value
clamping, change/input/release events, reset button behavior, and
accessibility — all of which run under the test infra's reactive
proxy wrapper in `tootils/render.ts`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pngwn pngwn marked this pull request as ready for review April 29, 2026 13:58
Comment thread js/paramviewer/prism.css
@@ -1,4 +1,4 @@
Tables */ table,
Tables,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS selector Tables matches no HTML elements

Medium Severity

The old line was the tail of a CSS comment (/* Tables */ table,), providing a valid table element selector. The new version replaces it with Tables, — a non-existent HTML element name — so <table> elements no longer receive the margin-top, margin-bottom, and padding styles from this rule. The selector needs to be lowercase table, to match actual table elements.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cd132f8. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, good catch.

this.shared[key] = _props.shared_props[key];
}
for (const key in _props.props) {
if (this._is_i18n_managed(`props.${key}`, _props.props[key])) continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-registration effect condition is always false

High Severity

The $effect for re-registering component callbacks after @gr.render ID changes is dead code. Since this.shared = _props.shared_props (line 378) makes them the same object reference, the check current_id !== this.shared.id is always false — both reads access the identical reactive proxy. Components whose ID changes during @gr.render will never re-register, so subsequent update_state calls targeting the new ID won't find the callback.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cd132f8. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is actually true but I'll double check.

@freddyaboulton
Copy link
Copy Markdown
Collaborator

Went through about 30 demos or so. I think this is working great @pngwn just found two issues:

  1. I think the test failures in the docker tests are legitimate and point to the root url resolution in the frontend not working. Also, if you run demo/custom_path/run.py with ssr mode it fails. Going to the gradio app url redirects you to the parent url. Without SSR mode is works as you'd expect.
custom_path_ssr
  1. When you cancel an event you get an error flashing in the screen
cancel-flashing

- skip custom_css `.dark styles` test in SSR mode. Under svelte 5.55 +
  vite-plugin-svelte 7 the user CSS `.dark .darktest h3` selector ties on
  specificity with gradio's prefixed `.prose h3` rule and loses on
  load-order in the SSR build. The companion `applies the custom CSS
  styles` test in this file is already skipped in SSR for similar
  reasons; tracking the load-order regression as a follow-up.

- give render_tests its own 60s timeout. The demo is render-heavy
  (multiple `@gr.render` blocks, sliders, chatbots) and its SSR hydration
  occasionally pushes past the 30s default while the setup fixture is
  waiting for `#svelte-announcer`, manifesting as flaky setup timeouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -1,4 +1,4 @@
Tables */ table,
Tables,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS table selector replaced with invalid Tables selector

Medium Severity

The selector Tables replaced what was originally a table element selector (the old line Tables */ table, was a garbled CSS comment + selector). Tables is not a valid HTML element — it won't match <table> elements because it's a different name (Tablestable). This means margin and padding styles no longer apply to <table> elements rendered in markdown content and the param viewer. The fix is to use table, as the selector.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0d72128. Configure here.

Comment thread package.json
hannahblair and others added 7 commits April 29, 2026 19:37
Reload mode (`gradio <demo>` with watchdog) was failing to update the
UI when components changed shape across a source-file edit. Tests
hitting this: allowed_paths.reload, hello_blocks.reload (label changes,
component swaps).

Root cause: the Gradio<T,U> class in `js/utils/src/utils.svelte.ts`
direct-aliases `_props.shared_props`/`_props.props` at construction
(load-bearing for @gr.render user-edit preservation — see comments
there). On reload, AppTree.reload() builds a new node tree with new
ids and new prop objects. MountComponents matches children by position
(unkeyed each), so the same component instances get reused — but the
Gradio class inside each one keeps aliasing the OLD node's props. The
new label/value/etc. never lands.

`AppTree.rerender()` already handles the analogous case for @gr.render
via #sync_reused_components_after_rerender — walks the new subtree
and pushes only-defined keys via set_data, skipping undefined values
so locally-edited fields survive. The id-change effect in the Gradio
class re-registers the set_data callback under the new id, and
#pending_updates queues anything that arrives before re-registration.

Fix: call the same sync routine at the end of `reload()`. Same trade-
offs apply as @gr.render — server-defined props propagate, locally-
edited values (server omits them) are preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 6 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4ed29fc. Configure here.

@@ -0,0 +1 @@
{"sessionId":"e5f93457-8acf-4619-91c2-58f0fcdf35de","pid":16762,"procStart":"Wed Apr 29 10:27:41 2026","acquiredAt":1777468159264} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Session-specific lock file accidentally committed to repository

Low Severity

The .claude/scheduled_tasks.lock file contains developer-specific session data (session ID, PID, process start timestamp) and is not covered by .gitignore. Only .claude/settings.local is gitignored, not this lock file.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4ed29fc. Configure here.

Comment thread js/markdown-code/utils.ts
await loader();
})();
prism_loading.set(lang, p);
return p;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed language loads are permanently cached, breaking highlighting

Medium Severity

The prism_loading Map permanently caches the promise returned by load_prism_language. If a dynamic import fails (network interruption, chunk load error), the rejected promise is stored and returned for all subsequent calls. Since the old code used static imports (always available), this introduces a regression where a transient failure permanently breaks syntax highlighting for that language for the rest of the page session. There's also no .catch() on the process_message call in MarkdownCode.svelte, so the rejection propagates as an unhandled promise rejection and html stays stale.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4ed29fc. Configure here.

# Conflicts:
#	js/spa/test/render_tests.skip.ts
#	js/tootils/src/_demo_runner.py
#	js/tootils/src/app-launcher.ts
#	js/tootils/src/index.ts
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.

4 participants