diff --git a/examples/openui-tui-chat/README.md b/examples/openui-tui-chat/README.md new file mode 100644 index 000000000..b637316d2 --- /dev/null +++ b/examples/openui-tui-chat/README.md @@ -0,0 +1,86 @@ +# OpenUI TUI Chat (Ink) + +A proof-of-concept **terminal** chat client that renders streamed **OpenUI Lang** +as an interactive TUI, built with [Ink](https://github.com/vadimdemedes/ink) +(React for the terminal). + +It demonstrates that OpenUI Lang is renderer-agnostic: the same language, +prompt, parser, and headless chat runtime that power the browser SDK also drive +a terminal UI — you just swap the view layer. + +``` +you ──▶ prompt + ──▶ OpenAI-compatible stream (react-headless) + ──▶ createStreamingParser().set(text) (lang-core, incremental) + ──▶ evaluateElementProps (lang-core runtime/store) + ──▶ Ink components (Card→box, BarChart→ASCII, Table→grid, Form→inputs) +``` + +## What it reuses + +- **`@openuidev/lang-core`** — `createStreamingParser`, `evaluateElementProps`, and the runtime store. No React/DOM. +- **`@openuidev/react-headless`** — `ChatProvider` chat state + `openAIReadableStreamAdapter` streaming. DOM-free (Ink is React). +- **New here** — an Ink component library (`src/genui/`) that maps `typeName → Ink component`, and a small tree walker (`RenderValue`) that mirrors react-lang's renderer. + +## Run + +Requires Node 20+ and an OpenAI-compatible key. + +```sh +export OPENAI_API_KEY=sk-... +# optional: export OPENAI_BASE_URL=... OPENAI_MODEL=... +pnpm --filter openui-tui-chat dev +``` + +Then type a prompt, e.g. _"Compare the 4 largest countries by population as a bar chart"_ +or _"Build a contact form with name, email and a topic dropdown"_. + +### Controls + +- Type + **Enter** — send a message. +- **Tab / Shift+Tab** — move focus between the composer and interactive UI (follow-ups, buttons, form fields). +- **Enter** — activate the focused follow-up/button (also confirms the highlighted Select option). +- **↑ / ↓** or **number keys** — choose an option in a focused Select; the highlighted option is selected immediately. +- **Mouse click** — click a dropdown option to select it, a button/follow-up to activate it, or a text field to focus it (see caveats below). +- **Ctrl+C** — quit. + +### Mouse support (form elements) + +Clicking targets the **latest** turn's interactive elements (dropdown options, buttons, follow-ups, text fields). It uses click-only SGR mouse tracking (`?1000`/`?1006`) enabled once at the root; clicks are hit-tested against the live region using Yoga layout offsets. + +Caveats (inherent to terminal mouse tracking + natural-height layout): + +- Hit-testing anchors to the bottom of the terminal (where the live region ends), so it's reliable for content that fills the screen — including a long form's visible fields. For a short exchange with empty space below, clicks may be slightly off; keyboard (Tab + number keys/arrows) is exact everywhere. +- While mouse tracking is active, the terminal's native click-drag **text selection/copy is disabled** (hold Shift in most terminals to bypass and select text). +- Run the app **directly in a terminal** so it receives mouse events; through tmux you must `set -g mouse on` (otherwise tmux captures the mouse). Keyboard is the fully-portable path. + +## Supported components + +`Card`, `CardHeader`, `TextContent`, `Callout` (colored banner), `TagBlock` (colored pills), +`Table`/`Col`, `BarChart`/`Series` (gradient bars), `FollowUpBlock`/`FollowUpItem`, +`Form`/`FormControl`/`Input`/`Select`/`Buttons`/`Button`. + +Headings, chart bars and the header use a truecolor gradient; `ink-spinner` shows while +streaming and `ink-big-text` renders the welcome logo. Follow-ups, buttons and form submits +drive the assistant loop via the OpenUI `@ToAssistant` action. + +## Test + +```sh +pnpm --filter openui-tui-chat test # vitest + ink-testing-library +pnpm --filter openui-tui-chat typecheck +``` + +## Chat UI + +- A gradient header, a welcome splash (big-text logo) with example prompts, and a bordered composer with key hints. +- The current exchange renders at **natural height**. Content taller than the terminal (e.g. a long form) scrolls in the terminal's native scrollback rather than corrupting the layout — the composer stays intact and the app never "breaks". Short exchanges stay compact. +- User messages render as bubbles; the assistant turn renders as generative UI with an animated spinner while streaming. + +## Limitations (POC) + +- Read-oriented charts/tables render as ASCII; not pixel-faithful. +- On a form taller than the screen, upper fields scroll out of view — reach them with keyboard focus (Tab) or by scrolling the terminal; mouse clicks target the visible (on-screen) fields. +- Mouse hit-testing is best-effort for short exchanges (see caveats above); keyboard works everywhere. +- Only the current exchange is shown; there is no in-app multi-turn history log. +- Queries/`$state` two-way binding beyond simple form fields are out of scope for v1. diff --git a/examples/openui-tui-chat/package.json b/examples/openui-tui-chat/package.json new file mode 100644 index 000000000..bc19fb89e --- /dev/null +++ b/examples/openui-tui-chat/package.json @@ -0,0 +1,35 @@ +{ + "name": "openui-tui-chat", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "A terminal (Ink/TUI) chat client that renders streamed OpenUI Lang as an interactive terminal UI.", + "bin": { + "openui-tui-chat": "src/cli.tsx" + }, + "scripts": { + "dev": "tsx src/cli.tsx", + "start": "tsx src/cli.tsx", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@openuidev/lang-core": "workspace:*", + "@openuidev/react-headless": "workspace:*", + "ink": "^5.1.0", + "ink-big-text": "^2.0.0", + "ink-spinner": "^5.0.0", + "openai": "^6.22.0", + "react": "^18.3.1", + "zod": "^4.3.6", + "zustand": "^4.5.5" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^19", + "ink-testing-library": "^4.0.0", + "tsx": "^4.19.2", + "typescript": "^5.9.3", + "vitest": "^4.0.18" + } +} diff --git a/examples/openui-tui-chat/src/__tests__/genui.test.tsx b/examples/openui-tui-chat/src/__tests__/genui.test.tsx new file mode 100644 index 000000000..fa716d2d1 --- /dev/null +++ b/examples/openui-tui-chat/src/__tests__/genui.test.tsx @@ -0,0 +1,253 @@ +import { + createStore, + createStreamingParser, + evaluateElementProps, +} from "@openuidev/lang-core"; +import { render } from "ink-testing-library"; +import { createElement, type ReactNode } from "react"; +import { describe, expect, it } from "vitest"; +import { RenderValue } from "../genui/components.js"; +import { TuiProvider, type TuiContextValue } from "../genui/context.js"; +import { tuiLibrary } from "../genui/library.js"; +import { useGenUi } from "../genui/state.js"; + +const delay = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +// Strip ANSI SGR codes so assertions match text even when it's colored/gradient +// (gradient text inserts a color code between every character). +// eslint-disable-next-line no-control-regex +const plain = (s: string | undefined) => (s ?? "").replace(/\u001B\[[0-9;]*m/g, ""); + +/** Parse + evaluate an OpenUI Lang program with the TUI library. */ +function evalProgram(src: string) { + const sp = createStreamingParser(tuiLibrary.toJSONSchema(), tuiLibrary.root); + const pr = sp.set(src); + const store = createStore(); + store.initialize(pr.stateDeclarations ?? {}, {}); + const root = pr.root + ? evaluateElementProps(pr.root, { + ctx: { getState: (n) => store.get(n), resolveRef: () => undefined }, + library: tuiLibrary, + store, + errors: [], + }) + : null; + return root; +} + +const noopCtx: TuiContextValue = { + library: tuiLibrary, + interactive: true, + triggerAction: () => {}, + getFieldValue: () => undefined, + setFieldValue: () => {}, +}; + +/** Drives the real state hook (parse → evaluate → action loop), no LLM/react-headless. */ +function Harness({ src, onSend }: { src: string; onSend: (c: string) => void }): ReactNode { + const { result, ctx } = useGenUi(tuiLibrary, "m1", src, false, onSend); + return createElement( + TuiProvider, + { value: ctx }, + result?.root ? createElement(RenderValue, { value: result.root }) : null, + ); +} + +describe("TUI renderer", () => { + it("renders header, bar chart and table from streamed OpenUI Lang", () => { + const src = [ + "root = Card([h, chart, tbl])", + 'h = CardHeader("Setup Status", "All green")', + 'chart = BarChart(["lang-core", "react-headless"], [s1], "Package", "Tests")', + 's1 = Series("Tests", [68, 70])', + "tbl = Table([c1, c2])", + 'c1 = Col("Step", ["install", "build"])', + 'c2 = Col("Result", ["ok", "ok"])', + ].join("\n"); + + const root = evalProgram(src); + const { lastFrame } = render( + createElement(TuiProvider, { value: noopCtx }, createElement(RenderValue, { value: root })), + ); + + const frame = plain(lastFrame()); + expect(frame).toContain("Setup Status"); + expect(frame).toContain("All green"); + expect(frame).toContain("█"); // chart bars + expect(frame).toContain("Step"); // table header + expect(frame).toContain("install"); // table cell + expect(frame).toContain("react-headless"); + }); + + it("renders rich Callout and TagBlock components", () => { + const src = [ + "root = Card([c, tags])", + 'c = Callout("success", "All set", "Your Pro plan is active")', + 'tags = TagBlock(["Pro", "Fast", "New"])', + ].join("\n"); + const root = evalProgram(src); + const { lastFrame } = render( + createElement(TuiProvider, { value: noopCtx }, createElement(RenderValue, { value: root })), + ); + const frame = plain(lastFrame()); + expect(frame).toContain("All set"); + expect(frame).toContain("Your Pro plan is active"); + expect(frame).toContain("Pro"); + expect(frame).toContain("Fast"); + expect(frame).toContain("New"); + }); + + it("renders unknown components as a visible marker instead of crashing", () => { + const root = evalProgram('root = Card([x])\nx = TextContent("hello world")'); + const { lastFrame } = render( + createElement(TuiProvider, { value: noopCtx }, createElement(RenderValue, { value: root })), + ); + expect(plain(lastFrame())).toContain("hello world"); + }); + + it("renders finalized turns display-only (buttons are not interactive)", () => { + const root = evalProgram( + 'root = Card([btns])\nbtns = Buttons([b1])\nb1 = Button("Retry", Action([@ToAssistant("retry")]))', + ); + const staticCtx: TuiContextValue = { ...noopCtx, interactive: false }; + const { lastFrame } = render( + createElement(TuiProvider, { value: staticCtx }, createElement(RenderValue, { value: root })), + ); + expect(plain(lastFrame())).toContain("[ Retry ]"); + }); +}); + +describe("TUI interactivity", () => { + it("sends a follow-up's text to the assistant on Enter", async () => { + const sent: string[] = []; + const src = [ + "root = Card([fu])", + "fu = FollowUpBlock([f1])", + 'f1 = FollowUpItem("Show this as a table")', + ].join("\n"); + + const { stdin } = render(createElement(Harness, { src, onSend: (c) => sent.push(c) })); + await delay(30); + stdin.write("\t"); // focus the follow-up + await delay(30); + stdin.write("\r"); // activate it + await delay(30); + + expect(sent).toContain("Show this as a table"); + }); + + it("shows a Select choice immediately after Enter (no extra keypress needed)", async () => { + const src = [ + "root = Card([form])", + 'form = Form("f", btns, [topicField])', + 'topicField = FormControl("Topic", topic)', + 'topic = Select("topic", [o1, o2])', + 'o1 = SelectItem("sales", "Sales")', + 'o2 = SelectItem("support", "Support")', + "btns = Buttons([submit])", + 'submit = Button("Send", Action([@ToAssistant("go")]))', + ].join("\n"); + + const { stdin, lastFrame } = render(createElement(Harness, { src, onSend: () => {} })); + await delay(40); + stdin.write("\t"); // focus the Select (first focusable) + await delay(30); + stdin.write("\u001B[B"); // Down arrow → move cursor to Support + await delay(30); + stdin.write("\r"); // Enter → select Support + await delay(40); + + const frame = plain(lastFrame()); + expect(frame).toContain("(•) 2. Support"); + expect(frame).not.toContain("(•) 1. Sales"); + }); + + it("selects a Select option by number key (no cursor movement)", async () => { + const src = [ + "root = Card([form])", + 'form = Form("f", btns, [topicField])', + 'topicField = FormControl("Topic", topic)', + 'topic = Select("topic", [o1, o2])', + 'o1 = SelectItem("sales", "Sales")', + 'o2 = SelectItem("support", "Support")', + "btns = Buttons([submit])", + 'submit = Button("Send")', + ].join("\n"); + const { stdin, lastFrame } = render(createElement(Harness, { src, onSend: () => {} })); + await delay(40); + stdin.write("\t"); // focus the Select + await delay(30); + stdin.write("2"); // press "2" → pick the 2nd option directly + await delay(40); + const frame = plain(lastFrame()); + expect(frame).toContain("(•) 2. Support"); + expect(frame).not.toContain("(•) 1. Sales"); + }); + + it("selects a Select option immediately on arrow (no Enter needed)", async () => { + const src = [ + "root = Card([form])", + 'form = Form("f", btns, [topicField])', + 'topicField = FormControl("Topic", topic)', + 'topic = Select("topic", [o1, o2])', + 'o1 = SelectItem("sales", "Sales")', + 'o2 = SelectItem("support", "Support")', + "btns = Buttons([submit])", + 'submit = Button("Send")', + ].join("\n"); + const { stdin, lastFrame } = render(createElement(Harness, { src, onSend: () => {} })); + await delay(40); + stdin.write("\t"); // focus the Select + await delay(30); + stdin.write("\u001B[B"); // Down arrow only — should select immediately + await delay(40); + expect(plain(lastFrame())).toContain("(•) 2. Support"); + }); + + it("shows typed Input text immediately", async () => { + const src = [ + "root = Card([form])", + 'form = Form("f", btns, [nameField])', + 'nameField = FormControl("Name", nameInput)', + 'nameInput = Input("name", "Your name")', + "btns = Buttons([submit])", + 'submit = Button("Send")', + ].join("\n"); + const { stdin, lastFrame } = render(createElement(Harness, { src, onSend: () => {} })); + await delay(40); + stdin.write("\t"); + await delay(30); + stdin.write("Hi"); + await delay(40); + expect(plain(lastFrame())).toContain("Hi"); + }); + + it("collects form field values and submits them via the button's action", async () => { + const sent: string[] = []; + const src = [ + "root = Card([form])", + "form = Form(\"contact\", btns, [nameField])", + 'nameField = FormControl("Name", nameInput)', + 'nameInput = Input("name", "Your name")', + "btns = Buttons([submit])", + 'submit = Button("Send", Action([@ToAssistant("Contact submitted")]))', + ].join("\n"); + + const { stdin } = render(createElement(Harness, { src, onSend: (c) => sent.push(c) })); + await delay(30); + stdin.write("\t"); // focus the name input + await delay(20); + for (const ch of "Ada") { + stdin.write(ch); + await delay(5); + } + stdin.write("\t"); // focus the submit button + await delay(20); + stdin.write("\r"); // submit + await delay(30); + + expect(sent.length).toBe(1); + expect(sent[0]).toContain("Contact submitted"); + expect(sent[0]).toContain('"name":"Ada"'); + }); +}); diff --git a/examples/openui-tui-chat/src/app.tsx b/examples/openui-tui-chat/src/app.tsx new file mode 100644 index 000000000..d8e9372a0 --- /dev/null +++ b/examples/openui-tui-chat/src/app.tsx @@ -0,0 +1,180 @@ +import type { Message } from "@openuidev/react-headless"; +import BigText from "ink-big-text"; +import Spinner from "ink-spinner"; +import { Box, Text, useFocus, useInput, type DOMElement } from "ink"; +import { useRef, useState } from "react"; +import { useLocalChat, type ProcessFn } from "./chat.js"; +import { RenderValue } from "./genui/components.js"; +import { TuiProvider } from "./genui/context.js"; +import { GradientText } from "./genui/gradient.js"; +import { tuiLibrary } from "./genui/library.js"; +import { isTypedText, MouseProvider } from "./genui/mouse.js"; +import { useGenUi } from "./genui/state.js"; + +function messageText(content: unknown): string { + if (typeof content === "string") return content; + if (Array.isArray(content)) { + return content + .map((p) => (p && typeof p === "object" && "text" in p ? String((p as { text: unknown }).text) : "")) + .join(""); + } + return ""; +} + +const firstLine = (s: string) => s.split("\n")[0] ?? ""; + +// ─────────────────────────── chrome ─────────────────────────── + +function Header() { + return ( + + + {" generative UI, streamed into your terminal"} + + ); +} + +function Welcome() { + return ( + + + Ask for UI and it renders live, right here in your terminal. Try: + · Compare the 4 largest countries by population as a bar chart + · Build a contact form with a name field and a topic dropdown + · A pricing callout with tags for a Pro plan + + ); +} + +function UserBubble({ text }: { text: string }) { + return ( + + + {text} + + + ); +} + +function Thinking() { + return ( + + + + + + + ); +} + +function Composer({ + draft, + focused, + isRunning, +}: { + draft: string; + focused: boolean; + isRunning: boolean; +}) { + return ( + + + {"❯ "} + {draft} + {focused ? : null} + {draft.length === 0 ? ( + {isRunning ? "waiting for response…" : "Message OpenUI…"} + ) : null} + + {" Enter send · Tab/click focus · ↑↓ or number keys choose · Ctrl+C quit"} + + ); +} + +// ─────────────────────────── assistant message ─────────────────────────── + +function AssistantMessageView({ + message, + isStreaming, + onSend, +}: { + message: Message; + isStreaming: boolean; + onSend: (content: string) => void; +}) { + const content = messageText(message.content); + const { result, ctx } = useGenUi(tuiLibrary, message.id, content, isStreaming, onSend, true); + + if (!result?.root) { + if (isStreaming) return null; + return ( + + {content ? content : "(no renderable UI)"} + + ); + } + + return ( + + + + + + + ); +} + +// ─────────────────────────── app ─────────────────────────── + +export function App({ processMessage }: { processMessage: ProcessFn }) { + const { messages, isRunning, send } = useLocalChat(processMessage); + const [draft, setDraft] = useState(""); + const dynamicRef = useRef(null); + + const { isFocused: composerFocused } = useFocus({ id: "composer", autoFocus: true }); + useInput( + (input, key) => { + if (key.return) { + const text = draft.trim(); + if (text && !isRunning) { + send(text); + setDraft(""); + } + return; + } + if (key.backspace || key.delete) { + setDraft((d) => d.slice(0, -1)); + return; + } + if (isTypedText(input) && !key.ctrl && !key.meta) { + setDraft((d) => d + input); + } + }, + { isActive: composerFocused }, + ); + + const last = messages[messages.length - 1]; + const liveAssistant = last && last.role === "assistant" ? last : null; + const liveContent = liveAssistant ? messageText(liveAssistant.content) : ""; + const showThinking = isRunning && liveContent.trim() === ""; + const lastUser = [...messages].reverse().find((m) => m.role === "user") ?? null; + + // Natural-height frame (no fixed height): the current exchange renders at its + // true size. Content taller than the terminal scrolls natively instead of + // corrupting the layout, so a long form never breaks. Mouse hit-testing tracks + // the region via a cursor-position query (see mouse.tsx). + return ( + + +
+ {messages.length === 0 ? : null} + {lastUser ? : null} + {liveAssistant ? ( + + ) : null} + {showThinking ? : null} + + + + ); +} diff --git a/examples/openui-tui-chat/src/chat.ts b/examples/openui-tui-chat/src/chat.ts new file mode 100644 index 000000000..8d735134c --- /dev/null +++ b/examples/openui-tui-chat/src/chat.ts @@ -0,0 +1,77 @@ +import { + openAIReadableStreamAdapter, + processStreamedMessage, + type AssistantMessage, + type Message, +} from "@openuidev/react-headless"; +import { useCallback, useRef, useState } from "react"; + +export type ProcessFn = (params: { + threadId: string; + messages: Message[]; + abortController: AbortController; +}) => Promise; + +export interface LocalChat { + messages: Message[]; + isRunning: boolean; + send: (content: string) => void; +} + +/** + * Minimal chat store that runs on the SAME React instance Ink uses. It reuses + * react-headless's DOM-free streaming pipeline (`processStreamedMessage` + + * `openAIReadableStreamAdapter`) without its React `ChatProvider`, which would + * otherwise pull in a second React copy and break Ink's reconciler. + */ +export function useLocalChat(processMessage: ProcessFn): LocalChat { + const [messages, setMessages] = useState([]); + const [isRunning, setIsRunning] = useState(false); + const messagesRef = useRef([]); + messagesRef.current = messages; + const runningRef = useRef(false); + + const send = useCallback( + async (content: string) => { + if (!content.trim() || runningRef.current) return; + runningRef.current = true; + + const userMessage = { + id: crypto.randomUUID(), + role: "user", + content, + } as unknown as Message; + + const next = [...messagesRef.current, userMessage]; + setMessages(next); + setIsRunning(true); + + const abortController = new AbortController(); + try { + const response = await processMessage({ threadId: "tui", messages: next, abortController }); + await processStreamedMessage({ + response, + adapter: openAIReadableStreamAdapter(), + createMessage: (m: AssistantMessage) => setMessages((cur) => [...cur, m]), + updateMessage: (m: AssistantMessage) => + setMessages((cur) => cur.map((x) => (x.id === m.id ? { ...m } : x))), + deleteMessage: (id: string) => setMessages((cur) => cur.filter((x) => x.id !== id)), + }); + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + const errorMessage = { + id: crypto.randomUUID(), + role: "assistant", + content: `root = Card([e])\ne = TextContent("Error: ${message.replace(/"/g, "'")}")`, + } as unknown as Message; + setMessages((cur) => [...cur, errorMessage]); + } finally { + runningRef.current = false; + setIsRunning(false); + } + }, + [processMessage], + ); + + return { messages, isRunning, send }; +} diff --git a/examples/openui-tui-chat/src/cli.tsx b/examples/openui-tui-chat/src/cli.tsx new file mode 100644 index 000000000..26042c436 --- /dev/null +++ b/examples/openui-tui-chat/src/cli.tsx @@ -0,0 +1,27 @@ +// Polyfill requestAnimationFrame for react-headless's streaming updates (it +// debounces message updates via rAF, which bare Node does not provide). +const g = globalThis as unknown as { + requestAnimationFrame?: (cb: (t: number) => void) => unknown; + cancelAnimationFrame?: (id: unknown) => void; +}; +if (typeof g.requestAnimationFrame !== "function") { + g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 16); + g.cancelAnimationFrame = (id) => clearTimeout(id as ReturnType); +} + +import { render } from "ink"; +import { createElement } from "react"; +import { App } from "./app.js"; +import { tuiLibrary } from "./genui/library.js"; +import { makeProcessMessage } from "./llm.js"; + +if (!process.env.OPENAI_API_KEY) { + console.error( + "\nOPENAI_API_KEY is not set. Export it (optionally OPENAI_BASE_URL / OPENAI_MODEL) and retry.\n", + ); + process.exit(1); +} + +const systemPrompt = tuiLibrary.prompt(); + +render(createElement(App, { processMessage: makeProcessMessage(systemPrompt) })); diff --git a/examples/openui-tui-chat/src/genui/chart.ts b/examples/openui-tui-chat/src/genui/chart.ts new file mode 100644 index 000000000..38276e4d9 --- /dev/null +++ b/examples/openui-tui-chat/src/genui/chart.ts @@ -0,0 +1,44 @@ +/** Element-node shape after evaluation (only the bits the chart needs). */ +interface SeriesNode { + props?: { category?: unknown; values?: unknown }; +} + +function padEnd(s: string, w: number): string { + return s.length >= w ? s : s + " ".repeat(w - s.length); +} + +/** + * Render a horizontal ASCII bar chart. Single-series charts show one bar per + * label; multi-series charts show one bar per (label, series) pair. + */ +export function renderBars( + labels: string[], + seriesNodes: SeriesNode[], + width = 32, +): string[] { + const series = seriesNodes.map((s) => ({ + category: String(s?.props?.category ?? ""), + values: Array.isArray(s?.props?.values) ? (s!.props!.values as unknown[]).map(Number) : [], + })); + + const rows: { name: string; value: number }[] = []; + labels.forEach((label, i) => { + series.forEach((s) => { + rows.push({ + name: series.length > 1 ? `${label} · ${s.category}` : label, + value: Number.isFinite(s.values[i]) ? s.values[i]! : 0, + }); + }); + }); + + if (rows.length === 0) return ["(no data)"]; + + const max = Math.max(1, ...rows.map((r) => Math.abs(r.value))); + const nameW = Math.max(0, ...rows.map((r) => r.name.length)); + + return rows.map((r) => { + const barLen = Math.max(0, Math.round((Math.abs(r.value) / max) * width)); + const bar = "█".repeat(barLen) || "▏"; + return `${padEnd(r.name, nameW)} │ ${bar} ${r.value}`; + }); +} diff --git a/examples/openui-tui-chat/src/genui/components.tsx b/examples/openui-tui-chat/src/genui/components.tsx new file mode 100644 index 000000000..d80a29355 --- /dev/null +++ b/examples/openui-tui-chat/src/genui/components.tsx @@ -0,0 +1,471 @@ +import { Box, Text, useFocus, useFocusManager, useInput } from "ink"; +import { Component, Fragment, type ReactNode, useState } from "react"; +import { renderBars } from "./chart.js"; +import { FormNameProvider, useFormName, useTui } from "./context.js"; +import { GradientText } from "./gradient.js"; +import { Clickable, isTypedText } from "./mouse.js"; + +/** The render contract each library component receives (matches lang-core's ComponentRenderProps). */ +interface ViewProps { + props: Record; + renderNode: (value: unknown) => ReactNode; + statementId?: string; +} + +/** Common signature for every library view (keeps the library's component type uniform). */ +export type View = (p: ViewProps) => ReactNode; + +const str = (v: unknown): string => (v == null ? "" : String(v)); + +// ─────────────────────────── tree walker ─────────────────────────── + +/** Walk any evaluated value into Ink nodes (mirrors react-lang's renderDeep). */ +export function RenderValue({ value }: { value: unknown }): ReactNode { + if (value == null || value === false) return null; + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + return {String(value)}; + } + if (Array.isArray(value)) { + return ( + <> + {value.map((v, i) => ( + + + + ))} + + ); + } + if (typeof value === "object" && (value as { type?: string }).type === "element") { + return ; + } + return null; +} + +interface ElementLike { + type: "element"; + typeName: string; + props: Record; + statementId?: string; +} + +function RenderNode({ node }: { node: ElementLike }): ReactNode { + const { library } = useTui(); + const Comp = library.components[node.typeName]?.component as + | ((p: ViewProps) => ReactNode) + | undefined; + + if (!Comp) return [unknown component: {node.typeName}]; + + return ( + + } + statementId={node.statementId} + /> + + ); +} + +class ElementErrorBoundary extends Component< + { name: string; children: ReactNode }, + { failed: boolean } +> { + state = { failed: false }; + static getDerivedStateFromError() { + return { failed: true }; + } + render() { + if (this.state.failed) { + return [render error in {this.props.name}]; + } + return this.props.children; + } +} + +// ─────────────────────────── content components ─────────────────────────── + +function CardView({ props, renderNode }: ViewProps) { + const children = Array.isArray(props.children) ? props.children : []; + return ( + + {children.map((c, i) => ( + + {renderNode(c)} + + ))} + + ); +} + +function CardHeaderView({ props }: ViewProps) { + return ( + + {props.title ? : null} + {props.subtitle ? {str(props.subtitle)} : null} + + ); +} + +function TextContentView({ props }: ViewProps) { + const size = str(props.size); + const heavy = size.includes("heavy"); + const large = size === "large" || size === "large-heavy"; + const text = str(props.text); + // Headline-sized text gets a gradient for a richer look. + if (large) { + return ; + } + return ( + + {text} + + ); +} + +const CALLOUT_STYLES: Record = { + info: { color: "cyan", icon: "ℹ" }, + success: { color: "green", icon: "✓" }, + warning: { color: "yellow", icon: "⚠" }, + error: { color: "red", icon: "✕" }, + neutral: { color: "gray", icon: "•" }, +}; + +function CalloutView({ props }: ViewProps) { + const variant = str(props.variant) || "info"; + const style = CALLOUT_STYLES[variant] ?? CALLOUT_STYLES.info!; + return ( + + + + {style.icon} {str(props.title)} + + + {props.description ? {str(props.description)} : null} + + ); +} + +const TAG_COLORS = ["cyan", "magenta", "green", "yellow", "blue", "red"] as const; + +function TagBlockView({ props }: ViewProps) { + const tags = Array.isArray(props.tags) ? props.tags.map(str) : []; + return ( + + {tags.map((t, i) => ( + + {` ${t} `} + + ))} + + ); +} + +function TableView({ props }: ViewProps) { + const columns = Array.isArray(props.columns) ? props.columns : []; + const headers = columns.map((c) => str((c as ElementLike)?.props?.label)); + const data = columns.map((c) => { + const d = (c as ElementLike)?.props?.data; + return Array.isArray(d) ? d.map((x) => str(x)) : []; + }); + const rowCount = Math.max(0, ...data.map((d) => d.length)); + const widths = headers.map((h, ci) => + Math.max(h.length, ...(data[ci]!.length ? data[ci]!.map((s) => s.length) : [0])), + ); + const pad = (s: string, w: number) => (s.length >= w ? s : s + " ".repeat(w - s.length)); + const line = (cells: string[]) => cells.map((c, ci) => pad(c, widths[ci]!)).join(" "); + + return ( + + {line(headers)} + {widths.map((w) => "─".repeat(w)).join(" ")} + {Array.from({ length: rowCount }).map((_, ri) => ( + {line(headers.map((_, ci) => data[ci]![ri] ?? ""))} + ))} + + ); +} + +function BarChartView({ props }: ViewProps) { + const labels = Array.isArray(props.labels) ? props.labels.map(str) : []; + const series = Array.isArray(props.series) ? (props.series as { props?: Record }[]) : []; + const lines = renderBars(labels, series); + return ( + + {props.yLabel ? {str(props.yLabel)} : null} + {lines.map((ln, i) => ( + + ))} + {props.xLabel ? {str(props.xLabel)} : null} + + ); +} + +// ─────────────────────────── interactive components ─────────────────────────── + +function FollowUpBlockView({ props, renderNode }: ViewProps) { + const items = Array.isArray(props.items) ? props.items : []; + return ( + + Related (Tab to focus, Enter to ask): + {items.map((it, i) => ( + {renderNode(it)} + ))} + + ); +} + +function FollowUpItemView({ props }: ViewProps) { + const { interactive } = useTui(); + const text = str(props.text); + return interactive ? ( + + ) : ( + {"• "}{text} + ); +} + +function FollowUpItemInteractive({ text }: { text: string }) { + const { isFocused } = useFocus(); + const { triggerAction } = useTui(); + useInput( + (_input, key) => { + if (key.return) triggerAction(text); + }, + { isActive: isFocused }, + ); + return ( + triggerAction(text)}> + + {isFocused ? "❯ " : "• "} + {text} + + + ); +} + +function ButtonsView({ props, renderNode }: ViewProps) { + const buttons = Array.isArray(props.buttons) ? props.buttons : []; + const direction = props.direction === "column" ? "column" : "row"; + return ( + + {buttons.map((b, i) => ( + {renderNode(b)} + ))} + + ); +} + +function ButtonView({ props }: ViewProps) { + const { interactive } = useTui(); + const label = str(props.label) || "Button"; + return interactive ? ( + + ) : ( + {`[ ${label} ]`} + ); +} + +function ButtonInteractive({ label, action }: { label: string; action: unknown }) { + const { isFocused } = useFocus(); + const formName = useFormName(); + const { triggerAction } = useTui(); + useInput( + (_input, key) => { + if (key.return) triggerAction(label, formName, action); + }, + { isActive: isFocused }, + ); + return ( + triggerAction(label, formName, action)}> + + {` ${label} `} + + + ); +} + +function FormView({ props, renderNode }: ViewProps) { + const name = str(props.name) || "form"; + const fields = Array.isArray(props.fields) ? props.fields : []; + return ( + + + Tab between fields · type to fill · number keys or ↑↓ to choose · Enter on a button to submit + {fields.map((f, i) => ( + {renderNode(f)} + ))} + {renderNode(props.buttons)} + + + ); +} + +function FormControlView({ props, renderNode }: ViewProps) { + return ( + + {str(props.label)} + {props.hint ? {str(props.hint)} : null} + {renderNode(props.input)} + + ); +} + +function InputView({ props }: ViewProps) { + const { interactive } = useTui(); + if (!interactive) { + return ( + + {" "} + {str(props.placeholder) || str(props.name)} + + ); + } + return ; +} + +function InputInteractive({ props }: { props: Record }) { + const formName = useFormName(); + const { getFieldValue, setFieldValue } = useTui(); + const name = str(props.name); + const focusId = `input:${formName ?? ""}:${name}`; + const { isFocused } = useFocus({ id: focusId }); + const { focus } = useFocusManager(); + const value = str(getFieldValue(formName, name)); + useInput( + (input, key) => { + if (key.backspace || key.delete) { + setFieldValue(formName, "Input", name, value.slice(0, -1)); + return; + } + if (key.return || key.tab || key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) + return; + // Accept printable text; drop mouse escape sequences that share stdin. + if (isTypedText(input) && !key.ctrl && !key.meta) { + setFieldValue(formName, "Input", name, value + input); + } + }, + { isActive: isFocused }, + ); + const shown = value.length ? value : str(props.placeholder); + return ( + focus(focusId)}> + + {isFocused ? "❯ " : " "} + {shown} + {isFocused ? "▏" : ""} + + + ); +} + +function SelectView({ props }: ViewProps) { + const { interactive } = useTui(); + const items = Array.isArray(props.items) ? props.items : []; + if (!interactive) { + return ( + + {items.map((it, i) => ( + + {" ( ) "} + {str((it as ElementLike)?.props?.label ?? (it as ElementLike)?.props?.value)} + + ))} + + ); + } + return ; +} + +function SelectInteractive({ props }: { props: Record }) { + const { isFocused } = useFocus(); + const formName = useFormName(); + const { getFieldValue, setFieldValue } = useTui(); + const name = str(props.name); + const items = Array.isArray(props.items) ? props.items : []; + const options = items.map((it) => ({ + value: str((it as ElementLike)?.props?.value), + label: str((it as ElementLike)?.props?.label ?? (it as ElementLike)?.props?.value), + })); + const current = str(getFieldValue(formName, name)); + const initialIndex = Math.max( + 0, + options.findIndex((o) => o.value === current), + ); + const [cursor, setCursor] = useState(initialIndex); + // Bumped on every choice so the row re-renders immediately, even when the + // cursor index is unchanged (e.g. pressing Enter) where a store-only update + // may not repaint on the next frame. + const [, bump] = useState(0); + + // Arrow keys select immediately (radio-group behaviour) for instant feedback; + // Enter also confirms the current row. + const choose = (index: number) => { + const clamped = Math.max(0, Math.min(options.length - 1, index)); + const option = options[clamped]; + setCursor(clamped); + bump((n) => n + 1); + if (option) setFieldValue(formName, "Select", name, option.value); + }; + + useInput( + (input, key) => { + // Number keys pick an option directly — no cursor movement needed. + if (/^[1-9]$/.test(input) && !key.ctrl && !key.meta) { + const index = Number(input) - 1; + if (index < options.length) choose(index); + return; + } + if (key.upArrow) choose(cursor - 1); + else if (key.downArrow) choose(cursor + 1); + else if (key.return) choose(cursor); + }, + { isActive: isFocused }, + ); + + return ( + + {options.map((o, i) => { + const isSel = o.value === current; + const isCursor = isFocused && i === cursor; + return ( + choose(i)}> + + {isCursor ? "❯ " : " "} + {isSel ? "(•) " : "( ) "} + {i + 1}. {o.label} + + + ); + })} + {isFocused ? click, press 1-{options.length}, or ↑↓ to choose : null} + + ); +} + +/** No-op view for purely structural nodes (parent reads their props directly). */ +function StructuralView(_props: ViewProps): ReactNode { + return null; +} + +export const views: Record = { + Card: CardView, + CardHeader: CardHeaderView, + TextContent: TextContentView, + Callout: CalloutView, + TagBlock: TagBlockView, + Table: TableView, + Col: StructuralView, + BarChart: BarChartView, + Series: StructuralView, + FollowUpBlock: FollowUpBlockView, + FollowUpItem: FollowUpItemView, + Form: FormView, + FormControl: FormControlView, + Input: InputView, + Select: SelectView, + SelectItem: StructuralView, + Buttons: ButtonsView, + Button: ButtonView, +}; diff --git a/examples/openui-tui-chat/src/genui/context.tsx b/examples/openui-tui-chat/src/genui/context.tsx new file mode 100644 index 000000000..ebe44174d --- /dev/null +++ b/examples/openui-tui-chat/src/genui/context.tsx @@ -0,0 +1,46 @@ +import type { Library } from "@openuidev/lang-core"; +import { createContext, useContext } from "react"; + +/** + * Everything the Ink component library needs at render time. Mirrors the role + * of react-lang's OpenUIContext, but scoped to what a terminal renderer uses. + */ +export interface TuiContextValue { + library: Library; + /** When false, components render display-only (no focus/keyboard) — used for finalized turns. */ + interactive: boolean; + /** Fire an action (button / follow-up / form submit) → sends a message to the assistant. */ + triggerAction: ( + userMessage: string, + formName?: string, + action?: unknown, + ) => void; + /** Read a form field value (unwrapped) from the runtime store. */ + getFieldValue: (formName: string | undefined, name: string) => unknown; + /** Write a form field value into the runtime store. */ + setFieldValue: ( + formName: string | undefined, + componentType: string, + name: string, + value: unknown, + ) => void; +} + +const TuiContext = createContext(null); + +export const TuiProvider = TuiContext.Provider; + +export function useTui(): TuiContextValue { + const ctx = useContext(TuiContext); + if (!ctx) throw new Error("useTui must be used within a TuiProvider"); + return ctx; +} + +/** The name of the enclosing Form, so inputs know where to store their value. */ +const FormNameContext = createContext(undefined); + +export const FormNameProvider = FormNameContext.Provider; + +export function useFormName(): string | undefined { + return useContext(FormNameContext); +} diff --git a/examples/openui-tui-chat/src/genui/gradient.tsx b/examples/openui-tui-chat/src/genui/gradient.tsx new file mode 100644 index 000000000..c53fc2036 --- /dev/null +++ b/examples/openui-tui-chat/src/genui/gradient.tsx @@ -0,0 +1,56 @@ +import { Text } from "ink"; + +type RGB = [number, number, number]; + +// Two-stop gradient presets (start → end). +const PRESETS: Record = { + mind: [ + [0, 224, 255], + [180, 90, 255], + ], // cyan → violet + pastel: [ + [255, 140, 200], + [255, 214, 120], + ], // pink → gold + ocean: [ + [0, 170, 255], + [0, 255, 190], + ], // blue → aqua +}; + +function toHex([r, g, b]: RGB): string { + const h = (v: number) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0"); + return `#${h(r)}${h(g)}${h(b)}`; +} + +function lerp(a: RGB, b: RGB, t: number): RGB { + return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t]; +} + +/** + * Gradient text using Ink's own truecolor support — one per + * character. Reliable (no external gradient dep) and degrades to plain text on + * terminals without color. + */ +export function GradientText({ + text, + preset = "mind", + bold, +}: { + text: string; + preset?: keyof typeof PRESETS | string; + bold?: boolean; +}) { + const [from, to] = PRESETS[preset] ?? PRESETS.mind!; + const chars = [...text]; + const n = Math.max(1, chars.length - 1); + return ( + + {chars.map((ch, i) => ( + + {ch} + + ))} + + ); +} diff --git a/examples/openui-tui-chat/src/genui/library.ts b/examples/openui-tui-chat/src/genui/library.ts new file mode 100644 index 000000000..ee541b6c4 --- /dev/null +++ b/examples/openui-tui-chat/src/genui/library.ts @@ -0,0 +1,196 @@ +import { createLibrary, defineComponent } from "@openuidev/lang-core"; +import { z } from "zod"; +import { views } from "./components.js"; + +// Leaf / structural components first so containers can reference `.ref`. + +const Series = defineComponent({ + name: "Series", + description: "One data series for a chart.", + props: z.object({ category: z.string(), values: z.array(z.number()) }), + component: views.Series, +}); + +const Col = defineComponent({ + name: "Col", + description: "A table column: a label plus its column data (one entry per row).", + props: z.object({ + label: z.string(), + data: z.array(z.union([z.string(), z.number()])), + type: z.enum(["string", "number"]).optional(), + }), + component: views.Col, +}); + +const SelectItem = defineComponent({ + name: "SelectItem", + description: "An option inside a Select.", + props: z.object({ value: z.string(), label: z.string() }), + component: views.SelectItem, +}); + +const FollowUpItem = defineComponent({ + name: "FollowUpItem", + description: "A clickable follow-up suggestion. Its text is sent to the assistant when chosen.", + props: z.object({ text: z.string() }), + component: views.FollowUpItem, +}); + +const Button = defineComponent({ + name: "Button", + description: + "A clickable button. Provide an action Action([@ToAssistant(\"message\")]) to control what is sent to the assistant.", + props: z.object({ + label: z.string(), + action: z.any().optional(), + variant: z.enum(["primary", "secondary", "tertiary"]).optional(), + }), + component: views.Button, +}); + +const Input = defineComponent({ + name: "Input", + description: "A single-line text field inside a Form.", + props: z.object({ + name: z.string(), + placeholder: z.string().optional(), + type: z.enum(["text", "email", "password", "number", "url"]).optional(), + }), + component: views.Input, +}); + +const Select = defineComponent({ + name: "Select", + description: "A single-choice dropdown inside a Form.", + props: z.object({ + name: z.string(), + items: z.array(SelectItem.ref), + placeholder: z.string().optional(), + }), + component: views.Select, +}); + +const FormControl = defineComponent({ + name: "FormControl", + description: "A labelled form field wrapping one Input or Select.", + props: z.object({ + label: z.string(), + input: z.union([Input.ref, Select.ref]), + hint: z.string().optional(), + }), + component: views.FormControl, +}); + +const Buttons = defineComponent({ + name: "Buttons", + description: "A group of Button components.", + props: z.object({ + buttons: z.array(Button.ref), + direction: z.enum(["row", "column"]).optional(), + }), + component: views.Buttons, +}); + +const Form = defineComponent({ + name: "Form", + description: "A form with fields and explicit action buttons. Provide Buttons(...) as the second argument.", + props: z.object({ + name: z.string(), + buttons: Buttons.ref, + fields: z.array(FormControl.ref).default([]), + }), + component: views.Form, +}); + +const BarChart = defineComponent({ + name: "BarChart", + description: "A bar chart. Use for comparing values across categories.", + props: z.object({ + labels: z.array(z.string()), + series: z.array(Series.ref), + xLabel: z.string().optional(), + yLabel: z.string().optional(), + }), + component: views.BarChart, +}); + +const Table = defineComponent({ + name: "Table", + description: "A column-oriented data table. Each Col holds its own data array.", + props: z.object({ columns: z.array(Col.ref) }), + component: views.Table, +}); + +const TextContent = defineComponent({ + name: "TextContent", + description: "A block of text. Optional size controls emphasis.", + props: z.object({ + text: z.string(), + size: z.enum(["small", "default", "large", "small-heavy", "large-heavy"]).optional(), + }), + component: views.TextContent, +}); + +const CardHeader = defineComponent({ + name: "CardHeader", + description: "A header with an optional title and subtitle.", + props: z.object({ title: z.string().optional(), subtitle: z.string().optional() }), + component: views.CardHeader, +}); + +const Callout = defineComponent({ + name: "Callout", + description: + "A colored callout banner for highlights, tips, or status. Choose a variant that matches the tone.", + props: z.object({ + variant: z.enum(["info", "success", "warning", "error", "neutral"]), + title: z.string(), + description: z.string().optional(), + }), + component: views.Callout, +}); + +const TagBlock = defineComponent({ + name: "TagBlock", + description: "A row of short colored tags/pills (keywords, categories, or labels).", + props: z.object({ tags: z.array(z.string()) }), + component: views.TagBlock, +}); + +const FollowUpBlock = defineComponent({ + name: "FollowUpBlock", + description: "A list of follow-up suggestions shown at the end of a response.", + props: z.object({ items: z.array(FollowUpItem.ref) }), + component: views.FollowUpBlock, +}); + +const Card = defineComponent({ + name: "Card", + description: "The root container. Children stack vertically. Every response is a single Card.", + props: z.object({ children: z.array(z.any()) }), + component: views.Card, +}); + +export const tuiLibrary = createLibrary({ + components: [ + Card, + CardHeader, + TextContent, + Callout, + TagBlock, + Table, + Col, + BarChart, + Series, + FollowUpBlock, + FollowUpItem, + Form, + FormControl, + Input, + Select, + SelectItem, + Buttons, + Button, + ], + root: "Card", +}); diff --git a/examples/openui-tui-chat/src/genui/mouse.tsx b/examples/openui-tui-chat/src/genui/mouse.tsx new file mode 100644 index 000000000..ceaed788e --- /dev/null +++ b/examples/openui-tui-chat/src/genui/mouse.tsx @@ -0,0 +1,170 @@ +import { appendFileSync } from "node:fs"; +import { Box, measureElement, useStdin, useStdout, type DOMElement } from "ink"; +import { + createContext, + useCallback, + useContext, + useEffect, + useRef, + type ReactNode, + type RefObject, +} from "react"; + +// Click-only SGR mouse tracking (?1000) + SGR extended coords (?1006). We use +// click-only (not all-motion ?1003) to avoid flooding stdin on every move. +const SGR_ENABLE = "\u001B[?1000h\u001B[?1006h"; +const SGR_DISABLE = "\u001B[?1000l\u001B[?1006l"; +// Press events end with "M"; release with "m". We only act on press. +const PRESS_RE = /\u001B\[<(\d+);(\d+);(\d+)M/g; + +const DEBUG = process.env.TUI_MOUSE_DEBUG === "1"; + +interface Zone { + ref: RefObject; + onClick: () => void; +} + +interface MouseContextValue { + register: (zone: Zone) => () => void; +} + +const MouseContext = createContext(null); + +/** Position of a node relative to `rootNode`, by summing Yoga offsets up the tree. */ +function positionRelativeTo(node: DOMElement, rootNode: DOMElement | null) { + const yoga = (node as unknown as { yogaNode?: any }).yogaNode; + const rootYoga = (rootNode as unknown as { yogaNode?: any } | null)?.yogaNode; + if (!yoga) return null; + let left = 0; + let top = 0; + let current: any = yoga; + while (current && current !== rootYoga) { + left += current.getComputedLeft?.() ?? 0; + top += current.getComputedTop?.() ?? 0; + current = current.getParent?.(); + } + return { + left, + top, + width: yoga.getComputedWidth?.() ?? 0, + height: yoga.getComputedHeight?.() ?? 0, + }; +} + +/** + * Enables click-only mouse tracking once, parses press events from stdin, and + * dispatches to the topmost registered zone. `rootRef` must point at the + * bottom-anchored dynamic region so we can map absolute screen rows to it. + */ +export function MouseProvider({ + rootRef, + children, +}: { + rootRef: RefObject; + children: ReactNode; +}) { + const { stdin, setRawMode, isRawModeSupported } = useStdin(); + const { stdout } = useStdout(); + const zones = useRef>(new Set()); + + const register = useCallback((zone: Zone) => { + zones.current.add(zone); + return () => { + zones.current.delete(zone); + }; + }, []); + + useEffect(() => { + if (!stdin || !isRawModeSupported) return; + setRawMode(true); + stdout.write(SGR_ENABLE); + + const dispatch = (cx: number, cy: number) => { + const rows = stdout.rows ?? 24; + const height = rootRef.current ? measureElement(rootRef.current).height : 0; + // The live region's last line (composer) sits at the bottom of the terminal + // output, so its top screen row is (rows - height). This is correct once the + // content fills the screen (including long forms); for a short exchange it's + // approximate, so keyboard remains the exact path there. + const offsetY = height > 0 ? rows - height : 0; + + let hit: Zone | null = null; + let hitInfo = "miss"; + for (const zone of zones.current) { + const node = zone.ref.current; + if (!node) continue; + const p = positionRelativeTo(node, rootRef.current); + if (!p) continue; + const top = p.top + offsetY; + if (DEBUG) hitInfo += ` | zone[l${p.left},t${top},w${p.width},h${p.height}]`; + if (cx >= p.left && cx < p.left + p.width && cy >= top && cy < top + p.height) { + hit = zone; + hitInfo = `hit@[l${p.left},t${top},w${p.width},h${p.height}]`; + break; + } + } + + if (DEBUG) { + try { + appendFileSync("/tmp/mouse-debug.log", `click(${cx},${cy}) rows=${rows} offY=${offsetY} ${hitInfo}\n`); + } catch { + // ignore + } + } + if (hit) hit.onClick(); + }; + + const onData = (data: Buffer) => { + const s = data.toString("utf8"); + PRESS_RE.lastIndex = 0; + let m: RegExpExecArray | null; + while ((m = PRESS_RE.exec(s)) !== null) { + const cb = Number(m[1]); + if ((cb & 3) !== 0 || (cb & 64) !== 0) continue; // left-button presses only + dispatch(Number(m[2]) - 1, Number(m[3]) - 1); + } + }; + + stdin.on("data", onData); + const cleanupOnExit = () => stdout.write(SGR_DISABLE); + process.once("exit", cleanupOnExit); + + return () => { + stdin.off("data", onData); + stdout.write(SGR_DISABLE); + process.off("exit", cleanupOnExit); + }; + }, [stdin, setRawMode, isRawModeSupported, stdout, rootRef]); + + return {children}; +} + +/** + * True for ordinary typed text; false for escape/control/mouse sequences that + * land on the same stdin when mouse tracking is enabled. + */ +export function isTypedText(input: string): boolean { + if (!input) return false; + // eslint-disable-next-line no-control-regex + if (/[\u0000-\u001f]/.test(input)) return false; // control chars incl. ESC + if (input.includes("[<")) return false; // SGR mouse marker (ESC already stripped) + return true; +} + +/** Register a click zone for the given ref. No-op when there is no MouseProvider. */ +export function useClickable(ref: RefObject, onClick: () => void) { + const ctx = useContext(MouseContext); + const cbRef = useRef(onClick); + cbRef.current = onClick; + useEffect(() => { + if (!ctx) return; + return ctx.register({ ref, onClick: () => cbRef.current() }); + }, [ctx, ref]); +} + +/** Wrap children in a Box that fires `onClick` when clicked (mouse). */ +export function Clickable({ onClick, children }: { onClick: () => void; children: ReactNode }) { + const ref = useRef(null); + useClickable(ref, onClick); + return {children}; +} diff --git a/examples/openui-tui-chat/src/genui/state.tsx b/examples/openui-tui-chat/src/genui/state.tsx new file mode 100644 index 000000000..73d19bb92 --- /dev/null +++ b/examples/openui-tui-chat/src/genui/state.tsx @@ -0,0 +1,151 @@ +import { + createStore, + createStreamingParser, + evaluateElementProps, + type EvaluationContext, + type Library, + type ParseResult, + type Store, +} from "@openuidev/lang-core"; +import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react"; +import type { TuiContextValue } from "./context.js"; + +/** Unwrap the { value, componentType } wrapper the store keeps for form fields. */ +function unwrap(v: unknown): unknown { + if (v && typeof v === "object" && !Array.isArray(v) && "value" in (v as Record)) { + return (v as Record).value; + } + return v; +} + +export interface GenUiState { + /** Evaluated parse result (props resolved to concrete values). */ + result: ParseResult | null; + ctx: TuiContextValue; +} + +/** + * Trimmed, terminal-oriented port of react-lang's useOpenUIState: streaming + * parse → runtime store → evaluate props → action/field helpers. No queries or + * DOM. `messageId` scopes the parser to a single assistant turn. + */ +export function useGenUi( + library: Library, + messageId: string | null, + response: string | null, + isStreaming: boolean, + onSend: (content: string) => void, + interactive = true, +): GenUiState { + const onSendRef = useRef(onSend); + onSendRef.current = onSend; + + // Fresh streaming parser per assistant turn (set() resets on replacement anyway). + const sp = useMemo( + () => createStreamingParser(library.toJSONSchema(), library.root), + [library, messageId], + ); + + const parseResult = useMemo(() => { + if (!response) return null; + try { + return sp.set(response); + } catch { + return null; + } + }, [sp, response]); + + const store = useMemo(() => createStore(), [messageId]); + + useEffect(() => { + store.initialize(parseResult?.stateDeclarations ?? {}, {}); + }, [parseResult?.stateDeclarations, store]); + + const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot); + + const evaluationContext = useMemo( + () => ({ + getState: (name: string) => unwrap(store.get(name)), + resolveRef: () => undefined, + }), + [store], + ); + + const result = useMemo(() => { + if (!parseResult?.root) return parseResult; + try { + const root = evaluateElementProps(parseResult.root, { + ctx: evaluationContext, + library, + store, + errors: [], + }); + return { ...parseResult, root }; + } catch { + return parseResult; + } + // snapshot is a dependency so form edits re-evaluate reactive props. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [parseResult, evaluationContext, library, store, snapshot]); + + const getFieldValue = useCallback( + (formName, name) => { + if (!formName) return unwrap(store.get(name)); + const formData = store.get(formName); + if (!formData || typeof formData !== "object" || Array.isArray(formData)) return undefined; + return unwrap((formData as Record)[name]); + }, + [store], + ); + + const setFieldValue = useCallback( + (formName, componentType, name, value) => { + const wrapped = { value, componentType }; + if (!formName) { + store.set(name, wrapped); + return; + } + const raw = store.get(formName); + const formData = + raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as Record) : {}; + store.set(formName, { ...formData, [name]: wrapped }); + }, + [store], + ); + + const triggerAction = useCallback( + (userMessage, formName, action) => { + let message = userMessage; + const steps = + action && typeof action === "object" && Array.isArray((action as { steps?: unknown }).steps) + ? ((action as { steps: { type?: string; message?: string }[] }).steps) + : null; + if (steps) { + const toAssistant = steps.find((s) => s?.type === "continue_conversation" || typeof s?.message === "string"); + if (toAssistant?.message) message = toAssistant.message; + } + + let content = message; + if (formName) { + const raw = store.get(formName); + const values: Record = {}; + if (raw && typeof raw === "object" && !Array.isArray(raw)) { + for (const [k, v] of Object.entries(raw as Record)) values[k] = unwrap(v); + } + content = `${message}\n\n[form "${formName}" values: ${JSON.stringify(values)}]`; + } + onSendRef.current(content); + }, + [store], + ); + + const ctx = useMemo( + () => ({ library, interactive, triggerAction, getFieldValue, setFieldValue }), + [library, interactive, triggerAction, getFieldValue, setFieldValue], + ); + + // isStreaming currently only affects display in the app; kept for parity/future use. + void isStreaming; + + return { result, ctx }; +} diff --git a/examples/openui-tui-chat/src/llm.ts b/examples/openui-tui-chat/src/llm.ts new file mode 100644 index 000000000..31bc62eb8 --- /dev/null +++ b/examples/openui-tui-chat/src/llm.ts @@ -0,0 +1,38 @@ +import type { Message } from "@openuidev/react-headless"; +import { openAIMessageFormat } from "@openuidev/react-headless"; +import OpenAI from "openai"; +import type { ChatCompletionMessageParam } from "openai/resources/chat/completions"; + +/** + * Build a react-headless `processMessage` that streams from an OpenAI-compatible + * endpoint. Returns a Response wrapping the SDK's NDJSON ReadableStream, which + * pairs with `openAIReadableStreamAdapter`. + */ +export function makeProcessMessage(systemPrompt: string) { + const client = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + baseURL: process.env.OPENAI_BASE_URL || undefined, + }); + const model = process.env.OPENAI_MODEL || "gpt-5.5"; + + return async ({ + messages, + abortController, + }: { + threadId: string; + messages: Message[]; + abortController: AbortController; + }): Promise => { + const apiMessages: ChatCompletionMessageParam[] = [ + { role: "system", content: systemPrompt }, + ...(openAIMessageFormat.toApi(messages) as ChatCompletionMessageParam[]), + ]; + + const stream = await client.chat.completions.create( + { model, messages: apiMessages, stream: true }, + { signal: abortController.signal }, + ); + + return new Response(stream.toReadableStream() as unknown as ReadableStream); + }; +} diff --git a/examples/openui-tui-chat/tsconfig.json b/examples/openui-tui-chat/tsconfig.json new file mode 100644 index 000000000..8bb361f5a --- /dev/null +++ b/examples/openui-tui-chat/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 759229d5a..b337db1ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,7 +320,7 @@ importers: version: 0.0.45 '@ag-ui/mastra': specifier: ^1.0.1 - version: 1.0.1(mo2d2k6urt4bfbrhfsfscm4ram) + version: 1.0.1(dd1f7da5383fb7dbf1cafaa81aeabe83) '@mastra/core': specifier: 1.15.0 version: 1.15.0(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@4.3.6) @@ -703,6 +703,55 @@ importers: specifier: ~5.9.2 version: 5.9.3 + examples/openui-tui-chat: + dependencies: + '@openuidev/lang-core': + specifier: workspace:* + version: link:../../packages/lang-core + '@openuidev/react-headless': + specifier: workspace:* + version: link:../../packages/react-headless + ink: + specifier: ^5.1.0 + version: 5.2.1(@types/react@19.2.14)(react@18.3.1) + ink-big-text: + specifier: ^2.0.0 + version: 2.0.0(ink@5.2.1(@types/react@19.2.14)(react@18.3.1))(react@18.3.1) + ink-spinner: + specifier: ^5.0.0 + version: 5.0.0(ink@5.2.1(@types/react@19.2.14)(react@18.3.1))(react@18.3.1) + openai: + specifier: ^6.22.0 + version: 6.34.0(ws@8.20.0)(zod@4.3.6) + react: + specifier: ^18.3.1 + version: 18.3.1 + zod: + specifier: ^4.3.6 + version: 4.3.6 + zustand: + specifier: ^4.5.5 + version: 4.5.7(@types/react@19.2.14)(react@18.3.1) + devDependencies: + '@types/node': + specifier: ^20 + version: 20.19.35 + '@types/react': + specifier: ^19 + version: 19.2.14 + ink-testing-library: + specifier: ^4.0.0 + version: 4.0.0(@types/react@19.2.14) + tsx: + specifier: ^4.19.2 + version: 4.20.3 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.18 + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.35)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + examples/react-email: dependencies: '@openuidev/cli': @@ -1752,6 +1801,10 @@ packages: peerDependencies: vue: ^3.3.4 + '@alcalzone/ansi-tokenize@0.1.3': + resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==} + engines: {node: '>=14.13.1'} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -2420,10 +2473,12 @@ packages: '@copilotkitnext/agent@0.0.0-mme-ag-ui-0-0-46-20260227141603': resolution: {integrity: sha512-HAaAVKWD+WS1/GTxY6xLMj65Ro9evnOM5UC0DueTFwlmgCkuHPVE4rDveiGVaNc0x4X75tofi5Ul9g6Tb9FT/w==} engines: {node: '>=18'} + deprecated: Moved into @copilotkit/runtime. Import from '@copilotkit/runtime/v2'. '@copilotkitnext/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603': resolution: {integrity: sha512-c6vosi7xzKvyujmmwb4rNvAnlr656ybCrPeeX3kO1V70zrnUVkS4EXlSz1T0fGqqNgf5Tfqmssy3xqyLLZwgbQ==} engines: {node: '>=18'} + deprecated: Moved into @copilotkit/runtime. Import from '@copilotkit/runtime/v2'. peerDependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 @@ -2433,6 +2488,7 @@ packages: '@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603': resolution: {integrity: sha512-tbw37m+MgOO58dxYsXvGTN9YqHt6DPLMqtDEQftJHrUrQkNqXOxhOporx4p2DG0R+RiQqWrT+r44D2eRCQhlkA==} engines: {node: '>=18'} + deprecated: Use @copilotkit/shared instead. '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} @@ -3583,89 +3639,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -4078,72 +4150,84 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-gnu@16.1.6': resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-gnu@16.2.3': resolution: {integrity: sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@15.5.12': resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-arm64-musl@16.2.3': resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@15.5.12': resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-gnu@16.2.3': resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@15.5.12': resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-linux-x64-musl@16.2.3': resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@15.5.12': resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==} @@ -4580,48 +4664,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-C3zapJconWpl2Y7LR3GkRkH6jxpuV2iVUfkFcHT5Ffn4Zu7l88mZa2dhcfdULZDybN1Phka/P34YUzuskUUrXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-minify/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-2T/Bm+3/qTfuNS4gKSzL8qbiYk+ErHW2122CtDx+ilZAzvWcJ8IbqdZIbEWOlwwe03lESTxPwTBLFqVgQU2OeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-MKLjpldYkeoB4T+yAi4aIAb0waifxUjLcKkCUDmYAY3RqBJTvWK34KtfaKZL0IBMIXfD92CbKkcxQirDUS9Xcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-UFVcbPvKUStry6JffriobBp8BHtjmLLPl4bCY+JMxIn/Q3pykCpZzRwFTcDurG/kY8tm+uSNfKKdRNa5Nh9A7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-minify/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-B9GyPQ1NKbvpETVAMyJMfRlD3c6UJ7kiuFUAlx9LTYiQL+YIyT6vpuRlq1zgsXxavZluVrfeJv6x0owV4KDx4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-fXfhtr+WWBGNy4M5GjAF5vu/lpulR4Me34FjTyaK9nDrTZs7LM595UDsP1wliksqp4hD/KdoqHGmbCrC+6d4vA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-jFBgGbx1oLadb83ntJmy1dWlAHSQanXTS21G4PgkxyONmxZdZ/UMKr7KsADzMuoPsd2YhJHxzRpwJd9U+4BFBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-minify/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-nxPd9vx1vYz8IlIMdl9HFdOK/ood1H5hzbSFsyO8JU55tkcJoBL8TLCbuFf9pHpOy27l2gcPyV6z3p4eAcTH5Q==} @@ -4699,48 +4791,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-QagKTDF4lrz8bCXbUi39Uq5xs7C7itAseKm51f33U+Dyar9eJY/zGKqfME9mKLOiahX7Fc1J3xMWVS0AdDXLPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-RPddpcE/0xxWaommWy0c5i/JdrXcXAkxBS2GOrAUh5LKmyCh03hpJedOAWszG4ADsKQwoUQQ1/tZVGRhZIWtKA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-ur/WVZF9FSOiZGxyP+nfxZzuv6r5OJDYoVxJnUR7fM/hhXLh4V/be6rjbzm9KLCDBRwYCEKJtt+XXNccwd06IA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-ujGcAx8xAMvhy7X5sBFi3GXML1EtyORuJZ5z2T6UV3U416WgDX/4OCi3GnoteeenvxIf6JgP45B+YTHpt71vpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-hbsfKjUwRjcMZZvvmpZSc+qS0bHcHRu8aV/I3Ikn9BzOA0ZAgUE7ctPtce5zCU7bM8dnTLi4sJ1Pi9YHdx6Urw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-1QrTrf8rige7UPJrYuDKJLQOuJlgkt+nRSJLBMHWNm9TdivzP48HaK3f4q18EjNlglKtn03lgjMu4fryDm8X4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-gRvK6HPzF5ITRL68fqb2WYYs/hGviPIbkV84HWCgiJX+LkaOpp+HIHQl3zVZdyKHwopXToTbXbtx/oFjDjl8pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-parser/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-QPJvFbnnDZZY7xc+xpbIBWLThcGBakwaYA9vKV8b3+oS5MGfAZUoTFJcix5+Zg2Ri46sOfrUim6Y6jsKNcssAQ==} @@ -4824,48 +4924,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-ykxpPQp0eAcSmhy0Y3qKvdanHY4d8THPonDfmCoktUXb6r0X6qnjpJB3V+taN1wevW55bOEZd97kxtjTKjqhmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-transform/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-Rvspti4Kr7eq6zSrURK5WjscfWQPvmy/KjJZV45neRKW8RLonE3r9+NgrwSLGoHvQ3F24fbqlkplox1RtlhH5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-Dr2ZW9ZZ4l1eQ5JUEUY3smBh4JFPCPuybWaDZTLn3ADZjyd8ZtNXEjeMT8rQbbhbgSL9hEgbwaqraole3FNThQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-oD1Bnes1bIC3LVBSrWEoSUBj6fvatESPwAVWfJVGVQlqWuOs/ZBn1e4Nmbipo3KGPHK7DJY75r/j7CQCxhrOFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-transform/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-qT//IAPLvse844t99Kff5j055qEbXfwzWgvCMb0FyjisnB8foy25iHZxZIocNBe6qwrCYWUP1M8rNrB/WyfS1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-2YEO5X+KgNzFqRVO5dAkhjcI5gwxus4NSWVl/+cs2sI6P0MNPjqE3VWPawl4RTC11LvetiiZdHcujUCPM8aaUw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-3wqWbTSaIFZvDr1aqmTul4cg8PRWYh6VC52E8bLI7ytgS/BwJLW+sDUU2YaGIds4sAf/1yKeJRmudRCDPW9INg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-transform/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-Ebxx6NPqhzlrjvx4+PdSqbOq+li0f7X59XtJljDghkbJsbnkHvhLmPR09ifHt5X32UlZN63ekjwcg/nbmHLLlA==} @@ -4925,36 +5033,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-wasm@2.5.6': resolution: {integrity: sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==} @@ -6883,6 +6997,7 @@ packages: '@rolldown/binding-darwin-arm64@1.0.0-rc.16': resolution: {integrity: sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==} engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.0.0-rc.12': @@ -6908,36 +7023,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} @@ -7107,121 +7228,145 @@ packages: resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-gnueabihf@4.60.1': resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.43.0': resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.43.0': resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.43.0': resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.43.0': resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.43.0': resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.43.0': resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.43.0': resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.43.0': resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.43.0': resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} @@ -7752,48 +7897,56 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.1': resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.1': resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.1': resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.1': resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} @@ -7876,24 +8029,28 @@ packages: engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@takumi-rs/core-linux-arm64-musl@0.68.17': resolution: {integrity: sha512-4CiEF518wDnujF0fjql2XN6uO+OXl0svy0WgAF2656dCx2gJtWscaHytT2rsQ0ZmoFWE0dyWcDW1g/FBVPvuvA==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@takumi-rs/core-linux-x64-gnu@0.68.17': resolution: {integrity: sha512-jm8lTe2E6Tfq2b97GJC31TWK1JAEv+MsVbvL9DCLlYcafgYFlMXDUnOkZFMjlrmh0HcFAYDaBkniNDgIQfXqzg==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@takumi-rs/core-linux-x64-musl@0.68.17': resolution: {integrity: sha512-nbdzQgC4ywzltDDV1fer1cKswwGE+xXZHdDiacdd7RM5XBng209Bmo3j1iv9dsX+4xXhByzCCGbxdWhhHqVXmw==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@takumi-rs/core-win32-arm64-msvc@0.68.17': resolution: {integrity: sha512-kE4F0LRmuhSwiNkFG7dTY9ID8+B7zb97QedyN/IO2fBJmRQDkqCGcip2gloh8YPPhCuKGjCqqqh2L+Tg9PKW7w==} @@ -8369,41 +8526,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -8845,6 +9010,10 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -8989,6 +9158,10 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + auto-bind@5.0.1: + resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + autoprefixer@10.4.27: resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} engines: {node: ^10 || ^12 || >=14} @@ -9301,6 +9474,11 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + cfonts@3.3.1: + resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==} + engines: {node: '>=10'} + hasBin: true + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -9437,10 +9615,18 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + cli-cursor@2.1.0: resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} engines: {node: '>=4'} + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-highlight@2.1.11: resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} @@ -9454,6 +9640,10 @@ packages: resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -9488,6 +9678,10 @@ packages: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} + code-excerpt@4.0.0: + resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -9618,6 +9812,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convert-to-spaces@2.0.1: + resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} @@ -10049,6 +10247,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -10306,6 +10508,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -11238,6 +11443,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + hast-util-from-dom@5.0.1: resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} @@ -11479,6 +11688,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -11493,6 +11706,42 @@ packages: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ink-big-text@2.0.0: + resolution: {integrity: sha512-Juzqv+rIOLGuhMJiE50VtS6dg6olWfzFdL7wsU/EARSL5Eaa5JNXMogMBm9AkjgzO2Y3UwWCOh87jbhSn8aNdw==} + engines: {node: '>=14.16'} + peerDependencies: + ink: '>=4' + react: '>=18' + + ink-spinner@5.0.0: + resolution: {integrity: sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==} + engines: {node: '>=14.16'} + peerDependencies: + ink: '>=4.0.0' + react: '>=18.0.0' + + ink-testing-library@4.0.0: + resolution: {integrity: sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=18.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + ink@5.2.1: + resolution: {integrity: sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=18.0.0' + react: '>=18.0.0' + react-devtools-core: ^4.19.1 + peerDependenciesMeta: + '@types/react': + optional: true + react-devtools-core: + optional: true + inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} @@ -11537,6 +11786,10 @@ packages: iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-accessor-descriptor@1.0.2: + resolution: {integrity: sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==} + engines: {node: '>= 0.4'} + is-alphabetical@1.0.4: resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} @@ -11573,6 +11826,9 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-bun-module@2.0.0: resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} @@ -11584,6 +11840,10 @@ packages: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + is-data-view@1.0.2: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} @@ -11598,6 +11858,10 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-descriptor@1.0.4: + resolution: {integrity: sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -11624,6 +11888,14 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.0: resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} @@ -11638,6 +11910,11 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + is-in-ssh@1.0.0: resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} engines: {node: '>=20'} @@ -11670,6 +11947,10 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -11952,6 +12233,10 @@ packages: khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -12098,48 +12383,56 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.31.1: resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.31.1: resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.31.1: resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.31.1: resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} @@ -12724,6 +13017,10 @@ packages: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} engines: {node: '>=4'} + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -13132,6 +13429,10 @@ packages: resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} engines: {node: '>=4'} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} @@ -13316,6 +13617,10 @@ packages: partial-json@0.1.7: resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + patch-console@2.0.0: + resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -13983,6 +14288,12 @@ packages: react-promise-suspense@0.3.4: resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} + react-reconciler@0.29.2: + resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^18.3.1 + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -14053,6 +14364,10 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + react@19.2.0: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} @@ -14283,6 +14598,10 @@ packages: resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} engines: {node: '>=4'} + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -14417,6 +14736,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -14577,6 +14899,14 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + slugify@1.6.8: resolution: {integrity: sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA==} engines: {node: '>=8.0.0'} @@ -14747,6 +15077,10 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom-string@1.0.0: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} @@ -15175,6 +15509,10 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-fest@5.5.0: resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} engines: {node: '>=20'} @@ -15991,6 +16329,15 @@ packages: engines: {node: '>=8'} hasBin: true + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + window-size@1.1.1: + resolution: {integrity: sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==} + engines: {node: '>= 0.10.0'} + hasBin: true + wonka@6.3.5: resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==} @@ -16040,18 +16387,6 @@ packages: utf-8-validate: optional: true - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.20.0: resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} @@ -16155,6 +16490,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + youch-core@0.3.3: resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} @@ -16294,12 +16632,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@1.0.1(mo2d2k6urt4bfbrhfsfscm4ram)': + '@ag-ui/mastra@1.0.1(dd1f7da5383fb7dbf1cafaa81aeabe83)': dependencies: '@ag-ui/client': 0.0.49 '@ag-ui/core': 0.0.45 '@ai-sdk/ui-utils': 1.2.11(zod@4.3.6) - '@copilotkit/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.49)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.46)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@mastra/client-js': 1.11.2(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@4.3.6) '@mastra/core': 1.15.0(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@4.3.6) rxjs: 7.8.1 @@ -16491,6 +16829,11 @@ snapshots: transitivePeerDependencies: - zod + '@alcalzone/ansi-tokenize@0.1.3': + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + '@alloc/quick-lru@5.2.0': {} '@andrewbranch/untar.js@1.0.3': {} @@ -16498,7 +16841,7 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.6.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 '@arethetypeswrong/cli@0.18.2': dependencies: @@ -17251,7 +17594,7 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@copilotkit/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.49)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@copilotkit/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.46)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 @@ -17260,7 +17603,7 @@ snapshots: '@ai-sdk/openai': 2.0.101(zod@3.25.76) '@copilotkit/shared': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/core@0.0.46) '@copilotkitnext/agent': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@cfworker/json-schema@4.1.1) - '@copilotkitnext/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.49)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603) + '@copilotkitnext/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.46)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603) '@graphql-yoga/plugin-defer-stream': 3.19.0(graphql-yoga@5.19.0(graphql@16.13.2))(graphql@16.13.2) '@hono/node-server': 1.19.12(hono@4.12.9) '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@4.3.6)) @@ -17320,11 +17663,11 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@copilotkitnext/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.49)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)': + '@copilotkitnext/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.46)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)': dependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 - '@ag-ui/encoder': 0.0.49 + '@ag-ui/encoder': 0.0.46 '@copilotkitnext/shared': 0.0.0-mme-ag-ui-0-0-46-20260227141603 cors: 2.8.6 express: 4.22.1 @@ -18662,7 +19005,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -19393,7 +19736,7 @@ snapshots: rc9: 3.0.0 std-env: 3.10.0 - '@nuxt/vite-builder@3.21.2(thirvmyz7u7sotpvl5josuvsem)': + '@nuxt/vite-builder@3.21.2(cda632d3acfa8c47554ee5be1e146aef)': dependencies: '@nuxt/kit': 3.21.2(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.60.1) @@ -20059,7 +20402,7 @@ snapshots: '@parcel/watcher-wasm@2.5.6': dependencies: is-glob: 4.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 '@parcel/watcher-win32-arm64@2.5.1': optional: true @@ -23498,10 +23841,10 @@ snapshots: '@rollup/pluginutils': 5.2.0(rollup@4.60.1) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.4) is-reference: 1.2.1 magic-string: 0.30.21 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: rollup: 4.60.1 @@ -24927,7 +25270,7 @@ snapshots: glob: 13.0.6 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 - picomatch: 4.0.3 + picomatch: 4.0.4 resolve-from: 5.0.0 transitivePeerDependencies: - encoding @@ -25003,6 +25346,14 @@ snapshots: optionalDependencies: vite: 6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + '@vitest/mocker@4.0.18(vite@6.4.1(@types/node@20.19.35)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.1(@types/node@20.19.35)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + '@vitest/mocker@4.0.18(vite@6.4.1(@types/node@22.15.32)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.0.18 @@ -25222,7 +25573,7 @@ snapshots: alien-signals: 3.1.2 muggle-string: 0.4.1 path-browserify: 1.0.1 - picomatch: 4.0.3 + picomatch: 4.0.4 '@vue/reactivity@3.5.31': dependencies: @@ -25481,6 +25832,8 @@ snapshots: ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -25655,6 +26008,8 @@ snapshots: atomic-sleep@1.0.0: {} + auto-bind@5.0.1: {} + autoprefixer@10.4.27(postcss@8.5.8): dependencies: browserslist: 4.28.1 @@ -26031,6 +26386,11 @@ snapshots: ccount@2.0.1: {} + cfonts@3.3.1: + dependencies: + supports-color: 8.1.1 + window-size: 1.1.1 + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -26171,10 +26531,16 @@ snapshots: dependencies: clsx: 2.1.1 + cli-boxes@3.0.0: {} + cli-cursor@2.1.0: dependencies: restore-cursor: 2.0.0 + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + cli-highlight@2.1.11: dependencies: chalk: 4.1.2 @@ -26192,6 +26558,11 @@ snapshots: optionalDependencies: '@colors/colors': 1.5.0 + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -26217,7 +26588,7 @@ snapshots: cliui@9.0.1: dependencies: string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 wrap-ansi: 9.0.2 clone@1.0.4: {} @@ -26226,6 +26597,10 @@ snapshots: cluster-key-slot@1.1.2: {} + code-excerpt@4.0.0: + dependencies: + convert-to-spaces: 2.0.1 + collapse-white-space@2.1.0: {} color-convert@1.9.3: @@ -26346,6 +26721,8 @@ snapshots: convert-source-map@2.0.0: {} + convert-to-spaces@2.0.1: {} + cookie-es@1.2.2: {} cookie-es@2.0.0: {} @@ -26782,6 +27159,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.4 + defu@6.1.4: {} delaunator@5.1.0: @@ -27056,6 +27437,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.50.0: {} + esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 @@ -27235,8 +27618,8 @@ snapshots: '@next/eslint-plugin-next': 16.1.6 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.29.0(jiti@2.6.1)) @@ -27255,8 +27638,8 @@ snapshots: '@next/eslint-plugin-next': 16.2.3 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.29.0(jiti@2.6.1)) @@ -27282,7 +27665,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -27293,22 +27676,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -27319,7 +27702,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -28326,6 +28709,10 @@ snapshots: dependencies: function-bind: 1.1.2 + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + hast-util-from-dom@5.0.1: dependencies: '@types/hast': 3.0.4 @@ -28655,6 +29042,8 @@ snapshots: indent-string@4.0.0: {} + indent-string@5.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -28666,6 +29055,56 @@ snapshots: ini@4.1.1: {} + ink-big-text@2.0.0(ink@5.2.1(@types/react@19.2.14)(react@18.3.1))(react@18.3.1): + dependencies: + cfonts: 3.3.1 + ink: 5.2.1(@types/react@19.2.14)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + + ink-spinner@5.0.0(ink@5.2.1(@types/react@19.2.14)(react@18.3.1))(react@18.3.1): + dependencies: + cli-spinners: 2.9.2 + ink: 5.2.1(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + + ink-testing-library@4.0.0(@types/react@19.2.14): + optionalDependencies: + '@types/react': 19.2.14 + + ink@5.2.1(@types/react@19.2.14)(react@18.3.1): + dependencies: + '@alcalzone/ansi-tokenize': 0.1.3 + ansi-escapes: 7.3.0 + ansi-styles: 6.2.1 + auto-bind: 5.0.1 + chalk: 5.6.2 + cli-boxes: 3.0.0 + cli-cursor: 4.0.0 + cli-truncate: 4.0.0 + code-excerpt: 4.0.0 + es-toolkit: 1.50.0 + indent-string: 5.0.0 + is-in-ci: 1.0.0 + patch-console: 2.0.0 + react: 18.3.1 + react-reconciler: 0.29.2(react@18.3.1) + scheduler: 0.23.2 + signal-exit: 3.0.7 + slice-ansi: 7.1.2 + stack-utils: 2.0.6 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + ws: 8.20.0 + yoga-layout: 3.2.1 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + inline-style-parser@0.2.4: {} inline-style-prefixer@7.0.1: @@ -28718,6 +29157,10 @@ snapshots: iron-webcrypto@1.2.1: {} + is-accessor-descriptor@1.0.2: + dependencies: + hasown: 2.0.4 + is-alphabetical@1.0.4: {} is-alphabetical@2.0.1: {} @@ -28764,6 +29207,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-buffer@1.1.6: {} + is-bun-module@2.0.0: dependencies: semver: 7.7.4 @@ -28774,6 +29219,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.2: dependencies: call-bound: 1.0.4 @@ -28789,6 +29238,11 @@ snapshots: is-decimal@2.0.1: {} + is-descriptor@1.0.4: + dependencies: + is-accessor-descriptor: 1.0.2 + is-data-descriptor: 1.0.1 + is-docker@2.2.1: {} is-docker@3.0.0: {} @@ -28803,6 +29257,12 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + is-generator-function@1.1.0: dependencies: call-bound: 1.0.4 @@ -28818,6 +29278,8 @@ snapshots: is-hexadecimal@2.0.1: {} + is-in-ci@1.0.0: {} + is-in-ssh@1.0.0: {} is-inside-container@1.0.0: @@ -28842,6 +29304,10 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + is-number@7.0.0: {} is-path-inside@4.0.0: {} @@ -29095,7 +29561,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.2 + ws: 8.20.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -29155,6 +29621,10 @@ snapshots: khroma@2.1.0: {} + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + kind-of@6.0.3: {} kleur@3.0.3: {} @@ -30392,6 +30862,8 @@ snapshots: mimic-fn@1.2.0: {} + mimic-fn@2.1.0: {} + mimic-fn@4.0.0: {} min-indent@1.0.1: {} @@ -30785,7 +31257,7 @@ snapshots: '@nuxt/nitro-server': 3.21.2(db0@0.3.4)(ioredis@5.10.1)(magicast@0.5.2)(nuxt@3.21.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@parcel/watcher@2.5.1)(@types/node@25.3.2)(@vue/compiler-sfc@3.5.31)(cac@6.7.14)(db0@0.3.4)(eslint@9.29.0(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(rollup@4.60.1))(rollup@4.60.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3))(vue-tsc@2.2.12(typescript@5.9.3))(yaml@2.8.3))(rolldown@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(typescript@5.9.3) '@nuxt/schema': 3.21.2 '@nuxt/telemetry': 2.7.0(@nuxt/kit@3.21.2(magicast@0.5.2)) - '@nuxt/vite-builder': 3.21.2(thirvmyz7u7sotpvl5josuvsem) + '@nuxt/vite-builder': 3.21.2(cda632d3acfa8c47554ee5be1e146aef) '@unhead/vue': 2.1.13(vue@3.5.31(typescript@5.9.3)) '@vue/shared': 3.5.31 c12: 3.3.3(magicast@0.5.2) @@ -31011,6 +31483,10 @@ snapshots: dependencies: mimic-fn: 1.2.0 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 @@ -31280,6 +31756,8 @@ snapshots: partial-json@0.1.7: {} + patch-console@2.0.0: {} + path-browserify@1.0.1: {} path-data-parser@0.1.0: {} @@ -32162,6 +32640,12 @@ snapshots: dependencies: fast-deep-equal: 2.0.1 + react-reconciler@0.29.2(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + react-refresh@0.14.2: {} react-refresh@0.17.0: {} @@ -32326,6 +32810,10 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + react@19.2.0: {} react@19.2.3: {} @@ -32669,6 +33157,11 @@ snapshots: onetime: 2.0.1 signal-exit: 3.0.7 + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + retry@0.13.1: {} reusify@1.1.0: {} @@ -32724,7 +33217,7 @@ snapshots: rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(rollup@4.60.1): dependencies: open: 11.0.0 - picomatch: 4.0.3 + picomatch: 4.0.4 source-map: 0.7.6 yargs: 18.0.0 optionalDependencies: @@ -32870,6 +33363,10 @@ snapshots: dependencies: xmlchars: 2.2.0 + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + scheduler@0.27.0: {} schema-utils@4.3.2: @@ -33111,6 +33608,16 @@ snapshots: slash@5.1.0: {} + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.1.0 + slugify@1.6.8: {} smob@1.6.1: {} @@ -33230,7 +33737,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 string-width@7.2.0: dependencies: @@ -33311,7 +33818,11 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 strip-bom-string@1.0.0: {} @@ -33731,7 +34242,7 @@ snapshots: tsx@4.20.3: dependencies: esbuild: 0.25.12 - get-tsconfig: 4.10.1 + get-tsconfig: 4.13.7 optionalDependencies: fsevents: 2.3.3 @@ -33749,6 +34260,8 @@ snapshots: type-fest@0.7.1: {} + type-fest@4.41.0: {} + type-fest@5.5.0: dependencies: tagged-tag: 1.0.0 @@ -33968,7 +34481,7 @@ snapshots: unplugin-utils@0.3.1: dependencies: pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 unplugin-vue-router@0.19.2(@vue/compiler-sfc@3.5.31)(vue-router@4.6.4(vue@3.5.31(typescript@5.9.3)))(vue@3.5.31(typescript@5.9.3)): dependencies: @@ -34004,7 +34517,7 @@ snapshots: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.16.0 - picomatch: 4.0.3 + picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 unplugin@3.0.0: @@ -34139,14 +34652,18 @@ snapshots: dependencies: react: 19.2.3 - use-sync-external-store@1.5.0(react@19.2.4): + use-sync-external-store@1.6.0(react@18.3.1): dependencies: - react: 19.2.4 + react: 18.3.1 use-sync-external-store@1.6.0(react@19.2.3): dependencies: react: 19.2.3 + use-sync-external-store@1.6.0(react@19.2.4): + dependencies: + react: 19.2.4 + util-deprecate@1.0.2: {} util@0.12.5: @@ -34271,7 +34788,7 @@ snapshots: chokidar: 4.0.3 npm-run-path: 6.0.0 picocolors: 1.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) @@ -34321,6 +34838,24 @@ snapshots: sass: 1.89.2 terser: 5.43.0 + vite@6.4.1(@types/node@20.19.35)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.43.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.35 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + sass: 1.89.2 + terser: 5.43.0 + tsx: 4.20.3 + yaml: 2.8.3 + vite@6.4.1(@types/node@22.15.32)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): dependencies: esbuild: 0.25.12 @@ -34360,8 +34895,8 @@ snapshots: vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 rollup: 4.60.1 tinyglobby: 0.2.15 @@ -34422,6 +34957,45 @@ snapshots: - tsx - yaml + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.35)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@6.4.1(@types/node@20.19.35)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.4 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 6.4.1(@types/node@20.19.35)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 20.19.35 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.15.32)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.18 @@ -34436,10 +35010,10 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 6.4.1(@types/node@22.15.32)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) @@ -34475,10 +35049,10 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) @@ -34692,6 +35266,15 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + window-size@1.1.1: + dependencies: + define-property: 1.0.0 + is-number: 3.0.0 + wonka@6.3.5: {} word-wrap@1.2.5: {} @@ -34706,7 +35289,7 @@ snapshots: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 wrap-ansi@9.0.2: dependencies: @@ -34727,8 +35310,6 @@ snapshots: ws@7.5.10: {} - ws@8.18.2: {} - ws@8.20.0: {} wsl-utils@0.1.0: @@ -34811,6 +35392,8 @@ snapshots: yoctocolors@2.1.2: {} + yoga-layout@3.2.1: {} + youch-core@0.3.3: dependencies: '@poppinss/exception': 1.2.3 @@ -34856,9 +35439,16 @@ snapshots: zod@4.3.6: {} + zustand@4.5.7(@types/react@19.2.14)(react@18.3.1): + dependencies: + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + react: 18.3.1 + zustand@4.5.7(@types/react@19.2.14)(react@19.2.4): dependencies: - use-sync-external-store: 1.5.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 react: 19.2.4