@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,461 @@
1
+ import type React from 'react';
2
+ import { useUpdateMemory, useDeleteMemory } from '@/hooks/use-query-hooks';
3
+ import type { Memory, UUID } from '@elizaos/core';
4
+ import { useState, useEffect, useRef, useCallback } from 'react';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
7
+ import { Textarea } from '@/components/ui/textarea';
8
+ import { Separator } from '@/components/ui/separator';
9
+ import { Trash, X, Save, AlertTriangle, Check, RotateCcw, Copy, Eye, EyeOff } from 'lucide-react';
10
+ import { useToast } from '@/hooks/use-toast';
11
+ import { useConfirmation } from '@/hooks/use-confirmation';
12
+ import ConfirmationDialog from './confirmation-dialog';
13
+
14
+ interface MemoryEditOverlayProps {
15
+ isOpen: boolean;
16
+ onClose: () => void;
17
+ memory: Memory;
18
+ agentId: UUID;
19
+ }
20
+
21
+ export default function MemoryEditOverlay({
22
+ isOpen,
23
+ onClose,
24
+ memory,
25
+ agentId,
26
+ }: MemoryEditOverlayProps) {
27
+ const { toast } = useToast();
28
+ const { mutate: updateMemory, isPending: isUpdating } = useUpdateMemory();
29
+ const { mutate: deleteMemory, isPending: isDeleting } = useDeleteMemory();
30
+
31
+ const [editedContent, setEditedContent] = useState('');
32
+ const [isValidJson, setIsValidJson] = useState(true);
33
+ const [isPreviewMode, setIsPreviewMode] = useState(false);
34
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
35
+
36
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
37
+ const isProcessing = isUpdating || isDeleting;
38
+ const originalContent = JSON.stringify(memory.content, null, 2);
39
+
40
+ const { confirm, isOpen: confirmOpen, onOpenChange, onConfirm, options } = useConfirmation();
41
+
42
+ // Initialize content when component opens
43
+ useEffect(() => {
44
+ if (isOpen) {
45
+ setEditedContent(originalContent);
46
+ setIsValidJson(true);
47
+ setHasUnsavedChanges(false);
48
+ setIsPreviewMode(false);
49
+ setTimeout(() => {
50
+ textareaRef.current?.focus();
51
+ }, 100);
52
+ }
53
+ }, [isOpen, originalContent]);
54
+
55
+ // Check for unsaved changes
56
+ useEffect(() => {
57
+ setHasUnsavedChanges(editedContent !== originalContent);
58
+ }, [editedContent, originalContent]);
59
+
60
+ // Validate JSON on content change
61
+ useEffect(() => {
62
+ if (!editedContent.trim()) {
63
+ setIsValidJson(false);
64
+ return;
65
+ }
66
+
67
+ try {
68
+ JSON.parse(editedContent);
69
+ setIsValidJson(true);
70
+ } catch {
71
+ setIsValidJson(false);
72
+ }
73
+ }, [editedContent]);
74
+
75
+ // Handle escape key
76
+ useEffect(() => {
77
+ const handleEscape = (e: KeyboardEvent) => {
78
+ if (e.key === 'Escape' && isOpen) {
79
+ if (hasUnsavedChanges) {
80
+ confirm(
81
+ {
82
+ title: 'Discard Unsaved Changes?',
83
+ description:
84
+ 'You have unsaved changes. Are you sure you want to close without saving?',
85
+ confirmText: 'Discard',
86
+ variant: 'destructive',
87
+ },
88
+ () => {
89
+ onClose();
90
+ }
91
+ );
92
+ } else {
93
+ onClose();
94
+ }
95
+ }
96
+ };
97
+
98
+ document.addEventListener('keydown', handleEscape);
99
+ return () => document.removeEventListener('keydown', handleEscape);
100
+ }, [isOpen, hasUnsavedChanges, onClose]);
101
+
102
+ const handleSave = useCallback(() => {
103
+ if (!editedContent.trim()) {
104
+ toast({
105
+ title: 'Error',
106
+ description: 'Memory content cannot be empty',
107
+ variant: 'destructive',
108
+ });
109
+ return;
110
+ }
111
+
112
+ if (!isValidJson) {
113
+ toast({
114
+ title: 'Invalid JSON',
115
+ description: 'Please fix JSON syntax errors before saving',
116
+ variant: 'destructive',
117
+ });
118
+ return;
119
+ }
120
+
121
+ try {
122
+ const parsedContent = JSON.parse(editedContent);
123
+
124
+ updateMemory(
125
+ {
126
+ agentId,
127
+ memoryId: memory.id!,
128
+ memoryData: {
129
+ content: parsedContent,
130
+ },
131
+ },
132
+ {
133
+ onSuccess: () => {
134
+ onClose();
135
+ toast({
136
+ title: 'Memory Updated',
137
+ description: 'The memory content has been successfully updated',
138
+ });
139
+ },
140
+ onError: (error) => {
141
+ toast({
142
+ title: 'Update Failed',
143
+ description: error.message || 'Failed to update memory',
144
+ variant: 'destructive',
145
+ });
146
+ },
147
+ }
148
+ );
149
+ } catch (error) {
150
+ toast({
151
+ title: 'Invalid JSON',
152
+ description: 'Please enter valid JSON format',
153
+ variant: 'destructive',
154
+ });
155
+ }
156
+ }, [editedContent, isValidJson, updateMemory, agentId, memory.id, onClose, toast]);
157
+
158
+ const handleDelete = useCallback(() => {
159
+ confirm(
160
+ {
161
+ title: 'Delete Memory',
162
+ description: 'Are you sure you want to delete this memory? This action cannot be undone.',
163
+ confirmText: 'Delete',
164
+ variant: 'destructive',
165
+ },
166
+ () => {
167
+ deleteMemory(
168
+ {
169
+ agentId,
170
+ memoryId: memory.id!,
171
+ },
172
+ {
173
+ onSuccess: () => {
174
+ onClose();
175
+ toast({
176
+ title: 'Memory Deleted',
177
+ description: 'The memory has been successfully removed',
178
+ });
179
+ },
180
+ onError: (error) => {
181
+ toast({
182
+ title: 'Delete Failed',
183
+ description: error.message || 'Failed to delete memory',
184
+ variant: 'destructive',
185
+ });
186
+ },
187
+ }
188
+ );
189
+ }
190
+ );
191
+ }, [deleteMemory, agentId, memory.id, onClose, toast]);
192
+
193
+ const handlePrettyFormat = useCallback(() => {
194
+ try {
195
+ const parsed = JSON.parse(editedContent);
196
+ setEditedContent(JSON.stringify(parsed, null, 2));
197
+ toast({
198
+ title: 'Formatted',
199
+ description: 'JSON has been formatted',
200
+ });
201
+ } catch (error) {
202
+ toast({
203
+ title: 'Format Error',
204
+ description: 'Cannot format invalid JSON',
205
+ variant: 'destructive',
206
+ });
207
+ }
208
+ }, [editedContent, toast]);
209
+
210
+ const handleReset = useCallback(() => {
211
+ confirm(
212
+ {
213
+ title: 'Reset Changes',
214
+ description: 'Reset to original content? All unsaved changes will be lost.',
215
+ confirmText: 'Reset',
216
+ variant: 'destructive',
217
+ },
218
+ () => {
219
+ setEditedContent(originalContent);
220
+ }
221
+ );
222
+ }, [originalContent]);
223
+
224
+ const handleCopyToClipboard = useCallback(async () => {
225
+ try {
226
+ await navigator.clipboard.writeText(editedContent);
227
+ toast({
228
+ title: 'Copied',
229
+ description: 'Content copied to clipboard',
230
+ });
231
+ } catch (error) {
232
+ toast({
233
+ title: 'Copy Failed',
234
+ description: 'Failed to copy to clipboard',
235
+ variant: 'destructive',
236
+ });
237
+ }
238
+ }, [editedContent, toast]);
239
+
240
+ const formatTimestamp = (timestamp: number) => {
241
+ return new Date(timestamp).toLocaleString();
242
+ };
243
+
244
+ const handleOverlayClick = (e: React.MouseEvent) => {
245
+ if (e.target === e.currentTarget) {
246
+ if (hasUnsavedChanges) {
247
+ confirm(
248
+ {
249
+ title: 'Discard Unsaved Changes?',
250
+ description: 'You have unsaved changes. Are you sure you want to close without saving?',
251
+ confirmText: 'Discard',
252
+ variant: 'destructive',
253
+ },
254
+ () => {
255
+ onClose();
256
+ }
257
+ );
258
+ } else {
259
+ onClose();
260
+ }
261
+ }
262
+ };
263
+
264
+ if (!isOpen) return null;
265
+
266
+ return (
267
+ <>
268
+ <div
269
+ className="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4"
270
+ onClick={handleOverlayClick}
271
+ role="dialog"
272
+ aria-modal="true"
273
+ aria-labelledby="memory-edit-title"
274
+ >
275
+ <Card
276
+ className="w-full max-w-4xl h-[85vh] max-h-[800px] flex flex-col"
277
+ onClick={(e) => e.stopPropagation()}
278
+ >
279
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2 flex-shrink-0">
280
+ <div className="flex items-center gap-2">
281
+ <CardTitle id="memory-edit-title" className="text-xl font-semibold">
282
+ Edit Memory Content
283
+ </CardTitle>
284
+ {hasUnsavedChanges && (
285
+ <span className="text-xs px-2 py-1 rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200">
286
+ Unsaved changes
287
+ </span>
288
+ )}
289
+ </div>
290
+ <Button variant="ghost" size="sm" onClick={onClose} aria-label="Close dialog">
291
+ <X className="h-4 w-4" />
292
+ </Button>
293
+ </CardHeader>
294
+
295
+ <Separator />
296
+
297
+ <CardContent className="pt-4 flex-1 overflow-hidden flex flex-col">
298
+ {/* Warning Banner */}
299
+ <div className="mb-4 flex items-start bg-amber-50 dark:bg-amber-950/50 border border-amber-200 dark:border-amber-800 rounded-md p-3">
300
+ <AlertTriangle className="h-4 w-4 text-amber-600 dark:text-amber-400 mr-2 mt-0.5 flex-shrink-0" />
301
+ <div className="text-sm text-amber-800 dark:text-amber-200">
302
+ <p className="font-medium mb-1">Editing Raw Memory Content</p>
303
+ <p>Changes may affect agent behavior. Ensure JSON format is valid before saving.</p>
304
+ </div>
305
+ </div>
306
+
307
+ {/* Memory Metadata */}
308
+ <div className="mb-4 grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
309
+ <div className="space-y-1">
310
+ <div className="flex items-center gap-2">
311
+ <span className="text-muted-foreground">ID:</span>
312
+ <code className="text-xs bg-muted px-1 rounded">{memory.id}</code>
313
+ </div>
314
+ <div className="flex items-center gap-2">
315
+ <span className="text-muted-foreground">Created:</span>
316
+ <span>{formatTimestamp(memory.createdAt || 0)}</span>
317
+ </div>
318
+ </div>
319
+ <div className="space-y-1">
320
+ <div className="flex items-center gap-2">
321
+ <span className="text-muted-foreground">Room:</span>
322
+ <code className="text-xs bg-muted px-1 rounded">{memory.roomId}</code>
323
+ </div>
324
+ <div className="flex items-center gap-2">
325
+ <span className="text-muted-foreground">Type:</span>
326
+ <span
327
+ className={`text-xs px-2 py-1 rounded-full ${
328
+ memory.entityId === agentId
329
+ ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'
330
+ : 'bg-white-100 text-white dark:bg-white-800 dark:text-white'
331
+ }`}
332
+ >
333
+ {memory.entityId === agentId ? 'Agent Message' : 'User Message'}
334
+ </span>
335
+ </div>
336
+ </div>
337
+ </div>
338
+
339
+ {/* Editor Controls */}
340
+ <div className="flex items-center justify-between mb-2">
341
+ <div className="flex items-center gap-2">
342
+ <label htmlFor="memory-content" className="text-sm font-medium">
343
+ Memory Content
344
+ </label>
345
+ <div className="flex items-center gap-1">
346
+ {isValidJson ? (
347
+ <Check className="h-3 w-3 text-green-500" />
348
+ ) : (
349
+ <AlertTriangle className="h-3 w-3 text-red-500" />
350
+ )}
351
+ <span className={`text-xs ${isValidJson ? 'text-green-600' : 'text-red-600'}`}>
352
+ {isValidJson ? 'Valid JSON' : 'Invalid JSON'}
353
+ </span>
354
+ </div>
355
+ </div>
356
+
357
+ <div className="flex items-center gap-1">
358
+ <Button
359
+ variant="outline"
360
+ size="sm"
361
+ onClick={() => setIsPreviewMode(!isPreviewMode)}
362
+ disabled={isProcessing}
363
+ title={isPreviewMode ? 'Switch to edit mode' : 'Switch to preview mode'}
364
+ >
365
+ {isPreviewMode ? <EyeOff className="h-3 w-3" /> : <Eye className="h-3 w-3" />}
366
+ </Button>
367
+ <Button
368
+ variant="outline"
369
+ size="sm"
370
+ onClick={handleCopyToClipboard}
371
+ disabled={isProcessing}
372
+ title="Copy to clipboard"
373
+ >
374
+ <Copy className="h-3 w-3" />
375
+ </Button>
376
+ <Button
377
+ variant="outline"
378
+ size="sm"
379
+ onClick={handlePrettyFormat}
380
+ disabled={isProcessing || !isValidJson}
381
+ title="Format JSON"
382
+ >
383
+ Format
384
+ </Button>
385
+ <Button
386
+ variant="outline"
387
+ size="sm"
388
+ onClick={handleReset}
389
+ disabled={isProcessing || !hasUnsavedChanges}
390
+ title="Reset to original"
391
+ >
392
+ <RotateCcw className="h-3 w-3" />
393
+ </Button>
394
+ </div>
395
+ </div>
396
+
397
+ {/* Content Editor */}
398
+ <div className="flex-1 min-h-0 flex flex-col">
399
+ {isPreviewMode ? (
400
+ <div className="flex-1 border rounded-md p-3 bg-muted/50 overflow-auto">
401
+ <pre className="text-sm whitespace-pre-wrap">
402
+ {isValidJson
403
+ ? JSON.stringify(JSON.parse(editedContent), null, 2)
404
+ : editedContent}
405
+ </pre>
406
+ </div>
407
+ ) : (
408
+ <Textarea
409
+ ref={textareaRef}
410
+ id="memory-content"
411
+ value={editedContent}
412
+ onChange={(e) => setEditedContent(e.target.value)}
413
+ className={`flex-1 resize-none font-mono text-sm min-h-[300px] ${
414
+ !isValidJson ? 'border-red-300 focus:border-red-500' : ''
415
+ }`}
416
+ placeholder="Memory content in JSON format..."
417
+ disabled={isProcessing}
418
+ aria-describedby="json-help"
419
+ />
420
+ )}
421
+
422
+ <p id="json-help" className="text-xs text-muted-foreground mt-2 flex-shrink-0">
423
+ Edit the JSON directly. Use Ctrl+A to select all, Ctrl+Z to undo.
424
+ </p>
425
+ </div>
426
+ </CardContent>
427
+
428
+ <CardFooter className="flex justify-between flex-shrink-0">
429
+ <Button variant="destructive" onClick={handleDelete} disabled={isProcessing}>
430
+ <Trash className="mr-2 h-4 w-4" />
431
+ Delete Memory
432
+ </Button>
433
+
434
+ <div className="flex gap-2">
435
+ <Button variant="outline" onClick={onClose} disabled={isProcessing}>
436
+ Cancel
437
+ </Button>
438
+ <Button
439
+ onClick={handleSave}
440
+ disabled={isProcessing || !isValidJson || !hasUnsavedChanges}
441
+ >
442
+ <Save className="mr-2 h-4 w-4" />
443
+ {isUpdating ? 'Saving...' : 'Save Changes'}
444
+ </Button>
445
+ </div>
446
+ </CardFooter>
447
+ </Card>
448
+ </div>
449
+ <ConfirmationDialog
450
+ open={confirmOpen}
451
+ onOpenChange={onOpenChange}
452
+ title={options?.title || ''}
453
+ description={options?.description || ''}
454
+ confirmText={options?.confirmText}
455
+ cancelText={options?.cancelText}
456
+ variant={options?.variant}
457
+ onConfirm={onConfirm}
458
+ />
459
+ </>
460
+ );
461
+ }