From 4b71db3a344c752cea18272f5113793f144018df Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sun, 12 Jul 2026 08:30:23 +0530 Subject: [PATCH] refactor(gui): Provider Instances Configurator into its own component --- apps/gui/next-env.d.ts | 2 +- apps/gui/package.json | 2 + apps/gui/src/app/config/page.tsx | 354 +------------ apps/gui/src/app/layout.tsx | 39 +- .../config/ProviderInstancesConfig.tsx | 476 ++++++++++++++++++ apps/gui/src/components/nav/NavBar.tsx | 35 -- apps/gui/src/components/play/PlayView.tsx | 321 +----------- apps/gui/src/components/play/PromptModal.tsx | 324 ++++++++++++ apps/gui/src/components/ui/empty.tsx | 104 ++++ apps/gui/src/components/ui/item.tsx | 201 ++++++++ apps/gui/src/components/ui/select.tsx | 119 +++-- apps/gui/src/components/ui/separator.tsx | 25 +- pnpm-lock.yaml | 76 +++ 13 files changed, 1306 insertions(+), 772 deletions(-) create mode 100644 apps/gui/src/components/config/ProviderInstancesConfig.tsx delete mode 100644 apps/gui/src/components/nav/NavBar.tsx create mode 100644 apps/gui/src/components/play/PromptModal.tsx create mode 100644 apps/gui/src/components/ui/empty.tsx create mode 100644 apps/gui/src/components/ui/item.tsx diff --git a/apps/gui/next-env.d.ts b/apps/gui/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/gui/next-env.d.ts +++ b/apps/gui/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/gui/package.json b/apps/gui/package.json index 013a8fa..010fb0f 100644 --- a/apps/gui/package.json +++ b/apps/gui/package.json @@ -10,6 +10,7 @@ "lint": "next lint" }, "dependencies": { + "@base-ui/react": "^1.6.0", "@omnia/actor": "workspace:*", "@omnia/architect": "workspace:*", "@omnia/core": "workspace:*", @@ -18,6 +19,7 @@ "@omnia/memory": "workspace:*", "@omnia/scenario": "workspace:*", "@omnia/spatial": "workspace:*", + "@radix-ui/react-separator": "^1.1.11", "@radix-ui/react-slot": "^1.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/apps/gui/src/app/config/page.tsx b/apps/gui/src/app/config/page.tsx index 9dd6ebf..8c17d94 100644 --- a/apps/gui/src/app/config/page.tsx +++ b/apps/gui/src/app/config/page.tsx @@ -4,28 +4,13 @@ import { useEffect, useState, useCallback } from "react"; import { getConfigStatus, listProviderInstances, - createProviderInstance, - deleteProviderInstance, - setActiveProviderInstance, getProviderMappings, setProviderMapping, - updateProviderInstance, getAvailableProviders, regenerateEmbeddings, } from "@/app/play/actions"; import type { ModelProviderInstance, ModelProviderMeta } from "@omnia/llm"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Badge } from "@/components/ui/badge"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { ProviderInstancesConfig } from "@/components/config/ProviderInstancesConfig"; import { Table, TableBody, @@ -50,61 +35,6 @@ export default function ConfigPage() { const [loading, setLoading] = useState(true); const [error, setError] = useState(""); - const [selectedInstanceId, setSelectedInstanceId] = useState(null); - const [editName, setEditName] = useState(""); - const [editProvider, setEditProvider] = useState("google-genai"); - const [editKey, setEditKey] = useState(""); - const [editModel, setEditModel] = useState("gemini-2.5-flash"); - const [editIsActive, setEditIsActive] = useState(false); - const [editType, setEditType] = useState<"generative" | "embedding">("generative"); - const [editMaxContext, setEditMaxContext] = useState(32768); - - useEffect(() => { - if (selectedInstanceId === null) { - setEditName(""); - setEditProvider("google-genai"); - setEditKey(""); - setEditModel("gemini-2.5-flash"); - setEditIsActive(false); - setEditType("generative"); - setEditMaxContext(32768); - } else if (selectedInstanceId === "new") { - setEditName(""); - const defaultProvider = "google-genai"; - setEditProvider(defaultProvider); - setEditKey(""); - setEditType("generative"); - const pMeta = availableProviders.find((p) => p.id === defaultProvider); - setEditModel(pMeta?.defaultModel || "gemini-2.5-flash"); - setEditIsActive(false); - setEditMaxContext(32768); - } else { - const inst = instances.find((i) => i.id === selectedInstanceId); - if (inst) { - setEditName(inst.name); - setEditProvider(inst.providerName); - setEditKey(""); - setEditType(inst.type || "generative"); - const pMeta = availableProviders.find((p) => p.id === inst.providerName); - setEditModel(inst.modelName || (inst.type === "embedding" ? pMeta?.defaultEmbeddingModel : pMeta?.defaultModel) || "gemini-2.5-flash"); - setEditIsActive(inst.isActive); - setEditMaxContext(inst.maxContext !== undefined && inst.maxContext !== null ? inst.maxContext : 32768); - } - } - }, [selectedInstanceId, instances, availableProviders]); - - const handleProviderChange = (providerId: string) => { - setEditProvider(providerId); - const pMeta = availableProviders.find((p) => p.id === providerId); - setEditModel(editType === "embedding" ? pMeta?.defaultEmbeddingModel || "" : pMeta?.defaultModel || ""); - }; - - const handleTypeChange = (type: "generative" | "embedding") => { - setEditType(type); - const pMeta = availableProviders.find((p) => p.id === editProvider); - setEditModel(type === "embedding" ? pMeta?.defaultEmbeddingModel || "" : pMeta?.defaultModel || ""); - }; - const loadInstances = useCallback(async () => { const list = await listProviderInstances(); setInstances(list); @@ -136,90 +66,6 @@ export default function ConfigPage() { loadAll(); }, [loadAll]); - const handleSave = async (e: React.FormEvent) => { - e.preventDefault(); - if (!editName.trim()) { - setError("Name is required."); - return; - } - - try { - setLoading(true); - setError(""); - - let shouldRegenerate = false; - let targetInstanceId = selectedInstanceId; - - if (selectedInstanceId === "new") { - if (!editKey.trim()) { - setError("API Key is required for new instances."); - setLoading(false); - return; - } - const created = await createProviderInstance(editName, editProvider, editKey, editModel || undefined, editType, editType === "generative" ? editMaxContext : 0); - if (editIsActive) { - await setActiveProviderInstance(created.id); - } - targetInstanceId = created.id; - setSelectedInstanceId(created.id); - } else { - if (!selectedInstanceId) return; - const inst = instances.find((i) => i.id === selectedInstanceId); - if (inst && inst.type === "embedding") { - const isMapped = mappings["embeddings"] === selectedInstanceId; - const isActive = inst.isActive && !mappings["embeddings"]; - if (isMapped || isActive) { - const hasChanged = inst.providerName !== editProvider || inst.modelName !== editModel; - if (hasChanged) { - const confirmChange = window.confirm( - "You have changed the configuration of the active embedding provider. This will delete all existing embeddings and regenerate them from scratch. Are you sure you want to do this?" - ); - if (!confirmChange) { - setLoading(false); - return; - } - shouldRegenerate = true; - } - } - } - - await updateProviderInstance(selectedInstanceId, editName, editProvider, editKey || undefined, editModel || undefined, editType, editType === "generative" ? editMaxContext : 0); - if (editIsActive) { - await setActiveProviderInstance(selectedInstanceId); - } - } - - await loadInstances(); - await loadMappings(); - - if (shouldRegenerate && targetInstanceId && targetInstanceId !== "new") { - await regenerateEmbeddings(targetInstanceId); - } - } catch (err) { - setError(err instanceof Error ? err.message : String(err)); - } finally { - setLoading(false); - } - }; - - const handleDelete = async () => { - if (selectedInstanceId === "new" || selectedInstanceId === null) return; - if (!confirm("Are you sure you want to delete this provider instance?")) return; - - try { - setLoading(true); - setError(""); - await deleteProviderInstance(selectedInstanceId); - setSelectedInstanceId(null); - await loadInstances(); - await loadMappings(); - } catch (err) { - setError(err instanceof Error ? err.message : String(err)); - } finally { - setLoading(false); - } - }; - const handleUpdateMapping = async (task: string, providerInstanceId: string) => { if (task === "embeddings" && mappings[task] !== providerInstanceId) { const confirmChange = window.confirm( @@ -255,195 +101,15 @@ export default function ConfigPage() { {config && (
-
-

LLM Provider Instances

-
- {/* 30% area */} -
-
-

- Instances -

- -
-
- {instances.length === 0 ? ( -
- No instances configured -
- ) : ( - instances.map((inst) => ( -
setSelectedInstanceId(inst.id)} - className={`cursor-pointer border-b border-gray-200 border-l-[3px] px-4 py-4 transition-all hover:bg-gray-100 ${ - selectedInstanceId === inst.id - ? "border-l-blue-500 bg-blue-50" - : "border-l-transparent" - }`} - > -
- {inst.name} -
-
- {inst.providerName} ({inst.type || "generative"}) - {inst.isActive && ( - - Active - - )} -
-
- )) - )} -
-
- - {/* 70% area */} -
- {selectedInstanceId === null ? ( -
- Press + to add or select an existing Instance to edit -
- ) : ( -
-
-

- {selectedInstanceId === "new" - ? "Create New Provider Instance" - : `Configure: ${editName}`} -

- -
- - setEditName(e.target.value)} - placeholder="e.g. Gemini - Production" - required - /> -
- -
- - -
- -
- - - {editProvider && availableProviders.length > 0 && ( - - {availableProviders.find((p) => p.id === editProvider)?.description} - - )} -
- -
- - setEditKey(e.target.value)} - placeholder={ - selectedInstanceId === "new" - ? "AIzaSy..." - : "•••••••• (unchanged)" - } - required={selectedInstanceId === "new"} - /> -
- -
- - setEditModel(e.target.value)} - placeholder="e.g. gemini-2.5-flash, gemini-2.5-pro" - /> -
- - {editType === "generative" && ( -
- - setEditMaxContext(parseInt(e.target.value) || 0)} - min={0} - placeholder="e.g. 32768" - /> -
- )} - -
- setEditIsActive(v === true)} - /> - -
-
- -
-
- {selectedInstanceId !== "new" && ( - - )} -
-
- -
-
-
- )} -
-
-
+ { + await loadInstances(); + await loadMappings(); + }} + />

Task Provider Routing

diff --git a/apps/gui/src/app/layout.tsx b/apps/gui/src/app/layout.tsx index a248d01..c35fbd3 100644 --- a/apps/gui/src/app/layout.tsx +++ b/apps/gui/src/app/layout.tsx @@ -1,7 +1,16 @@ +"use client"; + import type { ReactNode } from "react"; import { Archivo_Black, Space_Grotesk } from "next/font/google"; -import { NavBar } from "@/components/nav/NavBar"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; import "./globals.css"; +import { + NavigationMenu, + NavigationMenuList, + NavigationMenuItem, + NavigationMenuLink, +} from "@/components/ui/navigation-menu"; const archivoBlack = Archivo_Black({ subsets: ["latin"], @@ -16,16 +25,34 @@ const spaceGrotesk = Space_Grotesk({ display: "swap", }); -export const metadata = { - title: "Omnia", - description: "Omnia Narrative Simulation Engine", -}; +const links = [ + { href: "/", label: "Home" }, + { href: "/play", label: "Play" }, + { href: "/config", label: "Config" }, +]; export default function RootLayout({ children }: { children: ReactNode }) { + const pathname = usePathname(); + return ( - + {children} diff --git a/apps/gui/src/components/config/ProviderInstancesConfig.tsx b/apps/gui/src/components/config/ProviderInstancesConfig.tsx new file mode 100644 index 0000000..6b65810 --- /dev/null +++ b/apps/gui/src/components/config/ProviderInstancesConfig.tsx @@ -0,0 +1,476 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { + createProviderInstance, + updateProviderInstance, + setActiveProviderInstance, + regenerateEmbeddings, + deleteProviderInstance, +} from "@/app/play/actions"; +import type { ModelProviderInstance, ModelProviderMeta } from "@omnia/llm"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Badge } from "@/components/ui/badge"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Card, + CardHeader, + CardContent, + CardFooter, + CardTitle, + CardAction, +} from "@/components/ui/card"; +import { + Item, + ItemContent, + ItemGroup, + ItemTitle, + ItemDescription, +} from "@/components/ui/item"; +import { Empty, EmptyTitle, EmptyDescription } from "@/components/ui/empty"; +import { cn } from "@/lib/utils"; + +interface ProviderInstancesConfigProps { + instances: ModelProviderInstance[]; + availableProviders: ModelProviderMeta[]; + mappings: Record; + onChanged: () => Promise; +} + +export function ProviderInstancesConfig({ + instances, + availableProviders, + mappings, + onChanged, +}: ProviderInstancesConfigProps) { + const [selectedInstanceId, setSelectedInstanceId] = useState( + null, + ); + const [editName, setEditName] = useState(""); + const [editProvider, setEditProvider] = useState("google-genai"); + const [editKey, setEditKey] = useState(""); + const [editModel, setEditModel] = useState("gemini-2.5-flash"); + const [editIsActive, setEditIsActive] = useState(false); + const [editType, setEditType] = useState<"generative" | "embedding">( + "generative", + ); + const [editMaxContext, setEditMaxContext] = useState(32768); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + useEffect(() => { + if (selectedInstanceId === null) { + setEditName(""); + setEditProvider("google-genai"); + setEditKey(""); + setEditModel("gemini-2.5-flash"); + setEditIsActive(false); + setEditType("generative"); + setEditMaxContext(32768); + } else if (selectedInstanceId === "new") { + setEditName(""); + const defaultProvider = "google-genai"; + setEditProvider(defaultProvider); + setEditKey(""); + setEditType("generative"); + const pMeta = availableProviders.find((p) => p.id === defaultProvider); + setEditModel(pMeta?.defaultModel || "gemini-2.5-flash"); + setEditIsActive(false); + setEditMaxContext(32768); + } else { + const inst = instances.find((i) => i.id === selectedInstanceId); + if (inst) { + setEditName(inst.name); + setEditProvider(inst.providerName); + setEditKey(""); + setEditType(inst.type || "generative"); + const pMeta = availableProviders.find( + (p) => p.id === inst.providerName, + ); + setEditModel( + inst.modelName || + (inst.type === "embedding" + ? pMeta?.defaultEmbeddingModel + : pMeta?.defaultModel) || + "gemini-2.5-flash", + ); + setEditIsActive(inst.isActive); + setEditMaxContext( + inst.maxContext !== undefined && inst.maxContext !== null + ? inst.maxContext + : 32768, + ); + } + } + }, [selectedInstanceId, instances, availableProviders]); + + const handleProviderChange = (providerId: string | null) => { + if (!providerId) return; + setEditProvider(providerId); + const pMeta = availableProviders.find((p) => p.id === providerId); + setEditModel( + editType === "embedding" + ? pMeta?.defaultEmbeddingModel || "" + : pMeta?.defaultModel || "", + ); + }; + + const handleTypeChange = (type: "generative" | "embedding") => { + setEditType(type); + const pMeta = availableProviders.find((p) => p.id === editProvider); + setEditModel( + type === "embedding" + ? pMeta?.defaultEmbeddingModel || "" + : pMeta?.defaultModel || "", + ); + }; + + const handleSave = async (e: React.FormEvent) => { + e.preventDefault(); + if (!editName.trim()) { + setError("Name is required."); + return; + } + + try { + setLoading(true); + setError(""); + + let shouldRegenerate = false; + let targetInstanceId = selectedInstanceId; + + if (selectedInstanceId === "new") { + if (!editKey.trim()) { + setError("API Key is required for new instances."); + setLoading(false); + return; + } + const created = await createProviderInstance( + editName, + editProvider, + editKey, + editModel || undefined, + editType, + editType === "generative" ? editMaxContext : 0, + ); + if (editIsActive) { + await setActiveProviderInstance(created.id); + } + targetInstanceId = created.id; + setSelectedInstanceId(created.id); + } else { + if (!selectedInstanceId) return; + const inst = instances.find((i) => i.id === selectedInstanceId); + if (inst && inst.type === "embedding") { + const isMapped = mappings["embeddings"] === selectedInstanceId; + const isActive = inst.isActive && !mappings["embeddings"]; + if (isMapped || isActive) { + const hasChanged = + inst.providerName !== editProvider || + inst.modelName !== editModel; + if (hasChanged) { + const confirmChange = window.confirm( + "You have changed the configuration of the active embedding provider. This will delete all existing embeddings and regenerate them from scratch. Are you sure you want to do this?", + ); + if (!confirmChange) { + setLoading(false); + return; + } + shouldRegenerate = true; + } + } + } + + await updateProviderInstance( + selectedInstanceId, + editName, + editProvider, + editKey || undefined, + editModel || undefined, + editType, + editType === "generative" ? editMaxContext : 0, + ); + if (editIsActive) { + await setActiveProviderInstance(selectedInstanceId); + } + } + + await onChanged(); + + if (shouldRegenerate && targetInstanceId && targetInstanceId !== "new") { + await regenerateEmbeddings(targetInstanceId); + } + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setLoading(false); + } + }; + + const handleDelete = async () => { + if (selectedInstanceId === "new" || selectedInstanceId === null) return; + if (!confirm("Are you sure you want to delete this provider instance?")) + return; + + try { + setLoading(true); + setError(""); + await deleteProviderInstance(selectedInstanceId); + setSelectedInstanceId(null); + await onChanged(); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setLoading(false); + } + }; + + return ( +
+

LLM Provider Instances

+ {error && ( +
+ {error} +
+ )} +
+ {/* Left panel - Instance list */} + + + Instances + + + + + + {instances.length === 0 ? ( + + No instances configured + + ) : ( + + {instances.map((inst) => ( + setSelectedInstanceId(inst.id)} + > + + {inst.name} + + {inst.providerName} ({inst.type || "generative"}) + + + {inst.isActive && Active} + + ))} + + )} + + + + {/* Right panel - Form */} + + {selectedInstanceId === null ? ( + + + No instance selected + + Press + to add or select an existing Instance to edit + + + + ) : ( +
+ + + {selectedInstanceId === "new" + ? "Create New Provider Instance" + : `Configure: ${editName}`} + + + +
+ + setEditName(e.target.value)} + placeholder="e.g. Gemini - Production" + required + /> +
+ +
+
+ + +
+ +
+ + +
+
+ {editProvider && availableProviders.length > 0 && ( + + { + availableProviders.find((p) => p.id === editProvider) + ?.description + } + + )} + +
+ + setEditKey(e.target.value)} + placeholder={ + selectedInstanceId === "new" + ? "AIzaSy..." + : "•••••••• (unchanged)" + } + required={selectedInstanceId === "new"} + /> +
+ +
+ + setEditModel(e.target.value)} + placeholder="e.g. gemini-2.5-flash, gemini-2.5-pro" + /> +
+ + {editType === "generative" && ( +
+ + + setEditMaxContext(parseInt(e.target.value) || 0) + } + min={0} + placeholder="e.g. 32768" + /> +
+ )} + +
+ setEditIsActive(v === true)} + /> + +
+
+ + +
+ {selectedInstanceId !== "new" && ( + + )} +
+ +
+
+ )} +
+
+
+ ); +} diff --git a/apps/gui/src/components/nav/NavBar.tsx b/apps/gui/src/components/nav/NavBar.tsx deleted file mode 100644 index 7e31660..0000000 --- a/apps/gui/src/components/nav/NavBar.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import { Button } from "@/components/ui/button"; - -const links = [ - { href: "/", label: "Home" }, - { href: "/play", label: "Play" }, - { href: "/config", label: "Config" }, -]; - -export function NavBar() { - const pathname = usePathname(); - - return ( - - ); -} diff --git a/apps/gui/src/components/play/PlayView.tsx b/apps/gui/src/components/play/PlayView.tsx index 3105f2a..5f1caea 100644 --- a/apps/gui/src/components/play/PlayView.tsx +++ b/apps/gui/src/components/play/PlayView.tsx @@ -17,19 +17,7 @@ import type { ModelProviderInstance } from "@omnia/llm"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Spinner } from "@/components/ui/spinner"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { - Accordion, - AccordionItem, - AccordionTrigger, - AccordionContent, -} from "@/components/ui/accordion"; +import { PromptModal } from "./PromptModal"; function IntentTag({ intent, @@ -69,313 +57,6 @@ function IntentTag({ ); } -function PromptModal({ - entry, - onClose, -}: { - entry: SimSnapshot["log"][number]; - onClose: () => void; -}) { - const [activeTab, setActiveTab] = useState<"actor" | "decoder">("actor"); - - const parseActorPrompt = (systemPrompt: string, userContext: string, inputTokens: number) => { - const memoryHeader = "=== YOUR RECENT MEMORY ==="; - const idx = userContext.indexOf(memoryHeader); - - let worldStr = userContext; - let memStr = ""; - - if (idx !== -1) { - worldStr = userContext.substring(0, idx).trim(); - memStr = userContext.substring(idx).trim(); - } - - const sysLen = systemPrompt.length; - const worldLen = worldStr.length; - const memLen = memStr.length; - const totalLen = sysLen + worldLen + memLen; - - if (totalLen === 0) return null; - - const sysPct = (sysLen / totalLen) * 100; - const worldPct = (worldLen / totalLen) * 100; - const memPct = (memLen / totalLen) * 100; - - const sysTokens = Math.round((sysLen / totalLen) * inputTokens); - const worldTokens = Math.round((worldLen / totalLen) * inputTokens); - const memTokens = Math.max(0, inputTokens - sysTokens - worldTokens); - - return [ - { label: "System Prompt", pct: sysPct, relativePct: sysPct, tokens: sysTokens, type: "system", content: systemPrompt }, - { label: "World Info", pct: worldPct, relativePct: worldPct, tokens: worldTokens, type: "world", content: worldStr }, - { label: "Recent Memories", pct: memPct, relativePct: memPct, tokens: memTokens, type: "memories", content: memStr || "(No memories yet.)" }, - ]; - }; - - const parseDecoderPrompt = (systemPrompt: string, userContext: string, inputTokens: number) => { - const proseHeader = "=== NARRATIVE PROSE ==="; - const idx = userContext.indexOf(proseHeader); - - let worldStr = userContext; - let proseStr = ""; - - if (idx !== -1) { - worldStr = userContext.substring(0, idx).trim(); - proseStr = userContext.substring(idx).trim(); - } - - const sysLen = systemPrompt.length; - const worldLen = worldStr.length; - const proseLen = proseStr.length; - const totalLen = sysLen + worldLen + proseLen; - - if (totalLen === 0) return null; - - const sysPct = (sysLen / totalLen) * 100; - const worldPct = (worldLen / totalLen) * 100; - const prosePct = (proseLen / totalLen) * 100; - - const sysTokens = Math.round((sysLen / totalLen) * inputTokens); - const worldTokens = Math.round((worldLen / totalLen) * inputTokens); - const proseTokens = Math.max(0, inputTokens - sysTokens - worldTokens); - - return [ - { label: "System Prompt", pct: sysPct, relativePct: sysPct, tokens: sysTokens, type: "system", content: systemPrompt }, - { label: "Decoder Context", pct: worldPct, relativePct: worldPct, tokens: worldTokens, type: "world", content: worldStr }, - { label: "Narrative Prose", pct: prosePct, relativePct: prosePct, tokens: proseTokens, type: "memories", content: proseStr }, - ]; - }; - - const actorBreakdown = (entry.rawPrompt && entry.usage) ? parseActorPrompt(entry.rawPrompt.systemPrompt, entry.rawPrompt.userContext, entry.usage.inputTokens) : null; - const decoderBreakdown = (entry.decoderPrompt && entry.decoderUsage) ? parseDecoderPrompt(entry.decoderPrompt.systemPrompt, entry.decoderPrompt.userContext, entry.decoderUsage.inputTokens) : null; - - const actorMaxContext = entry.usage?.maxContext !== undefined ? entry.usage.maxContext : 32768; - const actorUsedTokens = entry.usage?.inputTokens || 0; - const actorUsagePctOfContext = actorMaxContext > 0 ? (actorUsedTokens / actorMaxContext) * 100 : 0; - const isActorAbsolute = actorMaxContext > 0 && actorUsagePctOfContext >= 20; - - const scaledActorBreakdown = actorBreakdown ? actorBreakdown.map((item) => ({ - ...item, - pct: isActorAbsolute ? item.relativePct * (actorUsedTokens / actorMaxContext) : item.relativePct - })) : null; - - const decoderMaxContext = entry.decoderUsage?.maxContext !== undefined ? entry.decoderUsage.maxContext : 32768; - const decoderUsedTokens = entry.decoderUsage?.inputTokens || 0; - const decoderUsagePctOfContext = decoderMaxContext > 0 ? (decoderUsedTokens / decoderMaxContext) * 100 : 0; - const isDecoderAbsolute = decoderMaxContext > 0 && decoderUsagePctOfContext >= 20; - - const scaledDecoderBreakdown = decoderBreakdown ? decoderBreakdown.map((item) => ({ - ...item, - pct: isDecoderAbsolute ? item.relativePct * (decoderUsedTokens / decoderMaxContext) : item.relativePct - })) : null; - - useEffect(() => { - if (!entry.rawPrompt && entry.decoderPrompt) { - setActiveTab("decoder"); - } - }, [entry]); - - return ( - !open && onClose()}> - - - Raw Prompts & Token Usage ({entry.entityName}) - - - setActiveTab(v as "actor" | "decoder")}> - - - Actor Prompt {entry.usage ? "📊" : ""} - - - Intent Decoder {entry.decoderUsage ? "📊" : ""} - - - -
- - {entry.rawPrompt && ( -
- {entry.usage ? ( -
- LLM Instance: {entry.usage.providerInstanceName || "Default"} - {entry.usage.modelName && ( - ({entry.usage.modelName}) - )} -
- ) : ( -
- No LLM token usage (Player turn used fixed prose). -
- )} - - {scaledActorBreakdown && ( -
-
- Input Prompt Breakdown - - Total Input Tokens: {actorUsedTokens} - {actorMaxContext > 0 ? ( - / {actorMaxContext} ({actorUsagePctOfContext.toFixed(1)}% used) - ) : ( - (infinite context) - )} - -
-
- {scaledActorBreakdown.map((item, idx) => { - const displayPct = actorMaxContext > 0 ? (item.tokens / actorMaxContext) * 100 : item.relativePct; - return ( -
- ); - })} - {isActorAbsolute && ( -
- )} -
- - {scaledActorBreakdown.map((item, idx) => { - const displayPct = actorMaxContext > 0 ? (item.tokens / actorMaxContext) * 100 : item.relativePct; - return ( - - - - {item.label}: {item.tokens} tokens ({displayPct.toFixed(0)}%) - - -
-                                  {item.content}
-                                
-
-
- ); - })} -
-
- )} - - {entry.usage && ( -
-
- LLM Output - Total Output Tokens: {entry.usage.outputTokens} -
-
-
-                          {entry.narrativeProse}
-                        
-
-
- )} -
- )} - - - - {entry.decoderPrompt && ( -
- {entry.decoderUsage && ( -
- LLM Instance: {entry.decoderUsage.providerInstanceName || "Default"} - {entry.decoderUsage.modelName && ( - ({entry.decoderUsage.modelName}) - )} -
- )} - - {scaledDecoderBreakdown && ( -
-
- Input Prompt Breakdown - - Total Input Tokens: {decoderUsedTokens} - {decoderMaxContext > 0 ? ( - / {decoderMaxContext} ({decoderUsagePctOfContext.toFixed(1)}% used) - ) : ( - (infinite context) - )} - -
-
- {scaledDecoderBreakdown.map((item, idx) => { - const displayPct = decoderMaxContext > 0 ? (item.tokens / decoderMaxContext) * 100 : item.relativePct; - return ( -
- ); - })} - {isDecoderAbsolute && ( -
- )} -
- - {scaledDecoderBreakdown.map((item, idx) => { - const displayPct = decoderMaxContext > 0 ? (item.tokens / decoderMaxContext) * 100 : item.relativePct; - return ( - - - - {item.label}: {item.tokens} tokens ({displayPct.toFixed(0)}%) - - -
-                                  {item.content}
-                                
-
-
- ); - })} -
-
- )} - - {entry.decoderUsage && ( -
-
- LLM Output - Total Output Tokens: {entry.decoderUsage.outputTokens} -
-
-
-                          {JSON.stringify(entry.intents, null, 2)}
-                        
-
-
- )} -
- )} - -
- - -
- ); -} function formatSimTime(isoString: string) { try { diff --git a/apps/gui/src/components/play/PromptModal.tsx b/apps/gui/src/components/play/PromptModal.tsx new file mode 100644 index 0000000..c7cd0c1 --- /dev/null +++ b/apps/gui/src/components/play/PromptModal.tsx @@ -0,0 +1,324 @@ +"use client"; + +import { useEffect, useState } from "react"; +import type { SimSnapshot } from "@/lib/simulation-types"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Accordion, + AccordionItem, + AccordionTrigger, + AccordionContent, +} from "@/components/ui/accordion"; + +interface PromptModalProps { + entry: SimSnapshot["log"][number]; + onClose: () => void; +} + +export function PromptModal({ entry, onClose }: PromptModalProps) { + const [activeTab, setActiveTab] = useState<"actor" | "decoder">("actor"); + + const parseActorPrompt = (systemPrompt: string, userContext: string, inputTokens: number) => { + const memoryHeader = "=== YOUR RECENT MEMORY ==="; + const idx = userContext.indexOf(memoryHeader); + + let worldStr = userContext; + let memStr = ""; + + if (idx !== -1) { + worldStr = userContext.substring(0, idx).trim(); + memStr = userContext.substring(idx).trim(); + } + + const sysLen = systemPrompt.length; + const worldLen = worldStr.length; + const memLen = memStr.length; + const totalLen = sysLen + worldLen + memLen; + + if (totalLen === 0) return null; + + const sysPct = (sysLen / totalLen) * 100; + const worldPct = (worldLen / totalLen) * 100; + const memPct = (memLen / totalLen) * 100; + + const sysTokens = Math.round((sysLen / totalLen) * inputTokens); + const worldTokens = Math.round((worldLen / totalLen) * inputTokens); + const memTokens = Math.max(0, inputTokens - sysTokens - worldTokens); + + return [ + { label: "System Prompt", pct: sysPct, relativePct: sysPct, tokens: sysTokens, type: "system", content: systemPrompt }, + { label: "World Info", pct: worldPct, relativePct: worldPct, tokens: worldTokens, type: "world", content: worldStr }, + { label: "Recent Memories", pct: memPct, relativePct: memPct, tokens: memTokens, type: "memories", content: memStr || "(No memories yet.)" }, + ]; + }; + + const parseDecoderPrompt = (systemPrompt: string, userContext: string, inputTokens: number) => { + const proseHeader = "=== NARRATIVE PROSE ==="; + const idx = userContext.indexOf(proseHeader); + + let worldStr = userContext; + let proseStr = ""; + + if (idx !== -1) { + worldStr = userContext.substring(0, idx).trim(); + proseStr = userContext.substring(idx).trim(); + } + + const sysLen = systemPrompt.length; + const worldLen = worldStr.length; + const proseLen = proseStr.length; + const totalLen = sysLen + worldLen + proseLen; + + if (totalLen === 0) return null; + + const sysPct = (sysLen / totalLen) * 100; + const worldPct = (worldLen / totalLen) * 100; + const prosePct = (proseLen / totalLen) * 100; + + const sysTokens = Math.round((sysLen / totalLen) * inputTokens); + const worldTokens = Math.round((worldLen / totalLen) * inputTokens); + const proseTokens = Math.max(0, inputTokens - sysTokens - worldTokens); + + return [ + { label: "System Prompt", pct: sysPct, relativePct: sysPct, tokens: sysTokens, type: "system", content: systemPrompt }, + { label: "Decoder Context", pct: worldPct, relativePct: worldPct, tokens: worldTokens, type: "world", content: worldStr }, + { label: "Narrative Prose", pct: prosePct, relativePct: prosePct, tokens: proseTokens, type: "memories", content: proseStr }, + ]; + }; + + const actorBreakdown = (entry.rawPrompt && entry.usage) ? parseActorPrompt(entry.rawPrompt.systemPrompt, entry.rawPrompt.userContext, entry.usage.inputTokens) : null; + const decoderBreakdown = (entry.decoderPrompt && entry.decoderUsage) ? parseDecoderPrompt(entry.decoderPrompt.systemPrompt, entry.decoderPrompt.userContext, entry.decoderUsage.inputTokens) : null; + + const actorMaxContext = entry.usage?.maxContext !== undefined ? entry.usage.maxContext : 32768; + const actorUsedTokens = entry.usage?.inputTokens || 0; + const actorUsagePctOfContext = actorMaxContext > 0 ? (actorUsedTokens / actorMaxContext) * 100 : 0; + const isActorAbsolute = actorMaxContext > 0 && actorUsagePctOfContext >= 20; + + const scaledActorBreakdown = actorBreakdown ? actorBreakdown.map((item) => ({ + ...item, + pct: isActorAbsolute ? item.relativePct * (actorUsedTokens / actorMaxContext) : item.relativePct + })) : null; + + const decoderMaxContext = entry.decoderUsage?.maxContext !== undefined ? entry.decoderUsage.maxContext : 32768; + const decoderUsedTokens = entry.decoderUsage?.inputTokens || 0; + const decoderUsagePctOfContext = decoderMaxContext > 0 ? (decoderUsedTokens / decoderMaxContext) * 100 : 0; + const isDecoderAbsolute = decoderMaxContext > 0 && decoderUsagePctOfContext >= 20; + + const scaledDecoderBreakdown = decoderBreakdown ? decoderBreakdown.map((item) => ({ + ...item, + pct: isDecoderAbsolute ? item.relativePct * (decoderUsedTokens / decoderMaxContext) : item.relativePct + })) : null; + + useEffect(() => { + if (!entry.rawPrompt && entry.decoderPrompt) { + setActiveTab("decoder"); + } + }, [entry]); + + return ( + !open && onClose()}> + + + Raw Prompts & Token Usage ({entry.entityName}) + + + setActiveTab(v as "actor" | "decoder")}> + + + Actor Prompt {entry.usage ? "📊" : ""} + + + Intent Decoder {entry.decoderUsage ? "📊" : ""} + + + +
+ + {entry.rawPrompt && ( +
+ {entry.usage ? ( +
+ LLM Instance: {entry.usage.providerInstanceName || "Default"} + {entry.usage.modelName && ( + ({entry.usage.modelName}) + )} +
+ ) : ( +
+ No LLM token usage (Player turn used fixed prose). +
+ )} + + {scaledActorBreakdown && ( +
+
+ Input Prompt Breakdown + + Total Input Tokens: {actorUsedTokens} + {actorMaxContext > 0 ? ( + / {actorMaxContext} ({actorUsagePctOfContext.toFixed(1)}% used) + ) : ( + (infinite context) + )} + +
+
+ {scaledActorBreakdown.map((item, idx) => { + const displayPct = actorMaxContext > 0 ? (item.tokens / actorMaxContext) * 100 : item.relativePct; + return ( +
+ ); + })} + {isActorAbsolute && ( +
+ )} +
+ + {scaledActorBreakdown.map((item, idx) => { + const displayPct = actorMaxContext > 0 ? (item.tokens / actorMaxContext) * 100 : item.relativePct; + return ( + + + + {item.label}: {item.tokens} tokens ({displayPct.toFixed(0)}%) + + +
+                                  {item.content}
+                                
+
+
+ ); + })} +
+
+ )} + + {entry.usage && ( +
+
+ LLM Output + Total Output Tokens: {entry.usage.outputTokens} +
+
+
+                          {entry.narrativeProse}
+                        
+
+
+ )} +
+ )} + + + + {entry.decoderPrompt && ( +
+ {entry.decoderUsage && ( +
+ LLM Instance: {entry.decoderUsage.providerInstanceName || "Default"} + {entry.decoderUsage.modelName && ( + ({entry.decoderUsage.modelName}) + )} +
+ )} + + {scaledDecoderBreakdown && ( +
+
+ Input Prompt Breakdown + + Total Input Tokens: {decoderUsedTokens} + {decoderMaxContext > 0 ? ( + / {decoderMaxContext} ({decoderUsagePctOfContext.toFixed(1)}% used) + ) : ( + (infinite context) + )} + +
+
+ {scaledDecoderBreakdown.map((item, idx) => { + const displayPct = decoderMaxContext > 0 ? (item.tokens / decoderMaxContext) * 100 : item.relativePct; + return ( +
+ ); + })} + {isDecoderAbsolute && ( +
+ )} +
+ + {scaledDecoderBreakdown.map((item, idx) => { + const displayPct = decoderMaxContext > 0 ? (item.tokens / decoderMaxContext) * 100 : item.relativePct; + return ( + + + + {item.label}: {item.tokens} tokens ({displayPct.toFixed(0)}%) + + +
+                                  {item.content}
+                                
+
+
+ ); + })} +
+
+ )} + + {entry.decoderUsage && ( +
+
+ LLM Output + Total Output Tokens: {entry.decoderUsage.outputTokens} +
+
+
+                          {JSON.stringify(entry.intents, null, 2)}
+                        
+
+
+ )} +
+ )} + +
+ + +
+ ); +} diff --git a/apps/gui/src/components/ui/empty.tsx b/apps/gui/src/components/ui/empty.tsx new file mode 100644 index 0000000..e03048b --- /dev/null +++ b/apps/gui/src/components/ui/empty.tsx @@ -0,0 +1,104 @@ +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +function Empty({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +const emptyMediaVariants = cva( + "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-transparent", + icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-4", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function EmptyMedia({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +
a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", + className + )} + {...props} + /> + ) +} + +function EmptyContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Empty, + EmptyHeader, + EmptyTitle, + EmptyDescription, + EmptyContent, + EmptyMedia, +} diff --git a/apps/gui/src/components/ui/item.tsx b/apps/gui/src/components/ui/item.tsx new file mode 100644 index 0000000..444b4c9 --- /dev/null +++ b/apps/gui/src/components/ui/item.tsx @@ -0,0 +1,201 @@ +import * as React from "react" +import { mergeProps } from "@base-ui/react/merge-props" +import { useRender } from "@base-ui/react/use-render" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Separator } from "@/components/ui/separator" + +function ItemGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const itemVariants = cva( + "group/item flex w-full flex-wrap items-center rounded border-2 text-sm transition-colors duration-100 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary [a]:transition-colors [a]:hover:bg-accent", + { + variants: { + variant: { + default: "border-transparent", + outline: "border-border", + muted: "border-transparent bg-muted/50", + }, + size: { + default: "gap-2.5 px-3 py-2.5", + sm: "gap-2.5 px-3 py-2.5", + xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Item({ + className, + variant = "default", + size = "default", + render, + ...props +}: useRender.ComponentProps<"div"> & VariantProps) { + return useRender({ + defaultTagName: "div", + props: mergeProps<"div">( + { + className: cn(itemVariants({ variant, size, className })), + }, + props + ), + render, + state: { + slot: "item", + variant, + size, + }, + }) +} + +const itemMediaVariants = cva( + "flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none", + { + variants: { + variant: { + default: "bg-transparent", + icon: "[&_svg:not([class*='size-'])]:size-4", + image: + "size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function ItemMedia({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function ItemContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +

a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", + className + )} + {...props} + /> + ) +} + +function ItemActions({ className, ...props }: React.ComponentProps<"div">) { + return ( +

+ ) +} + +function ItemHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Item, + ItemMedia, + ItemContent, + ItemActions, + ItemGroup, + ItemSeparator, + ItemTitle, + ItemDescription, + ItemHeader, + ItemFooter, +} diff --git a/apps/gui/src/components/ui/select.tsx b/apps/gui/src/components/ui/select.tsx index 046dc85..8ca2c77 100644 --- a/apps/gui/src/components/ui/select.tsx +++ b/apps/gui/src/components/ui/select.tsx @@ -1,21 +1,14 @@ "use client" import * as React from "react" +import { Select as SelectPrimitive } from "@base-ui/react/select" import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" -import { Select as SelectPrimitive } from "radix-ui" import { cn } from "@/lib/utils" -function Select({ - ...props -}: React.ComponentProps) { - return -} +const Select = SelectPrimitive.Root -function SelectGroup({ - className, - ...props -}: React.ComponentProps) { +function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) { return ( ) { - return +function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) { + return ( + + ) } function SelectTrigger({ @@ -36,7 +33,7 @@ function SelectTrigger({ size = "default", children, ...props -}: React.ComponentProps & { +}: SelectPrimitive.Trigger.Props & { size?: "sm" | "default" }) { return ( @@ -50,9 +47,11 @@ function SelectTrigger({ {...props} > {children} - - - + + } + /> ) } @@ -60,32 +59,38 @@ function SelectTrigger({ function SelectContent({ className, children, - position = "item-aligned", + side = "bottom", + sideOffset = 4, align = "center", + alignOffset = 0, + alignItemWithTrigger = true, ...props -}: React.ComponentProps) { +}: SelectPrimitive.Popup.Props & + Pick< + SelectPrimitive.Positioner.Props, + "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger" + >) { return ( - - - - {children} - - - + + {children} + + + ) } @@ -93,9 +98,9 @@ function SelectContent({ function SelectLabel({ className, ...props -}: React.ComponentProps) { +}: SelectPrimitive.GroupLabel.Props) { return ( - ) { +}: SelectPrimitive.Item.Props) { return ( - - - - - - {children} + + {children} + + + } + > + + ) } @@ -130,7 +139,7 @@ function SelectItem({ function SelectSeparator({ className, ...props -}: React.ComponentProps) { +}: SelectPrimitive.Separator.Props) { return ( ) { +}: React.ComponentProps) { return ( - - + ) } function SelectScrollDownButton({ className, ...props -}: React.ComponentProps) { +}: React.ComponentProps) { return ( - - + ) } diff --git a/apps/gui/src/components/ui/separator.tsx b/apps/gui/src/components/ui/separator.tsx index d457090..12d81c4 100644 --- a/apps/gui/src/components/ui/separator.tsx +++ b/apps/gui/src/components/ui/separator.tsx @@ -1,28 +1,31 @@ "use client" import * as React from "react" -import { Separator as SeparatorPrimitive } from "radix-ui" +import * as SeparatorPrimitive from "@radix-ui/react-separator" import { cn } from "@/lib/utils" -function Separator({ - className, - orientation = "horizontal", - decorative = true, - ...props -}: React.ComponentProps) { - return ( +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( ) -} +) +Separator.displayName = SeparatorPrimitive.Root.displayName export { Separator } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4aa73b9..ae8f5d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,6 +259,9 @@ importers: apps/gui: dependencies: + '@base-ui/react': + specifier: ^1.6.0 + version: 1.6.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@omnia/actor': specifier: workspace:* version: link:../../packages/actor @@ -283,6 +286,9 @@ importers: '@omnia/spatial': specifier: workspace:* version: link:../../packages/spatial + '@radix-ui/react-separator': + specifier: ^1.1.11 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': specifier: ^1.3.0 version: 1.3.0(@types/react@19.2.17)(react@19.2.7) @@ -712,6 +718,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -724,6 +734,33 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@base-ui/react@1.6.0': + resolution: {integrity: sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@date-fns/tz': ^1.2.0 + '@types/react': ^17 || ^18 || ^19 + date-fns: ^4.0.0 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@date-fns/tz': + optional: true + '@types/react': + optional: true + date-fns: + optional: true + + '@base-ui/utils@0.3.1': + resolution: {integrity: sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + '@braintree/sanitize-url@7.1.2': resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} @@ -5107,6 +5144,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5646,6 +5686,11 @@ packages: '@types/react': optional: true + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -6207,6 +6252,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/runtime@7.29.7': {} + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -6230,6 +6277,29 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@base-ui/react@1.6.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.7 + '@base-ui/utils': 0.3.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@floating-ui/utils': 0.2.11 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@base-ui/utils@0.3.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.7 + '@floating-ui/utils': 0.2.11 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + reselect: 5.2.0 + use-sync-external-store: 1.6.0(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@braintree/sanitize-url@7.1.2': {} '@bruits/satteri-darwin-arm64@0.9.4': @@ -11038,6 +11108,8 @@ snapshots: require-from-string@2.0.2: {} + reselect@5.2.0: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -11657,6 +11729,10 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + use-sync-external-store@1.6.0(react@19.2.7): + dependencies: + react: 19.2.7 + util-deprecate@1.0.2: {} uuid@14.0.1: {}