import { ArrowRight, Clock3, CloudUpload, FileCode2, Package, Play, RadioTower, RefreshCw, ShieldCheck, SquareTerminal, X, } from "lucide-react"; import { FormEvent, ReactNode, useState } from "react"; import { InvokeResult, RegisterComponentInput } from "../api"; import { FormattedInvocationOutput, Service, formatInvocationOutput, invocationInputSize, parseInvocationInput, } from "../console-model"; function DialogFrame({ title, description, icon, onClose, closeDisabled = false, children, }: { title: string; description: string; icon: ReactNode; onClose: () => void; closeDisabled?: boolean; children: ReactNode; }) { return (
{ if (closeDisabled) event.preventDefault(); else onClose(); }} onKeyDown={(event) => { if (event.key === "Escape" && !closeDisabled) onClose(); }} >
{icon}

{title}

{description}

{children}
); } export function ActivateDeploymentDialog({ service, currentRevision, submitting, onClose, onConfirm, }: { service: Service; currentRevision: string | null; submitting: boolean; onClose: () => void; onConfirm: () => void; }) { return ( } onClose={onClose} closeDisabled={submitting} >
当前版本 {currentRevision ?? "尚未部署"}
目标版本 {service.revision}

Wasmeld 将先确认 {service.id}@{service.revision} 的 Actor 可用,再更新对外路由。

); } export function DeployDialog({ onClose, onRegister, }: { onClose: () => void; onRegister: (input: RegisterComponentInput) => Promise; }) { const [file, setFile] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const fileName = file?.name ?? ""; async function submit(event: FormEvent) { event.preventDefault(); if (!file) return; if (!file.name.toLowerCase().endsWith(".wasmpkg")) { setError("请选择 .wasmpkg 组件包"); return; } setSubmitting(true); setError(""); try { await onRegister({ file }); } catch (submitError) { setError(submitError instanceof Error ? submitError.message : "组件注册失败"); setSubmitting(false); } } return ( } onClose={onClose} >
{error &&
{error}
}
); } export function WitPackageDialog({ onClose, onPublish, }: { onClose: () => void; onPublish: (file: File) => Promise; }) { const [file, setFile] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const fileName = file?.name ?? ""; async function submit(event: FormEvent) { event.preventDefault(); if (!file) return; if (!file.name.toLowerCase().endsWith(".wasm")) { setError("请选择由 wasmeld wit build 生成的 .wasm 文件"); return; } if (file.size > 4 * 1024 * 1024) { setError("WIT 包不能超过 4 MB"); return; } setSubmitting(true); setError(""); try { await onPublish(file); } catch (submitError) { setError(submitError instanceof Error ? submitError.message : "WIT 包发布失败"); setSubmitting(false); } } return ( } onClose={onClose} >
{error &&
{error}
}
); } export function InvokeDialog({ service, onClose, onInvoke, }: { service: Service; onClose: () => void; onInvoke: (service: Service, input: Uint8Array) => Promise; }) { const [input, setInput] = useState(service.id === "echo" ? "hello wasm" : ""); const [format, setFormat] = useState<"utf8" | "hex">("utf8"); const [output, setOutput] = useState(""); const [outputBytes, setOutputBytes] = useState(null); const [outputFormat, setOutputFormat] = useState(null); const [running, setRunning] = useState(false); const [failed, setFailed] = useState(false); const [latency, setLatency] = useState(null); async function invoke() { setRunning(true); setOutput(""); setOutputBytes(null); setOutputFormat(null); setFailed(false); setLatency(null); try { const result = await onInvoke(service, parseInvocationInput(input, format)); const formatted = formatInvocationOutput(service, result.output, format); setOutput(formatted.text); setOutputBytes(result.outputBytes); setOutputFormat(formatted); setLatency(result.latencyMs); } catch (invokeError) { setFailed(true); setOutput(invokeError instanceof Error ? invokeError.message : "组件调用失败"); } finally { setRunning(false); } } return ( } onClose={onClose} >
{invocationInputSize(input, format)} B