Initial commit: OmniAI Web Frontend
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
export interface MentionImageOption {
|
||||
id: string;
|
||||
src: string;
|
||||
name: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export function getImageMentionQuery(value: string, selectionStart: number | null | undefined): string | null {
|
||||
if (selectionStart === null || selectionStart === undefined) return null;
|
||||
const beforeCursor = value.slice(0, selectionStart);
|
||||
const match = beforeCursor.match(/(?:^|\s)@([^\s@]*)$/);
|
||||
return match ? match[1] ?? "" : null;
|
||||
}
|
||||
|
||||
export function insertImageMentionValue(
|
||||
value: string,
|
||||
selectionStart: number,
|
||||
imageName: string,
|
||||
maxLength: number,
|
||||
): { value: string; selectionStart: number } {
|
||||
const beforeCursor = value.slice(0, selectionStart);
|
||||
const afterCursor = value.slice(selectionStart);
|
||||
const match = beforeCursor.match(/(?:^|\s)@([^\s@]*)$/);
|
||||
const mentionStart = match ? beforeCursor.length - match[0].length + (match[0].startsWith("@") ? 0 : 1) : selectionStart;
|
||||
const mentionText = `@${imageName.trim() || "uploaded-image"} `;
|
||||
const nextValue = `${value.slice(0, mentionStart)}${mentionText}${afterCursor}`.slice(0, maxLength);
|
||||
const nextSelectionStart = Math.min(mentionStart + mentionText.length, nextValue.length);
|
||||
return { value: nextValue, selectionStart: nextSelectionStart };
|
||||
}
|
||||
|
||||
interface ImageMentionMenuProps {
|
||||
images: MentionImageOption[];
|
||||
query?: string;
|
||||
onSelect: (image: MentionImageOption) => void;
|
||||
}
|
||||
|
||||
function ImageMentionMenu({ images, query = "", onSelect }: ImageMentionMenuProps) {
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const visibleImages = normalizedQuery
|
||||
? images.filter((image) => `${image.label ?? ""} ${image.name}`.toLowerCase().includes(normalizedQuery))
|
||||
: images;
|
||||
|
||||
return (
|
||||
<div className="image-mention-menu" role="listbox" aria-label="选择上传图片">
|
||||
{visibleImages.length ? (
|
||||
visibleImages.map((image) => (
|
||||
<button
|
||||
key={image.id}
|
||||
type="button"
|
||||
className="image-mention-menu__item"
|
||||
role="option"
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault();
|
||||
onSelect(image);
|
||||
}}
|
||||
>
|
||||
<img src={image.src} alt="" />
|
||||
<span>
|
||||
<strong>{image.label ?? image.name}</strong>
|
||||
<em>{image.name}</em>
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<span className="image-mention-menu__empty">没有匹配的上传图片</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImageMentionMenu;
|
||||
Reference in New Issue
Block a user