@powerduck/md-editor 0.10.7 → 0.10.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -244,6 +244,142 @@ npm test # run all tests (vitest + jsdom)
244
244
  npm run build # vite build + d.ts generation
245
245
  ```
246
246
 
247
+ ## React Demo
248
+ ```jsx
249
+ import { useRef, useCallback } from "react";
250
+ import { type MentionItem, type DocItem } from "@powerduck/md-editor";
251
+ import {
252
+ MarkdownEditorReact,
253
+ type MarkdownEditorHandle,
254
+ } from "@powerduck/md-editor/react";
255
+ import "@powerduck/md-editor/dist/style.css";
256
+
257
+ const USERS: MentionItem[] = [
258
+ {
259
+ id: "1",
260
+ label: "Alice",
261
+ avatar: "https://i.pravatar.cc/40?u=alice",
262
+ description: "alice@example.com",
263
+ },
264
+ {
265
+ id: "2",
266
+ label: "Bob",
267
+ avatar: "https://i.pravatar.cc/40?u=bob",
268
+ description: "bob@example.com",
269
+ },
270
+ { id: "3", label: "Charlie", description: "charlie@example.com" },
271
+ ];
272
+
273
+ const DOCS: DocItem[] = [
274
+ {
275
+ id: "d1",
276
+ title: "Getting Started",
277
+ url: "https://docs.example.com/start",
278
+ description: "Quick start guide",
279
+ thumbnail: "https://picsum.photos/seed/start/96/72",
280
+ },
281
+ {
282
+ id: "d2",
283
+ title: "API Reference",
284
+ url: "https://docs.example.com/api",
285
+ description: "Full API documentation",
286
+ },
287
+ {
288
+ id: "d3",
289
+ title: "Changelog",
290
+ url: "https://docs.example.com/changelog",
291
+ description: "Release notes",
292
+ },
293
+ ];
294
+
295
+ function App() {
296
+ const editorRef = useRef<MarkdownEditorHandle>(null);
297
+
298
+ const handleSave = useCallback(() => {
299
+ const markdown = editorRef.current?.getValue() ?? "";
300
+ console.log("Saving:", markdown);
301
+ }, []);
302
+
303
+ // Stable callbacks so the editor does not recreate on every render
304
+ const searchUsers = useCallback(async (query: string) => {
305
+ return USERS.filter((u) =>
306
+ u.label.toLowerCase().includes(query.toLowerCase()),
307
+ );
308
+ }, []);
309
+
310
+ const searchDocs = useCallback(async (query: string) => {
311
+ return DOCS.filter((d) =>
312
+ d.title.toLowerCase().includes(query.toLowerCase()),
313
+ );
314
+ }, []);
315
+
316
+ const fetchDocMeta = useCallback(async (url: string) => {
317
+ // In production, route through your backend to avoid CORS:
318
+ // const res = await fetch(`/api/og?url=${encodeURIComponent(url)}`);
319
+ // return res.json();
320
+ // For demo: return whatever metadata the item already has
321
+ const doc = DOCS.find((d) => d.url === url);
322
+ return {
323
+ title: doc?.title ?? new URL(url).hostname,
324
+ description: doc?.description,
325
+ thumbnail: doc?.thumbnail,
326
+ url,
327
+ };
328
+ }, []);
329
+
330
+ return (
331
+ <div style={{ padding: 24, maxWidth: 1100, margin: "0 auto" }}>
332
+ <div style={{ marginBottom: 12, display: "flex", gap: 8 }}>
333
+ <button onClick={handleSave}>Save</button>
334
+ <button onClick={() => editorRef.current?.setTheme("dark")}>
335
+ Dark
336
+ </button>
337
+ <button onClick={() => editorRef.current?.setTheme("light")}>
338
+ Light
339
+ </button>
340
+ </div>
341
+
342
+ <MarkdownEditorReact
343
+ ref={editorRef}
344
+ defaultValue={[
345
+ "# Welcome",
346
+ "",
347
+ "Type **@** to mention a user, or **/** to insert a document link.",
348
+ "",
349
+ "## Task list",
350
+ "- [ ] This is checked (green)",
351
+ "- [x] This is unchecked",
352
+ "- [ x] Spaces inside brackets also work",
353
+ "",
354
+ "## Table",
355
+ "| Name | Role |",
356
+ "|------|------|",
357
+ "| Alice | Admin |",
358
+ "| Bob | Editor |",
359
+ ].join("\n")}
360
+ mode="complex"
361
+ theme="light"
362
+ onChange={(v) => console.log("auto-save:", v.length)}
363
+ mention={{
364
+ onMentionSearch: searchUsers,
365
+ minChars: 0,
366
+ maxItems: 8,
367
+ }}
368
+ docLink={{
369
+ onDocSearch: searchDocs,
370
+ onFetchDocMeta: fetchDocMeta,
371
+ insertStyle: "auto",
372
+ minChars: 0,
373
+ maxItems: 8,
374
+ }}
375
+ />
376
+ </div>
377
+ );
378
+ }
379
+
380
+ export default App;
381
+ ```
382
+
247
383
  ## Changelog
248
384
 
249
385
  ### v0.8.4
package/dist/Editor.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { EditorView } from '@codemirror/view';
2
2
  import { type MentionOptions } from './plugins/mention.js';
3
3
  import { type DocLinkOptions } from './plugins/doclink.js';
4
+ export type EditorTheme = 'light' | 'dark';
4
5
  export interface CodeEditorOptions {
5
6
  value: string;
6
7
  onChange?: (value: string) => void;
@@ -13,16 +14,20 @@ export interface CodeEditorOptions {
13
14
  mention?: MentionOptions;
14
15
  /** Document link inserter configuration */
15
16
  docLink?: DocLinkOptions;
17
+ /** Initial color theme for syntax highlighting. Defaults to 'light'. */
18
+ theme?: EditorTheme;
16
19
  }
17
20
  export declare class CodeEditor {
18
21
  readonly view: EditorView;
19
22
  private lineNumbersCompartment;
20
23
  private customKeymapCompartment;
24
+ private highlightCompartment;
21
25
  private onImageFile?;
22
26
  private mention;
23
27
  private docLink;
24
28
  private blurTimeout;
25
29
  private destroyed;
30
+ private theme;
26
31
  constructor(container: HTMLElement, options: CodeEditorOptions);
27
32
  private handlePaste;
28
33
  private handleDrop;
@@ -37,6 +42,12 @@ export declare class CodeEditor {
37
42
  getValue(): string;
38
43
  setValue(value: string): void;
39
44
  setLineNumbers(enabled: boolean): void;
45
+ /**
46
+ * Switch the syntax highlighting theme. Reconfigures only the highlight
47
+ * compartment — the document and view state are preserved, so this is
48
+ * effectively free on large documents.
49
+ */
50
+ setTheme(theme: EditorTheme): void;
40
51
  insertAtCursor(text: string): void;
41
52
  /**
42
53
  * Wrap the current selection with prefix and suffix. If nothing is