|
@@ -15,8 +15,9 @@
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
import type { Node as ProsemirrorNode } from "prosemirror-model"
|
|
import type { Node as ProsemirrorNode } from "prosemirror-model"
|
|
|
-import { Plugin, PluginKey } from "prosemirror-state"
|
|
|
|
|
|
|
+import { Plugin, PluginKey, TextSelection } from "prosemirror-state"
|
|
|
import { Decoration, DecorationSet } from "prosemirror-view"
|
|
import { Decoration, DecorationSet } from "prosemirror-view"
|
|
|
|
|
+import { renderKatexSync } from "@/lib/katex"
|
|
|
import { createLogger } from "@/lib/logger"
|
|
import { createLogger } from "@/lib/logger"
|
|
|
import {
|
|
import {
|
|
|
type DecorationRange,
|
|
type DecorationRange,
|
|
@@ -84,8 +85,8 @@ function buildDecorationsFromCache(
|
|
|
doc: ProsemirrorNode,
|
|
doc: ProsemirrorNode,
|
|
|
contentCaches: Map<string, BlockTokenCache>,
|
|
contentCaches: Map<string, BlockTokenCache>,
|
|
|
isFocused: boolean,
|
|
isFocused: boolean,
|
|
|
- _cursorFrom: number,
|
|
|
|
|
- _cursorTo: number,
|
|
|
|
|
|
|
+ cursorFrom: number,
|
|
|
|
|
+ cursorTo: number,
|
|
|
markerMode: MarkerVisibilityMode,
|
|
markerMode: MarkerVisibilityMode,
|
|
|
): DecorationSet {
|
|
): DecorationSet {
|
|
|
const decorationSpecs: Decoration[] = []
|
|
const decorationSpecs: Decoration[] = []
|
|
@@ -156,8 +157,8 @@ function buildDecorationsFromCache(
|
|
|
blockStart,
|
|
blockStart,
|
|
|
decorationSpecs,
|
|
decorationSpecs,
|
|
|
isFocused,
|
|
isFocused,
|
|
|
- _cursorFrom,
|
|
|
|
|
- _cursorTo,
|
|
|
|
|
|
|
+ cursorFrom,
|
|
|
|
|
+ cursorTo,
|
|
|
markerMode,
|
|
markerMode,
|
|
|
)
|
|
)
|
|
|
} else {
|
|
} else {
|
|
@@ -174,8 +175,8 @@ function buildDecorationsFromCache(
|
|
|
blockStart,
|
|
blockStart,
|
|
|
decorationSpecs,
|
|
decorationSpecs,
|
|
|
isFocused,
|
|
isFocused,
|
|
|
- _cursorFrom,
|
|
|
|
|
- _cursorTo,
|
|
|
|
|
|
|
+ cursorFrom,
|
|
|
|
|
+ cursorTo,
|
|
|
markerMode,
|
|
markerMode,
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
@@ -187,8 +188,8 @@ function buildDecorationsFromCache(
|
|
|
blockStart,
|
|
blockStart,
|
|
|
decorationSpecs,
|
|
decorationSpecs,
|
|
|
isFocused,
|
|
isFocused,
|
|
|
- _cursorFrom,
|
|
|
|
|
- _cursorTo,
|
|
|
|
|
|
|
+ cursorFrom,
|
|
|
|
|
+ cursorTo,
|
|
|
markerMode,
|
|
markerMode,
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
@@ -198,8 +199,8 @@ function buildDecorationsFromCache(
|
|
|
blockStart,
|
|
blockStart,
|
|
|
decorationSpecs,
|
|
decorationSpecs,
|
|
|
isFocused,
|
|
isFocused,
|
|
|
- _cursorFrom,
|
|
|
|
|
- _cursorTo,
|
|
|
|
|
|
|
+ cursorFrom,
|
|
|
|
|
+ cursorTo,
|
|
|
markerMode,
|
|
markerMode,
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
@@ -227,8 +228,8 @@ function buildTokenDecorations(
|
|
|
offset: number,
|
|
offset: number,
|
|
|
decorationSpecs: Decoration[],
|
|
decorationSpecs: Decoration[],
|
|
|
isFocused: boolean,
|
|
isFocused: boolean,
|
|
|
- _cursorFrom: number,
|
|
|
|
|
- _cursorTo: number,
|
|
|
|
|
|
|
+ cursorFrom: number,
|
|
|
|
|
+ cursorTo: number,
|
|
|
markerMode: MarkerVisibilityMode,
|
|
markerMode: MarkerVisibilityMode,
|
|
|
) {
|
|
) {
|
|
|
const ranges = token.decorationRanges
|
|
const ranges = token.decorationRanges
|
|
@@ -236,6 +237,48 @@ function buildTokenDecorations(
|
|
|
|
|
|
|
|
const sorted = [...ranges].sort((a, b) => a.from - b.from)
|
|
const sorted = [...ranges].sort((a, b) => a.from - b.from)
|
|
|
|
|
|
|
|
|
|
+ // ── Inline math: show KaTeX when blurred; show source when focused ──
|
|
|
|
|
+ if (token.type === "inline-math") {
|
|
|
|
|
+ if (!isFocused) {
|
|
|
|
|
+ const tokenStart = offset + sorted[0].from
|
|
|
|
|
+ // Hide all ranges
|
|
|
|
|
+ const source = token.wrapperAttrs?.["data-math"] ?? ""
|
|
|
|
|
+ for (const range of sorted) {
|
|
|
|
|
+ decorationSpecs.push(
|
|
|
|
|
+ Decoration.inline(offset + range.from, offset + range.to, {
|
|
|
|
|
+ class: "md-math-hidden",
|
|
|
|
|
+ }),
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ // Add clickable KaTeX widget
|
|
|
|
|
+ if (source) {
|
|
|
|
|
+ decorationSpecs.push(
|
|
|
|
|
+ Decoration.widget(
|
|
|
|
|
+ tokenStart,
|
|
|
|
|
+ (view) => {
|
|
|
|
|
+ const widgetSpan = document.createElement("span")
|
|
|
|
|
+ widgetSpan.className = "md-math-rendered"
|
|
|
|
|
+ renderKatexSync(source, widgetSpan, false)
|
|
|
|
|
+ widgetSpan.addEventListener("mousedown", (e) => {
|
|
|
|
|
+ e.preventDefault()
|
|
|
|
|
+ e.stopPropagation()
|
|
|
|
|
+ view.dispatch(
|
|
|
|
|
+ view.state.tr.setSelection(
|
|
|
|
|
+ TextSelection.near(view.state.doc.resolve(tokenStart)),
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+ view.focus()
|
|
|
|
|
+ })
|
|
|
|
|
+ return widgetSpan
|
|
|
|
|
+ },
|
|
|
|
|
+ { side: 1 },
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
for (const range of sorted) {
|
|
for (const range of sorted) {
|
|
|
const rangeFrom = offset + range.from
|
|
const rangeFrom = offset + range.from
|
|
|
const rangeTo = offset + range.to
|
|
const rangeTo = offset + range.to
|