From 42eb901e8696a287e6b6aefbb74ded29bdc48b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 21 May 2026 16:38:17 -0400 Subject: [PATCH] fix(studio): inject runtime env overrides for pre-built SPA mode VITE_STUDIO_* env vars set in the user's shell had no effect when running `hyperframes preview` because the pre-built studio bundle had them baked at Vite build time. The embedded Hono server now collects VITE_STUDIO_* vars from process.env and injects them as a `window.__HF_STUDIO_ENV__` script tag into index.html. The client merges this runtime object on top of the baked `import.meta.env`, so flags like VITE_STUDIO_ENABLE_BLOCKS_PANEL=1 work as expected at runtime. --- packages/cli/src/server/studioServer.ts | 23 ++++++++++++++++++- .../editor/manualEditingAvailability.ts | 11 ++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/server/studioServer.ts b/packages/cli/src/server/studioServer.ts index 63daca28e9..d7165e2c72 100644 --- a/packages/cli/src/server/studioServer.ts +++ b/packages/cli/src/server/studioServer.ts @@ -481,6 +481,22 @@ export function createStudioServer(options: StudioServerOptions): StudioServer { app.get("/icons/*", serveStudioStaticFile); app.get("/favicon.svg", serveStudioStaticFile); + // ── Runtime env injection ─────────────────────────────────────────────── + // When the studio is served as a pre-built SPA, Vite `VITE_STUDIO_*` env + // vars were baked at build time. Collect any such vars from the current + // process.env and inject them as `window.__HF_STUDIO_ENV__` so the client + // can pick them up at runtime, overriding the baked defaults. + function buildRuntimeEnvScript(): string { + const overrides: Record = {}; + for (const [key, value] of Object.entries(process.env)) { + if (key.startsWith("VITE_STUDIO_") && value !== undefined) { + overrides[key] = value; + } + } + if (Object.keys(overrides).length === 0) return ""; + return ``; + } + // SPA fallback app.get("*", (c) => { const indexPath = resolve(studioDir, "index.html"); @@ -540,7 +556,12 @@ export function createStudioServer(options: StudioServerOptions): StudioServer { 500, ); } - return c.html(readFileSync(indexPath, "utf-8")); + let html = readFileSync(indexPath, "utf-8"); + const envScript = buildRuntimeEnvScript(); + if (envScript) { + html = html.replace("", `${envScript}`); + } + return c.html(html); }); return { app, watcher }; diff --git a/packages/studio/src/components/editor/manualEditingAvailability.ts b/packages/studio/src/components/editor/manualEditingAvailability.ts index bd3b59c5b4..c373247304 100644 --- a/packages/studio/src/components/editor/manualEditingAvailability.ts +++ b/packages/studio/src/components/editor/manualEditingAvailability.ts @@ -30,7 +30,16 @@ export function resolveStudioBooleanEnvFlag( // and downstream `env[name]` reads would crash. Fall back to `{}` so // every flag resolves to its declared default outside Vite. Direct // property access keeps Vite's compile-time transform happy. -const env = (import.meta.env ?? {}) as StudioFeatureFlagEnv; +// +// When the studio is served as a pre-built SPA by the embedded Hono server, +// `import.meta.env` values were baked at build time. The server injects +// `window.__HF_STUDIO_ENV__` with any `VITE_STUDIO_*` env vars from the +// user's shell, so runtime overrides take precedence over baked defaults. +const runtimeEnv = + typeof window !== "undefined" + ? ((window as Window & { __HF_STUDIO_ENV__?: StudioFeatureFlagEnv }).__HF_STUDIO_ENV__ ?? {}) + : {}; +const env = { ...(import.meta.env ?? {}), ...runtimeEnv } as StudioFeatureFlagEnv; export const STUDIO_PREVIEW_MANUAL_EDITING_ENABLED = resolveStudioBooleanEnvFlag( env,