From 5d67f677bff47f7c070f2927b787cd8a12890bc5 Mon Sep 17 00:00:00 2001 From: Tarquinen Date: Sun, 8 Mar 2026 19:38:11 -0400 Subject: [PATCH 01/50] feat(tui): add dcp sidebar widget --- tui/commands.ts | 19 +++ tui/components/metric-row.tsx | 23 +++ tui/components/screen.tsx | 35 ++++ tui/components/section.tsx | 29 ++++ tui/data/context.ts | 289 +++++++++++++++++++++++++++++++++ tui/dcp-probe.tsx | 1 + tui/index.tsx | 30 ++++ tui/package.json | 12 ++ tui/routes/panel.tsx | 82 ++++++++++ tui/shared/config.ts | 14 ++ tui/shared/names.ts | 13 ++ tui/shared/navigation.ts | 47 ++++++ tui/shared/theme.ts | 48 ++++++ tui/shared/types.ts | 54 +++++++ tui/slots/sidebar-top.tsx | 291 ++++++++++++++++++++++++++++++++++ tui/tsconfig.json | 14 ++ 16 files changed, 1001 insertions(+) create mode 100644 tui/commands.ts create mode 100644 tui/components/metric-row.tsx create mode 100644 tui/components/screen.tsx create mode 100644 tui/components/section.tsx create mode 100644 tui/data/context.ts create mode 100644 tui/dcp-probe.tsx create mode 100644 tui/index.tsx create mode 100644 tui/package.json create mode 100644 tui/routes/panel.tsx create mode 100644 tui/shared/config.ts create mode 100644 tui/shared/names.ts create mode 100644 tui/shared/navigation.ts create mode 100644 tui/shared/theme.ts create mode 100644 tui/shared/types.ts create mode 100644 tui/slots/sidebar-top.tsx create mode 100644 tui/tsconfig.json diff --git a/tui/commands.ts b/tui/commands.ts new file mode 100644 index 00000000..5971ae2a --- /dev/null +++ b/tui/commands.ts @@ -0,0 +1,19 @@ +// @ts-nocheck +import type { TuiApi } from "@opencode-ai/plugin/tui" +import { openPanel } from "./shared/navigation" +import type { DcpRouteNames, DcpTuiConfig } from "./shared/types" + +export const registerCommands = (api: TuiApi, config: DcpTuiConfig, names: DcpRouteNames) => { + api.command.register(() => [ + { + title: `${config.label} panel`, + value: names.commands.panel, + description: "Open the DCP placeholder panel", + category: config.label, + slash: { + name: "dcp-panel", + }, + onSelect: () => openPanel(api, names, "command"), + }, + ]) +} diff --git a/tui/components/metric-row.tsx b/tui/components/metric-row.tsx new file mode 100644 index 00000000..2474905d --- /dev/null +++ b/tui/components/metric-row.tsx @@ -0,0 +1,23 @@ +/** @jsxImportSource @opentui/solid */ +import type { DcpPalette } from "../shared/theme" + +const pad = (value: string, width: number) => { + if (value.length >= width) return value + return value.padEnd(width, " ") +} + +export const MetricRow = (props: { + palette: DcpPalette + label: string + value: string + tone?: "text" | "muted" | "accent" +}) => { + const fg = + props.tone === "accent" + ? props.palette.accent + : props.tone === "muted" + ? props.palette.muted + : props.palette.text + + return {`${pad(props.label, 18)} ${props.value}`} +} diff --git a/tui/components/screen.tsx b/tui/components/screen.tsx new file mode 100644 index 00000000..5025c1bc --- /dev/null +++ b/tui/components/screen.tsx @@ -0,0 +1,35 @@ +/** @jsxImportSource @opentui/solid */ +import type { DcpPalette } from "../shared/theme" + +export const Screen = (props: { + palette: DcpPalette + title: string + subtitle?: string + footer?: string + children?: unknown +}) => { + return ( + + + + + {props.title} + + {props.subtitle && {props.subtitle}} + + + {props.children} + + {props.footer && {props.footer}} + + + ) +} diff --git a/tui/components/section.tsx b/tui/components/section.tsx new file mode 100644 index 00000000..546152a5 --- /dev/null +++ b/tui/components/section.tsx @@ -0,0 +1,29 @@ +/** @jsxImportSource @opentui/solid */ +import type { DcpPalette } from "../shared/theme" + +export const Section = (props: { + palette: DcpPalette + title: string + subtitle?: string + children?: unknown +}) => { + return ( + + + {props.title} + + {props.subtitle && {props.subtitle}} + + {props.children} + + + ) +} diff --git a/tui/data/context.ts b/tui/data/context.ts new file mode 100644 index 00000000..5e64e7e9 --- /dev/null +++ b/tui/data/context.ts @@ -0,0 +1,289 @@ +import type { AssistantMessage, TextPart, ToolPart } from "@opencode-ai/sdk/v2" +import { Logger } from "../../lib/logger" +import { isIgnoredUserMessage } from "../../lib/messages/utils" +import { countTokens } from "../../lib/strategies/utils" +import { + createSessionState, + loadSessionState, + type SessionState, + type WithParts, +} from "../../lib/state" +import { + findLastCompactionTimestamp, + loadPruneMap, + loadPruneMessagesState, +} from "../../lib/state/utils" +import { isMessageCompacted } from "../../lib/shared-utils" +import type { DcpContextBreakdown, DcpContextSnapshot, DcpTuiClient } from "../shared/types" + +const logger = new Logger(false) +const snapshotCache = new Map() +const inflightSnapshots = new Map>() +const CACHE_TTL_MS = 5000 + +const emptyBreakdown = (): DcpContextBreakdown => ({ + system: 0, + user: 0, + assistant: 0, + tools: 0, + toolCount: 0, + toolsInContextCount: 0, + prunedTokens: 0, + prunedToolCount: 0, + prunedMessageCount: 0, + total: 0, + messageCount: 0, +}) + +const createSnapshot = (sessionID?: string, notes: string[] = []): DcpContextSnapshot => ({ + sessionID, + breakdown: emptyBreakdown(), + persisted: { + available: false, + activeBlockCount: 0, + activeBlockTopics: [], + }, + notes, + loadedAt: Date.now(), +}) + +const buildState = async ( + sessionID: string, + messages: WithParts[], +): Promise<{ state: SessionState; persisted: Awaited> }> => { + const state = createSessionState() + const persisted = await loadSessionState(sessionID, logger) + + state.sessionId = sessionID + state.lastCompaction = findLastCompactionTimestamp(messages) + state.stats.pruneTokenCounter = 0 + state.stats.totalPruneTokens = persisted?.stats?.totalPruneTokens || 0 + state.prune.tools = loadPruneMap(persisted?.prune?.tools) + state.prune.messages = loadPruneMessagesState(persisted?.prune?.messages) + + return { + state, + persisted, + } +} + +const analyzeTokens = (state: SessionState, messages: WithParts[]): DcpContextBreakdown => { + const breakdown = emptyBreakdown() + breakdown.prunedTokens = state.stats.totalPruneTokens + breakdown.messageCount = messages.length + + let firstAssistant: AssistantMessage | undefined + for (const msg of messages) { + if (msg.info.role !== "assistant") continue + const assistantInfo = msg.info as AssistantMessage + if ( + assistantInfo.tokens?.input > 0 || + assistantInfo.tokens?.cache?.read > 0 || + assistantInfo.tokens?.cache?.write > 0 + ) { + firstAssistant = assistantInfo + break + } + } + + let lastAssistant: AssistantMessage | undefined + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i] + if (msg.info.role !== "assistant") continue + const assistantInfo = msg.info as AssistantMessage + if (assistantInfo.tokens?.output > 0) { + lastAssistant = assistantInfo + break + } + } + + const apiInput = lastAssistant?.tokens?.input || 0 + const apiOutput = lastAssistant?.tokens?.output || 0 + const apiReasoning = lastAssistant?.tokens?.reasoning || 0 + const apiCacheRead = lastAssistant?.tokens?.cache?.read || 0 + const apiCacheWrite = lastAssistant?.tokens?.cache?.write || 0 + breakdown.total = apiInput + apiOutput + apiReasoning + apiCacheRead + apiCacheWrite + + const userTextParts: string[] = [] + const toolInputParts: string[] = [] + const toolOutputParts: string[] = [] + const allToolIds = new Set() + const activeToolIds = new Set() + const prunedByMessageToolIds = new Set() + const allMessageIds = new Set() + + let firstUserText = "" + let foundFirstUser = false + + for (const msg of messages) { + allMessageIds.add(msg.info.id) + const parts = Array.isArray(msg.parts) ? msg.parts : [] + const compacted = isMessageCompacted(state, msg) + const pruneEntry = state.prune.messages.byMessageId.get(msg.info.id) + const messagePruned = !!pruneEntry && pruneEntry.activeBlockIds.length > 0 + const ignoredUser = msg.info.role === "user" && isIgnoredUserMessage(msg) + + for (const part of parts) { + if (part.type === "tool") { + const toolPart = part as ToolPart + if (toolPart.callID) { + allToolIds.add(toolPart.callID) + if (!compacted) activeToolIds.add(toolPart.callID) + if (messagePruned) prunedByMessageToolIds.add(toolPart.callID) + } + + const toolPruned = toolPart.callID && state.prune.tools.has(toolPart.callID) + if (!compacted && !toolPruned) { + if (toolPart.state?.input) { + const inputText = + typeof toolPart.state.input === "string" + ? toolPart.state.input + : JSON.stringify(toolPart.state.input) + toolInputParts.push(inputText) + } + if (toolPart.state?.status === "completed" && toolPart.state?.output) { + const outputText = + typeof toolPart.state.output === "string" + ? toolPart.state.output + : JSON.stringify(toolPart.state.output) + toolOutputParts.push(outputText) + } + } + continue + } + + if (part.type === "text" && msg.info.role === "user" && !compacted && !ignoredUser) { + const textPart = part as TextPart + const text = textPart.text || "" + userTextParts.push(text) + if (!foundFirstUser) firstUserText += text + } + } + + if (msg.info.role === "user" && !ignoredUser && !foundFirstUser) { + foundFirstUser = true + } + } + + const prunedByToolIds = new Set() + for (const toolID of allToolIds) { + if (state.prune.tools.has(toolID)) prunedByToolIds.add(toolID) + } + + const prunedToolIds = new Set([...prunedByToolIds, ...prunedByMessageToolIds]) + breakdown.toolCount = allToolIds.size + breakdown.toolsInContextCount = [...activeToolIds].filter( + (id) => !prunedByToolIds.has(id), + ).length + breakdown.prunedToolCount = prunedToolIds.size + + for (const [messageID, entry] of state.prune.messages.byMessageId) { + if (allMessageIds.has(messageID) && entry.activeBlockIds.length > 0) { + breakdown.prunedMessageCount += 1 + } + } + + const firstUserTokens = countTokens(firstUserText) + breakdown.user = countTokens(userTextParts.join("\n")) + const toolInputTokens = countTokens(toolInputParts.join("\n")) + const toolOutputTokens = countTokens(toolOutputParts.join("\n")) + + if (firstAssistant) { + const firstInput = + (firstAssistant.tokens?.input || 0) + + (firstAssistant.tokens?.cache?.read || 0) + + (firstAssistant.tokens?.cache?.write || 0) + breakdown.system = Math.max(0, firstInput - firstUserTokens) + } + + breakdown.tools = toolInputTokens + toolOutputTokens + breakdown.assistant = Math.max( + 0, + breakdown.total - breakdown.system - breakdown.user - breakdown.tools, + ) + + return breakdown +} + +export const loadContextSnapshot = async ( + client: DcpTuiClient, + sessionID?: string, +): Promise => { + if (!sessionID) { + return createSnapshot(undefined, ["Open this panel from a session to inspect DCP context."]) + } + + const messagesResult = await client.session.messages({ sessionID }) + const messages = Array.isArray(messagesResult.data) + ? (messagesResult.data as WithParts[]) + : ([] as WithParts[]) + + const { state, persisted } = await buildState(sessionID, messages) + const breakdown = analyzeTokens(state, messages) + + const topics = Array.from(state.prune.messages.activeBlockIds) + .map((blockID) => state.prune.messages.blocksById.get(blockID)) + .filter((block): block is NonNullable => !!block) + .map((block) => block.topic) + .filter((topic) => !!topic) + .slice(0, 3) + + const notes: string[] = [] + if (persisted) { + notes.push("Using live session messages plus persisted DCP state.") + } else { + notes.push("No saved DCP state found for this session yet.") + } + if (messages.length === 0) { + notes.push("This session does not have any messages yet.") + } + + return { + sessionID, + breakdown, + persisted: { + available: !!persisted, + activeBlockCount: state.prune.messages.activeBlockIds.size, + activeBlockTopics: topics, + lastUpdated: persisted?.lastUpdated, + }, + notes, + loadedAt: Date.now(), + } +} + +export const peekContextSnapshot = (sessionID?: string): DcpContextSnapshot | undefined => { + if (!sessionID) return undefined + return snapshotCache.get(sessionID) +} + +export const loadContextSnapshotCached = async ( + client: DcpTuiClient, + sessionID?: string, +): Promise => { + if (!sessionID) { + return createSnapshot(undefined, ["Open this panel from a session to inspect DCP context."]) + } + + const cached = snapshotCache.get(sessionID) + if (cached && Date.now() - cached.loadedAt < CACHE_TTL_MS) { + return cached + } + + const inflight = inflightSnapshots.get(sessionID) + if (inflight) { + return inflight + } + + const request = loadContextSnapshot(client, sessionID) + .then((snapshot) => { + snapshotCache.set(sessionID, snapshot) + return snapshot + }) + .finally(() => { + inflightSnapshots.delete(sessionID) + }) + + inflightSnapshots.set(sessionID, request) + return request +} diff --git a/tui/dcp-probe.tsx b/tui/dcp-probe.tsx new file mode 100644 index 00000000..a84c7b34 --- /dev/null +++ b/tui/dcp-probe.tsx @@ -0,0 +1 @@ +export { default } from "./index" diff --git a/tui/index.tsx b/tui/index.tsx new file mode 100644 index 00000000..6f0c5f3b --- /dev/null +++ b/tui/index.tsx @@ -0,0 +1,30 @@ +// @ts-nocheck +/** @jsxImportSource @opentui/solid */ +import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import { registerCommands } from "./commands" +import { createPanelRoute } from "./routes/panel" +import { createSidebarTopSlot } from "./slots/sidebar-top" +import { readConfig } from "./shared/config" +import { createNames } from "./shared/names" + +const tui = async (input: TuiPluginInput, options?: Record) => { + if (options?.enabled === false) return + + const config = readConfig(options) + const names = createNames(config) + + input.api.route.register([ + createPanelRoute({ + api: input.api, + config, + names, + }), + ]) + + registerCommands(input.api, config, names) + input.slots.register(createSidebarTopSlot(input.api, input.client, config, names)) +} + +export default { + tui, +} diff --git a/tui/package.json b/tui/package.json new file mode 100644 index 00000000..3383012b --- /dev/null +++ b/tui/package.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "dcp-tui-probe-local", + "private": true, + "type": "module", + "dependencies": { + "@opencode-ai/plugin": "1.2.20", + "@opentui/core": "0.0.0-20260304-eee67156", + "@opentui/solid": "0.0.0-20260304-eee67156", + "solid-js": "1.9.9" + } +} diff --git a/tui/routes/panel.tsx b/tui/routes/panel.tsx new file mode 100644 index 00000000..69cb8b2b --- /dev/null +++ b/tui/routes/panel.tsx @@ -0,0 +1,82 @@ +// @ts-nocheck +/** @jsxImportSource @opentui/solid */ +import { useKeyboard } from "@opentui/solid" +import type { TuiApi, TuiRouteDefinition } from "@opencode-ai/plugin/tui" +import { MetricRow } from "../components/metric-row" +import { Screen } from "../components/screen" +import { Section } from "../components/section" +import { getRouteSource, getSessionIDFromParams, goBack } from "../shared/navigation" +import { getPalette } from "../shared/theme" +import type { DcpRouteNames, DcpTuiConfig } from "../shared/types" + +const PanelScreen = (props: { + api: TuiApi + config: DcpTuiConfig + names: DcpRouteNames + params?: Record +}) => { + const palette = getPalette(props.api.theme.current as Record) + const sessionID = () => getSessionIDFromParams(props.params) + const source = () => getRouteSource(props.params) + + useKeyboard((evt) => { + if (props.api.route.current.name !== props.names.routes.panel) return + if (evt.name !== "escape" && !(evt.ctrl && evt.name === "h")) return + evt.preventDefault() + evt.stopPropagation() + goBack(props.api, sessionID()) + }) + + return ( + +
+ + Use this page as the home for future DCP-specific TUI work. + + + The live context breakdown now lives directly in the session sidebar. + +
+ +
+ + + +
+ +
+ - block explorer + - prune history and diagnostics + - manual DCP actions +
+
+ ) +} + +export const createPanelRoute = (input: { + api: TuiApi + config: DcpTuiConfig + names: DcpRouteNames +}): TuiRouteDefinition => { + return { + name: input.names.routes.panel, + render: ({ params }) => ( + + ), + } +} diff --git a/tui/shared/config.ts b/tui/shared/config.ts new file mode 100644 index 00000000..6114f540 --- /dev/null +++ b/tui/shared/config.ts @@ -0,0 +1,14 @@ +import type { DcpTuiConfig } from "./types" + +const pick = (value: unknown, fallback: string) => { + if (typeof value !== "string") return fallback + if (!value.trim()) return fallback + return value +} + +export const readConfig = (options: Record | undefined): DcpTuiConfig => { + return { + label: pick(options?.label, "DCP"), + route: pick(options?.route, "dcp"), + } +} diff --git a/tui/shared/names.ts b/tui/shared/names.ts new file mode 100644 index 00000000..84cb8102 --- /dev/null +++ b/tui/shared/names.ts @@ -0,0 +1,13 @@ +import type { DcpRouteNames, DcpTuiConfig } from "./types" + +export const createNames = (config: DcpTuiConfig): DcpRouteNames => { + return { + slot: `${config.route}.sidebar`, + routes: { + panel: `${config.route}.panel`, + }, + commands: { + panel: `plugin.${config.route}.panel`, + }, + } +} diff --git a/tui/shared/navigation.ts b/tui/shared/navigation.ts new file mode 100644 index 00000000..c1369a6c --- /dev/null +++ b/tui/shared/navigation.ts @@ -0,0 +1,47 @@ +// @ts-nocheck +import type { TuiApi } from "@opencode-ai/plugin/tui" +import type { DcpRouteNames, DcpRouteParams, DcpRouteSource } from "./types" + +export const getSessionIDFromParams = (params?: Record) => { + if (typeof params?.session_id === "string") return params.session_id + return undefined +} + +export const getRouteSource = (params?: Record) => { + if (typeof params?.source === "string") return params.source + return "unknown" +} + +export const getCurrentSessionID = (api: TuiApi) => { + const current = api.route.current + if (current.name === "session") return current.params.sessionID + if ("params" in current && current.params && typeof current.params === "object") { + return getSessionIDFromParams(current.params) + } + return undefined +} + +const navigate = (api: TuiApi, routeName: string, source: DcpRouteSource, sessionID?: string) => { + const params: DcpRouteParams = { + source, + session_id: sessionID ?? getCurrentSessionID(api), + } + api.route.navigate(routeName, params) +} + +export const openPanel = ( + api: TuiApi, + names: DcpRouteNames, + source: DcpRouteSource, + sessionID?: string, +) => { + navigate(api, names.routes.panel, source, sessionID) +} + +export const goBack = (api: TuiApi, sessionID?: string) => { + if (sessionID) { + api.route.navigate("session", { sessionID }) + return + } + api.route.navigate("home") +} diff --git a/tui/shared/theme.ts b/tui/shared/theme.ts new file mode 100644 index 00000000..10da6467 --- /dev/null +++ b/tui/shared/theme.ts @@ -0,0 +1,48 @@ +import type { RGBA } from "@opentui/core" + +export type DcpColor = RGBA | string + +export interface DcpPalette { + panel: DcpColor + base: DcpColor + surface: DcpColor + border: DcpColor + text: DcpColor + muted: DcpColor + accent: DcpColor + success: DcpColor + warning: DcpColor +} + +const defaults = { + panel: "#111111", + base: "#1d1d1d", + surface: "#171717", + border: "#4a4a4a", + text: "#f0f0f0", + muted: "#a5a5a5", + accent: "#5f87ff", + success: "#67b95f", + warning: "#d7a94b", +} + +export const getPalette = (theme: Record): DcpPalette => { + const get = (name: string, fallback: string): DcpColor => { + const value = theme[name] + if (typeof value === "string") return value + if (value && typeof value === "object") return value as RGBA + return fallback + } + + return { + panel: get("backgroundPanel", defaults.panel), + base: get("backgroundElement", defaults.base), + surface: get("background", defaults.surface), + border: get("border", defaults.border), + text: get("text", defaults.text), + muted: get("textMuted", defaults.muted), + accent: get("primary", defaults.accent), + success: get("success", defaults.success), + warning: get("warning", defaults.warning), + } +} diff --git a/tui/shared/types.ts b/tui/shared/types.ts new file mode 100644 index 00000000..c2ca1409 --- /dev/null +++ b/tui/shared/types.ts @@ -0,0 +1,54 @@ +// @ts-nocheck +import type { TuiPluginInput } from "@opencode-ai/plugin/tui" + +export type DcpTuiClient = TuiPluginInput["client"] +export type DcpRouteSource = "sidebar" | "command" + +export interface DcpTuiConfig { + label: string + route: string +} + +export interface DcpRouteNames { + slot: string + routes: { + panel: string + } + commands: { + panel: string + } +} + +export interface DcpRouteParams { + session_id?: string + source?: string +} + +export interface DcpContextBreakdown { + system: number + user: number + assistant: number + tools: number + toolCount: number + toolsInContextCount: number + prunedTokens: number + prunedToolCount: number + prunedMessageCount: number + total: number + messageCount: number +} + +export interface DcpPersistedSummary { + available: boolean + activeBlockCount: number + activeBlockTopics: string[] + lastUpdated?: string +} + +export interface DcpContextSnapshot { + sessionID?: string + breakdown: DcpContextBreakdown + persisted: DcpPersistedSummary + notes: string[] + loadedAt: number +} diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx new file mode 100644 index 00000000..a2542787 --- /dev/null +++ b/tui/slots/sidebar-top.tsx @@ -0,0 +1,291 @@ +// @ts-nocheck +/** @jsxImportSource @opentui/solid */ +import { createEffect, createMemo, createSignal, onCleanup, Show } from "solid-js" +import { formatTokenCount } from "../../lib/ui/utils" +import { loadContextSnapshotCached, peekContextSnapshot } from "../data/context" +import { openPanel } from "../shared/navigation" +import { getPalette, type DcpPalette } from "../shared/theme" +import type { DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" + +const BAR_WIDTH = 12 + +const toneColor = ( + palette: DcpPalette, + tone: "text" | "muted" | "accent" | "success" | "warning" = "text", +) => { + if (tone === "accent") return palette.accent + if (tone === "success") return palette.success + if (tone === "warning") return palette.warning + if (tone === "muted") return palette.muted + return palette.text +} + +const compactTokenCount = (value: number) => formatTokenCount(value).replace(/ tokens$/, "") + +const buildBar = (value: number, total: number, char: string) => { + if (total <= 0) return " ".repeat(BAR_WIDTH) + const filled = Math.max(0, Math.round((value / total) * BAR_WIDTH)) + return char.repeat(filled).padEnd(BAR_WIDTH, " ") +} + +const SummaryRow = (props: { + palette: DcpPalette + label: string + value: string + tone?: "text" | "muted" | "accent" | "success" | "warning" +}) => { + return ( + + {props.label} + + {props.value} + + + ) +} + +const SidebarContextBar = (props: { + palette: DcpPalette + label: string + value: number + total: number + char: string + tone?: "text" | "muted" | "accent" | "success" | "warning" +}) => { + const percent = props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%" + const label = props.label.padEnd(8, " ") + const bar = buildBar(props.value, props.total, props.char) + return ( + {`${label} ${percent.padStart(4, " ")} |${bar}| ${compactTokenCount(props.value)}`} + ) +} + +const SidebarContext = (props: { + api: any + client: DcpTuiClient + config: DcpTuiConfig + names: DcpRouteNames + palette: DcpPalette + sessionID: string +}) => { + const [snapshot, setSnapshot] = createSignal(peekContextSnapshot(props.sessionID)) + const [loading, setLoading] = createSignal(!snapshot()) + const [error, setError] = createSignal() + + createEffect(() => { + const sessionID = props.sessionID + const cached = peekContextSnapshot(sessionID) + setSnapshot(cached) + setLoading(!cached) + setError(undefined) + + let active = true + void loadContextSnapshotCached(props.client, sessionID) + .then((value) => { + if (!active) return + setSnapshot(value) + setLoading(false) + }) + .catch((cause) => { + if (!active) return + setError(cause instanceof Error ? cause.message : String(cause)) + setLoading(false) + }) + + onCleanup(() => { + active = false + }) + }) + + const prunedItems = createMemo(() => { + const value = snapshot() + if (!value) return "No pruned items" + const parts: string[] = [] + if (value.breakdown.prunedToolCount > 0) { + parts.push( + `${value.breakdown.prunedToolCount} tool${value.breakdown.prunedToolCount === 1 ? "" : "s"}`, + ) + } + if (value.breakdown.prunedMessageCount > 0) { + parts.push( + `${value.breakdown.prunedMessageCount} msg${value.breakdown.prunedMessageCount === 1 ? "" : "s"}`, + ) + } + return parts.length > 0 ? `${parts.join(", ")} pruned` : "No pruned items" + }) + + const blockSummary = createMemo(() => { + const value = snapshot() + if (!value) return "0" + return `${value.persisted.activeBlockCount}` + }) + + const topicLine = createMemo(() => { + const value = snapshot() + if (!value) return "" + if (!value.persisted.activeBlockTopics.length) return "" + return `Topics: ${value.persisted.activeBlockTopics.join(" | ")}` + }) + + const status = createMemo(() => { + if (error() && snapshot()) return { label: "cached", tone: "warning" as const } + if (error()) return { label: "error", tone: "warning" as const } + if (loading() && snapshot()) return { label: "refreshing", tone: "warning" as const } + if (loading()) return { label: "loading", tone: "warning" as const } + return { label: "loaded", tone: "success" as const } + }) + + return ( + openPanel(props.api, props.names, "sidebar", props.sessionID)} + > + + + + + {props.config.label} + + + click for more + + {status().label} + + + + session {props.sessionID.slice(0, 18)} + + + + + Loading DCP context... + + + + + + DCP context failed to load. + + + + + {(value) => ( + + + Current + + ~{compactTokenCount(value().breakdown.total)} + + + + + + + + + + + + + + {prunedItems()} + + {topicLine()} + + + {value().notes[0]} + + + + )} + + + ) +} + +export const createSidebarTopSlot = ( + api: any, + client: DcpTuiClient, + config: DcpTuiConfig, + names: DcpRouteNames, +) => ({ + id: names.slot, + slots: { + sidebar_top(ctx, value: { session_id: string }) { + const palette = getPalette(ctx.theme.current as Record) + return ( + + ) + }, + }, +}) diff --git a/tui/tsconfig.json b/tui/tsconfig.json new file mode 100644 index 00000000..ca44f5cc --- /dev/null +++ b/tui/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "preserve", + "jsxImportSource": "@opentui/solid", + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["./**/*.ts", "./**/*.tsx"] +} From 3eefb010c1d79e418db4e019ce2a7d032e112c5a Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 9 Mar 2026 19:35:24 -0400 Subject: [PATCH 02/50] fix(tui): improve type safety and adopt plugin keybind API --- tui/commands.ts | 3 ++- tui/components/screen.tsx | 5 +++-- tui/components/section.tsx | 5 +++-- tui/index.tsx | 1 - tui/routes/panel.tsx | 35 ++++++++++++++++++++--------------- tui/shared/navigation.ts | 1 - tui/shared/types.ts | 1 - tui/slots/sidebar-top.tsx | 14 ++++++++------ 8 files changed, 36 insertions(+), 29 deletions(-) diff --git a/tui/commands.ts b/tui/commands.ts index 5971ae2a..af6a792d 100644 --- a/tui/commands.ts +++ b/tui/commands.ts @@ -1,15 +1,16 @@ -// @ts-nocheck import type { TuiApi } from "@opencode-ai/plugin/tui" import { openPanel } from "./shared/navigation" import type { DcpRouteNames, DcpTuiConfig } from "./shared/types" export const registerCommands = (api: TuiApi, config: DcpTuiConfig, names: DcpRouteNames) => { + const keys = api.keybind?.create({ close: "escape,ctrl+h" }) api.command.register(() => [ { title: `${config.label} panel`, value: names.commands.panel, description: "Open the DCP placeholder panel", category: config.label, + ...(keys ? { keybind: keys.get("close") } : {}), slash: { name: "dcp-panel", }, diff --git a/tui/components/screen.tsx b/tui/components/screen.tsx index 5025c1bc..22f48801 100644 --- a/tui/components/screen.tsx +++ b/tui/components/screen.tsx @@ -1,4 +1,5 @@ /** @jsxImportSource @opentui/solid */ +import type { JSX } from "solid-js" import type { DcpPalette } from "../shared/theme" export const Screen = (props: { @@ -6,7 +7,7 @@ export const Screen = (props: { title: string subtitle?: string footer?: string - children?: unknown + children?: JSX.Element }) => { return ( @@ -17,7 +18,7 @@ export const Screen = (props: { gap={1} padding={1} backgroundColor={props.palette.base} - border={["left"]} + border={{ type: "single" }} borderColor={props.palette.accent} > diff --git a/tui/components/section.tsx b/tui/components/section.tsx index 546152a5..6c95e95a 100644 --- a/tui/components/section.tsx +++ b/tui/components/section.tsx @@ -1,11 +1,12 @@ /** @jsxImportSource @opentui/solid */ +import type { JSX } from "solid-js" import type { DcpPalette } from "../shared/theme" export const Section = (props: { palette: DcpPalette title: string subtitle?: string - children?: unknown + children?: JSX.Element }) => { return ( diff --git a/tui/index.tsx b/tui/index.tsx index 6f0c5f3b..4cc1b3c6 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -1,4 +1,3 @@ -// @ts-nocheck /** @jsxImportSource @opentui/solid */ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" import { registerCommands } from "./commands" diff --git a/tui/routes/panel.tsx b/tui/routes/panel.tsx index 69cb8b2b..64f7b2f7 100644 --- a/tui/routes/panel.tsx +++ b/tui/routes/panel.tsx @@ -1,5 +1,5 @@ -// @ts-nocheck /** @jsxImportSource @opentui/solid */ +import { createMemo } from "solid-js" import { useKeyboard } from "@opentui/solid" import type { TuiApi, TuiRouteDefinition } from "@opencode-ai/plugin/tui" import { MetricRow } from "../components/metric-row" @@ -15,13 +15,18 @@ const PanelScreen = (props: { names: DcpRouteNames params?: Record }) => { - const palette = getPalette(props.api.theme.current as Record) + const palette = createMemo(() => getPalette(props.api.theme.current as Record)) const sessionID = () => getSessionIDFromParams(props.params) const source = () => getRouteSource(props.params) + const keys = props.api.keybind?.create({ close: "escape,ctrl+h" }) useKeyboard((evt) => { if (props.api.route.current.name !== props.names.routes.panel) return - if (evt.name !== "escape" && !(evt.ctrl && evt.name === "h")) return + if (props.api.ui?.dialog?.open) return + const matched = keys + ? keys.match("close", evt) + : evt.name === "escape" || (evt.ctrl && evt.name === "h") + if (!matched) return evt.preventDefault() evt.stopPropagation() goBack(props.api, sessionID()) @@ -29,35 +34,35 @@ const PanelScreen = (props: { return ( -
- +
+ Use this page as the home for future DCP-specific TUI work. - + The live context breakdown now lives directly in the session sidebar.
-
- - +
+ +
-
- - block explorer - - prune history and diagnostics - - manual DCP actions +
+ - block explorer + - prune history and diagnostics + - manual DCP actions
) diff --git a/tui/shared/navigation.ts b/tui/shared/navigation.ts index c1369a6c..2c14d727 100644 --- a/tui/shared/navigation.ts +++ b/tui/shared/navigation.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import type { TuiApi } from "@opencode-ai/plugin/tui" import type { DcpRouteNames, DcpRouteParams, DcpRouteSource } from "./types" diff --git a/tui/shared/types.ts b/tui/shared/types.ts index c2ca1409..88a95fa9 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import type { TuiPluginInput } from "@opencode-ai/plugin/tui" export type DcpTuiClient = TuiPluginInput["client"] diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index a2542787..b658a99f 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -1,6 +1,6 @@ -// @ts-nocheck /** @jsxImportSource @opentui/solid */ import { createEffect, createMemo, createSignal, onCleanup, Show } from "solid-js" +import type { TuiApi } from "@opencode-ai/plugin/tui" import { formatTokenCount } from "../../lib/ui/utils" import { loadContextSnapshotCached, peekContextSnapshot } from "../data/context" import { openPanel } from "../shared/navigation" @@ -70,7 +70,7 @@ const SidebarContextBar = (props: { } const SidebarContext = (props: { - api: any + api: TuiApi client: DcpTuiClient config: DcpTuiConfig names: DcpRouteNames @@ -150,7 +150,7 @@ const SidebarContext = (props: { flexDirection="column" gap={0} backgroundColor={props.palette.base} - border={["left"]} + border={{ type: "single" }} borderColor={props.palette.accent} paddingTop={1} paddingBottom={1} @@ -267,7 +267,7 @@ const SidebarContext = (props: { } export const createSidebarTopSlot = ( - api: any, + api: TuiApi, client: DcpTuiClient, config: DcpTuiConfig, names: DcpRouteNames, @@ -275,14 +275,16 @@ export const createSidebarTopSlot = ( id: names.slot, slots: { sidebar_top(ctx, value: { session_id: string }) { - const palette = getPalette(ctx.theme.current as Record) + const palette = createMemo(() => + getPalette(ctx.theme.current as Record), + ) return ( ) From f0943857cb8384c44648bba8e1b6f3ae18c15ddd Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 9 Mar 2026 19:35:28 -0400 Subject: [PATCH 03/50] chore(tui): bump dependencies and update readme image --- tui/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tui/package.json b/tui/package.json index 3383012b..2960722a 100644 --- a/tui/package.json +++ b/tui/package.json @@ -4,9 +4,9 @@ "private": true, "type": "module", "dependencies": { - "@opencode-ai/plugin": "1.2.20", - "@opentui/core": "0.0.0-20260304-eee67156", - "@opentui/solid": "0.0.0-20260304-eee67156", + "@opencode-ai/plugin": "1.2.21", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", "solid-js": "1.9.9" } } From a722afdcf99af3ac87987c6c1b8caf13ea8afed7 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 9 Mar 2026 23:00:12 -0400 Subject: [PATCH 04/50] feat(lib): add scope support to Logger --- lib/logger.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/logger.ts b/lib/logger.ts index 43b5d9e8..8949d6c1 100644 --- a/lib/logger.ts +++ b/lib/logger.ts @@ -5,12 +5,16 @@ import { homedir } from "os" export class Logger { private logDir: string + private scope?: string public enabled: boolean - constructor(enabled: boolean) { + constructor(enabled: boolean, scope?: string) { this.enabled = enabled + this.scope = scope?.replace(/[^A-Za-z0-9._-]/g, "_") const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config") - this.logDir = join(configHome, "opencode", "logs", "dcp") + this.logDir = this.scope + ? join(configHome, "opencode", "logs", "dcp", this.scope) + : join(configHome, "opencode", "logs", "dcp") } private async ensureLogDir() { @@ -78,12 +82,17 @@ export class Logger { const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""}\n` - const dailyLogDir = join(this.logDir, "daily") - if (!existsSync(dailyLogDir)) { - await mkdir(dailyLogDir, { recursive: true }) + const logFile = this.scope + ? join(this.logDir, `${new Date().toISOString().split("T")[0]}.log`) + : join(this.logDir, "daily", `${new Date().toISOString().split("T")[0]}.log`) + + if (!this.scope) { + const dailyLogDir = join(this.logDir, "daily") + if (!existsSync(dailyLogDir)) { + await mkdir(dailyLogDir, { recursive: true }) + } } - const logFile = join(dailyLogDir, `${new Date().toISOString().split("T")[0]}.log`) await writeFile(logFile, logLine, { flag: "a" }) } catch (error) {} } From 28af8c817e75f075b1fb83a9a1ffc43a53585126 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 9 Mar 2026 23:00:18 -0400 Subject: [PATCH 05/50] feat(tui): event-driven sidebar refresh with reactivity fix --- tui/data/context.ts | 80 +++++- tui/index.tsx | 22 +- tui/shared/config.ts | 6 + tui/shared/types.ts | 1 + tui/slots/sidebar-top.tsx | 502 ++++++++++++++++++++++++++++---------- 5 files changed, 482 insertions(+), 129 deletions(-) diff --git a/tui/data/context.ts b/tui/data/context.ts index 5e64e7e9..b9cf5ccf 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -16,11 +16,24 @@ import { import { isMessageCompacted } from "../../lib/shared-utils" import type { DcpContextBreakdown, DcpContextSnapshot, DcpTuiClient } from "../shared/types" -const logger = new Logger(false) +let logger = new Logger(false, "TUI") const snapshotCache = new Map() const inflightSnapshots = new Map>() const CACHE_TTL_MS = 5000 +const summarizeSnapshot = (snapshot: DcpContextSnapshot) => ({ + sessionID: snapshot.sessionID, + totalTokens: snapshot.breakdown.total, + messageCount: snapshot.breakdown.messageCount, + prunedTokens: snapshot.breakdown.prunedTokens, + activeBlockCount: snapshot.persisted.activeBlockCount, + loadedAt: snapshot.loadedAt, +}) + +export const setContextLogger = (nextLogger: Logger) => { + logger = nextLogger +} + const emptyBreakdown = (): DcpContextBreakdown => ({ system: 0, user: 0, @@ -35,7 +48,10 @@ const emptyBreakdown = (): DcpContextBreakdown => ({ messageCount: 0, }) -const createSnapshot = (sessionID?: string, notes: string[] = []): DcpContextSnapshot => ({ +export const createPlaceholderContextSnapshot = ( + sessionID?: string, + notes: string[] = [], +): DcpContextSnapshot => ({ sessionID, breakdown: emptyBreakdown(), persisted: { @@ -210,13 +226,21 @@ export const loadContextSnapshot = async ( sessionID?: string, ): Promise => { if (!sessionID) { - return createSnapshot(undefined, ["Open this panel from a session to inspect DCP context."]) + void logger.debug("Context snapshot requested without session") + return createPlaceholderContextSnapshot(undefined, [ + "Open this panel from a session to inspect DCP context.", + ]) } + void logger.debug("Loading context snapshot", { sessionID }) const messagesResult = await client.session.messages({ sessionID }) const messages = Array.isArray(messagesResult.data) ? (messagesResult.data as WithParts[]) : ([] as WithParts[]) + void logger.debug("Fetched session messages for context snapshot", { + sessionID, + messageCount: messages.length, + }) const { state, persisted } = await buildState(sessionID, messages) const breakdown = analyzeTokens(state, messages) @@ -238,7 +262,7 @@ export const loadContextSnapshot = async ( notes.push("This session does not have any messages yet.") } - return { + const snapshot = { sessionID, breakdown, persisted: { @@ -250,6 +274,13 @@ export const loadContextSnapshot = async ( notes, loadedAt: Date.now(), } + + void logger.debug("Loaded context snapshot", { + ...summarizeSnapshot(snapshot), + persisted: !!persisted, + }) + + return snapshot } export const peekContextSnapshot = (sessionID?: string): DcpContextSnapshot | undefined => { @@ -257,31 +288,70 @@ export const peekContextSnapshot = (sessionID?: string): DcpContextSnapshot | un return snapshotCache.get(sessionID) } +export const invalidateContextSnapshot = (sessionID?: string) => { + if (!sessionID) { + void logger.debug("Invalidating all context snapshots") + snapshotCache.clear() + inflightSnapshots.clear() + return + } + + void logger.debug("Invalidating context snapshot", { sessionID }) + snapshotCache.delete(sessionID) + inflightSnapshots.delete(sessionID) +} + export const loadContextSnapshotCached = async ( client: DcpTuiClient, sessionID?: string, ): Promise => { if (!sessionID) { - return createSnapshot(undefined, ["Open this panel from a session to inspect DCP context."]) + void logger.debug("Cached context snapshot requested without session") + return createPlaceholderContextSnapshot(undefined, [ + "Open this panel from a session to inspect DCP context.", + ]) } const cached = snapshotCache.get(sessionID) if (cached && Date.now() - cached.loadedAt < CACHE_TTL_MS) { + void logger.debug("Context snapshot cache hit", { + sessionID, + cacheAgeMs: Date.now() - cached.loadedAt, + }) return cached } + if (cached) { + void logger.debug("Context snapshot cache stale", { + sessionID, + cacheAgeMs: Date.now() - cached.loadedAt, + }) + } else { + void logger.debug("Context snapshot cache miss", { sessionID }) + } + const inflight = inflightSnapshots.get(sessionID) if (inflight) { + void logger.debug("Reusing inflight context snapshot request", { sessionID }) return inflight } const request = loadContextSnapshot(client, sessionID) .then((snapshot) => { snapshotCache.set(sessionID, snapshot) + void logger.debug("Stored context snapshot in cache", summarizeSnapshot(snapshot)) return snapshot }) + .catch((cause) => { + void logger.error("Context snapshot request failed", { + sessionID, + error: cause instanceof Error ? cause.message : String(cause), + }) + throw cause + }) .finally(() => { inflightSnapshots.delete(sessionID) + void logger.debug("Cleared inflight context snapshot request", { sessionID }) }) inflightSnapshots.set(sessionID, request) diff --git a/tui/index.tsx b/tui/index.tsx index 4cc1b3c6..24b22707 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -1,6 +1,8 @@ /** @jsxImportSource @opentui/solid */ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import { Logger } from "../lib/logger" import { registerCommands } from "./commands" +import { setContextLogger } from "./data/context" import { createPanelRoute } from "./routes/panel" import { createSidebarTopSlot } from "./slots/sidebar-top" import { readConfig } from "./shared/config" @@ -11,6 +13,14 @@ const tui = async (input: TuiPluginInput, options?: Record) => const config = readConfig(options) const names = createNames(config) + const logger = new Logger(config.debug, "TUI") + + setContextLogger(logger) + void logger.info("DCP TUI initialized", { + debug: config.debug, + label: config.label, + route: config.route, + }) input.api.route.register([ createPanelRoute({ @@ -21,7 +31,17 @@ const tui = async (input: TuiPluginInput, options?: Record) => ]) registerCommands(input.api, config, names) - input.slots.register(createSidebarTopSlot(input.api, input.client, config, names)) + input.slots.register( + createSidebarTopSlot( + input.api, + input.client, + input.event, + input.renderer, + logger, + config, + names, + ), + ) } export default { diff --git a/tui/shared/config.ts b/tui/shared/config.ts index 6114f540..7db8561a 100644 --- a/tui/shared/config.ts +++ b/tui/shared/config.ts @@ -6,8 +6,14 @@ const pick = (value: unknown, fallback: string) => { return value } +const pickBoolean = (value: unknown, fallback: boolean) => { + if (typeof value !== "boolean") return fallback + return value +} + export const readConfig = (options: Record | undefined): DcpTuiConfig => { return { + debug: pickBoolean(options?.debug, false), label: pick(options?.label, "DCP"), route: pick(options?.route, "dcp"), } diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 88a95fa9..2d95843a 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -4,6 +4,7 @@ export type DcpTuiClient = TuiPluginInput["client"] export type DcpRouteSource = "sidebar" | "command" export interface DcpTuiConfig { + debug: boolean label: string route: string } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index b658a99f..c24f76c1 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -1,13 +1,20 @@ /** @jsxImportSource @opentui/solid */ -import { createEffect, createMemo, createSignal, onCleanup, Show } from "solid-js" -import type { TuiApi } from "@opencode-ai/plugin/tui" +import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" +import type { TuiApi, TuiPluginInput } from "@opencode-ai/plugin/tui" +import { Logger } from "../../lib/logger" import { formatTokenCount } from "../../lib/ui/utils" -import { loadContextSnapshotCached, peekContextSnapshot } from "../data/context" +import { + createPlaceholderContextSnapshot, + invalidateContextSnapshot, + loadContextSnapshotCached, + peekContextSnapshot, +} from "../data/context" import { openPanel } from "../shared/navigation" import { getPalette, type DcpPalette } from "../shared/theme" import type { DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" const BAR_WIDTH = 12 +const REFRESH_DEBOUNCE_MS = 100 const toneColor = ( palette: DcpPalette, @@ -59,56 +66,299 @@ const SidebarContextBar = (props: { char: string tone?: "text" | "muted" | "accent" | "success" | "warning" }) => { - const percent = props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%" - const label = props.label.padEnd(8, " ") - const bar = buildBar(props.value, props.total, props.char) + const percent = createMemo(() => + props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%", + ) + const label = createMemo(() => props.label.padEnd(8, " ")) + const bar = createMemo(() => buildBar(props.value, props.total, props.char)) return ( {`${label} ${percent.padStart(4, " ")} |${bar}| ${compactTokenCount(props.value)}`} + >{`${label()} ${percent().padStart(4, " ")} |${bar()}| ${compactTokenCount(props.value)}`} ) } const SidebarContext = (props: { api: TuiApi client: DcpTuiClient + event: TuiPluginInput["event"] + renderer: TuiPluginInput["renderer"] + logger: Logger config: DcpTuiConfig names: DcpRouteNames palette: DcpPalette - sessionID: string + sessionID: () => string }) => { - const [snapshot, setSnapshot] = createSignal(peekContextSnapshot(props.sessionID)) - const [loading, setLoading] = createSignal(!snapshot()) + const initialSnapshot = peekContextSnapshot(props.sessionID()) + const [snapshot, setSnapshot] = createSignal( + initialSnapshot ?? createPlaceholderContextSnapshot(props.sessionID()), + ) + const [loading, setLoading] = createSignal(!initialSnapshot) const [error, setError] = createSignal() + let requestVersion = 0 + let renderTimeout: ReturnType | undefined + + const requestRender = (reason: string, data?: Record) => { + const activeSessionID = untrack(() => props.sessionID()) + void props.logger.debug("Sidebar requested renderer refresh", { + activeSessionID, + reason, + ...data, + }) + if (renderTimeout) clearTimeout(renderTimeout) + renderTimeout = setTimeout(() => { + renderTimeout = undefined + try { + void props.logger.debug("Sidebar renderer refresh dispatched", { + activeSessionID, + reason, + ...data, + }) + props.renderer.requestRender() + } catch (cause) { + void props.logger.warn("Sidebar renderer refresh failed", { + activeSessionID, + reason, + error: cause instanceof Error ? cause.message : String(cause), + ...data, + }) + } + }, 0) + } + + onCleanup(() => { + if (renderTimeout) clearTimeout(renderTimeout) + }) + + const refreshSnapshot = async ( + sessionID: string, + options?: { invalidate?: boolean; preserveSnapshot?: boolean; reason?: string }, + ) => { + const preserveSnapshot = options?.preserveSnapshot ?? false + const reason = options?.reason ?? "unspecified" + + void props.logger.debug("Sidebar refresh start", { + sessionID, + invalidate: !!options?.invalidate, + preserveSnapshot, + reason, + }) + + if (options?.invalidate) { + invalidateContextSnapshot(sessionID) + } - createEffect(() => { - const sessionID = props.sessionID const cached = peekContextSnapshot(sessionID) - setSnapshot(cached) - setLoading(!cached) + if (cached) { + void props.logger.debug("Sidebar using cached snapshot before reload", { + sessionID, + loadedAt: cached.loadedAt, + totalTokens: cached.breakdown.total, + }) + setSnapshot(cached) + setLoading(false) + } else { + const current = untrack(snapshot) + if (!preserveSnapshot || current?.sessionID !== sessionID) { + setSnapshot(createPlaceholderContextSnapshot(sessionID, ["Loading DCP context..."])) + } + setLoading(true) + void props.logger.debug("Sidebar entering loading state", { + sessionID, + hadCurrentSnapshot: !!current, + preservedSnapshot: preserveSnapshot && current?.sessionID === sessionID, + }) + } setError(undefined) + requestRender("refresh-start", { sessionID, reason }) + + const currentRequest = ++requestVersion + void props.logger.debug("Sidebar refresh request issued", { + sessionID, + requestVersion: currentRequest, + reason, + }) - let active = true - void loadContextSnapshotCached(props.client, sessionID) - .then((value) => { - if (!active) return - setSnapshot(value) - setLoading(false) + try { + const value = await loadContextSnapshotCached(props.client, sessionID) + if (currentRequest !== requestVersion || props.sessionID() !== sessionID) { + void props.logger.debug("Sidebar refresh result ignored as stale", { + sessionID, + requestVersion: currentRequest, + activeRequestVersion: requestVersion, + activeSessionID: props.sessionID(), + reason, + }) + return + } + setSnapshot(value) + setLoading(false) + void props.logger.debug("Sidebar refresh succeeded", { + sessionID, + requestVersion: currentRequest, + totalTokens: value.breakdown.total, + messageCount: value.breakdown.messageCount, + activeBlockCount: value.persisted.activeBlockCount, + reason, }) - .catch((cause) => { - if (!active) return - setError(cause instanceof Error ? cause.message : String(cause)) - setLoading(false) + requestRender("refresh-success", { sessionID, reason, requestVersion: currentRequest }) + } catch (cause) { + if (currentRequest !== requestVersion || props.sessionID() !== sessionID) { + void props.logger.debug("Sidebar refresh error ignored as stale", { + sessionID, + requestVersion: currentRequest, + activeRequestVersion: requestVersion, + activeSessionID: props.sessionID(), + reason, + }) + return + } + setError(cause instanceof Error ? cause.message : String(cause)) + setLoading(false) + void props.logger.error("Sidebar refresh failed", { + sessionID, + requestVersion: currentRequest, + error: cause instanceof Error ? cause.message : String(cause), + reason, }) + requestRender("refresh-error", { sessionID, reason, requestVersion: currentRequest }) + } + } - onCleanup(() => { - active = false - }) - }) + createEffect( + on( + props.sessionID, + (sessionID) => { + void props.logger.info("Sidebar active session changed", { sessionID }) + void refreshSnapshot(sessionID, { reason: "session-change" }) + }, + { defer: false }, + ), + ) + + createEffect( + on( + props.sessionID, + (sessionID) => { + let timeout: ReturnType | undefined + let pendingReason: string | undefined + + void props.logger.debug("Sidebar event subscriptions armed", { sessionID }) + + const scheduleRefresh = (reason: string, data?: Record) => { + if (!sessionID) return + if (timeout) clearTimeout(timeout) + pendingReason = reason + void props.logger.debug("Sidebar refresh scheduled", { + sessionID, + debounceMs: REFRESH_DEBOUNCE_MS, + reason, + ...data, + }) + timeout = setTimeout(() => { + const flushReason = pendingReason ?? reason + pendingReason = undefined + timeout = undefined + void props.logger.debug("Sidebar refresh debounce fired", { + sessionID, + reason: flushReason, + }) + void refreshSnapshot(sessionID, { + invalidate: true, + preserveSnapshot: true, + reason: flushReason, + }) + }, REFRESH_DEBOUNCE_MS) + } + + const unsubs = [ + props.event.on("message.updated", (event) => { + if (event.properties.info.sessionID !== sessionID) return + scheduleRefresh("message.updated", { + eventSessionID: event.properties.info.sessionID, + messageID: event.properties.info.id, + }) + }), + props.event.on("message.removed", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("message.removed", { + eventSessionID: event.properties.sessionID, + messageID: event.properties.messageID, + }) + }), + props.event.on("message.part.updated", (event) => { + if (event.properties.part.sessionID !== sessionID) return + scheduleRefresh("message.part.updated", { + eventSessionID: event.properties.part.sessionID, + messageID: event.properties.part.messageID, + partID: event.properties.part.id, + }) + }), + props.event.on("message.part.delta", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("message.part.delta", { + eventSessionID: event.properties.sessionID, + messageID: event.properties.messageID, + partID: event.properties.partID, + field: event.properties.field, + }) + }), + props.event.on("message.part.removed", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("message.part.removed", { + eventSessionID: event.properties.sessionID, + messageID: event.properties.messageID, + partID: event.properties.partID, + }) + }), + props.event.on("session.updated", (event) => { + if (event.properties.info.id !== sessionID) return + scheduleRefresh("session.updated", { + eventSessionID: event.properties.info.id, + }) + }), + props.event.on("session.deleted", (event) => { + if (event.properties.info.id !== sessionID) return + scheduleRefresh("session.deleted", { + eventSessionID: event.properties.info.id, + }) + }), + props.event.on("session.diff", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("session.diff", { + eventSessionID: event.properties.sessionID, + }) + }), + props.event.on("session.error", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("session.error", { + eventSessionID: event.properties.sessionID, + error: event.properties.error, + }) + }), + props.event.on("session.status", (event) => { + if (event.properties.sessionID !== sessionID) return + scheduleRefresh("session.status", { + eventSessionID: event.properties.sessionID, + status: event.properties.status, + }) + }), + ] + + onCleanup(() => { + if (timeout) clearTimeout(timeout) + void props.logger.debug("Sidebar event subscriptions cleaned up", { sessionID }) + for (const unsub of unsubs) { + unsub() + } + }) + }, + { defer: false }, + ), + ) const prunedItems = createMemo(() => { const value = snapshot() - if (!value) return "No pruned items" const parts: string[] = [] if (value.breakdown.prunedToolCount > 0) { parts.push( @@ -124,22 +374,34 @@ const SidebarContext = (props: { }) const blockSummary = createMemo(() => { - const value = snapshot() - if (!value) return "0" - return `${value.persisted.activeBlockCount}` + return `${snapshot().persisted.activeBlockCount}` }) const topicLine = createMemo(() => { const value = snapshot() - if (!value) return "" if (!value.persisted.activeBlockTopics.length) return "" return `Topics: ${value.persisted.activeBlockTopics.join(" | ")}` }) + const noteLine = createMemo(() => { + const topic = topicLine() + if (topic) return topic + return snapshot().notes[0] ?? "" + }) + + const stateLine = createMemo(() => { + if (error() && snapshot().breakdown.total === 0) return "DCP context failed to load." + if (error()) return `Refresh failed: ${error()}` + if (loading()) return "Loading DCP context..." + return "DCP context loaded." + }) + const status = createMemo(() => { - if (error() && snapshot()) return { label: "cached", tone: "warning" as const } + if (error() && snapshot().breakdown.total > 0) + return { label: "cached", tone: "warning" as const } if (error()) return { label: "error", tone: "warning" as const } - if (loading() && snapshot()) return { label: "refreshing", tone: "warning" as const } + if (loading() && snapshot().breakdown.total > 0) + return { label: "refreshing", tone: "warning" as const } if (loading()) return { label: "loading", tone: "warning" as const } return { label: "loaded", tone: "success" as const } }) @@ -156,7 +418,7 @@ const SidebarContext = (props: { paddingBottom={1} paddingLeft={1} paddingRight={1} - onMouseUp={() => openPanel(props.api, props.names, "sidebar", props.sessionID)} + onMouseUp={() => openPanel(props.api, props.names, "sidebar", props.sessionID())} > @@ -171,97 +433,82 @@ const SidebarContext = (props: { - session {props.sessionID.slice(0, 18)} + session {props.sessionID().slice(0, 18)} + + + + + {stateLine()} + - - - Loading DCP context... + + + Current + + ~{compactTokenCount(snapshot().breakdown.total)} + - + + - - - DCP context failed to load. + + + + + - - - - {(value) => ( - - - Current - - ~{compactTokenCount(value().breakdown.total)} - - - - - - - - - - - - - - {prunedItems()} - - {topicLine()} - - - {value().notes[0]} - - - - )} - + + + {prunedItems()} + {noteLine()} + + ) } @@ -269,12 +516,18 @@ const SidebarContext = (props: { export const createSidebarTopSlot = ( api: TuiApi, client: DcpTuiClient, + event: TuiPluginInput["event"], + renderer: TuiPluginInput["renderer"], + logger: Logger, config: DcpTuiConfig, names: DcpRouteNames, ) => ({ id: names.slot, slots: { sidebar_top(ctx, value: { session_id: string }) { + // value is a reactive proxy from @opentui/solid splitProps — + // value.session_id updates automatically when the host navigates + // to a different session (no event subscription needed). const palette = createMemo(() => getPalette(ctx.theme.current as Record), ) @@ -282,10 +535,13 @@ export const createSidebarTopSlot = ( value.session_id} /> ) }, From 8849e239346a6463a517b926ce6f430eff84c2e9 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 9 Mar 2026 23:00:24 -0400 Subject: [PATCH 06/50] chore(tui): add host runtime linking dev script --- package.json | 1 + scripts/link-tui-host-runtime.mjs | 67 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 scripts/link-tui-host-runtime.mjs diff --git a/package.json b/package.json index e8b4bea0..9d9a4326 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "prepublishOnly": "npm run check:package", "dev": "opencode plugin dev", "typecheck": "tsc --noEmit", + "tui:link-host-runtime": "node scripts/link-tui-host-runtime.mjs", "test": "node --import tsx --test tests/*.test.ts", "format": "prettier --write .", "format:check": "prettier --check .", diff --git a/scripts/link-tui-host-runtime.mjs b/scripts/link-tui-host-runtime.mjs new file mode 100644 index 00000000..458278d4 --- /dev/null +++ b/scripts/link-tui-host-runtime.mjs @@ -0,0 +1,67 @@ +import fs from "node:fs" +import path from "node:path" +import { createRequire } from "node:module" + +const repoRoot = path.resolve(import.meta.dirname, "..") +const tuiNodeModules = path.join(repoRoot, "tui", "node_modules") +const hostRoot = path.resolve( + process.argv[2] ?? process.env.OPENCODE_SOURCE_ROOT ?? "/home/dan/src/opencode", +) +const hostEntry = path.join(hostRoot, "packages", "opencode", "src", "cli", "cmd", "tui", "app.tsx") +const pluginEntry = path.join(repoRoot, "tui", "index.tsx") + +if (!fs.existsSync(hostEntry)) { + throw new Error(`OpenCode TUI entry not found: ${hostEntry}`) +} + +const hostRequire = createRequire(hostEntry) +const pluginRequire = createRequire(pluginEntry) + +const findPackageRoot = (resolvedFile) => { + let current = path.dirname(resolvedFile) + while (true) { + const manifest = path.join(current, "package.json") + if (fs.existsSync(manifest)) return current + const parent = path.dirname(current) + if (parent === current) { + throw new Error(`Could not find package root for ${resolvedFile}`) + } + current = parent + } +} + +const resolveHostPackage = (specifier) => findPackageRoot(hostRequire.resolve(specifier)) +const resolvePluginPackage = (specifier) => findPackageRoot(pluginRequire.resolve(specifier)) + +const links = [ + { + specifier: "solid-js", + dest: path.join(tuiNodeModules, "solid-js"), + }, + { + specifier: "@opentui/solid", + dest: path.join(tuiNodeModules, "@opentui", "solid"), + }, + { + specifier: "@opentui/core", + dest: path.join(tuiNodeModules, "@opentui", "core"), + }, +] + +for (const link of links) { + const target = resolveHostPackage(link.specifier) + fs.mkdirSync(path.dirname(link.dest), { recursive: true }) + fs.rmSync(link.dest, { recursive: true, force: true }) + fs.symlinkSync(target, link.dest, "dir") + + const pluginResolved = resolvePluginPackage(link.specifier) + const pluginReal = fs.realpathSync.native(pluginResolved) + const targetReal = fs.realpathSync.native(target) + if (pluginReal !== targetReal) { + throw new Error(`Failed to link ${link.specifier}: ${pluginReal} != ${targetReal}`) + } + + console.log(`linked ${link.specifier}`) + console.log(` host: ${targetReal}`) + console.log(` plugin: ${pluginReal}`) +} From c7dfd86a8d7b0c8c05bbe8bd8f11540b681a400a Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 01:54:57 -0400 Subject: [PATCH 07/50] feat(tui): redesign sidebar layout with silent refresh and topic display --- lib/ui/utils.ts | 5 +- tui/data/context.ts | 7 +- tui/shared/types.ts | 1 + tui/slots/sidebar-top.tsx | 217 ++++++++++++++++++-------------------- 4 files changed, 110 insertions(+), 120 deletions(-) diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts index a3de072b..ad31542c 100644 --- a/lib/ui/utils.ts +++ b/lib/ui/utils.ts @@ -144,7 +144,10 @@ export function formatStatsHeader(totalTokensSaved: number, pruneTokenCounter: n export function formatTokenCount(tokens: number, compact?: boolean): string { const suffix = compact ? "" : " tokens" - if (tokens >= 1000) { + if (tokens >= 100_000) { + return `${Math.round(tokens / 1000)}K` + suffix + } + if (tokens >= 10_000 || (compact && tokens >= 1000)) { return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + suffix } return tokens.toString() + suffix diff --git a/tui/data/context.ts b/tui/data/context.ts index b9cf5ccf..71e6d689 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -58,6 +58,7 @@ export const createPlaceholderContextSnapshot = ( available: false, activeBlockCount: 0, activeBlockTopics: [], + activeBlockTopicTotal: 0, }, notes, loadedAt: Date.now(), @@ -245,12 +246,13 @@ export const loadContextSnapshot = async ( const { state, persisted } = await buildState(sessionID, messages) const breakdown = analyzeTokens(state, messages) - const topics = Array.from(state.prune.messages.activeBlockIds) + const allTopics = Array.from(state.prune.messages.activeBlockIds) .map((blockID) => state.prune.messages.blocksById.get(blockID)) .filter((block): block is NonNullable => !!block) .map((block) => block.topic) .filter((topic) => !!topic) - .slice(0, 3) + const topics = allTopics.slice(0, 5) + const topicTotal = allTopics.length const notes: string[] = [] if (persisted) { @@ -269,6 +271,7 @@ export const loadContextSnapshot = async ( available: !!persisted, activeBlockCount: state.prune.messages.activeBlockIds.size, activeBlockTopics: topics, + activeBlockTopicTotal: topicTotal, lastUpdated: persisted?.lastUpdated, }, notes, diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 2d95843a..c67c8484 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -42,6 +42,7 @@ export interface DcpPersistedSummary { available: boolean activeBlockCount: number activeBlockTopics: string[] + activeBlockTopicTotal: number lastUpdated?: string } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index c24f76c1..07bf2d0d 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -14,6 +14,11 @@ import { getPalette, type DcpPalette } from "../shared/theme" import type { DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" const BAR_WIDTH = 12 +// Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5) +const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5 + +const truncate = (text: string, max: number) => + text.length > max ? text.slice(0, max - 3) + "..." : text const REFRESH_DEBOUNCE_MS = 100 const toneColor = ( @@ -40,17 +45,16 @@ const SummaryRow = (props: { label: string value: string tone?: "text" | "muted" | "accent" | "success" | "warning" + marginTop?: number }) => { return ( - {props.label} + {props.label} {props.value} @@ -69,12 +73,15 @@ const SidebarContextBar = (props: { const percent = createMemo(() => props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%", ) - const label = createMemo(() => props.label.padEnd(8, " ")) + const label = createMemo(() => props.label.padEnd(9, " ")) const bar = createMemo(() => buildBar(props.value, props.total, props.char)) return ( - {`${label()} ${percent().padStart(4, " ")} |${bar()}| ${compactTokenCount(props.value)}`} + + {label()} + + {` ${percent().padStart(4, " ")} |${bar()}| ${compactTokenCount(props.value).padStart(5, " ")}`} + + ) } @@ -149,6 +156,7 @@ const SidebarContext = (props: { } const cached = peekContextSnapshot(sessionID) + let silentRefresh = false if (cached) { void props.logger.debug("Sidebar using cached snapshot before reload", { sessionID, @@ -159,18 +167,24 @@ const SidebarContext = (props: { setLoading(false) } else { const current = untrack(snapshot) - if (!preserveSnapshot || current?.sessionID !== sessionID) { + if (preserveSnapshot && current?.sessionID === sessionID) { + silentRefresh = true + void props.logger.debug("Sidebar silent refresh, keeping current snapshot", { + sessionID, + }) + } else { setSnapshot(createPlaceholderContextSnapshot(sessionID, ["Loading DCP context..."])) + setLoading(true) + void props.logger.debug("Sidebar entering loading state", { + sessionID, + hadCurrentSnapshot: !!current, + }) } - setLoading(true) - void props.logger.debug("Sidebar entering loading state", { - sessionID, - hadCurrentSnapshot: !!current, - preservedSnapshot: preserveSnapshot && current?.sessionID === sessionID, - }) } setError(undefined) - requestRender("refresh-start", { sessionID, reason }) + if (!silentRefresh) { + requestRender("refresh-start", { sessionID, reason }) + } const currentRequest = ++requestVersion void props.logger.debug("Sidebar refresh request issued", { @@ -357,44 +371,14 @@ const SidebarContext = (props: { ), ) - const prunedItems = createMemo(() => { - const value = snapshot() - const parts: string[] = [] - if (value.breakdown.prunedToolCount > 0) { - parts.push( - `${value.breakdown.prunedToolCount} tool${value.breakdown.prunedToolCount === 1 ? "" : "s"}`, - ) - } - if (value.breakdown.prunedMessageCount > 0) { - parts.push( - `${value.breakdown.prunedMessageCount} msg${value.breakdown.prunedMessageCount === 1 ? "" : "s"}`, - ) - } - return parts.length > 0 ? `${parts.join(", ")} pruned` : "No pruned items" - }) - const blockSummary = createMemo(() => { return `${snapshot().persisted.activeBlockCount}` }) - const topicLine = createMemo(() => { - const value = snapshot() - if (!value.persisted.activeBlockTopics.length) return "" - return `Topics: ${value.persisted.activeBlockTopics.join(" | ")}` - }) - - const noteLine = createMemo(() => { - const topic = topicLine() - if (topic) return topic - return snapshot().notes[0] ?? "" - }) - - const stateLine = createMemo(() => { - if (error() && snapshot().breakdown.total === 0) return "DCP context failed to load." - if (error()) return `Refresh failed: ${error()}` - if (loading()) return "Loading DCP context..." - return "DCP context loaded." - }) + const topics = createMemo(() => snapshot().persisted.activeBlockTopics) + const topicTotal = createMemo(() => snapshot().persisted.activeBlockTopicTotal) + const topicOverflow = createMemo(() => topicTotal() - topics().length) + const fallbackNote = createMemo(() => snapshot().notes[0] ?? "") const status = createMemo(() => { if (error() && snapshot().breakdown.total > 0) @@ -411,7 +395,7 @@ const SidebarContext = (props: { width="100%" flexDirection="column" gap={0} - backgroundColor={props.palette.base} + backgroundColor={props.palette.surface} border={{ type: "single" }} borderColor={props.palette.accent} paddingTop={1} @@ -427,87 +411,86 @@ const SidebarContext = (props: { {props.config.label} - click for more + click for more {status().label} - session {props.sessionID().slice(0, 18)} - - - - - {stateLine()} + + {props.sessionID().length > 27 + ? props.sessionID().slice(0, 27) + "..." + : props.sessionID()} + + + - - Current - - ~{compactTokenCount(snapshot().breakdown.total)} - - - - + + + - - - - - - - - - {prunedItems()} - {noteLine()} - + + {topics().length > 0 ? ( + <> + + Compressed Topics + + {topics().map((t) => ( + {truncate(t, CONTENT_WIDTH)} + ))} + {topicOverflow() > 0 ? ( + + ... {topicOverflow()} more topics + + ) : null} + + ) : fallbackNote() ? ( + {fallbackNote()} + ) : null} ) From ea6813c97c60a86eabb6be4691ab4b58e5729c0d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 15:25:15 -0400 Subject: [PATCH 08/50] refactor(lib): extract shared analyzeTokens module --- lib/analysis/tokens.ts | 225 ++++++++++++++++++++++++++++++++++++++++ lib/commands/context.ts | 181 +------------------------------- tui/data/context.ts | 163 +---------------------------- tui/shared/types.ts | 18 +--- 4 files changed, 239 insertions(+), 348 deletions(-) create mode 100644 lib/analysis/tokens.ts diff --git a/lib/analysis/tokens.ts b/lib/analysis/tokens.ts new file mode 100644 index 00000000..354ef83a --- /dev/null +++ b/lib/analysis/tokens.ts @@ -0,0 +1,225 @@ +/** + * Shared Token Analysis + * Computes a breakdown of token usage across categories for a session. + * + * TOKEN CALCULATION STRATEGY + * ========================== + * We minimize tokenizer estimation by leveraging API-reported values wherever possible. + * + * WHAT WE GET FROM THE API (exact): + * - tokens.input : Input tokens for each assistant response + * - tokens.output : Output tokens generated (includes text + tool calls) + * - tokens.reasoning: Reasoning tokens used + * - tokens.cache : Cache read/write tokens + * + * HOW WE CALCULATE EACH CATEGORY: + * + * SYSTEM = firstAssistant.input + cache.read + cache.write - tokenizer(firstUserMessage) + * The first response's total input (input + cache.read + cache.write) + * contains system + first user message. On the first request of a + * session, the system prompt appears in cache.write (cache creation), + * not cache.read. + * + * TOOLS = tokenizer(toolInputs + toolOutputs) - prunedTokens + * We must tokenize tools anyway for pruning decisions. + * + * USER = tokenizer(all user messages) + * User messages are typically small, so estimation is acceptable. + * + * ASSISTANT = total - system - user - tools + * Calculated as residual. This absorbs: + * - Assistant text output tokens + * - Reasoning tokens (if persisted by the model) + * - Any estimation errors + * + * TOTAL = input + output + reasoning + cache.read + cache.write + * Matches opencode's UI display. + * + * WHY ASSISTANT IS THE RESIDUAL: + * If reasoning tokens persist in context (model-dependent), they semantically + * belong with "Assistant" since reasoning IS assistant-generated content. + */ + +import type { AssistantMessage, TextPart, ToolPart } from "@opencode-ai/sdk/v2" +import type { SessionState, WithParts } from "../state" +import { isIgnoredUserMessage } from "../messages/query" +import { isMessageCompacted } from "../state/utils" +import { countTokens, extractCompletedToolOutput } from "../token-utils" + +export type MessageStatus = "active" | "pruned" + +export interface TokenBreakdown { + system: number + user: number + assistant: number + tools: number + toolCount: number + toolsInContextCount: number + prunedTokens: number + prunedToolCount: number + prunedMessageCount: number + total: number + messageCount: number +} + +export interface TokenAnalysis { + breakdown: TokenBreakdown + messageStatuses: MessageStatus[] +} + +export function emptyBreakdown(): TokenBreakdown { + return { + system: 0, + user: 0, + assistant: 0, + tools: 0, + toolCount: 0, + toolsInContextCount: 0, + prunedTokens: 0, + prunedToolCount: 0, + prunedMessageCount: 0, + total: 0, + messageCount: 0, + } +} + +export function analyzeTokens(state: SessionState, messages: WithParts[]): TokenAnalysis { + const breakdown = emptyBreakdown() + const messageStatuses: MessageStatus[] = [] + breakdown.prunedTokens = state.stats.totalPruneTokens + + let firstAssistant: AssistantMessage | undefined + for (const msg of messages) { + if (msg.info.role !== "assistant") continue + const assistantInfo = msg.info as AssistantMessage + if ( + assistantInfo.tokens?.input > 0 || + assistantInfo.tokens?.cache?.read > 0 || + assistantInfo.tokens?.cache?.write > 0 + ) { + firstAssistant = assistantInfo + break + } + } + + let lastAssistant: AssistantMessage | undefined + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i] + if (msg.info.role !== "assistant") continue + const assistantInfo = msg.info as AssistantMessage + if (assistantInfo.tokens?.output > 0) { + lastAssistant = assistantInfo + break + } + } + + const apiInput = lastAssistant?.tokens?.input || 0 + const apiOutput = lastAssistant?.tokens?.output || 0 + const apiReasoning = lastAssistant?.tokens?.reasoning || 0 + const apiCacheRead = lastAssistant?.tokens?.cache?.read || 0 + const apiCacheWrite = lastAssistant?.tokens?.cache?.write || 0 + breakdown.total = apiInput + apiOutput + apiReasoning + apiCacheRead + apiCacheWrite + + const userTextParts: string[] = [] + const toolInputParts: string[] = [] + const toolOutputParts: string[] = [] + const allToolIds = new Set() + const activeToolIds = new Set() + const prunedByMessageToolIds = new Set() + const allMessageIds = new Set() + + let firstUserText = "" + let foundFirstUser = false + + for (const msg of messages) { + const ignoredUser = msg.info.role === "user" && isIgnoredUserMessage(msg) + if (ignoredUser) continue + + allMessageIds.add(msg.info.id) + const parts = Array.isArray(msg.parts) ? msg.parts : [] + const compacted = isMessageCompacted(state, msg) + const pruneEntry = state.prune.messages.byMessageId.get(msg.info.id) + const messagePruned = !!pruneEntry && pruneEntry.activeBlockIds.length > 0 + const messageActive = !compacted && !messagePruned + + breakdown.messageCount += 1 + messageStatuses.push(messageActive ? "active" : "pruned") + + for (const part of parts) { + if (part.type === "tool") { + const toolPart = part as ToolPart + if (toolPart.callID) { + allToolIds.add(toolPart.callID) + if (!compacted) activeToolIds.add(toolPart.callID) + if (messagePruned) prunedByMessageToolIds.add(toolPart.callID) + } + + const toolPruned = toolPart.callID && state.prune.tools.has(toolPart.callID) + if (!compacted && !toolPruned) { + if (toolPart.state?.input) { + const inputText = + typeof toolPart.state.input === "string" + ? toolPart.state.input + : JSON.stringify(toolPart.state.input) + toolInputParts.push(inputText) + } + const outputText = extractCompletedToolOutput(toolPart) + if (outputText !== undefined) { + toolOutputParts.push(outputText) + } + } + continue + } + + if (part.type === "text" && msg.info.role === "user" && !compacted) { + const textPart = part as TextPart + const text = textPart.text || "" + userTextParts.push(text) + if (!foundFirstUser) firstUserText += text + } + } + + if (msg.info.role === "user" && !foundFirstUser) { + foundFirstUser = true + } + } + + const prunedByToolIds = new Set() + for (const toolID of allToolIds) { + if (state.prune.tools.has(toolID)) prunedByToolIds.add(toolID) + } + + const prunedToolIds = new Set([...prunedByToolIds, ...prunedByMessageToolIds]) + breakdown.toolCount = allToolIds.size + breakdown.toolsInContextCount = [...activeToolIds].filter( + (id) => !prunedByToolIds.has(id), + ).length + breakdown.prunedToolCount = prunedToolIds.size + + for (const [messageID, entry] of state.prune.messages.byMessageId) { + if (allMessageIds.has(messageID) && entry.activeBlockIds.length > 0) { + breakdown.prunedMessageCount += 1 + } + } + + const firstUserTokens = countTokens(firstUserText) + breakdown.user = countTokens(userTextParts.join("\n")) + const toolInputTokens = countTokens(toolInputParts.join("\n")) + const toolOutputTokens = countTokens(toolOutputParts.join("\n")) + + if (firstAssistant) { + const firstInput = + (firstAssistant.tokens?.input || 0) + + (firstAssistant.tokens?.cache?.read || 0) + + (firstAssistant.tokens?.cache?.write || 0) + breakdown.system = Math.max(0, firstInput - firstUserTokens) + } + + breakdown.tools = toolInputTokens + toolOutputTokens + breakdown.assistant = Math.max( + 0, + breakdown.total - breakdown.system - breakdown.user - breakdown.tools, + ) + + return { breakdown, messageStatuses } +} diff --git a/lib/commands/context.ts b/lib/commands/context.ts index c4f0408e..b297c719 100644 --- a/lib/commands/context.ts +++ b/lib/commands/context.ts @@ -1,6 +1,7 @@ /** * DCP Context Command * Shows a visual breakdown of token usage in the current session. + * Token calculation logic lives in ../analysis/tokens.ts * * TOKEN CALCULATION STRATEGY * ========================== @@ -44,10 +45,8 @@ import type { Logger } from "../logger" import type { SessionState, WithParts } from "../state" import { sendIgnoredMessage } from "../ui/notification" import { formatTokenCount } from "../ui/utils" -import { isIgnoredUserMessage } from "../messages/query" -import { isMessageCompacted } from "../state/utils" -import { countTokens, extractCompletedToolOutput, getCurrentParams } from "../token-utils" -import type { AssistantMessage, TextPart, ToolPart } from "@opencode-ai/sdk/v2" +import { getCurrentParams } from "../token-utils" +import { analyzeTokens, type TokenBreakdown } from "../analysis/tokens" export interface ContextCommandContext { client: any @@ -56,178 +55,6 @@ export interface ContextCommandContext { sessionId: string messages: WithParts[] } - -interface TokenBreakdown { - system: number - user: number - assistant: number - tools: number - toolCount: number - toolsInContextCount: number - prunedTokens: number - prunedToolCount: number - prunedMessageCount: number - total: number -} - -function analyzeTokens(state: SessionState, messages: WithParts[]): TokenBreakdown { - const breakdown: TokenBreakdown = { - system: 0, - user: 0, - assistant: 0, - tools: 0, - toolCount: 0, - toolsInContextCount: 0, - prunedTokens: state.stats.totalPruneTokens, - prunedToolCount: 0, - prunedMessageCount: 0, - total: 0, - } - - let firstAssistant: AssistantMessage | undefined - for (const msg of messages) { - if (msg.info.role === "assistant") { - const assistantInfo = msg.info as AssistantMessage - if ( - assistantInfo.tokens?.input > 0 || - assistantInfo.tokens?.cache?.read > 0 || - assistantInfo.tokens?.cache?.write > 0 - ) { - firstAssistant = assistantInfo - break - } - } - } - - let lastAssistant: AssistantMessage | undefined - for (let i = messages.length - 1; i >= 0; i--) { - const msg = messages[i] - if (msg.info.role === "assistant") { - const assistantInfo = msg.info as AssistantMessage - if (assistantInfo.tokens?.output > 0) { - lastAssistant = assistantInfo - break - } - } - } - - const apiInput = lastAssistant?.tokens?.input || 0 - const apiOutput = lastAssistant?.tokens?.output || 0 - const apiReasoning = lastAssistant?.tokens?.reasoning || 0 - const apiCacheRead = lastAssistant?.tokens?.cache?.read || 0 - const apiCacheWrite = lastAssistant?.tokens?.cache?.write || 0 - breakdown.total = apiInput + apiOutput + apiReasoning + apiCacheRead + apiCacheWrite - - const userTextParts: string[] = [] - const toolInputParts: string[] = [] - const toolOutputParts: string[] = [] - let firstUserText = "" - let foundFirstUser = false - const allToolIds = new Set() - const activeToolIds = new Set() - const prunedByMessageToolIds = new Set() - const allMessageIds = new Set() - - for (const msg of messages) { - allMessageIds.add(msg.info.id) - const parts = Array.isArray(msg.parts) ? msg.parts : [] - const isCompacted = isMessageCompacted(state, msg) - const pruneEntry = state.prune.messages.byMessageId.get(msg.info.id) - const isMessagePruned = !!pruneEntry && pruneEntry.activeBlockIds.length > 0 - const isIgnoredUser = isIgnoredUserMessage(msg) - - for (const part of parts) { - if (part.type === "tool") { - const toolPart = part as ToolPart - if (toolPart.callID) { - allToolIds.add(toolPart.callID) - if (!isCompacted) { - activeToolIds.add(toolPart.callID) - } - if (isMessagePruned) { - prunedByMessageToolIds.add(toolPart.callID) - } - } - - const isPruned = toolPart.callID && state.prune.tools.has(toolPart.callID) - if (!isCompacted && !isPruned) { - if (toolPart.state?.input) { - const inputStr = - typeof toolPart.state.input === "string" - ? toolPart.state.input - : JSON.stringify(toolPart.state.input) - toolInputParts.push(inputStr) - } - - const outputStr = extractCompletedToolOutput(toolPart) - if (outputStr !== undefined) { - toolOutputParts.push(outputStr) - } - } - } else if ( - part.type === "text" && - msg.info.role === "user" && - !isCompacted && - !isIgnoredUser - ) { - const textPart = part as TextPart - const text = textPart.text || "" - userTextParts.push(text) - if (!foundFirstUser) { - firstUserText += text - } - } - } - - if (msg.info.role === "user" && !isIgnoredUser && !foundFirstUser) { - foundFirstUser = true - } - } - - const prunedByToolIds = new Set() - for (const id of allToolIds) { - if (state.prune.tools.has(id)) { - prunedByToolIds.add(id) - } - } - - const prunedToolIds = new Set([...prunedByToolIds, ...prunedByMessageToolIds]) - const toolsInContextCount = [...activeToolIds].filter((id) => !prunedByToolIds.has(id)).length - - let prunedMessageCount = 0 - for (const [id, entry] of state.prune.messages.byMessageId) { - if (allMessageIds.has(id) && entry.activeBlockIds.length > 0) { - prunedMessageCount++ - } - } - - breakdown.toolCount = allToolIds.size - breakdown.toolsInContextCount = toolsInContextCount - breakdown.prunedToolCount = prunedToolIds.size - breakdown.prunedMessageCount = prunedMessageCount - - const firstUserTokens = countTokens(firstUserText) - breakdown.user = countTokens(userTextParts.join("\n")) - const toolInputTokens = countTokens(toolInputParts.join("\n")) - const toolOutputTokens = countTokens(toolOutputParts.join("\n")) - - if (firstAssistant) { - const firstInput = - (firstAssistant.tokens?.input || 0) + - (firstAssistant.tokens?.cache?.read || 0) + - (firstAssistant.tokens?.cache?.write || 0) - breakdown.system = Math.max(0, firstInput - firstUserTokens) - } - - breakdown.tools = toolInputTokens + toolOutputTokens - breakdown.assistant = Math.max( - 0, - breakdown.total - breakdown.system - breakdown.user - breakdown.tools, - ) - - return breakdown -} - function createBar(value: number, maxValue: number, width: number, char: string = "█"): string { if (maxValue === 0) return "" const filled = Math.round((value / maxValue) * width) @@ -296,7 +123,7 @@ function formatContextMessage(breakdown: TokenBreakdown): string { export async function handleContextCommand(ctx: ContextCommandContext): Promise { const { client, state, logger, sessionId, messages } = ctx - const breakdown = analyzeTokens(state, messages) + const { breakdown } = analyzeTokens(state, messages) const message = formatContextMessage(breakdown) diff --git a/tui/data/context.ts b/tui/data/context.ts index 71e6d689..b76eb673 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -1,7 +1,4 @@ -import type { AssistantMessage, TextPart, ToolPart } from "@opencode-ai/sdk/v2" import { Logger } from "../../lib/logger" -import { isIgnoredUserMessage } from "../../lib/messages/utils" -import { countTokens } from "../../lib/strategies/utils" import { createSessionState, loadSessionState, @@ -13,8 +10,8 @@ import { loadPruneMap, loadPruneMessagesState, } from "../../lib/state/utils" -import { isMessageCompacted } from "../../lib/shared-utils" -import type { DcpContextBreakdown, DcpContextSnapshot, DcpTuiClient } from "../shared/types" +import { analyzeTokens, emptyBreakdown } from "../../lib/analysis/tokens" +import type { DcpContextSnapshot, DcpTuiClient } from "../shared/types" let logger = new Logger(false, "TUI") const snapshotCache = new Map() @@ -34,20 +31,6 @@ export const setContextLogger = (nextLogger: Logger) => { logger = nextLogger } -const emptyBreakdown = (): DcpContextBreakdown => ({ - system: 0, - user: 0, - assistant: 0, - tools: 0, - toolCount: 0, - toolsInContextCount: 0, - prunedTokens: 0, - prunedToolCount: 0, - prunedMessageCount: 0, - total: 0, - messageCount: 0, -}) - export const createPlaceholderContextSnapshot = ( sessionID?: string, notes: string[] = [], @@ -60,6 +43,7 @@ export const createPlaceholderContextSnapshot = ( activeBlockTopics: [], activeBlockTopicTotal: 0, }, + messageStatuses: [], notes, loadedAt: Date.now(), }) @@ -84,144 +68,6 @@ const buildState = async ( } } -const analyzeTokens = (state: SessionState, messages: WithParts[]): DcpContextBreakdown => { - const breakdown = emptyBreakdown() - breakdown.prunedTokens = state.stats.totalPruneTokens - breakdown.messageCount = messages.length - - let firstAssistant: AssistantMessage | undefined - for (const msg of messages) { - if (msg.info.role !== "assistant") continue - const assistantInfo = msg.info as AssistantMessage - if ( - assistantInfo.tokens?.input > 0 || - assistantInfo.tokens?.cache?.read > 0 || - assistantInfo.tokens?.cache?.write > 0 - ) { - firstAssistant = assistantInfo - break - } - } - - let lastAssistant: AssistantMessage | undefined - for (let i = messages.length - 1; i >= 0; i--) { - const msg = messages[i] - if (msg.info.role !== "assistant") continue - const assistantInfo = msg.info as AssistantMessage - if (assistantInfo.tokens?.output > 0) { - lastAssistant = assistantInfo - break - } - } - - const apiInput = lastAssistant?.tokens?.input || 0 - const apiOutput = lastAssistant?.tokens?.output || 0 - const apiReasoning = lastAssistant?.tokens?.reasoning || 0 - const apiCacheRead = lastAssistant?.tokens?.cache?.read || 0 - const apiCacheWrite = lastAssistant?.tokens?.cache?.write || 0 - breakdown.total = apiInput + apiOutput + apiReasoning + apiCacheRead + apiCacheWrite - - const userTextParts: string[] = [] - const toolInputParts: string[] = [] - const toolOutputParts: string[] = [] - const allToolIds = new Set() - const activeToolIds = new Set() - const prunedByMessageToolIds = new Set() - const allMessageIds = new Set() - - let firstUserText = "" - let foundFirstUser = false - - for (const msg of messages) { - allMessageIds.add(msg.info.id) - const parts = Array.isArray(msg.parts) ? msg.parts : [] - const compacted = isMessageCompacted(state, msg) - const pruneEntry = state.prune.messages.byMessageId.get(msg.info.id) - const messagePruned = !!pruneEntry && pruneEntry.activeBlockIds.length > 0 - const ignoredUser = msg.info.role === "user" && isIgnoredUserMessage(msg) - - for (const part of parts) { - if (part.type === "tool") { - const toolPart = part as ToolPart - if (toolPart.callID) { - allToolIds.add(toolPart.callID) - if (!compacted) activeToolIds.add(toolPart.callID) - if (messagePruned) prunedByMessageToolIds.add(toolPart.callID) - } - - const toolPruned = toolPart.callID && state.prune.tools.has(toolPart.callID) - if (!compacted && !toolPruned) { - if (toolPart.state?.input) { - const inputText = - typeof toolPart.state.input === "string" - ? toolPart.state.input - : JSON.stringify(toolPart.state.input) - toolInputParts.push(inputText) - } - if (toolPart.state?.status === "completed" && toolPart.state?.output) { - const outputText = - typeof toolPart.state.output === "string" - ? toolPart.state.output - : JSON.stringify(toolPart.state.output) - toolOutputParts.push(outputText) - } - } - continue - } - - if (part.type === "text" && msg.info.role === "user" && !compacted && !ignoredUser) { - const textPart = part as TextPart - const text = textPart.text || "" - userTextParts.push(text) - if (!foundFirstUser) firstUserText += text - } - } - - if (msg.info.role === "user" && !ignoredUser && !foundFirstUser) { - foundFirstUser = true - } - } - - const prunedByToolIds = new Set() - for (const toolID of allToolIds) { - if (state.prune.tools.has(toolID)) prunedByToolIds.add(toolID) - } - - const prunedToolIds = new Set([...prunedByToolIds, ...prunedByMessageToolIds]) - breakdown.toolCount = allToolIds.size - breakdown.toolsInContextCount = [...activeToolIds].filter( - (id) => !prunedByToolIds.has(id), - ).length - breakdown.prunedToolCount = prunedToolIds.size - - for (const [messageID, entry] of state.prune.messages.byMessageId) { - if (allMessageIds.has(messageID) && entry.activeBlockIds.length > 0) { - breakdown.prunedMessageCount += 1 - } - } - - const firstUserTokens = countTokens(firstUserText) - breakdown.user = countTokens(userTextParts.join("\n")) - const toolInputTokens = countTokens(toolInputParts.join("\n")) - const toolOutputTokens = countTokens(toolOutputParts.join("\n")) - - if (firstAssistant) { - const firstInput = - (firstAssistant.tokens?.input || 0) + - (firstAssistant.tokens?.cache?.read || 0) + - (firstAssistant.tokens?.cache?.write || 0) - breakdown.system = Math.max(0, firstInput - firstUserTokens) - } - - breakdown.tools = toolInputTokens + toolOutputTokens - breakdown.assistant = Math.max( - 0, - breakdown.total - breakdown.system - breakdown.user - breakdown.tools, - ) - - return breakdown -} - export const loadContextSnapshot = async ( client: DcpTuiClient, sessionID?: string, @@ -244,7 +90,7 @@ export const loadContextSnapshot = async ( }) const { state, persisted } = await buildState(sessionID, messages) - const breakdown = analyzeTokens(state, messages) + const { breakdown, messageStatuses } = analyzeTokens(state, messages) const allTopics = Array.from(state.prune.messages.activeBlockIds) .map((blockID) => state.prune.messages.blocksById.get(blockID)) @@ -274,6 +120,7 @@ export const loadContextSnapshot = async ( activeBlockTopicTotal: topicTotal, lastUpdated: persisted?.lastUpdated, }, + messageStatuses, notes, loadedAt: Date.now(), } diff --git a/tui/shared/types.ts b/tui/shared/types.ts index c67c8484..3264dc77 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -24,19 +24,10 @@ export interface DcpRouteParams { source?: string } -export interface DcpContextBreakdown { - system: number - user: number - assistant: number - tools: number - toolCount: number - toolsInContextCount: number - prunedTokens: number - prunedToolCount: number - prunedMessageCount: number - total: number - messageCount: number -} +export { + type TokenBreakdown as DcpContextBreakdown, + type MessageStatus as DcpMessageStatus, +} from "../../lib/analysis/tokens" export interface DcpPersistedSummary { available: boolean @@ -50,6 +41,7 @@ export interface DcpContextSnapshot { sessionID?: string breakdown: DcpContextBreakdown persisted: DcpPersistedSummary + messageStatuses: DcpMessageStatus[] notes: string[] loadedAt: number } From b452bd48a7a1b8afa6c58098331bff5256502f5d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 15:25:23 -0400 Subject: [PATCH 09/50] feat(tui): add message bar, graph improvements, and compact token format --- tui/slots/sidebar-top.tsx | 113 +++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 07bf2d0d..b0c4c9e6 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -10,8 +10,8 @@ import { peekContextSnapshot, } from "../data/context" import { openPanel } from "../shared/navigation" -import { getPalette, type DcpPalette } from "../shared/theme" -import type { DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" +import { getPalette, type DcpColor, type DcpPalette } from "../shared/theme" +import type { DcpMessageStatus, DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" const BAR_WIDTH = 12 // Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5) @@ -32,12 +32,53 @@ const toneColor = ( return palette.text } -const compactTokenCount = (value: number) => formatTokenCount(value).replace(/ tokens$/, "") +const compactTokenCount = (value: number): string => { + if (value >= 100_000) return `${Math.round(value / 1000)}K` + if (value >= 1_000) { + const k = (value / 1000).toFixed(1) + return k.endsWith(".0") ? `${Math.round(value / 1000)}K` : `${k}K` + } + const d = Math.round(value / 100) + if (d >= 10) return `${Math.round(value / 1000)}K` + if (d > 0) return `.${d}K` + return "0" +} -const buildBar = (value: number, total: number, char: string) => { +const buildBar = (value: number, total: number) => { if (total <= 0) return " ".repeat(BAR_WIDTH) const filled = Math.max(0, Math.round((value / total) * BAR_WIDTH)) - return char.repeat(filled).padEnd(BAR_WIDTH, " ") + return "█".repeat(filled).padEnd(BAR_WIDTH, " ") +} + +const buildMessageBar = ( + statuses: DcpMessageStatus[], + width: number = CONTENT_WIDTH, +): { text: string; status: DcpMessageStatus }[] => { + const ACTIVE = "█" + const PRUNED = "░" + if (statuses.length === 0) return [{ text: PRUNED.repeat(width), status: "pruned" }] + + // Map each bar position to a message status + const bar: DcpMessageStatus[] = new Array(width).fill("active") + for (let m = 0; m < statuses.length; m++) { + const start = Math.floor((m / statuses.length) * width) + const end = Math.floor(((m + 1) / statuses.length) * width) + for (let i = start; i < end; i++) { + bar[i] = statuses[m] + } + } + + // Group consecutive same-status positions into runs + const runs: { text: string; status: DcpMessageStatus }[] = [] + let runStart = 0 + for (let i = 1; i <= width; i++) { + if (i === width || bar[i] !== bar[runStart]) { + const char = bar[runStart] === "pruned" ? PRUNED : ACTIVE + runs.push({ text: char.repeat(i - runStart), status: bar[runStart] }) + runStart = i + } + } + return runs } const SummaryRow = (props: { @@ -45,6 +86,7 @@ const SummaryRow = (props: { label: string value: string tone?: "text" | "muted" | "accent" | "success" | "warning" + swatch?: DcpColor marginTop?: number }) => { return ( @@ -54,7 +96,10 @@ const SummaryRow = (props: { justifyContent="space-between" marginTop={props.marginTop} > - {props.label} + + {props.swatch && {"█ "}} + {props.label} + {props.value} @@ -67,20 +112,23 @@ const SidebarContextBar = (props: { label: string value: number total: number - char: string tone?: "text" | "muted" | "accent" | "success" | "warning" }) => { const percent = createMemo(() => props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%", ) const label = createMemo(() => props.label.padEnd(9, " ")) - const bar = createMemo(() => buildBar(props.value, props.total, props.char)) + const bar = createMemo(() => buildBar(props.value, props.total)) return ( - {label()} - - {` ${percent().padStart(4, " ")} |${bar()}| ${compactTokenCount(props.value).padStart(5, " ")}`} + + {label()} + {` ${percent().padStart(4, " ")} |`} + {bar()} + {`| ${compactTokenCount(props.value).padStart(5, " ")}`} ) } @@ -371,15 +419,13 @@ const SidebarContext = (props: { ), ) - const blockSummary = createMemo(() => { - return `${snapshot().persisted.activeBlockCount}` - }) - const topics = createMemo(() => snapshot().persisted.activeBlockTopics) const topicTotal = createMemo(() => snapshot().persisted.activeBlockTopicTotal) const topicOverflow = createMemo(() => topicTotal() - topics().length) const fallbackNote = createMemo(() => snapshot().notes[0] ?? "") + const messageBarRuns = createMemo(() => buildMessageBar(snapshot().messageStatuses)) + const status = createMemo(() => { if (error() && snapshot().breakdown.total > 0) return { label: "cached", tone: "warning" as const } @@ -394,7 +440,6 @@ const SidebarContext = (props: { {status().label} - - - {props.sessionID().length > 27 - ? props.sessionID().slice(0, 27) + "..." - : props.sessionID()} - - - - + {snapshot().messageStatuses.length > 0 && ( + + {messageBarRuns().map((run) => ( + + {run.text} + + ))} + + )} + + From 0e615244e5db003f52dc598cd475e7b1fd0a44ef Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 15:38:53 -0400 Subject: [PATCH 10/50] refactor(tui): consolidate sidebar helpers --- tui/components/metric-row.tsx | 17 +++++++---------- tui/data/context.ts | 2 +- tui/shared/theme.ts | 10 ++++++++++ tui/slots/sidebar-top.tsx | 17 ++--------------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/tui/components/metric-row.tsx b/tui/components/metric-row.tsx index 2474905d..c3b03bf9 100644 --- a/tui/components/metric-row.tsx +++ b/tui/components/metric-row.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @opentui/solid */ -import type { DcpPalette } from "../shared/theme" +import { toneColor, type DcpPalette, type DcpTone } from "../shared/theme" const pad = (value: string, width: number) => { if (value.length >= width) return value @@ -10,14 +10,11 @@ export const MetricRow = (props: { palette: DcpPalette label: string value: string - tone?: "text" | "muted" | "accent" + tone?: DcpTone }) => { - const fg = - props.tone === "accent" - ? props.palette.accent - : props.tone === "muted" - ? props.palette.muted - : props.palette.text - - return {`${pad(props.label, 18)} ${props.value}`} + return ( + {`${pad(props.label, 18)} ${props.value}`} + ) } diff --git a/tui/data/context.ts b/tui/data/context.ts index b76eb673..d55a686f 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -68,7 +68,7 @@ const buildState = async ( } } -export const loadContextSnapshot = async ( +const loadContextSnapshot = async ( client: DcpTuiClient, sessionID?: string, ): Promise => { diff --git a/tui/shared/theme.ts b/tui/shared/theme.ts index 10da6467..fa73b106 100644 --- a/tui/shared/theme.ts +++ b/tui/shared/theme.ts @@ -14,6 +14,8 @@ export interface DcpPalette { warning: DcpColor } +export type DcpTone = "text" | "muted" | "accent" | "success" | "warning" + const defaults = { panel: "#111111", base: "#1d1d1d", @@ -46,3 +48,11 @@ export const getPalette = (theme: Record): DcpPalette => { warning: get("warning", defaults.warning), } } + +export const toneColor = (palette: DcpPalette, tone: DcpTone = "text") => { + if (tone === "accent") return palette.accent + if (tone === "success") return palette.success + if (tone === "warning") return palette.warning + if (tone === "muted") return palette.muted + return palette.text +} diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index b0c4c9e6..6d4b8f3d 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -2,7 +2,7 @@ import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" import type { TuiApi, TuiPluginInput } from "@opencode-ai/plugin/tui" import { Logger } from "../../lib/logger" -import { formatTokenCount } from "../../lib/ui/utils" +import { truncate } from "../../lib/ui/utils" import { createPlaceholderContextSnapshot, invalidateContextSnapshot, @@ -10,28 +10,15 @@ import { peekContextSnapshot, } from "../data/context" import { openPanel } from "../shared/navigation" -import { getPalette, type DcpColor, type DcpPalette } from "../shared/theme" +import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared/theme" import type { DcpMessageStatus, DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" const BAR_WIDTH = 12 // Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5) const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5 -const truncate = (text: string, max: number) => - text.length > max ? text.slice(0, max - 3) + "..." : text const REFRESH_DEBOUNCE_MS = 100 -const toneColor = ( - palette: DcpPalette, - tone: "text" | "muted" | "accent" | "success" | "warning" = "text", -) => { - if (tone === "accent") return palette.accent - if (tone === "success") return palette.success - if (tone === "warning") return palette.warning - if (tone === "muted") return palette.muted - return palette.text -} - const compactTokenCount = (value: number): string => { if (value >= 100_000) return `${Math.round(value / 1000)}K` if (value >= 1_000) { From 7759b445ab1246ebf7eca92aa7f85b69f55d7eef Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 18:36:31 -0400 Subject: [PATCH 11/50] feat(tui): add all-time stats and rename context label --- tui/data/context.ts | 11 ++++++++++- tui/shared/types.ts | 6 ++++++ tui/slots/sidebar-top.tsx | 26 +++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tui/data/context.ts b/tui/data/context.ts index d55a686f..4c55dcd6 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -10,6 +10,7 @@ import { loadPruneMap, loadPruneMessagesState, } from "../../lib/state/utils" +import { loadAllSessionStats } from "../../lib/state/persistence" import { analyzeTokens, emptyBreakdown } from "../../lib/analysis/tokens" import type { DcpContextSnapshot, DcpTuiClient } from "../shared/types" @@ -44,6 +45,7 @@ export const createPlaceholderContextSnapshot = ( activeBlockTopicTotal: 0, }, messageStatuses: [], + allTimeStats: { totalTokensSaved: 0, sessionCount: 0 }, notes, loadedAt: Date.now(), }) @@ -90,7 +92,10 @@ const loadContextSnapshot = async ( }) const { state, persisted } = await buildState(sessionID, messages) - const { breakdown, messageStatuses } = analyzeTokens(state, messages) + const [{ breakdown, messageStatuses }, aggregated] = await Promise.all([ + Promise.resolve(analyzeTokens(state, messages)), + loadAllSessionStats(logger), + ]) const allTopics = Array.from(state.prune.messages.activeBlockIds) .map((blockID) => state.prune.messages.blocksById.get(blockID)) @@ -121,6 +126,10 @@ const loadContextSnapshot = async ( lastUpdated: persisted?.lastUpdated, }, messageStatuses, + allTimeStats: { + totalTokensSaved: aggregated.totalTokens, + sessionCount: aggregated.sessionCount, + }, notes, loadedAt: Date.now(), } diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 3264dc77..dc5e5540 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -37,11 +37,17 @@ export interface DcpPersistedSummary { lastUpdated?: string } +export interface DcpAllTimeStats { + totalTokensSaved: number + sessionCount: number +} + export interface DcpContextSnapshot { sessionID?: string breakdown: DcpContextBreakdown persisted: DcpPersistedSummary messageStatuses: DcpMessageStatus[] + allTimeStats: DcpAllTimeStats notes: string[] loadedAt: number } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 6d4b8f3d..ff44b359 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -20,6 +20,10 @@ const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5 const REFRESH_DEBOUNCE_MS = 100 const compactTokenCount = (value: number): string => { + if (value >= 1_000_000) { + const m = (value / 1_000_000).toFixed(2) + return `${m}M` + } if (value >= 100_000) return `${Math.round(value / 1000)}K` if (value >= 1_000) { const k = (value / 1000).toFixed(1) @@ -458,7 +462,7 @@ const SidebarContext = (props: { /> {fallbackNote()} ) : null} + + {snapshot().allTimeStats.sessionCount > 0 && ( + + + All Time + + + + + )} ) } From 7b5a8baa125bb67c19363a8a71a50cd56d9668dc Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 18:54:17 -0400 Subject: [PATCH 12/50] fix(tui): remove ctrl+h keybind from panel --- tui/commands.ts | 2 +- tui/routes/panel.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tui/commands.ts b/tui/commands.ts index af6a792d..1098de57 100644 --- a/tui/commands.ts +++ b/tui/commands.ts @@ -3,7 +3,7 @@ import { openPanel } from "./shared/navigation" import type { DcpRouteNames, DcpTuiConfig } from "./shared/types" export const registerCommands = (api: TuiApi, config: DcpTuiConfig, names: DcpRouteNames) => { - const keys = api.keybind?.create({ close: "escape,ctrl+h" }) + const keys = api.keybind?.create({ close: "escape" }) api.command.register(() => [ { title: `${config.label} panel`, diff --git a/tui/routes/panel.tsx b/tui/routes/panel.tsx index 64f7b2f7..f8ae7b57 100644 --- a/tui/routes/panel.tsx +++ b/tui/routes/panel.tsx @@ -18,14 +18,12 @@ const PanelScreen = (props: { const palette = createMemo(() => getPalette(props.api.theme.current as Record)) const sessionID = () => getSessionIDFromParams(props.params) const source = () => getRouteSource(props.params) - const keys = props.api.keybind?.create({ close: "escape,ctrl+h" }) + const keys = props.api.keybind?.create({ close: "escape" }) useKeyboard((evt) => { if (props.api.route.current.name !== props.names.routes.panel) return if (props.api.ui?.dialog?.open) return - const matched = keys - ? keys.match("close", evt) - : evt.name === "escape" || (evt.ctrl && evt.name === "h") + const matched = keys ? keys.match("close", evt) : evt.name === "escape" if (!matched) return evt.preventDefault() evt.stopPropagation() From 86c0e692e4168442692ac6ee6e1a0664652aaf92 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 10 Mar 2026 21:26:34 -0400 Subject: [PATCH 13/50] feat(tui): load tui config from dcp.jsonc Unify TUI behavior with file-based plugin config and ship the TUI entrypoint so sidebar updates actually reach users. --- dcp.schema.json | 21 + index.ts | 8 +- lib/config.ts | 153 +- package-lock.json | 2751 +++++++++++++++++++++++++++- package.json | 6 +- tsconfig.json | 3 +- tui/commands.ts | 8 +- tui/components/screen.tsx | 4 +- tui/components/section.tsx | 4 +- tui/data/context.ts | 64 +- tui/index.tsx | 57 +- tui/routes/panel.tsx | 15 +- tui/shared/config.ts | 20 - tui/shared/names.ts | 24 +- tui/shared/navigation.ts | 3 +- tui/shared/types.ts | 25 +- tui/slots/sidebar-top.tsx | 193 +- tui/types/opencode-plugin-tui.d.ts | 73 + 18 files changed, 3014 insertions(+), 418 deletions(-) delete mode 100644 tui/shared/config.ts create mode 100644 tui/types/opencode-plugin-tui.d.ts diff --git a/dcp.schema.json b/dcp.schema.json index 58495e64..0346231d 100644 --- a/dcp.schema.json +++ b/dcp.schema.json @@ -94,6 +94,27 @@ } } }, + "tui": { + "type": "object", + "description": "Configuration for the DCP TUI integration", + "additionalProperties": false, + "properties": { + "sidebar": { + "type": "boolean", + "default": true, + "description": "Show the DCP sidebar widget in the TUI" + }, + "debug": { + "type": "boolean", + "default": false, + "description": "Enable debug/error logging for the DCP TUI" + } + }, + "default": { + "sidebar": true, + "debug": false + } + }, "experimental": { "type": "object", "description": "Experimental settings that may change in future releases", diff --git a/index.ts b/index.ts index e69357e6..9aa61e32 100644 --- a/index.ts +++ b/index.ts @@ -9,6 +9,7 @@ import { import { Logger } from "./lib/logger" import { createSessionState } from "./lib/state" import { PromptStore } from "./lib/prompts/store" +import tuiPlugin from "./tui/index" import { createChatMessageHandler, createChatMessageTransformHandler, @@ -19,7 +20,7 @@ import { } from "./lib/hooks" import { configureClientAuth, isSecureMode } from "./lib/auth" -const plugin: Plugin = (async (ctx) => { +const server: Plugin = (async (ctx) => { const config = getConfig(ctx) if (!config.enabled) { @@ -133,4 +134,7 @@ const plugin: Plugin = (async (ctx) => { } }) satisfies Plugin -export default plugin +export default { + server, + ...tuiPlugin, +} diff --git a/lib/config.ts b/lib/config.ts index bd19f768..49e9adc9 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -49,6 +49,11 @@ export interface TurnProtection { turns: number } +export interface TuiConfig { + sidebar: boolean + debug: boolean +} + export interface ExperimentalConfig { allowSubAgents: boolean customPrompts: boolean @@ -62,6 +67,7 @@ export interface PluginConfig { commands: Commands manualMode: ManualModeConfig turnProtection: TurnProtection + tui: TuiConfig experimental: ExperimentalConfig protectedFilePatterns: string[] compress: CompressConfig @@ -98,6 +104,9 @@ export const VALID_CONFIG_KEYS = new Set([ "turnProtection", "turnProtection.enabled", "turnProtection.turns", + "tui", + "tui.sidebar", + "tui.debug", "experimental", "experimental.allowSubAgents", "experimental.customPrompts", @@ -161,6 +170,13 @@ interface ValidationError { actual: string } +type ConfigWarningNotifier = (title: string, message: string) => void + +interface ConfigWarningCallbacks { + onParseWarning?: (title: string, message: string) => void + onConfigWarning?: (configPath: string, data: Record, isProject: boolean) => void +} + export function validateConfigTypes(config: Record): ValidationError[] { const errors: ValidationError[] = [] @@ -278,6 +294,32 @@ export function validateConfigTypes(config: Record): ValidationErro } } + const tui = config.tui + if (tui !== undefined) { + if (typeof tui !== "object" || tui === null || Array.isArray(tui)) { + errors.push({ + key: "tui", + expected: "object", + actual: typeof tui, + }) + } else { + if (tui.sidebar !== undefined && typeof tui.sidebar !== "boolean") { + errors.push({ + key: "tui.sidebar", + expected: "boolean", + actual: typeof tui.sidebar, + }) + } + if (tui.debug !== undefined && typeof tui.debug !== "boolean") { + errors.push({ + key: "tui.debug", + expected: "boolean", + actual: typeof tui.debug, + }) + } + } + } + const commands = config.commands if (commands !== undefined) { if (typeof commands !== "object" || commands === null || Array.isArray(commands)) { @@ -592,8 +634,21 @@ export function validateConfigTypes(config: Record): ValidationErro return errors } +function scheduleConfigWarning( + notify: ConfigWarningNotifier | undefined, + title: string, + message: string, +): void { + setTimeout(() => { + if (!notify) return + try { + notify(title, message) + } catch {} + }, 7000) +} + function showConfigWarnings( - ctx: PluginInput, + notify: ConfigWarningNotifier | undefined, configPath: string, configData: Record, isProject: boolean, @@ -623,18 +678,11 @@ function showConfigWarnings( } } - setTimeout(() => { - try { - ctx.client.tui.showToast({ - body: { - title: `DCP: ${configType} warning`, - message: `${configPath}\n${messages.join("\n")}`, - variant: "warning", - duration: 7000, - }, - }) - } catch {} - }, 7000) + scheduleConfigWarning( + notify, + `DCP: ${configType} warning`, + `${configPath}\n${messages.join("\n")}`, + ) } const defaultConfig: PluginConfig = { @@ -650,6 +698,10 @@ const defaultConfig: PluginConfig = { enabled: false, automaticStrategies: true, }, + tui: { + sidebar: true, + debug: false, + }, turnProtection: { enabled: false, turns: 4, @@ -707,7 +759,7 @@ function findOpencodeDir(startDir: string): string | null { return null } -function getConfigPaths(ctx?: PluginInput): { +function getConfigPaths(directory?: string): { global: string | null configDir: string | null project: string | null @@ -731,8 +783,8 @@ function getConfigPaths(ctx?: PluginInput): { } let project: string | null = null - if (ctx?.directory) { - const opencodeDir = findOpencodeDir(ctx.directory) + if (directory) { + const opencodeDir = findOpencodeDir(directory) if (opencodeDir) { const projectJsonc = join(opencodeDir, "dcp.jsonc") const projectJson = join(opencodeDir, "dcp.json") @@ -865,6 +917,18 @@ function mergeManualMode( } } +function mergeTui( + base: PluginConfig["tui"], + override?: Partial, +): PluginConfig["tui"] { + if (override === undefined) return base + + return { + sidebar: override.sidebar ?? base.sidebar, + debug: override.debug ?? base.debug, + } +} + function mergeExperimental( base: PluginConfig["experimental"], override?: Partial, @@ -888,6 +952,7 @@ function deepCloneConfig(config: PluginConfig): PluginConfig { enabled: config.manualMode.enabled, automaticStrategies: config.manualMode.automaticStrategies, }, + tui: { ...config.tui }, turnProtection: { ...config.turnProtection }, experimental: { ...config.experimental }, protectedFilePatterns: [...config.protectedFilePatterns], @@ -916,6 +981,7 @@ function mergeLayer(config: PluginConfig, data: Record): PluginConf debug: data.debug ?? config.debug, pruneNotification: data.pruneNotification ?? config.pruneNotification, pruneNotificationType: data.pruneNotificationType ?? config.pruneNotificationType, + tui: mergeTui(config.tui, data.tui as any), commands: mergeCommands(config.commands, data.commands as any), manualMode: mergeManualMode(config.manualMode, data.manualMode as any), turnProtection: { @@ -931,24 +997,21 @@ function mergeLayer(config: PluginConfig, data: Record): PluginConf } } -function scheduleParseWarning(ctx: PluginInput, title: string, message: string): void { - setTimeout(() => { - try { - ctx.client.tui.showToast({ - body: { - title, - message, - variant: "warning", - duration: 7000, - }, - }) - } catch {} - }, 7000) +function createConfigWarningCallbacks( + notify?: ConfigWarningNotifier, +): ConfigWarningCallbacks | undefined { + if (!notify) return undefined + + return { + onParseWarning: (title, message) => scheduleConfigWarning(notify, title, message), + onConfigWarning: (configPath, data, isProject) => + showConfigWarnings(notify, configPath, data, isProject), + } } -export function getConfig(ctx: PluginInput): PluginConfig { +function loadMergedConfig(directory?: string, callbacks?: ConfigWarningCallbacks): PluginConfig { let config = deepCloneConfig(defaultConfig) - const configPaths = getConfigPaths(ctx) + const configPaths = getConfigPaths(directory) if (!configPaths.global) { createDefaultConfig() @@ -967,8 +1030,7 @@ export function getConfig(ctx: PluginInput): PluginConfig { const result = loadConfigFile(layer.path) if (result.parseError) { - scheduleParseWarning( - ctx, + callbacks?.onParseWarning?.( `DCP: Invalid ${layer.name}`, `${layer.path}\n${result.parseError}\nUsing previous/default values`, ) @@ -979,9 +1041,32 @@ export function getConfig(ctx: PluginInput): PluginConfig { continue } - showConfigWarnings(ctx, layer.path, result.data, layer.isProject) + callbacks?.onConfigWarning?.(layer.path, result.data, layer.isProject) config = mergeLayer(config, result.data) } return config } + +export function getConfigForDirectory( + directory?: string, + notify?: ConfigWarningNotifier, +): PluginConfig { + return loadMergedConfig(directory, createConfigWarningCallbacks(notify)) +} + +export function getConfig(ctx: PluginInput): PluginConfig { + return loadMergedConfig( + ctx.directory, + createConfigWarningCallbacks((title, message) => { + ctx.client.tui.showToast({ + body: { + title, + message, + variant: "warning", + duration: 7000, + }, + }) + }), + ) +} diff --git a/package-lock.json b/package-lock.json index 84453442..416042e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,13 +17,31 @@ }, "devDependencies": { "@opencode-ai/plugin": "^1.3.2", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", "tsx": "^4.21.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@opencode-ai/plugin": ">=0.13.7" + "@opencode-ai/plugin": ">=1.2.0", + "@opentui/core": ">=0.1.87", + "@opentui/solid": ">=0.0.0-20260307" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@anthropic-ai/tokenizer": { @@ -51,6 +69,447 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier2d-simd-compat": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@dimforge/rapier2d-simd-compat/-/rapier2d-simd-compat-0.17.3.tgz", + "integrity": "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", @@ -493,56 +952,1234 @@ "node": ">=18" } }, - "node_modules/@opencode-ai/plugin": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.2.tgz", - "integrity": "sha512-eT0ZovMCOQlfTdAnfbEWgW343mJ9SHgEVfdiOSX1NMIVXac6hxE2xwUsRVTV3wLvfA6dKZhN800f8wLUEyPlyg==", + "node_modules/@jimp/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", + "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", "dev": true, "license": "MIT", "dependencies": { - "@opencode-ai/sdk": "1.3.2", - "zod": "4.1.8" + "@jimp/file-ops": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "await-to-js": "^3.0.0", + "exif-parser": "^0.1.12", + "file-type": "^16.0.0", + "mime": "3" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@opencode-ai/plugin/node_modules/zod": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", - "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "node_modules/@jimp/diff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", + "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "dependencies": { + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "pixelmatch": "^5.3.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@opencode-ai/sdk": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.2.tgz", - "integrity": "sha512-u7sXVKn0kyAA5vVVHuHQfq3+3UGWOU1Sh6d/e+aS4zO8AwriTSWNQ9r8Qy5yxBH+PoeOGl5WIVdp+s2Ea2zuAg==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "node_modules/@jimp/file-ops": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", + "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", "dev": true, "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" + "engines": { + "node": ">=18" } }, - "node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "node_modules/@jimp/js-bmp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", + "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "bmp-ts": "^1.0.9" }, "engines": { "node": ">=18" - }, + } + }, + "node_modules/@jimp/js-gif": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", + "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "gifwrap": "^0.10.1", + "omggif": "^1.0.10" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-jpeg": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", + "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "jpeg-js": "^0.4.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-png": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", + "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "pngjs": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-tiff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", + "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "utif2": "^4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", + "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", + "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", + "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", + "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "tinycolor2": "^1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-color/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", + "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-contain/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", + "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-cover/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", + "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-crop/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", + "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-displace/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", + "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", + "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", + "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-flip/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-hash": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", + "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "any-base": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", + "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", + "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/types": "1.6.0", + "parse-bmfont-ascii": "^1.0.6", + "parse-bmfont-binary": "^1.0.6", + "parse-bmfont-xml": "^1.1.6", + "simple-xml-to-json": "^1.2.2", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-print/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-quantize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", + "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-quantize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", + "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-resize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", + "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-rotate/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-threshold": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", + "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-threshold/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/types": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", + "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/types/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@opencode-ai/plugin": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.2.tgz", + "integrity": "sha512-eT0ZovMCOQlfTdAnfbEWgW343mJ9SHgEVfdiOSX1NMIVXac6hxE2xwUsRVTV3wLvfA6dKZhN800f8wLUEyPlyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.3.2", + "zod": "4.1.8" + } + }, + "node_modules/@opencode-ai/plugin/node_modules/zod": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", + "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.2.tgz", + "integrity": "sha512-u7sXVKn0kyAA5vVVHuHQfq3+3UGWOU1Sh6d/e+aS4zO8AwriTSWNQ9r8Qy5yxBH+PoeOGl5WIVdp+s2Ea2zuAg==", + "license": "MIT" + }, + "node_modules/@opentui/core": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-e/n7hCtpOzS57X9llODu0SUXCQBWSxHQeTA0iuL7j0nhSFgM6KpL8kJ7VQBU1EEn33pytA0udbfKSJ6sqWmEJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-ffi-structs": "0.1.2", + "diff": "8.0.2", + "jimp": "1.6.0", + "marked": "17.0.1", + "yoga-layout": "3.2.1" + }, + "optionalDependencies": { + "@dimforge/rapier2d-simd-compat": "^0.17.3", + "@opentui/core-darwin-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-darwin-x64": "0.0.0-20260307-536c401b", + "@opentui/core-linux-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-linux-x64": "0.0.0-20260307-536c401b", + "@opentui/core-win32-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-win32-x64": "0.0.0-20260307-536c401b", + "bun-webgpu": "0.1.5", + "planck": "^1.4.2", + "three": "0.177.0" + }, + "peerDependencies": { + "web-tree-sitter": "0.25.10" + } + }, + "node_modules/@opentui/core-darwin-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-y46MUgcjkIqC/IBxErchM51KmLARxudrKqr09Gyy25ry+GUE8gzaEIx6EeMAUnWDWvetMacKgEaNCjtdkfGgDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@opentui/core-darwin-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-USf14JkFaCyKvn9FfLn6AZv14o5ED7uHBNq4kCmggD28HmqHsklDhGNyDnswUggCworJ6xz7jICZTiKKrSwRbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@opentui/core-linux-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-fzNf0Mv7OjNktJFg17WsvdDD5Ej12eSwPVMProlQFbklC8qCEsZfLJKYq9ExYLRoxHX7wFm9Eq6L7hVaGcn2sA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@opentui/core-linux-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-+80TgK5ZhdJvM2+fiCbeCJvXk9De3oNB42wcCtGcwt3x1wyPYAbWIetw6dIGqXIbica/El+7+6Y2DMV06PUUug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@opentui/core-win32-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-SBeHYwNpWJlHxMX6+aO8KsatWpMMxOs+LpFA7M2PTV0g81WUHPlxm6kHi6UHpjwYuslvtcYKgUL0IyQs1jbdNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opentui/core-win32-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-QIU/s6NrXJLRlTyLJZ/41E3MhVGGZazPrwv6MnMx7LOE/uBQo4OGjcRdsIIkhXYIqNRUIH/Yfd5Hyf6twJpBBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opentui/core/node_modules/bun-ffi-structs": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/bun-ffi-structs/-/bun-ffi-structs-0.1.2.tgz", + "integrity": "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/@opentui/core/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@opentui/solid": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-wfItFCVBsP2iWvFgj2/lVRN7/O7R5eu9NReY4Wl34Z+c9d7P6FVSa1xOriziTPysukW1OhFe8MNN7MIaggYdHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.28.0", + "@babel/preset-typescript": "7.27.1", + "@opentui/core": "0.0.0-20260307-536c401b", + "babel-plugin-module-resolver": "5.0.2", + "babel-preset-solid": "1.9.9", + "entities": "7.0.1", + "s-js": "^0.4.9" + }, + "peerDependencies": { + "solid-js": "1.9.9" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.40.6", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.6.tgz", + "integrity": "sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-plugin-module-resolver": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", + "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-babel-config": "^2.1.1", + "glob": "^9.3.3", + "pkg-up": "^3.1.0", + "reselect": "^4.1.7", + "resolve": "^1.22.8" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", + "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.40.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "solid-js": "^1.9.8" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz", + "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bmp-ts": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bun-webgpu": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/bun-webgpu/-/bun-webgpu-0.1.5.tgz", + "integrity": "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@webgpu/types": "^0.1.60" + }, + "optionalDependencies": { + "bun-webgpu-darwin-arm64": "^0.1.5", + "bun-webgpu-darwin-x64": "^0.1.5", + "bun-webgpu-linux-x64": "^0.1.5", + "bun-webgpu-win32-x64": "^0.1.5" + } + }, + "node_modules/bun-webgpu-darwin-arm64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-darwin-arm64/-/bun-webgpu-darwin-arm64-0.1.6.tgz", + "integrity": "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/bun-webgpu-darwin-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-darwin-x64/-/bun-webgpu-darwin-x64-0.1.6.tgz", + "integrity": "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/bun-webgpu-linux-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-linux-x64/-/bun-webgpu-linux-x64-0.1.6.tgz", + "integrity": "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/bun-webgpu-win32-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-win32-x64/-/bun-webgpu-win32-x64-0.1.6.tgz", + "integrity": "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", + "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", @@ -572,62 +2209,676 @@ "@esbuild/win32-x64": "0.27.0" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/find-babel-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", + "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.3" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuzzball": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fuzzball/-/fuzzball-2.2.3.tgz", + "integrity": "sha512-sQDb3kjI7auA4YyE1YgEW85MTparcSgRgcCweUK06Cn0niY5lN+uhFiRUZKN4MQVGGiHxlbrYCA4nL1QjOXBLQ==", + "license": "MIT", + "dependencies": { + "heap": ">=0.2.0", + "lodash": "^4.17.21", + "setimmediate": "^1.0.5" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, + "node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "license": "MIT" + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "16.9.1" + } + }, + "node_modules/image-q/node_modules/@types/node": { + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jimp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", + "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/diff": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-gif": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-blur": "1.6.0", + "@jimp/plugin-circle": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-contain": "1.6.0", + "@jimp/plugin-cover": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-displace": "1.6.0", + "@jimp/plugin-dither": "1.6.0", + "@jimp/plugin-fisheye": "1.6.0", + "@jimp/plugin-flip": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/plugin-mask": "1.6.0", + "@jimp/plugin-print": "1.6.0", + "@jimp/plugin-quantize": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/plugin-rotate": "1.6.0", + "@jimp/plugin-threshold": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/marked": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", + "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/minimatch": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/fuzzball": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fuzzball/-/fuzzball-2.2.3.tgz", - "integrity": "sha512-sQDb3kjI7auA4YyE1YgEW85MTparcSgRgcCweUK06Cn0niY5lN+uhFiRUZKN4MQVGGiHxlbrYCA4nL1QjOXBLQ==", + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "heap": ">=0.2.0", - "lodash": "^4.17.21", - "setimmediate": "^1.0.5" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", "dev": true, "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", - "license": "MIT" + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "license": "MIT" + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/planck": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/planck/-/planck-1.4.3.tgz", + "integrity": "sha512-B+lHKhRSeg7vZOfEyEzyQVu7nx8JHcX3QgnAcHXrPW0j04XYKX5eXSiUrxH2Z5QR8OoqvjD6zKIaPMdMYAd0uA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=24.0" + }, + "peerDependencies": { + "stage-js": "^1.0.0-alpha.12" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } }, "node_modules/prettier": { "version": "3.8.1", @@ -645,6 +2896,78 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -655,18 +2978,200 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/s-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/s-js/-/s-js-0.4.9.tgz", + "integrity": "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz", + "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, + "node_modules/simple-xml-to-json": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.4.tgz", + "integrity": "sha512-3MY16e0ocMHL7N1ufpdObURGyX+lCo0T/A+y6VCwosLdH1HSda4QZl1Sdt/O+2qWp48WFi26XEp5rF0LoaL0Dg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.12.2" + } + }, + "node_modules/solid-js": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", + "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.3.0", + "seroval-plugins": "~1.3.0" + } + }, + "node_modules/stage-js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stage-js/-/stage-js-1.0.1.tgz", + "integrity": "sha512-cz14aPp/wY0s3bkb/B93BPP5ZAEhgBbRmAT3CCDqert8eCAqIpQ0RB2zpK8Ksxf+Pisl5oTzvPHtL4CVzzeHcw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/three": { + "version": "0.177.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", + "integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/tiktoken": { "version": "1.0.22", "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.22.tgz", "integrity": "sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==", "license": "MIT" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", @@ -708,6 +3213,108 @@ "dev": true, "license": "MIT" }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.11" + } + }, + "node_modules/web-tree-sitter": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.25.10.tgz", + "integrity": "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/emscripten": "^1.40.0" + }, + "peerDependenciesMeta": { + "@types/emscripten": { + "optional": true + } + } + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", + "dev": true, + "license": "MIT" + }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/package.json b/package.json index 9d9a4326..fa04aae1 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,9 @@ "author": "tarquinen", "license": "AGPL-3.0-or-later", "peerDependencies": { - "@opencode-ai/plugin": ">=0.13.7" + "@opencode-ai/plugin": ">=1.2.0", + "@opentui/core": ">=0.1.87", + "@opentui/solid": ">=0.0.0-20260307" }, "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", @@ -51,6 +53,8 @@ }, "devDependencies": { "@opencode-ai/plugin": "^1.3.2", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", "tsx": "^4.21.0", diff --git a/tsconfig.json b/tsconfig.json index c20d8a54..3eda4128 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "target": "ES2022", "module": "ESNext", "lib": ["ES2023"], + "jsx": "react-jsx", "moduleResolution": "bundler", "resolveJsonModule": true, "allowJs": true, @@ -19,6 +20,6 @@ "sourceMap": true, "types": ["node"] }, - "include": ["index.ts", "lib/**/*"], + "include": ["index.ts", "lib/**/*", "tui/**/*"], "exclude": ["node_modules", "dist", "logs"] } diff --git a/tui/commands.ts b/tui/commands.ts index 1098de57..ec9af211 100644 --- a/tui/commands.ts +++ b/tui/commands.ts @@ -1,15 +1,15 @@ import type { TuiApi } from "@opencode-ai/plugin/tui" import { openPanel } from "./shared/navigation" -import type { DcpRouteNames, DcpTuiConfig } from "./shared/types" +import { LABEL, type DcpRouteNames } from "./shared/names" -export const registerCommands = (api: TuiApi, config: DcpTuiConfig, names: DcpRouteNames) => { +export const registerCommands = (api: TuiApi, names: DcpRouteNames) => { const keys = api.keybind?.create({ close: "escape" }) api.command.register(() => [ { - title: `${config.label} panel`, + title: `${LABEL} panel`, value: names.commands.panel, description: "Open the DCP placeholder panel", - category: config.label, + category: LABEL, ...(keys ? { keybind: keys.get("close") } : {}), slash: { name: "dcp-panel", diff --git a/tui/components/screen.tsx b/tui/components/screen.tsx index 22f48801..d8fbbfca 100644 --- a/tui/components/screen.tsx +++ b/tui/components/screen.tsx @@ -2,6 +2,8 @@ import type { JSX } from "solid-js" import type { DcpPalette } from "../shared/theme" +const SINGLE_BORDER = { type: "single" } as any + export const Screen = (props: { palette: DcpPalette title: string @@ -18,7 +20,7 @@ export const Screen = (props: { gap={1} padding={1} backgroundColor={props.palette.base} - border={{ type: "single" }} + border={SINGLE_BORDER} borderColor={props.palette.accent} > diff --git a/tui/components/section.tsx b/tui/components/section.tsx index 6c95e95a..1508123f 100644 --- a/tui/components/section.tsx +++ b/tui/components/section.tsx @@ -2,6 +2,8 @@ import type { JSX } from "solid-js" import type { DcpPalette } from "../shared/theme" +const SINGLE_BORDER = { type: "single" } as any + export const Section = (props: { palette: DcpPalette title: string @@ -15,7 +17,7 @@ export const Section = (props: { gap={1} padding={1} backgroundColor={props.palette.base} - border={{ type: "single" }} + border={SINGLE_BORDER} borderColor={props.palette.border} > diff --git a/tui/data/context.ts b/tui/data/context.ts index 4c55dcd6..914eb088 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -14,24 +14,10 @@ import { loadAllSessionStats } from "../../lib/state/persistence" import { analyzeTokens, emptyBreakdown } from "../../lib/analysis/tokens" import type { DcpContextSnapshot, DcpTuiClient } from "../shared/types" -let logger = new Logger(false, "TUI") const snapshotCache = new Map() const inflightSnapshots = new Map>() const CACHE_TTL_MS = 5000 -const summarizeSnapshot = (snapshot: DcpContextSnapshot) => ({ - sessionID: snapshot.sessionID, - totalTokens: snapshot.breakdown.total, - messageCount: snapshot.breakdown.messageCount, - prunedTokens: snapshot.breakdown.prunedTokens, - activeBlockCount: snapshot.persisted.activeBlockCount, - loadedAt: snapshot.loadedAt, -}) - -export const setContextLogger = (nextLogger: Logger) => { - logger = nextLogger -} - export const createPlaceholderContextSnapshot = ( sessionID?: string, notes: string[] = [], @@ -53,6 +39,7 @@ export const createPlaceholderContextSnapshot = ( const buildState = async ( sessionID: string, messages: WithParts[], + logger: Logger, ): Promise<{ state: SessionState; persisted: Awaited> }> => { const state = createSessionState() const persisted = await loadSessionState(sessionID, logger) @@ -72,26 +59,21 @@ const buildState = async ( const loadContextSnapshot = async ( client: DcpTuiClient, + logger: Logger, sessionID?: string, ): Promise => { if (!sessionID) { - void logger.debug("Context snapshot requested without session") return createPlaceholderContextSnapshot(undefined, [ "Open this panel from a session to inspect DCP context.", ]) } - void logger.debug("Loading context snapshot", { sessionID }) const messagesResult = await client.session.messages({ sessionID }) const messages = Array.isArray(messagesResult.data) ? (messagesResult.data as WithParts[]) : ([] as WithParts[]) - void logger.debug("Fetched session messages for context snapshot", { - sessionID, - messageCount: messages.length, - }) - const { state, persisted } = await buildState(sessionID, messages) + const { state, persisted } = await buildState(sessionID, messages, logger) const [{ breakdown, messageStatuses }, aggregated] = await Promise.all([ Promise.resolve(analyzeTokens(state, messages)), loadAllSessionStats(logger), @@ -115,7 +97,7 @@ const loadContextSnapshot = async ( notes.push("This session does not have any messages yet.") } - const snapshot = { + return { sessionID, breakdown, persisted: { @@ -133,13 +115,6 @@ const loadContextSnapshot = async ( notes, loadedAt: Date.now(), } - - void logger.debug("Loaded context snapshot", { - ...summarizeSnapshot(snapshot), - persisted: !!persisted, - }) - - return snapshot } export const peekContextSnapshot = (sessionID?: string): DcpContextSnapshot | undefined => { @@ -149,23 +124,20 @@ export const peekContextSnapshot = (sessionID?: string): DcpContextSnapshot | un export const invalidateContextSnapshot = (sessionID?: string) => { if (!sessionID) { - void logger.debug("Invalidating all context snapshots") snapshotCache.clear() inflightSnapshots.clear() return } - - void logger.debug("Invalidating context snapshot", { sessionID }) snapshotCache.delete(sessionID) inflightSnapshots.delete(sessionID) } export const loadContextSnapshotCached = async ( client: DcpTuiClient, + logger: Logger, sessionID?: string, ): Promise => { if (!sessionID) { - void logger.debug("Cached context snapshot requested without session") return createPlaceholderContextSnapshot(undefined, [ "Open this panel from a session to inspect DCP context.", ]) @@ -173,44 +145,28 @@ export const loadContextSnapshotCached = async ( const cached = snapshotCache.get(sessionID) if (cached && Date.now() - cached.loadedAt < CACHE_TTL_MS) { - void logger.debug("Context snapshot cache hit", { - sessionID, - cacheAgeMs: Date.now() - cached.loadedAt, - }) return cached } - if (cached) { - void logger.debug("Context snapshot cache stale", { - sessionID, - cacheAgeMs: Date.now() - cached.loadedAt, - }) - } else { - void logger.debug("Context snapshot cache miss", { sessionID }) - } - const inflight = inflightSnapshots.get(sessionID) if (inflight) { - void logger.debug("Reusing inflight context snapshot request", { sessionID }) return inflight } - const request = loadContextSnapshot(client, sessionID) + const request = loadContextSnapshot(client, logger, sessionID) .then((snapshot) => { snapshotCache.set(sessionID, snapshot) - void logger.debug("Stored context snapshot in cache", summarizeSnapshot(snapshot)) return snapshot }) - .catch((cause) => { - void logger.error("Context snapshot request failed", { + .catch((error) => { + logger.error("Failed to load TUI context snapshot", { sessionID, - error: cause instanceof Error ? cause.message : String(cause), + error: error instanceof Error ? error.message : String(error), }) - throw cause + throw error }) .finally(() => { inflightSnapshots.delete(sessionID) - void logger.debug("Cleared inflight context snapshot request", { sessionID }) }) inflightSnapshots.set(sessionID, request) diff --git a/tui/index.tsx b/tui/index.tsx index 24b22707..02a8e8a7 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -1,47 +1,46 @@ /** @jsxImportSource @opentui/solid */ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import { getConfigForDirectory } from "../lib/config" import { Logger } from "../lib/logger" import { registerCommands } from "./commands" -import { setContextLogger } from "./data/context" import { createPanelRoute } from "./routes/panel" import { createSidebarTopSlot } from "./slots/sidebar-top" -import { readConfig } from "./shared/config" -import { createNames } from "./shared/names" +import { NAMES } from "./shared/names" -const tui = async (input: TuiPluginInput, options?: Record) => { - if (options?.enabled === false) return - - const config = readConfig(options) - const names = createNames(config) - const logger = new Logger(config.debug, "TUI") - - setContextLogger(logger) - void logger.info("DCP TUI initialized", { - debug: config.debug, - label: config.label, - route: config.route, +const tui = async (input: TuiPluginInput) => { + const config = getConfigForDirectory(process.cwd(), (title, message) => { + input.api.ui.toast({ + title, + message, + variant: "warning", + duration: 7000, + }) }) + if (!config.enabled) return + + const logger = new Logger(config.tui.debug, "tui") input.api.route.register([ createPanelRoute({ api: input.api, - config, - names, + names: NAMES, }), ]) - registerCommands(input.api, config, names) - input.slots.register( - createSidebarTopSlot( - input.api, - input.client, - input.event, - input.renderer, - logger, - config, - names, - ), - ) + registerCommands(input.api, NAMES) + + if (config.tui.sidebar) { + input.slots.register( + createSidebarTopSlot( + input.api, + input.client, + input.event, + input.renderer, + NAMES, + logger, + ), + ) + } } export default { diff --git a/tui/routes/panel.tsx b/tui/routes/panel.tsx index f8ae7b57..e2d34572 100644 --- a/tui/routes/panel.tsx +++ b/tui/routes/panel.tsx @@ -7,11 +7,10 @@ import { Screen } from "../components/screen" import { Section } from "../components/section" import { getRouteSource, getSessionIDFromParams, goBack } from "../shared/navigation" import { getPalette } from "../shared/theme" -import type { DcpRouteNames, DcpTuiConfig } from "../shared/types" +import { LABEL, type DcpRouteNames } from "../shared/names" const PanelScreen = (props: { api: TuiApi - config: DcpTuiConfig names: DcpRouteNames params?: Record }) => { @@ -33,7 +32,7 @@ const PanelScreen = (props: { return ( @@ -68,18 +67,10 @@ const PanelScreen = (props: { export const createPanelRoute = (input: { api: TuiApi - config: DcpTuiConfig names: DcpRouteNames }): TuiRouteDefinition => { return { name: input.names.routes.panel, - render: ({ params }) => ( - - ), + render: ({ params }) => , } } diff --git a/tui/shared/config.ts b/tui/shared/config.ts deleted file mode 100644 index 7db8561a..00000000 --- a/tui/shared/config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { DcpTuiConfig } from "./types" - -const pick = (value: unknown, fallback: string) => { - if (typeof value !== "string") return fallback - if (!value.trim()) return fallback - return value -} - -const pickBoolean = (value: unknown, fallback: boolean) => { - if (typeof value !== "boolean") return fallback - return value -} - -export const readConfig = (options: Record | undefined): DcpTuiConfig => { - return { - debug: pickBoolean(options?.debug, false), - label: pick(options?.label, "DCP"), - route: pick(options?.route, "dcp"), - } -} diff --git a/tui/shared/names.ts b/tui/shared/names.ts index 84cb8102..2457e6a0 100644 --- a/tui/shared/names.ts +++ b/tui/shared/names.ts @@ -1,13 +1,13 @@ -import type { DcpRouteNames, DcpTuiConfig } from "./types" +export const LABEL = "DCP" -export const createNames = (config: DcpTuiConfig): DcpRouteNames => { - return { - slot: `${config.route}.sidebar`, - routes: { - panel: `${config.route}.panel`, - }, - commands: { - panel: `plugin.${config.route}.panel`, - }, - } -} +export const NAMES = { + slot: "dcp.sidebar", + routes: { + panel: "dcp.panel", + }, + commands: { + panel: "plugin.dcp.panel", + }, +} as const + +export type DcpRouteNames = typeof NAMES diff --git a/tui/shared/navigation.ts b/tui/shared/navigation.ts index 2c14d727..6160294e 100644 --- a/tui/shared/navigation.ts +++ b/tui/shared/navigation.ts @@ -1,5 +1,6 @@ import type { TuiApi } from "@opencode-ai/plugin/tui" -import type { DcpRouteNames, DcpRouteParams, DcpRouteSource } from "./types" +import type { DcpRouteNames } from "./names" +import type { DcpRouteParams, DcpRouteSource } from "./types" export const getSessionIDFromParams = (params?: Record) => { if (typeof params?.session_id === "string") return params.session_id diff --git a/tui/shared/types.ts b/tui/shared/types.ts index dc5e5540..2a92cec5 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -1,33 +1,18 @@ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { + MessageStatus as DcpMessageStatus, + TokenBreakdown as DcpContextBreakdown, +} from "../../lib/analysis/tokens" export type DcpTuiClient = TuiPluginInput["client"] export type DcpRouteSource = "sidebar" | "command" -export interface DcpTuiConfig { - debug: boolean - label: string - route: string -} - -export interface DcpRouteNames { - slot: string - routes: { - panel: string - } - commands: { - panel: string - } -} - export interface DcpRouteParams { session_id?: string source?: string } -export { - type TokenBreakdown as DcpContextBreakdown, - type MessageStatus as DcpMessageStatus, -} from "../../lib/analysis/tokens" +export type { DcpContextBreakdown, DcpMessageStatus } export interface DcpPersistedSummary { available: boolean diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index ff44b359..e8754e7b 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -11,9 +11,12 @@ import { } from "../data/context" import { openPanel } from "../shared/navigation" import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared/theme" -import type { DcpMessageStatus, DcpRouteNames, DcpTuiClient, DcpTuiConfig } from "../shared/types" +import { LABEL, type DcpRouteNames } from "../shared/names" +import type { DcpMessageStatus, DcpTuiClient } from "../shared/types" const BAR_WIDTH = 12 +const SINGLE_BORDER = { type: "single" } as any +const DIM_TEXT = { dim: true } as any // Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5) const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5 @@ -129,11 +132,10 @@ const SidebarContext = (props: { client: DcpTuiClient event: TuiPluginInput["event"] renderer: TuiPluginInput["renderer"] - logger: Logger - config: DcpTuiConfig names: DcpRouteNames palette: DcpPalette sessionID: () => string + logger: Logger }) => { const initialSnapshot = peekContextSnapshot(props.sessionID()) const [snapshot, setSnapshot] = createSignal( @@ -144,29 +146,15 @@ const SidebarContext = (props: { let requestVersion = 0 let renderTimeout: ReturnType | undefined - const requestRender = (reason: string, data?: Record) => { - const activeSessionID = untrack(() => props.sessionID()) - void props.logger.debug("Sidebar requested renderer refresh", { - activeSessionID, - reason, - ...data, - }) + const requestRender = () => { if (renderTimeout) clearTimeout(renderTimeout) renderTimeout = setTimeout(() => { renderTimeout = undefined try { - void props.logger.debug("Sidebar renderer refresh dispatched", { - activeSessionID, - reason, - ...data, - }) props.renderer.requestRender() - } catch (cause) { - void props.logger.warn("Sidebar renderer refresh failed", { - activeSessionID, - reason, - error: cause instanceof Error ? cause.message : String(cause), - ...data, + } catch (error) { + props.logger.warn("Failed to request TUI render", { + error: error instanceof Error ? error.message : String(error), }) } }, 0) @@ -178,18 +166,8 @@ const SidebarContext = (props: { const refreshSnapshot = async ( sessionID: string, - options?: { invalidate?: boolean; preserveSnapshot?: boolean; reason?: string }, + options?: { invalidate?: boolean; preserveSnapshot?: boolean }, ) => { - const preserveSnapshot = options?.preserveSnapshot ?? false - const reason = options?.reason ?? "unspecified" - - void props.logger.debug("Sidebar refresh start", { - sessionID, - invalidate: !!options?.invalidate, - preserveSnapshot, - reason, - }) - if (options?.invalidate) { invalidateContextSnapshot(sessionID) } @@ -197,84 +175,43 @@ const SidebarContext = (props: { const cached = peekContextSnapshot(sessionID) let silentRefresh = false if (cached) { - void props.logger.debug("Sidebar using cached snapshot before reload", { - sessionID, - loadedAt: cached.loadedAt, - totalTokens: cached.breakdown.total, - }) setSnapshot(cached) setLoading(false) } else { const current = untrack(snapshot) - if (preserveSnapshot && current?.sessionID === sessionID) { + if (options?.preserveSnapshot && current?.sessionID === sessionID) { silentRefresh = true - void props.logger.debug("Sidebar silent refresh, keeping current snapshot", { - sessionID, - }) } else { setSnapshot(createPlaceholderContextSnapshot(sessionID, ["Loading DCP context..."])) setLoading(true) - void props.logger.debug("Sidebar entering loading state", { - sessionID, - hadCurrentSnapshot: !!current, - }) } } setError(undefined) if (!silentRefresh) { - requestRender("refresh-start", { sessionID, reason }) + requestRender() } const currentRequest = ++requestVersion - void props.logger.debug("Sidebar refresh request issued", { - sessionID, - requestVersion: currentRequest, - reason, - }) try { - const value = await loadContextSnapshotCached(props.client, sessionID) + const value = await loadContextSnapshotCached(props.client, props.logger, sessionID) if (currentRequest !== requestVersion || props.sessionID() !== sessionID) { - void props.logger.debug("Sidebar refresh result ignored as stale", { - sessionID, - requestVersion: currentRequest, - activeRequestVersion: requestVersion, - activeSessionID: props.sessionID(), - reason, - }) return } setSnapshot(value) setLoading(false) - void props.logger.debug("Sidebar refresh succeeded", { - sessionID, - requestVersion: currentRequest, - totalTokens: value.breakdown.total, - messageCount: value.breakdown.messageCount, - activeBlockCount: value.persisted.activeBlockCount, - reason, - }) - requestRender("refresh-success", { sessionID, reason, requestVersion: currentRequest }) + requestRender() } catch (cause) { if (currentRequest !== requestVersion || props.sessionID() !== sessionID) { - void props.logger.debug("Sidebar refresh error ignored as stale", { - sessionID, - requestVersion: currentRequest, - activeRequestVersion: requestVersion, - activeSessionID: props.sessionID(), - reason, - }) return } - setError(cause instanceof Error ? cause.message : String(cause)) - setLoading(false) - void props.logger.error("Sidebar refresh failed", { + props.logger.warn("Failed to refresh sidebar snapshot", { sessionID, - requestVersion: currentRequest, error: cause instanceof Error ? cause.message : String(cause), - reason, }) - requestRender("refresh-error", { sessionID, reason, requestVersion: currentRequest }) + setError(cause instanceof Error ? cause.message : String(cause)) + setLoading(false) + requestRender() } } @@ -282,8 +219,7 @@ const SidebarContext = (props: { on( props.sessionID, (sessionID) => { - void props.logger.info("Sidebar active session changed", { sessionID }) - void refreshSnapshot(sessionID, { reason: "session-change" }) + void refreshSnapshot(sessionID) }, { defer: false }, ), @@ -294,32 +230,15 @@ const SidebarContext = (props: { props.sessionID, (sessionID) => { let timeout: ReturnType | undefined - let pendingReason: string | undefined - void props.logger.debug("Sidebar event subscriptions armed", { sessionID }) - - const scheduleRefresh = (reason: string, data?: Record) => { + const scheduleRefresh = () => { if (!sessionID) return if (timeout) clearTimeout(timeout) - pendingReason = reason - void props.logger.debug("Sidebar refresh scheduled", { - sessionID, - debounceMs: REFRESH_DEBOUNCE_MS, - reason, - ...data, - }) timeout = setTimeout(() => { - const flushReason = pendingReason ?? reason - pendingReason = undefined timeout = undefined - void props.logger.debug("Sidebar refresh debounce fired", { - sessionID, - reason: flushReason, - }) void refreshSnapshot(sessionID, { invalidate: true, preserveSnapshot: true, - reason: flushReason, }) }, REFRESH_DEBOUNCE_MS) } @@ -327,80 +246,48 @@ const SidebarContext = (props: { const unsubs = [ props.event.on("message.updated", (event) => { if (event.properties.info.sessionID !== sessionID) return - scheduleRefresh("message.updated", { - eventSessionID: event.properties.info.sessionID, - messageID: event.properties.info.id, - }) + scheduleRefresh() }), props.event.on("message.removed", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("message.removed", { - eventSessionID: event.properties.sessionID, - messageID: event.properties.messageID, - }) + scheduleRefresh() }), props.event.on("message.part.updated", (event) => { if (event.properties.part.sessionID !== sessionID) return - scheduleRefresh("message.part.updated", { - eventSessionID: event.properties.part.sessionID, - messageID: event.properties.part.messageID, - partID: event.properties.part.id, - }) + scheduleRefresh() }), props.event.on("message.part.delta", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("message.part.delta", { - eventSessionID: event.properties.sessionID, - messageID: event.properties.messageID, - partID: event.properties.partID, - field: event.properties.field, - }) + scheduleRefresh() }), props.event.on("message.part.removed", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("message.part.removed", { - eventSessionID: event.properties.sessionID, - messageID: event.properties.messageID, - partID: event.properties.partID, - }) + scheduleRefresh() }), props.event.on("session.updated", (event) => { if (event.properties.info.id !== sessionID) return - scheduleRefresh("session.updated", { - eventSessionID: event.properties.info.id, - }) + scheduleRefresh() }), props.event.on("session.deleted", (event) => { if (event.properties.info.id !== sessionID) return - scheduleRefresh("session.deleted", { - eventSessionID: event.properties.info.id, - }) + scheduleRefresh() }), props.event.on("session.diff", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("session.diff", { - eventSessionID: event.properties.sessionID, - }) + scheduleRefresh() }), props.event.on("session.error", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("session.error", { - eventSessionID: event.properties.sessionID, - error: event.properties.error, - }) + scheduleRefresh() }), props.event.on("session.status", (event) => { if (event.properties.sessionID !== sessionID) return - scheduleRefresh("session.status", { - eventSessionID: event.properties.sessionID, - status: event.properties.status, - }) + scheduleRefresh() }), ] onCleanup(() => { if (timeout) clearTimeout(timeout) - void props.logger.debug("Sidebar event subscriptions cleaned up", { sessionID }) for (const unsub of unsubs) { unsub() } @@ -432,7 +319,7 @@ const SidebarContext = (props: { width="100%" flexDirection="column" backgroundColor={props.palette.surface} - border={{ type: "single" }} + border={SINGLE_BORDER} borderColor={props.palette.accent} paddingTop={1} paddingBottom={1} @@ -444,7 +331,7 @@ const SidebarContext = (props: { - {props.config.label} + {LABEL} click for more @@ -523,7 +410,7 @@ const SidebarContext = (props: { {truncate(t, CONTENT_WIDTH)} ))} {topicOverflow() > 0 ? ( - + ... {topicOverflow()} more topics ) : null} @@ -561,16 +448,15 @@ export const createSidebarTopSlot = ( client: DcpTuiClient, event: TuiPluginInput["event"], renderer: TuiPluginInput["renderer"], - logger: Logger, - config: DcpTuiConfig, names: DcpRouteNames, + logger: Logger, ) => ({ id: names.slot, slots: { - sidebar_top(ctx, value: { session_id: string }) { - // value is a reactive proxy from @opentui/solid splitProps — - // value.session_id updates automatically when the host navigates - // to a different session (no event subscription needed). + sidebar_top( + ctx: { theme: { current: Record } }, + value: { session_id: string }, + ) { const palette = createMemo(() => getPalette(ctx.theme.current as Record), ) @@ -580,11 +466,10 @@ export const createSidebarTopSlot = ( client={client} event={event} renderer={renderer} - logger={logger} - config={config} names={names} palette={palette()} sessionID={() => value.session_id} + logger={logger} /> ) }, diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts new file mode 100644 index 00000000..5df963d7 --- /dev/null +++ b/tui/types/opencode-plugin-tui.d.ts @@ -0,0 +1,73 @@ +declare module "@opencode-ai/plugin/tui" { + export interface TuiCommand { + title: string + value: string + description?: string + category?: string + keybind?: unknown + slash?: { + name: string + } + onSelect?: () => void + } + + export interface TuiRouteDefinition { + name: string + render: (input: { params?: Record }) => Node + } + + export interface TuiKeybindSet { + get: (key: string) => unknown + match: (key: string, evt: unknown) => boolean + } + + export interface TuiApi { + command: { + register: (cb: () => TuiCommand[]) => void + trigger: (value: string) => void + } + route: { + register: (routes: TuiRouteDefinition[]) => () => void + navigate: (name: string, params?: any) => void + current: { + name: string + params?: any + } + } + ui: { + toast: (input: { + title: string + message: string + variant?: string + duration?: number + }) => void + dialog: { + open?: boolean + } + } + keybind?: { + create: ( + defaults: Record, + overrides?: Record, + ) => TuiKeybindSet + } + theme: { + current: unknown + } + } + + export interface TuiPluginInput< + Renderer extends { requestRender: () => void } = { requestRender: () => void }, + Node = unknown, + > { + client: any + event: { + on: (name: string, cb: (event: any) => void) => () => void + } + renderer: Renderer + slots: { + register: (slot: any) => () => void + } + api: TuiApi + } +} From 8ecca6c3299c861c66530ad11eb0440ee3520a7d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 11 Mar 2026 22:17:22 -0400 Subject: [PATCH 14/50] refactor(tui): remove panel page and keep only sidebar widget --- tui/commands.ts | 20 --------- tui/components/metric-row.tsx | 20 --------- tui/components/screen.tsx | 38 ------------------ tui/components/section.tsx | 32 --------------- tui/data/context.ts | 8 +--- tui/index.tsx | 20 +-------- tui/routes/panel.tsx | 76 ----------------------------------- tui/shared/names.ts | 6 --- tui/shared/navigation.ts | 47 ---------------------- tui/shared/types.ts | 6 --- tui/slots/sidebar-top.tsx | 10 +---- 11 files changed, 4 insertions(+), 279 deletions(-) delete mode 100644 tui/commands.ts delete mode 100644 tui/components/metric-row.tsx delete mode 100644 tui/components/screen.tsx delete mode 100644 tui/components/section.tsx delete mode 100644 tui/routes/panel.tsx delete mode 100644 tui/shared/navigation.ts diff --git a/tui/commands.ts b/tui/commands.ts deleted file mode 100644 index ec9af211..00000000 --- a/tui/commands.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { TuiApi } from "@opencode-ai/plugin/tui" -import { openPanel } from "./shared/navigation" -import { LABEL, type DcpRouteNames } from "./shared/names" - -export const registerCommands = (api: TuiApi, names: DcpRouteNames) => { - const keys = api.keybind?.create({ close: "escape" }) - api.command.register(() => [ - { - title: `${LABEL} panel`, - value: names.commands.panel, - description: "Open the DCP placeholder panel", - category: LABEL, - ...(keys ? { keybind: keys.get("close") } : {}), - slash: { - name: "dcp-panel", - }, - onSelect: () => openPanel(api, names, "command"), - }, - ]) -} diff --git a/tui/components/metric-row.tsx b/tui/components/metric-row.tsx deleted file mode 100644 index c3b03bf9..00000000 --- a/tui/components/metric-row.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { toneColor, type DcpPalette, type DcpTone } from "../shared/theme" - -const pad = (value: string, width: number) => { - if (value.length >= width) return value - return value.padEnd(width, " ") -} - -export const MetricRow = (props: { - palette: DcpPalette - label: string - value: string - tone?: DcpTone -}) => { - return ( - {`${pad(props.label, 18)} ${props.value}`} - ) -} diff --git a/tui/components/screen.tsx b/tui/components/screen.tsx deleted file mode 100644 index d8fbbfca..00000000 --- a/tui/components/screen.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import type { JSX } from "solid-js" -import type { DcpPalette } from "../shared/theme" - -const SINGLE_BORDER = { type: "single" } as any - -export const Screen = (props: { - palette: DcpPalette - title: string - subtitle?: string - footer?: string - children?: JSX.Element -}) => { - return ( - - - - - {props.title} - - {props.subtitle && {props.subtitle}} - - - {props.children} - - {props.footer && {props.footer}} - - - ) -} diff --git a/tui/components/section.tsx b/tui/components/section.tsx deleted file mode 100644 index 1508123f..00000000 --- a/tui/components/section.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import type { JSX } from "solid-js" -import type { DcpPalette } from "../shared/theme" - -const SINGLE_BORDER = { type: "single" } as any - -export const Section = (props: { - palette: DcpPalette - title: string - subtitle?: string - children?: JSX.Element -}) => { - return ( - - - {props.title} - - {props.subtitle && {props.subtitle}} - - {props.children} - - - ) -} diff --git a/tui/data/context.ts b/tui/data/context.ts index 914eb088..a41a84ba 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -63,9 +63,7 @@ const loadContextSnapshot = async ( sessionID?: string, ): Promise => { if (!sessionID) { - return createPlaceholderContextSnapshot(undefined, [ - "Open this panel from a session to inspect DCP context.", - ]) + return createPlaceholderContextSnapshot(undefined, ["No active session."]) } const messagesResult = await client.session.messages({ sessionID }) @@ -138,9 +136,7 @@ export const loadContextSnapshotCached = async ( sessionID?: string, ): Promise => { if (!sessionID) { - return createPlaceholderContextSnapshot(undefined, [ - "Open this panel from a session to inspect DCP context.", - ]) + return createPlaceholderContextSnapshot(undefined, ["No active session."]) } const cached = snapshotCache.get(sessionID) diff --git a/tui/index.tsx b/tui/index.tsx index 02a8e8a7..a28d1121 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -2,8 +2,6 @@ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" import { getConfigForDirectory } from "../lib/config" import { Logger } from "../lib/logger" -import { registerCommands } from "./commands" -import { createPanelRoute } from "./routes/panel" import { createSidebarTopSlot } from "./slots/sidebar-top" import { NAMES } from "./shared/names" @@ -20,25 +18,9 @@ const tui = async (input: TuiPluginInput) => { const logger = new Logger(config.tui.debug, "tui") - input.api.route.register([ - createPanelRoute({ - api: input.api, - names: NAMES, - }), - ]) - - registerCommands(input.api, NAMES) - if (config.tui.sidebar) { input.slots.register( - createSidebarTopSlot( - input.api, - input.client, - input.event, - input.renderer, - NAMES, - logger, - ), + createSidebarTopSlot(input.client, input.event, input.renderer, NAMES, logger), ) } } diff --git a/tui/routes/panel.tsx b/tui/routes/panel.tsx deleted file mode 100644 index e2d34572..00000000 --- a/tui/routes/panel.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { createMemo } from "solid-js" -import { useKeyboard } from "@opentui/solid" -import type { TuiApi, TuiRouteDefinition } from "@opencode-ai/plugin/tui" -import { MetricRow } from "../components/metric-row" -import { Screen } from "../components/screen" -import { Section } from "../components/section" -import { getRouteSource, getSessionIDFromParams, goBack } from "../shared/navigation" -import { getPalette } from "../shared/theme" -import { LABEL, type DcpRouteNames } from "../shared/names" - -const PanelScreen = (props: { - api: TuiApi - names: DcpRouteNames - params?: Record -}) => { - const palette = createMemo(() => getPalette(props.api.theme.current as Record)) - const sessionID = () => getSessionIDFromParams(props.params) - const source = () => getRouteSource(props.params) - const keys = props.api.keybind?.create({ close: "escape" }) - - useKeyboard((evt) => { - if (props.api.route.current.name !== props.names.routes.panel) return - if (props.api.ui?.dialog?.open) return - const matched = keys ? keys.match("close", evt) : evt.name === "escape" - if (!matched) return - evt.preventDefault() - evt.stopPropagation() - goBack(props.api, sessionID()) - }) - - return ( - -
- - Use this page as the home for future DCP-specific TUI work. - - - The live context breakdown now lives directly in the session sidebar. - -
- -
- - - -
- -
- - block explorer - - prune history and diagnostics - - manual DCP actions -
-
- ) -} - -export const createPanelRoute = (input: { - api: TuiApi - names: DcpRouteNames -}): TuiRouteDefinition => { - return { - name: input.names.routes.panel, - render: ({ params }) => , - } -} diff --git a/tui/shared/names.ts b/tui/shared/names.ts index 2457e6a0..a2187087 100644 --- a/tui/shared/names.ts +++ b/tui/shared/names.ts @@ -2,12 +2,6 @@ export const LABEL = "DCP" export const NAMES = { slot: "dcp.sidebar", - routes: { - panel: "dcp.panel", - }, - commands: { - panel: "plugin.dcp.panel", - }, } as const export type DcpRouteNames = typeof NAMES diff --git a/tui/shared/navigation.ts b/tui/shared/navigation.ts deleted file mode 100644 index 6160294e..00000000 --- a/tui/shared/navigation.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { TuiApi } from "@opencode-ai/plugin/tui" -import type { DcpRouteNames } from "./names" -import type { DcpRouteParams, DcpRouteSource } from "./types" - -export const getSessionIDFromParams = (params?: Record) => { - if (typeof params?.session_id === "string") return params.session_id - return undefined -} - -export const getRouteSource = (params?: Record) => { - if (typeof params?.source === "string") return params.source - return "unknown" -} - -export const getCurrentSessionID = (api: TuiApi) => { - const current = api.route.current - if (current.name === "session") return current.params.sessionID - if ("params" in current && current.params && typeof current.params === "object") { - return getSessionIDFromParams(current.params) - } - return undefined -} - -const navigate = (api: TuiApi, routeName: string, source: DcpRouteSource, sessionID?: string) => { - const params: DcpRouteParams = { - source, - session_id: sessionID ?? getCurrentSessionID(api), - } - api.route.navigate(routeName, params) -} - -export const openPanel = ( - api: TuiApi, - names: DcpRouteNames, - source: DcpRouteSource, - sessionID?: string, -) => { - navigate(api, names.routes.panel, source, sessionID) -} - -export const goBack = (api: TuiApi, sessionID?: string) => { - if (sessionID) { - api.route.navigate("session", { sessionID }) - return - } - api.route.navigate("home") -} diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 2a92cec5..bae36262 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -5,12 +5,6 @@ import type { } from "../../lib/analysis/tokens" export type DcpTuiClient = TuiPluginInput["client"] -export type DcpRouteSource = "sidebar" | "command" - -export interface DcpRouteParams { - session_id?: string - source?: string -} export type { DcpContextBreakdown, DcpMessageStatus } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index e8754e7b..9af6fa12 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @opentui/solid */ import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" -import type { TuiApi, TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { TuiPluginInput } from "@opencode-ai/plugin/tui" import { Logger } from "../../lib/logger" import { truncate } from "../../lib/ui/utils" import { @@ -9,7 +9,6 @@ import { loadContextSnapshotCached, peekContextSnapshot, } from "../data/context" -import { openPanel } from "../shared/navigation" import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared/theme" import { LABEL, type DcpRouteNames } from "../shared/names" import type { DcpMessageStatus, DcpTuiClient } from "../shared/types" @@ -128,11 +127,9 @@ const SidebarContextBar = (props: { } const SidebarContext = (props: { - api: TuiApi client: DcpTuiClient event: TuiPluginInput["event"] renderer: TuiPluginInput["renderer"] - names: DcpRouteNames palette: DcpPalette sessionID: () => string logger: Logger @@ -325,7 +322,6 @@ const SidebarContext = (props: { paddingBottom={1} paddingLeft={1} paddingRight={1} - onMouseUp={() => openPanel(props.api, props.names, "sidebar", props.sessionID())} > @@ -334,7 +330,6 @@ const SidebarContext = (props: { {LABEL}
- click for more {status().label} @@ -444,7 +439,6 @@ const SidebarContext = (props: { } export const createSidebarTopSlot = ( - api: TuiApi, client: DcpTuiClient, event: TuiPluginInput["event"], renderer: TuiPluginInput["renderer"], @@ -462,11 +456,9 @@ export const createSidebarTopSlot = ( ) return ( value.session_id} logger={logger} From 2b2719a45497dcb780c739999cda7b981f6a9ce4 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 11 Mar 2026 22:38:54 -0400 Subject: [PATCH 15/50] fix(tui): use flexbox layout for sidebar bars to adapt to scrollbar width --- tui/slots/sidebar-top.tsx | 74 +++++++++++++++------------------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 9af6fa12..334b3300 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -2,7 +2,6 @@ import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" import type { TuiPluginInput } from "@opencode-ai/plugin/tui" import { Logger } from "../../lib/logger" -import { truncate } from "../../lib/ui/utils" import { createPlaceholderContextSnapshot, invalidateContextSnapshot, @@ -13,11 +12,8 @@ import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared import { LABEL, type DcpRouteNames } from "../shared/names" import type { DcpMessageStatus, DcpTuiClient } from "../shared/types" -const BAR_WIDTH = 12 const SINGLE_BORDER = { type: "single" } as any const DIM_TEXT = { dim: true } as any -// Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5) -const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5 const REFRESH_DEBOUNCE_MS = 100 @@ -37,37 +33,17 @@ const compactTokenCount = (value: number): string => { return "0" } -const buildBar = (value: number, total: number) => { - if (total <= 0) return " ".repeat(BAR_WIDTH) - const filled = Math.max(0, Math.round((value / total) * BAR_WIDTH)) - return "█".repeat(filled).padEnd(BAR_WIDTH, " ") -} - -const buildMessageBar = ( +const buildMessageRuns = ( statuses: DcpMessageStatus[], - width: number = CONTENT_WIDTH, -): { text: string; status: DcpMessageStatus }[] => { - const ACTIVE = "█" - const PRUNED = "░" - if (statuses.length === 0) return [{ text: PRUNED.repeat(width), status: "pruned" }] - - // Map each bar position to a message status - const bar: DcpMessageStatus[] = new Array(width).fill("active") - for (let m = 0; m < statuses.length; m++) { - const start = Math.floor((m / statuses.length) * width) - const end = Math.floor(((m + 1) / statuses.length) * width) - for (let i = start; i < end; i++) { - bar[i] = statuses[m] - } - } +): { count: number; status: DcpMessageStatus }[] => { + if (statuses.length === 0) return [{ count: 1, status: "pruned" }] - // Group consecutive same-status positions into runs - const runs: { text: string; status: DcpMessageStatus }[] = [] + // Group consecutive same-status messages into runs + const runs: { count: number; status: DcpMessageStatus }[] = [] let runStart = 0 - for (let i = 1; i <= width; i++) { - if (i === width || bar[i] !== bar[runStart]) { - const char = bar[runStart] === "pruned" ? PRUNED : ACTIVE - runs.push({ text: char.repeat(i - runStart), status: bar[runStart] }) + for (let i = 1; i <= statuses.length; i++) { + if (i === statuses.length || statuses[i] !== statuses[runStart]) { + runs.push({ count: i - runStart, status: statuses[runStart] }) runStart = i } } @@ -111,17 +87,24 @@ const SidebarContextBar = (props: { props.total > 0 ? `${Math.round((props.value / props.total) * 100)}%` : "0%", ) const label = createMemo(() => props.label.padEnd(9, " ")) - const bar = createMemo(() => buildBar(props.value, props.total)) return ( - + {label()} {` ${percent().padStart(4, " ")} |`} - {bar()} - {`| ${compactTokenCount(props.value).padStart(5, " ")}`} + + {props.value > 0 && ( + + )} + {props.total > props.value && } + + + {`| ${compactTokenCount(props.value).padStart(5, " ")}`} + ) } @@ -299,7 +282,7 @@ const SidebarContext = (props: { const topicOverflow = createMemo(() => topicTotal() - topics().length) const fallbackNote = createMemo(() => snapshot().notes[0] ?? "") - const messageBarRuns = createMemo(() => buildMessageBar(snapshot().messageStatuses)) + const messageBarRuns = createMemo(() => buildMessageRuns(snapshot().messageStatuses)) const status = createMemo(() => { if (error() && snapshot().breakdown.total > 0) @@ -351,15 +334,14 @@ const SidebarContext = (props: { /> {snapshot().messageStatuses.length > 0 && ( - + {messageBarRuns().map((run) => ( - - {run.text} - + /> ))} )} @@ -402,7 +384,7 @@ const SidebarContext = (props: { Compressed Topics
{topics().map((t) => ( - {truncate(t, CONTENT_WIDTH)} + {t} ))} {topicOverflow() > 0 ? ( From e84df83ed9dacc4ff3aef4f4966dca1aa9eded8c Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 11 Mar 2026 22:53:41 -0400 Subject: [PATCH 16/50] fix: lazy-load tui plugin to prevent server crash when tui deps are missing --- index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 9aa61e32..b0124643 100644 --- a/index.ts +++ b/index.ts @@ -9,7 +9,6 @@ import { import { Logger } from "./lib/logger" import { createSessionState } from "./lib/state" import { PromptStore } from "./lib/prompts/store" -import tuiPlugin from "./tui/index" import { createChatMessageHandler, createChatMessageTransformHandler, @@ -20,6 +19,11 @@ import { } from "./lib/hooks" import { configureClientAuth, isSecureMode } from "./lib/auth" +let tuiPlugin: Record = {} +try { + tuiPlugin = (await import("./tui/index")).default +} catch {} + const server: Plugin = (async (ctx) => { const config = getConfig(ctx) From 5280bffbff60717ad5c3dd6d095d7f16c20c68a1 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 11 Mar 2026 22:57:24 -0400 Subject: [PATCH 17/50] fix(ci): skip devDependencies in security audit Transitive vulnerabilities (seroval, diff, file-type) come from TUI framework devDependencies that are not shipped to production. These cannot be fixed without upstream patches. --- .github/workflows/pr-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 124c9091..46711e33 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -32,5 +32,5 @@ jobs: run: npm run build - name: Security audit - run: npm audit --audit-level=high + run: npm audit --omit=dev --audit-level=high continue-on-error: false From 153d9593d8bd35110168d21dce52ff4f1fb5e529 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 12 Mar 2026 00:50:11 -0400 Subject: [PATCH 18/50] feat(tui): add compression summary route with collapsible sections --- tui/data/context.ts | 22 ++-- tui/index.tsx | 12 +- tui/routes/summary.tsx | 171 +++++++++++++++++++++++++++++ tui/shared/names.ts | 3 + tui/shared/types.ts | 7 +- tui/slots/sidebar-top.tsx | 61 ++++++++-- tui/types/opencode-plugin-tui.d.ts | 2 +- 7 files changed, 257 insertions(+), 21 deletions(-) create mode 100644 tui/routes/summary.tsx diff --git a/tui/data/context.ts b/tui/data/context.ts index a41a84ba..ddc24563 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -27,7 +27,7 @@ export const createPlaceholderContextSnapshot = ( persisted: { available: false, activeBlockCount: 0, - activeBlockTopics: [], + activeBlocks: [], activeBlockTopicTotal: 0, }, messageStatuses: [], @@ -36,6 +36,13 @@ export const createPlaceholderContextSnapshot = ( loadedAt: Date.now(), }) +function cleanBlockSummary(raw: string): string { + return raw + .replace(/^\s*\[Compressed conversation section\]\s*/i, "") + .replace(/\s*b\d+<\/dcp-message-id>\s*$/i, "") + .trim() +} + const buildState = async ( sessionID: string, messages: WithParts[], @@ -77,13 +84,12 @@ const loadContextSnapshot = async ( loadAllSessionStats(logger), ]) - const allTopics = Array.from(state.prune.messages.activeBlockIds) + const allBlocks = Array.from(state.prune.messages.activeBlockIds) .map((blockID) => state.prune.messages.blocksById.get(blockID)) - .filter((block): block is NonNullable => !!block) - .map((block) => block.topic) - .filter((topic) => !!topic) - const topics = allTopics.slice(0, 5) - const topicTotal = allTopics.length + .filter((block): block is NonNullable => !!block && !!block.topic) + .map((block) => ({ topic: block.topic, summary: cleanBlockSummary(block.summary) })) + const blocks = allBlocks.slice(0, 5) + const topicTotal = allBlocks.length const notes: string[] = [] if (persisted) { @@ -101,7 +107,7 @@ const loadContextSnapshot = async ( persisted: { available: !!persisted, activeBlockCount: state.prune.messages.activeBlockIds.size, - activeBlockTopics: topics, + activeBlocks: blocks, activeBlockTopicTotal: topicTotal, lastUpdated: persisted?.lastUpdated, }, diff --git a/tui/index.tsx b/tui/index.tsx index a28d1121..8060ac9b 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -3,6 +3,7 @@ import type { TuiPluginInput } from "@opencode-ai/plugin/tui" import { getConfigForDirectory } from "../lib/config" import { Logger } from "../lib/logger" import { createSidebarTopSlot } from "./slots/sidebar-top" +import { createSummaryRoute } from "./routes/summary" import { NAMES } from "./shared/names" const tui = async (input: TuiPluginInput) => { @@ -18,9 +19,18 @@ const tui = async (input: TuiPluginInput) => { const logger = new Logger(config.tui.debug, "tui") + input.api.route.register([createSummaryRoute(input.api)]) + if (config.tui.sidebar) { input.slots.register( - createSidebarTopSlot(input.client, input.event, input.renderer, NAMES, logger), + createSidebarTopSlot( + input.api, + input.client, + input.event, + input.renderer, + NAMES, + logger, + ), ) } } diff --git a/tui/routes/summary.tsx b/tui/routes/summary.tsx new file mode 100644 index 00000000..ff195344 --- /dev/null +++ b/tui/routes/summary.tsx @@ -0,0 +1,171 @@ +/** @jsxImportSource @opentui/solid */ +import { createMemo, createSignal, For, Show } from "solid-js" +import { useKeyboard } from "@opentui/solid" +import type { TuiApi } from "@opencode-ai/plugin/tui" +import { getPalette, type DcpPalette } from "../shared/theme" +import { LABEL, NAMES } from "../shared/names" + +const SINGLE_BORDER = { type: "single" } as any + +interface SummaryRouteParams { + topic?: string + summary?: string + sessionID?: string +} + +interface CollapsibleSection { + label: string + content: string +} + +interface ParsedSummary { + body: string + sections: CollapsibleSection[] +} + +const SECTION_HEADINGS: { pattern: RegExp; label: string }[] = [ + { + pattern: /\n*The following user messages were sent in this conversation verbatim:/, + label: "Protected User Messages", + }, + { + pattern: /\n*The following protected tools were used in this conversation as well:/, + label: "Protected Tools", + }, + { + pattern: + /\n*The following previously compressed summaries were also part of this conversation section:/, + label: "Included Compressed Summaries", + }, +] + +function parseSummary(raw: string): ParsedSummary { + if (!raw) return { body: "", sections: [] } + + const matches: { index: number; length: number; label: string }[] = [] + for (const heading of SECTION_HEADINGS) { + const match = raw.match(heading.pattern) + if (match && match.index !== undefined) { + matches.push({ index: match.index, length: match[0].length, label: heading.label }) + } + } + + if (matches.length === 0) { + return { body: raw, sections: [] } + } + + matches.sort((a, b) => a.index - b.index) + + const body = raw.slice(0, matches[0].index).trimEnd() + const sections: CollapsibleSection[] = [] + + for (let i = 0; i < matches.length; i++) { + const start = matches[i].index + matches[i].length + const end = i + 1 < matches.length ? matches[i + 1].index : raw.length + const content = raw.slice(start, end).trim() + if (content) { + sections.push({ label: matches[i].label, content }) + } + } + + return { body, sections } +} + +function CollapsibleSectionRow(props: { section: CollapsibleSection; palette: DcpPalette }) { + const [expanded, setExpanded] = createSignal(false) + + return ( + + + setExpanded(!expanded())} + > + {expanded() ? " ▼ " : " ▶ "} + + + setExpanded(!expanded())}> + {props.section.label} + + + + + + {props.section.content} + + + + ) +} + +function SummaryScreen(props: { api: TuiApi }) { + const params = createMemo(() => (props.api.route.current.params ?? {}) as SummaryRouteParams) + const palette = createMemo(() => getPalette(props.api.theme.current as Record)) + const parsed = createMemo(() => parseSummary(params().summary || "")) + + const keys = props.api.keybind?.create({ close: "escape" }) + + useKeyboard((evt: any) => { + if (props.api.route.current.name !== NAMES.routes.summary) return + if (props.api.ui?.dialog?.open) return + const matched = keys ? keys.match("close", evt) : evt.name === "escape" + if (!matched) return + evt.preventDefault() + evt.stopPropagation() + const sessionID = params().sessionID + if (sessionID) { + props.api.route.navigate("session", { sessionID }) + } else { + props.api.route.navigate("home") + } + }) + + return ( + + + + + {LABEL} + + + + {params().topic || "Compression Summary"} + + + + + {parsed().body || "(no summary available)"} + + + {(section) => } + + + + + + Press Escape to return + + + + ) +} + +export const createSummaryRoute = (api: TuiApi) => ({ + name: NAMES.routes.summary, + render: () => , +}) diff --git a/tui/shared/names.ts b/tui/shared/names.ts index a2187087..abffd675 100644 --- a/tui/shared/names.ts +++ b/tui/shared/names.ts @@ -2,6 +2,9 @@ export const LABEL = "DCP" export const NAMES = { slot: "dcp.sidebar", + routes: { + summary: "dcp.summary", + }, } as const export type DcpRouteNames = typeof NAMES diff --git a/tui/shared/types.ts b/tui/shared/types.ts index bae36262..a358d43b 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -8,10 +8,15 @@ export type DcpTuiClient = TuiPluginInput["client"] export type { DcpContextBreakdown, DcpMessageStatus } +export interface DcpActiveBlockInfo { + topic: string + summary: string +} + export interface DcpPersistedSummary { available: boolean activeBlockCount: number - activeBlockTopics: string[] + activeBlocks: DcpActiveBlockInfo[] activeBlockTopicTotal: number lastUpdated?: string } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 334b3300..8b4603e1 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @opentui/solid */ import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" -import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { TuiApi, TuiPluginInput } from "@opencode-ai/plugin/tui" import { Logger } from "../../lib/logger" import { createPlaceholderContextSnapshot, @@ -10,12 +10,16 @@ import { } from "../data/context" import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared/theme" import { LABEL, type DcpRouteNames } from "../shared/names" -import type { DcpMessageStatus, DcpTuiClient } from "../shared/types" +import type { DcpActiveBlockInfo, DcpMessageStatus, DcpTuiClient } from "../shared/types" const SINGLE_BORDER = { type: "single" } as any const DIM_TEXT = { dim: true } as any const REFRESH_DEBOUNCE_MS = 100 +const MAX_TOPIC_LEN = 30 + +const truncateTopic = (topic: string): string => + topic.length > MAX_TOPIC_LEN ? topic.slice(0, MAX_TOPIC_LEN - 3) + "..." : topic const compactTokenCount = (value: number): string => { if (value >= 1_000_000) { @@ -110,9 +114,11 @@ const SidebarContextBar = (props: { } const SidebarContext = (props: { + api: TuiApi client: DcpTuiClient event: TuiPluginInput["event"] renderer: TuiPluginInput["renderer"] + names: DcpRouteNames palette: DcpPalette sessionID: () => string logger: Logger @@ -277,9 +283,17 @@ const SidebarContext = (props: { ), ) - const topics = createMemo(() => snapshot().persisted.activeBlockTopics) + const blocks = createMemo(() => snapshot().persisted.activeBlocks) const topicTotal = createMemo(() => snapshot().persisted.activeBlockTopicTotal) - const topicOverflow = createMemo(() => topicTotal() - topics().length) + const topicOverflow = createMemo(() => topicTotal() - blocks().length) + + const navigateToSummary = (block: DcpActiveBlockInfo) => { + props.api.route.navigate(props.names.routes.summary, { + topic: block.topic, + summary: block.summary, + sessionID: props.sessionID(), + }) + } const fallbackNote = createMemo(() => snapshot().notes[0] ?? "") const messageBarRuns = createMemo(() => buildMessageRuns(snapshot().messageStatuses)) @@ -378,18 +392,42 @@ const SidebarContext = (props: { - {topics().length > 0 ? ( + {blocks().length > 0 ? ( <> Compressed Topics - {topics().map((t) => ( - {t} + {blocks().map((block) => ( + + + + {truncateTopic(block.topic)} + + + + navigateToSummary(block)} + > + + + + ))} {topicOverflow() > 0 ? ( - - ... {topicOverflow()} more topics - + + + + ... {topicOverflow()} more topics + + + + + + + + ) : null} ) : fallbackNote() ? ( @@ -421,6 +459,7 @@ const SidebarContext = (props: { } export const createSidebarTopSlot = ( + api: TuiApi, client: DcpTuiClient, event: TuiPluginInput["event"], renderer: TuiPluginInput["renderer"], @@ -438,9 +477,11 @@ export const createSidebarTopSlot = ( ) return ( value.session_id} logger={logger} diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index 5df963d7..bf2b2336 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -41,7 +41,7 @@ declare module "@opencode-ai/plugin/tui" { variant?: string duration?: number }) => void - dialog: { + dialog?: { open?: boolean } } From c966d7e1827dbbe63cfa7083b7e7849259431d8c Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 12 Mar 2026 01:08:42 -0400 Subject: [PATCH 19/50] feat(tui): add expandable topic list in sidebar --- tui/data/context.ts | 6 +----- tui/shared/types.ts | 1 - tui/slots/sidebar-top.tsx | 24 ++++++++++++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tui/data/context.ts b/tui/data/context.ts index ddc24563..d258b75d 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -28,7 +28,6 @@ export const createPlaceholderContextSnapshot = ( available: false, activeBlockCount: 0, activeBlocks: [], - activeBlockTopicTotal: 0, }, messageStatuses: [], allTimeStats: { totalTokensSaved: 0, sessionCount: 0 }, @@ -88,8 +87,6 @@ const loadContextSnapshot = async ( .map((blockID) => state.prune.messages.blocksById.get(blockID)) .filter((block): block is NonNullable => !!block && !!block.topic) .map((block) => ({ topic: block.topic, summary: cleanBlockSummary(block.summary) })) - const blocks = allBlocks.slice(0, 5) - const topicTotal = allBlocks.length const notes: string[] = [] if (persisted) { @@ -107,8 +104,7 @@ const loadContextSnapshot = async ( persisted: { available: !!persisted, activeBlockCount: state.prune.messages.activeBlockIds.size, - activeBlocks: blocks, - activeBlockTopicTotal: topicTotal, + activeBlocks: allBlocks, lastUpdated: persisted?.lastUpdated, }, messageStatuses, diff --git a/tui/shared/types.ts b/tui/shared/types.ts index a358d43b..492644a1 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -17,7 +17,6 @@ export interface DcpPersistedSummary { available: boolean activeBlockCount: number activeBlocks: DcpActiveBlockInfo[] - activeBlockTopicTotal: number lastUpdated?: string } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 8b4603e1..e8dbb761 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -283,9 +283,13 @@ const SidebarContext = (props: { ), ) - const blocks = createMemo(() => snapshot().persisted.activeBlocks) - const topicTotal = createMemo(() => snapshot().persisted.activeBlockTopicTotal) - const topicOverflow = createMemo(() => topicTotal() - blocks().length) + const TOPIC_LIMIT = 3 + const allBlocks = createMemo(() => snapshot().persisted.activeBlocks) + const [topicsExpanded, setTopicsExpanded] = createSignal(false) + const blocks = createMemo(() => + topicsExpanded() ? allBlocks() : allBlocks().slice(0, TOPIC_LIMIT), + ) + const topicOverflow = createMemo(() => allBlocks().length - TOPIC_LIMIT) const navigateToSummary = (block: DcpActiveBlockInfo) => { props.api.route.navigate(props.names.routes.summary, { @@ -419,12 +423,20 @@ const SidebarContext = (props: { - ... {topicOverflow()} more topics + {topicsExpanded() + ? `showing all ${allBlocks().length} topics` + : `... ${topicOverflow()} more topics`} - - + setTopicsExpanded(!topicsExpanded())} + > + + {topicsExpanded() ? " ▲ " : " ▼ "} + From 9bbc8d83beb123366100b6341db97be4d3ceb840 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 12 Mar 2026 01:24:43 -0400 Subject: [PATCH 20/50] fix(tui): rename and reorder sidebar summary rows --- tui/slots/sidebar-top.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index e8dbb761..0233937b 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -337,18 +337,18 @@ const SidebarContext = (props: { {snapshot().messageStatuses.length > 0 && ( From b6b414f3a94b645b5ab01913cf78995cf0e6c973 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 12 Mar 2026 21:22:29 -0400 Subject: [PATCH 21/50] chore: remove dead code --- lib/config.ts | 1 - lib/ui/utils.ts | 17 ----------------- tui/shared/types.ts | 2 +- tui/slots/sidebar-top.tsx | 2 +- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 49e9adc9..39ae41b7 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -98,7 +98,6 @@ export const VALID_CONFIG_KEYS = new Set([ "$schema", "enabled", "debug", - "showUpdateToasts", "pruneNotification", "pruneNotificationType", "turnProtection", diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts index ad31542c..9cd92ef1 100644 --- a/lib/ui/utils.ts +++ b/lib/ui/utils.ts @@ -288,20 +288,3 @@ export function formatPrunedItemsList( return lines } - -export function formatPruningResultForTool( - prunedIds: string[], - toolMetadata: Map, - workingDirectory?: string, -): string { - const lines: string[] = [] - lines.push(`Context pruning complete. Pruned ${prunedIds.length} tool outputs.`) - lines.push("") - - if (prunedIds.length > 0) { - lines.push(`Semantically pruned (${prunedIds.length}):`) - lines.push(...formatPrunedItemsList(prunedIds, toolMetadata, workingDirectory)) - } - - return lines.join("\n").trim() -} diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 492644a1..70632697 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -6,7 +6,7 @@ import type { export type DcpTuiClient = TuiPluginInput["client"] -export type { DcpContextBreakdown, DcpMessageStatus } +export type { DcpMessageStatus } export interface DcpActiveBlockInfo { topic: string diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 0233937b..3236e094 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -40,7 +40,7 @@ const compactTokenCount = (value: number): string => { const buildMessageRuns = ( statuses: DcpMessageStatus[], ): { count: number; status: DcpMessageStatus }[] => { - if (statuses.length === 0) return [{ count: 1, status: "pruned" }] + if (statuses.length === 0) return [] // Group consecutive same-status messages into runs const runs: { count: number; status: DcpMessageStatus }[] = [] From 2d9d1334e8192111a67768f6d4d08324c5e89f3b Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 23 Mar 2026 12:33:26 -0400 Subject: [PATCH 22/50] fix(lib): pass session messages to compress notifications --- lib/compress/pipeline.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/compress/pipeline.ts b/lib/compress/pipeline.ts index 5f9875e6..390932cc 100644 --- a/lib/compress/pipeline.ts +++ b/lib/compress/pipeline.ts @@ -88,9 +88,10 @@ export async function finalizeSession( await saveSessionState(ctx.state, ctx.logger) const params = getCurrentParams(ctx.state, rawMessages, ctx.logger) - const sessionMessageIds = rawMessages - .filter((msg) => !isIgnoredUserMessage(msg)) - .map((msg) => msg.info.id) + const totalSessionTokens = getCurrentTokenUsage(ctx.state, rawMessages) + const sessionMessages = rawMessages.filter( + (msg) => !(msg.info.role === "user" && isIgnoredUserMessage(msg)), + ) await sendCompressNotification( ctx.client, @@ -100,7 +101,8 @@ export async function finalizeSession( toolCtx.sessionID, entries, batchTopic, - sessionMessageIds, + totalSessionTokens, + sessionMessages, params, ) } From fa3f448f29dfa0286b20d20fb2c9c83cdaf725fa Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 23 Mar 2026 12:33:26 -0400 Subject: [PATCH 23/50] refactor(tui): port sidebar plugin to snapshot api --- tui/index.tsx | 19 +- tui/routes/summary.tsx | 11 +- tui/shared/types.ts | 5 +- tui/slots/sidebar-top.tsx | 43 ++-- tui/types/opencode-plugin-tui.d.ts | 368 +++++++++++++++++++++++++---- 5 files changed, 353 insertions(+), 93 deletions(-) diff --git a/tui/index.tsx b/tui/index.tsx index 8060ac9b..a159bc56 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -1,14 +1,14 @@ /** @jsxImportSource @opentui/solid */ -import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { TuiPlugin } from "@opencode-ai/plugin/tui" import { getConfigForDirectory } from "../lib/config" import { Logger } from "../lib/logger" import { createSidebarTopSlot } from "./slots/sidebar-top" import { createSummaryRoute } from "./routes/summary" import { NAMES } from "./shared/names" -const tui = async (input: TuiPluginInput) => { +const tui: TuiPlugin = async (api) => { const config = getConfigForDirectory(process.cwd(), (title, message) => { - input.api.ui.toast({ + api.ui.toast({ title, message, variant: "warning", @@ -19,19 +19,10 @@ const tui = async (input: TuiPluginInput) => { const logger = new Logger(config.tui.debug, "tui") - input.api.route.register([createSummaryRoute(input.api)]) + api.route.register([createSummaryRoute(api)]) if (config.tui.sidebar) { - input.slots.register( - createSidebarTopSlot( - input.api, - input.client, - input.event, - input.renderer, - NAMES, - logger, - ), - ) + api.slots.register(createSidebarTopSlot(api, NAMES, logger)) } } diff --git a/tui/routes/summary.tsx b/tui/routes/summary.tsx index ff195344..8cd42fff 100644 --- a/tui/routes/summary.tsx +++ b/tui/routes/summary.tsx @@ -100,16 +100,19 @@ function CollapsibleSectionRow(props: { section: CollapsibleSection; palette: Dc } function SummaryScreen(props: { api: TuiApi }) { - const params = createMemo(() => (props.api.route.current.params ?? {}) as SummaryRouteParams) + const params = createMemo(() => { + const current = props.api.route.current + return ("params" in current ? current.params : {}) as SummaryRouteParams + }) const palette = createMemo(() => getPalette(props.api.theme.current as Record)) const parsed = createMemo(() => parseSummary(params().summary || "")) - const keys = props.api.keybind?.create({ close: "escape" }) + const keys = props.api.keybind.create({ close: "escape" }) useKeyboard((evt: any) => { if (props.api.route.current.name !== NAMES.routes.summary) return - if (props.api.ui?.dialog?.open) return - const matched = keys ? keys.match("close", evt) : evt.name === "escape" + if (props.api.ui.dialog.open) return + const matched = keys.match("close", evt) if (!matched) return evt.preventDefault() evt.stopPropagation() diff --git a/tui/shared/types.ts b/tui/shared/types.ts index 70632697..f1128fc4 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -1,10 +1,11 @@ -import type { TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { TuiPluginApi } from "@opencode-ai/plugin/tui" import type { MessageStatus as DcpMessageStatus, TokenBreakdown as DcpContextBreakdown, } from "../../lib/analysis/tokens" -export type DcpTuiClient = TuiPluginInput["client"] +export type DcpTuiApi = TuiPluginApi +export type DcpTuiClient = DcpTuiApi["client"] export type { DcpMessageStatus } diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 3236e094..a878e5a0 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @opentui/solid */ import { createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js" -import type { TuiApi, TuiPluginInput } from "@opencode-ai/plugin/tui" +import type { TuiSlotPlugin } from "@opencode-ai/plugin/tui" import { Logger } from "../../lib/logger" import { createPlaceholderContextSnapshot, @@ -10,7 +10,7 @@ import { } from "../data/context" import { getPalette, toneColor, type DcpColor, type DcpPalette } from "../shared/theme" import { LABEL, type DcpRouteNames } from "../shared/names" -import type { DcpActiveBlockInfo, DcpMessageStatus, DcpTuiClient } from "../shared/types" +import type { DcpActiveBlockInfo, DcpMessageStatus, DcpTuiApi } from "../shared/types" const SINGLE_BORDER = { type: "single" } as any const DIM_TEXT = { dim: true } as any @@ -114,10 +114,7 @@ const SidebarContextBar = (props: { } const SidebarContext = (props: { - api: TuiApi - client: DcpTuiClient - event: TuiPluginInput["event"] - renderer: TuiPluginInput["renderer"] + api: DcpTuiApi names: DcpRouteNames palette: DcpPalette sessionID: () => string @@ -137,7 +134,7 @@ const SidebarContext = (props: { renderTimeout = setTimeout(() => { renderTimeout = undefined try { - props.renderer.requestRender() + props.api.renderer.requestRender() } catch (error) { props.logger.warn("Failed to request TUI render", { error: error instanceof Error ? error.message : String(error), @@ -180,7 +177,7 @@ const SidebarContext = (props: { const currentRequest = ++requestVersion try { - const value = await loadContextSnapshotCached(props.client, props.logger, sessionID) + const value = await loadContextSnapshotCached(props.api.client, props.logger, sessionID) if (currentRequest !== requestVersion || props.sessionID() !== sessionID) { return } @@ -230,43 +227,43 @@ const SidebarContext = (props: { } const unsubs = [ - props.event.on("message.updated", (event) => { + props.api.event.on("message.updated", (event) => { if (event.properties.info.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("message.removed", (event) => { + props.api.event.on("message.removed", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("message.part.updated", (event) => { + props.api.event.on("message.part.updated", (event) => { if (event.properties.part.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("message.part.delta", (event) => { + props.api.event.on("message.part.delta", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("message.part.removed", (event) => { + props.api.event.on("message.part.removed", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("session.updated", (event) => { + props.api.event.on("session.updated", (event) => { if (event.properties.info.id !== sessionID) return scheduleRefresh() }), - props.event.on("session.deleted", (event) => { + props.api.event.on("session.deleted", (event) => { if (event.properties.info.id !== sessionID) return scheduleRefresh() }), - props.event.on("session.diff", (event) => { + props.api.event.on("session.diff", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("session.error", (event) => { + props.api.event.on("session.error", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), - props.event.on("session.status", (event) => { + props.api.event.on("session.status", (event) => { if (event.properties.sessionID !== sessionID) return scheduleRefresh() }), @@ -471,13 +468,10 @@ const SidebarContext = (props: { } export const createSidebarTopSlot = ( - api: TuiApi, - client: DcpTuiClient, - event: TuiPluginInput["event"], - renderer: TuiPluginInput["renderer"], + api: DcpTuiApi, names: DcpRouteNames, logger: Logger, -) => ({ +): TuiSlotPlugin => ({ id: names.slot, slots: { sidebar_top( @@ -490,9 +484,6 @@ export const createSidebarTopSlot = ( return ( value.session_id} diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index bf2b2336..daf16b65 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -1,73 +1,347 @@ declare module "@opencode-ai/plugin/tui" { - export interface TuiCommand { + import type { + createOpencodeClient as createOpencodeClientV2, + Event as TuiEvent, + LspStatus, + McpStatus, + Todo, + } from "@opencode-ai/sdk/v2" + import type { CliRenderer, ParsedKey, Plugin as CorePlugin, SlotMode } from "@opentui/core" + import type { JSX } from "@opentui/solid" + import type { Plugin as ServerPlugin } from "@opencode-ai/plugin" + + export type { CliRenderer, SlotMode } + + export type TuiRouteCurrent = + | { + name: "home" + } + | { + name: "session" + params: { + sessionID: string + initialPrompt?: unknown + } + } + | { + name: string + params?: Record + } + + export type TuiRouteDefinition = { + name: string + render: (input: { params?: Record }) => JSX.Element + } + + export type TuiCommand = { title: string value: string description?: string category?: string - keybind?: unknown + keybind?: string + suggested?: boolean + hidden?: boolean + enabled?: boolean slash?: { name: string + aliases?: string[] } onSelect?: () => void } - export interface TuiRouteDefinition { + export type TuiKeybind = { name: string - render: (input: { params?: Record }) => Node + ctrl: boolean + meta: boolean + shift: boolean + super?: boolean + leader: boolean + } + + export type TuiKeybindMap = Record + + export type TuiKeybindSet = { + readonly all: TuiKeybindMap + get: (name: string) => string + match: (name: string, evt: ParsedKey) => boolean + print: (name: string) => string + } + + export type TuiDialogProps = { + size?: "medium" | "large" + onClose: () => void + children?: JSX.Element } - export interface TuiKeybindSet { - get: (key: string) => unknown - match: (key: string, evt: unknown) => boolean + export type TuiDialogStack = { + replace: (render: () => JSX.Element, onClose?: () => void) => void + clear: () => void + setSize: (size: "medium" | "large") => void + readonly size: "medium" | "large" + readonly depth: number + readonly open: boolean } - export interface TuiApi { + export type TuiDialogAlertProps = { + title: string + message: string + onConfirm?: () => void + } + + export type TuiDialogConfirmProps = { + title: string + message: string + onConfirm?: () => void + onCancel?: () => void + } + + export type TuiDialogPromptProps = { + title: string + description?: () => JSX.Element + placeholder?: string + value?: string + onConfirm?: (value: string) => void + onCancel?: () => void + } + + export type TuiDialogSelectOption = { + title: string + value: Value + description?: string + footer?: JSX.Element | string + category?: string + disabled?: boolean + onSelect?: () => void + } + + export type TuiDialogSelectProps = { + title: string + placeholder?: string + options: TuiDialogSelectOption[] + flat?: boolean + onMove?: (option: TuiDialogSelectOption) => void + onFilter?: (query: string) => void + onSelect?: (option: TuiDialogSelectOption) => void + skipFilter?: boolean + current?: Value + } + + export type TuiToast = { + variant?: "info" | "success" | "warning" | "error" + title?: string + message: string + duration?: number + } + + export type TuiTheme = { + readonly current: Record + readonly selected: string + has: (name: string) => boolean + set: (name: string) => boolean + install: (jsonPath: string) => Promise + mode: () => "dark" | "light" + readonly ready: boolean + } + + export type TuiKV = { + get: (key: string, fallback?: Value) => Value + set: (key: string, value: unknown) => void + readonly ready: boolean + } + + export type TuiState = { + session: { + diff: (sessionID: string) => ReadonlyArray + todo: (sessionID: string) => ReadonlyArray + } + lsp: () => ReadonlyArray + mcp: () => ReadonlyArray + } + + export type TuiApi = { command: { - register: (cb: () => TuiCommand[]) => void + register: (cb: () => TuiCommand[]) => () => void trigger: (value: string) => void } route: { - register: (routes: TuiRouteDefinition[]) => () => void - navigate: (name: string, params?: any) => void - current: { - name: string - params?: any - } + register: (routes: TuiRouteDefinition[]) => () => void + navigate: (name: string, params?: Record) => void + readonly current: TuiRouteCurrent } ui: { - toast: (input: { - title: string - message: string - variant?: string - duration?: number - }) => void - dialog?: { - open?: boolean - } - } - keybind?: { - create: ( - defaults: Record, - overrides?: Record, - ) => TuiKeybindSet - } - theme: { - current: unknown - } - } - - export interface TuiPluginInput< - Renderer extends { requestRender: () => void } = { requestRender: () => void }, - Node = unknown, - > { - client: any - event: { - on: (name: string, cb: (event: any) => void) => () => void + Dialog: (props: TuiDialogProps) => JSX.Element + DialogAlert: (props: TuiDialogAlertProps) => JSX.Element + DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element + DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element + DialogSelect: (props: TuiDialogSelectProps) => JSX.Element + toast: (input: TuiToast) => void + dialog: TuiDialogStack } - renderer: Renderer - slots: { - register: (slot: any) => () => void + keybind: { + match: (key: string, evt: ParsedKey) => boolean + print: (key: string) => string + create: (defaults: TuiKeybindMap, overrides?: Record) => TuiKeybindSet } - api: TuiApi + kv: TuiKV + state: TuiState + theme: TuiTheme + } + + export type TuiSidebarMcpItem = { + name: string + status: McpStatus["status"] + error?: string + } + + export type TuiSidebarLspItem = Pick + + export type TuiSidebarTodoItem = Pick + + export type TuiSidebarFileItem = { + file: string + additions: number + deletions: number + } + + export type TuiSlotMap = { + app: {} + home_logo: {} + home_tips: { + show_tips: boolean + tips_hidden: boolean + first_time_user: boolean + } + home_below_tips: { + show_tips: boolean + tips_hidden: boolean + first_time_user: boolean + } + sidebar_top: { + session_id: string + } + sidebar_title: { + session_id: string + title: string + share_url?: string + } + sidebar_context: { + session_id: string + tokens: number + percentage: number | null + cost: number + } + sidebar_mcp: { + session_id: string + items: TuiSidebarMcpItem[] + connected: number + errors: number + } + sidebar_lsp: { + session_id: string + items: TuiSidebarLspItem[] + disabled: boolean + } + sidebar_todo: { + session_id: string + items: TuiSidebarTodoItem[] + } + sidebar_files: { + session_id: string + items: TuiSidebarFileItem[] + } + sidebar_getting_started: { + session_id: string + show_getting_started: boolean + has_providers: boolean + dismissed: boolean + } + sidebar_directory: { + session_id: string + directory: string + directory_parent: string + directory_name: string + } + sidebar_version: { + session_id: string + version: string + } + sidebar_bottom: { + session_id: string + directory: string + directory_parent: string + directory_name: string + version: string + show_getting_started: boolean + has_providers: boolean + dismissed: boolean + } + } + + export type TuiSlotContext = { + theme: TuiTheme + } + + export type TuiSlotPlugin = CorePlugin + + export type TuiSlots = { + register: (plugin: TuiSlotPlugin) => () => void + } + + export type TuiEventBus = { + on: ( + type: Type, + handler: (event: Extract) => void, + ) => () => void + } + + export type TuiDispose = () => void | Promise + + export type TuiLifecycle = { + readonly signal: AbortSignal + onDispose: (fn: TuiDispose) => () => void + } + + export type TuiPluginState = "first" | "updated" | "same" + + export type TuiPluginEntry = { + name: string + source: "file" | "npm" | "internal" + spec: string + target: string + requested?: string + version?: string + modified?: number + first_time: number + last_time: number + time_changed: number + load_count: number + fingerprint: string + } + + export type TuiPluginMeta = TuiPluginEntry & { + state: TuiPluginState + } + + export type TuiHostPluginApi = TuiApi & { + client: ReturnType + event: TuiEventBus + renderer: Renderer + } + + export type TuiPluginApi = TuiHostPluginApi & { + slots: TuiSlots + lifecycle: TuiLifecycle + } + + export type TuiPlugin = ( + api: TuiPluginApi, + options: Record | undefined, + meta: TuiPluginMeta, + ) => Promise + + export type TuiPluginModule = { + server?: ServerPlugin + tui?: TuiPlugin + slots?: TuiSlotPlugin } } From 6e62a67fb45b995005b910fd0992b7ad2eb1c14e Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 25 Mar 2026 01:51:55 -0400 Subject: [PATCH 24/50] fix(tui): restore sidebar widget on new host layout --- tui/slots/sidebar-top.tsx | 46 ++++++++++++++++-------------- tui/types/opencode-plugin-tui.d.ts | 3 ++ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index a878e5a0..3042f042 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -471,25 +471,29 @@ export const createSidebarTopSlot = ( api: DcpTuiApi, names: DcpRouteNames, logger: Logger, -): TuiSlotPlugin => ({ - id: names.slot, - slots: { - sidebar_top( - ctx: { theme: { current: Record } }, - value: { session_id: string }, - ) { - const palette = createMemo(() => - getPalette(ctx.theme.current as Record), - ) - return ( - value.session_id} - logger={logger} - /> - ) +): TuiSlotPlugin => { + const renderSidebar = ( + ctx: { theme: { current: Record } }, + value: { session_id: string }, + ) => { + const palette = createMemo(() => getPalette(ctx.theme.current as Record)) + return ( + value.session_id} + logger={logger} + /> + ) + } + + return { + id: names.slot, + order: 90, + slots: { + sidebar_content: renderSidebar, + sidebar_top: renderSidebar, }, - }, -}) + } +} diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index daf16b65..ebf7befc 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -219,6 +219,9 @@ declare module "@opencode-ai/plugin/tui" { sidebar_top: { session_id: string } + sidebar_content: { + session_id: string + } sidebar_title: { session_id: string title: string From b72e6203ca2de88064815b37c31d5baee0df4ca8 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 25 Mar 2026 18:49:58 -0400 Subject: [PATCH 25/50] chore(deps): update opencode sdk/plugin to 1.3.2 --- tui/package-lock.json | 2453 +++++++++++++++++++++++++++++++++++++++++ tui/package.json | 2 +- 2 files changed, 2454 insertions(+), 1 deletion(-) create mode 100644 tui/package-lock.json diff --git a/tui/package-lock.json b/tui/package-lock.json new file mode 100644 index 00000000..ca7a02c3 --- /dev/null +++ b/tui/package-lock.json @@ -0,0 +1,2453 @@ +{ + "name": "dcp-tui-probe-local", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dcp-tui-probe-local", + "dependencies": { + "@opencode-ai/plugin": "1.3.2", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", + "solid-js": "1.9.9" + } + }, + "../../../../../src/opencode/node_modules/.bun/solid-js@1.9.10/node_modules/solid-js": { + "version": "1.9.10", + "extraneous": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.3.0", + "seroval-plugins": "~1.3.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier2d-simd-compat": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@dimforge/rapier2d-simd-compat/-/rapier2d-simd-compat-0.17.3.tgz", + "integrity": "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/@jimp/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", + "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", + "license": "MIT", + "dependencies": { + "@jimp/file-ops": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "await-to-js": "^3.0.0", + "exif-parser": "^0.1.12", + "file-type": "^16.0.0", + "mime": "3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/diff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", + "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", + "license": "MIT", + "dependencies": { + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "pixelmatch": "^5.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/file-ops": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", + "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-bmp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", + "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "bmp-ts": "^1.0.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-gif": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", + "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "gifwrap": "^0.10.1", + "omggif": "^1.0.10" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-jpeg": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", + "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "jpeg-js": "^0.4.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-png": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", + "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "pngjs": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-tiff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", + "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "utif2": "^4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", + "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", + "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", + "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", + "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "tinycolor2": "^1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-color/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", + "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-contain/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", + "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-cover/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", + "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-crop/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", + "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-displace/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", + "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", + "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", + "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-flip/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-hash": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", + "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "any-base": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", + "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", + "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/types": "1.6.0", + "parse-bmfont-ascii": "^1.0.6", + "parse-bmfont-binary": "^1.0.6", + "parse-bmfont-xml": "^1.1.6", + "simple-xml-to-json": "^1.2.2", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-print/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-quantize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", + "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-quantize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", + "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-resize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", + "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-rotate/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-threshold": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", + "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-threshold/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/types": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", + "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "license": "MIT", + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/types/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@opencode-ai/plugin": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.2.tgz", + "integrity": "sha512-eT0ZovMCOQlfTdAnfbEWgW343mJ9SHgEVfdiOSX1NMIVXac6hxE2xwUsRVTV3wLvfA6dKZhN800f8wLUEyPlyg==", + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.3.2", + "zod": "4.1.8" + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.2.tgz", + "integrity": "sha512-u7sXVKn0kyAA5vVVHuHQfq3+3UGWOU1Sh6d/e+aS4zO8AwriTSWNQ9r8Qy5yxBH+PoeOGl5WIVdp+s2Ea2zuAg==", + "license": "MIT" + }, + "node_modules/@opentui/core": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-e/n7hCtpOzS57X9llODu0SUXCQBWSxHQeTA0iuL7j0nhSFgM6KpL8kJ7VQBU1EEn33pytA0udbfKSJ6sqWmEJg==", + "license": "MIT", + "dependencies": { + "bun-ffi-structs": "0.1.2", + "diff": "8.0.2", + "jimp": "1.6.0", + "marked": "17.0.1", + "yoga-layout": "3.2.1" + }, + "optionalDependencies": { + "@dimforge/rapier2d-simd-compat": "^0.17.3", + "@opentui/core-darwin-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-darwin-x64": "0.0.0-20260307-536c401b", + "@opentui/core-linux-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-linux-x64": "0.0.0-20260307-536c401b", + "@opentui/core-win32-arm64": "0.0.0-20260307-536c401b", + "@opentui/core-win32-x64": "0.0.0-20260307-536c401b", + "bun-webgpu": "0.1.5", + "planck": "^1.4.2", + "three": "0.177.0" + }, + "peerDependencies": { + "web-tree-sitter": "0.25.10" + } + }, + "node_modules/@opentui/core-darwin-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-y46MUgcjkIqC/IBxErchM51KmLARxudrKqr09Gyy25ry+GUE8gzaEIx6EeMAUnWDWvetMacKgEaNCjtdkfGgDQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@opentui/core-darwin-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-USf14JkFaCyKvn9FfLn6AZv14o5ED7uHBNq4kCmggD28HmqHsklDhGNyDnswUggCworJ6xz7jICZTiKKrSwRbQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@opentui/core-linux-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-fzNf0Mv7OjNktJFg17WsvdDD5Ej12eSwPVMProlQFbklC8qCEsZfLJKYq9ExYLRoxHX7wFm9Eq6L7hVaGcn2sA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@opentui/core-linux-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-+80TgK5ZhdJvM2+fiCbeCJvXk9De3oNB42wcCtGcwt3x1wyPYAbWIetw6dIGqXIbica/El+7+6Y2DMV06PUUug==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@opentui/core-win32-arm64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-SBeHYwNpWJlHxMX6+aO8KsatWpMMxOs+LpFA7M2PTV0g81WUHPlxm6kHi6UHpjwYuslvtcYKgUL0IyQs1jbdNA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opentui/core-win32-x64": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-QIU/s6NrXJLRlTyLJZ/41E3MhVGGZazPrwv6MnMx7LOE/uBQo4OGjcRdsIIkhXYIqNRUIH/Yfd5Hyf6twJpBBA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opentui/solid": { + "version": "0.0.0-20260307-536c401b", + "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.0.0-20260307-536c401b.tgz", + "integrity": "sha512-wfItFCVBsP2iWvFgj2/lVRN7/O7R5eu9NReY4Wl34Z+c9d7P6FVSa1xOriziTPysukW1OhFe8MNN7MIaggYdHg==", + "license": "MIT", + "dependencies": { + "@babel/core": "7.28.0", + "@babel/preset-typescript": "7.27.1", + "@opentui/core": "0.0.0-20260307-536c401b", + "babel-plugin-module-resolver": "5.0.2", + "babel-preset-solid": "1.9.9", + "entities": "7.0.1", + "s-js": "^0.4.9" + }, + "peerDependencies": { + "solid-js": "1.9.9" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", + "license": "MIT" + }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "license": "MIT" + }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.40.6", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.6.tgz", + "integrity": "sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-plugin-module-resolver": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", + "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", + "license": "MIT", + "dependencies": { + "find-babel-config": "^2.1.1", + "glob": "^9.3.3", + "pkg-up": "^3.1.0", + "reselect": "^4.1.7", + "resolve": "^1.22.8" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", + "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", + "license": "MIT", + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.40.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "solid-js": "^1.9.8" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", + "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bmp-ts": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bun-ffi-structs": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/bun-ffi-structs/-/bun-ffi-structs-0.1.2.tgz", + "integrity": "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w==", + "license": "MIT", + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/bun-webgpu": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/bun-webgpu/-/bun-webgpu-0.1.5.tgz", + "integrity": "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@webgpu/types": "^0.1.60" + }, + "optionalDependencies": { + "bun-webgpu-darwin-arm64": "^0.1.5", + "bun-webgpu-darwin-x64": "^0.1.5", + "bun-webgpu-linux-x64": "^0.1.5", + "bun-webgpu-win32-x64": "^0.1.5" + } + }, + "node_modules/bun-webgpu-darwin-arm64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-darwin-arm64/-/bun-webgpu-darwin-arm64-0.1.6.tgz", + "integrity": "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/bun-webgpu-darwin-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-darwin-x64/-/bun-webgpu-darwin-x64-0.1.6.tgz", + "integrity": "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/bun-webgpu-linux-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-linux-x64/-/bun-webgpu-linux-x64-0.1.6.tgz", + "integrity": "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/bun-webgpu-win32-x64": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bun-webgpu-win32-x64/-/bun-webgpu-win32-x64-0.1.6.tgz", + "integrity": "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.325", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.325.tgz", + "integrity": "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA==", + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" + }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/find-babel-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", + "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", + "license": "MIT", + "dependencies": { + "json5": "^2.2.3" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, + "node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "license": "MIT", + "dependencies": { + "@types/node": "16.9.1" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jimp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", + "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/diff": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-gif": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-blur": "1.6.0", + "@jimp/plugin-circle": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-contain": "1.6.0", + "@jimp/plugin-cover": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-displace": "1.6.0", + "@jimp/plugin-dither": "1.6.0", + "@jimp/plugin-fisheye": "1.6.0", + "@jimp/plugin-flip": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/plugin-mask": "1.6.0", + "@jimp/plugin-print": "1.6.0", + "@jimp/plugin-quantize": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/plugin-rotate": "1.6.0", + "@jimp/plugin-threshold": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "license": "BSD-3-Clause" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/marked": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", + "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/minimatch": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "license": "MIT" + }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "license": "ISC", + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/planck": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/planck/-/planck-1.4.3.tgz", + "integrity": "sha512-B+lHKhRSeg7vZOfEyEzyQVu7nx8JHcX3QgnAcHXrPW0j04XYKX5eXSiUrxH2Z5QR8OoqvjD6zKIaPMdMYAd0uA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=24.0" + }, + "peerDependencies": { + "stage-js": "^1.0.0-alpha.12" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/s-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/s-js/-/s-js-0.4.9.tgz", + "integrity": "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz", + "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/simple-xml-to-json": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.4.tgz", + "integrity": "sha512-3MY16e0ocMHL7N1ufpdObURGyX+lCo0T/A+y6VCwosLdH1HSda4QZl1Sdt/O+2qWp48WFi26XEp5rF0LoaL0Dg==", + "license": "MIT", + "engines": { + "node": ">=20.12.2" + } + }, + "node_modules/solid-js": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", + "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.3.0", + "seroval-plugins": "~1.3.0" + } + }, + "node_modules/stage-js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stage-js/-/stage-js-1.0.1.tgz", + "integrity": "sha512-cz14aPp/wY0s3bkb/B93BPP5ZAEhgBbRmAT3CCDqert8eCAqIpQ0RB2zpK8Ksxf+Pisl5oTzvPHtL4CVzzeHcw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/three": { + "version": "0.177.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", + "integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==", + "license": "MIT", + "optional": true + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.11" + } + }, + "node_modules/web-tree-sitter": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.25.10.tgz", + "integrity": "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/emscripten": "^1.40.0" + }, + "peerDependenciesMeta": { + "@types/emscripten": { + "optional": true + } + } + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", + "license": "MIT" + }, + "node_modules/zod": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", + "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/tui/package.json b/tui/package.json index 2960722a..e59f69fd 100644 --- a/tui/package.json +++ b/tui/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@opencode-ai/plugin": "1.2.21", + "@opencode-ai/plugin": "1.3.2", "@opentui/core": "0.0.0-20260307-536c401b", "@opentui/solid": "0.0.0-20260307-536c401b", "solid-js": "1.9.9" From bb945060e8c86800699ffc2118b3e78d0539076c Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 25 Mar 2026 18:49:58 -0400 Subject: [PATCH 26/50] fix(tui): align plugin with snapshot tui api --- tui/shared/names.ts | 1 - tui/slots/sidebar-top.tsx | 2 - tui/types/opencode-plugin-tui.d.ts | 197 ++++++++++++++++++----------- 3 files changed, 126 insertions(+), 74 deletions(-) diff --git a/tui/shared/names.ts b/tui/shared/names.ts index abffd675..5e39b154 100644 --- a/tui/shared/names.ts +++ b/tui/shared/names.ts @@ -1,7 +1,6 @@ export const LABEL = "DCP" export const NAMES = { - slot: "dcp.sidebar", routes: { summary: "dcp.summary", }, diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-top.tsx index 3042f042..1adbbc91 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-top.tsx @@ -489,11 +489,9 @@ export const createSidebarTopSlot = ( } return { - id: names.slot, order: 90, slots: { sidebar_content: renderSidebar, - sidebar_top: renderSidebar, }, } } diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index ebf7befc..8daf59c5 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -5,12 +5,25 @@ declare module "@opencode-ai/plugin/tui" { LspStatus, McpStatus, Todo, + Message, + Part, + Provider, + PermissionRequest, + QuestionRequest, + SessionStatus, + Workspace, + Config as SdkConfig, } from "@opencode-ai/sdk/v2" - import type { CliRenderer, ParsedKey, Plugin as CorePlugin, SlotMode } from "@opentui/core" - import type { JSX } from "@opentui/solid" + + import type { CliRenderer, ParsedKey, RGBA } from "@opentui/core" + import type { JSX, SolidPlugin } from "@opentui/solid" import type { Plugin as ServerPlugin } from "@opencode-ai/plugin" - export type { CliRenderer, SlotMode } + // PluginOptions = Record — not yet exported by installed @opencode-ai/plugin + type PluginOptions = Record + + export type { CliRenderer } + export type { SlotMode } from "@opentui/core" export type TuiRouteCurrent = | { @@ -133,8 +146,64 @@ declare module "@opencode-ai/plugin/tui" { duration?: number } + export type TuiThemeCurrent = { + readonly primary: RGBA + readonly secondary: RGBA + readonly accent: RGBA + readonly error: RGBA + readonly warning: RGBA + readonly success: RGBA + readonly info: RGBA + readonly text: RGBA + readonly textMuted: RGBA + readonly selectedListItemText: RGBA + readonly background: RGBA + readonly backgroundPanel: RGBA + readonly backgroundElement: RGBA + readonly backgroundMenu: RGBA + readonly border: RGBA + readonly borderActive: RGBA + readonly borderSubtle: RGBA + readonly diffAdded: RGBA + readonly diffRemoved: RGBA + readonly diffContext: RGBA + readonly diffHunkHeader: RGBA + readonly diffHighlightAdded: RGBA + readonly diffHighlightRemoved: RGBA + readonly diffAddedBg: RGBA + readonly diffRemovedBg: RGBA + readonly diffContextBg: RGBA + readonly diffLineNumber: RGBA + readonly diffAddedLineNumberBg: RGBA + readonly diffRemovedLineNumberBg: RGBA + readonly markdownText: RGBA + readonly markdownHeading: RGBA + readonly markdownLink: RGBA + readonly markdownLinkText: RGBA + readonly markdownCode: RGBA + readonly markdownBlockQuote: RGBA + readonly markdownEmph: RGBA + readonly markdownStrong: RGBA + readonly markdownHorizontalRule: RGBA + readonly markdownListItem: RGBA + readonly markdownListEnumeration: RGBA + readonly markdownImage: RGBA + readonly markdownImageText: RGBA + readonly markdownCodeBlock: RGBA + readonly syntaxComment: RGBA + readonly syntaxKeyword: RGBA + readonly syntaxFunction: RGBA + readonly syntaxVariable: RGBA + readonly syntaxString: RGBA + readonly syntaxNumber: RGBA + readonly syntaxType: RGBA + readonly syntaxOperator: RGBA + readonly syntaxPunctuation: RGBA + readonly thinkingOpacity: number + } + export type TuiTheme = { - readonly current: Record + readonly current: TuiThemeCurrent readonly selected: string has: (name: string) => boolean set: (name: string) => boolean @@ -150,15 +219,52 @@ declare module "@opencode-ai/plugin/tui" { } export type TuiState = { + readonly ready: boolean + readonly config: SdkConfig + readonly provider: ReadonlyArray + readonly path: { + state: string + config: string + worktree: string + directory: string + } + readonly vcs: { branch?: string } | undefined + readonly workspace: { + list: () => ReadonlyArray + get: (workspaceID: string) => Workspace | undefined + } session: { + count: () => number diff: (sessionID: string) => ReadonlyArray todo: (sessionID: string) => ReadonlyArray + messages: (sessionID: string) => ReadonlyArray + status: (sessionID: string) => SessionStatus | undefined + permission: (sessionID: string) => ReadonlyArray + question: (sessionID: string) => ReadonlyArray } + part: (messageID: string) => ReadonlyArray lsp: () => ReadonlyArray mcp: () => ReadonlyArray } + // Inlined: Pick & NonNullable + // PluginConfig (opencode.json schema) is not re-exported by @opencode-ai/plugin at installed version. + type TuiConfigView = Record + + type Frozen = Value extends (...args: never[]) => unknown + ? Value + : Value extends ReadonlyArray + ? ReadonlyArray> + : Value extends object + ? { readonly [Key in keyof Value]: Frozen } + : Value + + export type TuiApp = { + readonly version: string + } + export type TuiApi = { + app: TuiApp command: { register: (cb: () => TuiCommand[]) => () => void trigger: (value: string) => void @@ -182,6 +288,7 @@ declare module "@opencode-ai/plugin/tui" { print: (key: string) => string create: (defaults: TuiKeybindMap, overrides?: Record) => TuiKeybindSet } + readonly tuiConfig: Frozen kv: TuiKV state: TuiState theme: TuiTheme @@ -206,77 +313,17 @@ declare module "@opencode-ai/plugin/tui" { export type TuiSlotMap = { app: {} home_logo: {} - home_tips: { - show_tips: boolean - tips_hidden: boolean - first_time_user: boolean - } - home_below_tips: { - show_tips: boolean - tips_hidden: boolean - first_time_user: boolean - } - sidebar_top: { - session_id: string - } - sidebar_content: { - session_id: string - } + home_bottom: {} sidebar_title: { session_id: string title: string share_url?: string } - sidebar_context: { - session_id: string - tokens: number - percentage: number | null - cost: number - } - sidebar_mcp: { - session_id: string - items: TuiSidebarMcpItem[] - connected: number - errors: number - } - sidebar_lsp: { - session_id: string - items: TuiSidebarLspItem[] - disabled: boolean - } - sidebar_todo: { - session_id: string - items: TuiSidebarTodoItem[] - } - sidebar_files: { - session_id: string - items: TuiSidebarFileItem[] - } - sidebar_getting_started: { - session_id: string - show_getting_started: boolean - has_providers: boolean - dismissed: boolean - } - sidebar_directory: { + sidebar_content: { session_id: string - directory: string - directory_parent: string - directory_name: string } - sidebar_version: { + sidebar_footer: { session_id: string - version: string - } - sidebar_bottom: { - session_id: string - directory: string - directory_parent: string - directory_name: string - version: string - show_getting_started: boolean - has_providers: boolean - dismissed: boolean } } @@ -284,10 +331,12 @@ declare module "@opencode-ai/plugin/tui" { theme: TuiTheme } - export type TuiSlotPlugin = CorePlugin + export type TuiSlotPlugin = Omit, "id"> & { + id?: never + } export type TuiSlots = { - register: (plugin: TuiSlotPlugin) => () => void + register: (plugin: TuiSlotPlugin) => string } export type TuiEventBus = { @@ -325,8 +374,15 @@ declare module "@opencode-ai/plugin/tui" { state: TuiPluginState } + export type TuiWorkspace = { + current: () => string | undefined + set: (workspaceID?: string) => void + } + export type TuiHostPluginApi = TuiApi & { client: ReturnType + scopedClient: (workspaceID?: string) => ReturnType + workspace: TuiWorkspace event: TuiEventBus renderer: Renderer } @@ -338,13 +394,12 @@ declare module "@opencode-ai/plugin/tui" { export type TuiPlugin = ( api: TuiPluginApi, - options: Record | undefined, + options: PluginOptions | undefined, meta: TuiPluginMeta, ) => Promise export type TuiPluginModule = { server?: ServerPlugin tui?: TuiPlugin - slots?: TuiSlotPlugin } } From 15964c7da8139b4c15e403f587a3b884e06f767d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Fri, 27 Mar 2026 15:32:30 -0400 Subject: [PATCH 27/50] refactor(tui): align plugin with flat TuiPluginApi and updated slot/route contracts --- index.ts | 3 + tui/data/context.ts | 10 +- tui/dcp-probe.tsx | 1 - tui/index.tsx | 7 +- tui/package.json | 2 +- tui/routes/summary.tsx | 23 ++-- .../{sidebar-top.tsx => sidebar-content.tsx} | 2 +- tui/types/opencode-plugin-tui.d.ts | 122 ++++++++++++------ 8 files changed, 109 insertions(+), 61 deletions(-) delete mode 100644 tui/dcp-probe.tsx rename tui/slots/{sidebar-top.tsx => sidebar-content.tsx} (99%) diff --git a/index.ts b/index.ts index b0124643..b6e9f5d5 100644 --- a/index.ts +++ b/index.ts @@ -19,6 +19,8 @@ import { } from "./lib/hooks" import { configureClientAuth, isSecureMode } from "./lib/auth" +const id = "opencode-dynamic-context-pruning" + let tuiPlugin: Record = {} try { tuiPlugin = (await import("./tui/index")).default @@ -139,6 +141,7 @@ const server: Plugin = (async (ctx) => { }) satisfies Plugin export default { + id, server, ...tuiPlugin, } diff --git a/tui/data/context.ts b/tui/data/context.ts index d258b75d..a82af9de 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -38,7 +38,7 @@ export const createPlaceholderContextSnapshot = ( function cleanBlockSummary(raw: string): string { return raw .replace(/^\s*\[Compressed conversation section\]\s*/i, "") - .replace(/\s*b\d+<\/dcp-message-id>\s*$/i, "") + .replace(/\s*b\d+<\/dcp-message-id>\s*$/i, "") .trim() } @@ -73,9 +73,11 @@ const loadContextSnapshot = async ( } const messagesResult = await client.session.messages({ sessionID }) - const messages = Array.isArray(messagesResult.data) - ? (messagesResult.data as WithParts[]) - : ([] as WithParts[]) + const rawMessages = + messagesResult && typeof messagesResult === "object" && "data" in messagesResult + ? messagesResult.data + : messagesResult + const messages = Array.isArray(rawMessages) ? (rawMessages as WithParts[]) : ([] as WithParts[]) const { state, persisted } = await buildState(sessionID, messages, logger) const [{ breakdown, messageStatuses }, aggregated] = await Promise.all([ diff --git a/tui/dcp-probe.tsx b/tui/dcp-probe.tsx deleted file mode 100644 index a84c7b34..00000000 --- a/tui/dcp-probe.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./index" diff --git a/tui/index.tsx b/tui/index.tsx index a159bc56..b277888d 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -2,7 +2,7 @@ import type { TuiPlugin } from "@opencode-ai/plugin/tui" import { getConfigForDirectory } from "../lib/config" import { Logger } from "../lib/logger" -import { createSidebarTopSlot } from "./slots/sidebar-top" +import { createSidebarContentSlot } from "./slots/sidebar-content" import { createSummaryRoute } from "./routes/summary" import { NAMES } from "./shared/names" @@ -22,10 +22,13 @@ const tui: TuiPlugin = async (api) => { api.route.register([createSummaryRoute(api)]) if (config.tui.sidebar) { - api.slots.register(createSidebarTopSlot(api, NAMES, logger)) + api.slots.register(createSidebarContentSlot(api, NAMES, logger)) } } +const id = "opencode-dynamic-context-pruning" + export default { + id, tui, } diff --git a/tui/package.json b/tui/package.json index e59f69fd..d435eaa1 100644 --- a/tui/package.json +++ b/tui/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "name": "dcp-tui-probe-local", + "name": "dcp-tui-local", "private": true, "type": "module", "dependencies": { diff --git a/tui/routes/summary.tsx b/tui/routes/summary.tsx index 8cd42fff..d3d01f8b 100644 --- a/tui/routes/summary.tsx +++ b/tui/routes/summary.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @opentui/solid */ import { createMemo, createSignal, For, Show } from "solid-js" import { useKeyboard } from "@opentui/solid" -import type { TuiApi } from "@opencode-ai/plugin/tui" +import type { TuiPluginApi } from "@opencode-ai/plugin/tui" import { getPalette, type DcpPalette } from "../shared/theme" import { LABEL, NAMES } from "../shared/names" @@ -23,6 +23,9 @@ interface ParsedSummary { sections: CollapsibleSection[] } +// These patterns must stay in sync with the exact headings produced by +// lib/compress (compactSummary output). If compression wording changes, +// update these patterns accordingly. const SECTION_HEADINGS: { pattern: RegExp; label: string }[] = [ { pattern: /\n*The following user messages were sent in this conversation verbatim:/, @@ -99,13 +102,9 @@ function CollapsibleSectionRow(props: { section: CollapsibleSection; palette: Dc ) } -function SummaryScreen(props: { api: TuiApi }) { - const params = createMemo(() => { - const current = props.api.route.current - return ("params" in current ? current.params : {}) as SummaryRouteParams - }) +function SummaryScreen(props: { api: TuiPluginApi; params: SummaryRouteParams }) { const palette = createMemo(() => getPalette(props.api.theme.current as Record)) - const parsed = createMemo(() => parseSummary(params().summary || "")) + const parsed = createMemo(() => parseSummary(props.params.summary || "")) const keys = props.api.keybind.create({ close: "escape" }) @@ -116,7 +115,7 @@ function SummaryScreen(props: { api: TuiApi }) { if (!matched) return evt.preventDefault() evt.stopPropagation() - const sessionID = params().sessionID + const sessionID = props.params.sessionID if (sessionID) { props.api.route.navigate("session", { sessionID }) } else { @@ -139,7 +138,7 @@ function SummaryScreen(props: { api: TuiApi }) { - {params().topic || "Compression Summary"} + {props.params.topic || "Compression Summary"} @@ -168,7 +167,9 @@ function SummaryScreen(props: { api: TuiApi }) { ) } -export const createSummaryRoute = (api: TuiApi) => ({ +export const createSummaryRoute = (api: TuiPluginApi) => ({ name: NAMES.routes.summary, - render: () => , + render: (input: { params?: Record }) => ( + + ), }) diff --git a/tui/slots/sidebar-top.tsx b/tui/slots/sidebar-content.tsx similarity index 99% rename from tui/slots/sidebar-top.tsx rename to tui/slots/sidebar-content.tsx index 1adbbc91..abde8045 100644 --- a/tui/slots/sidebar-top.tsx +++ b/tui/slots/sidebar-content.tsx @@ -467,7 +467,7 @@ const SidebarContext = (props: { ) } -export const createSidebarTopSlot = ( +export const createSidebarContentSlot = ( api: DcpTuiApi, names: DcpRouteNames, logger: Logger, diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index 8daf59c5..b4346e5f 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -81,7 +81,7 @@ declare module "@opencode-ai/plugin/tui" { } export type TuiDialogProps = { - size?: "medium" | "large" + size?: "medium" | "large" | "xlarge" onClose: () => void children?: JSX.Element } @@ -89,8 +89,8 @@ declare module "@opencode-ai/plugin/tui" { export type TuiDialogStack = { replace: (render: () => JSX.Element, onClose?: () => void) => void clear: () => void - setSize: (size: "medium" | "large") => void - readonly size: "medium" | "large" + setSize: (size: "medium" | "large" | "xlarge") => void + readonly size: "medium" | "large" | "xlarge" readonly depth: number readonly open: boolean } @@ -247,9 +247,17 @@ declare module "@opencode-ai/plugin/tui" { mcp: () => ReadonlyArray } - // Inlined: Pick & NonNullable + // Upstream: Pick & NonNullable & { plugin_enabled?: Record } // PluginConfig (opencode.json schema) is not re-exported by @opencode-ai/plugin at installed version. - type TuiConfigView = Record + // Approximation using known TUI-native config keys: + type TuiConfigView = { + $schema?: string + theme?: string + keybinds?: Record + plugin?: Record + plugin_enabled?: Record + [key: string]: unknown + } type Frozen = Value extends (...args: never[]) => unknown ? Value @@ -263,37 +271,6 @@ declare module "@opencode-ai/plugin/tui" { readonly version: string } - export type TuiApi = { - app: TuiApp - command: { - register: (cb: () => TuiCommand[]) => () => void - trigger: (value: string) => void - } - route: { - register: (routes: TuiRouteDefinition[]) => () => void - navigate: (name: string, params?: Record) => void - readonly current: TuiRouteCurrent - } - ui: { - Dialog: (props: TuiDialogProps) => JSX.Element - DialogAlert: (props: TuiDialogAlertProps) => JSX.Element - DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element - DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element - DialogSelect: (props: TuiDialogSelectProps) => JSX.Element - toast: (input: TuiToast) => void - dialog: TuiDialogStack - } - keybind: { - match: (key: string, evt: ParsedKey) => boolean - print: (key: string) => string - create: (defaults: TuiKeybindMap, overrides?: Record) => TuiKeybindSet - } - readonly tuiConfig: Frozen - kv: TuiKV - state: TuiState - theme: TuiTheme - } - export type TuiSidebarMcpItem = { name: string status: McpStatus["status"] @@ -356,7 +333,7 @@ declare module "@opencode-ai/plugin/tui" { export type TuiPluginState = "first" | "updated" | "same" export type TuiPluginEntry = { - name: string + id: string source: "file" | "npm" | "internal" spec: string target: string @@ -374,21 +351,84 @@ declare module "@opencode-ai/plugin/tui" { state: TuiPluginState } + export type TuiPluginStatus = { + id: string + source: "file" | "npm" | "internal" + spec: string + target: string + enabled: boolean + active: boolean + } + + export type TuiPluginInstallOptions = { + global?: boolean + } + + export type TuiPluginInstallResult = + | { + ok: true + dir: string + tui: boolean + } + | { + ok: false + message: string + missing?: boolean + } + export type TuiWorkspace = { current: () => string | undefined set: (workspaceID?: string) => void } - export type TuiHostPluginApi = TuiApi & { + // Flat TuiPluginApi matching upstream spec from PR #19347. + // Previous local shim split this into TuiApi -> TuiHostPluginApi -> TuiPluginApi; + // the upstream API is a single flat type. + export type TuiPluginApi = { + app: TuiApp + command: { + register: (cb: () => TuiCommand[]) => () => void + trigger: (value: string) => void + } + route: { + register: (routes: TuiRouteDefinition[]) => () => void + navigate: (name: string, params?: Record) => void + readonly current: TuiRouteCurrent + } + ui: { + Dialog: (props: TuiDialogProps) => JSX.Element + DialogAlert: (props: TuiDialogAlertProps) => JSX.Element + DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element + DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element + DialogSelect: (props: TuiDialogSelectProps) => JSX.Element + toast: (input: TuiToast) => void + dialog: TuiDialogStack + } + keybind: { + match: (key: string, evt: ParsedKey) => boolean + print: (key: string) => string + create: (defaults: TuiKeybindMap, overrides?: Record) => TuiKeybindSet + } + readonly tuiConfig: Frozen + kv: TuiKV + state: TuiState + theme: TuiTheme client: ReturnType scopedClient: (workspaceID?: string) => ReturnType workspace: TuiWorkspace event: TuiEventBus renderer: Renderer - } - - export type TuiPluginApi = TuiHostPluginApi & { slots: TuiSlots + plugins: { + list: () => ReadonlyArray + activate: (id: string) => Promise + deactivate: (id: string) => Promise + add: (spec: string) => Promise + install: ( + spec: string, + options?: TuiPluginInstallOptions, + ) => Promise + } lifecycle: TuiLifecycle } From 187194ffdb4fd33f9ea9548e66cbd9e1965ac64b Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Fri, 27 Mar 2026 16:37:18 -0400 Subject: [PATCH 28/50] fix(ui): revert compress notification to match dev --- lib/compress/pipeline.ts | 12 +++++------- lib/ui/notification.ts | 1 - lib/ui/utils.ts | 5 +---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/compress/pipeline.ts b/lib/compress/pipeline.ts index 390932cc..32912066 100644 --- a/lib/compress/pipeline.ts +++ b/lib/compress/pipeline.ts @@ -4,7 +4,7 @@ import { saveSessionState } from "../state/persistence" import { assignMessageRefs } from "../message-ids" import { isIgnoredUserMessage } from "../messages/query" import { deduplicate, purgeErrors } from "../strategies" -import { getCurrentParams, getCurrentTokenUsage } from "../token-utils" +import { getCurrentParams } from "../token-utils" import { sendCompressNotification } from "../ui/notification" import type { ToolContext } from "./types" import { buildSearchContext, fetchSessionMessages } from "./search" @@ -88,10 +88,9 @@ export async function finalizeSession( await saveSessionState(ctx.state, ctx.logger) const params = getCurrentParams(ctx.state, rawMessages, ctx.logger) - const totalSessionTokens = getCurrentTokenUsage(ctx.state, rawMessages) - const sessionMessages = rawMessages.filter( - (msg) => !(msg.info.role === "user" && isIgnoredUserMessage(msg)), - ) + const sessionMessageIds = rawMessages + .filter((msg) => !isIgnoredUserMessage(msg)) + .map((msg) => msg.info.id) await sendCompressNotification( ctx.client, @@ -101,8 +100,7 @@ export async function finalizeSession( toolCtx.sessionID, entries, batchTopic, - totalSessionTokens, - sessionMessages, + sessionMessageIds, params, ) } diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts index e65c070c..e4303a58 100644 --- a/lib/ui/notification.ts +++ b/lib/ui/notification.ts @@ -249,7 +249,6 @@ export async function sendCompressNotification( message = `${notificationHeader} — ${compressionLabel}` } else { message = notificationHeader - const activePrunedMessages = new Map() for (const [messageId, entry] of state.prune.messages.byMessageId) { if (entry.activeBlockIds.length > 0) { diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts index 9cd92ef1..286ff967 100644 --- a/lib/ui/utils.ts +++ b/lib/ui/utils.ts @@ -144,10 +144,7 @@ export function formatStatsHeader(totalTokensSaved: number, pruneTokenCounter: n export function formatTokenCount(tokens: number, compact?: boolean): string { const suffix = compact ? "" : " tokens" - if (tokens >= 100_000) { - return `${Math.round(tokens / 1000)}K` + suffix - } - if (tokens >= 10_000 || (compact && tokens >= 1000)) { + if (tokens >= 1000) { return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + suffix } return tokens.toString() + suffix From abe10e4476693e29db324fa57737456ddfe3864c Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sat, 28 Mar 2026 23:10:53 -0400 Subject: [PATCH 29/50] fix(tui): restore dedicated tui entrypoint --- index.ts | 6 ------ package-lock.json | 21 +++++++++++++++------ package.json | 25 ++++++++++++++++++++++++- tui/index.tsx | 2 +- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/index.ts b/index.ts index b6e9f5d5..a7f073f0 100644 --- a/index.ts +++ b/index.ts @@ -21,11 +21,6 @@ import { configureClientAuth, isSecureMode } from "./lib/auth" const id = "opencode-dynamic-context-pruning" -let tuiPlugin: Record = {} -try { - tuiPlugin = (await import("./tui/index")).default -} catch {} - const server: Plugin = (async (ctx) => { const config = getConfig(ctx) @@ -143,5 +138,4 @@ const server: Plugin = (async (ctx) => { export default { id, server, - ...tuiPlugin, } diff --git a/package-lock.json b/package-lock.json index 416042e6..dea61508 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,13 +21,26 @@ "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", + "solid-js": "1.9.9", "tsx": "^4.21.0", "typescript": "^6.0.2" }, "peerDependencies": { "@opencode-ai/plugin": ">=1.2.0", "@opentui/core": ">=0.1.87", - "@opentui/solid": ">=0.0.0-20260307" + "@opentui/solid": ">=0.0.0-20260307", + "solid-js": ">=1.9.9 <2" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + }, + "solid-js": { + "optional": true + } } }, "node_modules/@ampproject/remapping": { @@ -2116,8 +2129,7 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", @@ -3032,7 +3044,6 @@ "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -3043,7 +3054,6 @@ "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3073,7 +3083,6 @@ "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", diff --git a/package.json b/package.json index fa04aae1..ac80f62e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,16 @@ "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./tui": { + "types": "./dist/tui/index.d.ts", + "import": "./dist/tui/index.js" + } + }, "scripts": { "clean": "rm -rf dist", "build": "npm run clean && tsc", @@ -42,7 +52,19 @@ "peerDependencies": { "@opencode-ai/plugin": ">=1.2.0", "@opentui/core": ">=0.1.87", - "@opentui/solid": ">=0.0.0-20260307" + "@opentui/solid": ">=0.0.0-20260307", + "solid-js": ">=1.9.9 <2" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + }, + "solid-js": { + "optional": true + } }, "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", @@ -57,6 +79,7 @@ "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", + "solid-js": "1.9.9", "tsx": "^4.21.0", "typescript": "^6.0.2" }, diff --git a/tui/index.tsx b/tui/index.tsx index b277888d..596a72b1 100644 --- a/tui/index.tsx +++ b/tui/index.tsx @@ -7,7 +7,7 @@ import { createSummaryRoute } from "./routes/summary" import { NAMES } from "./shared/names" const tui: TuiPlugin = async (api) => { - const config = getConfigForDirectory(process.cwd(), (title, message) => { + const config = getConfigForDirectory(api.state.path.directory, (title, message) => { api.ui.toast({ title, message, From ec32a560fcd16d9229fc5b2e17136534f83fc885 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sat, 28 Mar 2026 23:10:53 -0400 Subject: [PATCH 30/50] fix(types): refresh vendored tui plugin shim --- tui/types/opencode-plugin-tui.d.ts | 46 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/tui/types/opencode-plugin-tui.d.ts b/tui/types/opencode-plugin-tui.d.ts index b4346e5f..9c43070e 100644 --- a/tui/types/opencode-plugin-tui.d.ts +++ b/tui/types/opencode-plugin-tui.d.ts @@ -17,7 +17,6 @@ declare module "@opencode-ai/plugin/tui" { import type { CliRenderer, ParsedKey, RGBA } from "@opentui/core" import type { JSX, SolidPlugin } from "@opentui/solid" - import type { Plugin as ServerPlugin } from "@opencode-ai/plugin" // PluginOptions = Record — not yet exported by installed @opencode-ai/plugin type PluginOptions = Record @@ -113,6 +112,8 @@ declare module "@opencode-ai/plugin/tui" { description?: () => JSX.Element placeholder?: string value?: string + busy?: boolean + busyText?: string onConfirm?: (value: string) => void onCancel?: () => void } @@ -139,6 +140,19 @@ declare module "@opencode-ai/plugin/tui" { current?: Value } + export type TuiPromptProps = { + workspaceID?: string + visible?: boolean + disabled?: boolean + onSubmit?: () => void + hint?: JSX.Element + showPlaceholder?: boolean + placeholders?: { + normal?: string[] + shell?: string[] + } + } + export type TuiToast = { variant?: "info" | "success" | "warning" | "error" title?: string @@ -247,14 +261,13 @@ declare module "@opencode-ai/plugin/tui" { mcp: () => ReadonlyArray } - // Upstream: Pick & NonNullable & { plugin_enabled?: Record } - // PluginConfig (opencode.json schema) is not re-exported by @opencode-ai/plugin at installed version. - // Approximation using known TUI-native config keys: + // Current upstream shape is based on the host TUI config plus plugin tuples. + // PluginConfig is not re-exported by the installed @opencode-ai/plugin version. type TuiConfigView = { $schema?: string theme?: string keybinds?: Record - plugin?: Record + plugin?: Array plugin_enabled?: Record [key: string]: unknown } @@ -290,6 +303,9 @@ declare module "@opencode-ai/plugin/tui" { export type TuiSlotMap = { app: {} home_logo: {} + home_prompt: { + workspace_id?: string + } home_bottom: {} sidebar_title: { session_id: string @@ -381,10 +397,8 @@ declare module "@opencode-ai/plugin/tui" { set: (workspaceID?: string) => void } - // Flat TuiPluginApi matching upstream spec from PR #19347. - // Previous local shim split this into TuiApi -> TuiHostPluginApi -> TuiPluginApi; - // the upstream API is a single flat type. - export type TuiPluginApi = { + // Flat TuiPluginApi matching the current upstream TUI plugin API. + export type TuiPluginApi = { app: TuiApp command: { register: (cb: () => TuiCommand[]) => () => void @@ -401,6 +415,7 @@ declare module "@opencode-ai/plugin/tui" { DialogConfirm: (props: TuiDialogConfirmProps) => JSX.Element DialogPrompt: (props: TuiDialogPromptProps) => JSX.Element DialogSelect: (props: TuiDialogSelectProps) => JSX.Element + Prompt: (props: TuiPromptProps) => JSX.Element toast: (input: TuiToast) => void dialog: TuiDialogStack } @@ -417,7 +432,7 @@ declare module "@opencode-ai/plugin/tui" { scopedClient: (workspaceID?: string) => ReturnType workspace: TuiWorkspace event: TuiEventBus - renderer: Renderer + renderer: CliRenderer slots: TuiSlots plugins: { list: () => ReadonlyArray @@ -432,14 +447,15 @@ declare module "@opencode-ai/plugin/tui" { lifecycle: TuiLifecycle } - export type TuiPlugin = ( - api: TuiPluginApi, + export type TuiPlugin = ( + api: TuiPluginApi, options: PluginOptions | undefined, meta: TuiPluginMeta, ) => Promise - export type TuiPluginModule = { - server?: ServerPlugin - tui?: TuiPlugin + export type TuiPluginModule = { + id?: string + tui: TuiPlugin + server?: never } } From 7412802cf28ad1ec376e0e1514d927502ac59e23 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 00:02:01 -0400 Subject: [PATCH 31/50] feat: add plugin install targets --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index ac80f62e..9023ec91 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "import": "./dist/tui/index.js" } }, + "oc-plugin": [ + "server", + "tui" + ], "scripts": { "clean": "rm -rf dist", "build": "npm run clean && tsc", From d0c0dff2e2d9b2845438d1d6cfb694131206d2d7 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 01:37:37 -0400 Subject: [PATCH 32/50] fix(tui): show summary token totals --- tui/data/context.ts | 5 ++++- tui/shared/types.ts | 1 + tui/slots/sidebar-content.tsx | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tui/data/context.ts b/tui/data/context.ts index a82af9de..2dec84a4 100644 --- a/tui/data/context.ts +++ b/tui/data/context.ts @@ -7,6 +7,7 @@ import { } from "../../lib/state" import { findLastCompactionTimestamp, + getActiveSummaryTokenUsage, loadPruneMap, loadPruneMessagesState, } from "../../lib/state/utils" @@ -24,6 +25,7 @@ export const createPlaceholderContextSnapshot = ( ): DcpContextSnapshot => ({ sessionID, breakdown: emptyBreakdown(), + activeSummaryTokens: 0, persisted: { available: false, activeBlockCount: 0, @@ -38,7 +40,7 @@ export const createPlaceholderContextSnapshot = ( function cleanBlockSummary(raw: string): string { return raw .replace(/^\s*\[Compressed conversation section\]\s*/i, "") - .replace(/\s*b\d+<\/dcp-message-id>\s*$/i, "") + .replace(/(?:\r?\n)*b\d+<\/dcp-message-id>\s*$/i, "") .trim() } @@ -103,6 +105,7 @@ const loadContextSnapshot = async ( return { sessionID, breakdown, + activeSummaryTokens: getActiveSummaryTokenUsage(state), persisted: { available: !!persisted, activeBlockCount: state.prune.messages.activeBlockIds.size, diff --git a/tui/shared/types.ts b/tui/shared/types.ts index f1128fc4..36db52df 100644 --- a/tui/shared/types.ts +++ b/tui/shared/types.ts @@ -29,6 +29,7 @@ export interface DcpAllTimeStats { export interface DcpContextSnapshot { sessionID?: string breakdown: DcpContextBreakdown + activeSummaryTokens: number persisted: DcpPersistedSummary messageStatuses: DcpMessageStatus[] allTimeStats: DcpAllTimeStats diff --git a/tui/slots/sidebar-content.tsx b/tui/slots/sidebar-content.tsx index abde8045..7d39429b 100644 --- a/tui/slots/sidebar-content.tsx +++ b/tui/slots/sidebar-content.tsx @@ -395,9 +395,14 @@ const SidebarContext = (props: { {blocks().length > 0 ? ( <> - - Compressed Topics - + + + Compressed Summaries + + + {`~${compactTokenCount(snapshot().activeSummaryTokens)}`} + + {blocks().map((block) => ( From 88afd186eee34934d7bf0183188e8da4e184bb4a Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 01:37:37 -0400 Subject: [PATCH 33/50] docs: add tui plugin install config --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 81d31790..3831afff 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,26 @@ opencode plugin @tarquinen/opencode-dcp@latest --global This installs the package and adds it to your global OpenCode config. +Or add it to your OpenCode configs manually: + +```jsonc +// opencode.jsonc +{ + "plugin": ["@tarquinen/opencode-dcp@latest"], +} +``` + +```jsonc +// tui.jsonc +{ + "plugin": ["@tarquinen/opencode-dcp@latest"], +} +``` + +Using `@latest` ensures you always get the newest version automatically when OpenCode starts. + +Restart OpenCode. The plugin will automatically start optimizing your sessions. + ## How It Works DCP reduces context size through a compress tool and automatic cleanup. Your session history is never modified — DCP replaces pruned content with placeholders before sending requests to your LLM. From 33d1e59fde4b00dcfd2444c577e7fdeea7d61a5f Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 03:56:47 -0400 Subject: [PATCH 34/50] v3.2.0-beta0 - Bump version --- README.md | 6 +++--- package-lock.json | 4 ++-- package.json | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3831afff..6d5bac30 100644 --- a/README.md +++ b/README.md @@ -22,18 +22,18 @@ Or add it to your OpenCode configs manually: ```jsonc // opencode.jsonc { - "plugin": ["@tarquinen/opencode-dcp@latest"], + "plugin": ["@tarquinen/opencode-dcp@beta"], } ``` ```jsonc // tui.jsonc { - "plugin": ["@tarquinen/opencode-dcp@latest"], + "plugin": ["@tarquinen/opencode-dcp@beta"], } ``` -Using `@latest` ensures you always get the newest version automatically when OpenCode starts. +Using `@beta` keeps you on the current prerelease channel when OpenCode starts. Restart OpenCode. The plugin will automatically start optimizing your sessions. diff --git a/package-lock.json b/package-lock.json index dea61508..d2176383 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.1.6", + "version": "3.2.0-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.1.6", + "version": "3.2.0-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index 9023ec91..d55999b8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.1.6", + "version": "3.2.0-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -23,9 +23,7 @@ "scripts": { "clean": "rm -rf dist", "build": "npm run clean && tsc", - "verify:package": "node scripts/verify-package.mjs", - "check:package": "npm run build && npm run verify:package", - "prepublishOnly": "npm run check:package", + "prepublishOnly": "npm run build", "dev": "opencode plugin dev", "typecheck": "tsc --noEmit", "tui:link-host-runtime": "node scripts/link-tui-host-runtime.mjs", From c3e63649b413d82c7b0e96300295d4ab86ebe6e8 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 04:17:30 -0400 Subject: [PATCH 35/50] v3.2.1-beta0 - Fix TUI runtime deps --- package-lock.json | 225 ++-------------------------------------------- package.json | 24 ++--- 2 files changed, 12 insertions(+), 237 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2176383..09d16ff2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,53 +1,38 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.0-beta0", + "version": "3.2.1-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.0-beta0", + "version": "3.2.1-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", + "solid-js": "1.9.9", "zod": "^4.3.6" }, "devDependencies": { "@opencode-ai/plugin": "^1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", - "solid-js": "1.9.9", "tsx": "^4.21.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@opencode-ai/plugin": ">=1.2.0", - "@opentui/core": ">=0.1.87", - "@opentui/solid": ">=0.0.0-20260307", - "solid-js": ">=1.9.9 <2" - }, - "peerDependenciesMeta": { - "@opentui/core": { - "optional": true - }, - "@opentui/solid": { - "optional": true - }, - "solid-js": { - "optional": true - } + "@opencode-ai/plugin": ">=1.2.0" } }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -86,7 +71,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -101,7 +85,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -111,7 +94,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -142,7 +124,6 @@ "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", @@ -159,7 +140,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -172,7 +152,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", @@ -189,7 +168,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -211,7 +189,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -221,7 +198,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.5", @@ -235,7 +211,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", @@ -249,7 +224,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -267,7 +241,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" @@ -280,7 +253,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -290,7 +262,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -308,7 +279,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -322,7 +292,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -332,7 +301,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -342,7 +310,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -352,7 +319,6 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", @@ -366,7 +332,6 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -382,7 +347,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -398,7 +362,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -414,7 +377,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -431,7 +393,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -451,7 +412,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -471,7 +431,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -486,7 +445,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -505,7 +463,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -519,7 +476,6 @@ "version": "0.17.3", "resolved": "https://registry.npmjs.org/@dimforge/rapier2d-simd-compat/-/rapier2d-simd-compat-0.17.3.tgz", "integrity": "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==", - "dev": true, "license": "Apache-2.0", "optional": true }, @@ -969,7 +925,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/file-ops": "1.6.0", @@ -988,7 +943,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/plugin-resize": "1.6.0", @@ -1004,7 +958,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1014,7 +967,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1030,7 +982,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1046,7 +997,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1061,7 +1011,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1076,7 +1025,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1091,7 +1039,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1106,7 +1053,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1116,7 +1062,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1130,7 +1075,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1144,7 +1088,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1154,7 +1097,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1171,7 +1113,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1181,7 +1122,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1199,7 +1139,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1209,7 +1148,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1226,7 +1164,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1236,7 +1173,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1252,7 +1188,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1262,7 +1197,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1277,7 +1211,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1287,7 +1220,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0" @@ -1300,7 +1232,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1315,7 +1246,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1325,7 +1255,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1339,7 +1268,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1349,7 +1277,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1371,7 +1298,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1385,7 +1311,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1395,7 +1320,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1417,7 +1341,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1427,7 +1350,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", - "dev": true, "license": "MIT", "dependencies": { "image-q": "^4.0.0", @@ -1441,7 +1363,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1451,7 +1372,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1466,7 +1386,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1476,7 +1395,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1494,7 +1412,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1504,7 +1421,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -1522,7 +1438,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1532,7 +1447,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", - "dev": true, "license": "MIT", "dependencies": { "zod": "^3.23.8" @@ -1545,7 +1459,6 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1555,7 +1468,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/types": "1.6.0", @@ -1569,7 +1481,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1580,7 +1491,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1590,14 +1500,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1635,7 +1543,6 @@ "version": "0.0.0-20260307-536c401b", "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.0.0-20260307-536c401b.tgz", "integrity": "sha512-e/n7hCtpOzS57X9llODu0SUXCQBWSxHQeTA0iuL7j0nhSFgM6KpL8kJ7VQBU1EEn33pytA0udbfKSJ6sqWmEJg==", - "dev": true, "license": "MIT", "dependencies": { "bun-ffi-structs": "0.1.2", @@ -1667,7 +1574,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1681,7 +1587,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1695,7 +1600,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1709,7 +1613,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1723,7 +1626,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1737,7 +1639,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1748,7 +1649,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/bun-ffi-structs/-/bun-ffi-structs-0.1.2.tgz", "integrity": "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w==", - "dev": true, "license": "MIT", "peerDependencies": { "typescript": "^5" @@ -1758,7 +1658,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -1773,7 +1672,6 @@ "version": "0.0.0-20260307-536c401b", "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.0.0-20260307-536c401b.tgz", "integrity": "sha512-wfItFCVBsP2iWvFgj2/lVRN7/O7R5eu9NReY4Wl34Z+c9d7P6FVSa1xOriziTPysukW1OhFe8MNN7MIaggYdHg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "7.28.0", @@ -1792,7 +1690,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -1809,7 +1706,6 @@ "version": "0.1.69", "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", - "dev": true, "license": "BSD-3-Clause", "optional": true }, @@ -1817,7 +1713,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -1830,14 +1725,12 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "dev": true, "license": "MIT" }, "node_modules/await-to-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1847,7 +1740,6 @@ "version": "0.40.6", "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.6.tgz", "integrity": "sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "7.18.6", @@ -1864,7 +1756,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" @@ -1877,7 +1768,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", - "dev": true, "license": "MIT", "dependencies": { "find-babel-config": "^2.1.1", @@ -1891,7 +1781,6 @@ "version": "1.9.9", "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", - "dev": true, "license": "MIT", "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" @@ -1910,14 +1799,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -1938,7 +1825,6 @@ "version": "2.10.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz", "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -1951,14 +1837,12 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", - "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1968,7 +1852,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2002,7 +1885,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -2027,7 +1909,6 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/bun-webgpu/-/bun-webgpu-0.1.5.tgz", "integrity": "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA==", - "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2047,7 +1928,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2061,7 +1941,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2075,7 +1954,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2089,7 +1967,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -2100,7 +1977,6 @@ "version": "1.0.30001781", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2121,21 +1997,18 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2153,7 +2026,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2163,14 +2035,12 @@ "version": "1.5.328", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", - "dev": true, "license": "ISC" }, "node_modules/entities": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -2225,7 +2095,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2235,7 +2104,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2245,7 +2113,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -2254,14 +2121,12 @@ "node_modules/exif-parser": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", - "dev": true + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" }, "node_modules/file-type": { "version": "16.5.4", "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dev": true, "license": "MIT", "dependencies": { "readable-web-to-node-stream": "^3.0.0", @@ -2279,7 +2144,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", - "dev": true, "license": "MIT", "dependencies": { "json5": "^2.2.3" @@ -2289,7 +2153,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^3.0.0" @@ -2302,7 +2165,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -2324,7 +2186,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2345,7 +2206,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2368,7 +2228,6 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", - "dev": true, "license": "MIT", "dependencies": { "image-q": "^4.0.0", @@ -2380,7 +2239,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -2399,7 +2257,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2418,14 +2275,12 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true, "license": "MIT" }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -2446,7 +2301,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "16.9.1" @@ -2456,14 +2310,12 @@ "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", - "dev": true, "license": "MIT" }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -2479,7 +2331,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", - "dev": true, "license": "MIT", "dependencies": { "@jimp/core": "1.6.0", @@ -2518,21 +2369,18 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -2545,7 +2393,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -2564,7 +2411,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^3.0.0", @@ -2584,7 +2430,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -2594,7 +2439,6 @@ "version": "17.0.1", "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", - "dev": true, "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2607,7 +2451,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true, "license": "MIT", "bin": { "mime": "cli.js" @@ -2620,7 +2463,6 @@ "version": "8.0.7", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -2636,7 +2478,6 @@ "version": "4.2.8", "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -2646,28 +2487,24 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "dev": true, "license": "MIT" }, "node_modules/omggif": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", - "dev": true, "license": "MIT" }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -2683,7 +2520,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.0.0" @@ -2696,7 +2532,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2706,28 +2541,24 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/parse-bmfont-ascii": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", - "dev": true, "license": "MIT" }, "node_modules/parse-bmfont-binary": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", - "dev": true, "license": "MIT" }, "node_modules/parse-bmfont-xml": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", - "dev": true, "license": "MIT", "dependencies": { "xml-parse-from-string": "^1.0.0", @@ -2738,7 +2569,6 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -2751,7 +2581,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -2764,7 +2593,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -2774,14 +2602,12 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -2798,14 +2624,12 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/path-scurry/node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" @@ -2815,7 +2639,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2829,14 +2652,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/pixelmatch": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", - "dev": true, "license": "ISC", "dependencies": { "pngjs": "^6.0.0" @@ -2849,7 +2670,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.13.0" @@ -2859,7 +2679,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, "license": "MIT", "dependencies": { "find-up": "^3.0.0" @@ -2872,7 +2691,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/planck/-/planck-1.4.3.tgz", "integrity": "sha512-B+lHKhRSeg7vZOfEyEzyQVu7nx8JHcX3QgnAcHXrPW0j04XYKX5eXSiUrxH2Z5QR8OoqvjD6zKIaPMdMYAd0uA==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -2886,7 +2704,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "dev": true, "license": "MIT", "engines": { "node": ">=14.19.0" @@ -2912,7 +2729,6 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -2922,7 +2738,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -2939,7 +2754,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", - "dev": true, "license": "MIT", "dependencies": { "readable-stream": "^4.7.0" @@ -2956,14 +2770,12 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", - "dev": true, "license": "MIT" }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -2994,14 +2806,12 @@ "version": "0.4.9", "resolved": "https://registry.npmjs.org/s-js/-/s-js-0.4.9.tgz", "integrity": "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ==", - "dev": true, "license": "MIT" }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -3022,7 +2832,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -3032,7 +2841,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3042,7 +2850,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3052,7 +2859,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz", "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3071,7 +2877,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.4.tgz", "integrity": "sha512-3MY16e0ocMHL7N1ufpdObURGyX+lCo0T/A+y6VCwosLdH1HSda4QZl1Sdt/O+2qWp48WFi26XEp5rF0LoaL0Dg==", - "dev": true, "license": "MIT", "engines": { "node": ">=20.12.2" @@ -3081,7 +2886,6 @@ "version": "1.9.9", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.1.0", @@ -3093,7 +2897,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/stage-js/-/stage-js-1.0.1.tgz", "integrity": "sha512-cz14aPp/wY0s3bkb/B93BPP5ZAEhgBbRmAT3CCDqert8eCAqIpQ0RB2zpK8Ksxf+Pisl5oTzvPHtL4CVzzeHcw==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -3105,7 +2908,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -3115,7 +2917,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "dev": true, "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", @@ -3133,7 +2934,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3146,7 +2946,6 @@ "version": "0.177.0", "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", "integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==", - "dev": true, "license": "MIT", "optional": true }, @@ -3160,14 +2959,12 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "dev": true, "license": "MIT" }, "node_modules/token-types": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "dev": true, "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", @@ -3226,7 +3023,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, "funding": [ { "type": "opencollective", @@ -3257,7 +3053,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", - "dev": true, "license": "MIT", "dependencies": { "pako": "^1.0.11" @@ -3267,7 +3062,6 @@ "version": "0.25.10", "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.25.10.tgz", "integrity": "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA==", - "dev": true, "license": "MIT", "peer": true, "peerDependencies": { @@ -3283,14 +3077,12 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", - "dev": true, "license": "MIT" }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, "license": "MIT", "dependencies": { "sax": ">=0.6.0", @@ -3304,7 +3096,6 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4.0" @@ -3314,14 +3105,12 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, "license": "ISC" }, "node_modules/yoga-layout": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", - "dev": true, "license": "MIT" }, "node_modules/zod": { diff --git a/package.json b/package.json index d55999b8..657771ba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.0-beta0", + "version": "3.2.1-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -52,36 +52,22 @@ "author": "tarquinen", "license": "AGPL-3.0-or-later", "peerDependencies": { - "@opencode-ai/plugin": ">=1.2.0", - "@opentui/core": ">=0.1.87", - "@opentui/solid": ">=0.0.0-20260307", - "solid-js": ">=1.9.9 <2" - }, - "peerDependenciesMeta": { - "@opentui/core": { - "optional": true - }, - "@opentui/solid": { - "optional": true - }, - "solid-js": { - "optional": true - } + "@opencode-ai/plugin": ">=1.2.0" }, "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", + "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/solid": "0.0.0-20260307-536c401b", "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", + "solid-js": "1.9.9", "zod": "^4.3.6" }, "devDependencies": { "@opencode-ai/plugin": "^1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", "@types/node": "^25.5.0", "prettier": "^3.8.1", - "solid-js": "1.9.9", "tsx": "^4.21.0", "typescript": "^6.0.2" }, From 9ea92b2eeb3862ef57d4e272112e5acd7cffe119 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 12:51:46 -0400 Subject: [PATCH 36/50] v3.2.2-beta0 - Fix npm TUI source entry --- package-lock.json | 4 ++-- package.json | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09d16ff2..762f5712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.1-beta0", + "version": "3.2.2-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.1-beta0", + "version": "3.2.2-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index 657771ba..ba59ed2b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.1-beta0", + "version": "3.2.2-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -13,7 +13,7 @@ }, "./tui": { "types": "./dist/tui/index.d.ts", - "import": "./dist/tui/index.js" + "import": "./tui/index.tsx" } }, "oc-plugin": [ @@ -73,6 +73,17 @@ }, "files": [ "dist/", + "lib/analysis/", + "lib/state/", + "lib/config.ts", + "lib/logger.ts", + "lib/messages/query.ts", + "lib/token-utils.ts", + "tui/data/", + "tui/routes/", + "tui/shared/", + "tui/slots/", + "tui/index.tsx", "README.md", "LICENSE" ] From b15aa2f7b1000bcc026ec13ab5acc3cf5803ea71 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 13:36:10 -0400 Subject: [PATCH 37/50] chore: add package directories metadata --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ba59ed2b..101edbc0 100644 --- a/package.json +++ b/package.json @@ -86,5 +86,10 @@ "tui/index.tsx", "README.md", "LICENSE" - ] + ], + "directories": { + "doc": "docs", + "lib": "lib", + "test": "tests" + } } From 7d3067dec0384ccdb8f1d575b5a3b3ba28c02bbe Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 13:55:37 -0400 Subject: [PATCH 38/50] fix(tui): bump opentui deps for audit --- package-lock.json | 104 ++++++++++++++++++++-------------------- package.json | 6 +-- tui/package-lock.json | 108 +++++++++++++++++++++--------------------- tui/package.json | 6 +-- 4 files changed, 112 insertions(+), 112 deletions(-) diff --git a/package-lock.json b/package-lock.json index 762f5712..1f66824a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,11 @@ "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", + "@opentui/core": "0.1.92", + "@opentui/solid": "0.1.92", "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", - "solid-js": "1.9.9", + "solid-js": "1.9.11", "zod": "^4.3.6" }, "devDependencies": { @@ -1540,9 +1540,9 @@ "license": "MIT" }, "node_modules/@opentui/core": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-e/n7hCtpOzS57X9llODu0SUXCQBWSxHQeTA0iuL7j0nhSFgM6KpL8kJ7VQBU1EEn33pytA0udbfKSJ6sqWmEJg==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.1.92.tgz", + "integrity": "sha512-c+KdYAIH3M8n24RYaor+t7AQtKZ3l84L7xdP7DEaN4xtuYH8W08E6Gi+wUal4g+HSai3HS9irox68yFf0VPAxw==", "license": "MIT", "dependencies": { "bun-ffi-structs": "0.1.2", @@ -1553,12 +1553,12 @@ }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", - "@opentui/core-darwin-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-darwin-x64": "0.0.0-20260307-536c401b", - "@opentui/core-linux-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-linux-x64": "0.0.0-20260307-536c401b", - "@opentui/core-win32-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-win32-x64": "0.0.0-20260307-536c401b", + "@opentui/core-darwin-arm64": "0.1.92", + "@opentui/core-darwin-x64": "0.1.92", + "@opentui/core-linux-arm64": "0.1.92", + "@opentui/core-linux-x64": "0.1.92", + "@opentui/core-win32-arm64": "0.1.92", + "@opentui/core-win32-x64": "0.1.92", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" @@ -1568,9 +1568,9 @@ } }, "node_modules/@opentui/core-darwin-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-y46MUgcjkIqC/IBxErchM51KmLARxudrKqr09Gyy25ry+GUE8gzaEIx6EeMAUnWDWvetMacKgEaNCjtdkfGgDQ==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.1.92.tgz", + "integrity": "sha512-NX/qFRuc7My0pazyOrw9fdTXmU7omXcZzQuHcsaVnwssljaT52UYMrJ7mCKhSo69RhHw0lnGCymTorvz3XBdsA==", "cpu": [ "arm64" ], @@ -1581,9 +1581,9 @@ ] }, "node_modules/@opentui/core-darwin-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-USf14JkFaCyKvn9FfLn6AZv14o5ED7uHBNq4kCmggD28HmqHsklDhGNyDnswUggCworJ6xz7jICZTiKKrSwRbQ==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.1.92.tgz", + "integrity": "sha512-Zb4jn33hOf167llINKLniOabQIycs14LPOBZnQ6l4khbeeTPVJdG8gy9PhlAyIQygDKmRTFncVlP0RP+L6C7og==", "cpu": [ "x64" ], @@ -1594,9 +1594,9 @@ ] }, "node_modules/@opentui/core-linux-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-fzNf0Mv7OjNktJFg17WsvdDD5Ej12eSwPVMProlQFbklC8qCEsZfLJKYq9ExYLRoxHX7wFm9Eq6L7hVaGcn2sA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.1.92.tgz", + "integrity": "sha512-4VA1A91OTMPJ3LkAyaxKEZVJsk5jIc3Kz0gV2vip8p2aGLPpYHHpkFZpXP/FyzsnJzoSGftBeA6ya1GKa5bkXg==", "cpu": [ "arm64" ], @@ -1607,9 +1607,9 @@ ] }, "node_modules/@opentui/core-linux-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-+80TgK5ZhdJvM2+fiCbeCJvXk9De3oNB42wcCtGcwt3x1wyPYAbWIetw6dIGqXIbica/El+7+6Y2DMV06PUUug==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.1.92.tgz", + "integrity": "sha512-tr7va8hfKS1uY+TBmulQBoBlwijzJk56K/U/L9/tbHfW7oJctqxPVwEFHIh1HDcOQ3/UhMMWGvMfeG6cFiK8/A==", "cpu": [ "x64" ], @@ -1620,9 +1620,9 @@ ] }, "node_modules/@opentui/core-win32-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-SBeHYwNpWJlHxMX6+aO8KsatWpMMxOs+LpFA7M2PTV0g81WUHPlxm6kHi6UHpjwYuslvtcYKgUL0IyQs1jbdNA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.1.92.tgz", + "integrity": "sha512-34YM3uPtDjzUVeSnJWIK2J8mxyduzV7f3mYc4Hub0glNpUdM1jjzF2HvvvnrKK5ElzTsIcno3c3lOYT8yvG1Zg==", "cpu": [ "arm64" ], @@ -1633,9 +1633,9 @@ ] }, "node_modules/@opentui/core-win32-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-QIU/s6NrXJLRlTyLJZ/41E3MhVGGZazPrwv6MnMx7LOE/uBQo4OGjcRdsIIkhXYIqNRUIH/Yfd5Hyf6twJpBBA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.1.92.tgz", + "integrity": "sha512-uk442kA2Vn0mmJHHqk5sPM+Zai/AN9sgl7egekhoEOUx2VK3gxftKsVlx2YVpCHTvTE/S+vnD2WpQaJk2SNjww==", "cpu": [ "x64" ], @@ -1669,21 +1669,21 @@ } }, "node_modules/@opentui/solid": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-wfItFCVBsP2iWvFgj2/lVRN7/O7R5eu9NReY4Wl34Z+c9d7P6FVSa1xOriziTPysukW1OhFe8MNN7MIaggYdHg==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.1.92.tgz", + "integrity": "sha512-0Sx1+6zRpmMJ5oDEY0JS9b9+eGd/Q0fPndNllrQNnp7w2FCjpXmvHdBdq+pFI6kFp01MHq2ZOkUU5zX5/9YMSQ==", "license": "MIT", "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", - "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/core": "0.1.92", "babel-plugin-module-resolver": "5.0.2", - "babel-preset-solid": "1.9.9", + "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { - "solid-js": "1.9.9" + "solid-js": "1.9.11" } }, "node_modules/@tokenizer/token": { @@ -1778,16 +1778,16 @@ } }, "node_modules/babel-preset-solid": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", - "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.10.tgz", + "integrity": "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ==", "license": "MIT", "dependencies": { - "babel-plugin-jsx-dom-expressions": "^0.40.1" + "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", - "solid-js": "^1.9.8" + "solid-js": "^1.9.10" }, "peerDependenciesMeta": { "solid-js": { @@ -2847,18 +2847,18 @@ } }, "node_modules/seroval": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", - "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.1.tgz", + "integrity": "sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==", "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/seroval-plugins": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz", - "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.1.tgz", + "integrity": "sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw==", "license": "MIT", "engines": { "node": ">=10" @@ -2883,14 +2883,14 @@ } }, "node_modules/solid-js": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", - "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", + "version": "1.9.11", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz", + "integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==", "license": "MIT", "dependencies": { "csstype": "^3.1.0", - "seroval": "~1.3.0", - "seroval-plugins": "~1.3.0" + "seroval": "~1.5.0", + "seroval-plugins": "~1.5.0" } }, "node_modules/stage-js": { diff --git a/package.json b/package.json index 101edbc0..930126d0 100644 --- a/package.json +++ b/package.json @@ -57,11 +57,11 @@ "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", + "@opentui/core": "0.1.92", + "@opentui/solid": "0.1.92", "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", - "solid-js": "1.9.9", + "solid-js": "1.9.11", "zod": "^4.3.6" }, "devDependencies": { diff --git a/tui/package-lock.json b/tui/package-lock.json index ca7a02c3..23559e90 100644 --- a/tui/package-lock.json +++ b/tui/package-lock.json @@ -1,15 +1,15 @@ { - "name": "dcp-tui-probe-local", + "name": "dcp-tui-local", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "dcp-tui-probe-local", + "name": "dcp-tui-local", "dependencies": { "@opencode-ai/plugin": "1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", - "solid-js": "1.9.9" + "@opentui/core": "0.1.92", + "@opentui/solid": "0.1.92", + "solid-js": "1.9.11" } }, "../../../../../src/opencode/node_modules/.bun/solid-js@1.9.10/node_modules/solid-js": { @@ -1055,9 +1055,9 @@ "license": "MIT" }, "node_modules/@opentui/core": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-e/n7hCtpOzS57X9llODu0SUXCQBWSxHQeTA0iuL7j0nhSFgM6KpL8kJ7VQBU1EEn33pytA0udbfKSJ6sqWmEJg==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.1.92.tgz", + "integrity": "sha512-c+KdYAIH3M8n24RYaor+t7AQtKZ3l84L7xdP7DEaN4xtuYH8W08E6Gi+wUal4g+HSai3HS9irox68yFf0VPAxw==", "license": "MIT", "dependencies": { "bun-ffi-structs": "0.1.2", @@ -1068,12 +1068,12 @@ }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", - "@opentui/core-darwin-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-darwin-x64": "0.0.0-20260307-536c401b", - "@opentui/core-linux-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-linux-x64": "0.0.0-20260307-536c401b", - "@opentui/core-win32-arm64": "0.0.0-20260307-536c401b", - "@opentui/core-win32-x64": "0.0.0-20260307-536c401b", + "@opentui/core-darwin-arm64": "0.1.92", + "@opentui/core-darwin-x64": "0.1.92", + "@opentui/core-linux-arm64": "0.1.92", + "@opentui/core-linux-x64": "0.1.92", + "@opentui/core-win32-arm64": "0.1.92", + "@opentui/core-win32-x64": "0.1.92", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" @@ -1083,9 +1083,9 @@ } }, "node_modules/@opentui/core-darwin-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-y46MUgcjkIqC/IBxErchM51KmLARxudrKqr09Gyy25ry+GUE8gzaEIx6EeMAUnWDWvetMacKgEaNCjtdkfGgDQ==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-arm64/-/core-darwin-arm64-0.1.92.tgz", + "integrity": "sha512-NX/qFRuc7My0pazyOrw9fdTXmU7omXcZzQuHcsaVnwssljaT52UYMrJ7mCKhSo69RhHw0lnGCymTorvz3XBdsA==", "cpu": [ "arm64" ], @@ -1096,9 +1096,9 @@ ] }, "node_modules/@opentui/core-darwin-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-USf14JkFaCyKvn9FfLn6AZv14o5ED7uHBNq4kCmggD28HmqHsklDhGNyDnswUggCworJ6xz7jICZTiKKrSwRbQ==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-darwin-x64/-/core-darwin-x64-0.1.92.tgz", + "integrity": "sha512-Zb4jn33hOf167llINKLniOabQIycs14LPOBZnQ6l4khbeeTPVJdG8gy9PhlAyIQygDKmRTFncVlP0RP+L6C7og==", "cpu": [ "x64" ], @@ -1109,9 +1109,9 @@ ] }, "node_modules/@opentui/core-linux-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-fzNf0Mv7OjNktJFg17WsvdDD5Ej12eSwPVMProlQFbklC8qCEsZfLJKYq9ExYLRoxHX7wFm9Eq6L7hVaGcn2sA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-arm64/-/core-linux-arm64-0.1.92.tgz", + "integrity": "sha512-4VA1A91OTMPJ3LkAyaxKEZVJsk5jIc3Kz0gV2vip8p2aGLPpYHHpkFZpXP/FyzsnJzoSGftBeA6ya1GKa5bkXg==", "cpu": [ "arm64" ], @@ -1122,9 +1122,9 @@ ] }, "node_modules/@opentui/core-linux-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-+80TgK5ZhdJvM2+fiCbeCJvXk9De3oNB42wcCtGcwt3x1wyPYAbWIetw6dIGqXIbica/El+7+6Y2DMV06PUUug==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-linux-x64/-/core-linux-x64-0.1.92.tgz", + "integrity": "sha512-tr7va8hfKS1uY+TBmulQBoBlwijzJk56K/U/L9/tbHfW7oJctqxPVwEFHIh1HDcOQ3/UhMMWGvMfeG6cFiK8/A==", "cpu": [ "x64" ], @@ -1135,9 +1135,9 @@ ] }, "node_modules/@opentui/core-win32-arm64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-SBeHYwNpWJlHxMX6+aO8KsatWpMMxOs+LpFA7M2PTV0g81WUHPlxm6kHi6UHpjwYuslvtcYKgUL0IyQs1jbdNA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-arm64/-/core-win32-arm64-0.1.92.tgz", + "integrity": "sha512-34YM3uPtDjzUVeSnJWIK2J8mxyduzV7f3mYc4Hub0glNpUdM1jjzF2HvvvnrKK5ElzTsIcno3c3lOYT8yvG1Zg==", "cpu": [ "arm64" ], @@ -1148,9 +1148,9 @@ ] }, "node_modules/@opentui/core-win32-x64": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-QIU/s6NrXJLRlTyLJZ/41E3MhVGGZazPrwv6MnMx7LOE/uBQo4OGjcRdsIIkhXYIqNRUIH/Yfd5Hyf6twJpBBA==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/core-win32-x64/-/core-win32-x64-0.1.92.tgz", + "integrity": "sha512-uk442kA2Vn0mmJHHqk5sPM+Zai/AN9sgl7egekhoEOUx2VK3gxftKsVlx2YVpCHTvTE/S+vnD2WpQaJk2SNjww==", "cpu": [ "x64" ], @@ -1161,21 +1161,21 @@ ] }, "node_modules/@opentui/solid": { - "version": "0.0.0-20260307-536c401b", - "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.0.0-20260307-536c401b.tgz", - "integrity": "sha512-wfItFCVBsP2iWvFgj2/lVRN7/O7R5eu9NReY4Wl34Z+c9d7P6FVSa1xOriziTPysukW1OhFe8MNN7MIaggYdHg==", + "version": "0.1.92", + "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.1.92.tgz", + "integrity": "sha512-0Sx1+6zRpmMJ5oDEY0JS9b9+eGd/Q0fPndNllrQNnp7w2FCjpXmvHdBdq+pFI6kFp01MHq2ZOkUU5zX5/9YMSQ==", "license": "MIT", "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", - "@opentui/core": "0.0.0-20260307-536c401b", + "@opentui/core": "0.1.92", "babel-plugin-module-resolver": "5.0.2", - "babel-preset-solid": "1.9.9", + "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { - "solid-js": "1.9.9" + "solid-js": "1.9.11" } }, "node_modules/@tokenizer/token": { @@ -1266,16 +1266,16 @@ } }, "node_modules/babel-preset-solid": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.9.tgz", - "integrity": "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw==", + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.10.tgz", + "integrity": "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ==", "license": "MIT", "dependencies": { - "babel-plugin-jsx-dom-expressions": "^0.40.1" + "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", - "solid-js": "^1.9.8" + "solid-js": "^1.9.10" }, "peerDependenciesMeta": { "solid-js": { @@ -2213,18 +2213,18 @@ } }, "node_modules/seroval": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", - "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.1.tgz", + "integrity": "sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==", "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/seroval-plugins": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz", - "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.1.tgz", + "integrity": "sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw==", "license": "MIT", "engines": { "node": ">=10" @@ -2243,14 +2243,14 @@ } }, "node_modules/solid-js": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", - "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", + "version": "1.9.11", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz", + "integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==", "license": "MIT", "dependencies": { "csstype": "^3.1.0", - "seroval": "~1.3.0", - "seroval-plugins": "~1.3.0" + "seroval": "~1.5.0", + "seroval-plugins": "~1.5.0" } }, "node_modules/stage-js": { diff --git a/tui/package.json b/tui/package.json index d435eaa1..fc6ff3ba 100644 --- a/tui/package.json +++ b/tui/package.json @@ -5,8 +5,8 @@ "type": "module", "dependencies": { "@opencode-ai/plugin": "1.3.2", - "@opentui/core": "0.0.0-20260307-536c401b", - "@opentui/solid": "0.0.0-20260307-536c401b", - "solid-js": "1.9.9" + "@opentui/core": "0.1.92", + "@opentui/solid": "0.1.92", + "solid-js": "1.9.11" } } From 398f4f045d328a4c2618cb429309e056e76c6956 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Sun, 29 Mar 2026 14:46:59 -0400 Subject: [PATCH 39/50] docs: add global plugin install command --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d5bac30..ca4bd9f6 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ Automatically reduces token usage in OpenCode by managing conversation context. Install from the CLI: ```bash -opencode plugin @tarquinen/opencode-dcp@latest --global +opencode plugin @tarquinen/opencode-dcp@beta --global ``` -This installs the package and adds it to your global OpenCode config. +This installs the package and adds it to your global OpenCode config files. Or add it to your OpenCode configs manually: From bee8c52de5ca6ddcced8d2a2aff113da8992d10f Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Mon, 30 Mar 2026 14:19:06 -0400 Subject: [PATCH 40/50] docs: simplify installation instructions --- README.md | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/README.md b/README.md index ca4bd9f6..60420000 100644 --- a/README.md +++ b/README.md @@ -15,27 +15,7 @@ Install from the CLI: opencode plugin @tarquinen/opencode-dcp@beta --global ``` -This installs the package and adds it to your global OpenCode config files. - -Or add it to your OpenCode configs manually: - -```jsonc -// opencode.jsonc -{ - "plugin": ["@tarquinen/opencode-dcp@beta"], -} -``` - -```jsonc -// tui.jsonc -{ - "plugin": ["@tarquinen/opencode-dcp@beta"], -} -``` - -Using `@beta` keeps you on the current prerelease channel when OpenCode starts. - -Restart OpenCode. The plugin will automatically start optimizing your sessions. +This installs the package and adds it to your global OpenCode config. ## How It Works From 8097dbd912d20d6e6db7761061d6053e70cbac9d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 31 Mar 2026 21:11:28 -0400 Subject: [PATCH 41/50] change default mode to message --- lib/config.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 39ae41b7..5cec1048 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -642,7 +642,7 @@ function scheduleConfigWarning( if (!notify) return try { notify(title, message) - } catch {} + } catch { } }, 7000) } @@ -711,7 +711,7 @@ const defaultConfig: PluginConfig = { }, protectedFilePatterns: [], compress: { - mode: "range", + mode: "message", permission: "allow", showCompression: false, summaryBuffer: true, @@ -766,8 +766,8 @@ function getConfigPaths(directory?: string): { const global = existsSync(GLOBAL_CONFIG_PATH_JSONC) ? GLOBAL_CONFIG_PATH_JSONC : existsSync(GLOBAL_CONFIG_PATH_JSON) - ? GLOBAL_CONFIG_PATH_JSON - : null + ? GLOBAL_CONFIG_PATH_JSON + : null let configDir: string | null = null const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR @@ -777,8 +777,8 @@ function getConfigPaths(directory?: string): { configDir = existsSync(configJsonc) ? configJsonc : existsSync(configJson) - ? configJson - : null + ? configJson + : null } let project: string | null = null @@ -790,8 +790,8 @@ function getConfigPaths(directory?: string): { project = existsSync(projectJsonc) ? projectJsonc : existsSync(projectJson) - ? projectJson - : null + ? projectJson + : null } } From f0e42730b8e940f57d50c21e164d24f37182b549 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 31 Mar 2026 21:12:57 -0400 Subject: [PATCH 42/50] v3.2.3-beta0 - Bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f66824a..f7e5d178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.2-beta0", + "version": "3.2.3-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.2-beta0", + "version": "3.2.3-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index 930126d0..34de4762 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.2-beta0", + "version": "3.2.3-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", From a62f259b2a97843462df1110ddbccd385ca7f776 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 31 Mar 2026 21:14:23 -0400 Subject: [PATCH 43/50] format --- lib/config.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 5cec1048..643dbf03 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -642,7 +642,7 @@ function scheduleConfigWarning( if (!notify) return try { notify(title, message) - } catch { } + } catch {} }, 7000) } @@ -766,8 +766,8 @@ function getConfigPaths(directory?: string): { const global = existsSync(GLOBAL_CONFIG_PATH_JSONC) ? GLOBAL_CONFIG_PATH_JSONC : existsSync(GLOBAL_CONFIG_PATH_JSON) - ? GLOBAL_CONFIG_PATH_JSON - : null + ? GLOBAL_CONFIG_PATH_JSON + : null let configDir: string | null = null const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR @@ -777,8 +777,8 @@ function getConfigPaths(directory?: string): { configDir = existsSync(configJsonc) ? configJsonc : existsSync(configJson) - ? configJson - : null + ? configJson + : null } let project: string | null = null @@ -790,8 +790,8 @@ function getConfigPaths(directory?: string): { project = existsSync(projectJsonc) ? projectJsonc : existsSync(projectJson) - ? projectJson - : null + ? projectJson + : null } } From 5884e22ebccbf5a4791910a42dffc66d1a0f42d5 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 31 Mar 2026 21:40:55 -0400 Subject: [PATCH 44/50] v3.2.4-beta0 - Fix npm TUI packaging --- package-lock.json | 4 ++-- package.json | 4 +++- tui/package-lock.json | 2 ++ tui/package.json | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7e5d178..2d6b0889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.3-beta0", + "version": "3.2.4-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.3-beta0", + "version": "3.2.4-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index 34de4762..99df4e7c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.3-beta0", + "version": "3.2.4-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -74,9 +74,11 @@ "files": [ "dist/", "lib/analysis/", + "lib/compress/", "lib/state/", "lib/config.ts", "lib/logger.ts", + "lib/message-ids.ts", "lib/messages/query.ts", "lib/token-utils.ts", "tui/data/", diff --git a/tui/package-lock.json b/tui/package-lock.json index 23559e90..b6009868 100644 --- a/tui/package-lock.json +++ b/tui/package-lock.json @@ -1,10 +1,12 @@ { "name": "dcp-tui-local", + "version": "3.2.4-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dcp-tui-local", + "version": "3.2.4-beta0", "dependencies": { "@opencode-ai/plugin": "1.3.2", "@opentui/core": "0.1.92", diff --git a/tui/package.json b/tui/package.json index fc6ff3ba..2311f599 100644 --- a/tui/package.json +++ b/tui/package.json @@ -8,5 +8,6 @@ "@opentui/core": "0.1.92", "@opentui/solid": "0.1.92", "solid-js": "1.9.11" - } + }, + "version": "3.2.4-beta0" } From 3c2c9ac1e73ebcb295105c4eb82c5660bc24f9cd Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 31 Mar 2026 22:15:51 -0400 Subject: [PATCH 45/50] chore: switch npm publish selection to .npmignore --- .npmignore | 41 ++++++++++++++++++++++++++++++----------- package.json | 18 ------------------ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.npmignore b/.npmignore index c1b05f17..3f12308a 100644 --- a/.npmignore +++ b/.npmignore @@ -1,25 +1,44 @@ -# Development files +# Dependencies node_modules/ + +# Build artifacts +*.tgz + +# Logs logs/ dist/logs/ *.log +npm-debug.log* + +# Local/dev files .DS_Store tsconfig.json watch-logs.sh bun.lock -# Documentation -ANALYSIS.md -docs/ -notes/ - -# Source files (since we're shipping dist/) -index.ts -lib/ - # Git .git/ .gitignore +.github/ -# OpenCode +# OpenCode local config .opencode/ + +# Docs and local notes +ANALYSIS.md +docs/ +notes/ +SCHEMA_NOTES.md +assets/ +CONTRIBUTING.md +.prettierrc +.repomixignore + +# Tests and local helpers +tests/ +tests/results/ +test-update.ts +repomix-output.xml +scripts/ +tui/package-lock.json +tui/types/ diff --git a/package.json b/package.json index 99df4e7c..ba4ddc37 100644 --- a/package.json +++ b/package.json @@ -71,24 +71,6 @@ "tsx": "^4.21.0", "typescript": "^6.0.2" }, - "files": [ - "dist/", - "lib/analysis/", - "lib/compress/", - "lib/state/", - "lib/config.ts", - "lib/logger.ts", - "lib/message-ids.ts", - "lib/messages/query.ts", - "lib/token-utils.ts", - "tui/data/", - "tui/routes/", - "tui/shared/", - "tui/slots/", - "tui/index.tsx", - "README.md", - "LICENSE" - ], "directories": { "doc": "docs", "lib": "lib", From 438af46bb28f0fcaf72e43e0757743e6638f1b74 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 1 Apr 2026 04:39:51 -0400 Subject: [PATCH 46/50] chore(release): verify package contents for beta publish --- .npmignore | 1 + package-lock.json | 4 +- package.json | 19 ++- scripts/verify-package.mjs | 229 +++++++++---------------------------- 4 files changed, 74 insertions(+), 179 deletions(-) diff --git a/.npmignore b/.npmignore index 3f12308a..bd1973ad 100644 --- a/.npmignore +++ b/.npmignore @@ -40,5 +40,6 @@ tests/results/ test-update.ts repomix-output.xml scripts/ +tui/node_modules/ tui/package-lock.json tui/types/ diff --git a/package-lock.json b/package-lock.json index 2d6b0889..3d937f07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.4-beta0", + "version": "3.2.6-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.4-beta0", + "version": "3.2.6-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index ba4ddc37..a46d97ec 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.4-beta0", + "version": "3.2.6-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -20,10 +20,25 @@ "server", "tui" ], + "files": [ + "dist/", + "index.ts", + "lib/**/*.ts", + "tui/index.tsx", + "tui/data/*.ts", + "tui/routes/*.tsx", + "tui/shared/*.ts", + "tui/slots/*.tsx", + "README.md", + "LICENSE", + "dcp.schema.json" + ], "scripts": { "clean": "rm -rf dist", "build": "npm run clean && tsc", - "prepublishOnly": "npm run build", + "verify:package": "node scripts/verify-package.mjs", + "check:package": "npm run build && npm run verify:package", + "prepublishOnly": "npm run check:package", "dev": "opencode plugin dev", "typecheck": "tsc --noEmit", "tui:link-host-runtime": "node scripts/link-tui-host-runtime.mjs", diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index e2547494..2457c430 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -1,24 +1,27 @@ -import { builtinModules, createRequire } from "node:module" -import { existsSync, readFileSync, statSync } from "node:fs" import { execFileSync } from "node:child_process" -import path from "node:path" -import process from "node:process" -import { fileURLToPath } from "node:url" +import { existsSync, readFileSync } from "node:fs" -const require = createRequire(import.meta.url) -const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))) - -const builtinNames = new Set([ - ...builtinModules, - ...builtinModules.map((name) => name.replace(/^node:/, "")), -]) +const repoRoot = new URL("../", import.meta.url) +const packageJsonPath = new URL("./package.json", repoRoot) +const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) const requiredRepoFiles = [ "dist/index.js", "dist/index.d.ts", "dist/lib/config.js", + "dist/tui/index.d.ts", + "index.ts", + "lib/config.ts", + "tui/index.tsx", + "tui/data/context.ts", + "tui/routes/summary.tsx", + "tui/shared/names.ts", + "tui/shared/theme.ts", + "tui/shared/types.ts", + "tui/slots/sidebar-content.tsx", "README.md", "LICENSE", + "dcp.schema.json", ] const requiredTarballFiles = [ @@ -26,14 +29,25 @@ const requiredTarballFiles = [ "dist/index.js", "dist/index.d.ts", "dist/lib/config.js", + "dist/tui/index.js", + "dist/tui/index.d.ts", + "index.ts", + "lib/config.ts", + "tui/index.tsx", + "tui/data/context.ts", + "tui/routes/summary.tsx", + "tui/shared/names.ts", + "tui/shared/theme.ts", + "tui/shared/types.ts", + "tui/slots/sidebar-content.tsx", "README.md", "LICENSE", + "dcp.schema.json", ] const forbiddenTarballPatterns = [ + /^tui\/node_modules\//, /^node_modules\//, - /^lib\//, - /^index\.ts$/, /^tests\//, /^scripts\//, /^docs\//, @@ -41,188 +55,53 @@ const forbiddenTarballPatterns = [ /^notes\//, /^\.github\//, /^package-lock\.json$/, - /^tsconfig\.json$/, ] -const packageInfoCache = new Map() - -function fail(message) { +const fail = (message) => { console.error(`package verification failed: ${message}`) process.exit(1) } -function assertRepoFilesExist() { - for (const relativePath of requiredRepoFiles) { - if (!existsSync(path.join(root, relativePath))) { - fail(`missing required file: ${relativePath}`) - } +for (const relativePath of requiredRepoFiles) { + const absolutePath = new URL(`./${relativePath}`, repoRoot) + if (!existsSync(absolutePath)) { + fail(`missing required repo file '${relativePath}'`) } } -function assertPackageJsonShape() { - const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")) - - if (pkg.main !== "./dist/index.js") { - fail(`package.json main must remain ./dist/index.js, found ${pkg.main ?? ""}`) - } - - const files = Array.isArray(pkg.files) ? pkg.files : [] - for (const entry of ["dist/", "README.md", "LICENSE"]) { - if (!files.includes(entry)) { - fail(`package.json files must include ${entry}`) - } - } +if (packageJson.exports?.["./tui"]?.import !== "./tui/index.tsx") { + fail("expected package.json exports['./tui'].import to be './tui/index.tsx'") } -function getImportStatements(source) { - const pattern = /^\s*import\s+([^\n;]+?)\s+from\s+["']([^"']+)["']/gm - return Array.from(source.matchAll(pattern), (match) => ({ - clause: match[1].trim(), - specifier: match[2], - })) +if (packageJson.exports?.["."]?.import !== "./dist/index.js") { + fail("expected package.json exports['.'].import to be './dist/index.js'") } -function getImportKind(clause) { - if (clause.startsWith("type ")) return "type" - if (clause.startsWith("* as ")) return "namespace" - if (clause.startsWith("{")) return "named" - if (clause.includes(",")) { - const [, trailing = ""] = clause.split(",", 2) - return trailing.trim().startsWith("* as ") ? "default+namespace" : "default+named" - } - return "default" -} +const packOutput = execFileSync("npm", ["pack", "--dry-run", "--json"], { + cwd: repoRoot, + encoding: "utf8", +}) -function getPackageName(specifier) { - if (specifier.startsWith("@")) { - const parts = specifier.split("/") - return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier - } - return specifier.split("/")[0] +const packResult = JSON.parse(packOutput) +if (!Array.isArray(packResult) || packResult.length !== 1 || !Array.isArray(packResult[0]?.files)) { + fail("unexpected npm pack JSON output") } -function resolveLocalImport(importerPath, specifier) { - const basePath = path.resolve(path.dirname(importerPath), specifier) - const candidates = [ - basePath, - `${basePath}.ts`, - `${basePath}.tsx`, - `${basePath}.js`, - `${basePath}.mjs`, - path.join(basePath, "index.ts"), - path.join(basePath, "index.tsx"), - path.join(basePath, "index.js"), - path.join(basePath, "index.mjs"), - ] +const tarballFiles = new Set(packResult[0].files.map((entry) => entry.path)) - for (const candidate of candidates) { - if (existsSync(candidate) && statSync(candidate).isFile()) return candidate +for (const relativePath of requiredTarballFiles) { + if (!tarballFiles.has(relativePath)) { + fail(`tarball is missing required file '${relativePath}'`) } - - fail(`unable to resolve local import ${specifier} from ${path.relative(root, importerPath)}`) } -function findPackageInfo(packageName, importerPath) { - const cacheKey = `${packageName}::${path.dirname(importerPath)}` - if (packageInfoCache.has(cacheKey)) { - return packageInfoCache.get(cacheKey) - } - - let entry - try { - entry = require.resolve(packageName, { paths: [path.dirname(importerPath)] }) - } catch { - packageInfoCache.set(cacheKey, null) - return null - } - - let current = path.dirname(entry) - while (true) { - const manifest = path.join(current, "package.json") - if (existsSync(manifest)) { - const info = JSON.parse(readFileSync(manifest, "utf8")) - packageInfoCache.set(cacheKey, info) - return info - } - const parent = path.dirname(current) - if (parent === current) { - packageInfoCache.set(cacheKey, null) - return null +for (const relativePath of tarballFiles) { + for (const pattern of forbiddenTarballPatterns) { + if (pattern.test(relativePath)) { + fail(`tarball contains forbidden path '${relativePath}'`) } - current = parent } } -function packageLooksCommonJs(pkg) { - if (!pkg) return false - if (pkg.type === "commonjs") return true - - const main = typeof pkg.main === "string" ? pkg.main : "" - return /(?:^|\/)(cjs|umd)(?:\/|$)/.test(main) || main.endsWith(".cjs") -} - -function validateRuntimeImportGraph() { - const pending = [path.join(root, "index.ts")] - const seen = new Set() - - while (pending.length > 0) { - const filePath = pending.pop() - if (!filePath || seen.has(filePath)) continue - seen.add(filePath) - - const source = readFileSync(filePath, "utf8") - for (const entry of getImportStatements(source)) { - if (entry.specifier.startsWith(".")) { - pending.push(resolveLocalImport(filePath, entry.specifier)) - continue - } - - const packageName = getPackageName(entry.specifier) - if (builtinNames.has(packageName)) continue - - const kind = getImportKind(entry.clause) - if (kind === "type" || kind === "namespace") continue - - const pkg = findPackageInfo(packageName, filePath) - if (packageLooksCommonJs(pkg)) { - fail( - `${path.relative(root, filePath)} uses ${kind} import from CommonJS-style package ${packageName}`, - ) - } - } - } -} - -function validatePackedFiles() { - const output = execFileSync("npm", ["pack", "--dry-run", "--json"], { - cwd: root, - encoding: "utf8", - }) - - const [result] = JSON.parse(output) - if (!result || !Array.isArray(result.files)) { - fail("npm pack --dry-run --json did not return file metadata") - } - - const packedPaths = result.files.map((file) => file.path) - for (const required of requiredTarballFiles) { - if (!packedPaths.includes(required)) { - fail(`packed tarball is missing ${required}`) - } - } - - const forbidden = packedPaths.find((file) => - forbiddenTarballPatterns.some((pattern) => pattern.test(file)), - ) - if (forbidden) { - fail(`packed tarball contains forbidden path ${forbidden}`) - } - - console.log(`package verification passed for ${result.name}@${result.version}`) - console.log(`tarball entries: ${result.entryCount}`) -} - -assertRepoFilesExist() -assertPackageJsonShape() -validateRuntimeImportGraph() -validatePackedFiles() +console.log(`package verification passed for ${packageJson.name}@${packageJson.version}`) +console.log(`tarball entries: ${packResult[0].entryCount}`) From 8c835cb935ea0e14b279d2ea902355ae8892e3b0 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 1 Apr 2026 18:19:57 -0400 Subject: [PATCH 47/50] some stuff that makes 0 sense to me but it works --- lib/config.ts | 4 ++-- lib/token-utils.ts | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 643dbf03..4884d2f8 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,7 +1,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs" import { join, dirname } from "path" import { homedir } from "os" -import * as jsoncParser from "jsonc-parser" +import { parse } from "jsonc-parser/lib/esm/main.js" import type { PluginInput } from "@opencode-ai/plugin" type Permission = "ask" | "allow" | "deny" @@ -824,7 +824,7 @@ function loadConfigFile(configPath: string): ConfigLoadResult { } try { - const parsed = jsoncParser.parse(fileContent, undefined, { allowTrailingComma: true }) + const parsed = parse(fileContent, undefined, { allowTrailingComma: true }) if (parsed === undefined || parsed === null) { return { data: null, parseError: "Config file is empty or invalid" } } diff --git a/lib/token-utils.ts b/lib/token-utils.ts index 6d514a64..ac1c09df 100644 --- a/lib/token-utils.ts +++ b/lib/token-utils.ts @@ -1,7 +1,9 @@ import { SessionState, WithParts } from "./state" import { AssistantMessage, UserMessage } from "@opencode-ai/sdk/v2" import { Logger } from "./logger" -import * as anthropicTokenizer from "@anthropic-ai/tokenizer" +import * as _anthropicTokenizer from "@anthropic-ai/tokenizer" +const anthropicCountTokens = (_anthropicTokenizer.countTokens ?? + (_anthropicTokenizer as any).default?.countTokens) as typeof _anthropicTokenizer.countTokens import { getLastUserMessage } from "./messages/query" export function getCurrentTokenUsage(state: SessionState, messages: WithParts[]): number { @@ -67,7 +69,7 @@ export function getCurrentParams( export function countTokens(text: string): number { if (!text) return 0 try { - return anthropicTokenizer.countTokens(text) + return anthropicCountTokens(text) } catch { return Math.round(text.length / 4) } From 25d87498f9aad430a7f3eeb92f634ca0e082a2e1 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 1 Apr 2026 19:10:44 -0400 Subject: [PATCH 48/50] v3.2.8-beta0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d937f07..507d0e06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "3.2.6-beta0", + "version": "3.2.8-beta0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "3.2.6-beta0", + "version": "3.2.8-beta0", "license": "AGPL-3.0-or-later", "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", diff --git a/package.json b/package.json index a46d97ec..22974440 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "3.2.6-beta0", + "version": "3.2.8-beta0", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", From 1ebb369bda33d52913c4a2ec7897337a99c3ce6d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 1 Apr 2026 22:42:40 -0400 Subject: [PATCH 49/50] fix: align package exports and deps with opencode plugin loader conventions --- package.json | 16 ++++++++-------- scripts/verify-package.mjs | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 22974440..036fd13d 100644 --- a/package.json +++ b/package.json @@ -11,15 +11,15 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js" }, + "./server": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, "./tui": { "types": "./dist/tui/index.d.ts", "import": "./tui/index.tsx" } }, - "oc-plugin": [ - "server", - "tui" - ], "files": [ "dist/", "index.ts", @@ -67,16 +67,16 @@ "author": "tarquinen", "license": "AGPL-3.0-or-later", "peerDependencies": { - "@opencode-ai/plugin": ">=1.2.0" + "@opencode-ai/plugin": ">=1.2.0", + "@opentui/core": ">=0.1.0", + "@opentui/solid": ">=0.1.0", + "solid-js": ">=1.9.0" }, "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", - "@opentui/core": "0.1.92", - "@opentui/solid": "0.1.92", "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", - "solid-js": "1.9.11", "zod": "^4.3.6" }, "devDependencies": { diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 2457c430..53778f10 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -77,6 +77,10 @@ if (packageJson.exports?.["."]?.import !== "./dist/index.js") { fail("expected package.json exports['.'].import to be './dist/index.js'") } +if (packageJson.exports?.["./server"]?.import !== "./dist/index.js") { + fail("expected package.json exports['./server'].import to be './dist/index.js'") +} + const packOutput = execFileSync("npm", ["pack", "--dry-run", "--json"], { cwd: repoRoot, encoding: "utf8", From cbbc086ad53a6c9d6d95c994a16cf636cefd698f Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 2 Apr 2026 14:19:51 -0400 Subject: [PATCH 50/50] fix: remove unused fuzzball dependency to resolve audit vulnerability --- package-lock.json | 324 ++++++++++++++++++++++++++++++++++------------ package.json | 1 - 2 files changed, 244 insertions(+), 81 deletions(-) diff --git a/package-lock.json b/package-lock.json index 507d0e06..a6e6eae2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,7 @@ "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", - "@opentui/core": "0.1.92", - "@opentui/solid": "0.1.92", - "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", - "solid-js": "1.9.11", "zod": "^4.3.6" }, "devDependencies": { @@ -26,7 +22,10 @@ "typescript": "^6.0.2" }, "peerDependencies": { - "@opencode-ai/plugin": ">=1.2.0" + "@opencode-ai/plugin": ">=1.2.0", + "@opentui/core": ">=0.1.0", + "@opentui/solid": ">=0.1.0", + "solid-js": ">=1.9.0" } }, "node_modules/@ampproject/remapping": { @@ -34,6 +33,7 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -72,6 +72,7 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", @@ -86,6 +87,7 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -95,6 +97,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -125,6 +128,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", @@ -141,6 +145,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.27.3" }, @@ -153,6 +158,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", @@ -169,6 +175,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -190,6 +197,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -199,6 +207,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" @@ -212,6 +221,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" @@ -225,6 +235,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", @@ -242,6 +253,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.27.1" }, @@ -254,6 +266,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -263,6 +276,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", @@ -280,6 +294,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -293,6 +308,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -302,6 +318,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -311,6 +328,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -320,6 +338,7 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" @@ -333,6 +352,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.29.0" }, @@ -348,6 +368,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -363,6 +384,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -378,6 +400,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -394,6 +417,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -413,6 +437,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", @@ -432,6 +457,7 @@ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", @@ -446,6 +472,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -464,6 +491,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -477,7 +505,8 @@ "resolved": "https://registry.npmjs.org/@dimforge/rapier2d-simd-compat/-/rapier2d-simd-compat-0.17.3.tgz", "integrity": "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg==", "license": "Apache-2.0", - "optional": true + "optional": true, + "peer": true }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.0", @@ -926,6 +955,7 @@ "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", @@ -944,6 +974,7 @@ "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", @@ -959,6 +990,7 @@ "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -968,6 +1000,7 @@ "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -983,6 +1016,7 @@ "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -998,6 +1032,7 @@ "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1012,6 +1047,7 @@ "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1026,6 +1062,7 @@ "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1040,6 +1077,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", @@ -1054,6 +1092,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1063,6 +1102,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/utils": "1.6.0" @@ -1076,6 +1116,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" @@ -1089,6 +1130,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1098,6 +1140,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1114,6 +1157,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1123,6 +1167,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-blit": "1.6.0", @@ -1140,6 +1185,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1149,6 +1195,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", @@ -1165,6 +1212,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1174,6 +1222,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1189,6 +1238,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1198,6 +1248,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", @@ -1212,6 +1263,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1221,6 +1273,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0" }, @@ -1233,6 +1286,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", @@ -1247,6 +1301,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1256,6 +1311,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" @@ -1269,6 +1325,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1278,6 +1335,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-bmp": "1.6.0", @@ -1299,6 +1357,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" @@ -1312,6 +1371,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1321,6 +1381,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-jpeg": "1.6.0", @@ -1342,6 +1403,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1351,6 +1413,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", "license": "MIT", + "peer": true, "dependencies": { "image-q": "^4.0.0", "zod": "^3.23.8" @@ -1364,6 +1427,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1373,6 +1437,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", @@ -1387,6 +1452,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1396,6 +1462,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", @@ -1413,6 +1480,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1422,6 +1490,7 @@ "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-color": "1.6.0", @@ -1439,6 +1508,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1448,6 +1518,7 @@ "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", "license": "MIT", + "peer": true, "dependencies": { "zod": "^3.23.8" }, @@ -1460,6 +1531,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -1469,6 +1541,7 @@ "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/types": "1.6.0", "tinycolor2": "^1.6.0" @@ -1482,6 +1555,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -1492,6 +1566,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -1500,13 +1575,15 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1544,6 +1621,7 @@ "resolved": "https://registry.npmjs.org/@opentui/core/-/core-0.1.92.tgz", "integrity": "sha512-c+KdYAIH3M8n24RYaor+t7AQtKZ3l84L7xdP7DEaN4xtuYH8W08E6Gi+wUal4g+HSai3HS9irox68yFf0VPAxw==", "license": "MIT", + "peer": true, "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", @@ -1578,7 +1656,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@opentui/core-darwin-x64": { "version": "0.1.92", @@ -1591,7 +1670,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@opentui/core-linux-arm64": { "version": "0.1.92", @@ -1604,7 +1684,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@opentui/core-linux-x64": { "version": "0.1.92", @@ -1617,7 +1698,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@opentui/core-win32-arm64": { "version": "0.1.92", @@ -1630,7 +1712,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@opentui/core-win32-x64": { "version": "0.1.92", @@ -1643,13 +1726,15 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@opentui/core/node_modules/bun-ffi-structs": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/bun-ffi-structs/-/bun-ffi-structs-0.1.2.tgz", "integrity": "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w==", "license": "MIT", + "peer": true, "peerDependencies": { "typescript": "^5" } @@ -1673,6 +1758,7 @@ "resolved": "https://registry.npmjs.org/@opentui/solid/-/solid-0.1.92.tgz", "integrity": "sha512-0Sx1+6zRpmMJ5oDEY0JS9b9+eGd/Q0fPndNllrQNnp7w2FCjpXmvHdBdq+pFI6kFp01MHq2ZOkUU5zX5/9YMSQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", @@ -1690,7 +1776,8 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/node": { "version": "25.5.0", @@ -1707,13 +1794,15 @@ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", "license": "BSD-3-Clause", - "optional": true + "optional": true, + "peer": true }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", + "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -1725,13 +1814,15 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/await-to-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -1741,6 +1832,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.6.tgz", "integrity": "sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", @@ -1757,6 +1849,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -1769,6 +1862,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", "license": "MIT", + "peer": true, "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", @@ -1782,6 +1876,7 @@ "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.10.tgz", "integrity": "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ==", "license": "MIT", + "peer": true, "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, @@ -1799,7 +1894,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -1819,13 +1915,15 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/baseline-browser-mapping": { "version": "2.10.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz", "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==", "license": "Apache-2.0", + "peer": true, "bin": { "baseline-browser-mapping": "dist/cli.cjs" }, @@ -1837,13 +1935,15 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/brace-expansion": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -1867,6 +1967,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -1900,6 +2001,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -1911,6 +2013,7 @@ "integrity": "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "@webgpu/types": "^0.1.60" }, @@ -1932,7 +2035,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/bun-webgpu-darwin-x64": { "version": "0.1.6", @@ -1945,7 +2049,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/bun-webgpu-linux-x64": { "version": "0.1.6", @@ -1958,7 +2063,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/bun-webgpu-win32-x64": { "version": "0.1.6", @@ -1971,7 +2077,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/caniuse-lite": { "version": "1.0.30001781", @@ -1991,25 +2098,29 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0" + "license": "CC-BY-4.0", + "peer": true }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2027,6 +2138,7 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.3.1" } @@ -2035,13 +2147,15 @@ "version": "1.5.328", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/entities": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.12" }, @@ -2096,6 +2210,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -2105,6 +2220,7 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -2114,6 +2230,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.x" } @@ -2121,13 +2238,15 @@ "node_modules/exif-parser": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "peer": true }, "node_modules/file-type": { "version": "16.5.4", "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", "license": "MIT", + "peer": true, "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", @@ -2145,6 +2264,7 @@ "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.2.tgz", "integrity": "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==", "license": "MIT", + "peer": true, "dependencies": { "json5": "^2.2.3" } @@ -2154,6 +2274,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^3.0.0" }, @@ -2165,7 +2286,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -2187,26 +2309,17 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fuzzball": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fuzzball/-/fuzzball-2.2.3.tgz", - "integrity": "sha512-sQDb3kjI7auA4YyE1YgEW85MTparcSgRgcCweUK06Cn0niY5lN+uhFiRUZKN4MQVGGiHxlbrYCA4nL1QjOXBLQ==", - "license": "MIT", - "dependencies": { - "heap": ">=0.2.0", - "lodash": "^4.17.21", - "setimmediate": "^1.0.5" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -2229,6 +2342,7 @@ "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", "license": "MIT", + "peer": true, "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" @@ -2240,6 +2354,7 @@ "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", @@ -2258,6 +2373,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2265,17 +2381,12 @@ "node": ">= 0.4" } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", - "license": "MIT" - }, "node_modules/html-entities": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/ieee754": { "version": "1.2.1", @@ -2295,13 +2406,15 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/image-q": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "16.9.1" } @@ -2310,13 +2423,15 @@ "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", + "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -2332,6 +2447,7 @@ "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", "license": "MIT", + "peer": true, "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", @@ -2369,19 +2485,22 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", + "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -2394,6 +2513,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", + "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -2412,6 +2532,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -2420,17 +2541,12 @@ "node": ">=6" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "license": "ISC", + "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -2440,6 +2556,7 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -2452,6 +2569,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", + "peer": true, "bin": { "mime": "cli.js" }, @@ -2464,6 +2582,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2479,6 +2598,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=8" } @@ -2487,25 +2607,29 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/node-releases": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/omggif": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "license": "MIT", + "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -2521,6 +2645,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^2.0.0" }, @@ -2533,6 +2658,7 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -2541,25 +2667,29 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" + "license": "(MIT AND Zlib)", + "peer": true }, "node_modules/parse-bmfont-ascii": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/parse-bmfont-binary": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/parse-bmfont-xml": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", "license": "MIT", + "peer": true, "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" @@ -2570,6 +2700,7 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", + "peer": true, "dependencies": { "entities": "^6.0.0" }, @@ -2582,6 +2713,7 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.12" }, @@ -2594,6 +2726,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -2602,13 +2735,15 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -2624,13 +2759,15 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/path-scurry/node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -2640,6 +2777,7 @@ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -2652,13 +2790,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/pixelmatch": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", "license": "ISC", + "peer": true, "dependencies": { "pngjs": "^6.0.0" }, @@ -2671,6 +2811,7 @@ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", "license": "MIT", + "peer": true, "engines": { "node": ">=12.13.0" } @@ -2680,6 +2821,7 @@ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "license": "MIT", + "peer": true, "dependencies": { "find-up": "^3.0.0" }, @@ -2693,6 +2835,7 @@ "integrity": "sha512-B+lHKhRSeg7vZOfEyEzyQVu7nx8JHcX3QgnAcHXrPW0j04XYKX5eXSiUrxH2Z5QR8OoqvjD6zKIaPMdMYAd0uA==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=24.0" }, @@ -2705,6 +2848,7 @@ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "license": "MIT", + "peer": true, "engines": { "node": ">=14.19.0" } @@ -2730,6 +2874,7 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6.0" } @@ -2739,6 +2884,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", + "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -2755,6 +2901,7 @@ "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", "license": "MIT", + "peer": true, "dependencies": { "readable-stream": "^4.7.0" }, @@ -2770,13 +2917,15 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", + "peer": true, "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", @@ -2806,7 +2955,8 @@ "version": "0.4.9", "resolved": "https://registry.npmjs.org/s-js/-/s-js-0.4.9.tgz", "integrity": "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/safe-buffer": { "version": "5.2.1", @@ -2826,13 +2976,15 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/sax": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=11.0.0" } @@ -2842,6 +2994,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -2851,6 +3004,7 @@ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.1.tgz", "integrity": "sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" } @@ -2860,6 +3014,7 @@ "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.1.tgz", "integrity": "sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -2867,17 +3022,12 @@ "seroval": "^1.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/simple-xml-to-json": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.4.tgz", "integrity": "sha512-3MY16e0ocMHL7N1ufpdObURGyX+lCo0T/A+y6VCwosLdH1HSda4QZl1Sdt/O+2qWp48WFi26XEp5rF0LoaL0Dg==", "license": "MIT", + "peer": true, "engines": { "node": ">=20.12.2" } @@ -2887,6 +3037,7 @@ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz", "integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", @@ -2909,6 +3060,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -2918,6 +3070,7 @@ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", "license": "MIT", + "peer": true, "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" @@ -2935,6 +3088,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -2947,7 +3101,8 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", "integrity": "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/tiktoken": { "version": "1.0.22", @@ -2959,13 +3114,15 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/token-types": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", "license": "MIT", + "peer": true, "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" @@ -3038,6 +3195,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -3054,6 +3212,7 @@ "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", "license": "MIT", + "peer": true, "dependencies": { "pako": "^1.0.11" } @@ -3077,13 +3236,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "license": "MIT", + "peer": true, "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -3097,6 +3258,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "license": "MIT", + "peer": true, "engines": { "node": ">=4.0" } @@ -3105,13 +3267,15 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/yoga-layout": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/zod": { "version": "4.3.6", diff --git a/package.json b/package.json index 036fd13d..cb46eeb4 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "dependencies": { "@anthropic-ai/tokenizer": "^0.0.4", "@opencode-ai/sdk": "^1.3.2", - "fuzzball": "^2.2.3", "jsonc-parser": "^3.3.1", "zod": "^4.3.6" },