@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.
Files changed (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/assets/empty-module-CLMscLYw.js +1 -0
  4. package/dist/assets/main-BBZ_3lkn.css +5999 -0
  5. package/dist/assets/main-C5zNUkXH.js +7 -0
  6. package/dist/assets/main-Dz64ENQg.js +614 -0
  7. package/dist/assets/react-vendor-DM5m98rr.js +545 -0
  8. package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
  9. package/dist/elizaos-avatar.png +0 -0
  10. package/dist/elizaos-icon.png +0 -0
  11. package/dist/elizaos-logo-light.png +0 -0
  12. package/dist/elizaos.webp +0 -0
  13. package/dist/favicon.ico +0 -0
  14. package/dist/images/agents/agent1.png +0 -0
  15. package/dist/images/agents/agent2.png +0 -0
  16. package/dist/images/agents/agent3.png +0 -0
  17. package/dist/images/agents/agent4.png +0 -0
  18. package/dist/images/agents/agent5.png +0 -0
  19. package/dist/index.html +14 -0
  20. package/index.html +24 -0
  21. package/package.json +159 -0
  22. package/postcss.config.js +3 -0
  23. package/public/elizaos-avatar.png +0 -0
  24. package/public/elizaos-icon.png +0 -0
  25. package/public/elizaos-logo-light.png +0 -0
  26. package/public/elizaos.webp +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/images/agents/agent1.png +0 -0
  29. package/public/images/agents/agent2.png +0 -0
  30. package/public/images/agents/agent3.png +0 -0
  31. package/public/images/agents/agent4.png +0 -0
  32. package/public/images/agents/agent5.png +0 -0
  33. package/src/App.tsx +222 -0
  34. package/src/components/AgentDetailsPanel.tsx +147 -0
  35. package/src/components/ChatInputArea.tsx +196 -0
  36. package/src/components/ChatMessageListComponent.tsx +139 -0
  37. package/src/components/actionTool.tsx +186 -0
  38. package/src/components/add-agent-card.tsx +77 -0
  39. package/src/components/agent-action-viewer.tsx +816 -0
  40. package/src/components/agent-avatar-stack.tsx +121 -0
  41. package/src/components/agent-card.cy.tsx +259 -0
  42. package/src/components/agent-card.tsx +177 -0
  43. package/src/components/agent-creator.tsx +142 -0
  44. package/src/components/agent-log-viewer.tsx +645 -0
  45. package/src/components/agent-memory-edit-overlay.tsx +461 -0
  46. package/src/components/agent-memory-viewer.tsx +504 -0
  47. package/src/components/agent-settings.tsx +270 -0
  48. package/src/components/agent-sidebar.tsx +178 -0
  49. package/src/components/api-key-dialog.tsx +113 -0
  50. package/src/components/app-sidebar.tsx +685 -0
  51. package/src/components/array-input.tsx +116 -0
  52. package/src/components/audio-recorder.tsx +292 -0
  53. package/src/components/avatar-panel.tsx +141 -0
  54. package/src/components/character-form.tsx +1138 -0
  55. package/src/components/chat.tsx +1813 -0
  56. package/src/components/combobox.tsx +187 -0
  57. package/src/components/confirmation-dialog.tsx +59 -0
  58. package/src/components/connection-error-banner.tsx +101 -0
  59. package/src/components/connection-status.cy.tsx +73 -0
  60. package/src/components/connection-status.tsx +155 -0
  61. package/src/components/copy-button.tsx +35 -0
  62. package/src/components/delete-button.tsx +24 -0
  63. package/src/components/env-settings.tsx +261 -0
  64. package/src/components/group-card.tsx +160 -0
  65. package/src/components/group-panel.tsx +543 -0
  66. package/src/components/input-copy.tsx +21 -0
  67. package/src/components/logs-page.tsx +41 -0
  68. package/src/components/media-content.tsx +385 -0
  69. package/src/components/memory-graph.tsx +170 -0
  70. package/src/components/missing-secrets-dialog.tsx +72 -0
  71. package/src/components/onboarding-tour.tsx +247 -0
  72. package/src/components/page-title.tsx +8 -0
  73. package/src/components/plugins-panel.tsx +383 -0
  74. package/src/components/profile-card.tsx +66 -0
  75. package/src/components/profile-overlay.tsx +283 -0
  76. package/src/components/retry-button.tsx +28 -0
  77. package/src/components/secret-panel.tsx +1505 -0
  78. package/src/components/server-management.tsx +264 -0
  79. package/src/components/split-button.tsx +148 -0
  80. package/src/components/stop-agent-button.tsx +99 -0
  81. package/src/components/ui/alert-dialog.cy.tsx +333 -0
  82. package/src/components/ui/alert-dialog.tsx +115 -0
  83. package/src/components/ui/alert.tsx +49 -0
  84. package/src/components/ui/avatar.cy.tsx +180 -0
  85. package/src/components/ui/avatar.tsx +57 -0
  86. package/src/components/ui/badge.cy.tsx +146 -0
  87. package/src/components/ui/badge.tsx +43 -0
  88. package/src/components/ui/button.cy.tsx +177 -0
  89. package/src/components/ui/button.tsx +56 -0
  90. package/src/components/ui/card.cy.tsx +160 -0
  91. package/src/components/ui/card.tsx +73 -0
  92. package/src/components/ui/chat/animated-markdown.tsx +59 -0
  93. package/src/components/ui/chat/chat-bubble.tsx +178 -0
  94. package/src/components/ui/chat/chat-container.tsx +51 -0
  95. package/src/components/ui/chat/chat-input.cy.tsx +169 -0
  96. package/src/components/ui/chat/chat-input.tsx +47 -0
  97. package/src/components/ui/chat/chat-message-list.tsx +61 -0
  98. package/src/components/ui/chat/chat-tts-button.tsx +199 -0
  99. package/src/components/ui/chat/code-block.tsx +79 -0
  100. package/src/components/ui/chat/expandable-chat.tsx +131 -0
  101. package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
  102. package/src/components/ui/chat/markdown.tsx +209 -0
  103. package/src/components/ui/chat/message-loading.tsx +48 -0
  104. package/src/components/ui/checkbox.cy.tsx +170 -0
  105. package/src/components/ui/checkbox.tsx +30 -0
  106. package/src/components/ui/collapsible.cy.tsx +283 -0
  107. package/src/components/ui/collapsible.tsx +9 -0
  108. package/src/components/ui/command.cy.tsx +313 -0
  109. package/src/components/ui/command.tsx +143 -0
  110. package/src/components/ui/dialog.cy.tsx +279 -0
  111. package/src/components/ui/dialog.tsx +104 -0
  112. package/src/components/ui/dropdown-menu.cy.tsx +273 -0
  113. package/src/components/ui/dropdown-menu.tsx +281 -0
  114. package/src/components/ui/input.cy.tsx +82 -0
  115. package/src/components/ui/input.tsx +27 -0
  116. package/src/components/ui/label.cy.tsx +157 -0
  117. package/src/components/ui/label.tsx +19 -0
  118. package/src/components/ui/resizable.tsx +42 -0
  119. package/src/components/ui/scroll-area.cy.tsx +242 -0
  120. package/src/components/ui/scroll-area.tsx +46 -0
  121. package/src/components/ui/select.cy.tsx +277 -0
  122. package/src/components/ui/select.tsx +155 -0
  123. package/src/components/ui/separator.cy.tsx +145 -0
  124. package/src/components/ui/separator.tsx +29 -0
  125. package/src/components/ui/sheet.cy.tsx +324 -0
  126. package/src/components/ui/sheet.tsx +119 -0
  127. package/src/components/ui/sidebar.tsx +734 -0
  128. package/src/components/ui/skeleton.cy.tsx +149 -0
  129. package/src/components/ui/skeleton.tsx +17 -0
  130. package/src/components/ui/split-button.cy.tsx +274 -0
  131. package/src/components/ui/split-button.tsx +112 -0
  132. package/src/components/ui/switch.tsx +28 -0
  133. package/src/components/ui/tabs.cy.tsx +271 -0
  134. package/src/components/ui/tabs.tsx +53 -0
  135. package/src/components/ui/textarea.cy.tsx +136 -0
  136. package/src/components/ui/textarea.tsx +26 -0
  137. package/src/components/ui/toast.cy.tsx +209 -0
  138. package/src/components/ui/toast.tsx +126 -0
  139. package/src/components/ui/toaster.tsx +29 -0
  140. package/src/components/ui/tooltip.cy.tsx +244 -0
  141. package/src/components/ui/tooltip.tsx +30 -0
  142. package/src/config/agent-templates.ts +349 -0
  143. package/src/config/voice-models.ts +181 -0
  144. package/src/constants.ts +23 -0
  145. package/src/context/AuthContext.tsx +44 -0
  146. package/src/context/ConnectionContext.tsx +194 -0
  147. package/src/entry.tsx +9 -0
  148. package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
  149. package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
  150. package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
  151. package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
  152. package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
  153. package/src/hooks/use-agent-management.ts +130 -0
  154. package/src/hooks/use-agent-tab-state.ts +74 -0
  155. package/src/hooks/use-agent-update.ts +469 -0
  156. package/src/hooks/use-character-convert.ts +138 -0
  157. package/src/hooks/use-confirmation.ts +55 -0
  158. package/src/hooks/use-delete-agent.ts +123 -0
  159. package/src/hooks/use-dm-channels.ts +198 -0
  160. package/src/hooks/use-elevenlabs-voices.ts +83 -0
  161. package/src/hooks/use-file-upload.ts +224 -0
  162. package/src/hooks/use-mobile.tsx +19 -0
  163. package/src/hooks/use-onboarding.tsx +49 -0
  164. package/src/hooks/use-panel-width-state.ts +147 -0
  165. package/src/hooks/use-partial-update.ts +288 -0
  166. package/src/hooks/use-plugin-details.ts +462 -0
  167. package/src/hooks/use-plugins.ts +119 -0
  168. package/src/hooks/use-query-hooks.ts +1263 -0
  169. package/src/hooks/use-server-agents.ts +62 -0
  170. package/src/hooks/use-server-version.tsx +47 -0
  171. package/src/hooks/use-sidebar-state.ts +50 -0
  172. package/src/hooks/use-socket-chat.ts +264 -0
  173. package/src/hooks/use-toast.ts +260 -0
  174. package/src/hooks/use-version.tsx +64 -0
  175. package/src/index.css +146 -0
  176. package/src/lib/api-client-config.ts +53 -0
  177. package/src/lib/api-type-mappers.ts +196 -0
  178. package/src/lib/export-utils.ts +123 -0
  179. package/src/lib/logger.ts +19 -0
  180. package/src/lib/media-utils.ts +170 -0
  181. package/src/lib/pca.test.ts +17 -0
  182. package/src/lib/pca.ts +52 -0
  183. package/src/lib/socketio-manager.ts +664 -0
  184. package/src/lib/utils.ts +168 -0
  185. package/src/main.tsx +16 -0
  186. package/src/mocks/empty-module.ts +12 -0
  187. package/src/mocks/node-module.ts +57 -0
  188. package/src/polyfills.ts +37 -0
  189. package/src/routes/agent-detail.tsx +30 -0
  190. package/src/routes/agent-list.tsx +27 -0
  191. package/src/routes/agent-settings.tsx +48 -0
  192. package/src/routes/character-detail.tsx +52 -0
  193. package/src/routes/character-form.tsx +79 -0
  194. package/src/routes/character-list.tsx +38 -0
  195. package/src/routes/chat.tsx +128 -0
  196. package/src/routes/createAgent.tsx +13 -0
  197. package/src/routes/group-new.tsx +50 -0
  198. package/src/routes/group.tsx +29 -0
  199. package/src/routes/home.tsx +218 -0
  200. package/src/routes/not-found.tsx +71 -0
  201. package/src/test/setup.ts +154 -0
  202. package/src/types/crypto-browserify.d.ts +4 -0
  203. package/src/types/index.ts +13 -0
  204. package/src/types/rooms.ts +8 -0
  205. package/src/types.ts +84 -0
  206. package/src/vite-env.d.ts +40 -0
  207. package/tailwind.config.ts +90 -0
  208. package/tsconfig.json +10 -0
  209. package/vite.config.ts +102 -0
@@ -0,0 +1,155 @@
1
+ 'use client';
2
+
3
+ import * as SelectPrimitive from '@radix-ui/react-select';
4
+ import { Check, ChevronDown, ChevronUp } from 'lucide-react';
5
+ import * as React from 'react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ const Select = SelectPrimitive.Root;
10
+
11
+ const SelectGroup = SelectPrimitive.Group;
12
+
13
+ const SelectValue = SelectPrimitive.Value;
14
+
15
+ const SelectTrigger = React.forwardRef<
16
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
17
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
18
+ >(({ className, children, ...props }, ref) => (
19
+ <SelectPrimitive.Trigger
20
+ ref={ref}
21
+ className={cn(
22
+ 'flex h-12 w-full items-center justify-between whitespace-nowrap rounded border border-input bg-card px-4 py-3 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
23
+ className
24
+ )}
25
+ {...props}
26
+ >
27
+ {children}
28
+ <SelectPrimitive.Icon asChild>
29
+ <ChevronDown className="h-4 w-4 opacity-50" />
30
+ </SelectPrimitive.Icon>
31
+ </SelectPrimitive.Trigger>
32
+ ));
33
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
34
+
35
+ const SelectScrollUpButton = React.forwardRef<
36
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
37
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
38
+ >(({ className, ...props }, ref) => (
39
+ <SelectPrimitive.ScrollUpButton
40
+ ref={ref}
41
+ className={cn('flex cursor-default items-center justify-center py-1', className)}
42
+ {...props}
43
+ >
44
+ <ChevronUp className="h-4 w-4" />
45
+ </SelectPrimitive.ScrollUpButton>
46
+ ));
47
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
48
+
49
+ const SelectScrollDownButton = React.forwardRef<
50
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
51
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
52
+ >(({ className, ...props }, ref) => (
53
+ <SelectPrimitive.ScrollDownButton
54
+ ref={ref}
55
+ className={cn('flex cursor-default items-center justify-center py-1', className)}
56
+ {...props}
57
+ >
58
+ <ChevronDown className="h-4 w-4" />
59
+ </SelectPrimitive.ScrollDownButton>
60
+ ));
61
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
62
+
63
+ const SelectContent = React.forwardRef<
64
+ React.ElementRef<typeof SelectPrimitive.Content>,
65
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
66
+ >(({ className, children, position = 'popper', ...props }, ref) => (
67
+ <SelectPrimitive.Portal>
68
+ <SelectPrimitive.Content
69
+ ref={ref}
70
+ className={cn(
71
+ 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-xl border border-input bg-card text-popover-foreground shadow-lg 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',
72
+ position === 'popper' &&
73
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
74
+ className
75
+ )}
76
+ position={position}
77
+ {...props}
78
+ >
79
+ <SelectScrollUpButton />
80
+ <SelectPrimitive.Viewport
81
+ className={cn(
82
+ 'p-2',
83
+ position === 'popper' &&
84
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
85
+ )}
86
+ >
87
+ {children}
88
+ </SelectPrimitive.Viewport>
89
+ <SelectScrollDownButton />
90
+ </SelectPrimitive.Content>
91
+ </SelectPrimitive.Portal>
92
+ ));
93
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
94
+
95
+ const SelectLabel = React.forwardRef<
96
+ React.ElementRef<typeof SelectPrimitive.Label>,
97
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
98
+ >(({ className, ...props }, ref) => (
99
+ <SelectPrimitive.Label
100
+ ref={ref}
101
+ className={cn(
102
+ 'px-4 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider',
103
+ className
104
+ )}
105
+ {...props}
106
+ />
107
+ ));
108
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
109
+
110
+ const SelectItem = React.forwardRef<
111
+ React.ElementRef<typeof SelectPrimitive.Item>,
112
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
113
+ >(({ className, children, ...props }, ref) => (
114
+ <SelectPrimitive.Item
115
+ ref={ref}
116
+ className={cn(
117
+ 'relative flex w-full cursor-default select-none items-center rounded py-3 pl-4 pr-8 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
118
+ className
119
+ )}
120
+ {...props}
121
+ >
122
+ <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
123
+ <SelectPrimitive.ItemIndicator>
124
+ <Check className="h-4 w-4" />
125
+ </SelectPrimitive.ItemIndicator>
126
+ </span>
127
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
128
+ </SelectPrimitive.Item>
129
+ ));
130
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
131
+
132
+ const SelectSeparator = React.forwardRef<
133
+ React.ElementRef<typeof SelectPrimitive.Separator>,
134
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
135
+ >(({ className, ...props }, ref) => (
136
+ <SelectPrimitive.Separator
137
+ ref={ref}
138
+ className={cn('-mx-1 my-1 h-px bg-muted', className)}
139
+ {...props}
140
+ />
141
+ ));
142
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
143
+
144
+ export {
145
+ Select,
146
+ SelectGroup,
147
+ SelectValue,
148
+ SelectTrigger,
149
+ SelectContent,
150
+ SelectLabel,
151
+ SelectItem,
152
+ SelectSeparator,
153
+ SelectScrollUpButton,
154
+ SelectScrollDownButton,
155
+ };
@@ -0,0 +1,145 @@
1
+ /// <reference types="cypress" />
2
+ /// <reference path="../../../cypress/support/types.d.ts" />
3
+
4
+ import React from 'react';
5
+ import { Separator } from './separator';
6
+
7
+ describe('Separator Component', () => {
8
+ it('renders correctly with default props', () => {
9
+ cy.mount(<Separator />);
10
+
11
+ cy.get('[data-testid="separator"]').should('exist');
12
+ cy.get('[data-testid="separator"]').should('have.attr', 'role', 'none'); // decorative is true by default
13
+ cy.get('[data-testid="separator"]').should('have.class', 'shrink-0');
14
+ });
15
+
16
+ describe('Orientation', () => {
17
+ it('renders horizontal separator correctly', () => {
18
+ cy.mount(<Separator orientation="horizontal" />);
19
+
20
+ cy.get('[data-testid="separator"]')
21
+ .should('have.attr', 'data-orientation', 'horizontal')
22
+ .should('have.class', 'h-[1px]')
23
+ .should('have.class', 'w-full');
24
+ });
25
+
26
+ it('renders vertical separator correctly', () => {
27
+ cy.mount(
28
+ <div className="flex h-20">
29
+ <div>Left</div>
30
+ <Separator orientation="vertical" />
31
+ <div>Right</div>
32
+ </div>
33
+ );
34
+
35
+ cy.get('[data-testid="separator"]')
36
+ .should('have.attr', 'data-orientation', 'vertical')
37
+ .should('have.class', 'h-full')
38
+ .should('have.class', 'w-[1px]');
39
+ });
40
+ });
41
+
42
+ it('applies custom className', () => {
43
+ cy.mount(<Separator className="my-custom-separator bg-red-500" />);
44
+
45
+ cy.get('[data-testid="separator"]')
46
+ .should('have.class', 'my-custom-separator')
47
+ .should('have.class', 'bg-red-500');
48
+ });
49
+
50
+ it('decorative prop sets correct aria attributes', () => {
51
+ cy.mount(<Separator decorative />);
52
+
53
+ cy.get('[data-testid="separator"]').should('have.attr', 'role', 'none');
54
+ });
55
+
56
+ it('renders in a card layout', () => {
57
+ cy.mount(
58
+ <div className="max-w-sm rounded-lg border p-4">
59
+ <h3 className="font-semibold">Card Title</h3>
60
+ <Separator className="my-2" />
61
+ <p className="text-sm text-gray-600">Card content goes here.</p>
62
+ </div>
63
+ );
64
+
65
+ cy.get('[data-testid="separator"]').should('be.visible');
66
+ cy.get('.my-2').should('exist');
67
+ });
68
+
69
+ it('renders multiple separators in a list', () => {
70
+ cy.mount(
71
+ <ul className="space-y-2">
72
+ <li>Item 1</li>
73
+ <Separator />
74
+ <li>Item 2</li>
75
+ <Separator />
76
+ <li>Item 3</li>
77
+ </ul>
78
+ );
79
+
80
+ cy.get('[data-testid="separator"]').should('have.length', 2);
81
+ });
82
+
83
+ it('works with different spacing', () => {
84
+ cy.mount(
85
+ <div>
86
+ <div>Content 1</div>
87
+ <Separator className="my-1" />
88
+ <div>Content 2</div>
89
+ <Separator className="my-4" />
90
+ <div>Content 3</div>
91
+ <Separator className="my-8" />
92
+ <div>Content 4</div>
93
+ </div>
94
+ );
95
+
96
+ cy.get('[data-testid="separator"]').should('have.length', 3);
97
+ cy.get('.my-1').should('exist');
98
+ cy.get('.my-4').should('exist');
99
+ cy.get('.my-8').should('exist');
100
+ });
101
+
102
+ it('renders in a form layout', () => {
103
+ cy.mount(
104
+ <form className="space-y-4">
105
+ <div>
106
+ <label>Name</label>
107
+ <input type="text" className="w-full border rounded px-2 py-1" />
108
+ </div>
109
+ <Separator />
110
+ <div>
111
+ <label>Email</label>
112
+ <input type="email" className="w-full border rounded px-2 py-1" />
113
+ </div>
114
+ </form>
115
+ );
116
+
117
+ cy.get('[data-testid="separator"]').should('exist');
118
+ cy.get('form').find('[data-testid="separator"]').should('have.length', 1);
119
+ });
120
+
121
+ it('supports custom styles', () => {
122
+ cy.mount(
123
+ <Separator
124
+ className="bg-gradient-to-r from-transparent via-gray-500 to-transparent"
125
+ style={{ height: '2px' }}
126
+ />
127
+ );
128
+
129
+ cy.get('[data-testid="separator"]')
130
+ .should('have.class', 'bg-gradient-to-r')
131
+ .should('have.css', 'height', '2px');
132
+ });
133
+
134
+ it('works in dark mode', () => {
135
+ cy.mount(
136
+ <div className="dark bg-gray-900 p-4">
137
+ <div className="text-white">Dark mode content</div>
138
+ <Separator className="my-4 bg-gray-700" />
139
+ <div className="text-white">More content</div>
140
+ </div>
141
+ );
142
+
143
+ cy.get('[data-testid="separator"]').should('have.class', 'bg-gray-700');
144
+ });
145
+ });
@@ -0,0 +1,29 @@
1
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
2
+ import * as React from 'react';
3
+
4
+ import { cn } from '@/lib/utils';
5
+
6
+ interface SeparatorProps extends React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> {
7
+ 'data-testid'?: string;
8
+ }
9
+
10
+ const Separator = React.forwardRef<
11
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
12
+ SeparatorProps
13
+ >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
14
+ <SeparatorPrimitive.Root
15
+ ref={ref}
16
+ decorative={decorative}
17
+ orientation={orientation}
18
+ className={cn(
19
+ 'shrink-0 bg-border',
20
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
21
+ className
22
+ )}
23
+ data-testid={props['data-testid'] || 'separator'}
24
+ {...props}
25
+ />
26
+ ));
27
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
28
+
29
+ export { Separator };
@@ -0,0 +1,324 @@
1
+ /// <reference types="cypress" />
2
+ /// <reference path="../../../cypress/support/types.d.ts" />
3
+
4
+ import React from 'react';
5
+ import {
6
+ Sheet,
7
+ SheetClose,
8
+ SheetContent,
9
+ SheetDescription,
10
+ SheetFooter,
11
+ SheetHeader,
12
+ SheetTitle,
13
+ SheetTrigger,
14
+ } from './sheet';
15
+ import { Button } from './button';
16
+
17
+ describe('Sheet Component', () => {
18
+ it('renders basic sheet from right', () => {
19
+ cy.mount(
20
+ <Sheet>
21
+ <SheetTrigger asChild>
22
+ <Button>Open Sheet</Button>
23
+ </SheetTrigger>
24
+ <SheetContent>
25
+ <SheetHeader>
26
+ <SheetTitle>Sheet Title</SheetTitle>
27
+ <SheetDescription>This is a sheet description.</SheetDescription>
28
+ </SheetHeader>
29
+ </SheetContent>
30
+ </Sheet>
31
+ );
32
+
33
+ cy.contains('button', 'Open Sheet').should('be.visible');
34
+ cy.contains('Sheet Title').should('not.exist');
35
+
36
+ // Open sheet
37
+ cy.contains('button', 'Open Sheet').click();
38
+ cy.contains('Sheet Title').should('be.visible');
39
+ cy.contains('This is a sheet description.').should('be.visible');
40
+ });
41
+
42
+ it('renders sheet from different sides', () => {
43
+ cy.mount(
44
+ <div className="flex gap-4">
45
+ <Sheet>
46
+ <SheetTrigger>From Top</SheetTrigger>
47
+ <SheetContent side="top">
48
+ <SheetTitle>Top Sheet</SheetTitle>
49
+ </SheetContent>
50
+ </Sheet>
51
+
52
+ <Sheet>
53
+ <SheetTrigger>From Bottom</SheetTrigger>
54
+ <SheetContent side="bottom">
55
+ <SheetTitle>Bottom Sheet</SheetTitle>
56
+ </SheetContent>
57
+ </Sheet>
58
+
59
+ <Sheet>
60
+ <SheetTrigger>From Left</SheetTrigger>
61
+ <SheetContent side="left">
62
+ <SheetTitle>Left Sheet</SheetTitle>
63
+ </SheetContent>
64
+ </Sheet>
65
+ </div>
66
+ );
67
+
68
+ // Test top sheet
69
+ cy.contains('From Top').click();
70
+ cy.contains('Top Sheet').should('be.visible');
71
+ cy.get('body').type('{esc}');
72
+
73
+ // Test bottom sheet
74
+ cy.contains('From Bottom').click();
75
+ cy.contains('Bottom Sheet').should('be.visible');
76
+ });
77
+
78
+ it('closes sheet when clicking overlay', () => {
79
+ cy.mount(
80
+ <Sheet>
81
+ <SheetTrigger>Open</SheetTrigger>
82
+ <SheetContent>
83
+ <SheetTitle>Click Outside Test</SheetTitle>
84
+ </SheetContent>
85
+ </Sheet>
86
+ );
87
+
88
+ cy.contains('Open').click();
89
+ cy.contains('Click Outside Test').should('be.visible');
90
+
91
+ // Use escape key instead of clicking overlay
92
+ cy.get('body').type('{esc}');
93
+ cy.contains('Click Outside Test').should('not.exist');
94
+ });
95
+
96
+ it('renders with footer actions', () => {
97
+ const onSave = cy.stub();
98
+ const onCancel = cy.stub();
99
+
100
+ cy.mount(
101
+ <Sheet>
102
+ <SheetTrigger>Edit Profile</SheetTrigger>
103
+ <SheetContent>
104
+ <SheetHeader>
105
+ <SheetTitle>Edit Profile</SheetTitle>
106
+ <SheetDescription>Make changes to your profile here.</SheetDescription>
107
+ </SheetHeader>
108
+ <div className="py-4">
109
+ <input placeholder="Name" className="w-full p-2 border rounded" />
110
+ </div>
111
+ <SheetFooter>
112
+ <SheetClose asChild>
113
+ <Button variant="outline" onClick={onCancel}>
114
+ Cancel
115
+ </Button>
116
+ </SheetClose>
117
+ <Button onClick={onSave}>Save changes</Button>
118
+ </SheetFooter>
119
+ </SheetContent>
120
+ </Sheet>
121
+ );
122
+
123
+ cy.contains('Edit Profile').click();
124
+ cy.get('input[placeholder="Name"]').should('be.visible');
125
+
126
+ cy.contains('button', 'Save changes').click();
127
+ cy.wrap(onSave).should('have.been.called');
128
+ });
129
+
130
+ it('works as controlled component', () => {
131
+ const TestComponent = () => {
132
+ const [open, setOpen] = React.useState(false);
133
+
134
+ return (
135
+ <>
136
+ <button onClick={() => setOpen(true)}>External Open</button>
137
+ <Sheet open={open} onOpenChange={setOpen}>
138
+ <SheetContent>
139
+ <SheetTitle>Controlled Sheet</SheetTitle>
140
+ <button onClick={() => setOpen(false)}>Close Sheet</button>
141
+ </SheetContent>
142
+ </Sheet>
143
+ </>
144
+ );
145
+ };
146
+
147
+ cy.mount(<TestComponent />);
148
+
149
+ cy.contains('Controlled Sheet').should('not.exist');
150
+ cy.contains('External Open').click();
151
+ cy.contains('Controlled Sheet').should('be.visible');
152
+
153
+ cy.contains('button', 'Close Sheet').click();
154
+ cy.contains('Controlled Sheet').should('not.exist');
155
+ });
156
+
157
+ it('handles keyboard navigation', () => {
158
+ cy.mount(
159
+ <Sheet>
160
+ <SheetTrigger>Open</SheetTrigger>
161
+ <SheetContent>
162
+ <SheetTitle>Keyboard Test</SheetTitle>
163
+ <button>First Button</button>
164
+ <button>Second Button</button>
165
+ </SheetContent>
166
+ </Sheet>
167
+ );
168
+
169
+ cy.contains('Open').click();
170
+ cy.contains('Keyboard Test').should('be.visible');
171
+
172
+ // ESC key should close
173
+ cy.get('body').type('{esc}');
174
+ cy.contains('Keyboard Test').should('not.exist');
175
+ });
176
+
177
+ it('supports custom className', () => {
178
+ cy.mount(
179
+ <Sheet>
180
+ <SheetTrigger className="custom-trigger">Open</SheetTrigger>
181
+ <SheetContent className="bg-blue-50 custom-content">
182
+ <SheetTitle>Custom Styled Sheet</SheetTitle>
183
+ </SheetContent>
184
+ </Sheet>
185
+ );
186
+
187
+ cy.get('.custom-trigger').should('exist');
188
+ cy.contains('Open').click();
189
+ cy.get('.custom-content').should('have.class', 'bg-blue-50');
190
+ });
191
+
192
+ it('renders form inside sheet', () => {
193
+ const onSubmit = cy.stub();
194
+
195
+ cy.mount(
196
+ <Sheet>
197
+ <SheetTrigger>Add Item</SheetTrigger>
198
+ <SheetContent>
199
+ <form
200
+ onSubmit={(e) => {
201
+ e.preventDefault();
202
+ onSubmit('submitted');
203
+ }}
204
+ >
205
+ <SheetHeader>
206
+ <SheetTitle>Add New Item</SheetTitle>
207
+ <SheetDescription>Fill in the details below</SheetDescription>
208
+ </SheetHeader>
209
+ <div className="space-y-4 py-4">
210
+ <input name="title" placeholder="Title" required />
211
+ <textarea name="description" placeholder="Description" />
212
+ </div>
213
+ <SheetFooter>
214
+ <SheetClose asChild>
215
+ <Button type="button" variant="outline">
216
+ Cancel
217
+ </Button>
218
+ </SheetClose>
219
+ <Button type="submit">Add</Button>
220
+ </SheetFooter>
221
+ </form>
222
+ </SheetContent>
223
+ </Sheet>
224
+ );
225
+
226
+ cy.contains('Add Item').click();
227
+ cy.get('input[name="title"]').type('Test Item');
228
+ cy.get('textarea[name="description"]').type('Test Description');
229
+ cy.get('form').submit();
230
+ cy.wrap(onSubmit).should('have.been.calledWith', 'submitted');
231
+ });
232
+
233
+ it('handles long content with scroll', () => {
234
+ cy.mount(
235
+ <Sheet>
236
+ <SheetTrigger>View Content</SheetTrigger>
237
+ <SheetContent className="overflow-y-auto">
238
+ <SheetHeader>
239
+ <SheetTitle>Long Content</SheetTitle>
240
+ </SheetHeader>
241
+ <div className="py-4">
242
+ {Array.from({ length: 50 }, (_, i) => (
243
+ <p key={i} className="py-2">
244
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
245
+ incididunt ut labore et dolore magna aliqua.
246
+ </p>
247
+ ))}
248
+ </div>
249
+ </SheetContent>
250
+ </Sheet>
251
+ );
252
+
253
+ cy.contains('View Content').click();
254
+ cy.get('.overflow-y-auto').should('be.visible');
255
+ });
256
+
257
+ it('renders navigation menu in sheet', () => {
258
+ cy.mount(
259
+ <Sheet>
260
+ <SheetTrigger>Menu</SheetTrigger>
261
+ <SheetContent side="left">
262
+ <SheetHeader>
263
+ <SheetTitle>Navigation</SheetTitle>
264
+ </SheetHeader>
265
+ <nav className="mt-8">
266
+ <ul className="space-y-2">
267
+ <li>
268
+ <a href="#" className="block p-2 hover:bg-gray-100">
269
+ Home
270
+ </a>
271
+ </li>
272
+ <li>
273
+ <a href="#" className="block p-2 hover:bg-gray-100">
274
+ About
275
+ </a>
276
+ </li>
277
+ <li>
278
+ <a href="#" className="block p-2 hover:bg-gray-100">
279
+ Services
280
+ </a>
281
+ </li>
282
+ <li>
283
+ <a href="#" className="block p-2 hover:bg-gray-100">
284
+ Contact
285
+ </a>
286
+ </li>
287
+ </ul>
288
+ </nav>
289
+ </SheetContent>
290
+ </Sheet>
291
+ );
292
+
293
+ cy.contains('Menu').click();
294
+ cy.contains('Navigation').should('be.visible');
295
+ cy.contains('Home').should('be.visible');
296
+ cy.contains('About').should('be.visible');
297
+ cy.contains('Services').should('be.visible');
298
+ cy.contains('Contact').should('be.visible');
299
+ });
300
+
301
+ it('maintains focus management', () => {
302
+ cy.mount(
303
+ <Sheet>
304
+ <SheetTrigger>Open Sheet</SheetTrigger>
305
+ <SheetContent>
306
+ <SheetHeader>
307
+ <SheetTitle>Focus Test</SheetTitle>
308
+ </SheetHeader>
309
+ <div className="space-y-4 py-4">
310
+ <input placeholder="First input" />
311
+ <input placeholder="Second input" />
312
+ <button>Action Button</button>
313
+ </div>
314
+ </SheetContent>
315
+ </Sheet>
316
+ );
317
+
318
+ cy.contains('Open Sheet').click();
319
+
320
+ // Focus should be trapped within sheet
321
+ cy.get('input[placeholder="First input"]').focus();
322
+ cy.focused().should('have.attr', 'placeholder', 'First input');
323
+ });
324
+ });