AIAssistantHeader.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { Clipboard, Ellipsis, Plus, Settings, X } from 'lucide-react'
  2. import { useState } from 'react'
  3. import {
  4. AiIconAnimation,
  5. Button,
  6. copyToClipboard,
  7. DropdownMenu,
  8. DropdownMenuContent,
  9. DropdownMenuItem,
  10. DropdownMenuTrigger,
  11. } from 'ui'
  12. import { Admonition } from 'ui-patterns'
  13. import { ButtonTooltip } from '../ButtonTooltip'
  14. import { AIAssistantChatSelector } from './AIAssistantChatSelector'
  15. import { AIOptInModal } from './AIOptInModal'
  16. import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
  17. interface AIAssistantHeaderProps {
  18. isChatLoading: boolean
  19. onNewChat: () => void
  20. onCloseAssistant: () => void
  21. showMetadataWarning: boolean
  22. updatedOptInSinceMCP: boolean
  23. isHipaaProjectDisallowed: boolean
  24. aiOptInLevel: 'disabled' | 'schema' | 'full' | string | undefined
  25. }
  26. export const AIAssistantHeader = ({
  27. isChatLoading,
  28. onNewChat,
  29. onCloseAssistant,
  30. showMetadataWarning,
  31. updatedOptInSinceMCP,
  32. isHipaaProjectDisallowed,
  33. aiOptInLevel,
  34. }: AIAssistantHeaderProps) => {
  35. const snap = useAiAssistantStateSnapshot()
  36. const [isOptInModalOpen, setIsOptInModalOpen] = useState(false)
  37. return (
  38. <div className="z-30 sticky top-0">
  39. <div className="border-b border-b-muted flex items-center bg gap-x-4 pl-4 pr-3 min-h-(--header-height)">
  40. <div className="text-sm flex-1 flex items-center">
  41. <AiIconAnimation size={20} allowHoverEffect={false} />
  42. <span className="text-border-stronger dark:text-border-strong ml-3">
  43. <svg
  44. viewBox="0 0 24 24"
  45. width="16"
  46. height="16"
  47. stroke="currentColor"
  48. strokeWidth="1"
  49. strokeLinecap="round"
  50. strokeLinejoin="round"
  51. fill="none"
  52. shapeRendering="geometricPrecision"
  53. >
  54. <path d="M16 3.549L7.12 20.600" />
  55. </svg>
  56. </span>
  57. <AIAssistantChatSelector />
  58. </div>
  59. <div className="flex items-center gap-x-4">
  60. <div className="flex items-center">
  61. <ButtonTooltip
  62. type="text"
  63. size="tiny"
  64. icon={<Plus strokeWidth={1.5} />}
  65. onClick={onNewChat}
  66. className="h-7 w-7 p-0"
  67. tooltip={{ content: { side: 'bottom', text: 'New chat' } }}
  68. />
  69. <ButtonTooltip
  70. type="text"
  71. size="tiny"
  72. icon={<Settings strokeWidth={1.5} />}
  73. onClick={() => setIsOptInModalOpen(true)}
  74. className="h-7 w-7 p-0"
  75. disabled={isChatLoading}
  76. tooltip={{
  77. content: { side: 'bottom', text: 'Permission settings' },
  78. }}
  79. />
  80. <DropdownMenu>
  81. <DropdownMenuTrigger asChild>
  82. <ButtonTooltip
  83. type="text"
  84. size="tiny"
  85. icon={<Ellipsis strokeWidth={1.5} />}
  86. className="h-7 w-7 p-0"
  87. tooltip={{ content: { side: 'bottom', text: 'More options' } }}
  88. />
  89. </DropdownMenuTrigger>
  90. <DropdownMenuContent align="end">
  91. <DropdownMenuItem
  92. className="gap-x-2"
  93. onClick={() => copyToClipboard(snap.activeChatId ?? '')}
  94. >
  95. <Clipboard size={14} strokeWidth={1.5} />
  96. <span>Copy chat ID</span>
  97. </DropdownMenuItem>
  98. </DropdownMenuContent>
  99. </DropdownMenu>
  100. <ButtonTooltip
  101. type="text"
  102. className="w-7 h-7"
  103. onClick={onCloseAssistant}
  104. icon={<X strokeWidth={1.5} />}
  105. tooltip={{ content: { side: 'bottom', text: 'Close assistant' } }}
  106. />
  107. </div>
  108. </div>
  109. </div>
  110. {showMetadataWarning && (
  111. <Admonition
  112. type="default"
  113. title={
  114. !updatedOptInSinceMCP
  115. ? 'The Assistant has just been updated to help you better!'
  116. : isHipaaProjectDisallowed
  117. ? 'Project metadata is not shared due to HIPAA'
  118. : aiOptInLevel === 'disabled'
  119. ? 'Project metadata is currently not shared'
  120. : 'Limited metadata is shared to the Assistant'
  121. }
  122. description={
  123. !updatedOptInSinceMCP
  124. ? 'You may now opt-in to share schema metadata and even logs for better results'
  125. : isHipaaProjectDisallowed
  126. ? 'Your organization has the HIPAA addon and will not send project metadata with your prompts for projects marked as HIPAA.'
  127. : aiOptInLevel === 'disabled'
  128. ? 'The Assistant can provide better answers if you opt-in to share schema metadata.'
  129. : aiOptInLevel === 'schema'
  130. ? 'Sharing query data in addition to schema can further improve responses. Update AI settings to enable this.'
  131. : ''
  132. }
  133. className="border-0 border-b rounded-none bg-background"
  134. >
  135. {!isHipaaProjectDisallowed && (
  136. <Button type="default" className="w-fit mt-4" onClick={() => setIsOptInModalOpen(true)}>
  137. Permission settings
  138. </Button>
  139. )}
  140. </Admonition>
  141. )}
  142. <AIOptInModal visible={isOptInModalOpen} onCancel={() => setIsOptInModalOpen(false)} />
  143. </div>
  144. )
  145. }