diff --git a/apps/web/components.json b/apps/web/components.json index 7a96948..4d83704 100644 --- a/apps/web/components.json +++ b/apps/web/components.json @@ -12,7 +12,7 @@ }, "iconLibrary": "lucide", "aliases": { - "components": "@/components", + "components": "@code/ui/components", "utils": "@code/ui/lib/utils", "ui": "@code/ui/components", "lib": "@/lib", diff --git a/apps/web/package.json b/apps/web/package.json index 054c429..e44dc1e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,7 +7,7 @@ "dev": "react-router dev", "dev:tailscale": "react-router dev --host 0.0.0.0", "start": "react-router-serve ./build/server/index.js", - "typecheck": "react-router typegen && tsc" + "check-types": "react-router typegen && tsc --noEmit" }, "dependencies": { "@code/auth": "workspace:*", @@ -40,7 +40,6 @@ "tailwindcss": "catalog:", "typescript": "catalog:", "vite": "catalog:", - "vitest": "catalog:", - "vite-tsconfig-paths": "^6.1.1" + "vitest": "catalog:" } } diff --git a/apps/web/src/components/chat/assistant-identity.tsx b/apps/web/src/components/chat/assistant-identity.tsx index 05f6314..47038ef 100644 --- a/apps/web/src/components/chat/assistant-identity.tsx +++ b/apps/web/src/components/chat/assistant-identity.tsx @@ -1,5 +1,4 @@ -import { MessageHeader } from "@code/ui/components/message"; -import { Sparkles } from "lucide-react"; +import { MobileChatAssistantLabel } from "@code/ui/components/mobile-chat"; import type { AssistantResponseState } from "@/lib/chat/types"; @@ -8,20 +7,18 @@ interface AssistantIdentityProps { } export const AssistantIdentity = ({ state }: AssistantIdentityProps) => ( - - - - - Zopu - {state && ( - - - {state === "thinking" ? "Thinking" : "Writing"} - - )} - + ) : undefined + } + /> ); diff --git a/apps/web/src/components/chat/chat-composer.tsx b/apps/web/src/components/chat/chat-composer.tsx index 6c0fad8..2276af5 100644 --- a/apps/web/src/components/chat/chat-composer.tsx +++ b/apps/web/src/components/chat/chat-composer.tsx @@ -1,10 +1,5 @@ -import { - InputGroup, - InputGroupAddon, - InputGroupButton, - InputGroupTextarea, -} from "@code/ui/components/input-group"; -import { CircleAlert, LoaderCircle, Send } from "lucide-react"; +import { MobileChatComposer } from "@code/ui/components/mobile-chat"; +import { LoaderCircle } from "lucide-react"; import type { ReactNode } from "react"; import { useChatComposer } from "@/hooks/chat/use-chat-composer"; @@ -15,90 +10,31 @@ export const ChatComposer = ({ error, onSend, status }: ChatComposerProps) => { status === "connecting" || status === "submitted" || status === "streaming"; const composer = useChatComposer({ busy, onSend }); - let statusContent: ReactNode = ( - - Enter to send · Shift + Enter for a new line - - ); + let statusMessage: ReactNode; if (busy) { - statusContent = ( - <> - + statusMessage = ( + + {status === "connecting" ? "Preparing Zopu" : "Zopu is responding"} - - ); - } - if (error) { - statusContent = ( - - - {error.message || "Message failed to send"} ); } return ( -
-
- - - - - {busy ? ( - - ) : ( - - )} - - - -
- {composer.isFocused || busy || error ? ( - {statusContent} - ) : ( - Zopu can make mistakes. Check important information. - )} -
-
-
+ ); }; diff --git a/apps/web/src/components/chat/chat-conversation.tsx b/apps/web/src/components/chat/chat-conversation.tsx index 686e675..98f9c59 100644 --- a/apps/web/src/components/chat/chat-conversation.tsx +++ b/apps/web/src/components/chat/chat-conversation.tsx @@ -1,15 +1,6 @@ +import { ConversationEmptyState } from "@code/ui/components/ai-elements/conversation"; import { Button } from "@code/ui/components/button"; -import { - Empty, - EmptyContent, - EmptyDescription, - EmptyHeader, - EmptyMedia, - EmptyTitle, -} from "@code/ui/components/empty"; -import { MessageGroup } from "@code/ui/components/message"; -import { MessageScrollerItem } from "@code/ui/components/message-scroller"; -import { Sparkles } from "lucide-react"; +import { MobileChatMark } from "@code/ui/components/mobile-chat"; import { SUGGESTIONS } from "@/lib/chat/constants"; import type { ChatConversationProps } from "@/lib/chat/types"; @@ -25,23 +16,21 @@ export const ChatConversation = ({ }: ChatConversationProps) => { if (historyReady && messages.length === 0) { return ( - - - - - - + + +
+

What can I help with? - - +

+

Think, write, plan, or explore an idea with Zopu. - - - +

+
+
{SUGGESTIONS.map(([title, prompt]) => ( ))} - - +
+
); } return ( - - {messages.map((message, index) => ( - - - +
+ {messages.map((message) => ( + ))} - {status === "submitted" && ( - - - - )} - + {status === "submitted" && } +
); }; diff --git a/apps/web/src/components/chat/chat-header.tsx b/apps/web/src/components/chat/chat-header.tsx index d2bb0fd..eca9509 100644 --- a/apps/web/src/components/chat/chat-header.tsx +++ b/apps/web/src/components/chat/chat-header.tsx @@ -1,32 +1,11 @@ -import { Sparkles } from "lucide-react"; +import { MobileChatHeader } from "@code/ui/components/mobile-chat"; -import { MODEL_LABEL, STATUS_COPY } from "@/lib/chat/constants"; -import { getStatusDotClass } from "@/lib/chat/transforms"; +import { STATUS_COPY } from "@/lib/chat/constants"; import type { ChatHeaderProps } from "@/lib/chat/types"; export const ChatHeader = ({ status }: ChatHeaderProps) => ( -
-
-
-
- -
-
-

- Zopu -

-

-

-
-
-

- {MODEL_LABEL} -

-
-
+ ); diff --git a/apps/web/src/components/chat/chat-message.tsx b/apps/web/src/components/chat/chat-message.tsx index 71d0235..36e0ecf 100644 --- a/apps/web/src/components/chat/chat-message.tsx +++ b/apps/web/src/components/chat/chat-message.tsx @@ -1,7 +1,13 @@ -import { Bubble, BubbleContent } from "@code/ui/components/bubble"; -import { Message, MessageContent } from "@code/ui/components/message"; +import { + Message, + MessageContent, + MessageResponse, +} from "@code/ui/components/ai-elements/message"; +import { + MobileChatBubble, + MobileChatMessage, +} from "@code/ui/components/mobile-chat"; import type { ReactNode } from "react"; -import { Streamdown } from "streamdown"; import { getMessageText, isMessageStreaming } from "@/lib/chat/transforms"; import type { ChatMessageProps } from "@/lib/chat/types"; @@ -21,13 +27,13 @@ export const ChatMessage = ({ message }: ChatMessageProps) => { let content: ReactNode = text; if (!isUser) { content = text ? ( - {text} - + ) : (
@@ -37,41 +43,29 @@ export const ChatMessage = ({ message }: ChatMessageProps) => { ); } + const sender = isUser ? "user" : "assistant"; + return ( - - - {!isUser && ( - - )} - - - {message.parts.map((part) => - part.type === "dynamic-tool" ? ( - - ) : null + + + +
+ {!isUser && ( + )} - {content} - - + + {message.parts.map((part) => + part.type === "dynamic-tool" ? ( + + ) : null + )} + {content} + +
+
); diff --git a/apps/web/src/components/chat/chat-page.tsx b/apps/web/src/components/chat/chat-page.tsx index 6b4ca76..7d08176 100644 --- a/apps/web/src/components/chat/chat-page.tsx +++ b/apps/web/src/components/chat/chat-page.tsx @@ -1,10 +1,8 @@ import { - MessageScroller, - MessageScrollerButton, - MessageScrollerContent, - MessageScrollerProvider, - MessageScrollerViewport, -} from "@code/ui/components/message-scroller"; + Conversation, + ConversationContent, + ConversationScrollButton, +} from "@code/ui/components/ai-elements/conversation"; import { useChatAgent } from "@/hooks/chat/use-chat-agent"; @@ -17,36 +15,24 @@ export const ChatPage = () => { const handleSendMessage = (message: string) => agent.sendMessage(message); return ( -
+
- - - - - - void handleSendMessage(suggestion) - } - status={agent.status} - /> - - - + + void handleSendMessage(suggestion)} + status={agent.status} /> - - + + +
( - - - -
- - - -
+ + + +
+ + +
+ + + +
+
+
+
); diff --git a/apps/web/src/components/chat/chat-tool-call.tsx b/apps/web/src/components/chat/chat-tool-call.tsx index ab98098..37a87c8 100644 --- a/apps/web/src/components/chat/chat-tool-call.tsx +++ b/apps/web/src/components/chat/chat-tool-call.tsx @@ -1,4 +1,11 @@ -import { Check, LoaderCircle, Terminal, TriangleAlert } from "lucide-react"; +import { + Tool, + ToolContent, + ToolInput, + ToolOutput, +} from "@code/ui/components/ai-elements/tool"; +import { MobileChatToolCall } from "@code/ui/components/mobile-chat"; +import { Search, SquareTerminal } from "lucide-react"; import type { ChatToolCallProps } from "@/lib/chat/types"; @@ -7,35 +14,47 @@ export const ChatToolCall = ({ part }: ChatToolCallProps) => { typeof part.input === "string" ? part.input : JSON.stringify(part.input, null, 2); - let Icon = LoaderCircle; - let status = "Running"; + let status = "running"; + let tone: "error" | "neutral" | "success" = "neutral"; + if (part.state === "output-available") { detail = typeof part.output === "string" ? part.output : JSON.stringify(part.output, null, 2); - Icon = Check; - status = "Completed"; + status = "done"; + tone = "success"; } else if (part.state === "output-error") { detail = part.errorText; - Icon = TriangleAlert; - status = "Failed"; + status = "failed"; + tone = "error"; } - const isRunning = part.state === "input-available"; + + const isSearch = part.toolName.toLowerCase().includes("search"); + const output = part.state === "output-available" ? part.output : undefined; + const errorText = part.state === "output-error" ? part.errorText : undefined; return ( -
- - - - {part.toolName} - - {status} - - -
-        {detail}
-      
-
+ + + + ) : ( + + ) + } + status={status} + tone={tone} + toolName={part.toolName} + /> +
+ + +
+
+
); }; diff --git a/apps/web/src/components/mobile-workspace/mobile-flow-page.tsx b/apps/web/src/components/mobile-workspace/mobile-flow-page.tsx new file mode 100644 index 0000000..9f92ecd --- /dev/null +++ b/apps/web/src/components/mobile-workspace/mobile-flow-page.tsx @@ -0,0 +1,62 @@ +import { + MobileAssistantChatScreen, + MobileExpandedWorkScreen, + MobileHomeScreen, + MobileWorkListScreen, + MobileWorkUnitDetailScreen, +} from "@code/ui/components/mobile-product"; +import { useNavigate } from "react-router"; + +import { useMobileWorkspace } from "@/hooks/use-mobile-workspace"; + +export type MobileFlowScreen = + | "assistant-chat" + | "home" + | "work-list" + | "work-unit-detail"; + +interface MobileFlowPageProps { + screen: MobileFlowScreen; +} + +export const MobileFlowPage = ({ screen }: MobileFlowPageProps) => { + const navigate = useNavigate(); + const workspace = useMobileWorkspace(); + const homePath = "/"; + const workPath = "/work"; + const chatPath = "/chat"; + const detailPath = "/work/flow-08"; + + const handleOpenUnit = () => { + if (screen === "work-list" && !workspace.expanded) { + workspace.setExpanded(true); + return; + } + navigate(detailPath); + }; + + const screenProps = { + composerValue: workspace.composerValue, + onBack: () => navigate(homePath), + onComposerChange: workspace.handleComposerChange, + onComposerSubmit: workspace.handleComposerSubmit, + onOpenAssistant: () => navigate(chatPath), + onOpenUnit: handleOpenUnit, + onViewWork: () => navigate(workPath), + statusMessage: workspace.statusMessage, + }; + + if (screen === "assistant-chat") { + return ; + } + if (screen === "home") { + return ; + } + if (screen === "work-unit-detail") { + return ; + } + if (workspace.expanded) { + return ; + } + return ; +}; diff --git a/apps/web/src/hooks/use-mobile-workspace.ts b/apps/web/src/hooks/use-mobile-workspace.ts new file mode 100644 index 0000000..ff19753 --- /dev/null +++ b/apps/web/src/hooks/use-mobile-workspace.ts @@ -0,0 +1,35 @@ +import type { FormEvent } from "react"; +import { useState } from "react"; + +export const useMobileWorkspace = (initialExpanded = false) => { + const [activeIndex, setActiveIndex] = useState(0); + const [composerValue, setComposerValue] = useState(""); + const [expanded, setExpanded] = useState(initialExpanded); + const [statusMessage, setStatusMessage] = useState(); + + const handleComposerChange = (value: string) => { + setComposerValue(value); + setStatusMessage(undefined); + }; + + const handleComposerSubmit = (event: FormEvent) => { + event.preventDefault(); + const message = composerValue.trim(); + if (!message) { + return; + } + setComposerValue(""); + setStatusMessage("Added to Zopu’s queue"); + }; + + return { + activeIndex, + composerValue, + expanded, + handleActiveIndexChange: setActiveIndex, + handleComposerChange, + handleComposerSubmit, + setExpanded, + statusMessage, + }; +}; diff --git a/apps/web/src/index.css b/apps/web/src/index.css index d8ef689..779a563 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -4,49 +4,10 @@ html, body { min-height: 100%; - overflow: hidden; } -.chat-surface { - position: relative; - isolation: isolate; - background: - radial-gradient( - circle at 50% -12%, - oklch(0.72 0.045 255 / 9%), - transparent 32rem - ), - var(--background); -} - -.chat-surface::before { - position: absolute; - z-index: -1; - inset: 0; - background-image: radial-gradient( - oklch(0.5 0 0 / 9%) 0.55px, - transparent 0.55px - ); - background-size: 18px 18px; - mask-image: linear-gradient(to bottom, black, transparent 42%); - content: ""; - pointer-events: none; -} - -.chat-header { - background: color-mix(in oklch, var(--background) 88%, transparent); - backdrop-filter: blur(18px) saturate(1.25); -} - -.zopu-mark { - border: 1px solid color-mix(in oklch, var(--foreground) 10%, transparent); - background: - linear-gradient(145deg, oklch(1 0 0 / 65%), transparent 55%), - color-mix(in oklch, var(--muted) 88%, var(--background)); - color: var(--foreground); - box-shadow: - inset 0 1px 0 oklch(1 0 0 / 48%), - 0 6px 18px -12px oklch(0 0 0 / 55%); +.ai-conversation-mobile > div { + scrollbar-gutter: auto !important; } .chat-message { @@ -120,15 +81,6 @@ body { } } -.composer-dock { - position: relative; - background: linear-gradient(to top, var(--background) 68%, transparent); -} - -.composer-shell { - transform: translateZ(0); -} - @keyframes chat-message-enter { from { opacity: 0; diff --git a/apps/web/src/root.tsx b/apps/web/src/root.tsx index eb98fe3..f24caa7 100644 --- a/apps/web/src/root.tsx +++ b/apps/web/src/root.tsx @@ -68,7 +68,7 @@ export const Layout = ({ children }: { children: React.ReactNode }) => ( - + {children} @@ -81,13 +81,12 @@ const App = () => ( -
- -
+
diff --git a/apps/web/src/routes.ts b/apps/web/src/routes.ts index 30b5e6f..0898540 100644 --- a/apps/web/src/routes.ts +++ b/apps/web/src/routes.ts @@ -1,4 +1,19 @@ +import { index, layout, route } from "@react-router/dev/routes"; import type { RouteConfig } from "@react-router/dev/routes"; -import { flatRoutes } from "@react-router/fs-routes"; -export default flatRoutes() satisfies RouteConfig; +export default [ + layout("./routes/auth/layout.tsx", [ + route("login", "./routes/auth/login/page.tsx"), + route("signup", "./routes/auth/signup/page.tsx"), + ]), + layout("./routes/app/layout.tsx", [ + layout("./routes/app/mobile/layout.tsx", [ + index("./routes/app/mobile/page.tsx"), + route("chat", "./routes/app/mobile/chat/page.tsx"), + route("work", "./routes/app/mobile/work-list/page.tsx"), + route("work/:workUnitId", "./routes/app/mobile/work/page.tsx"), + ]), + route("dashboard", "./routes/app/dashboard/page.tsx"), + route("todos", "./routes/app/todos/page.tsx"), + ]), +] satisfies RouteConfig; diff --git a/apps/web/src/routes/_app._index.tsx b/apps/web/src/routes/_app._index.tsx deleted file mode 100644 index 9b7eaec..0000000 --- a/apps/web/src/routes/_app._index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ChatPage } from "@/components/chat/chat-page"; - -import type { Route } from "./+types/_app._index"; - -export const meta = (_args: Route.MetaArgs) => [ - { title: "Zopu" }, - { content: "Chat with Zopu", name: "description" }, -]; - -const Home = () => ; - -export default Home; diff --git a/apps/web/src/routes/_app.todos.tsx b/apps/web/src/routes/_app.todos.tsx deleted file mode 100644 index 9bc6699..0000000 --- a/apps/web/src/routes/_app.todos.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { api } from "@code/backend/convex/_generated/api"; -import type { Id } from "@code/backend/convex/_generated/dataModel"; -import { Button } from "@code/ui/components/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@code/ui/components/card"; -import { Checkbox } from "@code/ui/components/checkbox"; -import { Input } from "@code/ui/components/input"; -import { useMutation, useQuery } from "convex/react"; -import { Loader2, Trash2 } from "lucide-react"; -import { useState, type FormEvent } from "react"; - -export default function Todos() { - const [newTodoText, setNewTodoText] = useState(""); - - const todos = useQuery(api.todos.getAll); - const createTodo = useMutation(api.todos.create); - const toggleTodo = useMutation(api.todos.toggle); - const deleteTodo = useMutation(api.todos.deleteTodo); - - const handleAddTodo = async (e: FormEvent) => { - e.preventDefault(); - const text = newTodoText.trim(); - if (!text) return; - await createTodo({ text }); - setNewTodoText(""); - }; - - const handleToggleTodo = (id: Id<"todos">, currentCompleted: boolean) => { - toggleTodo({ id, completed: !currentCompleted }); - }; - - const handleDeleteTodo = (id: Id<"todos">) => { - deleteTodo({ id }); - }; - - return ( -
- - - Todo List - Manage your tasks efficiently - - -
- setNewTodoText(e.target.value)} - placeholder="Add a new task..." - /> - -
- - {todos === undefined ? ( -
- -
- ) : todos.length === 0 ? ( -

No todos yet. Add one above!

- ) : ( -
    - {todos.map((todo) => ( -
  • -
    - handleToggleTodo(todo._id, todo.completed)} - id={`todo-${todo._id}`} - /> - -
    - -
  • - ))} -
- )} -
-
-
- ); -} diff --git a/apps/web/src/routes/_app.dashboard.tsx b/apps/web/src/routes/app/dashboard/page.tsx similarity index 100% rename from apps/web/src/routes/_app.dashboard.tsx rename to apps/web/src/routes/app/dashboard/page.tsx diff --git a/apps/web/src/routes/_app.tsx b/apps/web/src/routes/app/layout.tsx similarity index 100% rename from apps/web/src/routes/_app.tsx rename to apps/web/src/routes/app/layout.tsx diff --git a/apps/web/src/routes/app/mobile/chat/page.tsx b/apps/web/src/routes/app/mobile/chat/page.tsx new file mode 100644 index 0000000..1326aab --- /dev/null +++ b/apps/web/src/routes/app/mobile/chat/page.tsx @@ -0,0 +1,12 @@ +import { MobileFlowPage } from "@/components/mobile-workspace/mobile-flow-page"; + +import type { Route } from "./+types/page"; + +export const meta = (_args: Route.MetaArgs) => [ + { title: "Zopu" }, + { content: "Chat with Zopu", name: "description" }, +]; + +export default function MobileChatPage() { + return ; +} diff --git a/apps/web/src/routes/app/mobile/layout.tsx b/apps/web/src/routes/app/mobile/layout.tsx new file mode 100644 index 0000000..ae63b03 --- /dev/null +++ b/apps/web/src/routes/app/mobile/layout.tsx @@ -0,0 +1,9 @@ +import { Outlet } from "react-router"; + +export default function MobileProductLayout() { + return ( +
+ +
+ ); +} diff --git a/apps/web/src/routes/app/mobile/page.tsx b/apps/web/src/routes/app/mobile/page.tsx new file mode 100644 index 0000000..4e058d8 --- /dev/null +++ b/apps/web/src/routes/app/mobile/page.tsx @@ -0,0 +1,15 @@ +import { MobileFlowPage } from "@/components/mobile-workspace/mobile-flow-page"; + +import type { Route } from "./+types/page"; + +export const meta = (_args: Route.MetaArgs) => [ + { title: "Daily focus | Zopu" }, + { + content: "Supervise active work and open the next action", + name: "description", + }, +]; + +export default function MobileHomePage() { + return ; +} diff --git a/apps/web/src/routes/app/mobile/work-list/page.tsx b/apps/web/src/routes/app/mobile/work-list/page.tsx new file mode 100644 index 0000000..95421d2 --- /dev/null +++ b/apps/web/src/routes/app/mobile/work-list/page.tsx @@ -0,0 +1,15 @@ +import { MobileFlowPage } from "@/components/mobile-workspace/mobile-flow-page"; + +import type { Route } from "./+types/page"; + +export const meta = (_args: Route.MetaArgs) => [ + { title: "Active work | Zopu" }, + { + content: "Browse active work and expand a work unit in place", + name: "description", + }, +]; + +export default function MobileWorkListPage() { + return ; +} diff --git a/apps/web/src/routes/app/mobile/work/page.tsx b/apps/web/src/routes/app/mobile/work/page.tsx new file mode 100644 index 0000000..27a88fb --- /dev/null +++ b/apps/web/src/routes/app/mobile/work/page.tsx @@ -0,0 +1,15 @@ +import { MobileFlowPage } from "@/components/mobile-workspace/mobile-flow-page"; + +import type { Route } from "./+types/page"; + +export const meta = (_args: Route.MetaArgs) => [ + { title: "Work unit | Zopu" }, + { + content: "Review the state, next milestone, and activity for a work unit", + name: "description", + }, +]; + +export default function MobileWorkUnitPage() { + return ; +} diff --git a/apps/web/src/routes/app/todos/page.tsx b/apps/web/src/routes/app/todos/page.tsx new file mode 100644 index 0000000..ce4be3c --- /dev/null +++ b/apps/web/src/routes/app/todos/page.tsx @@ -0,0 +1,123 @@ +import { api } from "@code/backend/convex/_generated/api"; +import type { Id } from "@code/backend/convex/_generated/dataModel"; +import { Button } from "@code/ui/components/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@code/ui/components/card"; +import { Checkbox } from "@code/ui/components/checkbox"; +import { Input } from "@code/ui/components/input"; +import { useMutation, useQuery } from "convex/react"; +import { Loader2, Trash2 } from "lucide-react"; +import { useState } from "react"; +import type { FormEvent, ReactNode } from "react"; + +export default function Todos() { + const [newTodoText, setNewTodoText] = useState(""); + + const todos = useQuery(api.todos.getAll); + const createTodo = useMutation(api.todos.create); + const toggleTodo = useMutation(api.todos.toggle); + const deleteTodo = useMutation(api.todos.deleteTodo); + + const handleAddTodo = async (event: FormEvent) => { + event.preventDefault(); + const text = newTodoText.trim(); + if (!text) { + return; + } + await createTodo({ text }); + setNewTodoText(""); + }; + + const handleToggleTodo = (id: Id<"todos">, currentCompleted: boolean) => { + void toggleTodo({ completed: !currentCompleted, id }); + }; + + const handleDeleteTodo = (id: Id<"todos">) => { + void deleteTodo({ id }); + }; + + let todoContent: ReactNode; + if (todos === undefined) { + todoContent = ( +
+ +
+ ); + } else if (todos.length === 0) { + todoContent = ( +

No todos yet. Add one above!

+ ); + } else { + todoContent = ( +
    + {todos.map((todo) => ( +
  • +
    + + handleToggleTodo(todo._id, todo.completed) + } + /> + +
    + +
  • + ))} +
+ ); + } + + return ( +
+ + + Todo List + Manage your tasks efficiently + + +
+ setNewTodoText(event.target.value)} + placeholder="Add a new task..." + value={newTodoText} + /> + +
+ + {todoContent} +
+
+
+ ); +} diff --git a/apps/web/src/routes/_auth.tsx b/apps/web/src/routes/auth/layout.tsx similarity index 100% rename from apps/web/src/routes/_auth.tsx rename to apps/web/src/routes/auth/layout.tsx diff --git a/apps/web/src/routes/_auth.login.tsx b/apps/web/src/routes/auth/login/page.tsx similarity index 88% rename from apps/web/src/routes/_auth.login.tsx rename to apps/web/src/routes/auth/login/page.tsx index 676bdf9..167dfcc 100644 --- a/apps/web/src/routes/_auth.login.tsx +++ b/apps/web/src/routes/auth/login/page.tsx @@ -1,7 +1,7 @@ import { LoginForm } from "@code/auth/web"; import { useNavigate } from "react-router"; -import type { Route } from "./+types/_auth.login"; +import type { Route } from "./+types/page"; export const meta = (_args: Route.MetaArgs) => [ { title: "Sign in | Zopu" }, diff --git a/apps/web/src/routes/_auth.signup.tsx b/apps/web/src/routes/auth/signup/page.tsx similarity index 88% rename from apps/web/src/routes/_auth.signup.tsx rename to apps/web/src/routes/auth/signup/page.tsx index 4bb1dee..aad7dbe 100644 --- a/apps/web/src/routes/_auth.signup.tsx +++ b/apps/web/src/routes/auth/signup/page.tsx @@ -1,7 +1,7 @@ import { SignupForm } from "@code/auth/web"; import { useNavigate } from "react-router"; -import type { Route } from "./+types/_auth.signup"; +import type { Route } from "./+types/page"; export const meta = (_args: Route.MetaArgs) => [ { title: "Create account | Zopu" }, diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 2b39733..02947ff 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -3,12 +3,12 @@ import path from "node:path"; import { reactRouter } from "@react-router/dev/vite"; import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite-plus"; -import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ envDir: path.resolve(import.meta.dirname, "../.."), - plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], + plugins: [tailwindcss(), reactRouter()], resolve: { dedupe: ["convex", "react", "react-dom"], + tsconfigPaths: true, }, }); diff --git a/bun.lock b/bun.lock index e142821..6900f42 100644 --- a/bun.lock +++ b/bun.lock @@ -139,7 +139,6 @@ "tailwindcss": "catalog:", "typescript": "catalog:", "vite": "catalog:", - "vite-tsconfig-paths": "^6.1.1", "vitest": "catalog:", }, }, @@ -248,6 +247,11 @@ "dependencies": { "@base-ui/react": "^1.6.0", "@shadcn/react": "^0.2.0", + "@streamdown/cjk": "^1.0.3", + "@streamdown/code": "^1.1.1", + "@streamdown/math": "^1.0.2", + "@streamdown/mermaid": "^1.0.2", + "ai": "^7.0.35", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "catalog:", @@ -255,9 +259,12 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "shadcn": "^4.12.0", + "shiki": "^4.3.1", "sonner": "catalog:", + "streamdown": "^2.5.0", "tailwind-merge": "catalog:", "tw-animate-css": "^1.4.0", + "use-stick-to-bottom": "^1.1.6", }, "devDependencies": { "@code/config": "workspace:*", @@ -330,6 +337,12 @@ "@agentos-software/tar": ["@agentos-software/tar@0.3.4", "", {}, "sha512-/SSqfgS5xOufvulsz5kVDTCFxpDy+5s7Og1iJe9krjU7Jvtgnp9TpPaJnSr721h6uY6tsWHcs3u8E457EwSkNw=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.12", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gqTMvV0N8/JirIZ3OzwjSZRYxzwZu/PeOFCKb8NB9fstWH39tI+L6CkeMNVou5/HCKEYAw6RCOHW59Vhquv8vA=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@4.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.12", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bbhlOgHeYwrIGheLkM6fhS8hVger8uFPmcOLg+kxc9EFh7y30XYorWhthlYAgpadO3SJhFZrIcEknN7qEqEVvA=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], @@ -1346,6 +1359,22 @@ "@shadcn/react": ["@shadcn/react@0.2.1", "", { "peerDependencies": { "@types/react": ">=19", "react": ">=19" }, "optionalPeers": ["@types/react", "react"] }, "sha512-5krgi3dRMKb5jH6a+qPzVJUy/54s0kKE4Rw4LjDfLqOdVQTWKUgxWf1kW8r912I0jX/Lzxqc+pgjkjWxUIK5BQ=="], + "@shikijs/core": ["@shikijs/core@4.3.1", "", { "dependencies": { "@shikijs/primitive": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg=="], + + "@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A=="], + + "@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="], + + "@shikijs/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@silvia-odwyer/photon-node": ["@silvia-odwyer/photon-node@0.3.4", "", {}, "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA=="], "@sinclair/typebox": ["@sinclair/typebox@0.34.52", "", {}, "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw=="], @@ -1392,6 +1421,14 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@streamdown/cjk": ["@streamdown/cjk@1.0.3", "", { "dependencies": { "remark-cjk-friendly": "^2.0.1", "remark-cjk-friendly-gfm-strikethrough": "^2.0.1", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-WRg8HR/gHbBoTgsMd91OKFUClIoDcEFVofJvluvEAyjx3KpU0aGgD9tGDqHkHj14ShoMSkX0IYetWGegTcwIJw=="], + + "@streamdown/code": ["@streamdown/code@1.1.1", "", { "dependencies": { "shiki": "^3.19.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-i7HTNuDgZWb+VdrNVOam9gQhIc5MSSDXKWXgbUrn/4vSRaSMM+Rtl10MQj4wLWPNpF7p80waJsAqFP8HZfb0Jg=="], + + "@streamdown/math": ["@streamdown/math@1.0.2", "", { "dependencies": { "katex": "^0.16.27", "rehype-katex": "^7.0.1", "remark-math": "^6.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-r8Ur9/lBuFnzZAFdEWrLUF2s/gRwRRRwruqltdZibyjbCBnuW7SJbFm26nXqvpJPW/gzpBUMrBVBzd88z05D5g=="], + + "@streamdown/mermaid": ["@streamdown/mermaid@1.0.2", "", { "dependencies": { "mermaid": "^11.12.2" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-Fr/4sBWnAeSnxM3PcrV/+DiZe5oPMq9gOkUIAH7ZauJeuwrZ/DVzD4g0zlav6AH0axh2m/sOfrfLtY5aLT7niw=="], + "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], @@ -1560,6 +1597,8 @@ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="], + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mime-types": ["@types/mime-types@2.1.4", "", {}, "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w=="], @@ -1618,6 +1657,8 @@ "@vercel/detect-agent": ["@vercel/detect-agent@1.2.3", "", {}, "sha512-VYNCgUc0nOmC4WJmWw9GkrKdfr8Zl4/rxhC5SvgacBgxiW9W/9NRttUoHHXV8xdII3MaRgkZZVX8Ikzc/Jmjag=="], + "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], + "@vitest/browser": ["@vitest/browser@4.1.9", "", { "dependencies": { "@blazediff/core": "1.9.1", "@vitest/mocker": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.1.0", "ws": "^8.19.0" }, "peerDependencies": { "vitest": "4.1.9" } }, "sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg=="], "@vitest/browser-preview": ["@vitest/browser-preview@4.1.9", "", { "dependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/user-event": "^14.6.1", "@vitest/browser": "4.1.9" }, "peerDependencies": { "vitest": "4.1.9" } }, "sha512-a4/OrkMDb/WUnE4OOB/4FJbK3rYVO7YykqtUgcTKG4p2a0R3XcjPVu7SLRHFBs2+NIYhv5yxp1Lz3dbdGBjIow=="], @@ -1654,6 +1695,8 @@ "@voidzero-dev/vite-plus-win32-x64-msvc": ["@voidzero-dev/vite-plus-win32-x64-msvc@0.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-+VUui1OIaFX0tqdUAXjmoKVlujEtWVdcsFDw2Jff+D6b4LUTQaOMAaaic8nNdfZL6wEjweRREQLZi/icZAXtNQ=="], + "@workflow/serde": ["@workflow/serde@4.1.0", "", {}, "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ=="], + "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], "@xterm/headless": ["@xterm/headless@6.0.0", "", {}, "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw=="], @@ -1670,6 +1713,8 @@ "agent-cli-detector": ["agent-cli-detector@0.1.4", "", { "bin": { "agent-cli-detector": "dist/cli.js" } }, "sha512-qPgevFvpaQoBaRJVKzr8R7h1WPvV3DtbgRIQlne4le66KBzXx5hNBwo/+NTw67LgkKBlhCzksrdautpUdlls0Q=="], + "ai": ["ai@7.0.36", "", { "dependencies": { "@ai-sdk/gateway": "4.0.27", "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.12" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-1XJjua58GVQ0CyO2Xbioyladt85x71Joup2U8qKrjHUl8tHYwrDw8iFRtav6e94AxSVCn9FVgTS17oX1OCquKA=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -2016,7 +2061,7 @@ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], - "d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], @@ -2156,7 +2201,7 @@ "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], @@ -2402,8 +2447,6 @@ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], - "goober": ["goober@2.1.19", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg=="], "google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="], @@ -2436,18 +2479,30 @@ "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + "hast-util-from-dom": ["hast-util-from-dom@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hastscript": "^9.0.0", "web-namespaces": "^2.0.0" } }, "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q=="], + + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], + + "hast-util-from-html-isomorphic": ["hast-util-from-html-isomorphic@2.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-dom": "^5.0.0", "hast-util-from-html": "^2.0.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw=="], + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], + "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], @@ -2616,6 +2671,8 @@ "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -2722,7 +2779,7 @@ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], - "marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], + "marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], "marky": ["marky@1.3.0", "", {}, "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ=="], @@ -2746,6 +2803,8 @@ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + "mdast-util-math": ["mdast-util-math@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "longest-streak": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.1.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w=="], + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], @@ -2758,6 +2817,10 @@ "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + "mdast-util-to-markdown-cjk-friendly": ["mdast-util-to-markdown-cjk-friendly@1.0.0", "", { "dependencies": { "mdast-util-to-markdown": "^2.1.2", "micromark-extension-cjk-friendly-util": "3.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "@types/mdast": "*" }, "optionalPeers": ["@types/mdast"] }, "sha512-BoaAm8mlJ+LAYz0Qs532Y3ciTuQYgBUPZcSFbvC/ZKmEMAKgulw84YvQK1gI34t/vL2euSfuaWlqczkTBgamkw=="], + + "mdast-util-to-markdown-cjk-friendly-gfm-strikethrough": ["mdast-util-to-markdown-cjk-friendly-gfm-strikethrough@1.0.0", "", { "dependencies": { "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-to-markdown": "^2.1.2", "micromark-extension-cjk-friendly-util": "3.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "@types/mdast": "*" }, "optionalPeers": ["@types/mdast"] }, "sha512-1ePVfB4P/vz3xSsm6H3D32r6VYGErxclnuLLFK02/2ReF+UdEKm7caulK6Vm0LBIp5gPRtB2Z1OYDznCkX3k2w=="], + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], "mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="], @@ -2808,6 +2871,12 @@ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + "micromark-extension-cjk-friendly": ["micromark-extension-cjk-friendly@2.0.1", "", { "dependencies": { "devlop": "^1.1.0", "micromark-extension-cjk-friendly-util": "3.0.1", "micromark-util-chunked": "^2.0.1", "micromark-util-resolve-all": "^2.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "micromark": "^4.0.0", "micromark-util-types": "^2.0.0" }, "optionalPeers": ["micromark-util-types"] }, "sha512-OkzoYVTL1ChbvQ8Cc1ayTIz7paFQz8iS9oIYmewncweUSwmWR+hkJF9spJ1lxB90XldJl26A1F4IkPOKS3bDXw=="], + + "micromark-extension-cjk-friendly-gfm-strikethrough": ["micromark-extension-cjk-friendly-gfm-strikethrough@2.0.1", "", { "dependencies": { "devlop": "^1.1.0", "get-east-asian-width": "^1.4.0", "micromark-extension-cjk-friendly-util": "3.0.1", "micromark-util-character": "^2.1.1", "micromark-util-chunked": "^2.0.1", "micromark-util-resolve-all": "^2.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "micromark": "^4.0.0", "micromark-util-types": "^2.0.0" }, "optionalPeers": ["micromark-util-types"] }, "sha512-wVC0zwjJNqQeX+bb07YTPu/CvSAyCTafyYb7sMhX1r62/Lw5M/df3JyYaANyp8g15c1ypJRFSsookTqA1IDsUg=="], + + "micromark-extension-cjk-friendly-util": ["micromark-extension-cjk-friendly-util@3.0.1", "", { "dependencies": { "get-east-asian-width": "^1.4.0", "micromark-util-character": "^2.1.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "micromark-util-types": "*" }, "optionalPeers": ["micromark-util-types"] }, "sha512-GcbXqTTHOsiZHyF753oIddP/J2eH8j9zpyQPhkof6B2JNxfEJabnQqxbCgzJNuNes0Y2jTNJ3LiYPSXr6eJA8w=="], + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], @@ -2822,6 +2891,8 @@ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + "micromark-extension-math": ["micromark-extension-math@3.1.0", "", { "dependencies": { "@types/katex": "^0.16.0", "devlop": "^1.0.0", "katex": "^0.16.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg=="], + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], @@ -2992,6 +3063,10 @@ "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], + "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], "openai": ["openai@6.26.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA=="], @@ -3266,6 +3341,12 @@ "regenerator-runtime": ["regenerator-runtime@0.13.11", "", {}, "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="], + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + "regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], "regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="], @@ -3274,12 +3355,20 @@ "rehype-harden": ["rehype-harden@1.1.8", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw=="], + "rehype-katex": ["rehype-katex@7.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/katex": "^0.16.0", "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" } }, "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA=="], + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="], + "remark-cjk-friendly": ["remark-cjk-friendly@2.3.1", "", { "dependencies": { "mdast-util-to-markdown-cjk-friendly": "1.0.0", "micromark-extension-cjk-friendly": "2.0.1" }, "peerDependencies": { "@types/mdast": "^4.0.0", "unified": "^11.0.0" }, "optionalPeers": ["@types/mdast"] }, "sha512-f+pKZRxCRwNEGFBKNRAZAqU91GIK1SAo3ZyFHWRUgC9zcxRR0BXKd6YwqgSsxtW0rNpUDtONj7H5nje2WL3fcA=="], + + "remark-cjk-friendly-gfm-strikethrough": ["remark-cjk-friendly-gfm-strikethrough@2.3.1", "", { "dependencies": { "mdast-util-to-markdown-cjk-friendly-gfm-strikethrough": "1.0.0", "micromark-extension-cjk-friendly-gfm-strikethrough": "2.0.1" }, "peerDependencies": { "@types/mdast": "^4.0.0", "unified": "^11.0.0" }, "optionalPeers": ["@types/mdast"] }, "sha512-JE3TGgouk/sy92SemNMEUhO5mNP4on04cmzOV3s3R5Dbk160ewmpM4tgPiinKKvoJ5UW2fTu7FOYsjVbusSA9w=="], + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="], + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], @@ -3384,6 +3473,8 @@ "shell-quote": ["shell-quote@1.8.4", "", {}, "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ=="], + "shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="], + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], @@ -3578,8 +3669,6 @@ "ts-morph": ["ts-morph@26.0.0", "", { "dependencies": { "@ts-morph/common": "~0.27.0", "code-block-writer": "^13.0.3" } }, "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug=="], - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], - "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -3632,10 +3721,14 @@ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], @@ -3662,6 +3755,8 @@ "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + "use-stick-to-bottom": ["use-stick-to-bottom@1.1.6", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-z3Up8jYQGTkUCsGBnwg6/wj70KgXoW5Kz1AAc1j8MtQuYMBo6ZsdhrIXoegxa7gaMMilgQYyTohTrt3p94jHog=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], @@ -3692,8 +3787,6 @@ "vite-plus": ["vite-plus@0.2.2", "", { "dependencies": { "@oxc-project/types": "=0.138.0", "@oxlint/plugins": "=1.68.0", "@vitest/browser": "4.1.9", "@vitest/browser-preview": "4.1.9", "@vitest/expect": "4.1.9", "@vitest/mocker": "4.1.9", "@vitest/pretty-format": "4.1.9", "@vitest/runner": "4.1.9", "@vitest/snapshot": "4.1.9", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "@voidzero-dev/vite-plus-core": "0.2.2", "oxfmt": "=0.57.0", "oxlint": "=1.72.0", "oxlint-tsgolint": "=0.24.0", "vitest": "4.1.9" }, "optionalDependencies": { "@voidzero-dev/vite-plus-darwin-arm64": "0.2.2", "@voidzero-dev/vite-plus-darwin-x64": "0.2.2", "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.2.2", "@voidzero-dev/vite-plus-linux-arm64-musl": "0.2.2", "@voidzero-dev/vite-plus-linux-x64-gnu": "0.2.2", "@voidzero-dev/vite-plus-linux-x64-musl": "0.2.2", "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.2.2", "@voidzero-dev/vite-plus-win32-x64-msvc": "0.2.2" }, "peerDependencies": { "@vitest/browser-playwright": "4.1.9", "@vitest/browser-webdriverio": "4.1.9" }, "optionalPeers": ["@vitest/browser-playwright", "@vitest/browser-webdriverio"], "bin": { "vp": "bin/vp", "vpr": "bin/vpr", "oxfmt": "bin/oxfmt", "oxlint": "bin/oxlint" } }, "sha512-bXO3O0F2/uxtvX9Ck0o67stTErH/Zh0GEcCMd9pAh22tTHABCNTDPrRMWVo733e7Ux3h0Y7HanJ7neOV/nid4g=="], - "vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="], - "vitest": ["vitest@4.1.10", "", { "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.10", "@vitest/browser-preview": "4.1.10", "@vitest/browser-webdriverio": "4.1.10", "@vitest/coverage-istanbul": "4.1.10", "@vitest/coverage-v8": "4.1.10", "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw=="], "vlq": ["vlq@1.0.1", "", {}, "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="], @@ -3892,6 +3985,8 @@ "@opentui/core/diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], + "@opentui/core/marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], + "@react-native/dev-middleware/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="], "@react-native/dev-middleware/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], @@ -3914,6 +4009,8 @@ "@secure-exec/nodejs/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "@streamdown/code/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + "@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], @@ -4000,24 +4097,24 @@ "d3/d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], - "d3/d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], - "d3/d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], - "d3-chord/d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], - "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], "d3-dsv/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + "d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], "degenerator/ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], "diffie-hellman/bn.js": ["bn.js@4.12.5", "", {}, "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], "elliptic/bn.js": ["bn.js@4.12.5", "", {}, "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ=="], @@ -4132,8 +4229,6 @@ "parse-png/pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="], - "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], "path-scurry/lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], @@ -4208,8 +4303,6 @@ "stacktrace-parser/type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="], - "streamdown/marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], - "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], "strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -4470,6 +4563,18 @@ "@secure-exec/nodejs/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@streamdown/code/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@streamdown/code/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@streamdown/code/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@streamdown/code/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@streamdown/code/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@streamdown/code/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + "@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], diff --git a/packages/ui/package.json b/packages/ui/package.json index 05cd4ab..98890f4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -16,6 +16,11 @@ "dependencies": { "@base-ui/react": "^1.6.0", "@shadcn/react": "^0.2.0", + "@streamdown/cjk": "^1.0.3", + "@streamdown/code": "^1.1.1", + "@streamdown/math": "^1.0.2", + "@streamdown/mermaid": "^1.0.2", + "ai": "^7.0.35", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "catalog:", @@ -23,9 +28,12 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "shadcn": "^4.12.0", + "shiki": "^4.3.1", "sonner": "catalog:", + "streamdown": "^2.5.0", "tailwind-merge": "catalog:", - "tw-animate-css": "^1.4.0" + "tw-animate-css": "^1.4.0", + "use-stick-to-bottom": "^1.1.6" }, "devDependencies": { "@code/config": "workspace:*", diff --git a/packages/ui/src/components/ai-elements/code-block.tsx b/packages/ui/src/components/ai-elements/code-block.tsx new file mode 100644 index 0000000..e8118f6 --- /dev/null +++ b/packages/ui/src/components/ai-elements/code-block.tsx @@ -0,0 +1,525 @@ +"use client"; + +import { Button } from "@code/ui/components/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@code/ui/components/select"; +import { cn } from "@code/ui/lib/utils"; +import { CheckIcon, CopyIcon } from "lucide-react"; +import type { ComponentProps, CSSProperties, HTMLAttributes } from "react"; +import { + createContext, + memo, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import type { + BundledLanguage, + BundledTheme, + HighlighterGeneric, + ThemedToken, +} from "shiki"; +import { createHighlighter } from "shiki"; + +// Shiki uses bitflags for font styles: 1=italic, 2=bold, 4=underline. +const hasFontStyle = (fontStyle: number | undefined, flag: number) => + Math.floor((fontStyle ?? 0) / flag) % 2 === 1; +const isItalic = (fontStyle: number | undefined) => hasFontStyle(fontStyle, 1); +const isBold = (fontStyle: number | undefined) => hasFontStyle(fontStyle, 2); +const isUnderline = (fontStyle: number | undefined) => + hasFontStyle(fontStyle, 4); + +// Transform tokens to include pre-computed keys to avoid noArrayIndexKey lint +interface KeyedToken { + token: ThemedToken; + key: string; +} +interface KeyedLine { + tokens: KeyedToken[]; + key: string; +} + +const addKeysToTokens = (lines: ThemedToken[][]): KeyedLine[] => + lines.map((line, lineIdx) => ({ + key: `line-${lineIdx}`, + tokens: line.map((token, tokenIdx) => ({ + key: `line-${lineIdx}-${tokenIdx}`, + token, + })), + })); + +// Token rendering component +const TokenSpan = ({ token }: { token: ThemedToken }) => ( + + {token.content} + +); + +// Line number styles using CSS counters +const LINE_NUMBER_CLASSES = cn( + "block", + "before:content-[counter(line)]", + "before:inline-block", + "before:[counter-increment:line]", + "before:w-8", + "before:mr-4", + "before:text-right", + "before:text-muted-foreground/50", + "before:font-mono", + "before:select-none" +); + +// Line rendering component +const LineSpan = ({ + keyedLine, + showLineNumbers, +}: { + keyedLine: KeyedLine; + showLineNumbers: boolean; +}) => ( + + {keyedLine.tokens.length === 0 + ? "\n" + : keyedLine.tokens.map(({ token, key }) => ( + + ))} + +); + +// Types +type CodeBlockProps = HTMLAttributes & { + code: string; + language: BundledLanguage; + showLineNumbers?: boolean; +}; + +interface TokenizedCode { + tokens: ThemedToken[][]; + fg: string; + bg: string; +} + +interface CodeBlockContextType { + code: string; +} + +// Context +const CodeBlockContext = createContext({ + code: "", +}); + +// Highlighter cache (singleton per language) +const highlighterCache = new Map< + string, + Promise> +>(); + +// Token cache +const tokensCache = new Map(); + +const getTokensCacheKey = (code: string, language: BundledLanguage) => { + const start = code.slice(0, 100); + const end = code.length > 100 ? code.slice(-100) : ""; + return `${language}:${code.length}:${start}:${end}`; +}; + +const getHighlighter = ( + language: BundledLanguage +): Promise> => { + const cached = highlighterCache.get(language); + if (cached) { + return cached; + } + + const highlighterPromise = createHighlighter({ + langs: [language], + themes: ["github-light", "github-dark"], + }); + + highlighterCache.set(language, highlighterPromise); + return highlighterPromise; +}; + +// Create raw tokens for immediate display while highlighting loads +const createRawTokens = (code: string): TokenizedCode => ({ + bg: "transparent", + fg: "inherit", + tokens: code.split("\n").map((line) => + line === "" + ? [] + : [ + { + color: "inherit", + content: line, + } as ThemedToken, + ] + ), +}); + +export const highlightCode = async ( + code: string, + language: BundledLanguage +): Promise => { + const tokensCacheKey = getTokensCacheKey(code, language); + + const cached = tokensCache.get(tokensCacheKey); + if (cached) { + return cached; + } + + const highlighter = await getHighlighter(language); + const availableLangs = highlighter.getLoadedLanguages(); + const langToUse = availableLangs.includes(language) ? language : "text"; + const result = highlighter.codeToTokens(code, { + lang: langToUse, + themes: { + dark: "github-dark", + light: "github-light", + }, + }); + const tokenized: TokenizedCode = { + bg: result.bg ?? "transparent", + fg: result.fg ?? "inherit", + tokens: result.tokens, + }; + + tokensCache.set(tokensCacheKey, tokenized); + return tokenized; +}; + +const CodeBlockBody = memo( + ({ + tokenized, + showLineNumbers, + className, + }: { + tokenized: TokenizedCode; + showLineNumbers: boolean; + className?: string; + }) => { + const preStyle = useMemo( + () => ({ + backgroundColor: tokenized.bg, + color: tokenized.fg, + }), + [tokenized.bg, tokenized.fg] + ); + + const keyedLines = useMemo( + () => addKeysToTokens(tokenized.tokens), + [tokenized.tokens] + ); + + return ( +
+        
+          {keyedLines.map((keyedLine) => (
+            
+          ))}
+        
+      
+ ); + }, + (prevProps, nextProps) => + prevProps.tokenized === nextProps.tokenized && + prevProps.showLineNumbers === nextProps.showLineNumbers && + prevProps.className === nextProps.className +); + +CodeBlockBody.displayName = "CodeBlockBody"; + +export const CodeBlockContainer = ({ + className, + language, + style, + ...props +}: HTMLAttributes & { language: string }) => ( +
+); + +export const CodeBlockHeader = ({ + children, + className, + ...props +}: HTMLAttributes) => ( +
+ {children} +
+); + +export const CodeBlockTitle = ({ + children, + className, + ...props +}: HTMLAttributes) => ( +
+ {children} +
+); + +export const CodeBlockFilename = ({ + children, + className, + ...props +}: HTMLAttributes) => ( + + {children} + +); + +export const CodeBlockActions = ({ + children, + className, + ...props +}: HTMLAttributes) => ( +
+ {children} +
+); + +export const CodeBlockContent = ({ + code, + language, + showLineNumbers = false, +}: { + code: string; + language: BundledLanguage; + showLineNumbers?: boolean; +}) => { + // Memoized raw tokens for immediate display + const rawTokens = useMemo(() => createRawTokens(code), [code]); + + const cacheKey = getTokensCacheKey(code, language); + + // Synchronous cache lookup avoids a loading flash after the first highlight. + const syncTokens = useMemo( + () => tokensCache.get(cacheKey) ?? rawTokens, + [cacheKey, rawTokens] + ); + + const [asyncResult, setAsyncResult] = useState<{ + cacheKey: string; + tokens: TokenizedCode; + } | null>(null); + + useEffect(() => { + let cancelled = false; + + const loadHighlightedTokens = async () => { + try { + const result = await highlightCode(code, language); + if (!cancelled) { + setAsyncResult({ cacheKey, tokens: result }); + } + } catch (error) { + console.error("Failed to highlight code:", error); + } + }; + void loadHighlightedTokens(); + + return () => { + cancelled = true; + }; + }, [cacheKey, code, language]); + + const tokenized = + asyncResult?.cacheKey === cacheKey ? asyncResult.tokens : syncTokens; + + return ( +
+ +
+ ); +}; + +export const CodeBlock = ({ + code, + language, + showLineNumbers = false, + className, + children, + ...props +}: CodeBlockProps) => { + const contextValue = useMemo(() => ({ code }), [code]); + + return ( + + + {children} + + + + ); +}; + +export type CodeBlockCopyButtonProps = ComponentProps & { + onCopy?: () => void; + onError?: (error: Error) => void; + timeout?: number; +}; + +export const CodeBlockCopyButton = ({ + onCopy, + onError, + timeout = 2000, + children, + className, + ...props +}: CodeBlockCopyButtonProps) => { + const [isCopied, setIsCopied] = useState(false); + const timeoutRef = useRef(0); + const { code } = useContext(CodeBlockContext); + + const copyToClipboard = useCallback(async () => { + if (typeof window === "undefined" || !navigator?.clipboard?.writeText) { + onError?.(new Error("Clipboard API not available")); + return; + } + + try { + if (!isCopied) { + await navigator.clipboard.writeText(code); + setIsCopied(true); + onCopy?.(); + timeoutRef.current = window.setTimeout( + () => setIsCopied(false), + timeout + ); + } + } catch (error) { + onError?.(error as Error); + } + }, [code, onCopy, onError, timeout, isCopied]); + + useEffect( + () => () => { + window.clearTimeout(timeoutRef.current); + }, + [] + ); + + const Icon = isCopied ? CheckIcon : CopyIcon; + + return ( + + ); +}; + +export type CodeBlockLanguageSelectorProps = ComponentProps; + +export const CodeBlockLanguageSelector = ( + props: CodeBlockLanguageSelectorProps +) =>