feat(hermesworld): embed v1 web client (play.hermes-world.ai/play/web) in workspace — real in-app iframe replacing open-in-tab placeholder, with loading veil + fallback + open-full affordance

This commit is contained in:
Aurora
2026-06-05 18:44:40 -04:00
parent f547ca1c4e
commit 51fc76611e

View File

@@ -1,12 +1,22 @@
import { useMemo } from 'react' import { useMemo, useState } from 'react'
import { WaveChatPanelsShowcase } from './components/wave-chat-panels-showcase' import { WaveChatPanelsShowcase } from './components/wave-chat-panels-showcase'
const HERMES_WORLD_ORIGIN = 'https://hermes-world.ai' // v1 web client (WebGL build, char-select + world entry verified). Sets
// COEP require-corp + COOP same-origin for WebGL threading, but has NO
// X-Frame-Options / restrictive frame-ancestors, so it can be embedded.
const HERMES_WEB_ORIGIN = 'https://play.hermes-world.ai'
const HERMES_SITE_ORIGIN = 'https://hermes-world.ai'
export function HermesWorldEmbed() { export function HermesWorldEmbed() {
const showPanelShowcase = typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('panels') === 'wave-chat' const showPanelShowcase =
const playUrl = useMemo(() => { typeof window !== 'undefined' &&
const url = new URL('/play/', HERMES_WORLD_ORIGIN) new URLSearchParams(window.location.search).get('panels') === 'wave-chat'
const [loaded, setLoaded] = useState(false)
const [failed, setFailed] = useState(false)
const webUrl = useMemo(() => {
const url = new URL('/play/web/', HERMES_WEB_ORIGIN)
url.searchParams.set('source', 'hermes-workspace') url.searchParams.set('source', 'hermes-workspace')
return url.toString() return url.toString()
}, []) }, [])
@@ -16,40 +26,84 @@ export function HermesWorldEmbed() {
} }
return ( return (
<main className="relative flex h-full min-h-0 items-center justify-center overflow-hidden bg-[#050015] px-4 text-white"> <main className="relative flex h-full min-h-0 flex-col overflow-hidden bg-[#050015] text-white">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(168,85,247,.24),transparent_48%),#050015]" /> {/* Embedded HermesWorld v1 web client */}
<div className="relative max-w-xl rounded-3xl border border-white/12 bg-black/45 px-6 py-6 text-center shadow-2xl backdrop-blur-xl"> {!failed && (
<div className="text-xs font-bold uppercase tracking-[0.24em] text-cyan-200/70"> <iframe
Hermes Workspace title="HermesWorld"
src={webUrl}
onLoad={() => setLoaded(true)}
onError={() => setFailed(true)}
allow="autoplay; fullscreen; gamepad; clipboard-write; cross-origin-isolated"
allowFullScreen
className="h-full w-full min-h-0 flex-1 border-0 bg-[#050015]"
/>
)}
{/* Loading veil over the iframe until first load */}
{!loaded && !failed && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center bg-[radial-gradient(circle_at_50%_35%,rgba(168,85,247,.24),transparent_48%),#050015]">
<div className="flex flex-col items-center gap-3">
<img
src="/hermesworld-logo.svg"
alt="HermesWorld"
className="h-14 w-14 animate-pulse rounded-2xl shadow-[0_0_34px_rgba(34,211,238,.25)]"
/>
<div className="text-xs font-bold uppercase tracking-[0.24em] text-cyan-200/70">
Loading HermesWorld
</div>
</div>
</div> </div>
<h1 className="mt-2 text-3xl font-black tracking-tight"> )}
Open HermesWorld in a full tab
</h1> {/* Fallback if the embed cannot load */}
<p className="mt-3 text-sm leading-relaxed text-white/65"> {failed && (
HermesWorld currently refuses iframe embedding, so Workspace no longer <div className="relative flex h-full items-center justify-center px-4">
loads it in an iframe that fails with refused to connect. The hosted <div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(168,85,247,.24),transparent_48%),#050015]" />
build should be opened directly while the game deployment fixes its <div className="relative max-w-xl rounded-3xl border border-white/12 bg-black/45 px-6 py-6 text-center shadow-2xl backdrop-blur-xl">
stale asset/MIME issue for <code className="rounded bg-white/10 px-1">/assets/styles-*.css</code>. <div className="text-xs font-bold uppercase tracking-[0.24em] text-cyan-200/70">
</p> Hermes Workspace
<div className="mt-5 flex flex-wrap justify-center gap-2"> </div>
<a <h1 className="mt-2 text-3xl font-black tracking-tight">
href={playUrl} Open HermesWorld in a full tab
target="_blank" </h1>
rel="noopener noreferrer" <p className="mt-3 text-sm leading-relaxed text-white/65">
className="rounded-full bg-cyan-300 px-5 py-2 text-sm font-black uppercase tracking-[0.14em] text-slate-950 transition hover:bg-white" The embedded client couldnt load here. Open the full web build in
> a new tab to play.
Open full </p>
</a> <div className="mt-5 flex flex-wrap justify-center gap-2">
<a <a
href={`${HERMES_WORLD_ORIGIN}/`} href={webUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="rounded-full border border-white/15 bg-white/8 px-5 py-2 text-sm font-bold uppercase tracking-[0.14em] text-white/75 transition hover:border-cyan-200/40 hover:text-white" className="rounded-full bg-cyan-300 px-5 py-2 text-sm font-black uppercase tracking-[0.14em] text-slate-950 transition hover:bg-white"
> >
Site root Open full
</a> </a>
<a
href={`${HERMES_SITE_ORIGIN}/`}
target="_blank"
rel="noopener noreferrer"
className="rounded-full border border-white/15 bg-white/8 px-5 py-2 text-sm font-bold uppercase tracking-[0.14em] text-white/75 transition hover:border-cyan-200/40 hover:text-white"
>
Site root
</a>
</div>
</div>
</div> </div>
</div> )}
{/* Persistent "open full tab" affordance while embedded */}
{!failed && (
<a
href={webUrl}
target="_blank"
rel="noopener noreferrer"
className="absolute right-3 top-3 z-10 rounded-full border border-white/15 bg-black/55 px-3 py-1.5 text-[11px] font-bold uppercase tracking-[0.14em] text-white/75 backdrop-blur-md transition hover:border-cyan-200/40 hover:text-white"
>
Open full
</a>
)}
</main> </main>
) )
} }