supamde 0.0.0-stage → 0.2.0

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1228 -2
  3. package/dist/commands/block.d.ts +20 -0
  4. package/dist/commands/code-indent.d.ts +38 -0
  5. package/dist/commands/history.d.ts +6 -0
  6. package/dist/commands/indent.d.ts +28 -0
  7. package/dist/commands/inline.d.ts +9 -0
  8. package/dist/commands/keymap.d.ts +8 -0
  9. package/dist/commands/link-image.d.ts +13 -0
  10. package/dist/commands/list.d.ts +20 -0
  11. package/dist/commands/prefixes.d.ts +29 -0
  12. package/dist/commands/queries.d.ts +25 -0
  13. package/dist/commands/table.d.ts +7 -0
  14. package/dist/commands/types.d.ts +17 -0
  15. package/dist/editor/extensions.d.ts +14 -0
  16. package/dist/editor/highlight.d.ts +11 -0
  17. package/dist/editor/link-click.d.ts +53 -0
  18. package/dist/editor/link-hover-cursor.d.ts +44 -0
  19. package/dist/editor/math.d.ts +8 -0
  20. package/dist/editor/setup.d.ts +26 -0
  21. package/dist/editor/theme.d.ts +7 -0
  22. package/dist/editor/tokens.d.ts +16 -0
  23. package/dist/editor/value.d.ts +10 -0
  24. package/dist/features/autosave.d.ts +31 -0
  25. package/dist/features/image-upload.d.ts +73 -0
  26. package/dist/features/storage.d.ts +30 -0
  27. package/dist/features/upload-dom.d.ts +20 -0
  28. package/dist/features/upload-placeholder.d.ts +35 -0
  29. package/dist/features/word-count.d.ts +5 -0
  30. package/dist/i18n/de.d.ts +3 -0
  31. package/dist/i18n/en.d.ts +3 -0
  32. package/dist/i18n/facet.d.ts +9 -0
  33. package/dist/i18n/translator.d.ts +17 -0
  34. package/dist/i18n/types.d.ts +82 -0
  35. package/dist/index.d.ts +133 -0
  36. package/dist/livepreview/index.d.ts +22 -0
  37. package/dist/livepreview/plugin.d.ts +3 -0
  38. package/dist/livepreview/ranges.d.ts +62 -0
  39. package/dist/markdown/katex-marked.d.ts +20 -0
  40. package/dist/markdown/parse.d.ts +27 -0
  41. package/dist/markdown/sanitize.d.ts +6 -0
  42. package/dist/options.d.ts +83 -0
  43. package/dist/supamde.css +2 -0
  44. package/dist/supamde.mjs +3617 -0
  45. package/dist/supamde.mjs.map +1 -0
  46. package/dist/ui/actions.d.ts +51 -0
  47. package/dist/ui/fullscreen.d.ts +16 -0
  48. package/dist/ui/icons.d.ts +4 -0
  49. package/dist/ui/inject-styles.d.ts +13 -0
  50. package/dist/ui/preview.d.ts +27 -0
  51. package/dist/ui/shortcut-label.d.ts +24 -0
  52. package/dist/ui/statusbar.d.ts +28 -0
  53. package/dist/ui/toolbar-config.d.ts +36 -0
  54. package/dist/ui/toolbar.d.ts +16 -0
  55. package/dist/ui/update-listener.d.ts +15 -0
  56. package/dist/util/text-format.d.ts +11 -0
  57. package/dist/utils/html.d.ts +2 -0
  58. package/dist/utils/text.d.ts +41 -0
  59. package/dist/version.d.ts +2 -0
  60. package/package.json +96 -4
@@ -0,0 +1,3 @@
1
+ import type { Locale } from './types';
2
+ /** Englisch — Default-Sprache und Rückfall für fehlende Schlüssel. Reine Daten. */
3
+ export declare const en: Locale;
@@ -0,0 +1,9 @@
1
+ import { Facet } from '@codemirror/state';
2
+ import { type Translator } from './translator';
3
+ /**
4
+ * Der Translator im Editor-State. Commands sind reine `(view) => boolean`-
5
+ * Funktionen ohne Zugriff auf die SupaMDE-Instanz; die Facet ist der
6
+ * idiomatische CM6-Weg, ihnen Konfiguration mitzugeben. Ohne Provider (z. B. in
7
+ * Unit-Tests ohne Instanz) gilt Englisch.
8
+ */
9
+ export declare const translatorFacet: Facet<Translator, Translator>;
@@ -0,0 +1,17 @@
1
+ import type { Locale, LocaleTexts, PluralTextKey, SimpleTextKey } from './types';
2
+ /** Liefert die UI-Texte einer Instanz. Einmal pro Instanz erzeugt, danach unveränderlich. */
3
+ export interface Translator {
4
+ /** Effektiver Locale-Code (nach Gültigkeitsprüfung). */
5
+ readonly code: string;
6
+ /** Einfacher Text mit Platzhaltern. */
7
+ t(key: SimpleTextKey, params?: Record<string, string>): string;
8
+ /** Pluralform zu `count`; setzt `{count}` und weitere Platzhalter ein. */
9
+ plural(key: PluralTextKey, count: number, params?: Record<string, string>): string;
10
+ /** Uhrzeit HH:MM im Format des Locale-Codes. */
11
+ formatTime(date: Date): string;
12
+ }
13
+ /**
14
+ * Baut den Translator einer Instanz. Rangfolge pro Schlüssel:
15
+ * `texts` → `locale.texts` → `en.texts`.
16
+ */
17
+ export declare function createTranslator(locale?: Locale, texts?: Partial<LocaleTexts>): Translator;
@@ -0,0 +1,82 @@
1
+ /** Pluralformen nach CLDR; `other` ist Pflicht und Rückfall für fehlende Formen. */
2
+ export interface PluralText {
3
+ zero?: string;
4
+ one?: string;
5
+ two?: string;
6
+ few?: string;
7
+ many?: string;
8
+ other: string;
9
+ }
10
+ /**
11
+ * Alle Texte, die Endnutzer sehen. Flache Schlüssel mit Punkt-Präfix statt
12
+ * verschachtelter Gruppen: `texts` kommt so mit `Partial<LocaleTexts>` aus (kein
13
+ * DeepPartial), und die Toolbar-Schlüssel entsprechen 1:1 den Aktionsnamen aus
14
+ * `BUILTIN_ACTIONS` (`toolbar.${name}`) — keine zweite Namensliste. Dass beide
15
+ * Listen übereinstimmen, prüft `i18n/__tests__/locales.test.ts`.
16
+ */
17
+ export interface LocaleTexts {
18
+ 'toolbar.bold': string;
19
+ 'toolbar.italic': string;
20
+ 'toolbar.strikethrough': string;
21
+ 'toolbar.code': string;
22
+ 'toolbar.heading-smaller': string;
23
+ 'toolbar.heading-bigger': string;
24
+ 'toolbar.heading-1': string;
25
+ 'toolbar.heading-2': string;
26
+ 'toolbar.heading-3': string;
27
+ 'toolbar.heading-4': string;
28
+ 'toolbar.heading-5': string;
29
+ 'toolbar.heading-6': string;
30
+ 'toolbar.quote': string;
31
+ 'toolbar.code-block': string;
32
+ 'toolbar.horizontal-rule': string;
33
+ 'toolbar.clean-block': string;
34
+ 'toolbar.unordered-list': string;
35
+ 'toolbar.ordered-list': string;
36
+ 'toolbar.check-list': string;
37
+ 'toolbar.link': string;
38
+ 'toolbar.image': string;
39
+ 'toolbar.table': string;
40
+ 'toolbar.undo': string;
41
+ 'toolbar.redo': string;
42
+ 'toolbar.side-by-side': string;
43
+ 'toolbar.fullscreen': string;
44
+ 'toolbar.preview-fullscreen': string;
45
+ 'toolbar.editor-mode': string;
46
+ 'toolbar.upload-image': string;
47
+ /** Platzhalter: `{count}`. */
48
+ 'status.lines': PluralText;
49
+ /** Platzhalter: `{count}`. */
50
+ 'status.words': PluralText;
51
+ /** Platzhalter: `{time}`. */
52
+ 'status.autosaved': string;
53
+ /** In das Dokument eingefügter Platzhalter. Platzhalter: `{name}`. */
54
+ 'upload.placeholder': string;
55
+ 'upload.statusInit': string;
56
+ /** Platzhalter: `{name}`. */
57
+ 'upload.statusUploading': string;
58
+ /** Platzhalter: `{name}`. */
59
+ 'upload.statusDone': string;
60
+ /** Platzhalter: `{name}`, `{maxSize}`. */
61
+ 'upload.errorTooLarge': string;
62
+ /** Platzhalter: `{name}`. */
63
+ 'upload.errorType': string;
64
+ /** Platzhalter: `{name}`. */
65
+ 'upload.errorFailed': string;
66
+ 'prompt.linkUrl': string;
67
+ 'prompt.imageUrl': string;
68
+ /** Spaltenüberschrift der Tabellenvorlage. Platzhalter: `{n}`. */
69
+ 'table.column': string;
70
+ }
71
+ /** Eine Sprache: BCP-47-Code plus vollständiger Textsatz. */
72
+ export interface Locale {
73
+ /** BCP-47-Code; steuert Pluralregeln und Uhrzeitformat. */
74
+ code: string;
75
+ texts: LocaleTexts;
76
+ }
77
+ /** Die Schlüssel von `LocaleTexts`, deren Wert ein `PluralText` ist. */
78
+ export type PluralTextKey = {
79
+ [K in keyof LocaleTexts]: LocaleTexts[K] extends PluralText ? K : never;
80
+ }[keyof LocaleTexts];
81
+ /** Die Schlüssel von `LocaleTexts`, deren Wert ein einfacher String ist. */
82
+ export type SimpleTextKey = Exclude<keyof LocaleTexts, PluralTextKey>;
@@ -0,0 +1,133 @@
1
+ import './ui/toolbar.css';
2
+ import './ui/statusbar.css';
3
+ import './ui/preview.css';
4
+ import './ui/fullscreen.css';
5
+ import type { EditorView } from '@codemirror/view';
6
+ import type { SupaMDEOptions } from './options';
7
+ import { type EditorMode } from './livepreview';
8
+ export type { SupaMDEOptions } from './options';
9
+ export type { KeyBinding } from '@codemirror/view';
10
+ export type { EditorMode } from './livepreview';
11
+ export type { SupaStorage } from './features/storage';
12
+ export type { AutosaveOptions } from './features/autosave';
13
+ export type { UploadImageOptions, UploadError } from './features/image-upload';
14
+ export type { Locale, LocaleTexts, PluralText } from './i18n/types';
15
+ /**
16
+ * SupaMDE — moderner Markdown-Editor auf Basis von CodeMirror 6.
17
+ *
18
+ * Dünne Fassade: der Konstruktor baut über `editor/setup.ts` eine EditorView aus
19
+ * der übergebenen Textarea, umgibt sie mit Toolbar und Statusbar und verdrahtet
20
+ * beide über EINEN updateListener. Alle Methoden delegieren an die Module.
21
+ */
22
+ export declare class SupaMDE {
23
+ /** Aktuelle SupaMDE-Version. */
24
+ static readonly version = "0.1.0";
25
+ /** Die (rohen) Optionen dieser Instanz. */
26
+ readonly options: SupaMDEOptions;
27
+ /** Die zugrunde liegende CM6-EditorView (NICHT das CM5-Objekt). */
28
+ readonly codemirror: EditorView;
29
+ private readonly handle;
30
+ private readonly container;
31
+ private readonly editorRow;
32
+ private readonly toolbar;
33
+ private readonly statusbar;
34
+ private readonly preview;
35
+ private readonly fullscreen;
36
+ /**
37
+ * Autosave dieser Instanz. Immer erzeugt, aber nur aktiv, wenn die Option es
38
+ * verlangt UND der Speicher trägt — `isActive()` ist die Wahrheit, nicht die
39
+ * Option.
40
+ */
41
+ private readonly autosave;
42
+ /**
43
+ * Der Uploader dieser Instanz. Wird NACH der View erzeugt (er braucht sie),
44
+ * die Drop/Paste-Extension zeigt daher über eine Closure auf dieses Feld statt
45
+ * direkt auf den Uploader.
46
+ */
47
+ private uploader;
48
+ /** Referenz auf den F8/F9/F10/F11-Keydown-Handler, damit toTextArea() ihn abräumt. */
49
+ private readonly onViewShortcuts;
50
+ /**
51
+ * EINMALIG im Konstruktor berechneter Render-Options-Snapshot — Panel UND
52
+ * `markdown()` nutzen GENAU dieses Feld (nicht `renderOptionsFrom(this.options)`
53
+ * bei jedem Aufruf neu), damit beide auch bei einer nachträglichen Mutation von
54
+ * `this.options` konsistent bleiben (eine Quelle der Wahrheit, kein stiller Split).
55
+ */
56
+ private readonly renderOpts;
57
+ /**
58
+ * Aktueller Darstellungsmodus. Bewusst ein Instanzfeld und KEIN StateField:
59
+ * Der Modus ist eine Eigenschaft der Ansicht, nicht des Dokuments — gleiches
60
+ * Muster wie bei Fullscreen und Side-by-Side.
61
+ */
62
+ private editorMode;
63
+ /**
64
+ * Die UI-Texte dieser Instanz — EINMAL im Konstruktor aus `locale`/`texts`
65
+ * erzeugt und an Toolbar, Statusbar, Uploader und Autosave weitergereicht;
66
+ * Commands lesen ihn über `translatorFacet` aus dem State. Kein Sprachwechsel
67
+ * zur Laufzeit.
68
+ */
69
+ private readonly translator;
70
+ constructor(options?: SupaMDEOptions);
71
+ value(): string;
72
+ value(val: string): void;
73
+ getValue(): string;
74
+ setValue(val: string): void;
75
+ /** Überschreibt den Inhalt eines Statusbar-Items (API-kompatibel zu easyMDE). */
76
+ updateStatusBar(itemName: string, content: string): void;
77
+ /** Rendert Markdown (inkl. LaTeX) zu HTML. Teilt exakt denselben Render-Options-Snapshot wie das Panel. */
78
+ markdown(text: string): string;
79
+ /**
80
+ * Schaltet die Nebeneinander-Vorschau gezielt an oder aus. Idempotent —
81
+ * ein Aufruf mit dem bereits aktiven Zustand ändert nichts.
82
+ */
83
+ setSideBySide(on: boolean): void;
84
+ toggleSideBySide(): void;
85
+ isSideBySideActive(): boolean;
86
+ /** Schaltet den Vollbildmodus gezielt an oder aus. Idempotent. */
87
+ setFullScreen(on: boolean): void;
88
+ toggleFullScreen(): void;
89
+ isFullscreenActive(): boolean;
90
+ /**
91
+ * Vorschau UND Vollbild gemeinsam schalten (Alles-oder-nichts): Ist nicht
92
+ * bereits beides aktiv, wird beides eingeschaltet — auch aus einem
93
+ * Teilzustand heraus. Sind beide aktiv, wird beides ausgeschaltet.
94
+ */
95
+ togglePreviewFullScreen(): void;
96
+ /** Ob Vorschau und Vollbild gleichzeitig aktiv sind. */
97
+ isPreviewFullScreenActive(): boolean;
98
+ /** Der aktuelle Darstellungsmodus. */
99
+ getEditorMode(): EditorMode;
100
+ /**
101
+ * Setzt den Darstellungsmodus. Idempotent — ein Aufruf mit dem bereits aktiven
102
+ * Modus dispatcht nichts. Der Wechsel läuft über ein Compartment-`reconfigure`,
103
+ * daher bleiben Dokument, Cursor, Historie und Scrollposition erhalten.
104
+ */
105
+ setEditorMode(mode: EditorMode): void;
106
+ /** Wechselt zwischen `'source'` und `'live'`. */
107
+ toggleEditorMode(): void;
108
+ /**
109
+ * Löscht den gespeicherten Entwurf UND stoppt den laufenden Debounce-Timer.
110
+ * Nach erfolgreichem Speichern im eigenen Backend zu rufen — sonst holt der
111
+ * Editor beim nächsten Öffnen den alten Entwurf zurück.
112
+ */
113
+ clearAutosavedValue(): Promise<void>;
114
+ /** Ob Autosave aktiv ist (aktiviert, `key` gültig, Speicher verfügbar). */
115
+ isAutosaveActive(): boolean;
116
+ /**
117
+ * Startet den Upload für die übergebenen Dateien. Jede Datei wird einzeln
118
+ * validiert; ungültige werden über `onError` gemeldet, ohne die gültigen
119
+ * aufzuhalten. Ohne aktivierten Bild-Upload folgenlos.
120
+ */
121
+ uploadImages(files: FileList | File[]): void;
122
+ /**
123
+ * Öffnet die Dateiauswahl. Der Input wird bei Bedarf erzeugt und nicht in der
124
+ * Toolbar geparkt — funktioniert deshalb auch bei `toolbar: false`.
125
+ */
126
+ openBrowseFileWindow(): void;
127
+ /** Baut den Editor zurück und stellt die ursprüngliche Textarea wieder her. */
128
+ toTextArea(): HTMLTextAreaElement;
129
+ }
130
+ export { VERSION } from './version';
131
+ export { en } from './i18n/en';
132
+ export { de } from './i18n/de';
133
+ export default SupaMDE;
@@ -0,0 +1,22 @@
1
+ import { Compartment, type Extension } from '@codemirror/state';
2
+ export type { HiddenRange, MarkSets } from './ranges';
3
+ export { computeHiddenRanges, DEFAULT_MARK_SETS } from './ranges';
4
+ /**
5
+ * Darstellungsmodus des Editors.
6
+ *
7
+ * - `'source'` — Markdown-Markup bleibt sichtbar (easyMDE-Parität). Default.
8
+ * - `'live'` — Markup wird ausgeblendet und nur am Cursor sichtbar (Obsidian-Stil).
9
+ */
10
+ export type EditorMode = 'source' | 'live';
11
+ /** Alle gültigen Modus-Werte — Grundlage der Options-Normalisierung. */
12
+ export declare const EDITOR_MODES: readonly EditorMode[];
13
+ /** Die Live-Preview-Extension: blendet inaktives Markup aus. */
14
+ export declare const livePreviewExtension: Extension;
15
+ /**
16
+ * Compartment für den Modus-Wechsel zur Laufzeit. Ein `reconfigure` darauf tauscht
17
+ * die Extension aus, ohne die View neu zu bauen — Dokument, Cursor, Selektion,
18
+ * Undo-Historie und Scrollposition bleiben erhalten.
19
+ */
20
+ export declare const livePreviewCompartment: Compartment;
21
+ /** Die zum Modus gehörende Extension (leer im Source-Modus). */
22
+ export declare function livePreviewFor(mode: EditorMode): Extension;
@@ -0,0 +1,3 @@
1
+ import { type Extension } from '@codemirror/state';
2
+ /** Die fertige Live-Preview-Extension (Ausblenden + atomare Bereiche). */
3
+ export declare const livePreviewPlugin: Extension;
@@ -0,0 +1,62 @@
1
+ import type { EditorState } from '@codemirror/state';
2
+ /** Ein auszublendender Dokumentbereich. */
3
+ export interface HiddenRange {
4
+ from: number;
5
+ to: number;
6
+ }
7
+ /**
8
+ * Bedingung an den Elternknoten eines Markers, unter der er als Markup zählt.
9
+ * `undefined` heißt: jeder Elternknoten genügt (keine Einschränkung).
10
+ *
11
+ * Nötig, weil manche Lezer-Knotennamen mehrdeutig sind: `CodeMark` markiert
12
+ * sowohl den Backtick von Inline-Code (Elternknoten `InlineCode`) als auch die
13
+ * Zäune eines Fenced Code Blocks (Elternknoten `FencedCode`); `HeaderMark`
14
+ * markiert sowohl das `#`-Präfix einer ATX-Überschrift (Elternknoten
15
+ * `ATXHeading1`…`ATXHeading6`) als auch die Unterstreichung einer
16
+ * Setext-Überschrift (Elternknoten `SetextHeading1`/`SetextHeading2`). Ohne
17
+ * diese Prüfung würden Fenced Code Blocks und Setext-Überschriften fälschlich
18
+ * als Markup behandelt und im Live-Modus unvollständig bzw. mit einer
19
+ * leeren Zeile dargestellt.
20
+ */
21
+ export type ParentFilter = (parentName: string) => boolean;
22
+ /** Welche Lezer-Knotennamen als Markup gelten, mit optionaler Elternknoten-Bedingung. */
23
+ export interface MarkSets {
24
+ /** Marker, die exakt ihren eigenen Bereich ausblenden. */
25
+ inline: ReadonlyMap<string, ParentFilter | undefined>;
26
+ /** Marker, deren angrenzendes Leerzeichen mit ausgeblendet wird. */
27
+ block: ReadonlyMap<string, ParentFilter | undefined>;
28
+ }
29
+ /**
30
+ * Die Marker der aktuell unterstützten Formatierungen. Am realen
31
+ * `@lezer/markdown`-Parser (mit `[GFM, Math]`) verifiziert; `StrikethroughMark`
32
+ * hat bewusst einen eigenen Namen und ist NICHT `EmphasisMark`.
33
+ *
34
+ * `CodeMark` gilt nur mit Elternknoten `InlineCode` als Markup (Fenced Code
35
+ * Blocks bleiben vollständig sichtbar), `HeaderMark` nur mit einem
36
+ * `ATXHeading*`-Elternknoten (Setext-Unterstreichungen bleiben sichtbar).
37
+ * Siehe `ParentFilter` für die Begründung.
38
+ *
39
+ * Exportiert und als Parameter durchgereicht, damit sich der Umfang erweitern
40
+ * lässt (Listen-Marker, Link-Syntax), ohne diese Datei zu editieren.
41
+ */
42
+ export declare const DEFAULT_MARK_SETS: MarkSets;
43
+ /**
44
+ * Berechnet die auszublendenden Markup-Bereiche für den Live-Modus.
45
+ *
46
+ * Regel (knoten-genau): Das Markup eines Knotens wird ausgeblendet, solange der
47
+ * Knoten KEINEN Cursor und keinen Selektionsbereich berührt. Sobald der Cursor ihn
48
+ * berührt — Grenzen eingeschlossen —, wird sein Markup sichtbar und normal editierbar.
49
+ *
50
+ * Bezugspunkt ist der ELTERNknoten, nicht der Marker: Bei einem mehrzeiligen
51
+ * Blockquote hängen alle `QuoteMark` am selben `Blockquote`, ein Cursor in einer
52
+ * beliebigen Zeile macht daher die Marker aller Zeilen sichtbar. Bewusst so — das
53
+ * Zitat ist eine Einheit.
54
+ *
55
+ * Reine Funktion: kein DOM, keine View, keine Dekorationen. Rückgabe ist nach `from`
56
+ * aufsteigend sortiert und überlappungsfrei (Vertrag für `RangeSet.of()`, das
57
+ * unsortierte Eingaben mit einem Fehler quittiert).
58
+ */
59
+ export declare function computeHiddenRanges(state: EditorState, visibleRanges: readonly {
60
+ from: number;
61
+ to: number;
62
+ }[], marks?: MarkSets): HiddenRange[];
@@ -0,0 +1,20 @@
1
+ import type { MarkedExtension } from 'marked';
2
+ /** Signatur von `katex.renderToString`. */
3
+ type KatexRender = (expr: string, opts: {
4
+ displayMode: boolean;
5
+ throwOnError: boolean;
6
+ }) => string;
7
+ /** Minimale Form des KaTeX-Objekts (global oder injiziert). */
8
+ interface KatexLike {
9
+ renderToString: KatexRender;
10
+ }
11
+ /**
12
+ * Injiziert eine KaTeX-Instanz (für Bundler-Setups, die `katex` als echtes
13
+ * Modul importieren und reinreichen). Fluchtluke — im Example/CDN-Fall nicht nötig.
14
+ */
15
+ export declare function setKatex(katex: KatexLike | null): void;
16
+ /** marked-Extension-Bündel für $$-Block- und $-Inline-Math. */
17
+ export declare const katexMarkedExtension: MarkedExtension;
18
+ /** Ob KaTeX aktuell verfügbar ist (für Tests/Diagnose). Synchron, ohne Race. */
19
+ export declare function isKatexAvailable(): boolean;
20
+ export {};
@@ -0,0 +1,27 @@
1
+ export interface RenderOptions {
2
+ /** marked `breaks`: einfacher Zeilenumbruch → <br>. Default true. */
3
+ singleLineBreaks?: boolean;
4
+ /** Ersetzt den eingebauten Renderer komplett (Fluchtluke). */
5
+ previewRender?: (text: string) => string;
6
+ }
7
+ /**
8
+ * Baut `RenderOptions` aus den (rohen) SupaMDE-Optionen — die EINE Stelle, an
9
+ * der die Render-Relevanten Felder aus `SupaMDEOptions` extrahiert werden.
10
+ * Panel-Verdrahtung UND `markdown()`-Fassade nutzen sie beide (keine
11
+ * doppelte Inline-Konstruktion, Design §3.1 „eine Quelle der Wahrheit").
12
+ *
13
+ * Typ bewusst strukturell (`Pick`), damit `parse.ts` NICHT `options.ts`
14
+ * importieren muss — bleibt frei von Editor-/Fassaden-Abhängigkeiten.
15
+ */
16
+ export declare function renderOptionsFrom(o: {
17
+ renderingConfig?: {
18
+ singleLineBreaks?: boolean;
19
+ };
20
+ previewRender?: (text: string) => string;
21
+ }): RenderOptions;
22
+ /**
23
+ * Markdown → HTML. Ohne `previewRender`: marked (GFM) + KaTeX-Extensions +
24
+ * addAnchorTargetBlank. Reine Funktion (kein DOM, kein Editor) — Basis der
25
+ * öffentlichen `markdown()`-Methode UND der Side-by-Side-Vorschau.
26
+ */
27
+ export declare function markdownToHtml(text: string, opts?: RenderOptions): string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Ergänzt bei allen <a>-Tags ohne target ein target="_blank" plus
3
+ * rel="noopener noreferrer". Minimales Sanitizing für die Preview — kein
4
+ * DOMPurify (bewusste Scope-Grenze, siehe Design §3.4).
5
+ */
6
+ export declare function addAnchorTargetBlank(html: string): string;
@@ -0,0 +1,83 @@
1
+ import type { ToolbarOption } from './ui/toolbar-config';
2
+ import type { StatusOption } from './ui/statusbar';
3
+ import type { KeyBinding } from '@codemirror/view';
4
+ import { type EditorMode } from './livepreview';
5
+ import type { AutosaveOptions } from './features/autosave';
6
+ import type { UploadImageOptions } from './features/image-upload';
7
+ import type { Locale, LocaleTexts } from './i18n/types';
8
+ /** Öffentliche Konfigurationsoptionen für SupaMDE (Kern-Set, M1). */
9
+ export interface SupaMDEOptions {
10
+ /** Das Textarea-Element, an das der Editor gebunden wird. */
11
+ element?: HTMLElement | null;
12
+ /** Zeilenumbruch statt horizontalem Scrollen (Default: true). */
13
+ lineWrapping?: boolean;
14
+ /** Platzhaltertext im leeren Editor. */
15
+ placeholder?: string;
16
+ /** Fokussiert den Editor nach Erzeugung (Default: false). */
17
+ autofocus?: boolean;
18
+ /** Tab-Breite in Spalten (Default: 2). */
19
+ tabSize?: number;
20
+ /** Einrücktiefe in Leerzeichen (Default: 2). */
21
+ indentUnit?: number;
22
+ /** Einrücktiefe in Code-Blöcken: Leerzeichen-Anzahl oder 'tab' (Default: indentUnit). */
23
+ codeIndentUnit?: number | 'tab';
24
+ /** Startwert; überschreibt den Textarea-Inhalt, falls gesetzt. */
25
+ initialValue?: string;
26
+ /** Eigene Tastenkürzel; haben Vorrang vor den SupaMDE-Defaults bei Konflikten. */
27
+ extraKeys?: KeyBinding[];
28
+ /**
29
+ * Darstellungsmodus: `'source'` zeigt das Markdown-Markup (Default),
30
+ * `'live'` blendet es aus und zeigt es nur am Cursor (Obsidian-Stil).
31
+ */
32
+ editorMode?: EditorMode;
33
+ /** Toolbar-Konfiguration: false (aus), oder Liste aus Built-in-Namen/Custom-Buttons. */
34
+ toolbar?: ToolbarOption;
35
+ /** Statusbar-Konfiguration: false (aus), oder Liste aus Built-in-Namen/Custom-Items. */
36
+ status?: StatusOption;
37
+ /** Ersetzt den eingebauten Markdown-Renderer der Vorschau komplett. */
38
+ previewRender?: (text: string) => string;
39
+ /** Zusätzliche CSS-Klasse(n) aufs Vorschau-Panel. */
40
+ previewClass?: string | string[];
41
+ /** marked-Feintuning für die Vorschau. */
42
+ renderingConfig?: {
43
+ singleLineBreaks?: boolean;
44
+ };
45
+ /** Bidirektionaler Scroll-Sync im Side-by-Side (Default true). */
46
+ syncSideBySidePreviewScroll?: boolean;
47
+ /** Callback bei Fullscreen-Wechsel. */
48
+ onToggleFullScreen?: (active: boolean) => void;
49
+ /**
50
+ * Fügt die SupaMDE-Styles automatisch als `<style>`-Tag in den Head ein
51
+ * (Default: true). Auf `false` setzen, wenn das Host-Projekt die Styles
52
+ * selbst kontrolliert — dann muss `supamde/style.css` manuell eingebunden
53
+ * werden, sonst bleiben Toolbar und Statusbar ungestylt.
54
+ */
55
+ injectStyles?: boolean;
56
+ /**
57
+ * Autosave (M5). Der Inhalt überlebt Absturz, Schließen und Reload.
58
+ * Per Default aus; `key` ist bei `enabled: true` Pflicht.
59
+ */
60
+ autosave?: AutosaveOptions;
61
+ /**
62
+ * Bild-Upload (M5). Drag & Drop, Einfügen aus der Zwischenablage und
63
+ * Dateiauswahl. Per Default aus; `upload` ist Pflicht.
64
+ */
65
+ uploadImage?: UploadImageOptions;
66
+ /** Sprache der UI-Texte (Default: en). */
67
+ locale?: Locale;
68
+ /** Überschreibt einzelne Texte; Vorrang vor `locale`. */
69
+ texts?: Partial<LocaleTexts>;
70
+ }
71
+ /** Normalisierte, immer vollständig belegte Optionen für die Extension-Erzeugung. */
72
+ export interface ResolvedOptions {
73
+ lineWrapping: boolean;
74
+ placeholder: string | null;
75
+ autofocus: boolean;
76
+ tabSize: number;
77
+ indentUnit: number;
78
+ codeIndentUnit: number | 'tab';
79
+ extraKeys: KeyBinding[];
80
+ editorMode: EditorMode;
81
+ }
82
+ /** Füllt fehlende Optionen mit Defaults und liefert eine vollständige Form. */
83
+ export declare function resolveOptions(options: SupaMDEOptions): ResolvedOptions;
@@ -0,0 +1,2 @@
1
+ .supamde-container{--supamde-border-color:#d0d0d0;--supamde-border-width:1px;--supamde-border-top-width:var(--supamde-border-width);--supamde-border-right-width:var(--supamde-border-width);--supamde-border-bottom-width:var(--supamde-border-width);--supamde-border-left-width:var(--supamde-border-width);--supamde-radius:4px;--supamde-divider-toolbar-width:1px;--supamde-divider-statusbar-width:1px;--supamde-divider-preview-width:1px;--supamde-toolbar-bg:#f7f7f7;--supamde-btn-hover:#e6e6e6;--supamde-btn-active:#d8e6ff;--supamde-btn-text:#333;border-style:solid;border-color:var(--supamde-border-color);border-top-width:var(--supamde-border-top-width);border-right-width:var(--supamde-border-right-width);border-bottom-width:var(--supamde-border-bottom-width);border-left-width:var(--supamde-border-left-width);border-radius:var(--supamde-radius);overflow:hidden}.supamde-toolbar{border-bottom:var(--supamde-divider-toolbar-width) solid var(--supamde-border-color);background:var(--supamde-toolbar-bg);flex-wrap:wrap;align-items:center;gap:2px;padding:4px;display:flex}.supamde-toolbar button{width:28px;height:28px;color:var(--supamde-btn-text);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:3px;justify-content:center;align-items:center;padding:0;display:inline-flex}.supamde-toolbar button:hover{background:var(--supamde-btn-hover)}.supamde-toolbar button.active{background:var(--supamde-btn-active)}.supamde-toolbar button svg{width:16px;height:16px}.supamde-separator{background:var(--supamde-border-color);width:1px;height:20px;margin:0 4px;display:inline-block}.supamde-statusbar{border-top:var(--supamde-divider-statusbar-width,1px) solid var(--supamde-border-color,#d0d0d0);background:var(--supamde-statusbar-bg,#f7f7f7);color:#666;gap:12px;padding:4px 8px;font-size:12px;display:flex}.supamde-statusbar span{white-space:nowrap}.supamde-editor-row{flex-direction:row;align-items:stretch;display:flex}.supamde-editor-row .cm-editor{flex:1;min-width:0}.supamde-container.supamde-sided .supamde-editor-row .cm-editor{flex:0 0 50%;width:50%}.supamde-preview-side{border-left:var(--supamde-divider-preview-width,1px) solid var(--supamde-border-color,#ddd);box-sizing:border-box;width:50%;padding:.5rem 1rem;overflow-y:auto}.supamde-container.supamde-fullscreen{z-index:1000;background:var(--supamde-bg,#fff);flex-direction:column;max-width:none;display:flex;position:fixed;inset:0}.supamde-container.supamde-fullscreen .supamde-editor-row{flex:1;min-height:0}.supamde-container.supamde-fullscreen .supamde-editor-row .cm-editor{height:100%}
2
+ /*$vite$:1*/