mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Add DevUI package for .NET (#1603)
* Implement DevUI * Review feedback * Fix build
This commit is contained in:
committed by
GitHub
Unverified
parent
94a5ba3448
commit
8855bfb065
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,11 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/agentframework.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="agentframework.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Agent Framework Dev UI</title>
|
||||
<script type="module" crossorigin src="/assets/index-D_Y1oSGu.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CE4pGoXh.css">
|
||||
<script type="module" crossorigin src="./assets/index.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -2,12 +2,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/agentframework.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="agentframework.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Agent Framework Dev UI</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script type="module" src="src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -45,6 +45,7 @@ import type {
|
||||
ExtendedResponseStreamEvent,
|
||||
} from "@/types";
|
||||
import { useDevUIStore } from "@/stores";
|
||||
import { loadStreamingState } from "@/services/streaming-state";
|
||||
|
||||
type DebugEventHandler = (event: ExtendedResponseStreamEvent | "clear") => void;
|
||||
|
||||
@@ -229,7 +230,6 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const accumulatedText = useRef<string>("");
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const currentMessageUsage = useRef<{
|
||||
total_tokens: number;
|
||||
@@ -237,6 +237,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
output_tokens: number;
|
||||
} | null>(null);
|
||||
const userJustSentMessage = useRef<boolean>(false);
|
||||
const accumulatedTextRef = useRef<string>("");
|
||||
|
||||
// Auto-scroll to bottom when new items arrive
|
||||
useEffect(() => {
|
||||
@@ -281,33 +282,270 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
|
||||
// Load conversations when agent changes
|
||||
useEffect(() => {
|
||||
// Resume streaming after page refresh
|
||||
const resumeStreaming = async (
|
||||
assistantMessage: import("@/types/openai").ConversationMessage,
|
||||
conversation: Conversation,
|
||||
agent: AgentInfo
|
||||
) => {
|
||||
// Load the stored state to get the response ID
|
||||
const storedState = loadStreamingState(conversation.id);
|
||||
if (!storedState || !storedState.responseId) {
|
||||
setIsStreaming(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the stored responseId to resume the stream via GET /v1/responses/{responseId}
|
||||
const openAIRequest: import("@/types/agent-framework").AgentFrameworkRequest = {
|
||||
model: agent.id,
|
||||
input: [], // Not needed for resume (using GET)
|
||||
stream: true,
|
||||
conversation: conversation.id,
|
||||
};
|
||||
|
||||
// Pass the response ID explicitly to trigger GET request
|
||||
const streamGenerator = apiClient.streamAgentExecutionOpenAIDirect(
|
||||
agent.id,
|
||||
openAIRequest,
|
||||
conversation.id,
|
||||
storedState.responseId // Pass response ID for resume
|
||||
);
|
||||
|
||||
for await (const openAIEvent of streamGenerator) {
|
||||
// Pass all events to debug panel
|
||||
onDebugEvent(openAIEvent);
|
||||
|
||||
// Handle response.completed event
|
||||
if (openAIEvent.type === "response.completed") {
|
||||
const completedEvent = openAIEvent as import("@/types/openai").ResponseCompletedEvent;
|
||||
const usage = completedEvent.response?.usage;
|
||||
|
||||
if (usage) {
|
||||
currentMessageUsage.current = {
|
||||
input_tokens: usage.input_tokens,
|
||||
output_tokens: usage.output_tokens,
|
||||
total_tokens: usage.total_tokens,
|
||||
};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle response.failed event
|
||||
if (openAIEvent.type === "response.failed") {
|
||||
const failedEvent = openAIEvent as import("@/types/openai").ResponseFailedEvent;
|
||||
const error = failedEvent.response?.error;
|
||||
const errorMessage = error
|
||||
? typeof error === "object" && "message" in error
|
||||
? (error as any).message
|
||||
: JSON.stringify(error)
|
||||
: "Request failed";
|
||||
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: accumulatedTextRef.current || errorMessage,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "incomplete" as const,
|
||||
}
|
||||
: item
|
||||
));
|
||||
setIsStreaming(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle function approval request events
|
||||
if (openAIEvent.type === "response.function_approval.requested") {
|
||||
const approvalEvent = openAIEvent as import("@/types/openai").ResponseFunctionApprovalRequestedEvent;
|
||||
setPendingApprovals([
|
||||
...useDevUIStore.getState().pendingApprovals,
|
||||
{
|
||||
request_id: approvalEvent.request_id,
|
||||
function_call: approvalEvent.function_call,
|
||||
},
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle function approval response events
|
||||
if (openAIEvent.type === "response.function_approval.responded") {
|
||||
const responseEvent = openAIEvent as import("@/types/openai").ResponseFunctionApprovalRespondedEvent;
|
||||
setPendingApprovals(
|
||||
useDevUIStore.getState().pendingApprovals.filter((a) => a.request_id !== responseEvent.request_id)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle error events
|
||||
if (openAIEvent.type === "error") {
|
||||
const errorEvent = openAIEvent as ExtendedResponseStreamEvent & { message?: string };
|
||||
const errorMessage = errorEvent.message || "An error occurred";
|
||||
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: accumulatedTextRef.current || errorMessage,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "incomplete" as const,
|
||||
}
|
||||
: item
|
||||
));
|
||||
setIsStreaming(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle text delta events
|
||||
if (
|
||||
openAIEvent.type === "response.output_text.delta" &&
|
||||
"delta" in openAIEvent &&
|
||||
openAIEvent.delta
|
||||
) {
|
||||
accumulatedTextRef.current += openAIEvent.delta;
|
||||
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: accumulatedTextRef.current,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "in_progress" as const,
|
||||
}
|
||||
: item
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Stream ended - mark as complete
|
||||
const finalUsage = currentMessageUsage.current;
|
||||
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
status: "completed" as const,
|
||||
usage: finalUsage || undefined,
|
||||
}
|
||||
: item
|
||||
));
|
||||
setIsStreaming(false);
|
||||
|
||||
if (finalUsage) {
|
||||
updateConversationUsage(finalUsage.total_tokens);
|
||||
}
|
||||
|
||||
currentMessageUsage.current = null;
|
||||
} catch (error) {
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error resuming stream: ${
|
||||
error instanceof Error ? error.message : "Unknown error"
|
||||
}`,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "incomplete" as const,
|
||||
}
|
||||
: item
|
||||
));
|
||||
setIsStreaming(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loadConversations = async () => {
|
||||
if (!selectedAgent) return;
|
||||
|
||||
setLoadingConversations(true);
|
||||
try {
|
||||
// Step 1: Try to list conversations from backend (DevUI extension)
|
||||
// This works with DevUI backend but fails with OpenAI/Azure (they don't have list endpoint)
|
||||
// Step 1: Always try to list conversations from backend first
|
||||
// This ensures we get the latest data from the server
|
||||
try {
|
||||
const { data: conversations } = await apiClient.listConversations(
|
||||
selectedAgent.id
|
||||
);
|
||||
|
||||
// Backend successfully returned conversations list
|
||||
setAvailableConversations(conversations);
|
||||
|
||||
if (conversations.length > 0) {
|
||||
// Found conversations on backend - use most recent
|
||||
const mostRecent = conversations[0];
|
||||
setAvailableConversations(conversations);
|
||||
setCurrentConversation(mostRecent);
|
||||
|
||||
// Load conversation items from backend
|
||||
try {
|
||||
const { data: items } = await apiClient.listConversationItems(
|
||||
mostRecent.id
|
||||
);
|
||||
// Load all conversation items with pagination
|
||||
let allItems: unknown[] = [];
|
||||
let hasMore = true;
|
||||
let after: string | undefined = undefined;
|
||||
|
||||
while (hasMore) {
|
||||
const result = await apiClient.listConversationItems(
|
||||
mostRecent.id,
|
||||
{ order: "asc", after } // Load in chronological order (oldest first)
|
||||
);
|
||||
allItems = allItems.concat(result.data);
|
||||
hasMore = result.has_more;
|
||||
|
||||
// Get the last item's ID for pagination
|
||||
if (hasMore && result.data.length > 0) {
|
||||
const lastItem = result.data[result.data.length - 1] as { id?: string };
|
||||
after = lastItem.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Use OpenAI ConversationItems directly (no conversion!)
|
||||
setChatItems(items as import("@/types/openai").ConversationItem[]);
|
||||
setChatItems(allItems as import("@/types/openai").ConversationItem[]);
|
||||
setIsStreaming(false);
|
||||
|
||||
// Check for incomplete stream and resume if needed
|
||||
const state = loadStreamingState(mostRecent.id);
|
||||
|
||||
if (state && !state.completed) {
|
||||
accumulatedTextRef.current = state.accumulatedText || "";
|
||||
// Add assistant message with resumed text
|
||||
const assistantMsg: import("@/types/openai").ConversationMessage = {
|
||||
id: state.lastMessageId || `assistant-${Date.now()}`,
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
content: state.accumulatedText ? [{ type: "text", text: state.accumulatedText }] : [],
|
||||
status: "in_progress",
|
||||
};
|
||||
setChatItems([...allItems as import("@/types/openai").ConversationItem[], assistantMsg]);
|
||||
setIsStreaming(true);
|
||||
|
||||
// Resume streaming from where we left off
|
||||
setTimeout(() => {
|
||||
resumeStreaming(assistantMsg, mostRecent, selectedAgent);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Scroll to bottom after loading conversation
|
||||
setTimeout(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}, 100);
|
||||
} catch {
|
||||
// 404 means conversation exists but has no items yet (newly created)
|
||||
// This is normal - just start with empty chat
|
||||
@@ -316,11 +554,6 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
setIsStreaming(false);
|
||||
}
|
||||
|
||||
// Cache to localStorage for faster future loads
|
||||
localStorage.setItem(
|
||||
`devui_convs_${selectedAgent.id}`,
|
||||
JSON.stringify(conversations)
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
@@ -371,9 +604,6 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
setAvailableConversations([newConversation]);
|
||||
setChatItems([]);
|
||||
setIsStreaming(false);
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem(cachedKey, JSON.stringify([newConversation]));
|
||||
} catch {
|
||||
setAvailableConversations([]);
|
||||
setChatItems([]);
|
||||
@@ -387,10 +617,12 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
setChatItems([]);
|
||||
setIsStreaming(false);
|
||||
setCurrentConversation(undefined);
|
||||
accumulatedText.current = "";
|
||||
accumulatedTextRef.current = "";
|
||||
|
||||
loadConversations();
|
||||
}, [selectedAgent, setLoadingConversations, setAvailableConversations, setCurrentConversation, setChatItems, setIsStreaming]);
|
||||
// currentConversation is intentionally excluded - this effect should only run when agent changes
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedAgent, onDebugEvent, setChatItems, setIsStreaming, setLoadingConversations, setAvailableConversations, setCurrentConversation, setPendingApprovals, updateConversationUsage]);
|
||||
|
||||
// Handle file uploads
|
||||
const handleFilesSelected = async (files: File[]) => {
|
||||
@@ -626,16 +858,11 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
setIsStreaming(false);
|
||||
// Reset conversation usage by setting it to initial state
|
||||
useDevUIStore.setState({ conversationUsage: { total_tokens: 0, message_count: 0 } });
|
||||
accumulatedText.current = "";
|
||||
|
||||
// Update localStorage cache with new conversation
|
||||
const cachedKey = `devui_convs_${selectedAgent.id}`;
|
||||
const updated = [newConversation, ...availableConversations];
|
||||
localStorage.setItem(cachedKey, JSON.stringify(updated));
|
||||
accumulatedTextRef.current = "";
|
||||
} catch {
|
||||
// Failed to create conversation
|
||||
}
|
||||
}, [selectedAgent, availableConversations, setCurrentConversation, setAvailableConversations, setChatItems, setIsStreaming]);
|
||||
}, [selectedAgent, setCurrentConversation, setAvailableConversations, setChatItems, setIsStreaming]);
|
||||
|
||||
// Handle conversation deletion
|
||||
const handleDeleteConversation = useCallback(
|
||||
@@ -660,15 +887,6 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
);
|
||||
setAvailableConversations(updatedConversations);
|
||||
|
||||
// Update localStorage cache
|
||||
if (selectedAgent) {
|
||||
const cachedKey = `devui_convs_${selectedAgent.id}`;
|
||||
localStorage.setItem(
|
||||
cachedKey,
|
||||
JSON.stringify(updatedConversations)
|
||||
);
|
||||
}
|
||||
|
||||
// If deleted conversation was selected, switch to another conversation or clear chat
|
||||
if (currentConversation?.id === conversationId) {
|
||||
if (updatedConversations.length > 0) {
|
||||
@@ -683,7 +901,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
setChatItems([]);
|
||||
setIsStreaming(false);
|
||||
useDevUIStore.setState({ conversationUsage: { total_tokens: 0, message_count: 0 } });
|
||||
accumulatedText.current = "";
|
||||
accumulatedTextRef.current = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,7 +912,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
alert("Failed to delete conversation. Please try again.");
|
||||
}
|
||||
},
|
||||
[availableConversations, currentConversation, selectedAgent, onDebugEvent, setAvailableConversations, setCurrentConversation, setChatItems, setIsStreaming]
|
||||
[availableConversations, currentConversation, onDebugEvent, setAvailableConversations, setCurrentConversation, setChatItems, setIsStreaming]
|
||||
);
|
||||
|
||||
// Handle conversation selection
|
||||
@@ -711,11 +929,28 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
onDebugEvent("clear");
|
||||
|
||||
try {
|
||||
// Load conversation history from backend
|
||||
const result = await apiClient.listConversationItems(conversationId);
|
||||
// Load conversation history from backend with pagination
|
||||
let allItems: unknown[] = [];
|
||||
let hasMore = true;
|
||||
let after: string | undefined = undefined;
|
||||
|
||||
while (hasMore) {
|
||||
const result = await apiClient.listConversationItems(conversationId, {
|
||||
order: "asc", // Load in chronological order (oldest first)
|
||||
after,
|
||||
});
|
||||
allItems = allItems.concat(result.data);
|
||||
hasMore = result.has_more;
|
||||
|
||||
// Get the last item's ID for pagination
|
||||
if (hasMore && result.data.length > 0) {
|
||||
const lastItem = result.data[result.data.length - 1] as { id?: string };
|
||||
after = lastItem.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Use OpenAI ConversationItems directly (no conversion!)
|
||||
const items = result.data as import("@/types/openai").ConversationItem[];
|
||||
const items = allItems as import("@/types/openai").ConversationItem[];
|
||||
|
||||
setChatItems(items);
|
||||
setIsStreaming(false);
|
||||
@@ -727,6 +962,27 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
message_count: items.length,
|
||||
}
|
||||
});
|
||||
|
||||
// Check for incomplete stream and restore accumulated text
|
||||
const state = loadStreamingState(conversationId);
|
||||
if (state?.accumulatedText) {
|
||||
accumulatedTextRef.current = state.accumulatedText;
|
||||
// Add assistant message with resumed text - streaming will continue automatically
|
||||
const assistantMsg: import("@/types/openai").ConversationMessage = {
|
||||
id: `assistant-${Date.now()}`,
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
content: [{ type: "output_text", text: state.accumulatedText }],
|
||||
status: "in_progress",
|
||||
};
|
||||
setChatItems([...items, assistantMsg]);
|
||||
setIsStreaming(true);
|
||||
}
|
||||
|
||||
// Scroll to bottom after loading conversation
|
||||
setTimeout(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}, 100);
|
||||
} catch {
|
||||
// 404 means conversation doesn't exist or has no items yet
|
||||
// This can happen if server restarted (in-memory store cleared)
|
||||
@@ -736,7 +992,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
useDevUIStore.setState({ conversationUsage: { total_tokens: 0, message_count: 0 } });
|
||||
}
|
||||
|
||||
accumulatedText.current = "";
|
||||
accumulatedTextRef.current = "";
|
||||
},
|
||||
[availableConversations, onDebugEvent, setCurrentConversation, setChatItems, setIsStreaming]
|
||||
);
|
||||
@@ -851,13 +1107,18 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any previous streaming state for this conversation before starting new message
|
||||
if (conversationToUse?.id) {
|
||||
apiClient.clearStreamingState(conversationToUse.id);
|
||||
}
|
||||
|
||||
const apiRequest = {
|
||||
input: request.input,
|
||||
conversation_id: conversationToUse?.id,
|
||||
};
|
||||
|
||||
// Clear text accumulator for new response
|
||||
accumulatedText.current = "";
|
||||
accumulatedTextRef.current = "";
|
||||
|
||||
// Use OpenAI-compatible API streaming - direct event handling
|
||||
const streamGenerator = apiClient.streamAgentExecutionOpenAI(
|
||||
@@ -884,6 +1145,35 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
continue; // Continue processing other events
|
||||
}
|
||||
|
||||
// Handle response.failed event
|
||||
if (openAIEvent.type === "response.failed") {
|
||||
const failedEvent = openAIEvent as import("@/types/openai").ResponseFailedEvent;
|
||||
const error = failedEvent.response?.error;
|
||||
const errorMessage = error
|
||||
? typeof error === "object" && "message" in error
|
||||
? (error as any).message
|
||||
: JSON.stringify(error)
|
||||
: "Request failed";
|
||||
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
setChatItems(currentItems.map((item) =>
|
||||
item.id === assistantMessage.id && item.type === "message"
|
||||
? {
|
||||
...item,
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: accumulatedTextRef.current || errorMessage,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "incomplete" as const,
|
||||
}
|
||||
: item
|
||||
));
|
||||
setIsStreaming(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle function approval request events
|
||||
if (openAIEvent.type === "response.function_approval.requested") {
|
||||
const approvalEvent = openAIEvent as import("@/types/openai").ResponseFunctionApprovalRequestedEvent;
|
||||
@@ -943,7 +1233,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
"delta" in openAIEvent &&
|
||||
openAIEvent.delta
|
||||
) {
|
||||
accumulatedText.current += openAIEvent.delta;
|
||||
accumulatedTextRef.current += openAIEvent.delta;
|
||||
|
||||
// Update assistant message with accumulated content
|
||||
const currentItems = useDevUIStore.getState().chatItems;
|
||||
@@ -954,7 +1244,7 @@ export function AgentView({ selectedAgent, onDebugEvent }: AgentViewProps) {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: accumulatedText.current,
|
||||
text: accumulatedTextRef.current,
|
||||
} as import("@/types/openai").MessageTextContent,
|
||||
],
|
||||
status: "in_progress" as const,
|
||||
|
||||
+3
-1
@@ -23,7 +23,7 @@ interface ContentRendererProps {
|
||||
|
||||
// Text content renderer
|
||||
function TextContentRenderer({ content, className, isStreaming }: ContentRendererProps) {
|
||||
if (content.type !== "text") return null;
|
||||
if (content.type !== "text" && content.type !== "input_text" && content.type !== "output_text") return null;
|
||||
|
||||
const text = content.text;
|
||||
|
||||
@@ -160,6 +160,8 @@ function FileContentRenderer({ content, className }: ContentRendererProps) {
|
||||
export function OpenAIContentRenderer({ content, className, isStreaming }: ContentRendererProps) {
|
||||
switch (content.type) {
|
||||
case "text":
|
||||
case "input_text":
|
||||
case "output_text":
|
||||
return <TextContentRenderer content={content} className={className} isStreaming={isStreaming} />;
|
||||
case "input_image":
|
||||
return <ImageContentRenderer content={content} className={className} />;
|
||||
|
||||
@@ -554,6 +554,10 @@ export function WorkflowView({
|
||||
try {
|
||||
const request = { input_data: inputData };
|
||||
|
||||
// Clear any previous streaming state before starting new workflow execution
|
||||
// Note: Workflows don't use conversation IDs, so we use workflow ID as the key
|
||||
apiClient.clearStreamingState(selectedWorkflow.id);
|
||||
|
||||
// Use OpenAI-compatible API streaming - direct event handling
|
||||
const streamGenerator = apiClient.streamWorkflowExecutionOpenAI(
|
||||
selectedWorkflow.id,
|
||||
|
||||
@@ -3,6 +3,10 @@ import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
import { ThemeProvider } from "./components/theme-provider"
|
||||
import { initStreamingState } from "./services/api"
|
||||
|
||||
// Initialize streaming state management (clears expired states)
|
||||
initStreamingState();
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -14,6 +14,12 @@ import type {
|
||||
} from "@/types";
|
||||
import type { AgentFrameworkRequest } from "@/types/agent-framework";
|
||||
import type { ExtendedResponseStreamEvent } from "@/types/openai";
|
||||
import {
|
||||
loadStreamingState,
|
||||
updateStreamingState,
|
||||
markStreamingCompleted,
|
||||
clearStreamingState,
|
||||
} from "./streaming-state";
|
||||
|
||||
// Backend API response type - polymorphic entity that can be agent or workflow
|
||||
// This matches the Python Pydantic EntityInfo model which has all fields optional
|
||||
@@ -57,9 +63,27 @@ const DEFAULT_API_BASE_URL =
|
||||
? import.meta.env.VITE_API_BASE_URL
|
||||
: "http://localhost:8080";
|
||||
|
||||
// Retry configuration for streaming
|
||||
const RETRY_INTERVAL_MS = 1000; // Retry every second
|
||||
const MAX_RETRY_ATTEMPTS = 600; // Max 600 retries (10 minutes total)
|
||||
|
||||
// Get backend URL from localStorage or default
|
||||
function getBackendUrl(): string {
|
||||
return localStorage.getItem("devui_backend_url") || DEFAULT_API_BASE_URL;
|
||||
const stored = localStorage.getItem("devui_backend_url");
|
||||
if (stored) return stored;
|
||||
|
||||
// If VITE_API_BASE_URL is explicitly set to empty string, use relative path
|
||||
// This allows the frontend to call the same host it's served from
|
||||
if (import.meta.env.VITE_API_BASE_URL === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return DEFAULT_API_BASE_URL;
|
||||
}
|
||||
|
||||
// Helper to sleep for a given duration
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
class ApiClient {
|
||||
@@ -272,6 +296,8 @@ class ApiClient {
|
||||
await this.request(`/v1/conversations/${conversationId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
// Clear streaming state when conversation is deleted
|
||||
clearStreamingState(conversationId);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -297,10 +323,230 @@ class ApiClient {
|
||||
|
||||
// OpenAI-compatible streaming methods using /v1/responses endpoint
|
||||
|
||||
// Private helper method that handles the actual streaming with retry logic
|
||||
private async *streamOpenAIResponse(
|
||||
openAIRequest: AgentFrameworkRequest,
|
||||
conversationId?: string,
|
||||
resumeResponseId?: string
|
||||
): AsyncGenerator<ExtendedResponseStreamEvent, void, unknown> {
|
||||
let lastSequenceNumber = -1;
|
||||
let retryCount = 0;
|
||||
let hasYieldedAnyEvent = false;
|
||||
let currentResponseId: string | undefined = resumeResponseId;
|
||||
let lastMessageId: string | undefined = undefined;
|
||||
|
||||
// Try to resume from stored state if conversation ID is provided
|
||||
if (conversationId) {
|
||||
const storedState = loadStreamingState(conversationId);
|
||||
if (storedState) {
|
||||
// Use stored response ID if no explicit one provided
|
||||
if (!resumeResponseId) {
|
||||
currentResponseId = storedState.responseId;
|
||||
}
|
||||
|
||||
lastSequenceNumber = storedState.lastSequenceNumber;
|
||||
lastMessageId = storedState.lastMessageId;
|
||||
|
||||
// Replay stored events only if we're not explicitly resuming
|
||||
// (explicit resume means the caller already has the events)
|
||||
if (!resumeResponseId) {
|
||||
for (const event of storedState.events) {
|
||||
hasYieldedAnyEvent = true;
|
||||
yield event;
|
||||
}
|
||||
} else {
|
||||
// Mark that we've already seen events up to this sequence number
|
||||
hasYieldedAnyEvent = storedState.events.length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (retryCount <= MAX_RETRY_ATTEMPTS) {
|
||||
try {
|
||||
// If we have a response_id from a previous attempt, use GET endpoint to resume
|
||||
// Otherwise, use POST to create a new response
|
||||
let response: Response;
|
||||
if (currentResponseId) {
|
||||
const params = new URLSearchParams();
|
||||
params.set("stream", "true");
|
||||
if (lastSequenceNumber >= 0) {
|
||||
params.set("starting_after", lastSequenceNumber.toString());
|
||||
}
|
||||
const url = `${this.baseUrl}/v1/responses/${currentResponseId}?${params.toString()}`;
|
||||
response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "text/event-stream",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const url = `${this.baseUrl}/v1/responses`;
|
||||
response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
},
|
||||
body: JSON.stringify(openAIRequest),
|
||||
});
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
// Try to extract detailed error message from response body
|
||||
let errorMessage = `Request failed with status ${response.status}`;
|
||||
try {
|
||||
const errorBody = await response.json();
|
||||
if (errorBody.error && errorBody.error.message) {
|
||||
errorMessage = errorBody.error.message;
|
||||
} else if (errorBody.detail) {
|
||||
errorMessage = errorBody.detail;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to generic message if parsing fails
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error("Response body is not readable");
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
// Stream completed successfully
|
||||
if (conversationId) {
|
||||
markStreamingCompleted(conversationId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// Parse SSE events
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const dataStr = line.slice(6);
|
||||
|
||||
// Handle [DONE] signal
|
||||
if (dataStr === "[DONE]") {
|
||||
if (conversationId) {
|
||||
markStreamingCompleted(conversationId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const openAIEvent: ExtendedResponseStreamEvent =
|
||||
JSON.parse(dataStr);
|
||||
|
||||
// Capture response_id if present in the event for use in retries
|
||||
if ("response" in openAIEvent && openAIEvent.response && typeof openAIEvent.response === "object" && "id" in openAIEvent.response) {
|
||||
const newResponseId = openAIEvent.response.id as string;
|
||||
if (!currentResponseId || currentResponseId !== newResponseId) {
|
||||
currentResponseId = newResponseId;
|
||||
}
|
||||
} else if ("id" in openAIEvent && typeof openAIEvent.id === "string" && openAIEvent.id.startsWith("resp_")) {
|
||||
const newResponseId = openAIEvent.id;
|
||||
if (!currentResponseId || currentResponseId !== newResponseId) {
|
||||
currentResponseId = newResponseId;
|
||||
}
|
||||
}
|
||||
|
||||
// Track last message ID if present (for user/assistant messages)
|
||||
if ("item_id" in openAIEvent && openAIEvent.item_id) {
|
||||
lastMessageId = openAIEvent.item_id;
|
||||
}
|
||||
|
||||
// Check for sequence number restart (server restarted response)
|
||||
const eventSeq = "sequence_number" in openAIEvent ? openAIEvent.sequence_number : undefined;
|
||||
if (eventSeq !== undefined) {
|
||||
// If we've received events before and sequence restarted from 0/1
|
||||
if (hasYieldedAnyEvent && eventSeq <= 1 && lastSequenceNumber > 1) {
|
||||
// Server restarted the response - clear old state and start fresh
|
||||
if (conversationId) {
|
||||
clearStreamingState(conversationId);
|
||||
}
|
||||
yield {
|
||||
type: "error",
|
||||
message: "Connection lost - previous response failed. Starting new response.",
|
||||
} as ExtendedResponseStreamEvent;
|
||||
lastSequenceNumber = eventSeq;
|
||||
hasYieldedAnyEvent = true;
|
||||
|
||||
// Save new event to storage
|
||||
if (conversationId && currentResponseId) {
|
||||
updateStreamingState(conversationId, openAIEvent, currentResponseId, lastMessageId);
|
||||
}
|
||||
|
||||
yield openAIEvent;
|
||||
}
|
||||
// Skip events we've already seen (resume from last position)
|
||||
else if (eventSeq <= lastSequenceNumber) {
|
||||
continue; // Skip duplicate event
|
||||
} else {
|
||||
lastSequenceNumber = eventSeq;
|
||||
hasYieldedAnyEvent = true;
|
||||
|
||||
// Save event to storage before yielding
|
||||
if (conversationId && currentResponseId) {
|
||||
updateStreamingState(conversationId, openAIEvent, currentResponseId, lastMessageId);
|
||||
}
|
||||
|
||||
yield openAIEvent;
|
||||
}
|
||||
} else {
|
||||
// No sequence number - just yield the event
|
||||
hasYieldedAnyEvent = true;
|
||||
|
||||
// Still save to storage if we have conversation context
|
||||
if (conversationId && currentResponseId) {
|
||||
updateStreamingState(conversationId, openAIEvent, currentResponseId, lastMessageId);
|
||||
}
|
||||
|
||||
yield openAIEvent;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to parse OpenAI SSE event:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
} catch (error) {
|
||||
// Network error occurred - prepare to retry
|
||||
retryCount++;
|
||||
|
||||
if (retryCount > MAX_RETRY_ATTEMPTS) {
|
||||
// Max retries exceeded - give up
|
||||
throw new Error(
|
||||
`Connection failed after ${MAX_RETRY_ATTEMPTS} retry attempts: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
|
||||
// Wait before retrying
|
||||
await sleep(RETRY_INTERVAL_MS);
|
||||
// Loop will retry with GET if we have response_id, otherwise POST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stream agent execution using OpenAI format with simplified routing
|
||||
async *streamAgentExecutionOpenAI(
|
||||
agentId: string,
|
||||
request: RunAgentRequest
|
||||
request: RunAgentRequest,
|
||||
resumeResponseId?: string
|
||||
): AsyncGenerator<ExtendedResponseStreamEvent, void, unknown> {
|
||||
const openAIRequest: AgentFrameworkRequest = {
|
||||
model: agentId, // Model IS the entity_id (simplified routing!)
|
||||
@@ -309,87 +555,20 @@ class ApiClient {
|
||||
conversation: request.conversation_id, // OpenAI standard conversation param
|
||||
};
|
||||
|
||||
return yield* this.streamAgentExecutionOpenAIDirect(agentId, openAIRequest);
|
||||
return yield* this.streamAgentExecutionOpenAIDirect(agentId, openAIRequest, request.conversation_id, resumeResponseId);
|
||||
}
|
||||
|
||||
// Stream agent execution using direct OpenAI format
|
||||
async *streamAgentExecutionOpenAIDirect(
|
||||
_agentId: string,
|
||||
openAIRequest: AgentFrameworkRequest
|
||||
openAIRequest: AgentFrameworkRequest,
|
||||
conversationId?: string,
|
||||
resumeResponseId?: string
|
||||
): AsyncGenerator<ExtendedResponseStreamEvent, void, unknown> {
|
||||
|
||||
const response = await fetch(`${this.baseUrl}/v1/responses`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
},
|
||||
body: JSON.stringify(openAIRequest),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// Try to extract detailed error message from response body
|
||||
let errorMessage = `Request failed with status ${response.status}`;
|
||||
try {
|
||||
const errorBody = await response.json();
|
||||
if (errorBody.error && errorBody.error.message) {
|
||||
errorMessage = errorBody.error.message;
|
||||
} else if (errorBody.detail) {
|
||||
errorMessage = errorBody.detail;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to generic message if parsing fails
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error("Response body is not readable");
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// Parse SSE events
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const dataStr = line.slice(6);
|
||||
|
||||
// Handle [DONE] signal
|
||||
if (dataStr === "[DONE]") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const openAIEvent: ExtendedResponseStreamEvent =
|
||||
JSON.parse(dataStr);
|
||||
yield openAIEvent; // Direct pass-through - no conversion!
|
||||
} catch (e) {
|
||||
console.error("Failed to parse OpenAI SSE event:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
yield* this.streamOpenAIResponse(openAIRequest, conversationId, resumeResponseId);
|
||||
}
|
||||
|
||||
// Stream workflow execution using OpenAI format - direct event pass-through
|
||||
// Stream workflow execution using OpenAI format
|
||||
async *streamWorkflowExecutionOpenAI(
|
||||
workflowId: string,
|
||||
request: RunWorkflowRequest
|
||||
@@ -402,75 +581,7 @@ class ApiClient {
|
||||
conversation: request.conversation_id, // Include conversation if present
|
||||
};
|
||||
|
||||
const response = await fetch(`${this.baseUrl}/v1/responses`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
},
|
||||
body: JSON.stringify(openAIRequest),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// Try to extract detailed error message from response body
|
||||
let errorMessage = `Request failed with status ${response.status}`;
|
||||
try {
|
||||
const errorBody = await response.json();
|
||||
if (errorBody.error && errorBody.error.message) {
|
||||
errorMessage = errorBody.error.message;
|
||||
} else if (errorBody.detail) {
|
||||
errorMessage = errorBody.detail;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to generic message if parsing fails
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error("Response body is not readable");
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// Parse SSE events
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ")) {
|
||||
const dataStr = line.slice(6);
|
||||
|
||||
// Handle [DONE] signal
|
||||
if (dataStr === "[DONE]") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const openAIEvent: ExtendedResponseStreamEvent =
|
||||
JSON.parse(dataStr);
|
||||
yield openAIEvent; // Direct pass-through - no conversion!
|
||||
} catch (e) {
|
||||
console.error("Failed to parse OpenAI SSE event:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
yield* this.streamOpenAIResponse(openAIRequest, request.conversation_id);
|
||||
}
|
||||
|
||||
// REMOVED: Legacy streaming methods - use streamAgentExecutionOpenAI and streamWorkflowExecutionOpenAI instead
|
||||
@@ -503,8 +614,16 @@ class ApiClient {
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
// Clear streaming state for a conversation (e.g., when starting a new message)
|
||||
clearStreamingState(conversationId: string): void {
|
||||
clearStreamingState(conversationId);
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const apiClient = new ApiClient();
|
||||
export { ApiClient };
|
||||
|
||||
// Export streaming state init function
|
||||
export { initStreamingState } from "./streaming-state";
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Streaming State Persistence
|
||||
*
|
||||
* Manages browser storage of streaming response state to enable:
|
||||
* - Resume interrupted streams after page refresh
|
||||
* - Replay cached events before fetching new ones
|
||||
* - Graceful recovery from network disconnections
|
||||
*/
|
||||
|
||||
import type { ExtendedResponseStreamEvent } from "@/types/openai";
|
||||
|
||||
export interface StreamingState {
|
||||
conversationId: string;
|
||||
responseId: string;
|
||||
lastMessageId?: string;
|
||||
lastSequenceNumber: number;
|
||||
events: ExtendedResponseStreamEvent[];
|
||||
timestamp: number; // When this state was last updated
|
||||
completed: boolean; // Whether the stream completed successfully
|
||||
accumulatedText?: string; // Accumulated text content for quick restoration
|
||||
}
|
||||
|
||||
const STORAGE_KEY_PREFIX = "devui_streaming_state_";
|
||||
const STATE_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
/**
|
||||
* Storage key for a specific conversation
|
||||
*/
|
||||
function getStorageKey(conversationId: string): string {
|
||||
return `${STORAGE_KEY_PREFIX}${conversationId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract accumulated text from events (for quick restoration)
|
||||
*/
|
||||
function extractAccumulatedText(events: ExtendedResponseStreamEvent[]): string {
|
||||
let text = "";
|
||||
for (const event of events) {
|
||||
if (event.type === "response.output_text.delta" && "delta" in event) {
|
||||
text += event.delta;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save streaming state to browser storage
|
||||
*/
|
||||
export function saveStreamingState(state: StreamingState): void {
|
||||
try {
|
||||
const key = getStorageKey(state.conversationId);
|
||||
const data = JSON.stringify(state);
|
||||
localStorage.setItem(key, data);
|
||||
} catch (error) {
|
||||
console.error("Failed to save streaming state:", error);
|
||||
// If storage is full, try to clear old states
|
||||
try {
|
||||
clearExpiredStreamingStates();
|
||||
// Try again
|
||||
const key = getStorageKey(state.conversationId);
|
||||
const data = JSON.stringify(state);
|
||||
localStorage.setItem(key, data);
|
||||
} catch {
|
||||
console.error("Failed to save streaming state even after cleanup");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load streaming state from browser storage
|
||||
*/
|
||||
export function loadStreamingState(conversationId: string): StreamingState | null {
|
||||
try {
|
||||
const key = getStorageKey(conversationId);
|
||||
const data = localStorage.getItem(key);
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state: StreamingState = JSON.parse(data);
|
||||
|
||||
// Check if state has expired
|
||||
const age = Date.now() - state.timestamp;
|
||||
if (age > STATE_EXPIRY_MS) {
|
||||
clearStreamingState(conversationId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If stream was completed, no need to resume
|
||||
if (state.completed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return state;
|
||||
} catch (error) {
|
||||
console.error("Failed to load streaming state:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update streaming state with a new event
|
||||
*/
|
||||
export function updateStreamingState(
|
||||
conversationId: string,
|
||||
event: ExtendedResponseStreamEvent,
|
||||
responseId: string,
|
||||
lastMessageId?: string
|
||||
): void {
|
||||
try {
|
||||
const existing = loadStreamingState(conversationId);
|
||||
const sequenceNumber = "sequence_number" in event ? event.sequence_number : undefined;
|
||||
|
||||
const newEvents = existing ? [...existing.events, event] : [event];
|
||||
|
||||
const state: StreamingState = {
|
||||
conversationId,
|
||||
responseId,
|
||||
lastMessageId,
|
||||
lastSequenceNumber: sequenceNumber ?? (existing?.lastSequenceNumber ?? -1),
|
||||
events: newEvents,
|
||||
timestamp: Date.now(),
|
||||
completed: event.type === "response.completed" || event.type === "response.failed",
|
||||
accumulatedText: extractAccumulatedText(newEvents),
|
||||
};
|
||||
|
||||
saveStreamingState(state);
|
||||
} catch (error) {
|
||||
console.error("Failed to update streaming state:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark streaming state as completed
|
||||
*/
|
||||
export function markStreamingCompleted(conversationId: string): void {
|
||||
try {
|
||||
const existing = loadStreamingState(conversationId);
|
||||
if (existing) {
|
||||
existing.completed = true;
|
||||
existing.timestamp = Date.now();
|
||||
saveStreamingState(existing);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to mark streaming as completed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear streaming state for a conversation
|
||||
*/
|
||||
export function clearStreamingState(conversationId: string): void {
|
||||
try {
|
||||
const key = getStorageKey(conversationId);
|
||||
localStorage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.error("Failed to clear streaming state:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all expired streaming states
|
||||
*/
|
||||
export function clearExpiredStreamingStates(): void {
|
||||
try {
|
||||
const keys = Object.keys(localStorage);
|
||||
const now = Date.now();
|
||||
|
||||
for (const key of keys) {
|
||||
if (key.startsWith(STORAGE_KEY_PREFIX)) {
|
||||
try {
|
||||
const data = localStorage.getItem(key);
|
||||
if (data) {
|
||||
const state: StreamingState = JSON.parse(data);
|
||||
const age = now - state.timestamp;
|
||||
|
||||
if (age > STATE_EXPIRY_MS || state.completed) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Invalid state, remove it
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to clear expired streaming states:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize streaming state management (call on app startup)
|
||||
*/
|
||||
export function initStreamingState(): void {
|
||||
// Clear expired states on startup
|
||||
clearExpiredStreamingStates();
|
||||
}
|
||||
@@ -347,6 +347,18 @@ export interface MessageTextContent {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface MessageInputTextContent {
|
||||
type: "input_text";
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface MessageOutputTextContent {
|
||||
type: "output_text";
|
||||
text: string;
|
||||
annotations?: any[];
|
||||
logprobs?: any[];
|
||||
}
|
||||
|
||||
export interface MessageInputImage {
|
||||
type: "input_image";
|
||||
image_url: string;
|
||||
@@ -376,6 +388,8 @@ export interface MessageFunctionApprovalResponseContent {
|
||||
|
||||
export type MessageContent =
|
||||
| MessageTextContent
|
||||
| MessageInputTextContent
|
||||
| MessageOutputTextContent
|
||||
| MessageInputImage
|
||||
| MessageInputFile
|
||||
| MessageFunctionApprovalResponseContent;
|
||||
|
||||
@@ -5,6 +5,7 @@ import path from "path";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
base: "",
|
||||
plugins: [react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -12,12 +13,20 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
// Enable deterministic builds, as per https://github.com/vitejs/vite/issues/13672#issuecomment-1784110536
|
||||
strictRequires: true,
|
||||
},
|
||||
outDir: "../agent_framework_devui/ui",
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: undefined,
|
||||
inlineDynamicImports: true,
|
||||
// Use static filenames instead of content hashes
|
||||
entryFileNames: "assets/index.js",
|
||||
chunkFileNames: "assets/[name].js",
|
||||
assetFileNames: "assets/[name].[ext]",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz"
|
||||
integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==
|
||||
|
||||
"@babel/core@^7.28.3":
|
||||
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.28.3":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz"
|
||||
integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==
|
||||
@@ -168,156 +168,9 @@
|
||||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
|
||||
"@emnapi/core@^1.4.3", "@emnapi/core@^1.4.5":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.5.0.tgz#85cd84537ec989cebb2343606a1ee663ce4edaf0"
|
||||
integrity sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==
|
||||
dependencies:
|
||||
"@emnapi/wasi-threads" "1.1.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/runtime@^1.4.3", "@emnapi/runtime@^1.4.5":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.5.0.tgz#9aebfcb9b17195dce3ab53c86787a6b7d058db73"
|
||||
integrity sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/wasi-threads@1.1.0", "@emnapi/wasi-threads@^1.0.4":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf"
|
||||
integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@esbuild/aix-ppc64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz#bef96351f16520055c947aba28802eede3c9e9a9"
|
||||
integrity sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==
|
||||
|
||||
"@esbuild/android-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz#d2e70be7d51a529425422091e0dcb90374c1546c"
|
||||
integrity sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==
|
||||
|
||||
"@esbuild/android-arm@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.9.tgz#d2a753fe2a4c73b79437d0ba1480e2d760097419"
|
||||
integrity sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==
|
||||
|
||||
"@esbuild/android-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.9.tgz#5278836e3c7ae75761626962f902a0d55352e683"
|
||||
integrity sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==
|
||||
|
||||
"@esbuild/darwin-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz"
|
||||
integrity sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==
|
||||
|
||||
"@esbuild/darwin-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz#e27dbc3b507b3a1cea3b9280a04b8b6b725f82be"
|
||||
integrity sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==
|
||||
|
||||
"@esbuild/freebsd-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz#364e3e5b7a1fd45d92be08c6cc5d890ca75908ca"
|
||||
integrity sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==
|
||||
|
||||
"@esbuild/freebsd-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz#7c869b45faeb3df668e19ace07335a0711ec56ab"
|
||||
integrity sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==
|
||||
|
||||
"@esbuild/linux-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz#48d42861758c940b61abea43ba9a29b186d6cb8b"
|
||||
integrity sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==
|
||||
|
||||
"@esbuild/linux-arm@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz#6ce4b9cabf148274101701d112b89dc67cc52f37"
|
||||
integrity sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==
|
||||
|
||||
"@esbuild/linux-ia32@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz#207e54899b79cac9c26c323fc1caa32e3143f1c4"
|
||||
integrity sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==
|
||||
|
||||
"@esbuild/linux-loong64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz#0ba48a127159a8f6abb5827f21198b999ffd1fc0"
|
||||
integrity sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==
|
||||
|
||||
"@esbuild/linux-mips64el@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz#a4d4cc693d185f66a6afde94f772b38ce5d64eb5"
|
||||
integrity sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==
|
||||
|
||||
"@esbuild/linux-ppc64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz#0f5805c1c6d6435a1dafdc043cb07a19050357db"
|
||||
integrity sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==
|
||||
|
||||
"@esbuild/linux-riscv64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz#6776edece0f8fca79f3386398b5183ff2a827547"
|
||||
integrity sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==
|
||||
|
||||
"@esbuild/linux-s390x@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz#3f6f29ef036938447c2218d309dc875225861830"
|
||||
integrity sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==
|
||||
|
||||
"@esbuild/linux-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz#831fe0b0e1a80a8b8391224ea2377d5520e1527f"
|
||||
integrity sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==
|
||||
|
||||
"@esbuild/netbsd-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz#06f99d7eebe035fbbe43de01c9d7e98d2a0aa548"
|
||||
integrity sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==
|
||||
|
||||
"@esbuild/netbsd-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz#db99858e6bed6e73911f92a88e4edd3a8c429a52"
|
||||
integrity sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==
|
||||
|
||||
"@esbuild/openbsd-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz#afb886c867e36f9d86bb21e878e1185f5d5a0935"
|
||||
integrity sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==
|
||||
|
||||
"@esbuild/openbsd-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz#30855c9f8381fac6a0ef5b5f31ac6e7108a66ecf"
|
||||
integrity sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==
|
||||
|
||||
"@esbuild/openharmony-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz#2f2144af31e67adc2a8e3705c20c2bd97bd88314"
|
||||
integrity sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==
|
||||
|
||||
"@esbuild/sunos-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz#69b99a9b5bd226c9eb9c6a73f990fddd497d732e"
|
||||
integrity sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==
|
||||
|
||||
"@esbuild/win32-arm64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz#d789330a712af916c88325f4ffe465f885719c6b"
|
||||
integrity sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==
|
||||
|
||||
"@esbuild/win32-ia32@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz#52fc735406bd49688253e74e4e837ac2ba0789e3"
|
||||
integrity sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==
|
||||
|
||||
"@esbuild/win32-x64@0.25.9":
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz#585624dc829cfb6e7c0aa6c3ca7d7e6daa87e34f"
|
||||
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz"
|
||||
integrity sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0":
|
||||
@@ -368,7 +221,7 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@9.33.0", "@eslint/js@^9.33.0":
|
||||
"@eslint/js@^9.33.0", "@eslint/js@9.33.0":
|
||||
version "9.33.0"
|
||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz"
|
||||
integrity sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==
|
||||
@@ -482,15 +335,6 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@napi-rs/wasm-runtime@^0.2.12":
|
||||
version "0.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2"
|
||||
integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.4.3"
|
||||
"@emnapi/runtime" "^1.4.3"
|
||||
"@tybys/wasm-util" "^0.10.0"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
|
||||
@@ -499,7 +343,7 @@
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
@@ -740,7 +584,7 @@
|
||||
aria-hidden "^1.2.4"
|
||||
react-remove-scroll "^2.6.3"
|
||||
|
||||
"@radix-ui/react-slot@1.2.3", "@radix-ui/react-slot@^1.2.3":
|
||||
"@radix-ui/react-slot@^1.2.3", "@radix-ui/react-slot@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz"
|
||||
integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==
|
||||
@@ -829,104 +673,9 @@
|
||||
resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz"
|
||||
integrity sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.47.1.tgz#6e236cd2fd29bb01a300ad4ff6ed0f1a17550e69"
|
||||
integrity sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.47.1.tgz#808f2c9c7e68161add613ebcb0eac5a058a0df3c"
|
||||
integrity sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.47.1.tgz"
|
||||
integrity sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.47.1.tgz#9aac64e886435493f2e3a0aa5e4aad098a90814c"
|
||||
integrity sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==
|
||||
|
||||
"@rollup/rollup-freebsd-arm64@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.47.1.tgz#9fc804264f7b7a7cdad3747950299f990163be1f"
|
||||
integrity sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==
|
||||
|
||||
"@rollup/rollup-freebsd-x64@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.47.1.tgz#933feaff864feb03bbbcd0c18ea351ade957cf79"
|
||||
integrity sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.47.1.tgz#02915e6b2c55fe5961c27404aba2d9c8ef48ac6c"
|
||||
integrity sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.47.1.tgz#1afef33191b26e76ae7f0d0dc767efc6be1285ce"
|
||||
integrity sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.47.1.tgz#6e7f38fb99d14143de3ce33204e6cd61e1c2c780"
|
||||
integrity sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.47.1.tgz#25ab09f14bbcba85a604bcee2962d2486db90794"
|
||||
integrity sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.47.1.tgz#d3e3a3fd61e21b2753094391dee9b515a2bc9ecd"
|
||||
integrity sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.47.1.tgz#6b44445e2bd5866692010de241bf18d2ae8b0cb8"
|
||||
integrity sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.47.1.tgz#3ff412d20d3b157e6aadabf84788e8c5cb221ba7"
|
||||
integrity sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.47.1.tgz#104f451497d53d82a49c6d08c13c59f5f30eed57"
|
||||
integrity sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.47.1.tgz#d04de7b21d181f30750760cb3553946306506172"
|
||||
integrity sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.47.1.tgz#a6ba88ff7480940a435b1e67ddbb3f207a7ae02f"
|
||||
integrity sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.47.1.tgz#c912c8ffa0c242ed3175cd91cdeaef98109afa54"
|
||||
integrity sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.47.1.tgz#ca5eaae89443554b461bb359112a056528cfdac0"
|
||||
integrity sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.47.1.tgz#34e76172515fb4b374eb990d59f54faff938246e"
|
||||
integrity sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.47.1":
|
||||
version "4.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz#e5e0a0bae2c9d4858cc9b8dc508b2e10d7f0df8b"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz"
|
||||
integrity sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==
|
||||
|
||||
"@tailwindcss/node@4.1.12":
|
||||
@@ -942,71 +691,9 @@
|
||||
source-map-js "^1.2.1"
|
||||
tailwindcss "4.1.12"
|
||||
|
||||
"@tailwindcss/oxide-android-arm64@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz#27920fe61fa2743afe8a8ca296fa640b609d17d5"
|
||||
integrity sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz"
|
||||
integrity sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz#8ddb7e5ddfd9b049ec84a2bda99f2b04a86859f5"
|
||||
integrity sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz#da1c0b16b7a5f95a1e400f299a3ec94fb6fd40ac"
|
||||
integrity sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz#34e558aa6e869c6fe9867cb78ed7ba651b9fcaa4"
|
||||
integrity sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz#0a00a8146ab6215f81b2d385056c991441bf390e"
|
||||
integrity sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz#b138f494068884ae0d8c343dc1904b22f5e98dc6"
|
||||
integrity sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz#5b9d5f23b15cdb714639f5b9741c0df5d610f794"
|
||||
integrity sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz#f68ec530d3ca6875ea9015bcd5dd0762ee5e2f5d"
|
||||
integrity sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz#9fd15a1ebde6076c42c445c5e305c31673ead965"
|
||||
integrity sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.4.5"
|
||||
"@emnapi/runtime" "^1.4.5"
|
||||
"@emnapi/wasi-threads" "^1.0.4"
|
||||
"@napi-rs/wasm-runtime" "^0.2.12"
|
||||
"@tybys/wasm-util" "^0.10.0"
|
||||
tslib "^2.8.0"
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz#938bcc6a82e1120ea4fe2ce94be0a8cdf3ae92c7"
|
||||
integrity sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.12":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz#b1ee2ed0ef2c4095ddec3684a1987e2b3613af36"
|
||||
resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz"
|
||||
integrity sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==
|
||||
|
||||
"@tailwindcss/oxide@4.1.12":
|
||||
@@ -1039,13 +726,6 @@
|
||||
"@tailwindcss/oxide" "4.1.12"
|
||||
tailwindcss "4.1.12"
|
||||
|
||||
"@tybys/wasm-util@^0.10.0":
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369"
|
||||
integrity sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@types/babel__core@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz"
|
||||
@@ -1118,7 +798,7 @@
|
||||
"@types/d3-interpolate" "*"
|
||||
"@types/d3-selection" "*"
|
||||
|
||||
"@types/estree@1.0.8", "@types/estree@^1.0.6":
|
||||
"@types/estree@^1.0.6", "@types/estree@1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz"
|
||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
@@ -1128,19 +808,19 @@
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/node@^24.3.0":
|
||||
"@types/node@^20.19.0 || >=22.12.0", "@types/node@^24.3.0":
|
||||
version "24.3.0"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz"
|
||||
integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==
|
||||
dependencies:
|
||||
undici-types "~7.10.0"
|
||||
|
||||
"@types/react-dom@^19.1.7":
|
||||
"@types/react-dom@*", "@types/react-dom@^19.1.7":
|
||||
version "19.1.7"
|
||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz"
|
||||
integrity sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==
|
||||
|
||||
"@types/react@^19.1.10":
|
||||
"@types/react@*", "@types/react@^19.0.0", "@types/react@^19.1.10", "@types/react@>=16.8", "@types/react@>=18.0.0":
|
||||
version "19.1.10"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz"
|
||||
integrity sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==
|
||||
@@ -1162,7 +842,7 @@
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/parser@8.40.0":
|
||||
"@typescript-eslint/parser@^8.40.0", "@typescript-eslint/parser@8.40.0":
|
||||
version "8.40.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz"
|
||||
integrity sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==
|
||||
@@ -1190,7 +870,7 @@
|
||||
"@typescript-eslint/types" "8.40.0"
|
||||
"@typescript-eslint/visitor-keys" "8.40.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@8.40.0", "@typescript-eslint/tsconfig-utils@^8.40.0":
|
||||
"@typescript-eslint/tsconfig-utils@^8.40.0", "@typescript-eslint/tsconfig-utils@8.40.0":
|
||||
version "8.40.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz"
|
||||
integrity sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==
|
||||
@@ -1206,7 +886,7 @@
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/types@8.40.0", "@typescript-eslint/types@^8.40.0":
|
||||
"@typescript-eslint/types@^8.40.0", "@typescript-eslint/types@8.40.0":
|
||||
version "8.40.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz"
|
||||
integrity sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==
|
||||
@@ -1259,7 +939,7 @@
|
||||
|
||||
"@xyflow/react@^12.8.4":
|
||||
version "12.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@xyflow/react/-/react-12.8.4.tgz#db0eabd9e356c25f5ebf427413a8c5dd46113394"
|
||||
resolved "https://registry.npmjs.org/@xyflow/react/-/react-12.8.4.tgz"
|
||||
integrity sha512-bqUu4T5QSHiCFPkoH+b+LROKwQJdLvcjhGbNW9c1dLafCBRjmH1IYz0zPE+lRDXCtQ9kRyFxz3tG19+8VORJ1w==
|
||||
dependencies:
|
||||
"@xyflow/system" "0.0.68"
|
||||
@@ -1286,7 +966,7 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.15.0:
|
||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.15.0:
|
||||
version "8.15.0"
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz"
|
||||
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
|
||||
@@ -1347,7 +1027,7 @@ braces@^3.0.3:
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.24.0:
|
||||
browserslist@^4.24.0, "browserslist@>= 4.21.0":
|
||||
version "4.25.3"
|
||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz"
|
||||
integrity sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==
|
||||
@@ -1443,7 +1123,7 @@ csstype@^3.0.2:
|
||||
resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz"
|
||||
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
|
||||
|
||||
"d3-drag@2 - 3", d3-drag@^3.0.0:
|
||||
d3-drag@^3.0.0, "d3-drag@2 - 3":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz"
|
||||
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
|
||||
@@ -1456,14 +1136,14 @@ csstype@^3.0.2:
|
||||
resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz"
|
||||
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
|
||||
|
||||
"d3-interpolate@1 - 3", d3-interpolate@^3.0.1:
|
||||
d3-interpolate@^3.0.1, "d3-interpolate@1 - 3":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz"
|
||||
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||
dependencies:
|
||||
d3-color "1 - 3"
|
||||
|
||||
"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
|
||||
d3-selection@^3.0.0, "d3-selection@2 - 3", d3-selection@3:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz"
|
||||
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
|
||||
@@ -1600,7 +1280,7 @@ eslint-visitor-keys@^4.2.1:
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz"
|
||||
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
|
||||
|
||||
eslint@^9.33.0:
|
||||
"eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9.33.0, eslint@>=8.40:
|
||||
version "9.33.0"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz"
|
||||
integrity sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==
|
||||
@@ -1846,7 +1526,7 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
jiti@^2.5.1:
|
||||
jiti@*, jiti@^2.5.1, jiti@>=1.21.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz"
|
||||
integrity sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==
|
||||
@@ -1903,57 +1583,12 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz"
|
||||
integrity sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==
|
||||
|
||||
lightningcss-darwin-x64@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz#e81105d3fd6330860c15fe860f64d39cff5fbd22"
|
||||
integrity sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==
|
||||
|
||||
lightningcss-freebsd-x64@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz#a0e732031083ff9d625c5db021d09eb085af8be4"
|
||||
integrity sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz#1f5ecca6095528ddb649f9304ba2560c72474908"
|
||||
integrity sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz#eee7799726103bffff1e88993df726f6911ec009"
|
||||
integrity sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz#f2e4b53f42892feeef8f620cbb889f7c064a7dfe"
|
||||
integrity sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz#2fc7096224bc000ebb97eea94aea248c5b0eb157"
|
||||
integrity sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz#66dca2b159fd819ea832c44895d07e5b31d75f26"
|
||||
integrity sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz#7d8110a19d7c2d22bfdf2f2bb8be68e7d1b69039"
|
||||
integrity sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz#fd7dd008ea98494b85d24b4bea016793f2e0e352"
|
||||
resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz"
|
||||
integrity sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==
|
||||
|
||||
lightningcss@1.30.1:
|
||||
lightningcss@^1.21.0, lightningcss@1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz"
|
||||
integrity sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==
|
||||
@@ -2063,7 +1698,7 @@ natural-compare@^1.4.0:
|
||||
|
||||
next-themes@^0.4.6:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.6.tgz#8d7e92d03b8fea6582892a50a928c9b23502e8b6"
|
||||
resolved "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz"
|
||||
integrity sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==
|
||||
|
||||
node-releases@^2.0.19:
|
||||
@@ -2124,7 +1759,7 @@ picomatch@^2.3.1:
|
||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
picomatch@^4.0.3:
|
||||
"picomatch@^3 || ^4", picomatch@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
@@ -2153,7 +1788,7 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
react-dom@^19.1.1:
|
||||
"react-dom@^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", react-dom@^19.1.1, react-dom@>=16.8.0, react-dom@>=17:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz"
|
||||
integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==
|
||||
@@ -2192,7 +1827,7 @@ react-style-singleton@^2.2.2, react-style-singleton@^2.2.3:
|
||||
get-nonce "^1.0.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
react@^19.1.1:
|
||||
"react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", react@^19.1.1, react@>=16.8, react@>=16.8.0, react@>=17, react@>=18.0.0:
|
||||
version "19.1.1"
|
||||
resolved "https://registry.npmjs.org/react/-/react-19.1.1.tgz"
|
||||
integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==
|
||||
@@ -2292,7 +1927,7 @@ tailwind-merge@^3.3.1:
|
||||
resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz"
|
||||
integrity sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==
|
||||
|
||||
tailwindcss@4.1.12, tailwindcss@^4.1.12:
|
||||
tailwindcss@^4.1.12, tailwindcss@4.1.12:
|
||||
version "4.1.12"
|
||||
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz"
|
||||
integrity sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==
|
||||
@@ -2316,7 +1951,7 @@ tar@^7.4.3:
|
||||
|
||||
tinyglobby@^0.2.15:
|
||||
version "0.2.15"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
||||
resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz"
|
||||
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
|
||||
dependencies:
|
||||
fdir "^6.5.0"
|
||||
@@ -2334,7 +1969,7 @@ ts-api-utils@^2.1.0:
|
||||
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz"
|
||||
integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.8.0:
|
||||
tslib@^2.0.0, tslib@^2.1.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
@@ -2361,7 +1996,7 @@ typescript-eslint@^8.39.1:
|
||||
"@typescript-eslint/typescript-estree" "8.40.0"
|
||||
"@typescript-eslint/utils" "8.40.0"
|
||||
|
||||
typescript@~5.8.3:
|
||||
typescript@>=4.8.4, "typescript@>=4.8.4 <6.0.0", typescript@~5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz"
|
||||
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
|
||||
@@ -2401,15 +2036,15 @@ use-sidecar@^1.1.3:
|
||||
detect-node-es "^1.1.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
use-sync-external-store@^1.2.2:
|
||||
use-sync-external-store@^1.2.2, use-sync-external-store@>=1.2.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz"
|
||||
integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==
|
||||
|
||||
vite@^7.1.11:
|
||||
version "7.1.11"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.11.tgz#4d006746112fee056df64985191e846ebfb6007e"
|
||||
integrity sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==
|
||||
"vite@^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vite@^5.2.0 || ^6 || ^7", vite@^7.1.11:
|
||||
version "7.1.12"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz"
|
||||
integrity sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==
|
||||
dependencies:
|
||||
esbuild "^0.25.0"
|
||||
fdir "^6.5.0"
|
||||
@@ -2456,5 +2091,5 @@ zustand@^4.4.0:
|
||||
|
||||
zustand@^5.0.8:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.8.tgz#b998a0c088c7027a20f2709141a91cb07ac57f8a"
|
||||
resolved "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz"
|
||||
integrity sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==
|
||||
|
||||
Reference in New Issue
Block a user