// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // SPDX-License-Identifier: MIT import type { MentionOptions } from "@tiptap/extension-mention"; import { ReactRenderer } from "@tiptap/react"; import { ResourceMentions, type ResourceMentionsProps, } from "./resource-mentions"; import type { Instance, Props } from "tippy.js"; import tippy from "tippy.js"; import { resolveServiceURL } from "~/core/api/resolve-service-url"; import type { Resource } from "~/core/messages"; export const resourceSuggestion: MentionOptions["suggestion"] = { items: ({ query }) => { return fetch(resolveServiceURL(`rag/resources?query=${query}`), { method: "GET", }) .then((res) => res.json()) .then((res) => { return res.resources as Array; }) .catch((err) => { return []; }); }, render: () => { let reactRenderer: ReactRenderer< { onKeyDown: (args: { event: KeyboardEvent }) => boolean }, ResourceMentionsProps >; let popup: Instance[] | null = null; return { onStart: (props) => { if (!props.clientRect) { return; } reactRenderer = new ReactRenderer(ResourceMentions, { props, editor: props.editor, }); popup = tippy("body", { getReferenceClientRect: props.clientRect as any, appendTo: () => document.body, content: reactRenderer.element, showOnCreate: true, interactive: true, trigger: "manual", placement: "top-start", }); }, onUpdate(props) { reactRenderer.updateProps(props); if (!props.clientRect) { return; } popup?.[0]?.setProps({ getReferenceClientRect: props.clientRect as any, }); }, onKeyDown(props) { if (props.event.key === "Escape") { popup?.[0]?.hide(); return true; } return reactRenderer.ref?.onKeyDown(props) ?? false; }, onExit() { popup?.[0]?.destroy(); reactRenderer.destroy(); }, }; }, };