feat: add assistant emoji picker
This commit is contained in:
parent
ad962c2cf6
commit
974f7901e6
@ -37,6 +37,7 @@
|
||||
"@uiw/react-textarea-code-editor": "^3.1.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"culori": "^4.0.1",
|
||||
"emoji-picker-react": "^4.12.2",
|
||||
"fzf": "^0.5.2",
|
||||
"i18next": "^25.0.1",
|
||||
"katex": "^0.16.22",
|
||||
|
||||
@ -36,6 +36,9 @@ const DropdownAssistant = () => {
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="bg-main-view-fg/5 py-0.5 hover:bg-main-view-fg/8 px-2 rounded font-medium cursor-pointer flex items-center gap-1.5 relative z-20 max-w-40">
|
||||
<span className="text-main-view-fg/80 truncate">
|
||||
{selectedAssistant?.avatar && (
|
||||
<span className="mr-1">{selectedAssistant?.avatar}</span>
|
||||
)}
|
||||
{selectedAssistant?.name || 'Jan'}
|
||||
</span>
|
||||
</button>
|
||||
@ -43,7 +46,6 @@ const DropdownAssistant = () => {
|
||||
<div
|
||||
className="size-5 cursor-pointer relative z-10 flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out "
|
||||
onClick={() => {
|
||||
console.log('edit clicked', selectedAssistant)
|
||||
if (selectedAssistant) {
|
||||
setEditingAssistantId(selectedAssistant.id)
|
||||
setDialogOpen(true)
|
||||
@ -73,6 +75,7 @@ const DropdownAssistant = () => {
|
||||
updateCurrentThreadAssistant(assistant)
|
||||
}}
|
||||
>
|
||||
<span className="mr-1">{assistant?.avatar}</span>
|
||||
{assistant.name}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -9,6 +9,7 @@ import {
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { IconPlus, IconTrash, IconChevronDown } from '@tabler/icons-react'
|
||||
import EmojiPicker, { EmojiClickData, Theme } from 'emoji-picker-react'
|
||||
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
@ -18,6 +19,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
|
||||
interface AddEditAssistantProps {
|
||||
open: boolean
|
||||
@ -43,9 +45,32 @@ export default function AddEditAssistant({
|
||||
const [instructions, setInstructions] = useState(
|
||||
initialData?.instructions || ''
|
||||
)
|
||||
const { isDark } = useTheme()
|
||||
const [paramsKeys, setParamsKeys] = useState<string[]>([''])
|
||||
const [paramsValues, setParamsValues] = useState<unknown[]>([''])
|
||||
const [paramsTypes, setParamsTypes] = useState<string[]>(['string'])
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const emojiPickerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Handle click outside emoji picker
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
emojiPickerRef.current &&
|
||||
!emojiPickerRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setShowEmojiPicker(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (showEmojiPicker) {
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [showEmojiPicker])
|
||||
|
||||
// Reset form when modal opens/closes or editing key changes
|
||||
useEffect(() => {
|
||||
@ -189,26 +214,80 @@ export default function AddEditAssistant({
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm mb-2 inline-block">Name</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Enter name"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative">
|
||||
<label className="text-sm mb-2 inline-block">Emoji</label>
|
||||
<div
|
||||
className="border rounded-sm p-2 w-9 h-9 flex items-center justify-center border-main-view-fg/10 cursor-pointer"
|
||||
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
|
||||
>
|
||||
{avatar || '😊'}
|
||||
</div>
|
||||
<div className="relative" ref={emojiPickerRef}>
|
||||
<EmojiPicker
|
||||
open={showEmojiPicker}
|
||||
theme={isDark ? ('dark' as Theme) : ('light' as Theme)}
|
||||
className="!absolute !z-40 !overflow-y-auto top-2"
|
||||
height={350}
|
||||
lazyLoadEmojis
|
||||
previewConfig={{ showPreview: false }}
|
||||
onEmojiClick={(emojiData: EmojiClickData) => {
|
||||
setAvatar(emojiData.emoji)
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 w-full">
|
||||
<label className="text-sm mb-2 inline-block">Name</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Enter name"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm mb-2 inline-block">
|
||||
Avatar (optional)
|
||||
</label>
|
||||
<Input
|
||||
value={avatar || ''}
|
||||
onChange={(e) => setAvatar(e.target.value)}
|
||||
placeholder="Enter avatar URL"
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="space-y-2">
|
||||
<label className="text-sm mb-2 inline-block">Emoji Avatar</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="border rounded-md p-2 w-12 h-12 flex items-center justify-center text-2xl">
|
||||
{avatar || '😊'}
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Button
|
||||
onClick={() =>
|
||||
document
|
||||
.getElementById('emoji-picker-container')
|
||||
?.classList.toggle('hidden')
|
||||
}
|
||||
>
|
||||
Choose Emoji
|
||||
</Button>
|
||||
<div
|
||||
id="emoji-picker-container"
|
||||
className="absolute z-50 mt-1 hidden"
|
||||
>
|
||||
<EmojiPicker
|
||||
open={true}
|
||||
theme={isDark ? ('dark' as Theme) : ('light' as Theme)}
|
||||
autoFocusSearch
|
||||
previewConfig={{ showPreview: false }}
|
||||
onEmojiClick={(emojiData: EmojiClickData) => {
|
||||
setAvatar(emojiData.emoji)
|
||||
document
|
||||
.getElementById('emoji-picker-container')
|
||||
?.classList.add('hidden')
|
||||
}}
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm mb-2 inline-block">
|
||||
|
||||
@ -12,7 +12,7 @@ interface AssistantState {
|
||||
}
|
||||
|
||||
export const defaultAssistant: Assistant = {
|
||||
avatar: '',
|
||||
avatar: '👋',
|
||||
id: 'jan',
|
||||
name: 'Jan',
|
||||
created_at: 1747029866.542,
|
||||
|
||||
@ -94,4 +94,12 @@
|
||||
@apply font-bold;
|
||||
color: color-mix(in srgb, currentColor 80%, white 20%);
|
||||
}
|
||||
}
|
||||
.epr-main {
|
||||
input {
|
||||
@apply border !border-main-view-fg/10 !h-9 !text-sm !bg-main-view;
|
||||
}
|
||||
input:focus {
|
||||
@apply !bg-main-view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +67,9 @@ function Assistant() {
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-base font-medium text-main-view-fg/80">
|
||||
{assistant.avatar && (
|
||||
<span className="mr-1">{assistant.avatar}</span>
|
||||
)}
|
||||
{assistant.name}
|
||||
</h3>
|
||||
<div className="flex items-center gap-0.5">
|
||||
|
||||
@ -71,12 +71,6 @@ function General() {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<CardItem
|
||||
title={t('settings.general.autoDownload', {
|
||||
ns: 'settings',
|
||||
})}
|
||||
actions={<Switch />}
|
||||
/>
|
||||
<CardItem
|
||||
title={t('common.language')}
|
||||
actions={<LanguageSwitcher />}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user