Skip to content
File

Blob: src/client/components/editor/extensions/collaboration-caret.ts

typescript51 lines
1import { CollaborationCaret } from "@tiptap/extension-collaboration-caret";
2import type { Awareness } from "y-protocols/awareness";
3import { awarenessColor } from "@/client/hooks/use-sync";
4import type { ResolveIdentity } from "@/client/lib/presence-identity";
5 
6function extractPresenceKeys(user: Record<string, unknown>): { userId: string | null; clientId: number } {
7 const clientId = typeof user.clientId === "number" ? user.clientId : 0;
8 const userId = typeof user.userId === "string" ? user.userId : null;
9 return { userId, clientId };
10}
11 
12interface CollaborationCaretOptions {
13 provider: { awareness: Awareness };
14 user: { userId: string | null };
15 resolveIdentity: ResolveIdentity;
16}
17 
18export function createCollaborationCaret({ provider, user, resolveIdentity }: CollaborationCaretOptions) {
19 return CollaborationCaret.configure({
20 provider,
21 user: { userId: user.userId },
22 render: (remote) => {
23 const { userId, clientId } = extractPresenceKeys(remote);
24 const color = awarenessColor(userId, clientId);
25 const { name } = resolveIdentity(userId, clientId);
26 
27 const cursor = document.createElement("span");
28 cursor.classList.add("collaboration-carets__caret");
29 cursor.setAttribute("style", `border-color: ${color}`);
30 
31 const label = document.createElement("div");
32 label.classList.add("collaboration-carets__label");
33 label.setAttribute("style", `background-color: ${color}`);
34 label.appendChild(document.createTextNode(name));
35 
36 cursor.appendChild(label);
37 return cursor;
38 },
39 selectionRender: (remote) => {
40 const { userId, clientId } = extractPresenceKeys(remote);
41 const color = awarenessColor(userId, clientId);
42 return {
43 nodeName: "span",
44 class: "collaboration-carets__selection",
45 style: `background-color: ${color}`,
46 "data-client-id": String(clientId),
47 };
48 },
49 });
50}