| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { Extension } from "@tiptap/core"; |
| import { Plugin, PluginKey } from "@tiptap/pm/state"; |
| import { Decoration, DecorationSet } from "@tiptap/pm/view"; |
|
|
| export type AgentHighlightPhase = "pending" | "rewriting"; |
|
|
| interface AgentHighlightState { |
| from: number | null; |
| to: number | null; |
| phase: AgentHighlightPhase | null; |
| } |
|
|
| interface AgentHighlightMeta { |
| from?: number | null; |
| to?: number | null; |
| phase?: AgentHighlightPhase | null; |
| clear?: boolean; |
| } |
|
|
| export const agentHighlightPluginKey = |
| new PluginKey<AgentHighlightState>("agent-highlight"); |
|
|
| declare module "@tiptap/core" { |
| |
| interface Commands<ReturnType> { |
| agentHighlight: { |
| |
| |
| |
| |
| |
| |
| |
| setAgentHighlight: (args: { |
| from?: number; |
| to?: number; |
| phase?: AgentHighlightPhase; |
| }) => ReturnType; |
| |
| clearAgentHighlight: () => ReturnType; |
| }; |
| } |
| } |
|
|
| export const AgentHighlight = Extension.create({ |
| name: "agentHighlight", |
|
|
| addProseMirrorPlugins() { |
| return [ |
| new Plugin<AgentHighlightState>({ |
| key: agentHighlightPluginKey, |
| state: { |
| init: (): AgentHighlightState => ({ |
| from: null, |
| to: null, |
| phase: null, |
| }), |
| apply(tr, prev): AgentHighlightState { |
| const meta = tr.getMeta(agentHighlightPluginKey) as |
| | AgentHighlightMeta |
| | undefined; |
| if (meta) { |
| if (meta.clear) { |
| return { from: null, to: null, phase: null }; |
| } |
| return { |
| from: meta.from !== undefined ? meta.from : prev.from, |
| to: meta.to !== undefined ? meta.to : prev.to, |
| phase: meta.phase !== undefined ? meta.phase : prev.phase, |
| }; |
| } |
|
|
| if (prev.from !== null && prev.to !== null) { |
| const from = tr.mapping.map(prev.from, 1); |
| const to = tr.mapping.map(prev.to, -1); |
| if (to > from) { |
| if (from === prev.from && to === prev.to) return prev; |
| return { ...prev, from, to }; |
| } |
| return { from: null, to: null, phase: null }; |
| } |
|
|
| return prev; |
| }, |
| }, |
| props: { |
| decorations(state) { |
| const s = agentHighlightPluginKey.getState(state); |
| if (!s || s.from === null || s.to === null || !s.phase) { |
| return null; |
| } |
|
|
| let $pos; |
| try { |
| $pos = state.doc.resolve(s.from); |
| } catch { |
| return null; |
| } |
|
|
| const depth = $pos.depth; |
| if (depth <= 0) return null; |
|
|
| const nodeStart = $pos.before(depth); |
| const nodeEnd = $pos.after(depth); |
| const cls = `demo-agent-${s.phase}`; |
|
|
| return DecorationSet.create(state.doc, [ |
| Decoration.node(nodeStart, nodeEnd, { class: cls }), |
| ]); |
| }, |
| }, |
| }), |
| ]; |
| }, |
|
|
| addCommands() { |
| return { |
| setAgentHighlight: |
| (args) => |
| ({ tr, dispatch }) => { |
| if (dispatch) { |
| tr.setMeta(agentHighlightPluginKey, args); |
| } |
| return true; |
| }, |
| clearAgentHighlight: |
| () => |
| ({ tr, dispatch }) => { |
| if (dispatch) { |
| tr.setMeta(agentHighlightPluginKey, { clear: true }); |
| } |
| return true; |
| }, |
| }; |
| }, |
| }); |
|
|