import React, { useCallback, useState, useRef, useMemo } from 'react'; import Button from '../../button'; import AIChatInput from '../index'; import Configure from '../configure'; import { IconFixedStroked, IconBookOpenStroked, IconClose, IconUpload } from '@douyinfe/semi-icons'; import { modelOptions, mcpOptions, radioButtonProps1, radioButtonProps2, skills, template, reference, uploadProps, suggestionTemplate, customReferences, refTypeToIconMap } from './constant'; import './stories.scss'; import { getAttachmentType, isImageType } from '@douyinfe/semi-foundation/aiChatInput/utils'; import suggestion from './suggestion'; import Mention from '@tiptap/extension-mention'; import ReferSlot from './referSlot'; import { RadioGroup, Radio, Cascader } from '../../index'; import getConfigureItem from '../configure/getConfigureItem'; export default { title: 'AIChatInput', } const outerStyle = { margin: 12, maxHeight: 300 }; export const Basic = () => { const [generating, setGenerating] = useState(false); const onContentChange = useCallback((content) => { console.log('onContentChange', content); }, []); const onUploadChange = useCallback((fileList) => { console.log('onUploadChange', fileList); }, []); const toggleGenerate = useCallback((props) => { setGenerating(value => !value); }, []); return (
); } const temp = { 'input-slot': `我是一名学生,帮我写一段面向的话术内容`, 'select-slot': `我的职业是,帮我写一份...`, // 'skill-slot': '帮我完成...', 'skill-slot': { type: "skillSlot", attrs: { label: "帮我写作", value: 'writing', hasTemplate: false } }, }; export const RichTextExample = () => { const [activeIndex, setActiveIndex] = useState(0); const ref = useRef(); const setTemplate = useCallback((event) => { const index = Number(event.target.dataset.index); setActiveIndex(index); const content = Object.values(temp)[index]; if (ref.current) { ref.current.setContent(content); ref.current.focusEditor(); } }, [ref]); const onContentChange = useCallback((content) => { console.log('onContentChange', content); }, []); const onSkillChange = useCallback((skill) => { console.log("skill", skill); }) return (<>
{Object.keys(temp).map((item, index) => { return
{item}
})}
); } export const SendMessage = () => { const [generating, setGenerating] = useState(false); const onContentChange = useCallback((content) => { console.log('onContentChange', content); }, []); const onUploadChange = useCallback((fileList) => { console.log('onUploadChange', fileList); }, []); const toggleGenerate = useCallback((props) => { setGenerating(value => !value); }, []); return ( ); } export const ReferenceExample = () => { const [references, setReferences] = useState(reference); const handleReferenceDelete = useCallback((item) => { const newReference = references.filter((ref) => ref.id !== item.id); setReferences(newReference); }, [references]); return ( ); } export const ConfigureDemo = () => { const renderLeftMenu = useCallback(() => (<> } field="deepThink">深度思考 } field="onlineSearch">联网搜索 ), []); const onConfigureChange = useCallback((value, changedValue) => { console.log('onConfigureChange', value, changedValue); }, []); const onMessageSend = useCallback((message) => { console.log('message', message); }, []) return ( ); } // 来个自定义的 Cascader const cascaderModalOptions = [ { label: 'GPT', value: 'GPT', children: [ { label: 'GPT-4o', value: 'GPT-4o', }, { value: 'GPT-5', label: 'GPT-5', } ], }, { label: 'Claude', value: 'Claude', children: [ { label: 'Claude 3.5 Sonnet', value: 'Claude 3.5 Sonnet', } ], } ]; const CustomCascader = getConfigureItem(Cascader, { className: 'aiChatInput-cascader-configure'}); export const CustomConfigure = () => { const renderLeftMenu = useCallback(() => (<> ), []); const onConfigureChange = useCallback((value, changedValue) => { console.log('onConfigureChange', value, changedValue); }, []); return ( ); } export const Square = () => { const [round, setRound] = useState(false); const renderLeftMenu = useCallback(() => <> } field="deepThink">深度思考 } field="onlineSearch">联网搜索 ); const onChange = useCallback((e) => { setRound(e.target.value); }, []); return (<> 圆形 方形 ); } export const Suggestion = () => { const [suggestion, setSuggestion] = useState([]); const onChange = useCallback((content) => { const value = content?.[0]?.text; if (value === undefined || value.includes('\n')) { if (suggestion === undefined || suggestion.length === 0) { return; } else { return setSuggestion([]); } } if (value.length === 0) { setSuggestion([]); } else if (value.length > 0 && value.length < 4) { const su = new Array(suggestionTemplate.length).fill(0).map((item, index) => { return `${value}, ${suggestionTemplate[index]}`; }); setSuggestion(su); } else if (value.length >= 4){ setSuggestion([]) } }, [suggestion]); const renderLeftMenu = useCallback(() => <> ); return ( ); } const TemplateContent = (props) => { const { onTemplateClick: onTemplateClickProps } = props; const [groupIndex, setGroupIndex] = useState(0); const onItemClick = useCallback((e) => { const index = e.target.dataset.index; setGroupIndex(Number(index)); }, []) const onTemplateClick = useCallback((item) => { const { content } = item; onTemplateClickProps(content); }, [onTemplateClickProps]) return (
{/* tabs */}
{template?.map((item, index) => { return
{item.group}
})}
{/* content */}
{template?.[groupIndex]?.children?.map((item, index) =>
onTemplateClick(item)} >
{item.icon}
{item.title}
{item.desc}
)}
); } export const Template = () => { const ref = useRef(); const setTemplate = useCallback((content) => { ref.current?.setContentWhileSaveTool(content); ref.current?.focusEditor(); }, [ref]); const renderTemplate = useCallback((skill, e) => { if (skill?.value === 'writing') { return } }, [setTemplate]); const renderLeftMenu = useCallback(() => <> } field="deepThink">深度思考 } field="onlineSearch">联网搜索 ); const onConfigureChange = useCallback((value, changedValue) => { console.log('onConfigureChange', value, changedValue); }, []); return ( ); } export const CustomRenderTop = () => { const ref = useRef(); const [reference, setReference] = useState(customReferences); const renderLeftMenu = useCallback(() => <> ); const renderTopSlot = useCallback((props) => { const { attachments = [], references } = props; return
{references?.map((item, index) => { const { type, name, detail, key, ...rest } = item; return (
{React.cloneElement(refTypeToIconMap.get(type), { className: 'item-left item-icon'})} { const newReferences = [...references]; newReferences.splice(index, 1); setReference(newReferences); }}/> {name} {type === 'branch' && {detail}}
) })} {attachments.map((item, index) => { const isImage = isImageType(item); const realType = getAttachmentType(item); const { uid, name, url, size, percent, status } = item; return (
{isImage ? {item.name} : } ref.current?.deleteUploadFile(item)}/> {name}
); })}
}, []); return ( ); } export const CustomRichTextExtension = () => { const ref = useRef(); const [reference, setReference] = useState(customReferences); const extensions = useMemo(() => { // 使用 @ 触发 return [ ReferSlot, Mention.configure({ HTMLAttributes: { class: 'mention', }, suggestion, }), ] }, []); const renderLeftMenu = useCallback(() => <> ); const renderTopSlot = useCallback((props) => { const { attachments = [], references = [], content = [] } = props; const showContent = content.filter((item) => item.type !== 'text'); return
{/* order: reference, rich text area content, attachments */} {showContent.map((item, index) => { const { type, value, name, key, detail, ...rest } = item; return (
{React.cloneElement(refTypeToIconMap.get(type), { className: 'item-left item-icon'})} { ref?.current?.deleteContent(item); }}/> {name ?? value} {type === 'branch' && {detail}}
) })} {references.map((item, index) => { const { type, name, detail, key, ...rest } = item; return (
{React.cloneElement(refTypeToIconMap.get(type), { className: 'item-left item-icon'})} { const newReferences = [...references]; newReferences.splice(index, 1); setReference(newReferences); }}/> {name} {type === 'branch' && {detail}}
) })} {attachments.map((item, index) => { const isImage = isImageType(item); const realType = getAttachmentType(item); const { uid, name, url, size, percent, status } = item; return (
{isImage ? {item.name} : } ref.current?.deleteUploadFile(item)}/> {name}
); })}
}, []); const onContentChange = useCallback((content) => { console.log('onContentChange', content); }, []); const onButtonClick = useCallback(() => { console.log('html', ref.current?.editor.getHTML()); console.log('json', ref.current?.editor.getJSON()); }, [ref]); const transformer = useMemo(() => { return new Map([ ['referSlot', (obj) => { const { attrs = {} } = obj; const { value, info, type = 'text', uniqueKey } = attrs; return { type: type, value: value, uniqueKey: uniqueKey, ...JSON.parse(info), }; }], ]); }, []); return ( <> {/* */} ); }