feat: add assistant emoji picker

This commit is contained in:
Faisal Amir 2025-05-21 23:45:49 +07:00
parent ad962c2cf6
commit 974f7901e6
7 changed files with 116 additions and 28 deletions

View File

@ -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",

View File

@ -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>

View File

@ -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">

View File

@ -12,7 +12,7 @@ interface AssistantState {
}
export const defaultAssistant: Assistant = {
avatar: '',
avatar: '👋',
id: 'jan',
name: 'Jan',
created_at: 1747029866.542,

View File

@ -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;
}
}
}

View File

@ -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">

View File

@ -71,12 +71,6 @@ function General() {
</>
}
/>
<CardItem
title={t('settings.general.autoDownload', {
ns: 'settings',
})}
actions={<Switch />}
/>
<CardItem
title={t('common.language')}
actions={<LanguageSwitcher />}