import {
type AppHostProps,
type AppSurfaceProps,
type ToolRenderProps,
AppHost,
ToolRegistry,
defineApp,
defineTool,
field,
useCollabState,
useHostCapability,
useWorldQuery,
} from "@vvd/sdk"
function LobbyScreen({ context }: ToolRenderProps) {
const canEdit = context.canEdit
const docs = useWorldQuery("documents")
const refs = useHostCapability("refs")
const nav = useHostCapability("nav")
const { data, actions } = useCollabState({
headline: field.value<string>("Welcome — make this place yours."),
pinnedIds: field.list<string>(),
})
const pinned = data?.pinnedIds ?? []
const togglePin = (id: string) => {
if (!actions || !canEdit) return
const i = pinned.indexOf(id)
if (i >= 0) actions.list("pinnedIds").remove(i)
else actions.list("pinnedIds").push(id)
}
const open = (id: string) => {
const coords = refs.coordsFor(id)
if (coords) nav.openDocument(coords)
}
return (
<div style={{ padding: 24, font: "inherit", color: "inherit" }}>
<h1 style={{ margin: 0, fontSize: 22 }}>Lobby</h1>
{canEdit ? (
<input
value={data?.headline ?? ""}
onChange={(e) => actions?.set("headline", e.target.value)}
style={{ marginTop: 10, width: "100%", border: "none", outline: "none", background: "transparent", color: "inherit", font: "inherit", fontSize: 15, fontStyle: "italic", opacity: 0.85 }}
/>
) : (
<p style={{ margin: "10px 0 0", fontSize: 15, fontStyle: "italic", opacity: 0.85 }}>{data?.headline}</p>
)}
<div style={{ marginTop: 20, display: "grid", gap: 4 }}>
{docs.map((d) => (
<span key={d.id} style={{ display: "flex", alignItems: "center", gap: 8 }}>
<button type="button" onClick={() => open(d.id)} style={{ flex: 1, textAlign: "left", padding: "6px 8px", borderRadius: 8, border: "none", background: "transparent", color: "inherit", font: "inherit", fontSize: 13.5, cursor: "pointer" }}>
{d.name}
</button>
{canEdit && (
<button type="button" onClick={() => togglePin(d.id)} style={{ border: "none", background: "transparent", color: "inherit", cursor: "pointer", opacity: pinned.includes(d.id) ? 0.9 : 0.3 }}>
★
</button>
)}
</span>
))}
</div>
</div>
)
}
const lobbyScreen = defineTool({
id: "lobby-screen",
name: "Lobby",
documentTypes: ["lobby-screen"],
needs: ["world"],
surface: "plain",
render: LobbyScreen,
})
const lobbyApp = defineApp({
id: "lobby",
name: "Lobby",
route: "lobby",
Host: function LobbyHost({ children }: AppHostProps) {
return <>{children}</>
},
Surface: function LobbySurface({ children }: AppSurfaceProps) {
return <div style={{ height: "100%" }}>{children}</div>
},
tools: new ToolRegistry().register(lobbyScreen),
})
export default defineTool({
id: "app-demo",
name: "App demo",
documentTypes: ["app-demo"],
surface: "plain",
render: function AppDemo({ document, context }: ToolRenderProps) {
return <AppHost app={lobbyApp} document={{ ...document, type: "lobby-screen" }} context={context} />
},
})