SessionDropdown.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import type { Session } from "@opencode-ai/sdk/client"
  2. import { SessionList } from "./SessionList"
  3. import { uiBridgeUpdate } from "../../state/uiBridgeState"
  4. interface SessionDropdownProps {
  5. sessions: Session[]
  6. currentSessionId: string | undefined
  7. filteredSessions: Session[]
  8. isDropdownOpen: boolean
  9. isSelectMode: boolean
  10. selectedSessions: Set<string>
  11. selectedSessionIndex: number
  12. searchQuery: string
  13. editingSessionId: string | null
  14. editingTitle: string
  15. searchInputRef: React.RefObject<HTMLInputElement | null>
  16. editInputRef: React.RefObject<HTMLInputElement | null>
  17. selectedSessionRef: React.RefObject<HTMLDivElement | null>
  18. sessionListRef: React.RefObject<HTMLDivElement | null>
  19. sharingSessionId: string | null
  20. onSearchChange: (value: string) => void
  21. onSearchKeyDown: (e: React.KeyboardEvent) => void
  22. onToggleSelectMode: () => void
  23. onSessionSelect: (sessionId: string) => void
  24. onEditStart: (sessionId: string, currentTitle: string, e: React.MouseEvent) => void
  25. onEditSave: (sessionId: string) => void
  26. onEditCancel: () => void
  27. onEditChange: (value: string) => void
  28. onDeleteStart: (sessionId: string, e: React.MouseEvent) => void
  29. onBulkDeleteStart: () => void
  30. onCheckboxChange: (sessionId: string, checked: boolean) => void
  31. onKeyDown: (e: React.KeyboardEvent) => void
  32. onToggleShare: (sessionId: string, e: React.MouseEvent) => void
  33. }
  34. export function SessionDropdown({
  35. sessions,
  36. currentSessionId,
  37. filteredSessions,
  38. isDropdownOpen,
  39. isSelectMode,
  40. selectedSessions,
  41. selectedSessionIndex,
  42. searchQuery,
  43. editingSessionId,
  44. editingTitle,
  45. searchInputRef,
  46. editInputRef,
  47. selectedSessionRef,
  48. sessionListRef,
  49. sharingSessionId,
  50. onSearchChange,
  51. onSearchKeyDown,
  52. onToggleSelectMode,
  53. onSessionSelect,
  54. onEditStart,
  55. onEditSave,
  56. onEditCancel,
  57. onEditChange,
  58. onDeleteStart,
  59. onBulkDeleteStart,
  60. onCheckboxChange,
  61. onKeyDown,
  62. onToggleShare,
  63. }: SessionDropdownProps) {
  64. if (!isDropdownOpen) return null
  65. const handleSelect = (sessionId: string) => {
  66. uiBridgeUpdate({ sessionID: sessionId })
  67. onSessionSelect(sessionId)
  68. }
  69. return (
  70. <div className="absolute top-full left-0 right-0 w-full bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 shadow-lg z-50 max-h-96 flex flex-col">
  71. {/* Search input and select mode toggle */}
  72. <div className="p-2 border-b border-gray-200 dark:border-gray-800">
  73. <div className="flex gap-2">
  74. <input
  75. ref={searchInputRef}
  76. type="text"
  77. placeholder="Search sessions..."
  78. value={searchQuery}
  79. onChange={(e) => onSearchChange(e.target.value)}
  80. onKeyDown={onSearchKeyDown}
  81. className="flex-1 px-2 py-1 text-sm bg-gray-50 dark:bg-gray-950 border border-gray-200 dark:border-gray-800 rounded outline-none focus:border-blue-500 dark:focus:border-blue-500 text-gray-900 dark:text-gray-100"
  82. />
  83. <button
  84. onClick={onToggleSelectMode}
  85. className={`px-2 h-[30px] flex items-center justify-center rounded border ${
  86. isSelectMode
  87. ? "bg-blue-500 text-white border-blue-500"
  88. : "bg-gray-100 text-gray-700 border-gray-300 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600"
  89. }`}
  90. title={isSelectMode ? "Cancel selection" : "Select multiple sessions"}
  91. data-tip={isSelectMode ? "Cancel selection" : "Select multiple sessions"}
  92. >
  93. {isSelectMode ? (
  94. <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  95. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
  96. </svg>
  97. ) : (
  98. <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  99. <path
  100. strokeLinecap="round"
  101. strokeLinejoin="round"
  102. strokeWidth={2}
  103. d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
  104. />
  105. </svg>
  106. )}
  107. </button>
  108. </div>
  109. </div>
  110. {/* Session list */}
  111. <SessionList
  112. sessions={sessions}
  113. currentSessionId={currentSessionId}
  114. filteredSessions={filteredSessions}
  115. isSelectMode={isSelectMode}
  116. selectedSessions={selectedSessions}
  117. selectedSessionIndex={selectedSessionIndex}
  118. editingSessionId={editingSessionId}
  119. editingTitle={editingTitle}
  120. editInputRef={editInputRef}
  121. selectedSessionRef={selectedSessionRef}
  122. sessionListRef={sessionListRef}
  123. sharingSessionId={sharingSessionId}
  124. onSessionSelect={handleSelect}
  125. onEditStart={onEditStart}
  126. onEditSave={onEditSave}
  127. onEditCancel={onEditCancel}
  128. onEditChange={onEditChange}
  129. onDeleteStart={onDeleteStart}
  130. onCheckboxChange={onCheckboxChange}
  131. onKeyDown={onKeyDown}
  132. onToggleShare={onToggleShare}
  133. />
  134. {/* Bulk delete button (shown in select mode when sessions are selected) */}
  135. {isSelectMode && selectedSessions.size > 0 && (
  136. <div className="p-2 border-t border-gray-200 dark:border-gray-800">
  137. <button
  138. onClick={onBulkDeleteStart}
  139. className="w-full px-3 py-2 text-sm bg-red-500 text-white rounded hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed"
  140. >
  141. Delete {selectedSessions.size} Session{selectedSessions.size > 1 ? "s" : ""}
  142. </button>
  143. </div>
  144. )}
  145. </div>
  146. )
  147. }