mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
changes
This commit is contained in:
1 parent
78fff9b6f4
commit
7cc36e3e40
37 files changed
+55361
-103
No files matched your search
@@ -0,0 +1,133 @@
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
||||
import type { Node as PMNode } from "@tiptap/pm/model";
|
||||
|
||||
export interface ProofIssue {
|
||||
from: number;
|
||||
to: number;
|
||||
kind: "spelling" | "grammar";
|
||||
category: string;
|
||||
message: string;
|
||||
suggestions: string[];
|
||||
word: string;
|
||||
}
|
||||
|
||||
export type ProofSeverity = "error" | "warn" | "suggest";
|
||||
|
||||
const WARN_CATEGORIES = new Set(["Redundancy", "Repetition", "Formatting", "Regionalism"]);
|
||||
const SUGGEST_CATEGORIES = new Set(["Style", "Readability", "Enhancement", "Miscellaneous"]);
|
||||
|
||||
export function severityFor(category: string): ProofSeverity {
|
||||
if (WARN_CATEGORIES.has(category)) return "warn";
|
||||
if (SUGGEST_CATEGORIES.has(category)) return "suggest";
|
||||
return "error";
|
||||
}
|
||||
|
||||
export interface ProofCoords {
|
||||
left: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
interface ProofingState {
|
||||
issues: ProofIssue[];
|
||||
decorations: DecorationSet;
|
||||
}
|
||||
|
||||
export const proofingKey = new PluginKey<ProofingState>("proofing");
|
||||
|
||||
function buildDecorations(doc: PMNode, issues: ProofIssue[]): DecorationSet {
|
||||
const size = doc.content.size;
|
||||
const decos = issues
|
||||
.filter((i) => i.from < i.to && i.to <= size)
|
||||
.map((i) => Decoration.inline(i.from, i.to, { class: `proof-mark sev-${severityFor(i.category)}` }));
|
||||
return DecorationSet.create(doc, decos);
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
proofing: {
|
||||
setProofIssues: (issues: ProofIssue[]) => ReturnType;
|
||||
clearProofIssues: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProofingStorage {
|
||||
onClickIssue: ((issue: ProofIssue, coords: ProofCoords) => void) | null;
|
||||
}
|
||||
|
||||
export const Proofing = Extension.create<unknown, ProofingStorage>({
|
||||
name: "proofing",
|
||||
|
||||
addStorage() {
|
||||
return { onClickIssue: null };
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
const storage = this.storage;
|
||||
return [
|
||||
new Plugin<ProofingState>({
|
||||
key: proofingKey,
|
||||
state: {
|
||||
init: () => ({ issues: [], decorations: DecorationSet.empty }),
|
||||
apply(tr, value, _old, newState) {
|
||||
const meta = tr.getMeta(proofingKey) as { issues: ProofIssue[] } | { clear: true } | undefined;
|
||||
if (meta && "issues" in meta) {
|
||||
return { issues: meta.issues, decorations: buildDecorations(newState.doc, meta.issues) };
|
||||
}
|
||||
if (meta && "clear" in meta) {
|
||||
return { issues: [], decorations: DecorationSet.empty };
|
||||
}
|
||||
if (tr.docChanged && value.issues.length) {
|
||||
const issues = value.issues
|
||||
.map((i) => ({ ...i, from: tr.mapping.map(i.from), to: tr.mapping.map(i.to) }))
|
||||
.filter((i) => i.to > i.from);
|
||||
return { issues, decorations: value.decorations.map(tr.mapping, tr.doc) };
|
||||
}
|
||||
return value;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
decorations(state) {
|
||||
return proofingKey.getState(state)?.decorations ?? DecorationSet.empty;
|
||||
},
|
||||
handleClick(view, pos) {
|
||||
const s = proofingKey.getState(view.state);
|
||||
if (!s || !s.issues.length) return false;
|
||||
const hits = s.issues.filter((i) => pos >= i.from && pos <= i.to);
|
||||
if (!hits.length) return false;
|
||||
const issue = hits.find((i) => i.kind === "spelling") ?? hits[0];
|
||||
if (!storage.onClickIssue) return false;
|
||||
const start = view.coordsAtPos(issue.from);
|
||||
const end = view.coordsAtPos(issue.to);
|
||||
storage.onClickIssue(issue, {
|
||||
left: (start.left + end.left) / 2,
|
||||
top: start.top,
|
||||
bottom: end.bottom,
|
||||
});
|
||||
return false;
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setProofIssues:
|
||||
(issues) =>
|
||||
({ tr, dispatch }) => {
|
||||
if (dispatch) dispatch(tr.setMeta(proofingKey, { issues }));
|
||||
return true;
|
||||
},
|
||||
clearProofIssues:
|
||||
() =>
|
||||
({ tr, dispatch }) => {
|
||||
if (dispatch) dispatch(tr.setMeta(proofingKey, { clear: true }));
|
||||
return true;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user