pkg/bridge: bootstrap @hanzo/gui runtime + provider in mountBridge

Phase 3 R2 swapped <button>/<input> primitives onto @hanzo/gui's Button
and Input components in SwapForm, WalletConnect, and AssetInput, but the
runtime bootstrap was never added. The library's primitives read from
globalThis.__guiConfig at render time and throw Error("Err0") when null,
and the JSX tree also needs a GuiProvider for theme context — without
both, the SDK mounts to a blank page.

Wire both inside the SDK so consumers don't need to know about them:

  - mount.ts: call createGui(getDefaultGuiConfig()) before React render,
    idempotent (skips when globalThis.__guiConfig already populated).
  - Bridge.tsx: wrap <BridgeApp /> in <GuiProvider defaultTheme="dark">.

@hanzogui/config-default added as a direct dependency (was transitive
only). Imports cast to unknown to bridge the source/types mismatch
introduced by the monorepo's postinstall rename of upstream
createHanzogui/HanzoguiProvider → createGui/GuiProvider on .ts files
but not the shipped .d.ts.
This commit is contained in:
2026-05-21 17:00:40 -07:00
parent 300354cb5a
commit 73fa005ef9
4 changed files with 129 additions and 21 deletions
+1
View File
@@ -49,6 +49,7 @@
"dependencies": {
"@luxfi/brand": "^1.0.0",
"@hanzo/gui": "^7.0.0",
"@hanzogui/config-default": "^7.0.0",
"@hanzogui/core": "^7.0.0",
"@hanzogui/helpers": "^7.0.0",
"@hanzogui/constants": "^7.0.0",
+24 -2
View File
@@ -19,12 +19,25 @@
// page). The bridge UI is now inlined under `./app/`, so there is no
// runtime dependency on any sibling workspace package.
import { useEffect, type FC } from 'react'
import { useEffect, type FC, type ReactNode } from 'react'
// @hanzo/gui source exports `GuiProvider` (after the monorepo's postinstall
// rename) but its shipped .d.ts files still carry the upstream
// `HanzoguiProvider` name. We pull the runtime symbol directly from the
// untyped re-export module to avoid forcing every consumer of @luxfi/bridge
// to also ship the rename patch.
import * as gui from '@hanzo/gui'
import { applyBrandMetadata, setConfig } from './config'
import type { BridgeConfig } from './types'
import { BridgeApp } from './app/BridgeApp'
const GuiProvider: FC<{
children: ReactNode
defaultTheme?: 'light' | 'dark'
}> = (gui as unknown as {
GuiProvider: FC<{ children: ReactNode; defaultTheme?: 'light' | 'dark' }>
}).GuiProvider
export interface BridgeProps {
/** Runtime config. Seeded into the SDK config cache on first render. */
config: BridgeConfig
@@ -41,7 +54,16 @@ export const Bridge: FC<BridgeProps> = ({ config }) => {
applyBrandMetadata(config.brand)
}, [config.brand])
return <BridgeApp />
// `GuiProvider` is required for @hanzo/gui's `Button` / `Input` primitives
// (used in SwapForm, WalletConnect, AssetInput) to find their theme. The
// SDK owns this wrapper so consumers don't have to know it exists — the
// runtime config is configured separately by `mountBridge` via
// `createGui(getDefaultGuiConfig())`.
return (
<GuiProvider defaultTheme="dark">
<BridgeApp />
</GuiProvider>
)
}
export default Bridge
+38 -1
View File
@@ -22,6 +22,40 @@ import type { MountBridgeOptions } from './types'
const DEFAULT_ROOT_ID = 'bridge-root'
/**
* Boot the @hanzo/gui runtime once globally. The library's `Button` and
* `Input` primitives (used by Phase 3 R2's SwapForm / WalletConnect /
* AssetInput) read from `globalThis.__guiConfig` at render time and throw
* `Error("Err0")` when it's null. The SDK owns this initialisation so
* every consumer doesn't have to know that detail.
*
* Idempotent — `createGui` is safe to call multiple times (later calls
* overwrite the global config, which is fine because the default config
* is deterministic).
*/
async function ensureGuiConfigured(): Promise<void> {
if (
typeof globalThis !== 'undefined' &&
(globalThis as { __guiConfig?: unknown }).__guiConfig
) {
return
}
// The @hanzo/gui package's source exports `createGui` (after the monorepo's
// postinstall rename of upstream `createHanzogui` → `createGui`), but its
// shipped .d.ts files still carry the original Hanzogui name. The runtime
// export name is canonical here — we widen the import type to `any` so
// tsc resolves the type-only mismatch without forcing every consumer to
// ship the same rename patch.
const [gui, cfgDefault] = await Promise.all([
import('@hanzo/gui'),
import('@hanzogui/config-default'),
])
const createGui = (gui as unknown as {
createGui: (c: unknown) => unknown
}).createGui
createGui(cfgDefault.getDefaultGuiConfig())
}
/**
* Boot the bridge UI inside the host page.
*
@@ -30,7 +64,8 @@ const DEFAULT_ROOT_ID = 'bridge-root'
* optional auth / kms / wallet / mpc blocks when present).
* 2. Apply brand metadata to `document` (title, favicon, CSS variables).
* 3. Resolve the DOM mount element (`#bridge-root` by default).
* 4. Lazy-import `react-dom/client` and `react`, then render `<Bridge />`.
* 4. Configure @hanzo/gui's global runtime so Button/Input render.
* 5. Lazy-import `react-dom/client` and `react`, then render `<Bridge />`.
*
* The dynamic imports keep `react-dom` out of the SDK's main-bundle critical
* path; consumers that only import types pay nothing for the runtime.
@@ -49,6 +84,8 @@ export async function mountBridge(opts: MountBridgeOptions): Promise<void> {
throw new Error(`mountBridge: #${rootId} not found in document`)
}
await ensureGuiConfigured()
const [{ createRoot }, react] = await Promise.all([
import('react-dom/client'),
import('react'),
+66 -18
View File
@@ -90,7 +90,7 @@ importers:
version: 2.2.0([email protected])
'@luxdefi/ui':
specifier: 0.2.3
version: 0.2.3(@next/[email protected](@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected])))(@types/[email protected])(@types/[email protected])([email protected]([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]))([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected]))))([email protected])([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected])))
version: 0.2.3(@next/[email protected](@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected])))(@types/[email protected])(@types/[email protected])([email protected]([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]))([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected]))))([email protected])([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected])))
'@next/mdx':
specifier: 14.1.0
version: 14.1.0(@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected]))
@@ -129,7 +129,7 @@ importers:
version: 0.307.0([email protected])
next:
specifier: 'catalog:'
version: 14.2.16(@opentelemetry/[email protected])([email protected]([email protected]))([email protected])
version: 14.2.16(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])
react:
specifier: 'catalog:'
version: 18.3.1
@@ -295,6 +295,18 @@ importers:
specifier: ^5.5.4
version: 5.7.2
e2e:
devDependencies:
'@playwright/test':
specifier: ^1.49.0
version: 1.60.0
'@types/node':
specifier: 20.14.15
version: 20.14.15
typescript:
specifier: 5.7.2
version: 5.7.2
pkg/bridge:
dependencies:
'@hanzo/gui':
@@ -315,6 +327,9 @@ importers:
'@hanzogui/card':
specifier: ^7.0.0
version: 7.0.0([email protected]([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
'@hanzogui/config-default':
specifier: ^7.0.0
version: 7.0.0([email protected]([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
'@hanzogui/constants':
specifier: ^7.0.0
version: 7.0.0([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
@@ -2630,6 +2645,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@playwright/[email protected]':
resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==}
engines: {node: '>=18'}
hasBin: true
'@prisma/[email protected]':
resolution: {integrity: sha512-ARAJaPs+eBkemdky/XU3cvGRl+mIPHCN2lCXsl5Vlb0E2gV+R6IN7aCI8CisRGszEZondwIsW9Iz8EJkTdykyA==}
engines: {node: '>=18.18'}
@@ -3476,6 +3496,9 @@ packages:
'@types/[email protected]':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/[email protected]':
resolution: {integrity: sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==}
'@types/[email protected]':
resolution: {integrity: sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==}
@@ -4365,10 +4388,6 @@ packages:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
[email protected]:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
@@ -6141,11 +6160,21 @@ packages:
engines: {node: '>=18'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
engines: {node: '>=18'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
engines: {node: '>=18'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==}
engines: {node: '>=18'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==}
engines: {node: '>=12'}
@@ -7047,6 +7076,9 @@ packages:
[email protected]:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
[email protected]:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
[email protected]:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
@@ -11084,7 +11116,7 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@luxdefi/[email protected](@next/[email protected](@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected])))(@types/[email protected])(@types/[email protected])([email protected]([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]))([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected]))))([email protected])([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected])))':
'@luxdefi/[email protected](@next/[email protected](@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected])))(@types/[email protected])(@types/[email protected])([email protected]([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]))([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected]))))([email protected])([email protected]([email protected](@swc/[email protected](@swc/[email protected]))(@types/[email protected])([email protected])))':
dependencies:
'@hookform/resolvers': 3.9.1([email protected]([email protected]))
'@next/mdx': 14.1.0(@mdx-js/[email protected]([email protected])([email protected](@swc/[email protected](@swc/[email protected]))))(@mdx-js/[email protected](@types/[email protected])([email protected]))
@@ -11101,8 +11133,8 @@ snapshots:
lodash.merge: 4.6.2
lucide-react: 0.307.0([email protected])
markdown-to-jsx: 7.7.2([email protected])
next: 14.2.16(@opentelemetry/[email protected])([email protected]([email protected]))([email protected])
next-themes: 0.2.1([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
next: 14.2.16(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])
next-themes: 0.2.1([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
postcss-selector-parser: 6.1.2
react: 18.3.1
react-device-detect: 2.2.3([email protected]([email protected]))([email protected])
@@ -11241,6 +11273,10 @@ snapshots:
'@pkgjs/[email protected]':
optional: true
'@playwright/[email protected]':
dependencies:
playwright: 1.60.0
'@prisma/[email protected]([email protected]([email protected]))([email protected])':
optionalDependencies:
prisma: 6.3.1([email protected])
@@ -11698,7 +11734,7 @@ snapshots:
metro-core: 0.81.0
node-fetch: 2.7.0
readline: 1.3.0
semver: 7.7.2
semver: 7.8.1
transitivePeerDependencies:
- '@babel/core'
- '@babel/preset-env'
@@ -12138,6 +12174,10 @@ snapshots:
dependencies:
'@types/node': 20.17.11
'@types/[email protected]':
dependencies:
undici-types: 5.26.5
'@types/[email protected]':
dependencies:
undici-types: 6.19.8
@@ -13198,9 +13238,6 @@ snapshots:
[email protected]: {}
[email protected]:
optional: true
[email protected]: {}
[email protected]: {}
@@ -14618,7 +14655,7 @@ snapshots:
[email protected]:
dependencies:
detect-libc: 2.0.3
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.32.0
lightningcss-darwin-arm64: 1.32.0
@@ -15322,13 +15359,13 @@ snapshots:
[email protected]: {}
[email protected]([email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]):
[email protected]([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]):
dependencies:
next: 14.2.16(@opentelemetry/[email protected])([email protected]([email protected]))([email protected])
next: 14.2.16(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])
react: 18.3.1
react-dom: 18.3.1([email protected])
[email protected](@opentelemetry/[email protected])([email protected]([email protected]))([email protected]):
[email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]):
dependencies:
'@next/env': 14.2.16
'@swc/helpers': 0.5.5
@@ -15350,6 +15387,7 @@ snapshots:
'@next/swc-win32-ia32-msvc': 14.2.16
'@next/swc-win32-x64-msvc': 14.2.16
'@opentelemetry/api': 1.9.0
'@playwright/test': 1.60.0
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -15547,12 +15585,20 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
playwright-core: 1.58.2
optionalDependencies:
fsevents: 2.3.2
[email protected]:
dependencies:
playwright-core: 1.60.0
optionalDependencies:
fsevents: 2.3.2
[email protected]:
dependencies:
queue-lit: 1.5.2
@@ -15776,7 +15822,7 @@ snapshots:
react-refresh: 0.14.2
regenerator-runtime: 0.13.11
scheduler: 0.24.0-canary-efb381bbf-20230505
semver: 7.7.2
semver: 7.8.1
stacktrace-parser: 0.1.10
whatwg-fetch: 3.6.20
ws: 6.2.3([email protected])([email protected])
@@ -16582,6 +16628,8 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: