mirror of
https://git.srhtnin.garden/sarah/SaraView.git
synced 2026-10-14 18:38:11 +02:00
Mermaid diagrams (flowcharts) in notes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b8609363b7
commit
292614c696
5 files changed
+244
-2
No files matched your search
@@ -3,10 +3,14 @@
|
||||
// > and list markers) hidden. Wherever the cursor is, the syntax comes back so
|
||||
// it can be edited: a heading's #s on its line, a bold's ** inside it.
|
||||
// The colours are the same roles as .MarkdownBody (Styles/Theme.css).
|
||||
// ```mermaid blocks show as their diagram until the cursor goes into them.
|
||||
|
||||
import { syntaxTree } from "@codemirror/language";
|
||||
import { StateField } from "@codemirror/state";
|
||||
import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view";
|
||||
|
||||
import { RenderDiagram } from "./Mermaid.js";
|
||||
|
||||
class BulletWidget extends WidgetType {
|
||||
eq() {
|
||||
return true;
|
||||
@@ -63,6 +67,42 @@ class CheckboxWidget extends WidgetType {
|
||||
}
|
||||
}
|
||||
|
||||
// A ```mermaid block drawn as its diagram; clicking it puts the cursor in
|
||||
// the block, which brings the code back.
|
||||
class DiagramWidget extends WidgetType {
|
||||
constructor(Source) {
|
||||
super();
|
||||
this.Source = Source;
|
||||
}
|
||||
|
||||
eq(Other) {
|
||||
return this.Source === Other.Source;
|
||||
}
|
||||
|
||||
toDOM(View) {
|
||||
const Element = document.createElement("div");
|
||||
Element.className = "MermaidDiagram cm-LiveDiagram";
|
||||
Element.dataset.source = this.Source;
|
||||
Element.addEventListener("mousedown", (Event) => {
|
||||
Event.preventDefault();
|
||||
const Line = View.state.doc.lineAt(View.posAtDOM(Element));
|
||||
const Inside = Math.min(Line.to + 1, View.state.doc.length);
|
||||
View.dispatch({ selection: { anchor: Inside } });
|
||||
View.focus();
|
||||
});
|
||||
RenderDiagram(Element);
|
||||
return Element;
|
||||
}
|
||||
|
||||
get estimatedHeight() {
|
||||
return 200;
|
||||
}
|
||||
|
||||
ignoreEvent() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const Hidden = Decoration.replace({});
|
||||
const Bullet = Decoration.replace({ widget: new BulletWidget() });
|
||||
const Rule = Decoration.replace({ widget: new RuleWidget() });
|
||||
@@ -216,7 +256,45 @@ function BuildDecorations(View) {
|
||||
return Decoration.set(Ranges, true);
|
||||
}
|
||||
|
||||
// Diagrams replace whole lines, which CodeMirror only allows from a state
|
||||
// field, not from the plugin above. Without focus info here, a block shows
|
||||
// as code while the cursor (or selection) is in it, even when unfocused.
|
||||
function BuildDiagrams(State) {
|
||||
const Doc = State.doc;
|
||||
const Ranges = [];
|
||||
|
||||
syntaxTree(State).iterate({
|
||||
enter(Node) {
|
||||
if (Node.name !== "FencedCode") return;
|
||||
const Info = Node.node.getChild("CodeInfo");
|
||||
if (!Info || Doc.sliceString(Info.from, Info.to).trim().toLowerCase() !== "mermaid") return false;
|
||||
|
||||
const From = Doc.lineAt(Node.from).from;
|
||||
const To = Doc.lineAt(Node.to).to;
|
||||
if (State.selection.ranges.some((Range) => Range.from <= To && Range.to >= From)) return false;
|
||||
|
||||
const Code = Node.node.getChild("CodeText");
|
||||
const Source = Code ? Doc.sliceString(Code.from, Code.to) : "";
|
||||
Ranges.push(Decoration.replace({ widget: new DiagramWidget(Source), block: true }).range(From, To));
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
return Decoration.set(Ranges, true);
|
||||
}
|
||||
|
||||
const Diagrams = StateField.define({
|
||||
create: BuildDiagrams,
|
||||
update(Current, Transaction) {
|
||||
const TreeChanged = syntaxTree(Transaction.state) !== syntaxTree(Transaction.startState);
|
||||
if (Transaction.docChanged || Transaction.selection || TreeChanged) return BuildDiagrams(Transaction.state);
|
||||
return Current;
|
||||
},
|
||||
provide: (Field) => EditorView.decorations.from(Field),
|
||||
});
|
||||
|
||||
export const LivePreview = [
|
||||
Diagrams,
|
||||
ViewPlugin.fromClass(
|
||||
class {
|
||||
constructor(View) {
|
||||
|
||||
Reference in new issue
Block a user