Files
paseo/plugin-examples/markdown-panel/client/web.ts
T
Mohamed Boudra 970b41cb24 docs(plugins): file panel examples and author reference
Adds the pdf-panel and markdown-panel examples, the File panels section of the plugin
reference, and an e2e spec that installs both examples, opens a PDF in the browser
viewer, and edits Markdown through Paseo's document across the switcher, Source,
autosave and conflicts.
2026-10-19 13:40:04 +02:00

72 lines
2.6 KiB
TypeScript

import type { PluginFilePanelProps, PluginTextDocument } from "@getpaseo/plugin/client";
import type { EditorView, EditorViewConfig } from "@codemirror/view";
import { Platform } from "react-native";
/** DOM code stays here; native never evaluates the editor dependencies. */
export function mountEditor(
parent: unknown,
document: PluginTextDocument,
theme: PluginFilePanelProps["theme"],
onChange: (text: string) => void,
) {
if (Platform.OS !== "web" || !parent) return null;
const { EditorState } = require("@codemirror/state") as typeof import("@codemirror/state");
const { EditorView, keymap, lineNumbers } =
require("@codemirror/view") as typeof import("@codemirror/view");
const { defaultKeymap, history, historyKeymap } =
require("@codemirror/commands") as typeof import("@codemirror/commands");
const { markdown } =
require("@codemirror/lang-markdown") as typeof import("@codemirror/lang-markdown");
const { syntaxHighlighting, defaultHighlightStyle } =
require("@codemirror/language") as typeof import("@codemirror/language");
let applyingHostText = false;
const view: EditorView = new EditorView({
parent: parent as EditorViewConfig["parent"],
state: EditorState.create({
doc: document.text,
extensions: [
markdown(),
syntaxHighlighting(defaultHighlightStyle),
lineNumbers(),
history(),
keymap.of([...defaultKeymap, ...historyKeymap]),
EditorView.lineWrapping,
EditorState.readOnly.of(!document.editable),
EditorView.editable.of(document.editable),
EditorView.contentAttributes.of({ "aria-label": "Rich Markdown editor" }),
EditorView.theme({
"&": {
height: "100%",
color: theme.colors.foreground,
backgroundColor: theme.colors.surface0,
},
".cm-scroller": { overflow: "auto" },
".cm-content": { padding: "16px", caretColor: theme.colors.foreground },
".cm-gutters": {
backgroundColor: theme.colors.surface0,
color: theme.colors.foregroundMuted,
border: "none",
},
}),
EditorView.updateListener.of((update) => {
if (update.docChanged && !applyingHostText) onChange(update.state.doc.toString());
}),
],
}),
});
return {
setText(text: string) {
if (view.state.doc.toString() === text) return;
applyingHostText = true;
try {
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: text } });
} finally {
applyingHostText = false;
}
},
destroy() {
view.destroy();
},
};
}