@townco/ui 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/hooks/index.d.ts +1 -0
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/use-acp-client.d.ts +22 -0
- package/dist/core/hooks/use-acp-client.js +63 -0
- package/dist/gui/components/ChatView.d.ts +7 -0
- package/dist/gui/components/ChatView.js +153 -0
- package/dist/gui/components/index.d.ts +1 -0
- package/dist/gui/components/index.js +1 -0
- package/package.json +3 -3
package/dist/core/hooks/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AcpClient } from "../../sdk/client/index.js";
|
|
2
|
+
export interface UseACPClientOptions {
|
|
3
|
+
serverUrl: string;
|
|
4
|
+
}
|
|
5
|
+
export interface UseACPClientReturn {
|
|
6
|
+
client: AcpClient | null;
|
|
7
|
+
error: string | null;
|
|
8
|
+
sessionId: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook to initialize and manage an ACP client connection
|
|
12
|
+
*
|
|
13
|
+
* Handles:
|
|
14
|
+
* - Extracting session ID from URL query parameters
|
|
15
|
+
* - Creating and initializing the ACP client
|
|
16
|
+
* - Cleanup on unmount
|
|
17
|
+
* - Error handling
|
|
18
|
+
*
|
|
19
|
+
* @param options - Configuration options for the ACP client
|
|
20
|
+
* @returns Object containing client, error state, and session ID
|
|
21
|
+
*/
|
|
22
|
+
export declare function useACPClient(options: UseACPClientOptions): UseACPClientReturn;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createLogger } from "@townco/core";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { AcpClient } from "../../sdk/client/index.js";
|
|
4
|
+
const logger = createLogger("acp-client-hook");
|
|
5
|
+
/**
|
|
6
|
+
* Custom hook to initialize and manage an ACP client connection
|
|
7
|
+
*
|
|
8
|
+
* Handles:
|
|
9
|
+
* - Extracting session ID from URL query parameters
|
|
10
|
+
* - Creating and initializing the ACP client
|
|
11
|
+
* - Cleanup on unmount
|
|
12
|
+
* - Error handling
|
|
13
|
+
*
|
|
14
|
+
* @param options - Configuration options for the ACP client
|
|
15
|
+
* @returns Object containing client, error state, and session ID
|
|
16
|
+
*/
|
|
17
|
+
export function useACPClient(options) {
|
|
18
|
+
const [client, setClient] = useState(null);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
const [sessionId, setSessionId] = useState(null);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// Extract session ID from URL query parameter (?session=abc123)
|
|
23
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
24
|
+
const urlSessionId = urlParams.get("session");
|
|
25
|
+
if (urlSessionId) {
|
|
26
|
+
logger.info("Session ID found in URL", { sessionId: urlSessionId });
|
|
27
|
+
setSessionId(urlSessionId);
|
|
28
|
+
}
|
|
29
|
+
// Create AcpClient with HTTP transport
|
|
30
|
+
try {
|
|
31
|
+
logger.info("Initializing ACP client", {
|
|
32
|
+
serverUrl: options.serverUrl,
|
|
33
|
+
});
|
|
34
|
+
const acpClient = new AcpClient({
|
|
35
|
+
type: "http",
|
|
36
|
+
options: {
|
|
37
|
+
baseUrl: options.serverUrl,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
setClient(acpClient);
|
|
41
|
+
logger.info("ACP client initialized successfully");
|
|
42
|
+
// Clean up on unmount
|
|
43
|
+
return () => {
|
|
44
|
+
logger.debug("Disconnecting ACP client");
|
|
45
|
+
acpClient.disconnect().catch((err) => {
|
|
46
|
+
logger.error("Failed to disconnect ACP client", {
|
|
47
|
+
error: err instanceof Error ? err.message : String(err),
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to initialize ACP client";
|
|
54
|
+
setError(errorMessage);
|
|
55
|
+
logger.error("Failed to initialize ACP client", {
|
|
56
|
+
error: err instanceof Error ? err.message : String(err),
|
|
57
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
58
|
+
});
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}, [options.serverUrl]);
|
|
62
|
+
return { client, error, sessionId };
|
|
63
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AcpClient } from "../../sdk/client/index.js";
|
|
2
|
+
export interface ChatViewProps {
|
|
3
|
+
client: AcpClient | null;
|
|
4
|
+
initialSessionId?: string | null;
|
|
5
|
+
error?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function ChatView({ client, initialSessionId, error: initError, }: ChatViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createLogger } from "@townco/core";
|
|
3
|
+
import { ArrowUp, ChevronUp, Code, PanelRight, Settings, Sparkles, } from "lucide-react";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { useChatMessages, useChatSession, useToolCalls, } from "../../core/hooks/index.js";
|
|
6
|
+
import { useChatStore } from "../../core/store/chat-store.js";
|
|
7
|
+
import { cn } from "../lib/utils.js";
|
|
8
|
+
import { ChatEmptyState, ChatHeader, ChatInputActions, ChatInputAttachment, ChatInputCommandMenu, ChatInputField, ChatInputRoot, ChatInputSubmit, ChatInputToolbar, ChatInputVoiceInput, ChatLayout, FilesTabContent, Message, MessageContent, PanelTabsHeader, SourcesTabContent, Tabs, TabsContent, TodoTabContent, } from "./index.js";
|
|
9
|
+
const logger = createLogger("gui");
|
|
10
|
+
// Mobile header component that uses ChatHeader context
|
|
11
|
+
function MobileHeader({ agentName }) {
|
|
12
|
+
const { isExpanded, setIsExpanded } = ChatHeader.useChatHeaderContext();
|
|
13
|
+
return (_jsxs("div", { className: "flex lg:hidden items-center gap-2 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-1", children: [_jsx("h1", { className: "text-[20px] font-semibold leading-[1.2] tracking-[-0.4px] text-foreground", children: agentName }), _jsx("div", { className: "flex items-center justify-center shrink-0", children: _jsx(ChevronUp, { className: "size-4 rotate-180 text-muted-foreground" }) })] }), _jsx("button", { type: "button", className: "flex items-center justify-center shrink-0 cursor-pointer", "aria-label": "Toggle menu", onClick: () => setIsExpanded(!isExpanded), children: _jsx(PanelRight, { className: "size-4 text-muted-foreground" }) })] }));
|
|
14
|
+
}
|
|
15
|
+
// Header component that uses ChatLayout context (must be inside ChatLayout.Root)
|
|
16
|
+
function AppChatHeader({ agentName, todos, sources, showHeader, }) {
|
|
17
|
+
const { panelSize, setPanelSize } = ChatLayout.useChatLayoutContext();
|
|
18
|
+
return (_jsxs(ChatHeader.Root, { className: cn("border-b border-border bg-card relative lg:p-0", "[border-bottom-width:0.5px]"), children: [_jsxs("div", { className: "hidden lg:flex items-center gap-2 w-full h-16 py-5 pl-6 pr-4", children: [showHeader && (_jsxs("div", { className: "flex items-center gap-2 flex-1", children: [_jsx("h1", { className: "text-[20px] font-semibold leading-[1.2] tracking-[-0.4px] text-foreground", children: agentName }), _jsx("div", { className: "flex items-center justify-center shrink-0", children: _jsx(ChevronUp, { className: "size-4 rotate-180 text-muted-foreground" }) })] })), !showHeader && _jsx("div", { className: "flex-1" }), _jsx("button", { type: "button", className: "flex items-center justify-center shrink-0 cursor-pointer", "aria-label": "Toggle sidebar", onClick: () => {
|
|
19
|
+
setPanelSize(panelSize === "hidden" ? "small" : "hidden");
|
|
20
|
+
}, children: _jsx(PanelRight, { className: "size-4 text-muted-foreground" }) })] }), _jsx(MobileHeader, { agentName: agentName }), _jsx(ChatHeader.ExpandablePanel, { className: cn("pt-6 pb-8 px-6", "border-b border-border bg-card", "shadow-[0_4px_16px_0_rgba(0,0,0,0.04)]", "[border-bottom-width:0.5px]"), children: _jsxs(Tabs, { defaultValue: "todo", className: "w-full", children: [_jsx(PanelTabsHeader, { showIcons: true, visibleTabs: ["todo", "files", "sources"], variant: "default" }), _jsx(TabsContent, { value: "todo", className: "mt-4", children: _jsx(TodoTabContent, { todos: todos }) }), _jsx(TabsContent, { value: "files", className: "mt-4", children: _jsx(FilesTabContent, {}) }), _jsx(TabsContent, { value: "sources", className: "mt-4", children: _jsx(SourcesTabContent, { sources: sources }) })] }) })] }));
|
|
21
|
+
}
|
|
22
|
+
export function ChatView({ client, initialSessionId, error: initError, }) {
|
|
23
|
+
// Use shared hooks from @townco/ui/core - MUST be called before any early returns
|
|
24
|
+
const { connectionStatus, connect, sessionId } = useChatSession(client, initialSessionId);
|
|
25
|
+
const { messages } = useChatMessages(client);
|
|
26
|
+
useToolCalls(client); // Still need to subscribe to tool call events
|
|
27
|
+
const error = useChatStore((state) => state.error);
|
|
28
|
+
const [agentName, setAgentName] = useState("Agent");
|
|
29
|
+
const [isLargeScreen, setIsLargeScreen] = useState(typeof window !== "undefined" ? window.innerWidth >= 1024 : true);
|
|
30
|
+
// Log connection status changes
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
logger.debug("Connection status changed", { status: connectionStatus });
|
|
33
|
+
if (connectionStatus === "error" && error) {
|
|
34
|
+
logger.error("Connection error occurred", { error });
|
|
35
|
+
}
|
|
36
|
+
}, [connectionStatus, error]);
|
|
37
|
+
// Get agent name from session metadata
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (client && sessionId) {
|
|
40
|
+
const session = client.getCurrentSession();
|
|
41
|
+
if (session?.metadata?.agentName) {
|
|
42
|
+
setAgentName(session.metadata.agentName);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [client, sessionId]);
|
|
46
|
+
// Monitor screen size changes and update isLargeScreen state
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const mediaQuery = window.matchMedia("(min-width: 1024px)");
|
|
49
|
+
const handleChange = (e) => {
|
|
50
|
+
setIsLargeScreen(e.matches);
|
|
51
|
+
};
|
|
52
|
+
// Set initial value
|
|
53
|
+
setIsLargeScreen(mediaQuery.matches);
|
|
54
|
+
// Listen for changes
|
|
55
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
56
|
+
return () => {
|
|
57
|
+
mediaQuery.removeEventListener("change", handleChange);
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
// If there's an initialization error, show error UI (after all hooks have been called)
|
|
61
|
+
if (initError) {
|
|
62
|
+
return (_jsx("div", { className: "flex items-center justify-center h-screen bg-background", children: _jsxs("div", { className: "text-center p-8 max-w-md", children: [_jsx("h1", { className: "text-2xl font-bold text-destructive mb-4", children: "Initialization Error" }), _jsx("p", { className: "text-foreground mb-4", children: initError }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Failed to initialize the ACP client. Check the console for details." })] }) }));
|
|
63
|
+
}
|
|
64
|
+
// TODO: Replace with useChatStore((state) => state.todos) when todos are added to the store
|
|
65
|
+
const todos = [];
|
|
66
|
+
// Dummy sources data based on Figma design
|
|
67
|
+
const sources = [
|
|
68
|
+
{
|
|
69
|
+
id: "1",
|
|
70
|
+
title: "Boeing Scores Early Wins at Dubai Airshow",
|
|
71
|
+
sourceName: "Reuters",
|
|
72
|
+
url: "https://www.reuters.com/markets/companies/BA.N",
|
|
73
|
+
snippet: "DUBAI, Nov 17 (Reuters) - Boeing (BA.N), opens new tab took centre stage at day one of the Dubai Airshow on Monday, booking a $38 billion order from host carrier Emirates and clinching more deals with African carriers, while China displayed its C919 in the Middle East for the first time.",
|
|
74
|
+
favicon: "https://www.google.com/s2/favicons?domain=reuters.com&sz=32",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "2",
|
|
78
|
+
title: "Boeing's Sustainable Aviation Goals Take Flight",
|
|
79
|
+
sourceName: "Forbes",
|
|
80
|
+
url: "https://www.forbes.com",
|
|
81
|
+
snippet: "SEATTLE, Nov 18 (Reuters) - Boeing is making headway towards its sustainability targets, unveiling plans for a new eco-friendly aircraft design aimed at reducing emissions by 50% by 2030.",
|
|
82
|
+
favicon: "https://www.google.com/s2/favicons?domain=forbes.com&sz=32",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "3",
|
|
86
|
+
title: "Boeing Faces Increased Competition in Global Aviation Market",
|
|
87
|
+
sourceName: "Reuters",
|
|
88
|
+
url: "https://www.reuters.com",
|
|
89
|
+
snippet: "CHICAGO, Nov 19 (Reuters) - As the global aviation industry rebounds post-pandemic, Boeing is grappling with intensified competition from rival manufacturers, particularly in the Asian market.",
|
|
90
|
+
favicon: "https://www.google.com/s2/favicons?domain=reuters.com&sz=32",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "4",
|
|
94
|
+
title: "Boeing's Starliner Successfully Completes Orbital Test Flight",
|
|
95
|
+
sourceName: "The Verge",
|
|
96
|
+
url: "https://www.theverge.com",
|
|
97
|
+
snippet: "NASA, Nov 20 (Reuters) - Boeing's CST-100 Starliner spacecraft achieves a significant milestone, successfully completing its orbital test flight, paving the way for future crewed missions to the International Space Station.",
|
|
98
|
+
favicon: "https://www.google.com/s2/favicons?domain=theverge.com&sz=32",
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
// Command menu items for chat input
|
|
102
|
+
const commandMenuItems = [
|
|
103
|
+
{
|
|
104
|
+
id: "model-sonnet",
|
|
105
|
+
label: "Use Sonnet 4.5",
|
|
106
|
+
description: "Switch to Claude Sonnet 4.5 model",
|
|
107
|
+
icon: _jsx(Sparkles, { className: "h-4 w-4" }),
|
|
108
|
+
category: "model",
|
|
109
|
+
onSelect: () => {
|
|
110
|
+
logger.info("User selected Sonnet 4.5 model");
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "model-opus",
|
|
115
|
+
label: "Use Opus",
|
|
116
|
+
description: "Switch to Claude Opus model",
|
|
117
|
+
icon: _jsx(Sparkles, { className: "h-4 w-4" }),
|
|
118
|
+
category: "model",
|
|
119
|
+
onSelect: () => {
|
|
120
|
+
logger.info("User selected Opus model");
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "settings",
|
|
125
|
+
label: "Open Settings",
|
|
126
|
+
description: "Configure chat preferences",
|
|
127
|
+
icon: _jsx(Settings, { className: "h-4 w-4" }),
|
|
128
|
+
category: "action",
|
|
129
|
+
onSelect: () => {
|
|
130
|
+
logger.info("User opened settings");
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "code-mode",
|
|
135
|
+
label: "Code Mode",
|
|
136
|
+
description: "Enable code-focused responses",
|
|
137
|
+
icon: _jsx(Code, { className: "h-4 w-4" }),
|
|
138
|
+
category: "mode",
|
|
139
|
+
onSelect: () => {
|
|
140
|
+
logger.info("User enabled code mode");
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
return (_jsxs(ChatLayout.Root, { defaultPanelSize: "hidden", defaultActiveTab: "todo", children: [_jsxs(ChatLayout.Main, { children: [_jsx(AppChatHeader, { agentName: agentName, todos: todos, sources: sources, showHeader: messages.length > 0 }), connectionStatus === "error" && error && (_jsx("div", { className: "border-b border-destructive/20 bg-destructive/10 px-6 py-4", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-sm font-semibold text-destructive", children: "Connection Error" }), _jsx("p", { className: "whitespace-pre-line text-sm text-foreground", children: error })] }), _jsx("button", { type: "button", onClick: connect, className: "rounded-lg bg-destructive px-4 py-2 text-sm font-medium text-destructive-foreground transition-colors hover:bg-destructive-hover", children: "Retry" })] }) })), _jsxs(ChatLayout.Body, { children: [_jsx(ChatLayout.Messages, { className: messages.length > 0 ? "pt-4" : "", children: messages.length === 0 ? (_jsx("div", { className: "flex flex-1 items-center px-4", children: _jsx(ChatEmptyState, { title: agentName, description: "This agent can help you with your tasks. Start a conversation by typing a message below.", suggestedPrompts: [
|
|
145
|
+
"Help me debug this code",
|
|
146
|
+
"Explain how this works",
|
|
147
|
+
"Create a new feature",
|
|
148
|
+
"Review my changes",
|
|
149
|
+
], onPromptClick: (prompt) => {
|
|
150
|
+
// TODO: Implement prompt click handler
|
|
151
|
+
logger.info("Prompt clicked", { prompt });
|
|
152
|
+
} }) })) : (_jsx("div", { className: "flex flex-col gap-4 px-4", children: messages.map((message, index) => (_jsx(Message, { message: message, isLastMessage: index === messages.length - 1, children: _jsx(MessageContent, { message: message, thinkingDisplayStyle: "collapsible" }) }, message.id))) })) }), _jsx(ChatLayout.Footer, { children: _jsxs(ChatInputRoot, { client: client, children: [_jsx(ChatInputCommandMenu, { commands: commandMenuItems }), _jsx(ChatInputField, { placeholder: "Type a message or / for commands...", autoFocus: true }), _jsxs(ChatInputToolbar, { children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputActions, {}), _jsx(ChatInputAttachment, {})] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputVoiceInput, {}), _jsx(ChatInputSubmit, { children: _jsx(ArrowUp, { className: "size-4" }) })] })] })] }) })] })] }), isLargeScreen && (_jsx(ChatLayout.Aside, { breakpoint: "lg", children: _jsxs(Tabs, { defaultValue: "todo", className: "flex flex-col h-full", children: [_jsx("div", { className: cn("border-b border-border bg-card", "px-6 py-2 h-16", "flex items-center", "[border-bottom-width:0.5px]"), children: _jsx(PanelTabsHeader, { showIcons: true, visibleTabs: ["todo", "files", "sources"], variant: "compact" }) }), _jsx(TabsContent, { value: "todo", className: "flex-1 p-4 mt-0", children: _jsx(TodoTabContent, { todos: todos }) }), _jsx(TabsContent, { value: "files", className: "flex-1 p-4 mt-0", children: _jsx(FilesTabContent, {}) }), _jsx(TabsContent, { value: "sources", className: "flex-1 p-4 mt-0", children: _jsx(SourcesTabContent, { sources: sources }) })] }) }))] }));
|
|
153
|
+
}
|
|
@@ -10,6 +10,7 @@ export { DatabaseTabContent, type DatabaseTabContentProps, FilesTabContent, type
|
|
|
10
10
|
export { ChatSecondaryPanel, type ChatSecondaryPanelProps, } from "./ChatSecondaryPanel.js";
|
|
11
11
|
export * as ChatSidebar from "./ChatSidebar.js";
|
|
12
12
|
export { ChatStatus, type ChatStatusProps } from "./ChatStatus.js";
|
|
13
|
+
export { ChatView, type ChatViewProps } from "./ChatView.js";
|
|
13
14
|
export { Conversation, type ConversationProps } from "./Conversation.js";
|
|
14
15
|
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./Dialog.js";
|
|
15
16
|
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "./DropdownMenu.js";
|
|
@@ -12,6 +12,7 @@ export { DatabaseTabContent, FilesTabContent, SourcesTabContent, TodoTabContent,
|
|
|
12
12
|
export { ChatSecondaryPanel, } from "./ChatSecondaryPanel.js";
|
|
13
13
|
export * as ChatSidebar from "./ChatSidebar.js";
|
|
14
14
|
export { ChatStatus } from "./ChatStatus.js";
|
|
15
|
+
export { ChatView } from "./ChatView.js";
|
|
15
16
|
// Chat components - shadcn.io/ai inspired primitives
|
|
16
17
|
export { Conversation } from "./Conversation.js";
|
|
17
18
|
// Dialog components
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@townco/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@agentclientprotocol/sdk": "^0.5.1",
|
|
43
|
-
"@townco/core": "0.0.
|
|
43
|
+
"@townco/core": "0.0.10",
|
|
44
44
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
45
45
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
46
46
|
"@radix-ui/react-label": "^2.1.8",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@tailwindcss/postcss": "^4.1.17",
|
|
65
|
-
"@townco/tsconfig": "0.1.
|
|
65
|
+
"@townco/tsconfig": "0.1.29",
|
|
66
66
|
"@types/node": "^24.10.0",
|
|
67
67
|
"@types/react": "^19.2.2",
|
|
68
68
|
"ink": "^6.4.0",
|