@nonoun/native-editor 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.
@@ -0,0 +1,17 @@
1
+ import type { EditorView } from '@codemirror/view';
2
+ export interface EditorCommands {
3
+ toggleBold(view: EditorView): boolean;
4
+ toggleItalic(view: EditorView): boolean;
5
+ toggleCode(view: EditorView): boolean;
6
+ toggleStrikethrough(view: EditorView): boolean;
7
+ setHeading(view: EditorView, level: 1 | 2 | 3): boolean;
8
+ toggleBulletList(view: EditorView): boolean;
9
+ toggleOrderedList(view: EditorView): boolean;
10
+ toggleBlockquote(view: EditorView): boolean;
11
+ insertLink(view: EditorView, url?: string, text?: string): boolean;
12
+ insertImage(view: EditorView, url?: string, alt?: string): boolean;
13
+ insertHorizontalRule(view: EditorView): boolean;
14
+ }
15
+ export declare const commands: EditorCommands;
16
+ export declare const markdownKeymap: import("@codemirror/state").Extension;
17
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAgRnD,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACtC,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACxC,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACtC,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IACxD,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC5C,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC7C,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC5C,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACnE,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACnE,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;CACjD;AAED,eAAO,MAAM,QAAQ,EAAE,cAYtB,CAAC;AAMF,eAAO,MAAM,cAAc,uCAQzB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Live Preview Decorations
3
+ *
4
+ * Reads the @lezer/markdown parse tree and builds a DecorationSet that applies
5
+ * visual formatting in live preview mode (Obsidian-style: markdown source with
6
+ * inline decorations). Wrapped in a Compartment for mode switching.
7
+ */
8
+ import { Compartment } from '@codemirror/state';
9
+ import type { Extension } from '@codemirror/state';
10
+ /** Compartment for toggling live preview on/off */
11
+ export declare const previewCompartment: Compartment;
12
+ /** Create the live preview extension (active state) */
13
+ export declare function createPreviewExtension(): Extension;
14
+ /** Empty extension (source mode -- no decorations) */
15
+ export declare const sourceExtension: Extension;
16
+ //# sourceMappingURL=decorations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorations.d.ts","sourceRoot":"","sources":["../src/decorations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAIjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,mDAAmD;AACnD,eAAO,MAAM,kBAAkB,aAAoB,CAAC;AAgWpD,uDAAuD;AACvD,wBAAgB,sBAAsB,IAAI,SAAS,CAElD;AAED,sDAAsD;AACtD,eAAO,MAAM,eAAe,EAAE,SAAc,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Signal, ReadonlySignal } from '@nonoun/native-ui';
2
+ export type ViewMode = 'preview' | 'source';
3
+ export declare const DEFAULT_TOOLBAR: string[];
4
+ export declare class EditorStore {
5
+ readonly mode: Signal<ViewMode>;
6
+ readonly disabled: Signal<boolean>;
7
+ readonly readonly: Signal<boolean>;
8
+ readonly placeholder: Signal<string>;
9
+ readonly toolbar: Signal<string[]>;
10
+ readonly src: Signal<string | null>;
11
+ /** True when disabled OR readonly — drives CodeMirror readOnly + editable. */
12
+ readonly locked: ReadonlySignal<boolean>;
13
+ setMode(mode: ViewMode): void;
14
+ toggleMode(): void;
15
+ setToolbar(val: string | string[]): void;
16
+ reset(): void;
17
+ }
18
+ //# sourceMappingURL=editor-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-store.d.ts","sourceRoot":"","sources":["../src/editor-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMhE,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE5C,eAAO,MAAM,eAAe,UAM3B,CAAC;AAMF,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAA+B;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAc;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAA2B;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAgB;IAEnD,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAEtC;IAIF,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAK7B,UAAU,IAAI,IAAI;IAIlB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAMxC,KAAK,IAAI,IAAI;CAUd"}
@@ -0,0 +1,7 @@
1
+ export { NuiEditor } from './nui-editor-element.ts';
2
+ export { EditorStore, DEFAULT_TOOLBAR } from './editor-store.ts';
3
+ export type { ViewMode } from './editor-store.ts';
4
+ export { commands, markdownKeymap } from './commands.ts';
5
+ export type { EditorCommands } from './commands.ts';
6
+ export { previewCompartment, createPreviewExtension, sourceExtension } from './decorations.ts';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,853 @@
1
+ import { GatewayController as e, UIElement as t, batch as n, computed as r, createDisabledEffect as i, signal as a } from "@nonoun/native-ui";
2
+ import { Compartment as o, EditorState as s, EditorView as c, nuiBaseExtensions as l, nuiSyntaxHighlighting as u, nuiTheme as ee } from "@nonoun/nui-codemirror";
3
+ import { markdown as d } from "@codemirror/lang-markdown";
4
+ import { Decoration as f, ViewPlugin as p, WidgetType as m, keymap as te, placeholder as h } from "@codemirror/view";
5
+ import { Compartment as ne, EditorSelection as g, RangeSetBuilder as _ } from "@codemirror/state";
6
+ import { syntaxTree as v } from "@codemirror/language";
7
+ function y(e, t) {
8
+ let { state: n } = e, r = [], i = [];
9
+ for (let e of n.selection.ranges) {
10
+ let { from: a, to: o } = e;
11
+ if (a === o) r.push({
12
+ from: a,
13
+ to: o,
14
+ insert: t + t
15
+ }), i.push({ anchor: a + t.length });
16
+ else {
17
+ let e = n.sliceDoc(a, o);
18
+ if (e.startsWith(t) && e.endsWith(t) && e.length >= t.length * 2) {
19
+ let n = e.slice(t.length, e.length - t.length);
20
+ r.push({
21
+ from: a,
22
+ to: o,
23
+ insert: n
24
+ }), i.push({
25
+ anchor: a,
26
+ head: a + n.length
27
+ });
28
+ } else {
29
+ let s = n.sliceDoc(a - t.length, a), c = n.sliceDoc(o, o + t.length);
30
+ s === t && c === t ? (r.push({
31
+ from: a - t.length,
32
+ to: a,
33
+ insert: ""
34
+ }), r.push({
35
+ from: o,
36
+ to: o + t.length,
37
+ insert: ""
38
+ }), i.push({
39
+ anchor: a - t.length,
40
+ head: o - t.length
41
+ })) : (r.push({
42
+ from: a,
43
+ to: o,
44
+ insert: t + e + t
45
+ }), i.push({
46
+ anchor: a + t.length,
47
+ head: a + t.length + e.length
48
+ }));
49
+ }
50
+ }
51
+ }
52
+ return e.dispatch({
53
+ changes: r,
54
+ selection: g.create(i.map((e) => g.range(e.anchor, e.head ?? e.anchor)))
55
+ }), !0;
56
+ }
57
+ /** Get the full lines covered by a selection range. */
58
+ function b(e, t, n) {
59
+ let { state: r } = e, i = [], a = r.doc.lineAt(t), o = r.doc.lineAt(n);
60
+ for (let e = a.number; e <= o.number; e++) {
61
+ let t = r.doc.line(e);
62
+ i.push({
63
+ from: t.from,
64
+ to: t.to,
65
+ text: t.text,
66
+ number: e
67
+ });
68
+ }
69
+ return i;
70
+ }
71
+ function re(e, t) {
72
+ let { state: n } = e, r = [];
73
+ for (let e of n.selection.ranges) {
74
+ let i = n.doc.lineAt(e.from), a = i.text.match(/^(#{1,6})\s/), o = "#".repeat(t) + " ";
75
+ if (a) {
76
+ let e = a[1].length, n = a[0];
77
+ e === t ? r.push({
78
+ from: i.from,
79
+ to: i.from + n.length,
80
+ insert: ""
81
+ }) : r.push({
82
+ from: i.from,
83
+ to: i.from + n.length,
84
+ insert: o
85
+ });
86
+ } else r.push({
87
+ from: i.from,
88
+ to: i.from,
89
+ insert: o
90
+ });
91
+ }
92
+ return e.dispatch({ changes: r }), !0;
93
+ }
94
+ function ie(e) {
95
+ let { state: t } = e, n = [];
96
+ for (let r of t.selection.ranges) {
97
+ let t = b(e, r.from, r.to), i = t.every((e) => /^- /.test(e.text));
98
+ for (let e of t) i ? n.push({
99
+ from: e.from,
100
+ to: e.from + 2,
101
+ insert: ""
102
+ }) : /^- /.test(e.text) || n.push({
103
+ from: e.from,
104
+ to: e.from,
105
+ insert: "- "
106
+ });
107
+ }
108
+ return e.dispatch({ changes: n }), !0;
109
+ }
110
+ function x(e) {
111
+ let { state: t } = e, n = [];
112
+ for (let r of t.selection.ranges) {
113
+ let t = b(e, r.from, r.to), i = t.every((e) => /^\d+\.\s/.test(e.text)), a = 1;
114
+ for (let e of t) if (i) {
115
+ let t = e.text.match(/^\d+\.\s/);
116
+ t && n.push({
117
+ from: e.from,
118
+ to: e.from + t[0].length,
119
+ insert: ""
120
+ });
121
+ } else if (/^\d+\.\s/.test(e.text)) {
122
+ let t = e.text.match(/^\d+\.\s/);
123
+ t && n.push({
124
+ from: e.from,
125
+ to: e.from + t[0].length,
126
+ insert: `${a}. `
127
+ }), a++;
128
+ } else n.push({
129
+ from: e.from,
130
+ to: e.from,
131
+ insert: `${a}. `
132
+ }), a++;
133
+ }
134
+ return e.dispatch({ changes: n }), !0;
135
+ }
136
+ function S(e) {
137
+ let { state: t } = e, n = [];
138
+ for (let r of t.selection.ranges) {
139
+ let t = b(e, r.from, r.to), i = t.every((e) => /^> /.test(e.text));
140
+ for (let e of t) i ? n.push({
141
+ from: e.from,
142
+ to: e.from + 2,
143
+ insert: ""
144
+ }) : /^> /.test(e.text) || n.push({
145
+ from: e.from,
146
+ to: e.from,
147
+ insert: "> "
148
+ });
149
+ }
150
+ return e.dispatch({ changes: n }), !0;
151
+ }
152
+ function C(e, t, n) {
153
+ let { state: r } = e, i = r.selection.main, a = r.sliceDoc(i.from, i.to), o = n ?? (a || "link text"), s = t ?? "url", c = `[${o}](${s})`, l = i.from + 1 + o.length + 2, u = l + s.length;
154
+ return e.dispatch({
155
+ changes: {
156
+ from: i.from,
157
+ to: i.to,
158
+ insert: c
159
+ },
160
+ selection: t ? { anchor: i.from + c.length } : {
161
+ anchor: l,
162
+ head: u
163
+ }
164
+ }), !0;
165
+ }
166
+ function w(e, t, n) {
167
+ let { state: r } = e, i = r.selection.main, a = n ?? "alt text", o = t ?? "url", s = `![${a}](${o})`, c = i.from + 2 + a.length + 2, l = c + o.length;
168
+ return e.dispatch({
169
+ changes: {
170
+ from: i.from,
171
+ to: i.to,
172
+ insert: s
173
+ },
174
+ selection: t ? { anchor: i.from + s.length } : {
175
+ anchor: c,
176
+ head: l
177
+ }
178
+ }), !0;
179
+ }
180
+ function T(e) {
181
+ let t = e.state.selection.main;
182
+ return e.dispatch({
183
+ changes: {
184
+ from: t.from,
185
+ to: t.to,
186
+ insert: "\n---\n"
187
+ },
188
+ selection: { anchor: t.from + 5 }
189
+ }), !0;
190
+ }
191
+ const E = {
192
+ toggleBold: (e) => y(e, "**"),
193
+ toggleItalic: (e) => y(e, "*"),
194
+ toggleCode: (e) => y(e, "`"),
195
+ toggleStrikethrough: (e) => y(e, "~~"),
196
+ setHeading: re,
197
+ toggleBulletList: ie,
198
+ toggleOrderedList: x,
199
+ toggleBlockquote: S,
200
+ insertLink: C,
201
+ insertImage: w,
202
+ insertHorizontalRule: T
203
+ }, D = te.of([
204
+ {
205
+ key: "Mod-b",
206
+ run: (e) => E.toggleBold(e)
207
+ },
208
+ {
209
+ key: "Mod-i",
210
+ run: (e) => E.toggleItalic(e)
211
+ },
212
+ {
213
+ key: "Mod-e",
214
+ run: (e) => E.toggleCode(e)
215
+ },
216
+ {
217
+ key: "Mod-k",
218
+ run: (e) => E.insertLink(e)
219
+ },
220
+ {
221
+ key: "Mod-Shift-1",
222
+ run: (e) => E.setHeading(e, 1)
223
+ },
224
+ {
225
+ key: "Mod-Shift-2",
226
+ run: (e) => E.setHeading(e, 2)
227
+ },
228
+ {
229
+ key: "Mod-Shift-3",
230
+ run: (e) => E.setHeading(e, 3)
231
+ }
232
+ ]), O = new ne();
233
+ var k = f.mark({ class: "md-bold" }), A = f.mark({ class: "md-italic" }), j = f.mark({ class: "md-code" }), M = f.mark({ class: "md-strikethrough" }), N = f.mark({ class: "md-link" }), P = f.mark({ class: "md-url" }), F = f.mark({ class: "md-syntax-marker" }), ae = f.mark({ class: "md-syntax-marker md-syntax-visible" }), I = f.line({ class: "md-heading md-heading-1" }), L = f.line({ class: "md-heading md-heading-2" }), R = f.line({ class: "md-heading md-heading-3" }), z = f.line({ class: "md-heading md-heading-4" }), B = f.line({ class: "md-heading md-heading-5" }), V = f.line({ class: "md-heading md-heading-6" }), H = f.line({ class: "md-list-item md-bullet-item" }), U = f.line({ class: "md-list-item md-ordered-item" }), W = f.line({ class: "md-blockquote" }), G = f.line({ class: "md-code-block" }), K = f.line({ class: "md-hr" }), q = {
234
+ ATXHeading1: I,
235
+ ATXHeading2: L,
236
+ ATXHeading3: R,
237
+ ATXHeading4: z,
238
+ ATXHeading5: B,
239
+ ATXHeading6: V
240
+ }, J = new Set([
241
+ "HeaderMark",
242
+ "EmphasisMark",
243
+ "CodeMark",
244
+ "StrikethroughMark",
245
+ "LinkMark",
246
+ "QuoteMark",
247
+ "ListMark"
248
+ ]), oe = class extends m {
249
+ src;
250
+ alt;
251
+ constructor(e, t) {
252
+ super(), this.src = e, this.alt = t;
253
+ }
254
+ eq(e) {
255
+ return this.src === e.src && this.alt === e.alt;
256
+ }
257
+ toDOM() {
258
+ let e = document.createElement("img");
259
+ return e.src = this.src, e.alt = this.alt, e.className = "md-image", e.style.maxWidth = "100%", e;
260
+ }
261
+ ignoreEvent() {
262
+ return !1;
263
+ }
264
+ };
265
+ function Y(e, t, n) {
266
+ return e.sliceString(t, n);
267
+ }
268
+ var se = p.fromClass(class {
269
+ decorations;
270
+ constructor(e) {
271
+ this.decorations = this.buildDecorations(e);
272
+ }
273
+ update(e) {
274
+ (e.docChanged || e.viewportChanged || e.selectionSet) && (this.decorations = this.buildDecorations(e.view));
275
+ }
276
+ buildDecorations(e) {
277
+ let t = new _(), n = e.state.doc, r = v(e.state), i = n.lineAt(e.state.selection.main.head).number, a = [], o = [], s = [], c = [];
278
+ r.iterate({
279
+ enter(e) {
280
+ let t = e.name, r = q[t];
281
+ if (r) {
282
+ let t = n.lineAt(e.from), i = n.lineAt(e.to);
283
+ for (let e = t.number; e <= i.number; e++) {
284
+ let t = n.line(e);
285
+ a.push({
286
+ pos: t.from,
287
+ deco: r
288
+ });
289
+ }
290
+ }
291
+ if ((t === "BulletList" || t === "OrderedList") && c.push(t), t === "ListItem") {
292
+ let t = c[c.length - 1] === "OrderedList" ? U : H, r = n.lineAt(e.from), i = n.lineAt(e.to);
293
+ for (let e = r.number; e <= i.number; e++) {
294
+ let r = n.line(e);
295
+ a.push({
296
+ pos: r.from,
297
+ deco: t
298
+ });
299
+ }
300
+ }
301
+ if (t === "Blockquote") {
302
+ let t = n.lineAt(e.from), r = n.lineAt(e.to);
303
+ for (let e = t.number; e <= r.number; e++) {
304
+ let t = n.line(e);
305
+ a.push({
306
+ pos: t.from,
307
+ deco: W
308
+ });
309
+ }
310
+ }
311
+ if (t === "FencedCode") {
312
+ let t = n.lineAt(e.from), r = n.lineAt(e.to);
313
+ for (let e = t.number; e <= r.number; e++) {
314
+ let t = n.line(e);
315
+ a.push({
316
+ pos: t.from,
317
+ deco: G
318
+ });
319
+ }
320
+ }
321
+ if (t === "HorizontalRule") {
322
+ let t = n.lineAt(e.from);
323
+ a.push({
324
+ pos: t.from,
325
+ deco: K
326
+ });
327
+ }
328
+ if (t === "StrongEmphasis" && o.push({
329
+ from: e.from,
330
+ to: e.to,
331
+ deco: k
332
+ }), t === "Emphasis" && o.push({
333
+ from: e.from,
334
+ to: e.to,
335
+ deco: A
336
+ }), t === "InlineCode" && o.push({
337
+ from: e.from,
338
+ to: e.to,
339
+ deco: j
340
+ }), t === "Strikethrough" && o.push({
341
+ from: e.from,
342
+ to: e.to,
343
+ deco: M
344
+ }), t === "Link" && o.push({
345
+ from: e.from,
346
+ to: e.to,
347
+ deco: N
348
+ }), t === "URL" && o.push({
349
+ from: e.from,
350
+ to: e.to,
351
+ deco: P
352
+ }), J.has(t)) {
353
+ let t = n.lineAt(e.from).number === i ? ae : F;
354
+ o.push({
355
+ from: e.from,
356
+ to: e.to,
357
+ deco: t
358
+ });
359
+ }
360
+ if (t === "Image") {
361
+ let t = "", r = "", i = e.node.cursor();
362
+ if (i.firstChild()) do
363
+ i.name === "URL" ? t = Y(n, i.from, i.to) : i.name === "AltText" && (r = Y(n, i.from, i.to));
364
+ while (i.nextSibling());
365
+ t && s.push({
366
+ pos: e.to,
367
+ deco: f.widget({
368
+ widget: new oe(t, r),
369
+ side: 1
370
+ })
371
+ });
372
+ }
373
+ },
374
+ leave(e) {
375
+ let t = e.name;
376
+ (t === "BulletList" || t === "OrderedList") && c.pop();
377
+ }
378
+ });
379
+ let l = [];
380
+ for (let e of a) l.push({
381
+ kind: "line",
382
+ pos: e.pos,
383
+ deco: e.deco
384
+ });
385
+ for (let e of o) l.push({
386
+ kind: "range",
387
+ from: e.from,
388
+ to: e.to,
389
+ deco: e.deco
390
+ });
391
+ for (let e of s) l.push({
392
+ kind: "widget",
393
+ pos: e.pos,
394
+ deco: e.deco
395
+ });
396
+ l.sort((e, t) => {
397
+ let n = e.kind === "range" ? e.from : e.pos, r = t.kind === "range" ? t.from : t.pos;
398
+ if (n !== r) return n - r;
399
+ let i = {
400
+ line: 0,
401
+ range: 1,
402
+ widget: 2
403
+ }, a = i[e.kind] - i[t.kind];
404
+ return a === 0 ? e.kind === "range" && t.kind === "range" ? e.to - t.to : 0 : a;
405
+ });
406
+ let u = /* @__PURE__ */ new Set();
407
+ for (let e of l) if (e.kind === "line") {
408
+ let n = `${e.pos}:${e.deco.spec.class ?? ""}`;
409
+ if (u.has(n)) continue;
410
+ u.add(n), t.add(e.pos, e.pos, e.deco);
411
+ } else e.kind === "range" ? e.from < e.to && t.add(e.from, e.to, e.deco) : t.add(e.pos, e.pos, e.deco);
412
+ return t.finish();
413
+ }
414
+ }, { decorations: (e) => e.decorations });
415
+ /** Create the live preview extension (active state) */
416
+ function X() {
417
+ return se;
418
+ }
419
+ /** Empty extension (source mode -- no decorations) */
420
+ const Z = [], Q = [
421
+ "bold",
422
+ "italic",
423
+ "code",
424
+ "|",
425
+ "h1",
426
+ "h2",
427
+ "h3",
428
+ "|",
429
+ "bullet-list",
430
+ "ordered-list",
431
+ "blockquote",
432
+ "|",
433
+ "link",
434
+ "image",
435
+ "|",
436
+ "mode-toggle"
437
+ ];
438
+ var $ = class {
439
+ mode = a("preview");
440
+ disabled = a(!1);
441
+ readonly = a(!1);
442
+ placeholder = a("");
443
+ toolbar = a(Q);
444
+ src = a(null);
445
+ /** True when disabled OR readonly — drives CodeMirror readOnly + editable. */
446
+ locked = r(() => this.disabled.value || this.readonly.value);
447
+ setMode(e) {
448
+ e !== "preview" && e !== "source" || (this.mode.value = e);
449
+ }
450
+ toggleMode() {
451
+ this.mode.value = this.mode.value === "preview" ? "source" : "preview";
452
+ }
453
+ setToolbar(e) {
454
+ this.toolbar.value = typeof e == "string" ? e.split(",").map((e) => e.trim()).filter(Boolean) : e;
455
+ }
456
+ reset() {
457
+ n(() => {
458
+ this.mode.value = "preview", this.disabled.value = !1, this.readonly.value = !1, this.placeholder.value = "", this.toolbar.value = Q, this.src.value = null;
459
+ });
460
+ }
461
+ }, ce = {
462
+ bold: {
463
+ icon: "text-b",
464
+ label: "Bold",
465
+ action: E.toggleBold
466
+ },
467
+ italic: {
468
+ icon: "text-italic",
469
+ label: "Italic",
470
+ action: E.toggleItalic
471
+ },
472
+ code: {
473
+ icon: "code",
474
+ label: "Code",
475
+ action: E.toggleCode
476
+ },
477
+ h1: {
478
+ icon: "text-h-one",
479
+ label: "Heading 1",
480
+ action: (e) => E.setHeading(e, 1)
481
+ },
482
+ h2: {
483
+ icon: "text-h-two",
484
+ label: "Heading 2",
485
+ action: (e) => E.setHeading(e, 2)
486
+ },
487
+ h3: {
488
+ icon: "text-h-three",
489
+ label: "Heading 3",
490
+ action: (e) => E.setHeading(e, 3)
491
+ },
492
+ "bullet-list": {
493
+ icon: "list-bullets",
494
+ label: "Bullet List",
495
+ action: E.toggleBulletList
496
+ },
497
+ "ordered-list": {
498
+ icon: "list-numbers",
499
+ label: "Ordered List",
500
+ action: E.toggleOrderedList
501
+ },
502
+ blockquote: {
503
+ icon: "quotes",
504
+ label: "Blockquote",
505
+ action: E.toggleBlockquote
506
+ },
507
+ link: {
508
+ icon: "link",
509
+ label: "Link",
510
+ action: E.insertLink
511
+ },
512
+ image: {
513
+ icon: "image",
514
+ label: "Image",
515
+ action: E.insertImage
516
+ }
517
+ }, le = class extends t {
518
+ static observedAttributes = [
519
+ "value",
520
+ "mode",
521
+ "toolbar",
522
+ "placeholder",
523
+ "readonly",
524
+ "disabled",
525
+ "name",
526
+ "src"
527
+ ];
528
+ static formAssociated = !0;
529
+ #e;
530
+ #t = new $();
531
+ #n = new e(this);
532
+ #r = null;
533
+ #i = null;
534
+ #a = "";
535
+ #o = new o();
536
+ #s = new o();
537
+ #c = null;
538
+ #l = null;
539
+ constructor() {
540
+ super(), this.#e = this.attachInternals();
541
+ }
542
+ get value() {
543
+ return this.#r ? this.#r.state.doc.toString() : this.getAttribute("value") ?? "";
544
+ }
545
+ set value(e) {
546
+ this.#r && this.#r.state.doc.toString() !== e && this.#r.dispatch({ changes: {
547
+ from: 0,
548
+ to: this.#r.state.doc.length,
549
+ insert: e
550
+ } }), this.#e.setFormValue(e);
551
+ }
552
+ get mode() {
553
+ return this.#t.mode.value;
554
+ }
555
+ set mode(e) {
556
+ this.#t.setMode(e), this.setAttribute("mode", e);
557
+ }
558
+ get toolbar() {
559
+ return this.#t.toolbar.value;
560
+ }
561
+ set toolbar(e) {
562
+ this.#t.setToolbar(e);
563
+ }
564
+ get placeholder() {
565
+ return this.#t.placeholder.value;
566
+ }
567
+ set placeholder(e) {
568
+ this.#t.placeholder.value = e, this.setAttribute("placeholder", e);
569
+ }
570
+ get readOnly() {
571
+ return this.#t.readonly.value;
572
+ }
573
+ set readOnly(e) {
574
+ this.#t.readonly.value = e, this.toggleAttribute("readonly", e);
575
+ }
576
+ get disabled() {
577
+ return this.#t.disabled.value;
578
+ }
579
+ set disabled(e) {
580
+ this.#t.disabled.value = e, this.toggleAttribute("disabled", e);
581
+ }
582
+ get name() {
583
+ return this.getAttribute("name") ?? "";
584
+ }
585
+ set name(e) {
586
+ this.setAttribute("name", e);
587
+ }
588
+ get src() {
589
+ return this.#t.src.value;
590
+ }
591
+ set src(e) {
592
+ this.#t.src.value = e, e == null ? this.removeAttribute("src") : this.setAttribute("src", e);
593
+ }
594
+ get loading() {
595
+ return this.#n.loading.value;
596
+ }
597
+ get saving() {
598
+ return this.#n.saving.value;
599
+ }
600
+ get dirty() {
601
+ return this.#n.dirty.value;
602
+ }
603
+ get error() {
604
+ return this.#n.error.value;
605
+ }
606
+ /** Proxy to editor commands — each method auto-passes the internal EditorView. */
607
+ get commands() {
608
+ let e = this.#r;
609
+ if (!e) {
610
+ let e = () => !1;
611
+ return {
612
+ toggleBold: e,
613
+ toggleItalic: e,
614
+ toggleCode: e,
615
+ toggleStrikethrough: e,
616
+ setHeading: e,
617
+ toggleBulletList: e,
618
+ toggleOrderedList: e,
619
+ toggleBlockquote: e,
620
+ insertLink: e,
621
+ insertImage: e,
622
+ insertHorizontalRule: e
623
+ };
624
+ }
625
+ return {
626
+ toggleBold: (t) => E.toggleBold(e),
627
+ toggleItalic: (t) => E.toggleItalic(e),
628
+ toggleCode: (t) => E.toggleCode(e),
629
+ toggleStrikethrough: (t) => E.toggleStrikethrough(e),
630
+ setHeading: (t, n) => E.setHeading(e, n),
631
+ toggleBulletList: (t) => E.toggleBulletList(e),
632
+ toggleOrderedList: (t) => E.toggleOrderedList(e),
633
+ toggleBlockquote: (t) => E.toggleBlockquote(e),
634
+ insertLink: (t, n, r) => E.insertLink(e, n, r),
635
+ insertImage: (t, n, r) => E.insertImage(e, n, r),
636
+ insertHorizontalRule: (t) => E.insertHorizontalRule(e)
637
+ };
638
+ }
639
+ focus() {
640
+ this.#r?.focus();
641
+ }
642
+ getSelection() {
643
+ if (!this.#r) return {
644
+ from: 0,
645
+ to: 0,
646
+ text: ""
647
+ };
648
+ let { from: e, to: t } = this.#r.state.selection.main;
649
+ return {
650
+ from: e,
651
+ to: t,
652
+ text: this.#r.state.sliceDoc(e, t)
653
+ };
654
+ }
655
+ insertText(e, t) {
656
+ if (!this.#r) return;
657
+ let n = t ?? this.#r.state.selection.main.head;
658
+ this.#r.dispatch({ changes: {
659
+ from: n,
660
+ to: n,
661
+ insert: e
662
+ } });
663
+ }
664
+ /** Save current content to the `src` URL via PUT. */
665
+ async save() {
666
+ let e = this.#t.src.value;
667
+ if (!e) return !1;
668
+ let t = await this.#n.save(e, this.value);
669
+ return t && this.dispatchEvent(new CustomEvent("nui-save", {
670
+ bubbles: !0,
671
+ composed: !0
672
+ })), t;
673
+ }
674
+ /** Reload content from the `src` URL. */
675
+ async reload() {
676
+ let e = this.#t.src.value;
677
+ if (!e) return;
678
+ let t = await this.#n.load(e);
679
+ t !== null && (this.value = t, this.dispatchEvent(new CustomEvent("nui-load", {
680
+ bubbles: !0,
681
+ composed: !0,
682
+ detail: { value: t }
683
+ })));
684
+ }
685
+ attributeChangedCallback(e, t, n) {
686
+ if (t === n) return;
687
+ let r = this.#t;
688
+ switch (e) {
689
+ case "value":
690
+ this.#r && this.#r.state.doc.toString() !== (n ?? "") && this.#r.dispatch({ changes: {
691
+ from: 0,
692
+ to: this.#r.state.doc.length,
693
+ insert: n ?? ""
694
+ } }), this.#e.setFormValue(n ?? "");
695
+ break;
696
+ case "mode":
697
+ (n === "preview" || n === "source") && (r.mode.value = n);
698
+ break;
699
+ case "toolbar":
700
+ r.toolbar.value = n === null ? Q : n.split(",").map((e) => e.trim()).filter(Boolean);
701
+ break;
702
+ case "placeholder":
703
+ r.placeholder.value = n ?? "";
704
+ break;
705
+ case "readonly":
706
+ r.readonly.value = n !== null;
707
+ break;
708
+ case "disabled":
709
+ r.disabled.value = n !== null;
710
+ break;
711
+ case "src":
712
+ r.src.value = n;
713
+ break;
714
+ }
715
+ super.attributeChangedCallback(e, t, n);
716
+ }
717
+ setup() {
718
+ super.setup();
719
+ let e = this.#t;
720
+ e.mode.value = this.getAttribute("mode") || "preview", e.disabled.value = this.hasAttribute("disabled"), e.readonly.value = this.hasAttribute("readonly"), e.placeholder.value = this.getAttribute("placeholder") ?? "";
721
+ let t = this.getAttribute("toolbar");
722
+ t && e.setToolbar(t), e.src.value = this.getAttribute("src"), this.#a = this.#u(), this.#d(), this.#m(), this.#h(), this.addEffect(i(this, e.disabled, this.#e)), this.addEffect(() => {
723
+ let t = e.mode.value;
724
+ this.#r && this.#r.dispatch({ effects: O.reconfigure(t === "preview" ? X() : Z) }), this.#l && this.#l.setAttribute("value", t), this.dispatchEvent(new CustomEvent("mode-change", {
725
+ bubbles: !0,
726
+ composed: !0,
727
+ detail: { mode: t }
728
+ }));
729
+ }), this.addEffect(() => {
730
+ let t = e.locked.value;
731
+ this.#r && this.#r.dispatch({ effects: this.#o.reconfigure([s.readOnly.of(t), c.editable.of(!t)]) }), this.#c && this.#c.toggleAttribute("disabled", e.disabled.value);
732
+ }), this.addEffect(() => {
733
+ let t = e.placeholder.value;
734
+ this.#r && this.#r.dispatch({ effects: this.#s.reconfigure(t ? h(t) : []) });
735
+ }), this.addEffect(() => {
736
+ e.toolbar.value, this.#f();
737
+ }), this.addEffect(() => {
738
+ e.src.value && this.reload();
739
+ }), this.addEffect(() => {
740
+ this.toggleAttribute("loading", this.#n.loading.value);
741
+ }), this.addEffect(() => {
742
+ this.toggleAttribute("saving", this.#n.saving.value);
743
+ }), this.addEffect(() => {
744
+ let e = this.#n.dirty.value;
745
+ this.toggleAttribute("dirty", e), this.dispatchEvent(new CustomEvent("dirty-change", {
746
+ bubbles: !0,
747
+ composed: !0,
748
+ detail: { dirty: e }
749
+ }));
750
+ }), this.addEffect(() => {
751
+ let e = this.#n.error.value;
752
+ e && this.dispatchEvent(new CustomEvent("nui-error", {
753
+ bubbles: !0,
754
+ composed: !0,
755
+ detail: { error: e }
756
+ }));
757
+ }), this.#e.setFormValue(this.#a);
758
+ }
759
+ teardown() {
760
+ this.#n.destroy(), this.#r &&= (this.#r.destroy(), null), this.#c = null, this.#l = null, this.#i = null, super.teardown();
761
+ }
762
+ /**
763
+ * Read initial content from a `<script type="text/markdown">` child,
764
+ * falling back to the `value` attribute. Consumes the script tag.
765
+ */
766
+ #u() {
767
+ let e = this.querySelector("script[type=\"text/markdown\"]");
768
+ if (e) {
769
+ let t = e.textContent ?? "";
770
+ return e.remove(), t.replace(/^\n/, "");
771
+ }
772
+ return this.getAttribute("value") ?? "";
773
+ }
774
+ #d() {
775
+ let e = document.createElement("ui-toolbar");
776
+ e.setAttribute("size", "sm"), this.#c = e, this.appendChild(e), this.#f();
777
+ }
778
+ #f() {
779
+ let e = this.#c;
780
+ if (!e) return;
781
+ e.innerHTML = "", this.#l = null;
782
+ let t = this.#t.toolbar.value;
783
+ for (let n of t) {
784
+ if (n === "|") {
785
+ let t = document.createElement("ui-divider");
786
+ t.setAttribute("vertical", ""), e.appendChild(t);
787
+ continue;
788
+ }
789
+ if (n === "mode-toggle") {
790
+ this.#p(e);
791
+ continue;
792
+ }
793
+ let t = ce[n];
794
+ if (!t) continue;
795
+ let r = document.createElement("ui-button");
796
+ r.setAttribute("variant", "ghost"), r.setAttribute("size", "sm"), r.setAttribute("square", ""), r.setAttribute("aria-label", t.label), r.setAttribute("title", t.label);
797
+ let i = document.createElement("ui-icon");
798
+ i.setAttribute("name", t.icon), r.appendChild(i), r.addEventListener("ui-press", this.#g(t.action)), e.appendChild(r);
799
+ }
800
+ }
801
+ #p(e) {
802
+ let t = document.createElement("ui-segmented-control");
803
+ t.setAttribute("size", "sm"), t.setAttribute("value", this.#t.mode.value), this.#l = t;
804
+ let n = document.createElement("ui-segment");
805
+ n.setAttribute("value", "preview"), n.textContent = "Preview";
806
+ let r = document.createElement("ui-segment");
807
+ r.setAttribute("value", "source"), r.textContent = "Source", t.appendChild(n), t.appendChild(r), t.addEventListener("ui-change", this.#_), e.appendChild(t);
808
+ }
809
+ #m() {
810
+ let e = document.createElement("div");
811
+ e.className = "nui-editor-surface", this.#i = e, this.appendChild(e);
812
+ }
813
+ #h() {
814
+ if (!this.#i) return;
815
+ let e = this.#t, t = this.#a, n = e.mode.value, r = e.placeholder.value, i = e.locked.value, a = [
816
+ ee,
817
+ u,
818
+ l,
819
+ d(),
820
+ D,
821
+ O.of(n === "preview" ? X() : Z),
822
+ this.#o.of([s.readOnly.of(i), c.editable.of(!i)]),
823
+ this.#s.of(r ? h(r) : []),
824
+ c.updateListener.of((e) => {
825
+ if (e.docChanged) {
826
+ let t = e.state.doc.toString();
827
+ this.#e.setFormValue(t), this.#n.markDirty(), this.dispatchEvent(new CustomEvent("change", {
828
+ bubbles: !0,
829
+ composed: !0,
830
+ detail: { value: t }
831
+ }));
832
+ }
833
+ })
834
+ ];
835
+ this.#r = new c({
836
+ state: s.create({
837
+ doc: t,
838
+ extensions: a
839
+ }),
840
+ parent: this.#i
841
+ });
842
+ }
843
+ #g(e) {
844
+ return () => {
845
+ this.#r && (e(this.#r), this.#r.focus());
846
+ };
847
+ }
848
+ #_ = (e) => {
849
+ let t = e.detail;
850
+ t && (t.value === "preview" || t.value === "source") && (this.#t.mode.value = t.value, this.setAttribute("mode", t.value));
851
+ };
852
+ };
853
+ export { O as a, D as c, X as i, Q as n, Z as o, $ as r, E as s, le as t };
@@ -0,0 +1,67 @@
1
+ import { UIElement } from '@nonoun/native-ui';
2
+ import type { EditorCommands } from './commands.ts';
3
+ import type { ViewMode } from './editor-store.ts';
4
+ /**
5
+ * Markdown content editor with live preview and source modes.
6
+ *
7
+ * Built on CodeMirror 6, renders a toolbar and a rich editing surface.
8
+ * Supports form association, reactive mode switching, and configurable toolbars.
9
+ *
10
+ * @attr {string} value - The markdown string
11
+ * @attr {string} mode - Active view mode: "preview" or "source"
12
+ * @attr {string} toolbar - Comma-separated toolbar button keys
13
+ * @attr {string} placeholder - Placeholder text for empty editor
14
+ * @attr {boolean} readonly - Disable editing
15
+ * @attr {boolean} disabled - Disabled state
16
+ * @attr {string} name - Form field name
17
+ * @attr {string} src - URL to load/save content from
18
+ * @fires change - Fired when document content changes with `{ value }` detail
19
+ * @fires mode-change - Fired when mode is toggled with `{ mode }` detail
20
+ * @fires nui-load - Fired after content is loaded from src
21
+ * @fires nui-save - Fired after content is saved to src
22
+ * @fires nui-error - Fired on load/save error with `{ error }` detail
23
+ * @fires dirty-change - Fired when dirty state changes with `{ dirty }` detail
24
+ */
25
+ export declare class NuiEditor extends UIElement {
26
+ #private;
27
+ static observedAttributes: string[];
28
+ static formAssociated: boolean;
29
+ constructor();
30
+ get value(): string;
31
+ set value(val: string);
32
+ get mode(): ViewMode;
33
+ set mode(val: ViewMode);
34
+ get toolbar(): string[];
35
+ set toolbar(val: string[] | string);
36
+ get placeholder(): string;
37
+ set placeholder(val: string);
38
+ get readOnly(): boolean;
39
+ set readOnly(val: boolean);
40
+ get disabled(): boolean;
41
+ set disabled(val: boolean);
42
+ get name(): string;
43
+ set name(val: string);
44
+ get src(): string | null;
45
+ set src(val: string | null);
46
+ get loading(): boolean;
47
+ get saving(): boolean;
48
+ get dirty(): boolean;
49
+ get error(): string | null;
50
+ /** Proxy to editor commands — each method auto-passes the internal EditorView. */
51
+ get commands(): EditorCommands;
52
+ focus(): void;
53
+ getSelection(): {
54
+ from: number;
55
+ to: number;
56
+ text: string;
57
+ };
58
+ insertText(text: string, position?: number): void;
59
+ /** Save current content to the `src` URL via PUT. */
60
+ save(): Promise<boolean>;
61
+ /** Reload content from the `src` URL. */
62
+ reload(): Promise<void>;
63
+ attributeChangedCallback(name: string, old: string | null, val: string | null): void;
64
+ setup(): void;
65
+ teardown(): void;
66
+ }
67
+ //# sourceMappingURL=nui-editor-element.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nui-editor-element.d.ts","sourceRoot":"","sources":["../src/nui-editor-element.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAA2C,MAAM,mBAAmB,CAAC;AAMvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AA8BlD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,SAAU,SAAQ,SAAS;;IACtC,MAAM,CAAC,kBAAkB,WAAsF;IAC/G,MAAM,CAAC,cAAc,UAAQ;;IAwB7B,IAAI,KAAK,IAAI,MAAM,CAKlB;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,MAAM,EAUpB;IAED,IAAI,IAAI,IAAI,QAAQ,CAEnB;IAED,IAAI,IAAI,CAAC,GAAG,EAAE,QAAQ,EAGrB;IAED,IAAI,OAAO,IAAI,MAAM,EAAE,CAEtB;IAED,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAEjC;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,WAAW,CAAC,GAAG,EAAE,MAAM,EAG1B;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,QAAQ,CAAC,GAAG,EAAE,OAAO,EAGxB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,QAAQ,CAAC,GAAG,EAAE,OAAO,EAGxB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAEnB;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAIzB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAEzB;IAID,kFAAkF;IAClF,IAAI,QAAQ,IAAI,cAAc,CAgC7B;IAEQ,KAAK,IAAI,IAAI;IAItB,YAAY,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAM1D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAQjD,qDAAqD;IAC/C,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAU9B,yCAAyC;IACnC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAc7B,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAyCpF,KAAK,IAAI,IAAI;IAuHb,QAAQ,IAAI,IAAI;CAkLjB"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * nui-editor.css
3
+ * Markdown content editor — toolbar + CodeMirror surface + optional preview pane.
4
+ * Follows NativeUI CSS conventions: @layer ui, :where() selectors, --n-* locals.
5
+ */
6
+
7
+ @layer ui {
8
+
9
+ /* ------------------------------------------------------------------ */
10
+ /* Host element */
11
+ /* ------------------------------------------------------------------ */
12
+
13
+ :where(nui-editor) {
14
+ display: grid;
15
+ grid-template-rows: auto 1fr;
16
+ border: 1px solid var(--n-border-muted-neutral);
17
+ border-radius: var(--n-radius, 0.5rem);
18
+ overflow: hidden;
19
+ min-height: 300px;
20
+ background: #282c34;
21
+ }
22
+
23
+ /* ------------------------------------------------------------------ */
24
+ /* Toolbar */
25
+ /* ------------------------------------------------------------------ */
26
+
27
+ :where(nui-editor) > :where(ui-toolbar) {
28
+ border-bottom: 1px solid color-mix(in oklch, var(--n-border-muted-neutral), transparent 50%);
29
+ background: #21252b;
30
+ min-height: 2.5rem;
31
+ }
32
+
33
+ /* ------------------------------------------------------------------ */
34
+ /* Editor surface */
35
+ /* ------------------------------------------------------------------ */
36
+
37
+ :where(nui-editor) :where(.nui-editor-surface) {
38
+ min-height: 0;
39
+ overflow: hidden;
40
+ }
41
+
42
+ :where(nui-editor) :where(.cm-editor) {
43
+ height: 100%;
44
+ font-size: 0.875rem;
45
+ line-height: 1.6;
46
+ }
47
+
48
+ :where(nui-editor) :where(.cm-scroller) {
49
+ overflow: auto;
50
+ padding: 1rem;
51
+ }
52
+
53
+ :where(nui-editor) :where(.cm-content) {
54
+ padding: 0;
55
+ }
56
+
57
+ :where(nui-editor) :where(.cm-gutters) {
58
+ display: none;
59
+ }
60
+
61
+ /* ------------------------------------------------------------------ */
62
+ /* Live preview — inline decorations */
63
+ /* ------------------------------------------------------------------ */
64
+
65
+ :where(nui-editor) :where(.md-bold) {
66
+ font-weight: 700;
67
+ }
68
+
69
+ :where(nui-editor) :where(.md-italic) {
70
+ font-style: italic;
71
+ }
72
+
73
+ :where(nui-editor) :where(.md-code) {
74
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
75
+ background: color-mix(in oklch, var(--n-panel-neutral, #3e4451), transparent 50%);
76
+ border-radius: 0.1875rem;
77
+ padding: 0.0625rem 0.25rem;
78
+ }
79
+
80
+ :where(nui-editor) :where(.md-strikethrough) {
81
+ text-decoration: line-through;
82
+ }
83
+
84
+ :where(nui-editor) :where(.md-link) {
85
+ color: #61afef;
86
+ text-decoration: underline;
87
+ }
88
+
89
+ :where(nui-editor) :where(.md-url) {
90
+ opacity: 0.4;
91
+ font-size: 0.85em;
92
+ }
93
+
94
+ /* ------------------------------------------------------------------ */
95
+ /* Live preview — syntax markers */
96
+ /* ------------------------------------------------------------------ */
97
+
98
+ :where(nui-editor) :where(.md-syntax-marker) {
99
+ opacity: 0.3;
100
+ }
101
+
102
+ :where(nui-editor) :where(.md-syntax-marker-visible) {
103
+ opacity: 0.6;
104
+ }
105
+
106
+ /* ------------------------------------------------------------------ */
107
+ /* Live preview — headings */
108
+ /* ------------------------------------------------------------------ */
109
+
110
+ :where(nui-editor) :where(.md-heading) {
111
+ font-weight: 700;
112
+ }
113
+
114
+ :where(nui-editor) :where(.md-heading-1) {
115
+ font-size: 1.5em;
116
+ }
117
+
118
+ :where(nui-editor) :where(.md-heading-2) {
119
+ font-size: 1.25em;
120
+ }
121
+
122
+ :where(nui-editor) :where(.md-heading-3) {
123
+ font-size: 1.1em;
124
+ }
125
+
126
+ /* ------------------------------------------------------------------ */
127
+ /* Live preview — lists */
128
+ /* ------------------------------------------------------------------ */
129
+
130
+ :where(nui-editor) :where(.md-list-item) {
131
+ padding-left: 0.25em;
132
+ }
133
+
134
+ /* ------------------------------------------------------------------ */
135
+ /* Live preview — blockquote */
136
+ /* ------------------------------------------------------------------ */
137
+
138
+ :where(nui-editor) :where(.md-blockquote) {
139
+ border-left: 3px solid #61afef;
140
+ padding-left: 1em;
141
+ opacity: 0.85;
142
+ }
143
+
144
+ /* ------------------------------------------------------------------ */
145
+ /* Live preview — code blocks */
146
+ /* ------------------------------------------------------------------ */
147
+
148
+ :where(nui-editor) :where(.md-code-block) {
149
+ background: color-mix(in oklch, #1e2127, transparent 30%);
150
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
151
+ font-size: 0.8125rem;
152
+ }
153
+
154
+ /* ------------------------------------------------------------------ */
155
+ /* Live preview — horizontal rule */
156
+ /* ------------------------------------------------------------------ */
157
+
158
+ :where(nui-editor) :where(.md-hr) {
159
+ border-bottom: 1px solid color-mix(in oklch, var(--n-border-muted-neutral), transparent 30%);
160
+ margin: 0.5em 0;
161
+ }
162
+
163
+ /* ------------------------------------------------------------------ */
164
+ /* Live preview — image widget */
165
+ /* ------------------------------------------------------------------ */
166
+
167
+ :where(nui-editor) :where(.md-image) {
168
+ max-width: 100%;
169
+ border-radius: 0.375rem;
170
+ margin: 0.5em 0;
171
+ }
172
+
173
+ /* ------------------------------------------------------------------ */
174
+ /* States */
175
+ /* ------------------------------------------------------------------ */
176
+
177
+ :where(nui-editor[readonly]) :where(.cm-content) {
178
+ cursor: default;
179
+ }
180
+
181
+ :where(nui-editor)[aria-disabled="true"] {
182
+ opacity: 0.5;
183
+ pointer-events: none;
184
+ }
185
+
186
+ /* ------------------------------------------------------------------ */
187
+ /* Placeholder */
188
+ /* ------------------------------------------------------------------ */
189
+
190
+ :where(nui-editor) :where(.cm-placeholder) {
191
+ color: #7d8799;
192
+ font-style: italic;
193
+ }
194
+
195
+ }
@@ -0,0 +1,2 @@
1
+ import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./nui-editor-element-S_Z8i0qQ.js";
2
+ export { r as DEFAULT_TOOLBAR, a as EditorStore, s as NuiEditor, o as commands, n as createPreviewExtension, t as markdownKeymap, e as previewCompartment, i as sourceExtension };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Register nui-editor
3
+ *
4
+ * Side-effect module that registers the <nui-editor> custom element.
5
+ *
6
+ * Usage:
7
+ * import '@nonoun/native-editor/register';
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,11 @@
1
+ import { t as e } from "./nui-editor-element-S_Z8i0qQ.js";
2
+ import { define as t } from "@nonoun/native-ui";
3
+ /**
4
+ * Register nui-editor
5
+ *
6
+ * Side-effect module that registers the <nui-editor> custom element.
7
+ *
8
+ * Usage:
9
+ * import '@nonoun/nui-editor/register';
10
+ */
11
+ t("nui-editor", e);
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@nonoun/native-editor",
3
+ "version": "0.2.0",
4
+ "description": "Markdown content editor with live preview for @nonoun/native-ui",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/native-editor.js",
8
+ "module": "./dist/native-editor.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/native-editor.js"
14
+ },
15
+ "./register": {
16
+ "types": "./dist/register.d.ts",
17
+ "default": "./dist/register.js"
18
+ },
19
+ "./css": "./dist/native-editor.css"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "sideEffects": [
25
+ "./dist/register.js"
26
+ ],
27
+ "peerDependencies": {
28
+ "@nonoun/native-ui": ">=0.5.0"
29
+ },
30
+ "dependencies": {
31
+ "@nonoun/native-codemirror": "^0.1.0",
32
+ "@codemirror/lang-markdown": "^6.3.2",
33
+ "@lezer/markdown": "^1.3.2"
34
+ },
35
+ "scripts": {
36
+ "build": "npm run build:js && npm run build:css && npm run build:types",
37
+ "build:js": "vite build",
38
+ "build:css": "cp src/css/native-editor.css dist/native-editor.css",
39
+ "build:types": "tsc -p tsconfig.build.json"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }