import { useEffect } from 'react' import { ClapEntity, ClapSegmentCategory, newEntity } from '@aitube/clap' import { useTimeline } from '@aitube/timeline' import { FormFile } from '@/components/forms/FormFile' import { FormInput } from '@/components/forms/FormInput' import { FormSection } from '@/components/forms/FormSection' import { FormSelect } from '@/components/forms/FormSelect' import { Button } from '@/components/ui/button' import { useEntityEditor, useIO } from '@/services' function EntityList({ onSelectEntity, }: { onSelectEntity: (entityId: string) => void }) { const entities = useTimeline((s) => s.entities) const setCurrent = useEntityEditor((s) => s.setCurrent) const addEntity = useEntityEditor((s) => s.addEntity) const removeEntity = useEntityEditor((s) => s.removeEntity) const handleAddEntity = () => { const entity: ClapEntity = newEntity({ id: Date.now().toString(), label: 'NEW_ENTITY', category: ClapSegmentCategory.CHARACTER, description: '', appearance: '', }) // ignoring some fields for now addEntity(entity) } return (

Entities

) } export function EntityEditor() { const entities = useTimeline((s) => s.entities) const updateEntities = useTimeline((s) => s.updateEntities) const saveEntitiesToClap = useIO((s) => s.saveEntitiesToClap) const openEntitiesFromClap = useIO((s) => s.openEntitiesFromClap) const current = useEntityEditor((s) => s.current) const setCurrent = useEntityEditor((s) => s.setCurrent) const draft = useEntityEditor((s) => s.draft) const setDraft = useEntityEditor((s) => s.setDraft) const showEntityList = useEntityEditor((s) => s.showEntityList) const setShowEntityList = useEntityEditor((s) => s.setShowEntityList) useEffect(() => { setCurrent(entities.at(0)) }, [entities, setCurrent]) useEffect(() => { setDraft(current) }, [current, setDraft]) const handleInputChange = ( field: keyof ClapEntity, value: string | number | undefined ) => { if (!draft) { return } let updatedValue = value if (field === 'age') { updatedValue = value === '' ? undefined : parseInt(value as string) } if (field === 'label') { updatedValue = value?.toString().toUpperCase() } setDraft({ ...draft, [field]: updatedValue }) } const handleSave = () => { if (!draft) { return } updateEntities([draft]) } const handleFileUpload = async (field: 'imageId' | 'audioId', file: File) => { if (!draft) { return } const dataUrl = await new Promise((resolve) => { const reader = new FileReader() reader.onload = (e) => resolve(e.target?.result as string) reader.readAsDataURL(file) }) setDraft({ ...draft, [field]: dataUrl }) } const handleExport = async () => { if (!draft) { return } await saveEntitiesToClap([draft]) } const handleImport = async (file: File) => { await openEntitiesFromClap(file) } const handleBack = () => { setShowEntityList(true) } const handleSelectEntity = (entityId: string) => { setShowEntityList(false) } return (
{showEntityList ? (
) : (
{draft && ( handleInputChange('label', value)} /> label="Category" selectedItemId={draft.category} items={Object.values(ClapSegmentCategory).map( (category: ClapSegmentCategory) => ({ id: category, label: category, value: category, }) )} onSelect={(value) => handleInputChange('category', value)} /> {/* ... form fields ... */} files[0] && handleFileUpload('imageId', files[0]) } /> {draft.imageId && (
Entity Preview
)} files[0] && handleFileUpload('audioId', files[0]) } /> {draft.audioId && (
)} handleInputChange('description', value)} /> handleInputChange('appearance', value)} /> handleInputChange('age', value)} /> handleInputChange('gender', value)} />
files[0] && handleImport(files[0])} />
)}
)}
) }