@townco/ui 0.1.14 → 0.1.16

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.
Files changed (42) hide show
  1. package/dist/core/hooks/index.d.ts +1 -0
  2. package/dist/core/hooks/index.js +1 -0
  3. package/dist/core/hooks/use-chat-messages.d.ts +50 -11
  4. package/dist/core/hooks/use-chat-session.d.ts +5 -5
  5. package/dist/core/hooks/use-tool-calls.d.ts +52 -0
  6. package/dist/core/hooks/use-tool-calls.js +61 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +1 -0
  9. package/dist/core/lib/logger.d.ts +24 -0
  10. package/dist/core/lib/logger.js +108 -0
  11. package/dist/core/schemas/chat.d.ts +166 -83
  12. package/dist/core/schemas/chat.js +27 -27
  13. package/dist/core/schemas/index.d.ts +1 -0
  14. package/dist/core/schemas/index.js +1 -0
  15. package/dist/core/schemas/tool-call.d.ts +174 -0
  16. package/dist/core/schemas/tool-call.js +130 -0
  17. package/dist/core/store/chat-store.d.ts +28 -28
  18. package/dist/core/store/chat-store.js +123 -59
  19. package/dist/gui/components/ChatLayout.js +11 -10
  20. package/dist/gui/components/MessageContent.js +4 -1
  21. package/dist/gui/components/ToolCall.d.ts +8 -0
  22. package/dist/gui/components/ToolCall.js +100 -0
  23. package/dist/gui/components/ToolCallList.d.ts +9 -0
  24. package/dist/gui/components/ToolCallList.js +22 -0
  25. package/dist/gui/components/index.d.ts +2 -0
  26. package/dist/gui/components/index.js +2 -0
  27. package/dist/gui/components/resizable.d.ts +7 -0
  28. package/dist/gui/components/resizable.js +7 -0
  29. package/dist/sdk/schemas/session.d.ts +390 -220
  30. package/dist/sdk/schemas/session.js +74 -29
  31. package/dist/sdk/transports/http.js +705 -472
  32. package/dist/sdk/transports/stdio.js +187 -32
  33. package/dist/tui/components/ChatView.js +19 -51
  34. package/dist/tui/components/MessageList.d.ts +2 -4
  35. package/dist/tui/components/MessageList.js +13 -37
  36. package/dist/tui/components/ToolCall.d.ts +9 -0
  37. package/dist/tui/components/ToolCall.js +41 -0
  38. package/dist/tui/components/ToolCallList.d.ts +8 -0
  39. package/dist/tui/components/ToolCallList.js +17 -0
  40. package/dist/tui/components/index.d.ts +2 -0
  41. package/dist/tui/components/index.js +2 -0
  42. package/package.json +4 -2
@@ -1,7 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ArrowDown } from "lucide-react";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/utils.js";
5
+ import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "./resizable.js";
5
6
  import { Toaster } from "./Sonner.js";
6
7
  const ChatLayoutContext = React.createContext(undefined);
7
8
  const useChatLayoutContext = () => {
@@ -22,7 +23,7 @@ const ChatLayoutRoot = React.forwardRef(({ defaultSidebarOpen = false, defaultPa
22
23
  setPanelSize,
23
24
  activeTab,
24
25
  setActiveTab,
25
- }, children: _jsx("div", { ref: ref, className: cn("flex h-screen flex-row bg-background text-foreground", className), ...props, children: children }) }));
26
+ }, children: _jsx("div", { ref: ref, className: cn("flex h-screen flex-row bg-background text-foreground", className), ...props, children: _jsx(ResizablePanelGroup, { direction: "horizontal", className: "flex-1", children: children }) }) }));
26
27
  });
27
28
  ChatLayoutRoot.displayName = "ChatLayout.Root";
28
29
  const ChatLayoutHeader = React.forwardRef(({ className, children, ...props }, ref) => {
@@ -30,7 +31,7 @@ const ChatLayoutHeader = React.forwardRef(({ className, children, ...props }, re
30
31
  });
31
32
  ChatLayoutHeader.displayName = "ChatLayout.Header";
32
33
  const ChatLayoutMain = React.forwardRef(({ className, children, ...props }, ref) => {
33
- return (_jsx("div", { ref: ref, className: cn("flex flex-1 flex-col overflow-hidden", className), ...props, children: children }));
34
+ return (_jsx(ResizablePanel, { defaultSize: 75, minSize: 50, children: _jsx("div", { ref: ref, className: cn("flex flex-1 flex-col overflow-hidden h-full", className), ...props, children: children }) }));
34
35
  });
35
36
  ChatLayoutMain.displayName = "ChatLayout.Main";
36
37
  const ChatLayoutBody = React.forwardRef(({ showToaster = true, className, children, ...props }, ref) => {
@@ -90,13 +91,13 @@ const ChatLayoutAside = React.forwardRef(({ breakpoint = "lg", className, childr
90
91
  // Hidden state - don't render
91
92
  if (panelSize === "hidden")
92
93
  return null;
93
- return (_jsx("div", { ref: ref, className: cn(
94
- // Hidden by default, visible at breakpoint
95
- "hidden border-l border-border bg-card overflow-y-auto transition-all duration-300",
96
- // Breakpoint visibility
97
- breakpoint === "md" && "md:block", breakpoint === "lg" && "lg:block", breakpoint === "xl" && "xl:block", breakpoint === "2xl" && "2xl:block",
98
- // Size variants
99
- panelSize === "small" && "w-80", panelSize === "large" && "w-lg", className), ...props, children: children }));
94
+ return (_jsxs(_Fragment, { children: [_jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 25, minSize: 15, maxSize: 50, children: _jsx("div", { ref: ref, className: cn(
95
+ // Hidden by default, visible at breakpoint
96
+ "hidden h-full border-l border-border bg-card overflow-y-auto transition-all duration-300",
97
+ // Breakpoint visibility
98
+ breakpoint === "md" && "md:block", breakpoint === "lg" && "lg:block", breakpoint === "xl" && "xl:block", breakpoint === "2xl" && "2xl:block",
99
+ // Size variants - width is now controlled by ResizablePanel
100
+ className), ...props, children: children }) })] }));
100
101
  });
101
102
  ChatLayoutAside.displayName = "ChatLayout.Aside";
102
103
  /* -------------------------------------------------------------------------------------------------
@@ -6,6 +6,7 @@ import { useChatStore } from "../../core/store/chat-store.js";
6
6
  import { cn } from "../lib/utils.js";
7
7
  import { Reasoning } from "./Reasoning.js";
8
8
  import { Response } from "./Response.js";
9
+ import { ToolCall } from "./ToolCall.js";
9
10
  /**
10
11
  * MessageContent component inspired by shadcn.io/ai
11
12
  * Provides the content container with role-based styling
@@ -97,7 +98,9 @@ export const MessageContent = React.forwardRef(({ role: roleProp, variant, isStr
97
98
  const hasThinking = !!thinking;
98
99
  // Check if waiting (streaming but no content yet)
99
100
  const isWaiting = message.isStreaming && !message.content && message.role === "assistant";
100
- content = (_jsxs(_Fragment, { children: [message.role === "assistant" && hasThinking && (_jsx(Reasoning, { content: thinking, isStreaming: message.isStreaming, mode: thinkingDisplayStyle, autoCollapse: true })), isWaiting && streamingStartTime && (_jsxs("div", { className: "flex items-center gap-2 opacity-50", children: [_jsx(Loader2Icon, { className: "size-4 animate-spin text-muted-foreground" }), _jsx(WaitingElapsedTime, { startTime: streamingStartTime })] })), message.role === "user" ? (_jsx("div", { className: "whitespace-pre-wrap", children: message.content })) : (_jsx(Response, { content: message.content, isStreaming: message.isStreaming, showEmpty: false }))] }));
101
+ content = (_jsxs(_Fragment, { children: [message.role === "assistant" && hasThinking && (_jsx(Reasoning, { content: thinking, isStreaming: message.isStreaming, mode: thinkingDisplayStyle, autoCollapse: true })), isWaiting && streamingStartTime && (_jsxs("div", { className: "flex items-center gap-2 opacity-50", children: [_jsx(Loader2Icon, { className: "size-4 animate-spin text-muted-foreground" }), _jsx(WaitingElapsedTime, { startTime: streamingStartTime })] })), message.role === "assistant" &&
102
+ message.toolCalls &&
103
+ message.toolCalls.length > 0 && (_jsx("div", { className: "flex flex-col gap-2 mb-3", children: message.toolCalls.map((toolCall) => (_jsx(ToolCall, { toolCall: toolCall }, toolCall.id))) })), message.role === "user" ? (_jsx("div", { className: "whitespace-pre-wrap", children: message.content })) : (_jsx(Response, { content: message.content, isStreaming: message.isStreaming, showEmpty: false }))] }));
101
104
  }
102
105
  return (_jsx("div", { ref: ref, className: cn(messageContentVariants({ role, variant }), isStreaming && "animate-pulse-subtle", className), ...props, children: content }));
103
106
  });
@@ -0,0 +1,8 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface ToolCallProps {
3
+ toolCall: ToolCallType;
4
+ }
5
+ /**
6
+ * ToolCall component - displays a single tool call with collapsible details
7
+ */
8
+ export declare function ToolCall({ toolCall }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Wrench } from "lucide-react";
3
+ import { useState } from "react";
4
+ import ReactJson from "react-json-view";
5
+ /**
6
+ * Tool call status badge styles
7
+ */
8
+ const statusStyles = {
9
+ pending: "bg-transparent text-muted-foreground",
10
+ in_progress: "bg-transparent text-muted-foreground",
11
+ completed: "bg-transparent text-muted-foreground",
12
+ failed: "bg-transparent text-muted-foreground",
13
+ };
14
+ /**
15
+ * Tool call kind icons (using emoji for simplicity)
16
+ */
17
+ const kindIcons = {
18
+ read: "\u{1F4C4}",
19
+ edit: "\u{270F}\u{FE0F}",
20
+ delete: "\u{1F5D1}\u{FE0F}",
21
+ move: "\u{1F4E6}",
22
+ search: "\u{1F50D}",
23
+ execute: "\u{2699}\u{FE0F}",
24
+ think: "\u{1F4AD}",
25
+ fetch: "\u{1F310}",
26
+ switch_mode: "\u{1F501}",
27
+ other: "\u{1F527}",
28
+ };
29
+ /**
30
+ * ToolCall component - displays a single tool call with collapsible details
31
+ */
32
+ export function ToolCall({ toolCall }) {
33
+ const [isExpanded, setIsExpanded] = useState(false);
34
+ return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [_jsxs("span", { className: `inline-flex items-center gap-1.5 px-2 py-1 text-xs font-medium rounded ${statusStyles[toolCall.status]}`, children: [_jsx(Wrench, { className: "h-3 w-3" }), _jsx("span", { children: toolCall.title }), toolCall.status === "completed" && _jsx("span", { children: "\u2713" })] }), _jsx("span", { className: "text-gray-400 text-xs opacity-0 group-hover:opacity-100 transition-opacity", children: isExpanded ? "▲" : "▼" })] }), isExpanded && (_jsxs("div", { className: "mt-2 space-y-3 text-sm border border-gray-200 rounded-md p-3 bg-white shadow-sm max-w-full", children: [toolCall.locations && toolCall.locations.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1.5", children: "Files" }), _jsx("ul", { className: "space-y-1", children: toolCall.locations.map((loc) => (_jsxs("li", { className: "font-mono text-xs text-gray-700 bg-gray-50 px-2 py-1 rounded", children: [loc.path, loc.line !== null &&
35
+ loc.line !== undefined &&
36
+ `:${loc.line}`] }, `${loc.path}:${loc.line ?? ""}`))) })] })), toolCall.rawInput && Object.keys(toolCall.rawInput).length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1.5", children: "Input" }), _jsx("div", { className: "bg-gray-50 p-2 rounded border border-gray-200", children: _jsx(ReactJson, { src: toolCall.rawInput, name: false, collapsed: 1, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: { fontSize: "11px", backgroundColor: "transparent" }, theme: "rjv-default" }) })] })), toolCall.content && toolCall.content.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1.5", children: "Output" }), _jsx("div", { className: "space-y-2", children: toolCall.content.map((block, idx) => {
37
+ // Generate a stable key based on content
38
+ const getBlockKey = () => {
39
+ if (block.type === "diff" && "path" in block) {
40
+ return `diff-${block.path}-${idx}`;
41
+ }
42
+ if (block.type === "terminal" && "terminalId" in block) {
43
+ return `terminal-${block.terminalId}`;
44
+ }
45
+ if (block.type === "text" && "text" in block) {
46
+ return `text-${block.text.substring(0, 20)}-${idx}`;
47
+ }
48
+ if (block.type === "content" && "content" in block) {
49
+ const innerContent = block.content;
50
+ return `content-${innerContent.text?.substring(0, 20)}-${idx}`;
51
+ }
52
+ return `block-${idx}`;
53
+ };
54
+ // Helper to render text content (with JSON parsing if applicable)
55
+ const renderTextContent = (text, key) => {
56
+ // Try to parse as JSON
57
+ try {
58
+ const parsed = JSON.parse(text);
59
+ // If it's an object or array, render with ReactJson
60
+ if (typeof parsed === "object" && parsed !== null) {
61
+ return (_jsx("div", { className: "bg-gray-50 p-2 rounded border border-gray-200", children: _jsx(ReactJson, { src: parsed, name: false, collapsed: 1, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: {
62
+ fontSize: "11px",
63
+ backgroundColor: "transparent",
64
+ }, theme: "rjv-default" }) }, key));
65
+ }
66
+ }
67
+ catch {
68
+ // Not valid JSON, render as plain text
69
+ }
70
+ // Render as plain text
71
+ return (_jsx("pre", { className: "bg-gray-50 p-2.5 rounded border border-gray-200 text-xs overflow-x-auto font-mono text-gray-800", children: text }, key));
72
+ };
73
+ // Handle nested content blocks (ACP format)
74
+ if (block.type === "content" && "content" in block) {
75
+ const innerContent = block.content;
76
+ if (innerContent.type === "text" && innerContent.text) {
77
+ return renderTextContent(innerContent.text, getBlockKey());
78
+ }
79
+ }
80
+ // Handle direct text blocks
81
+ if (block.type === "text" && "text" in block) {
82
+ return renderTextContent(block.text, getBlockKey());
83
+ }
84
+ // Handle diff blocks
85
+ if (block.type === "diff" &&
86
+ "path" in block &&
87
+ "oldText" in block &&
88
+ "newText" in block) {
89
+ return (_jsxs("div", { className: "border rounded", children: [_jsxs("div", { className: "bg-gray-100 px-2 py-1 text-xs font-mono", children: [block.path, "line" in block &&
90
+ block.line !== null &&
91
+ block.line !== undefined &&
92
+ `:${block.line}`] }), _jsxs("div", { className: "p-2 font-mono text-xs", children: [_jsxs("div", { className: "text-red-600", children: ["- ", block.oldText] }), _jsxs("div", { className: "text-green-600", children: ["+ ", block.newText] })] })] }, getBlockKey()));
93
+ }
94
+ // Handle terminal blocks
95
+ if (block.type === "terminal" && "terminalId" in block) {
96
+ return (_jsxs("div", { className: "bg-black text-green-400 p-2 rounded text-xs font-mono", children: ["Terminal: ", block.terminalId] }, getBlockKey()));
97
+ }
98
+ return null;
99
+ }) })] })), toolCall.error && (_jsxs("div", { className: "bg-red-50 border border-red-200 rounded p-2.5 text-red-700", children: [_jsx("div", { className: "text-xs font-semibold text-red-600 uppercase tracking-wide mb-1.5", children: "Error" }), _jsx("div", { className: "text-xs", children: toolCall.error })] })), toolCall.tokenUsage && (_jsxs("div", { className: "bg-gray-50 border border-gray-200 rounded p-2.5", children: [_jsx("div", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2", children: "Token Usage" }), _jsxs("div", { className: "grid grid-cols-3 gap-3 text-xs text-gray-700", children: [toolCall.tokenUsage.inputTokens !== undefined && (_jsxs("div", { children: [_jsx("div", { className: "text-gray-500 text-[10px] uppercase tracking-wide mb-0.5", children: "Input" }), _jsx("div", { className: "font-medium", children: toolCall.tokenUsage.inputTokens.toLocaleString() })] })), toolCall.tokenUsage.outputTokens !== undefined && (_jsxs("div", { children: [_jsx("div", { className: "text-gray-500 text-[10px] uppercase tracking-wide mb-0.5", children: "Output" }), _jsx("div", { className: "font-medium", children: toolCall.tokenUsage.outputTokens.toLocaleString() })] })), toolCall.tokenUsage.totalTokens !== undefined && (_jsxs("div", { children: [_jsx("div", { className: "text-gray-500 text-[10px] uppercase tracking-wide mb-0.5", children: "Total" }), _jsx("div", { className: "font-medium", children: toolCall.tokenUsage.totalTokens.toLocaleString() })] }))] })] })), toolCall.startedAt && (_jsxs("div", { className: "text-xs text-gray-500", children: ["Started: ", new Date(toolCall.startedAt).toLocaleTimeString(), toolCall.completedAt && (_jsxs(_Fragment, { children: [" ", "| Completed:", " ", new Date(toolCall.completedAt).toLocaleTimeString(), " (", Math.round((toolCall.completedAt - toolCall.startedAt) / 1000), "s)"] }))] }))] }))] }));
100
+ }
@@ -0,0 +1,9 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface ToolCallListProps {
3
+ toolCalls: ToolCallType[];
4
+ groupBy?: "status" | "chronological";
5
+ }
6
+ /**
7
+ * ToolCallList component - renders a list of tool calls, optionally grouped
8
+ */
9
+ export declare function ToolCallList({ toolCalls, groupBy, }: ToolCallListProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ToolCall } from "./ToolCall.js";
3
+ /**
4
+ * ToolCallList component - renders a list of tool calls, optionally grouped
5
+ */
6
+ export function ToolCallList({ toolCalls, groupBy = "chronological", }) {
7
+ if (!toolCalls || toolCalls.length === 0) {
8
+ return null;
9
+ }
10
+ // Group by status if requested
11
+ if (groupBy === "status") {
12
+ const grouped = {
13
+ in_progress: toolCalls.filter((tc) => tc.status === "in_progress"),
14
+ pending: toolCalls.filter((tc) => tc.status === "pending"),
15
+ completed: toolCalls.filter((tc) => tc.status === "completed"),
16
+ failed: toolCalls.filter((tc) => tc.status === "failed"),
17
+ };
18
+ return (_jsxs("div", { className: "space-y-4", children: [grouped.in_progress.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-2", children: "In Progress" }), grouped.in_progress.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.pending.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Pending" }), grouped.pending.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.completed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Completed" }), grouped.completed.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.failed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Failed" }), grouped.failed.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] }))] }));
19
+ }
20
+ // Default: chronological order
21
+ return (_jsx("div", { className: "space-y-2", children: toolCalls.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id))) }));
22
+ }
@@ -29,3 +29,5 @@ export { Textarea, type TextareaProps, textareaVariants } from "./Textarea.js";
29
29
  export { ThinkingBlock, type ThinkingBlockProps, } from "./ThinkingBlock.js";
30
30
  export { TodoList, type TodoListProps } from "./TodoList.js";
31
31
  export { type TodoItem, TodoListItem, type TodoListItemProps, } from "./TodoListItem.js";
32
+ export { ToolCall, type ToolCallProps } from "./ToolCall.js";
33
+ export { ToolCallList, type ToolCallListProps } from "./ToolCallList.js";
@@ -36,3 +36,5 @@ export { Textarea, textareaVariants } from "./Textarea.js";
36
36
  export { ThinkingBlock, } from "./ThinkingBlock.js";
37
37
  export { TodoList } from "./TodoList.js";
38
38
  export { TodoListItem, } from "./TodoListItem.js";
39
+ export { ToolCall } from "./ToolCall.js";
40
+ export { ToolCallList } from "./ToolCallList.js";
@@ -0,0 +1,7 @@
1
+ import * as ResizablePrimitive from "react-resizable-panels";
2
+ declare const ResizablePanelGroup: ({ className, ...props }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => import("react/jsx-runtime").JSX.Element;
3
+ declare const ResizablePanel: ({ className, ...props }: React.ComponentProps<typeof ResizablePrimitive.Panel>) => import("react/jsx-runtime").JSX.Element;
4
+ declare const ResizableHandle: ({ withHandle, className, ...props }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
5
+ withHandle?: boolean;
6
+ }) => import("react/jsx-runtime").JSX.Element;
7
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as ResizablePrimitive from "react-resizable-panels";
3
+ import { cn } from "../lib/utils.js";
4
+ const ResizablePanelGroup = ({ className, ...props }) => (_jsx(ResizablePrimitive.PanelGroup, { className: cn("flex h-full w-full data-[panel-group-direction=vertical]:flex-col", className), ...props }));
5
+ const ResizablePanel = ({ className, ...props }) => (_jsx(ResizablePrimitive.Panel, { className: cn(className), ...props }));
6
+ const ResizableHandle = ({ withHandle, className, ...props }) => (_jsx(ResizablePrimitive.PanelResizeHandle, { className: cn("relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", className), ...props, children: withHandle && (_jsx("div", { className: "z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-2.5 w-2.5", children: [_jsx("title", { children: "Resize Handle" }), _jsx("circle", { cx: "9", cy: "12", r: "1" }), _jsx("circle", { cx: "9", cy: "5", r: "1" }), _jsx("circle", { cx: "9", cy: "19", r: "1" }), _jsx("circle", { cx: "15", cy: "12", r: "1" }), _jsx("circle", { cx: "15", cy: "5", r: "1" }), _jsx("circle", { cx: "15", cy: "19", r: "1" })] }) })) }));
7
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle };