feat(console-web): replace TanStack UI with Solid host pages
- move the management frontend under the wasmeld-console crate\n- use a persistent Host shell with disposable iframe page documents\n- version Host/Page postMessage state and command contracts\n- migrate runtime, service, WIT, invocation, and settings workflows\n- add Tailwind CSS v4, responsive layouts, and Host-owned dialogs\n- emit a dependency-free PWA worker with API cache exclusions\n- remove the superseded root TanStack Start application
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { AlertTriangle, LoaderCircle } from "lucide-solid";
|
||||
import { lazy, Show, Suspense, type Component } from "solid-js";
|
||||
import { render } from "solid-js/web";
|
||||
import { isView, type View } from "../lib/model";
|
||||
import { createFrameBridge } from "../primitives/create-frame-bridge";
|
||||
import "../styles/app.css";
|
||||
import type { PageProps } from "./types";
|
||||
|
||||
// Each view is a dynamic import so a freshly-created iframe evaluates only the
|
||||
// page it owns. Destroying that iframe releases the chunk's runtime objects.
|
||||
const pages: Record<View, Component<PageProps>> = {
|
||||
overview: lazy(() => import("./overview")),
|
||||
services: lazy(() => import("./services")),
|
||||
instances: lazy(() => import("./instances")),
|
||||
"wit-packages": lazy(() => import("./wit-packages")),
|
||||
activity: lazy(() => import("./activity")),
|
||||
settings: lazy(() => import("./settings")),
|
||||
};
|
||||
|
||||
function FramePage() {
|
||||
const requested = new URLSearchParams(window.location.search).get("view");
|
||||
const view = isView(requested) ? requested : null;
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={view}
|
||||
fallback={
|
||||
<main class="flex min-h-dvh items-center justify-center p-6 text-center text-coral-strong">
|
||||
<div>
|
||||
<AlertTriangle class="mx-auto mb-3" size={28} />
|
||||
<strong class="block">未知页面</strong>
|
||||
<span class="mt-1 block text-sm text-muted">{requested ?? "(empty)"}</span>
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
>
|
||||
{(activeView) => {
|
||||
const bridge = createFrameBridge(activeView());
|
||||
const Page = pages[activeView()];
|
||||
document.title = `${activeView()} · Wasmeld`;
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<main class="flex min-h-dvh items-center justify-center text-muted">
|
||||
<LoaderCircle class="spin" size={24} />
|
||||
</main>
|
||||
}
|
||||
>
|
||||
<Page state={bridge.state} command={bridge.command} />
|
||||
</Suspense>
|
||||
);
|
||||
}}
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
const root = document.getElementById("app");
|
||||
if (!root) throw new Error("missing #app root");
|
||||
render(() => <FramePage />, root);
|
||||
Reference in New Issue
Block a user