SPB Git
1.8 KB · 27 lines tsx
1"use client";2import Mention from "@tiptap/extension-mention";3import { FileText, Database, FlaskConical, Plus } from "lucide-react";4import { SuggestionList, makeRenderer, type Item } from "./suggestion-ui";56async function searchPages(q: string): Promise<Item[]> {7  const r = await fetch(`/api/search?mode=quick&q=${encodeURIComponent(q)}&limit=8`).then((x) => x.json()).catch(() => []);8  const items: Item[] = (Array.isArray(r) ? r : []).map((p: any) => ({ id: p.id, title: p.title, hint: p.section, icon: p.icon ? <span>{p.icon}</span> : p.type === "database" ? <Database /> : p.type === "research" ? <FlaskConical /> : <FileText />, data: { id: p.id, label: p.title } }));9  if (q.trim()) items.push({ id: "__new", title: `Create page “${q.trim()}”`, icon: <Plus />, data: { create: q.trim() } });10  return items;11}12export const PageMention = Mention.configure({13  HTMLAttributes: { class: "mention" },14  renderHTML({ options, node }) { return ["a", { class: "mention", href: `/p/${node.attrs.id}`, "data-type": "mention", "data-id": node.attrs.id, "data-label": node.attrs.label }, `${options.suggestion.char ?? "@"}${node.attrs.label ?? node.attrs.id}`]; },15  renderText({ node }) { return `@${node.attrs.label ?? node.attrs.id}`; },16  suggestion: {17    char: "@", allowSpaces: true,18    items: ({ query }) => searchPages(query),19    command: async ({ editor, range, props }: any) => {20      let { id, label, create } = props.data ?? props;21      if (create) { const p = await fetch("/api/pages", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ title: create }) }).then((x) => x.json()); id = p.id; label = p.title; }22      editor.chain().focus().insertContentAt(range, [{ type: "mention", attrs: { id, label } }, { type: "text", text: " " }]).run();23    },24    render: makeRenderer(SuggestionList, { empty: "No page found" }),25  },26});27
Files · main
Menu

Theme