88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
|
|
import { FileTextOutlined, SoundOutlined } from "@ant-design/icons";
|
||
|
|
import type { PromptMentionItem, PromptMentionTokenRange, ReferenceItem } from "./workbenchConstants";
|
||
|
|
import { renderPromptPreviewNodes, getPromptMentionTokenRanges } from "./workbenchMentionUtils";
|
||
|
|
|
||
|
|
export { getPromptMentionTokenRanges };
|
||
|
|
|
||
|
|
export function findPromptMentionRangeInside(index: number, ranges: PromptMentionTokenRange[]) {
|
||
|
|
return ranges.find((range) => index > range.start && index < range.end);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function findPromptMentionRangeOverlap(start: number, end: number, ranges: PromptMentionTokenRange[]) {
|
||
|
|
return ranges.find((range) => start < range.end && end > range.start);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function ReferenceInlinePreview({
|
||
|
|
item,
|
||
|
|
}: {
|
||
|
|
item: Pick<ReferenceItem, "kind" | "name" | "previewUrl">;
|
||
|
|
}) {
|
||
|
|
if ((item.kind === "image" || item.kind === "video") && item.previewUrl) {
|
||
|
|
return item.kind === "video" ? (
|
||
|
|
<video src={item.previewUrl} muted playsInline />
|
||
|
|
) : (
|
||
|
|
<img src={item.previewUrl} alt={item.name} loading="lazy" />
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return item.kind === "audio" ? <SoundOutlined /> : <FileTextOutlined />;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function ReferencePreview({
|
||
|
|
item,
|
||
|
|
label,
|
||
|
|
}: {
|
||
|
|
item: Pick<ReferenceItem, "kind" | "name" | "previewUrl">;
|
||
|
|
label?: string;
|
||
|
|
}) {
|
||
|
|
if ((item.kind === "image" || item.kind === "video") && item.previewUrl) {
|
||
|
|
return item.kind === "video" ? (
|
||
|
|
<video src={item.previewUrl} muted playsInline />
|
||
|
|
) : (
|
||
|
|
<img src={item.previewUrl} alt={item.name} loading="lazy" />
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<span className="wb-composer__ref-icon">
|
||
|
|
{item.kind === "audio" ? <SoundOutlined /> : <FileTextOutlined />}
|
||
|
|
{label ? <span>{label}</span> : null}
|
||
|
|
</span>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function PromptPreviewLayer({
|
||
|
|
text,
|
||
|
|
items,
|
||
|
|
onTokenPointerDown,
|
||
|
|
}: {
|
||
|
|
text: string;
|
||
|
|
items: PromptMentionItem[];
|
||
|
|
onTokenPointerDown?: (index: number) => void;
|
||
|
|
}) {
|
||
|
|
const nodes = renderPromptPreviewNodes(text, items);
|
||
|
|
if (nodes.length === 0) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className="wb-composer__highlight"
|
||
|
|
aria-hidden="true"
|
||
|
|
onPointerDown={(event) => {
|
||
|
|
const target =
|
||
|
|
event.target instanceof Element
|
||
|
|
? event.target.closest<HTMLElement>(".wb-composer__mention-inline-chip")
|
||
|
|
: null;
|
||
|
|
if (!target) return;
|
||
|
|
|
||
|
|
event.preventDefault();
|
||
|
|
event.stopPropagation();
|
||
|
|
const tokenEnd = Number(target.dataset.tokenEnd);
|
||
|
|
if (Number.isFinite(tokenEnd)) {
|
||
|
|
onTokenPointerDown?.(tokenEnd);
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{nodes}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|