11"use client" ;
22
3- import React , { useState } from "react" ;
3+ import React , { useEffect , useState } from "react" ;
44import { App , Button , Card , Input , List , Menu , Switch , Tabs } from "antd" ;
55import { motion } from "framer-motion" ;
66import "./memory.css" ;
@@ -261,139 +261,19 @@ export default function MemoryContent({ onNavigate }: MemoryContentProps) {
261261 ) ;
262262 } ;
263263
264- // Render memory groups with menu (for agent shared and user agent)
265264 const renderMemoryWithMenu = (
266265 groups : { title : string ; key : string ; items : any [ ] } [ ] ,
267- showSwitch = false ,
268- tabKey ?: MemoryTabKey
269- ) => {
270- const [ selectedKey , setSelectedKey ] = useState < string > (
271- groups . length > 0 ? groups [ 0 ] . key : ""
272- ) ;
273-
274- if ( groups . length === 0 ) {
275- return (
276- < div className = "flex flex-col items-center justify-center py-20" >
277- < MessageSquareDashed className = "size-16 mb-4 text-gray-300" />
278- < p className = "text-base text-gray-500" > { t ( "memoryManageModal.noMemory" ) } </ p >
279- </ div >
280- ) ;
281- }
282-
283- const currentGroup = groups . find ( ( g ) => g . key === selectedKey ) || groups [ 0 ] ;
284- const isPlaceholder = / - p l a c e h o l d e r $ / . test ( currentGroup . key ) ;
285- const disabled = ! isPlaceholder && ! ! memory . disabledGroups [ currentGroup . key ] ;
286-
287- const menuItems = groups . map ( ( g ) => {
288- const groupDisabled = ! / - p l a c e h o l d e r $ / . test ( g . key ) && ! ! memory . disabledGroups [ g . key ] ;
289- return {
290- key : g . key ,
291- label : (
292- < div className = "flex items-center justify-between w-full" >
293- < span className = "truncate" > { g . title } </ span >
294- { showSwitch && ! / - p l a c e h o l d e r $ / . test ( g . key ) && (
295- < div onClick = { ( e ) => e . stopPropagation ( ) } >
296- < Switch
297- size = "small"
298- checked = { ! groupDisabled }
299- onChange = { ( val ) => memory . toggleGroup ( g . key , val ) }
300- />
301- </ div >
302- ) }
303- </ div >
304- ) ,
305- disabled : groupDisabled ,
306- } ;
307- } ) ;
308-
309- return (
310- < div className = "flex gap-4" style = { { height : "calc(100vh - 280px)" } } >
311- { /* Left Menu */ }
312- < Menu
313- mode = "inline"
314- selectedKeys = { [ selectedKey ] }
315- onClick = { ( { key } ) => setSelectedKey ( key ) }
316- items = { menuItems }
317- style = { { width : 280 , height : "100%" , overflowY : "auto" } }
318- />
319-
320- { /* Right List */ }
321- < div className = "flex-1" >
322- < List
323- header = {
324- < div className = "flex items-center justify-between" >
325- < span className = "text-base font-medium" > { currentGroup . title } </ span >
326- < div className = "flex items-center gap-2" >
327- < Button
328- type = "text"
329- size = "small"
330- icon = { < MessageSquarePlus className = "size-4" /> }
331- onClick = { ( ) => memory . startAddingMemory ( currentGroup . key ) }
332- disabled = { disabled }
333- className = "hover:bg-green-50 hover:text-green-600"
334- title = { t ( "memoryManageModal.addMemory" ) }
335- />
336- { currentGroup . items . length > 0 && (
337- < Button
338- type = "text"
339- size = "small"
340- icon = { < MessageSquareOff className = "size-4" /> }
341- onClick = { ( ) =>
342- ! isPlaceholder && handleClearConfirm ( currentGroup . key , currentGroup . title )
343- }
344- disabled = { disabled }
345- danger
346- className = "hover:bg-red-50"
347- title = { t ( "memoryManageModal.clearMemory" ) }
348- />
349- ) }
350- </ div >
351- </ div >
352- }
353- bordered
354- dataSource = { currentGroup . items }
355- locale = { {
356- emptyText : (
357- < div className = "flex flex-col items-center justify-center py-16 text-gray-400" >
358- < MessageSquareDashed className = "size-12 mb-3 opacity-50" />
359- < p className = "text-sm" > { t ( "memoryManageModal.noMemory" ) } </ p >
360- </ div >
361- ) ,
362- } }
363- style = { { height : "100%" , overflowY : "auto" } }
364- renderItem = { ( item ) => (
365- < List . Item
366- className = "hover:bg-gray-50 transition-colors"
367- actions = { [
368- < Button
369- key = "delete"
370- type = "text"
371- size = "small"
372- danger
373- icon = { < Eraser className = "size-4" /> }
374- onClick = { ( ) => memory . handleDeleteMemory ( item . id , currentGroup . key ) }
375- disabled = { disabled }
376- title = { t ( "memoryManageModal.deleteMemory" ) }
377- /> ,
378- ] }
379- >
380- < div className = "flex flex-col text-sm" > { item . memory } </ div >
381- </ List . Item >
382- ) }
383- >
384- { memory . addingMemoryKey === currentGroup . key && (
385- < List . Item
386- className = "bg-blue-50 border-t-2 border-blue-300 flex items-center"
387- style = { { minHeight : "100px" , padding : "20px" } }
388- >
389- { renderAddMemoryInput ( currentGroup . key ) }
390- </ List . Item >
391- ) }
392- </ List >
393- </ div >
394- </ div >
395- ) ;
396- } ;
266+ showSwitch = false
267+ ) => (
268+ < MemoryMenuList
269+ groups = { groups }
270+ showSwitch = { showSwitch }
271+ memory = { memory }
272+ t = { t }
273+ onClearConfirm = { handleClearConfirm }
274+ renderAddMemoryInput = { renderAddMemoryInput }
275+ />
276+ ) ;
397277
398278 const tabItems = [
399279 {
@@ -427,11 +307,7 @@ export default function MemoryContent({ onNavigate }: MemoryContentProps) {
427307 { t ( "memoryManageModal.agentShareTab" ) }
428308 </ span >
429309 ) ,
430- children : renderMemoryWithMenu (
431- memory . agentSharedGroups ,
432- true ,
433- MEMORY_TAB_KEYS . AGENT_SHARED
434- ) ,
310+ children : renderMemoryWithMenu ( memory . agentSharedGroups , true ) ,
435311 disabled :
436312 ! memory . memoryEnabled ||
437313 memory . shareOption === MEMORY_SHARE_STRATEGY . NEVER ,
@@ -457,11 +333,7 @@ export default function MemoryContent({ onNavigate }: MemoryContentProps) {
457333 { t ( "memoryManageModal.userAgentTab" ) }
458334 </ span >
459335 ) ,
460- children : renderMemoryWithMenu (
461- memory . userAgentGroups ,
462- true ,
463- MEMORY_TAB_KEYS . USER_AGENT
464- ) ,
336+ children : renderMemoryWithMenu ( memory . userAgentGroups , true ) ,
465337 disabled : ! memory . memoryEnabled ,
466338 } ,
467339 ] ;
@@ -516,3 +388,152 @@ export default function MemoryContent({ onNavigate }: MemoryContentProps) {
516388 ) ;
517389}
518390
391+ interface MemoryMenuListProps {
392+ groups : { title : string ; key : string ; items : any [ ] } [ ] ;
393+ showSwitch ?: boolean ;
394+ memory : ReturnType < typeof useMemory > ;
395+ t : ReturnType < typeof useTranslation > [ "t" ] ;
396+ onClearConfirm : ( groupKey : string , groupTitle : string ) => void ;
397+ renderAddMemoryInput : ( groupKey : string ) => React . ReactNode ;
398+ }
399+
400+ function MemoryMenuList ( {
401+ groups,
402+ showSwitch = false ,
403+ memory,
404+ t,
405+ onClearConfirm,
406+ renderAddMemoryInput,
407+ } : MemoryMenuListProps ) {
408+ const [ selectedKey , setSelectedKey ] = useState < string > (
409+ groups . length > 0 ? groups [ 0 ] . key : ""
410+ ) ;
411+
412+ useEffect ( ( ) => {
413+ if ( ! groups . some ( ( group ) => group . key === selectedKey ) ) {
414+ setSelectedKey ( groups [ 0 ] ?. key ?? "" ) ;
415+ }
416+ } , [ groups , selectedKey ] ) ;
417+
418+ if ( groups . length === 0 ) {
419+ return (
420+ < div className = "flex flex-col items-center justify-center py-20" >
421+ < MessageSquareDashed className = "size-16 mb-4 text-gray-300" />
422+ < p className = "text-base text-gray-500" > { t ( "memoryManageModal.noMemory" ) } </ p >
423+ </ div >
424+ ) ;
425+ }
426+
427+ const currentGroup = groups . find ( ( g ) => g . key === selectedKey ) || groups [ 0 ] ;
428+ const isPlaceholder = / - p l a c e h o l d e r $ / . test ( currentGroup . key ) ;
429+ const disabled = ! isPlaceholder && ! ! memory . disabledGroups [ currentGroup . key ] ;
430+
431+ const menuItems = groups . map ( ( g ) => {
432+ const groupDisabled = ! / - p l a c e h o l d e r $ / . test ( g . key ) && ! ! memory . disabledGroups [ g . key ] ;
433+ return {
434+ key : g . key ,
435+ label : (
436+ < div className = "flex items-center justify-between w-full" >
437+ < span className = "truncate" > { g . title } </ span >
438+ { showSwitch && ! / - p l a c e h o l d e r $ / . test ( g . key ) && (
439+ < div onClick = { ( e ) => e . stopPropagation ( ) } >
440+ < Switch
441+ size = "small"
442+ checked = { ! groupDisabled }
443+ onChange = { ( val ) => memory . toggleGroup ( g . key , val ) }
444+ />
445+ </ div >
446+ ) }
447+ </ div >
448+ ) ,
449+ disabled : groupDisabled ,
450+ } ;
451+ } ) ;
452+
453+ return (
454+ < div className = "flex gap-4" style = { { height : "calc(100vh - 280px)" } } >
455+ < Menu
456+ mode = "inline"
457+ selectedKeys = { [ selectedKey ] }
458+ onClick = { ( { key } ) => setSelectedKey ( key ) }
459+ items = { menuItems }
460+ style = { { width : 280 , height : "100%" , overflowY : "auto" } }
461+ />
462+
463+ < div className = "flex-1" >
464+ < List
465+ header = {
466+ < div className = "flex items-center justify-between" >
467+ < span className = "text-base font-medium" > { currentGroup . title } </ span >
468+ < div className = "flex items-center gap-2" >
469+ < Button
470+ type = "text"
471+ size = "small"
472+ icon = { < MessageSquarePlus className = "size-4" /> }
473+ onClick = { ( ) => memory . startAddingMemory ( currentGroup . key ) }
474+ disabled = { disabled }
475+ className = "hover:bg-green-50 hover:text-green-600"
476+ title = { t ( "memoryManageModal.addMemory" ) }
477+ />
478+ { currentGroup . items . length > 0 && (
479+ < Button
480+ type = "text"
481+ size = "small"
482+ icon = { < MessageSquareOff className = "size-4" /> }
483+ onClick = { ( ) =>
484+ ! isPlaceholder && onClearConfirm ( currentGroup . key , currentGroup . title )
485+ }
486+ disabled = { disabled }
487+ danger
488+ className = "hover:bg-red-50"
489+ title = { t ( "memoryManageModal.clearMemory" ) }
490+ />
491+ ) }
492+ </ div >
493+ </ div >
494+ }
495+ bordered
496+ dataSource = { currentGroup . items }
497+ locale = { {
498+ emptyText : (
499+ < div className = "flex flex-col items-center justify-center py-16 text-gray-400" >
500+ < MessageSquareDashed className = "size-12 mb-3 opacity-50" />
501+ < p className = "text-sm" > { t ( "memoryManageModal.noMemory" ) } </ p >
502+ </ div >
503+ ) ,
504+ } }
505+ style = { { height : "100%" , overflowY : "auto" } }
506+ renderItem = { ( item ) => (
507+ < List . Item
508+ className = "hover:bg-gray-50 transition-colors"
509+ actions = { [
510+ < Button
511+ key = "delete"
512+ type = "text"
513+ size = "small"
514+ danger
515+ icon = { < Eraser className = "size-4" /> }
516+ onClick = { ( ) => memory . handleDeleteMemory ( item . id , currentGroup . key ) }
517+ disabled = { disabled }
518+ title = { t ( "memoryManageModal.deleteMemory" ) }
519+ /> ,
520+ ] }
521+ >
522+ < div className = "flex flex-col text-sm" > { item . memory } </ div >
523+ </ List . Item >
524+ ) }
525+ >
526+ { memory . addingMemoryKey === currentGroup . key && (
527+ < List . Item
528+ className = "bg-blue-50 border-t-2 border-blue-300 flex items-center"
529+ style = { { minHeight : "100px" , padding : "20px" } }
530+ >
531+ { renderAddMemoryInput ( currentGroup . key ) }
532+ </ List . Item >
533+ ) }
534+ </ List >
535+ </ div >
536+ </ div >
537+ ) ;
538+ }
539+
0 commit comments