cosmic-ai-input
v1.0.40
Published
A powerful AI conversation input component for React with TypeScript support
Maintainers
Readme
Cosmic AI Input
一个强大的 React AI 对话输入组件,支持 TypeScript。
安装
npm install cosmic-ai-input
pnpm install cosmic-ai-input依赖
这个组件需要以下 peer dependencies:
- React 18.2.0+
- React-DOM 18.2.0+
- classnames 2.5.1+
请确保你的项目中已经安装了这些依赖
样式导入
重要: 使用本组件时,必须手动导入样式文件,否则组件将没有样式。
使用方法
ES 模块环境
import React, { useRef } from "react";
import { AiInput } from "cosmic-ai-input";
// 导入样式(推荐方法)
import "cosmic-ai-input/dist/style.css";
const App = () => {
const aiInputRef = useRef(null);
const varList = [
{
type: "input",
name: "name",
content: "",
placeholder: "Your name",
},
{
type: "select",
name: "gender",
content: "",
placeholder: "Select gender",
options: ["Male", "Female", "Other"],
},
{
type: "multiple-select",
name: "hobbies",
content: "",
placeholder: "Select hobbies",
options: ["Reading", "Sports", "Music", "Travel"],
},
{
type: "date-picker",
name: "birthday",
content: "",
placeholder: "Select birthday",
},
{
type: "label",
name: "tags",
content: "",
},
];
const mentions = [
{
prefix: "@",
options: ["User1", "User2", "User3"],
},
{
prefix: "[",
options: ["name", "gender", "hobbies", "birthday", "tags"],
},
];
const handleSend = (value) => {
console.log("发送的消息:", value);
// 在这里处理发送逻辑
};
const handleGetValue = () => {
if (aiInputRef.current) {
const currentValue = aiInputRef.current.getCurrentValue();
console.log("当前值:", currentValue);
}
};
const handleFocus = () => {
if (aiInputRef.current) {
aiInputRef.current.focus();
}
};
return (
<div>
<h1>AI 对话输入组件示例</h1>
<AiInput
ref={aiInputRef}
varList={varList}
mentions={mentions}
value="你好 [name],你的性别是 [gender],爱好是 [hobbies],生日是 [birthday]"
placeholder="在这里输入你的消息..."
onSend={handleSend}
/>
<button onClick={handleGetValue}>获取当前值</button>
<button onClick={handleFocus}>获得焦点</button>
</div>
);
};
export default App;键盘快捷键
| 快捷键 | 行为(enterMode="send") | 行为(enterMode="newline") | | ---------------- | ------------------------ | --------------------------- | | Enter | 发送消息 | 换行 | | Shift + Enter | 换行 | 发送消息 | | Ctrl/Cmd + Enter | - | 发送消息 | | 上下箭头 | 在提及选项列表中导航 | 在提及选项列表中导航 | | Escape | 关闭打开的下拉菜单 | 关闭打开的下拉菜单 |
API
AiInputProps
| 属性 | 类型 | 默认值 | 描述 |
| ------------------- | ------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------- |
| value | string | - | 输入框的值,支持使用 [变量名] 格式引用变量 |
| varList | VarItem[] | - | 变量列表,包含输入框、选择框、多选框、日期选择器和标签等 |
| placeholder | string | '' | 占位符文本 |
| maxLength | 1 | 2 | 3 | 4 | undefined | - | 最大行数限制 |
| defaultRows | number | 3 | 默认显示行数 |
| disabled | boolean | false | 是否禁用输入框 |
| defaultFocus | boolean | false | 是否默认获得焦点 |
| textIndex | number | 0 | 文本缩进距离(单位:像素) |
| mentions | MentionItem[] | [] | 提及配置,支持自定义前缀触发选项列表 |
| enterMode | 'send' | 'newline' | 'send' | 回车键行为模式。send:Enter 发送,Shift+Enter 换行;newline:Enter 换行,Shift/Ctrl/Cmd+Enter 发送 |
| mentionPlacement | 'cursor' | 'bottom' | 'cursor' | 提及浮窗定位模式。cursor:出现在光标位置旁;bottom:出现在输入框下方,宽度与输入框一致 |
| mentionMaxHeight | number | 240 | 提及浮窗的最大高度(px)。内容不足时按内容收缩,超出则浮窗内部滚动 |
| onMentionSelect | (payload: MentionSelectPayload) => void | - | 选中并插入某个提及项时触发,可拿到插入的值和完整选项对象 |
| onMentionDelete | (payload: MentionDeletePayload) => void | - | 在输入框中删除某个已插入的提及标签时触发 |
| onParse | (parsedValue: string) => void | - | 解析输入值时的回调函数 |
| onSend | (value: string) => void | - | 发送消息时的回调函数(按 Enter 键触发) |
| onMaxLengthExceeded | (value: string, maxLength: number) => void | - | 超过最大长度时的回调函数 |
| onFocus | (e: React.FocusEvent) => void | - | 获得焦点时的回调函数 |
| onBlur | (e: React.FocusEvent) => void | - | 失去焦点时的回调函数 |
| onChange | (value: string) => void | - | 值改变时的回调函数 |
| onClick | (e: React.MouseEvent) => void | - | 点击时的回调函数 |
| onKeyDown | (e: React.KeyboardEvent) => void | - | 按键按下时的回调函数 |
| onKeyUp | (e: React.KeyboardEvent) => void | - | 按键抬起时的回调函数 |
BaseInputItem
所有输入项的基础接口:
| 属性 | 类型 | 描述 | | ----------- | ------ | --------------------------- | | type | string | 组件类型 | | name | string | 变量名,用于在 value 中引用 | | content | string | 初始内容 | | placeholder | string | 占位符文本 |
InputItem
基础输入项:
interface InputItem extends BaseInputItem {
type: "input";
}SelectItem
下拉选择项:
interface SelectItem extends BaseInputItem {
type: "select";
options: string[]; // 选项列表
}MultipleSelectItem
多选框项:
interface MultipleSelectItem extends BaseInputItem {
type: "multiple-select";
options: string[]; // 选项列表
}DatePickerItem
日期选择器项:
interface DatePickerItem extends BaseInputItem {
type: "date-picker";
}LabelItem
标签项:
interface LabelItem {
type: "label"; // 类型为 label
name: string; // 变量名,用于在 value 中引用
content: string; // 标签内容
}MentionItem
提及配置项:
interface MentionOption {
label: string; // 显示标题,也是默认插入到输入框的值
value?: string; // 可选,标签上展示的文本,默认使用 label
insertText?: string; // 可选,真正序列化进输入框 value 的文本,默认使用 value。可实现「标签显示一种、实际插入另一种」
description?: string; // 描述文本(标题下方小字)
icon?: React.ReactNode; // 图标,可传图片 URL 字符串或 ReactNode
}
interface MentionItem {
prefix: string; // 触发前缀,如 '@' 或 '['
title?: string; // 下拉框顶部标题
searchable?: boolean; // 是否显示搜索框,默认 false
searchPlaceholder?: string; // 搜索框占位符,默认 '搜索'
options: (string | MentionOption)[]; // 选项列表,支持纯字符串或富对象
}
// onMentionSelect 回调载荷
interface MentionSelectPayload {
prefix: string; // 触发的前缀
value: string; // 实际插入到输入框的值
option: MentionOption; // 命中的完整选项对象
id: string; // 该标签在 DOM 中的唯一 id
}
// onMentionDelete 回调载荷
interface MentionDeletePayload {
prefix: string; // 触发的前缀
value: string; // 被删除标签的文本
option: MentionOption; // 命中的完整选项对象
id: string; // 被删除标签的 id
}简单用法(纯文本选项):
<AiInput
mentions={[
{
prefix: '@',
options: ['用户1', '用户2', '用户3']
},
{
prefix: '[',
options: ['话题1', '话题2', '话题3']
}
]}
// 当用户输入 @ 或 [ 时,会显示对应的选项列表
// 支持键盘上下箭头导航,回车选择
/>富选项用法(带图标、描述、搜索框):
<AiInput
mentions={[
{
prefix: '@',
title: '选择智能体',
searchable: true,
searchPlaceholder: '搜索智能体',
options: [
{
label: '嵌入式AI分析助手',
description: '过滤单据、生成图表助手',
icon: 'https://example.com/icon1.png'
},
{
label: '客户销售订单分析助手',
description: '输入客户名称,自动查询销售订单',
icon: <MyIconComponent />
}
]
}
]}
/>自定义插入文本 & 回显(insertText):
insertText 让「标签上展示的文本」和「真正插入到 value 的文本」解耦,并作为回显时的识别标记:
<AiInput
value={value} // 把之前保存的纯文本传回来即可回显
onChange={setValue}
mentions={[
{
prefix: '@',
options: [
{
label: '嵌入式AI分析助手', // 下拉里展示
insertText: '{{agent:embedded-ai}}', // 标签显示「嵌入式AI分析助手」,但 value 里序列化成这段
},
],
},
]}
/>- 选中后标签显示
label(或value),但getCurrentValue()/onChange输出的纯文本里这段是insertText。 - 回显:把保存的纯文本通过
value传回来,组件会扫描其中命中任一 optioninsertText的片段,自动还原成高亮标签。 - 回显能力仅对显式设置了
insertText的 option 生效。建议insertText用独特、不会和正常文本撞的 token(如{{agent:xxx}}),否则可能把用户手打的普通文字误识别成标签。 - 多个 token 存在子串包含关系时按最长优先匹配;同一 token 出现多次会各自还原。
AiInputRef
通过 ref 可以访问的方法:
| 方法 | 类型 | 描述 | | --------------- | ------------ | ---------------------------------------------- | | getCurrentValue | () => string | 获取当前输入框的值(包含变量替换后的完整文本) | | focus | () => void | 让输入框获得焦点 |
TypeScript 支持
本组件完全支持 TypeScript,并提供了完整的类型定义。所有组件属性和方法都有类型检查,可以在开发过程中获得更好的代码提示和错误检测。
类型定义
import React from "react";
export interface BaseInputItem {
name: string;
content: string;
placeholder: string;
}
export interface InputItem extends BaseInputItem {
type: "input";
}
export interface SelectItem extends BaseInputItem {
type: "select";
options: string[];
}
export interface MultipleSelectItem extends BaseInputItem {
type: "multiple-select";
options: string[];
}
export interface DatePickerItem extends BaseInputItem {
type: "date-picker";
}
export interface LabelItem {
type: "label";
name: string;
content: string;
}
export interface MentionOption {
label: string;
value?: string;
insertText?: string;
description?: string;
icon?: React.ReactNode;
}
export interface MentionItem {
prefix: string;
title?: string;
searchable?: boolean;
searchPlaceholder?: string;
options: (string | MentionOption)[];
}
export interface MentionSelectPayload {
prefix: string;
value: string;
option: MentionOption;
id: string;
}
export interface MentionDeletePayload {
prefix: string;
value: string;
option: MentionOption;
id: string;
}
export type VarItem =
| InputItem
| SelectItem
| MultipleSelectItem
| DatePickerItem
| LabelItem;
export interface AiInputProps {
value?: string;
varList: VarItem[];
placeholder?: string;
maxLength?: 1 | 2 | 3 | 4;
defaultRows?: number;
disabled?: boolean;
defaultFocus?: boolean;
textIndex?: number;
mentions?: MentionItem[];
enterMode?: "send" | "newline";
mentionPlacement?: "cursor" | "bottom";
mentionMaxHeight?: number;
onMentionSelect?: (payload: MentionSelectPayload) => void;
onMentionDelete?: (payload: MentionDeletePayload) => void;
onParse?: (parsedValue: string) => void;
onSend?: (value: string) => void;
onMaxLengthExceeded?: (value: string, maxLength: number) => void;
onFocus?: (e: React.FocusEvent) => void;
onBlur?: (e: React.FocusEvent) => void;
onChange?: (value: string) => void;
onClick?: (e: React.MouseEvent) => void;
onKeyDown?: (e: React.KeyboardEvent) => void;
onKeyUp?: (e: React.KeyboardEvent) => void;
}
export interface AiInputRef {
getCurrentValue: () => string;
focus: () => void;
}