@elizaos/client 1.5.5-alpha.10
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/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/assets/empty-module-CLMscLYw.js +1 -0
- package/dist/assets/main-BBZ_3lkn.css +5999 -0
- package/dist/assets/main-C5zNUkXH.js +7 -0
- package/dist/assets/main-Dz64ENQg.js +614 -0
- package/dist/assets/react-vendor-DM5m98rr.js +545 -0
- package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
- package/dist/elizaos-avatar.png +0 -0
- package/dist/elizaos-icon.png +0 -0
- package/dist/elizaos-logo-light.png +0 -0
- package/dist/elizaos.webp +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/images/agents/agent1.png +0 -0
- package/dist/images/agents/agent2.png +0 -0
- package/dist/images/agents/agent3.png +0 -0
- package/dist/images/agents/agent4.png +0 -0
- package/dist/images/agents/agent5.png +0 -0
- package/dist/index.html +14 -0
- package/index.html +24 -0
- package/package.json +159 -0
- package/postcss.config.js +3 -0
- package/public/elizaos-avatar.png +0 -0
- package/public/elizaos-icon.png +0 -0
- package/public/elizaos-logo-light.png +0 -0
- package/public/elizaos.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/agents/agent1.png +0 -0
- package/public/images/agents/agent2.png +0 -0
- package/public/images/agents/agent3.png +0 -0
- package/public/images/agents/agent4.png +0 -0
- package/public/images/agents/agent5.png +0 -0
- package/src/App.tsx +222 -0
- package/src/components/AgentDetailsPanel.tsx +147 -0
- package/src/components/ChatInputArea.tsx +196 -0
- package/src/components/ChatMessageListComponent.tsx +139 -0
- package/src/components/actionTool.tsx +186 -0
- package/src/components/add-agent-card.tsx +77 -0
- package/src/components/agent-action-viewer.tsx +816 -0
- package/src/components/agent-avatar-stack.tsx +121 -0
- package/src/components/agent-card.cy.tsx +259 -0
- package/src/components/agent-card.tsx +177 -0
- package/src/components/agent-creator.tsx +142 -0
- package/src/components/agent-log-viewer.tsx +645 -0
- package/src/components/agent-memory-edit-overlay.tsx +461 -0
- package/src/components/agent-memory-viewer.tsx +504 -0
- package/src/components/agent-settings.tsx +270 -0
- package/src/components/agent-sidebar.tsx +178 -0
- package/src/components/api-key-dialog.tsx +113 -0
- package/src/components/app-sidebar.tsx +685 -0
- package/src/components/array-input.tsx +116 -0
- package/src/components/audio-recorder.tsx +292 -0
- package/src/components/avatar-panel.tsx +141 -0
- package/src/components/character-form.tsx +1138 -0
- package/src/components/chat.tsx +1813 -0
- package/src/components/combobox.tsx +187 -0
- package/src/components/confirmation-dialog.tsx +59 -0
- package/src/components/connection-error-banner.tsx +101 -0
- package/src/components/connection-status.cy.tsx +73 -0
- package/src/components/connection-status.tsx +155 -0
- package/src/components/copy-button.tsx +35 -0
- package/src/components/delete-button.tsx +24 -0
- package/src/components/env-settings.tsx +261 -0
- package/src/components/group-card.tsx +160 -0
- package/src/components/group-panel.tsx +543 -0
- package/src/components/input-copy.tsx +21 -0
- package/src/components/logs-page.tsx +41 -0
- package/src/components/media-content.tsx +385 -0
- package/src/components/memory-graph.tsx +170 -0
- package/src/components/missing-secrets-dialog.tsx +72 -0
- package/src/components/onboarding-tour.tsx +247 -0
- package/src/components/page-title.tsx +8 -0
- package/src/components/plugins-panel.tsx +383 -0
- package/src/components/profile-card.tsx +66 -0
- package/src/components/profile-overlay.tsx +283 -0
- package/src/components/retry-button.tsx +28 -0
- package/src/components/secret-panel.tsx +1505 -0
- package/src/components/server-management.tsx +264 -0
- package/src/components/split-button.tsx +148 -0
- package/src/components/stop-agent-button.tsx +99 -0
- package/src/components/ui/alert-dialog.cy.tsx +333 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/avatar.cy.tsx +180 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.cy.tsx +146 -0
- package/src/components/ui/badge.tsx +43 -0
- package/src/components/ui/button.cy.tsx +177 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.cy.tsx +160 -0
- package/src/components/ui/card.tsx +73 -0
- package/src/components/ui/chat/animated-markdown.tsx +59 -0
- package/src/components/ui/chat/chat-bubble.tsx +178 -0
- package/src/components/ui/chat/chat-container.tsx +51 -0
- package/src/components/ui/chat/chat-input.cy.tsx +169 -0
- package/src/components/ui/chat/chat-input.tsx +47 -0
- package/src/components/ui/chat/chat-message-list.tsx +61 -0
- package/src/components/ui/chat/chat-tts-button.tsx +199 -0
- package/src/components/ui/chat/code-block.tsx +79 -0
- package/src/components/ui/chat/expandable-chat.tsx +131 -0
- package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
- package/src/components/ui/chat/markdown.tsx +209 -0
- package/src/components/ui/chat/message-loading.tsx +48 -0
- package/src/components/ui/checkbox.cy.tsx +170 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.cy.tsx +283 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.cy.tsx +313 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/dialog.cy.tsx +279 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/dropdown-menu.cy.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +281 -0
- package/src/components/ui/input.cy.tsx +82 -0
- package/src/components/ui/input.tsx +27 -0
- package/src/components/ui/label.cy.tsx +157 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/resizable.tsx +42 -0
- package/src/components/ui/scroll-area.cy.tsx +242 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.cy.tsx +277 -0
- package/src/components/ui/select.tsx +155 -0
- package/src/components/ui/separator.cy.tsx +145 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.cy.tsx +324 -0
- package/src/components/ui/sheet.tsx +119 -0
- package/src/components/ui/sidebar.tsx +734 -0
- package/src/components/ui/skeleton.cy.tsx +149 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/split-button.cy.tsx +274 -0
- package/src/components/ui/split-button.tsx +112 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tabs.cy.tsx +271 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.cy.tsx +136 -0
- package/src/components/ui/textarea.tsx +26 -0
- package/src/components/ui/toast.cy.tsx +209 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/toaster.tsx +29 -0
- package/src/components/ui/tooltip.cy.tsx +244 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/config/agent-templates.ts +349 -0
- package/src/config/voice-models.ts +181 -0
- package/src/constants.ts +23 -0
- package/src/context/AuthContext.tsx +44 -0
- package/src/context/ConnectionContext.tsx +194 -0
- package/src/entry.tsx +9 -0
- package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
- package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
- package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
- package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
- package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
- package/src/hooks/use-agent-management.ts +130 -0
- package/src/hooks/use-agent-tab-state.ts +74 -0
- package/src/hooks/use-agent-update.ts +469 -0
- package/src/hooks/use-character-convert.ts +138 -0
- package/src/hooks/use-confirmation.ts +55 -0
- package/src/hooks/use-delete-agent.ts +123 -0
- package/src/hooks/use-dm-channels.ts +198 -0
- package/src/hooks/use-elevenlabs-voices.ts +83 -0
- package/src/hooks/use-file-upload.ts +224 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-onboarding.tsx +49 -0
- package/src/hooks/use-panel-width-state.ts +147 -0
- package/src/hooks/use-partial-update.ts +288 -0
- package/src/hooks/use-plugin-details.ts +462 -0
- package/src/hooks/use-plugins.ts +119 -0
- package/src/hooks/use-query-hooks.ts +1263 -0
- package/src/hooks/use-server-agents.ts +62 -0
- package/src/hooks/use-server-version.tsx +47 -0
- package/src/hooks/use-sidebar-state.ts +50 -0
- package/src/hooks/use-socket-chat.ts +264 -0
- package/src/hooks/use-toast.ts +260 -0
- package/src/hooks/use-version.tsx +64 -0
- package/src/index.css +146 -0
- package/src/lib/api-client-config.ts +53 -0
- package/src/lib/api-type-mappers.ts +196 -0
- package/src/lib/export-utils.ts +123 -0
- package/src/lib/logger.ts +19 -0
- package/src/lib/media-utils.ts +170 -0
- package/src/lib/pca.test.ts +17 -0
- package/src/lib/pca.ts +52 -0
- package/src/lib/socketio-manager.ts +664 -0
- package/src/lib/utils.ts +168 -0
- package/src/main.tsx +16 -0
- package/src/mocks/empty-module.ts +12 -0
- package/src/mocks/node-module.ts +57 -0
- package/src/polyfills.ts +37 -0
- package/src/routes/agent-detail.tsx +30 -0
- package/src/routes/agent-list.tsx +27 -0
- package/src/routes/agent-settings.tsx +48 -0
- package/src/routes/character-detail.tsx +52 -0
- package/src/routes/character-form.tsx +79 -0
- package/src/routes/character-list.tsx +38 -0
- package/src/routes/chat.tsx +128 -0
- package/src/routes/createAgent.tsx +13 -0
- package/src/routes/group-new.tsx +50 -0
- package/src/routes/group.tsx +29 -0
- package/src/routes/home.tsx +218 -0
- package/src/routes/not-found.tsx +71 -0
- package/src/test/setup.ts +154 -0
- package/src/types/crypto-browserify.d.ts +4 -0
- package/src/types/index.ts +13 -0
- package/src/types/rooms.ts +8 -0
- package/src/types.ts +84 -0
- package/src/vite-env.d.ts +40 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +102 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
5
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
import { cn } from '@/lib/utils';
|
|
8
|
+
|
|
9
|
+
type PointerDownEvent = Parameters<
|
|
10
|
+
NonNullable<DropdownMenuPrimitive.DropdownMenuContentProps['onPointerDown']>
|
|
11
|
+
>[0];
|
|
12
|
+
type PointerDownOutsideEvent = Parameters<
|
|
13
|
+
NonNullable<DropdownMenuPrimitive.DropdownMenuContentProps['onPointerDownOutside']>
|
|
14
|
+
>[0];
|
|
15
|
+
|
|
16
|
+
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
17
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DropdownMenuPortal({
|
|
21
|
+
...props
|
|
22
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
|
23
|
+
return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function DropdownMenuTrigger({
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
29
|
+
return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function DropdownMenuContent({
|
|
33
|
+
className,
|
|
34
|
+
sideOffset = 4,
|
|
35
|
+
onPointerDown,
|
|
36
|
+
onPointerDownOutside,
|
|
37
|
+
onCloseAutoFocus,
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
40
|
+
const isCloseFromMouse = React.useRef<boolean>(false);
|
|
41
|
+
|
|
42
|
+
const handlePointerDown = React.useCallback(
|
|
43
|
+
(e: PointerDownEvent) => {
|
|
44
|
+
isCloseFromMouse.current = true;
|
|
45
|
+
onPointerDown?.(e);
|
|
46
|
+
},
|
|
47
|
+
[onPointerDown]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const handlePointerDownOutside = React.useCallback(
|
|
51
|
+
(e: PointerDownOutsideEvent) => {
|
|
52
|
+
isCloseFromMouse.current = true;
|
|
53
|
+
onPointerDownOutside?.(e);
|
|
54
|
+
},
|
|
55
|
+
[onPointerDownOutside]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const handleCloseAutoFocus = React.useCallback(
|
|
59
|
+
(e: Event) => {
|
|
60
|
+
if (onCloseAutoFocus) {
|
|
61
|
+
return onCloseAutoFocus(e);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!isCloseFromMouse.current) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
isCloseFromMouse.current = false;
|
|
70
|
+
},
|
|
71
|
+
[onCloseAutoFocus]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<DropdownMenuPrimitive.Portal>
|
|
76
|
+
<DropdownMenuPrimitive.Content
|
|
77
|
+
data-slot="dropdown-menu-content"
|
|
78
|
+
sideOffset={sideOffset}
|
|
79
|
+
className={cn(
|
|
80
|
+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md border p-1 shadow-lg',
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
onPointerDown={handlePointerDown}
|
|
84
|
+
onPointerDownOutside={handlePointerDownOutside}
|
|
85
|
+
onCloseAutoFocus={handleCloseAutoFocus}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
</DropdownMenuPrimitive.Portal>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
93
|
+
return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function DropdownMenuItem({
|
|
97
|
+
className,
|
|
98
|
+
inset,
|
|
99
|
+
variant = 'default',
|
|
100
|
+
...props
|
|
101
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
102
|
+
inset?: boolean;
|
|
103
|
+
variant?: 'default' | 'destructive';
|
|
104
|
+
}) {
|
|
105
|
+
return (
|
|
106
|
+
<DropdownMenuPrimitive.Item
|
|
107
|
+
data-slot="dropdown-menu-item"
|
|
108
|
+
data-inset={inset}
|
|
109
|
+
data-variant={variant}
|
|
110
|
+
className={cn(
|
|
111
|
+
'focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground data-[variant=destructive]:font-normal relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm font-medium outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function DropdownMenuCheckboxItem({
|
|
120
|
+
className,
|
|
121
|
+
children,
|
|
122
|
+
checked,
|
|
123
|
+
...props
|
|
124
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
125
|
+
return (
|
|
126
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
127
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
128
|
+
className={cn(
|
|
129
|
+
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
130
|
+
className
|
|
131
|
+
)}
|
|
132
|
+
checked={checked}
|
|
133
|
+
{...props}
|
|
134
|
+
>
|
|
135
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
136
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
137
|
+
<CheckIcon size={16} />
|
|
138
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
139
|
+
</span>
|
|
140
|
+
{children}
|
|
141
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function DropdownMenuRadioGroup({
|
|
146
|
+
...props
|
|
147
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
|
148
|
+
return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function DropdownMenuRadioItem({
|
|
152
|
+
className,
|
|
153
|
+
children,
|
|
154
|
+
...props
|
|
155
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
156
|
+
return (
|
|
157
|
+
<DropdownMenuPrimitive.RadioItem
|
|
158
|
+
data-slot="dropdown-menu-radio-item"
|
|
159
|
+
className={cn(
|
|
160
|
+
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
161
|
+
className
|
|
162
|
+
)}
|
|
163
|
+
{...props}
|
|
164
|
+
>
|
|
165
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
166
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
167
|
+
<CircleIcon className="size-2 fill-current" />
|
|
168
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
169
|
+
</span>
|
|
170
|
+
{children}
|
|
171
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function DropdownMenuLabel({
|
|
176
|
+
className,
|
|
177
|
+
inset,
|
|
178
|
+
...props
|
|
179
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
180
|
+
inset?: boolean;
|
|
181
|
+
}) {
|
|
182
|
+
return (
|
|
183
|
+
<DropdownMenuPrimitive.Label
|
|
184
|
+
data-slot="dropdown-menu-label"
|
|
185
|
+
data-inset={inset}
|
|
186
|
+
className={cn(
|
|
187
|
+
'text-muted-foreground px-2 py-1.5 text-xs font-medium data-[inset]:pl-8',
|
|
188
|
+
className
|
|
189
|
+
)}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function DropdownMenuSeparator({
|
|
196
|
+
className,
|
|
197
|
+
...props
|
|
198
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
199
|
+
return (
|
|
200
|
+
<DropdownMenuPrimitive.Separator
|
|
201
|
+
data-slot="dropdown-menu-separator"
|
|
202
|
+
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
|
203
|
+
{...props}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
|
|
209
|
+
return (
|
|
210
|
+
<kbd
|
|
211
|
+
data-slot="dropdown-menu-shortcut"
|
|
212
|
+
className={cn(
|
|
213
|
+
'bg-background text-muted-foreground/70 ms-auto -me-1 inline-flex h-5 max-h-full items-center rounded border px-1 font-[inherit] text-[0.625rem] font-medium',
|
|
214
|
+
className
|
|
215
|
+
)}
|
|
216
|
+
{...props}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
222
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function DropdownMenuSubTrigger({
|
|
226
|
+
className,
|
|
227
|
+
inset,
|
|
228
|
+
children,
|
|
229
|
+
...props
|
|
230
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
231
|
+
inset?: boolean;
|
|
232
|
+
}) {
|
|
233
|
+
return (
|
|
234
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
235
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
236
|
+
data-inset={inset}
|
|
237
|
+
className={cn(
|
|
238
|
+
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
|
239
|
+
className
|
|
240
|
+
)}
|
|
241
|
+
{...props}
|
|
242
|
+
>
|
|
243
|
+
{children}
|
|
244
|
+
<ChevronRightIcon size={16} className="text-muted-foreground/80 ml-auto" />
|
|
245
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function DropdownMenuSubContent({
|
|
250
|
+
className,
|
|
251
|
+
...props
|
|
252
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
253
|
+
return (
|
|
254
|
+
<DropdownMenuPrimitive.SubContent
|
|
255
|
+
data-slot="dropdown-menu-sub-content"
|
|
256
|
+
className={cn(
|
|
257
|
+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md border p-1 shadow-lg',
|
|
258
|
+
className
|
|
259
|
+
)}
|
|
260
|
+
{...props}
|
|
261
|
+
/>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export {
|
|
266
|
+
DropdownMenu,
|
|
267
|
+
DropdownMenuCheckboxItem,
|
|
268
|
+
DropdownMenuContent,
|
|
269
|
+
DropdownMenuGroup,
|
|
270
|
+
DropdownMenuItem,
|
|
271
|
+
DropdownMenuLabel,
|
|
272
|
+
DropdownMenuPortal,
|
|
273
|
+
DropdownMenuRadioGroup,
|
|
274
|
+
DropdownMenuRadioItem,
|
|
275
|
+
DropdownMenuSeparator,
|
|
276
|
+
DropdownMenuShortcut,
|
|
277
|
+
DropdownMenuSub,
|
|
278
|
+
DropdownMenuSubContent,
|
|
279
|
+
DropdownMenuSubTrigger,
|
|
280
|
+
DropdownMenuTrigger,
|
|
281
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
/// <reference path="../../../cypress/support/types.d.ts" />
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Input } from './input';
|
|
6
|
+
|
|
7
|
+
describe('Input Component', () => {
|
|
8
|
+
it('renders correctly', () => {
|
|
9
|
+
cy.mount(<Input placeholder="Enter text" />);
|
|
10
|
+
|
|
11
|
+
cy.get('input').should('exist');
|
|
12
|
+
cy.get('input').should('have.attr', 'placeholder', 'Enter text');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('accepts user input', () => {
|
|
16
|
+
cy.mount(<Input />);
|
|
17
|
+
|
|
18
|
+
const testText = 'Hello, World!';
|
|
19
|
+
cy.get('input').type(testText);
|
|
20
|
+
cy.get('input').should('have.value', testText);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('can be disabled', () => {
|
|
24
|
+
cy.mount(<Input disabled />);
|
|
25
|
+
|
|
26
|
+
cy.get('input').should('be.disabled');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('accepts different types', () => {
|
|
30
|
+
const types = ['text', 'email', 'password', 'number'] as const;
|
|
31
|
+
|
|
32
|
+
types.forEach((type) => {
|
|
33
|
+
cy.mount(<Input type={type} />);
|
|
34
|
+
cy.get('input').should('have.attr', 'type', type);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('applies custom className', () => {
|
|
39
|
+
cy.mount(<Input className="custom-class bg-red-500" />);
|
|
40
|
+
|
|
41
|
+
cy.get('input').should('have.class', 'custom-class');
|
|
42
|
+
cy.get('input').should('have.class', 'bg-red-500');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('handles onChange events', () => {
|
|
46
|
+
const onChange = cy.stub();
|
|
47
|
+
|
|
48
|
+
cy.mount(<Input onChange={onChange} />);
|
|
49
|
+
|
|
50
|
+
cy.get('input').type('test');
|
|
51
|
+
cy.wrap(onChange).should('have.been.called');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('forwards ref correctly', () => {
|
|
55
|
+
// Test that ref prop is accepted without errors
|
|
56
|
+
const ref = React.createRef<HTMLInputElement>();
|
|
57
|
+
cy.mount(<Input ref={ref} data-testid="ref-input" />);
|
|
58
|
+
|
|
59
|
+
// Verify the input is rendered
|
|
60
|
+
cy.get('[data-testid="ref-input"]').should('exist');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('supports readonly state', () => {
|
|
64
|
+
cy.mount(<Input readOnly value="Read only text" />);
|
|
65
|
+
|
|
66
|
+
cy.get('input').should('have.attr', 'readonly');
|
|
67
|
+
cy.get('input').should('have.value', 'Read only text');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('supports max length', () => {
|
|
71
|
+
cy.mount(<Input maxLength={10} />);
|
|
72
|
+
|
|
73
|
+
cy.get('input').type('This is a very long text');
|
|
74
|
+
cy.get('input').should('have.value', 'This is a ');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('supports autoComplete', () => {
|
|
78
|
+
cy.mount(<Input autoComplete="email" />);
|
|
79
|
+
|
|
80
|
+
cy.get('input').should('have.attr', 'autocomplete', 'email');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
interface InputProps extends React.ComponentProps<'input'> {
|
|
6
|
+
'data-testid'?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
10
|
+
({ className, type, ...props }, ref) => {
|
|
11
|
+
return (
|
|
12
|
+
<input
|
|
13
|
+
type={type}
|
|
14
|
+
className={cn(
|
|
15
|
+
'flex h-12 w-full rounded border border-input bg-card px-4 py-3 text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
ref={ref}
|
|
19
|
+
data-testid={props['data-testid'] || 'input'}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
Input.displayName = 'Input';
|
|
26
|
+
|
|
27
|
+
export { Input };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
/// <reference path="../../../cypress/support/types.d.ts" />
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Label } from './label';
|
|
6
|
+
|
|
7
|
+
describe('Label Component', () => {
|
|
8
|
+
it('renders correctly with default props', () => {
|
|
9
|
+
cy.mount(<Label>Email Address</Label>);
|
|
10
|
+
|
|
11
|
+
cy.get('label').should('exist');
|
|
12
|
+
cy.get('label').should('contain', 'Email Address');
|
|
13
|
+
cy.get('label').should('have.class', 'text-sm');
|
|
14
|
+
cy.get('label').should('have.class', 'font-normal');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('associates with form controls using htmlFor', () => {
|
|
18
|
+
cy.mount(
|
|
19
|
+
<div>
|
|
20
|
+
<Label htmlFor="email-input">Email</Label>
|
|
21
|
+
<input id="email-input" type="email" />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
cy.get('label').should('have.attr', 'for', 'email-input');
|
|
26
|
+
// Clicking the label should focus the input
|
|
27
|
+
cy.get('label').click();
|
|
28
|
+
cy.get('#email-input').should('have.focus');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('applies custom className', () => {
|
|
32
|
+
cy.mount(<Label className="custom-label text-lg">Custom Label</Label>);
|
|
33
|
+
|
|
34
|
+
cy.get('label').should('have.class', 'custom-label').should('have.class', 'text-lg');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders with required indicator', () => {
|
|
38
|
+
cy.mount(
|
|
39
|
+
<Label>
|
|
40
|
+
Username <span className="text-red-500">*</span>
|
|
41
|
+
</Label>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
cy.get('label').should('contain', 'Username');
|
|
45
|
+
cy.get('label').find('.text-red-500').should('contain', '*');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('works with checkbox inputs', () => {
|
|
49
|
+
cy.mount(
|
|
50
|
+
<div className="flex items-center space-x-2">
|
|
51
|
+
<input type="checkbox" id="terms" />
|
|
52
|
+
<Label htmlFor="terms">Accept terms and conditions</Label>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
cy.get('label').click();
|
|
57
|
+
cy.get('#terms').should('be.checked');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('works with radio inputs', () => {
|
|
61
|
+
cy.mount(
|
|
62
|
+
<div className="space-y-2">
|
|
63
|
+
<div className="flex items-center space-x-2">
|
|
64
|
+
<input type="radio" id="option1" name="options" />
|
|
65
|
+
<Label htmlFor="option1">Option 1</Label>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="flex items-center space-x-2">
|
|
68
|
+
<input type="radio" id="option2" name="options" />
|
|
69
|
+
<Label htmlFor="option2">Option 2</Label>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
cy.get('label').first().click();
|
|
75
|
+
cy.get('#option1').should('be.checked');
|
|
76
|
+
cy.get('#option2').should('not.be.checked');
|
|
77
|
+
|
|
78
|
+
cy.get('label').last().click();
|
|
79
|
+
cy.get('#option1').should('not.be.checked');
|
|
80
|
+
cy.get('#option2').should('be.checked');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('supports disabled state styling', () => {
|
|
84
|
+
cy.mount(<Label className="peer-disabled:opacity-50">Disabled Label</Label>);
|
|
85
|
+
|
|
86
|
+
cy.get('label').should('have.class', 'peer-disabled:opacity-50');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('renders in a form layout', () => {
|
|
90
|
+
cy.mount(
|
|
91
|
+
<form className="space-y-4">
|
|
92
|
+
<div className="space-y-2">
|
|
93
|
+
<Label htmlFor="name">Name</Label>
|
|
94
|
+
<input id="name" type="text" className="w-full border rounded px-2 py-1" />
|
|
95
|
+
</div>
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
<Label htmlFor="email">Email</Label>
|
|
98
|
+
<input id="email" type="email" className="w-full border rounded px-2 py-1" />
|
|
99
|
+
</div>
|
|
100
|
+
</form>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
cy.get('label').should('have.length', 2);
|
|
104
|
+
cy.get('label').first().should('contain', 'Name');
|
|
105
|
+
cy.get('label').last().should('contain', 'Email');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('supports custom styles', () => {
|
|
109
|
+
cy.mount(
|
|
110
|
+
<Label className="text-blue-600 uppercase tracking-wide" style={{ fontSize: '14px' }}>
|
|
111
|
+
Styled Label
|
|
112
|
+
</Label>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
cy.get('label')
|
|
116
|
+
.should('have.class', 'text-blue-600')
|
|
117
|
+
.should('have.class', 'uppercase')
|
|
118
|
+
.should('have.class', 'tracking-wide')
|
|
119
|
+
.should('have.css', 'font-size', '14px');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('works with nested content', () => {
|
|
123
|
+
cy.mount(
|
|
124
|
+
<Label>
|
|
125
|
+
<span className="font-bold">Bold part</span> normal part
|
|
126
|
+
</Label>
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
cy.get('label').find('.font-bold').should('contain', 'Bold part');
|
|
130
|
+
cy.get('label').should('contain', 'normal part');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('supports data attributes', () => {
|
|
134
|
+
cy.mount(
|
|
135
|
+
<Label data-testid="custom-label" data-field="username">
|
|
136
|
+
Username
|
|
137
|
+
</Label>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
cy.get('[data-testid="custom-label"]')
|
|
141
|
+
.should('exist')
|
|
142
|
+
.should('have.attr', 'data-field', 'username');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('maintains proper spacing in form groups', () => {
|
|
146
|
+
cy.mount(
|
|
147
|
+
<div className="space-y-1">
|
|
148
|
+
<Label htmlFor="field">Field Label</Label>
|
|
149
|
+
<input id="field" className="w-full" />
|
|
150
|
+
<p className="text-sm text-gray-500">Helper text</p>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
cy.get('label').should('be.visible');
|
|
155
|
+
cy.get('.space-y-1').should('exist');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
2
|
+
import { type VariantProps, cva } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
'text-sm font-normal text-muted-foreground leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
16
|
+
));
|
|
17
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
export { Label };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { GripVertical } from 'lucide-react';
|
|
4
|
+
import * as ResizablePrimitive from 'react-resizable-panels';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
const ResizablePanelGroup = ({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
|
|
12
|
+
<ResizablePrimitive.PanelGroup
|
|
13
|
+
className={cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const ResizablePanel = ResizablePrimitive.Panel;
|
|
19
|
+
|
|
20
|
+
const ResizableHandle = ({
|
|
21
|
+
withHandle,
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
|
25
|
+
withHandle?: boolean;
|
|
26
|
+
}) => (
|
|
27
|
+
<ResizablePrimitive.PanelResizeHandle
|
|
28
|
+
className={cn(
|
|
29
|
+
'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',
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{withHandle && (
|
|
35
|
+
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
|
|
36
|
+
<GripVertical className="h-2.5 w-2.5" />
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</ResizablePrimitive.PanelResizeHandle>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
|