Files
outline/shared/editor/lib/Lightbox.ts
T
Tom Moor ac06a06a66 feat: Diagrams/Draw.io integration (#10707)
* wip

* wip

* tsc

* lint

* Detect imported Draw.io

* Add empty diagram placeholder

* fix: Do not close editor on save
fix: Account for nodes moving / multiplayer

* fix: Reduce image menu for diagrams

* Add custom server settings page

* refactor

* sp

* Move edit button
2025-11-29 21:02:08 +01:00

86 lines
2.2 KiB
TypeScript

import { Node } from "prosemirror-model";
import { isCode } from "./isCode";
import { EditorView } from "prosemirror-view";
import { sanitizeUrl } from "@shared/utils/urls";
export abstract class LightboxImage {
public pos: number;
public src: string;
public alt: string;
public source: string;
protected element: Element;
constructor() {}
public abstract getElement(): Element | null | undefined;
}
class LightboxRegularImage extends LightboxImage {
constructor(view: EditorView, pos: number) {
super();
this.pos = pos;
const node = view.state.doc.nodeAt(pos);
this.src = sanitizeUrl(node?.attrs.src) ?? "";
this.alt = node?.attrs.alt ?? "";
this.source = node?.attrs.source;
this.element = view.nodeDOM(pos) as HTMLSpanElement;
}
getElement() {
return this.element.querySelector("img");
}
}
class LightboxMermaidImage extends LightboxImage {
constructor(view: EditorView, pos: number) {
super();
this.element = view.nodeDOM(pos) as HTMLDivElement;
this.pos = pos;
this.src = this.svgToSrc(this.extractSvg());
this.alt = "";
}
private svgToSrc(svg: string): string {
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}
private extractSvg(): string {
const mermaidWrapper = this.element.nextElementSibling;
if (!mermaidWrapper) {
return "";
}
const svg = mermaidWrapper.firstElementChild;
if (!svg || !(svg instanceof SVGElement)) {
return "";
}
return svg.outerHTML;
}
getElement() {
return this.element.nextElementSibling?.firstElementChild;
}
}
export class LightboxImageFactory {
static createLightboxImage(view: EditorView, pos: number): LightboxImage {
const node = view.state.doc.nodeAt(pos)!;
if (isImage(node)) {
return new LightboxRegularImage(view, pos);
}
if (isMermaid(node)) {
return new LightboxMermaidImage(view, pos);
}
throw new Error("Unsupported node type for LightboxImage");
}
}
const isImage = (node: Node) => node.type.name === "image";
const isMermaid = (node: Node) =>
isCode(node) && node.attrs.language === "mermaidjs";
export const isLightboxNode = (node: Node) => isImage(node) || isMermaid(node);