chatbot-template/lib/editor/suggestions.tsx

97 lines
2.2 KiB
TypeScript
Raw Normal View History

import type { Node } from "prosemirror-model";
import { Plugin, PluginKey } from "prosemirror-state";
import { DecorationSet } from "prosemirror-view";
import type { Suggestion } from "@/lib/db/schema";
2024-10-30 16:01:24 +05:30
export interface UISuggestion extends Suggestion {
selectionStart: number;
selectionEnd: number;
}
type Position = {
2024-10-30 16:01:24 +05:30
start: number;
end: number;
};
2024-10-30 16:01:24 +05:30
function findPositionsInDoc(doc: Node, searchText: string): Position | null {
let positions: { start: number; end: number } | null = null;
doc.nodesBetween(0, doc.content.size, (node, pos) => {
if (node.isText && node.text) {
const index = node.text.indexOf(searchText);
if (index !== -1) {
positions = {
start: pos + index,
end: pos + index + searchText.length,
};
return false;
}
}
return true;
});
return positions;
}
export function projectWithPositions(
2024-10-30 16:01:24 +05:30
doc: Node,
suggestions: Suggestion[]
): UISuggestion[] {
2024-10-30 16:01:24 +05:30
return suggestions.map((suggestion) => {
const positions = findPositionsInDoc(doc, suggestion.originalText);
if (!positions) {
return {
...suggestion,
selectionStart: 0,
selectionEnd: 0,
};
}
return {
...suggestion,
selectionStart: positions.start,
selectionEnd: positions.end,
};
});
}
export const suggestionsPluginKey = new PluginKey("suggestions");
2024-10-30 16:01:24 +05:30
export const suggestionsPlugin = new Plugin({
key: suggestionsPluginKey,
state: {
init() {
return { decorations: DecorationSet.empty, selected: null };
},
apply(tr, state) {
const newDecorations = tr.getMeta(suggestionsPluginKey);
if (newDecorations) {
return newDecorations;
}
2024-10-30 16:01:24 +05:30
return {
decorations: state.decorations.map(tr.mapping, tr.doc),
selected: state.selected,
};
},
},
props: {
decorations(state) {
return this.getState(state)?.decorations ?? DecorationSet.empty;
},
handleDOMEvents: {
mousedown(_view, event) {
const target = event.target as HTMLElement;
if (target.closest(".suggestion-highlight")) {
event.preventDefault();
return true;
}
return false;
},
},
2024-10-30 16:01:24 +05:30
},
});