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.
- package/LICENSE +21 -0
- package/README.md +1228 -2
- package/dist/commands/block.d.ts +20 -0
- package/dist/commands/code-indent.d.ts +38 -0
- package/dist/commands/history.d.ts +6 -0
- package/dist/commands/indent.d.ts +28 -0
- package/dist/commands/inline.d.ts +9 -0
- package/dist/commands/keymap.d.ts +8 -0
- package/dist/commands/link-image.d.ts +13 -0
- package/dist/commands/list.d.ts +20 -0
- package/dist/commands/prefixes.d.ts +29 -0
- package/dist/commands/queries.d.ts +25 -0
- package/dist/commands/table.d.ts +7 -0
- package/dist/commands/types.d.ts +17 -0
- package/dist/editor/extensions.d.ts +14 -0
- package/dist/editor/highlight.d.ts +11 -0
- package/dist/editor/link-click.d.ts +53 -0
- package/dist/editor/link-hover-cursor.d.ts +44 -0
- package/dist/editor/math.d.ts +8 -0
- package/dist/editor/setup.d.ts +26 -0
- package/dist/editor/theme.d.ts +7 -0
- package/dist/editor/tokens.d.ts +16 -0
- package/dist/editor/value.d.ts +10 -0
- package/dist/features/autosave.d.ts +31 -0
- package/dist/features/image-upload.d.ts +73 -0
- package/dist/features/storage.d.ts +30 -0
- package/dist/features/upload-dom.d.ts +20 -0
- package/dist/features/upload-placeholder.d.ts +35 -0
- package/dist/features/word-count.d.ts +5 -0
- package/dist/i18n/de.d.ts +3 -0
- package/dist/i18n/en.d.ts +3 -0
- package/dist/i18n/facet.d.ts +9 -0
- package/dist/i18n/translator.d.ts +17 -0
- package/dist/i18n/types.d.ts +82 -0
- package/dist/index.d.ts +133 -0
- package/dist/livepreview/index.d.ts +22 -0
- package/dist/livepreview/plugin.d.ts +3 -0
- package/dist/livepreview/ranges.d.ts +62 -0
- package/dist/markdown/katex-marked.d.ts +20 -0
- package/dist/markdown/parse.d.ts +27 -0
- package/dist/markdown/sanitize.d.ts +6 -0
- package/dist/options.d.ts +83 -0
- package/dist/supamde.css +2 -0
- package/dist/supamde.mjs +3617 -0
- package/dist/supamde.mjs.map +1 -0
- package/dist/ui/actions.d.ts +51 -0
- package/dist/ui/fullscreen.d.ts +16 -0
- package/dist/ui/icons.d.ts +4 -0
- package/dist/ui/inject-styles.d.ts +13 -0
- package/dist/ui/preview.d.ts +27 -0
- package/dist/ui/shortcut-label.d.ts +24 -0
- package/dist/ui/statusbar.d.ts +28 -0
- package/dist/ui/toolbar-config.d.ts +36 -0
- package/dist/ui/toolbar.d.ts +16 -0
- package/dist/ui/update-listener.d.ts +15 -0
- package/dist/util/text-format.d.ts +11 -0
- package/dist/utils/html.d.ts +2 -0
- package/dist/utils/text.d.ts +41 -0
- package/dist/version.d.ts +2 -0
- package/package.json +96 -4
|
@@ -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>;
|
package/dist/index.d.ts
ADDED
|
@@ -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,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;
|
package/dist/supamde.css
ADDED
|
@@ -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*/
|