@duskmoon-dev/el-code-engine 1.1.3 → 1.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/dist/esm/index.js +329 -3
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +329 -3
- package/dist/esm/register.js.map +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-code-engine.d.ts +29 -0
- package/dist/types/el-dm-code-engine.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { BaseElement, css } from "@duskmoon-dev/el-base";
|
|
|
11
11
|
import { EditorView } from "@duskmoon-dev/code-engine/view";
|
|
12
12
|
import { EditorState, Compartment } from "@duskmoon-dev/code-engine/state";
|
|
13
13
|
import { basicSetup } from "@duskmoon-dev/code-engine/setup";
|
|
14
|
+
import { undo, redo } from "@duskmoon-dev/code-engine/commands";
|
|
14
15
|
import * as _duskmoonTheme from "@duskmoon-dev/code-engine/theme/duskmoon";
|
|
15
16
|
import * as _sunshineTheme from "@duskmoon-dev/code-engine/theme/sunshine";
|
|
16
17
|
import * as _moonlightTheme from "@duskmoon-dev/code-engine/theme/moonlight";
|
|
@@ -70,6 +71,75 @@ var LANG_LOADERS = {
|
|
|
70
71
|
lezer: langLoader(() => import("@duskmoon-dev/code-engine/lang/lezer")),
|
|
71
72
|
caddyfile: langLoader(() => import("@duskmoon-dev/code-engine/lang/caddyfile"))
|
|
72
73
|
};
|
|
74
|
+
var LANG_BADGES = {
|
|
75
|
+
javascript: "JS",
|
|
76
|
+
typescript: "TS",
|
|
77
|
+
python: "PY",
|
|
78
|
+
rust: "RS",
|
|
79
|
+
go: "GO",
|
|
80
|
+
java: "JAVA",
|
|
81
|
+
cpp: "C++",
|
|
82
|
+
html: "HTML",
|
|
83
|
+
css: "CSS",
|
|
84
|
+
json: "JSON",
|
|
85
|
+
markdown: "MD",
|
|
86
|
+
sql: "SQL",
|
|
87
|
+
yaml: "YAML",
|
|
88
|
+
xml: "XML",
|
|
89
|
+
php: "PHP",
|
|
90
|
+
elixir: "EX",
|
|
91
|
+
erlang: "ERL",
|
|
92
|
+
heex: "HEEX",
|
|
93
|
+
dart: "DART",
|
|
94
|
+
zig: "ZIG",
|
|
95
|
+
vue: "VUE",
|
|
96
|
+
angular: "NG",
|
|
97
|
+
sass: "SASS",
|
|
98
|
+
less: "LESS",
|
|
99
|
+
wast: "WAST",
|
|
100
|
+
lezer: "LEZER",
|
|
101
|
+
caddyfile: "CADDY",
|
|
102
|
+
jinja: "JINJA",
|
|
103
|
+
liquid: "LIQUID"
|
|
104
|
+
};
|
|
105
|
+
var LANG_NAMES = {
|
|
106
|
+
javascript: "JavaScript",
|
|
107
|
+
typescript: "TypeScript",
|
|
108
|
+
python: "Python",
|
|
109
|
+
rust: "Rust",
|
|
110
|
+
go: "Go",
|
|
111
|
+
java: "Java",
|
|
112
|
+
cpp: "C++",
|
|
113
|
+
html: "HTML",
|
|
114
|
+
css: "CSS",
|
|
115
|
+
json: "JSON",
|
|
116
|
+
markdown: "Markdown",
|
|
117
|
+
sql: "SQL",
|
|
118
|
+
yaml: "YAML",
|
|
119
|
+
xml: "XML",
|
|
120
|
+
php: "PHP",
|
|
121
|
+
elixir: "Elixir",
|
|
122
|
+
erlang: "Erlang",
|
|
123
|
+
heex: "HEEx",
|
|
124
|
+
dart: "Dart",
|
|
125
|
+
zig: "Zig",
|
|
126
|
+
vue: "Vue",
|
|
127
|
+
angular: "Angular",
|
|
128
|
+
sass: "Sass",
|
|
129
|
+
less: "Less",
|
|
130
|
+
wast: "WAT",
|
|
131
|
+
lezer: "Lezer",
|
|
132
|
+
caddyfile: "Caddyfile",
|
|
133
|
+
jinja: "Jinja",
|
|
134
|
+
liquid: "Liquid"
|
|
135
|
+
};
|
|
136
|
+
var ICON_UNDO = `<svg viewBox="0 0 24 24"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>`;
|
|
137
|
+
var ICON_REDO = `<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>`;
|
|
138
|
+
var ICON_WRAP = `<svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><path d="M3 12h15a3 3 0 1 1 0 6h-4"/><polyline points="16 16 14 18 16 20"/><line x1="3" y1="18" x2="10" y2="18"/></svg>`;
|
|
139
|
+
var ICON_COPY = `<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
|
140
|
+
var ICON_CHECK = `<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>`;
|
|
141
|
+
var ICON_FULLSCREEN = `<svg viewBox="0 0 24 24"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>`;
|
|
142
|
+
var ICON_EXIT_FULLSCREEN = `<svg viewBox="0 0 24 24"><polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/><line x1="14" y1="10" x2="21" y2="3"/><line x1="3" y1="21" x2="10" y2="14"/></svg>`;
|
|
73
143
|
var styles = css`
|
|
74
144
|
:host {
|
|
75
145
|
display: block;
|
|
@@ -101,6 +171,129 @@ var styles = css`
|
|
|
101
171
|
.cm-host .cm-editor.cm-focused {
|
|
102
172
|
outline: none;
|
|
103
173
|
}
|
|
174
|
+
|
|
175
|
+
/* ── Topbar ────────────────────────────────────────── */
|
|
176
|
+
|
|
177
|
+
.topbar {
|
|
178
|
+
display: none;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
:host([show-topbar]) .topbar {
|
|
182
|
+
display: flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
justify-content: space-between;
|
|
185
|
+
gap: 0.5rem;
|
|
186
|
+
padding: 0.375rem 0.75rem;
|
|
187
|
+
border-bottom: 1px solid var(--dm-border, #e0e0e0);
|
|
188
|
+
background: var(--dm-surface-container, #f0f0f0);
|
|
189
|
+
font-size: 0.75rem;
|
|
190
|
+
color: var(--dm-on-surface, #1a1a1a);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.topbar-left {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 0.5rem;
|
|
197
|
+
min-width: 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.topbar-right {
|
|
201
|
+
display: flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
gap: 0.25rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.lang-badge {
|
|
207
|
+
padding: 0.0625rem 0.375rem;
|
|
208
|
+
border-radius: var(--dm-radius-sm, 0.25rem);
|
|
209
|
+
background: var(--dm-primary, #6750a4);
|
|
210
|
+
color: var(--dm-on-primary, #fff);
|
|
211
|
+
font-size: 0.625rem;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
text-transform: uppercase;
|
|
214
|
+
letter-spacing: 0.025em;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.topbar-title {
|
|
218
|
+
opacity: 0.7;
|
|
219
|
+
font-size: 0.6875rem;
|
|
220
|
+
white-space: nowrap;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
text-overflow: ellipsis;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.bar-btn {
|
|
226
|
+
display: inline-flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
width: 1.5rem;
|
|
230
|
+
height: 1.5rem;
|
|
231
|
+
padding: 0;
|
|
232
|
+
border: none;
|
|
233
|
+
border-radius: var(--dm-radius-sm, 0.25rem);
|
|
234
|
+
background: transparent;
|
|
235
|
+
color: var(--dm-on-surface-variant, #555);
|
|
236
|
+
font-family: inherit;
|
|
237
|
+
font-size: 0.75rem;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
transition:
|
|
240
|
+
background 0.15s,
|
|
241
|
+
color 0.15s;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.bar-btn:hover {
|
|
245
|
+
background: var(--dm-surface-container-high, #e0e0e0);
|
|
246
|
+
color: var(--dm-on-surface, #1a1a1a);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.bar-btn svg {
|
|
250
|
+
width: 14px;
|
|
251
|
+
height: 14px;
|
|
252
|
+
fill: none;
|
|
253
|
+
stroke: currentColor;
|
|
254
|
+
stroke-width: 2;
|
|
255
|
+
stroke-linecap: round;
|
|
256
|
+
stroke-linejoin: round;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ── Bottombar ─────────────────────────────────────── */
|
|
260
|
+
|
|
261
|
+
.bottombar {
|
|
262
|
+
display: none;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
:host([show-bottombar]) .bottombar {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
justify-content: space-between;
|
|
269
|
+
padding: 0.25rem 0.75rem;
|
|
270
|
+
border-top: 1px solid var(--dm-border, #e0e0e0);
|
|
271
|
+
background: var(--dm-surface-container-high, #e0e0e0);
|
|
272
|
+
font-size: 0.625rem;
|
|
273
|
+
color: var(--dm-on-surface-variant, #555);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.bottombar-left,
|
|
277
|
+
.bottombar-right {
|
|
278
|
+
display: flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: 0.75rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* ── Fullscreen ────────────────────────────────────── */
|
|
284
|
+
|
|
285
|
+
:host(.fullscreen) {
|
|
286
|
+
position: fixed !important;
|
|
287
|
+
inset: 0 !important;
|
|
288
|
+
z-index: 9999 !important;
|
|
289
|
+
min-height: 100vh !important;
|
|
290
|
+
display: flex;
|
|
291
|
+
flex-direction: column;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
:host(.fullscreen) .cm-host {
|
|
295
|
+
flex: 1;
|
|
296
|
+
}
|
|
104
297
|
`;
|
|
105
298
|
|
|
106
299
|
class ElDmCodeEngine extends BaseElement {
|
|
@@ -108,7 +301,10 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
108
301
|
language: { type: String, reflect: true },
|
|
109
302
|
readonly: { type: Boolean, reflect: true },
|
|
110
303
|
theme: { type: String, reflect: true, default: "duskmoon" },
|
|
111
|
-
wrap: { type: Boolean, reflect: true }
|
|
304
|
+
wrap: { type: Boolean, reflect: true },
|
|
305
|
+
showTopbar: { type: Boolean, reflect: true },
|
|
306
|
+
showBottombar: { type: Boolean, reflect: true },
|
|
307
|
+
title: { type: String, reflect: true }
|
|
112
308
|
};
|
|
113
309
|
#editor = null;
|
|
114
310
|
#pendingValue = null;
|
|
@@ -117,6 +313,11 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
117
313
|
#themeCompartment = new Compartment;
|
|
118
314
|
#wrapCompartment = new Compartment;
|
|
119
315
|
#langCache = new Map;
|
|
316
|
+
#cursorLine = 1;
|
|
317
|
+
#cursorCol = 1;
|
|
318
|
+
#lineCount = 0;
|
|
319
|
+
#isFullscreen = false;
|
|
320
|
+
#copyTimer = null;
|
|
120
321
|
constructor() {
|
|
121
322
|
super();
|
|
122
323
|
this.attachStyles(styles);
|
|
@@ -143,7 +344,7 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
143
344
|
this.value = v;
|
|
144
345
|
}
|
|
145
346
|
render() {
|
|
146
|
-
return
|
|
347
|
+
return `${this.#renderTopbar()}<div class="cm-host" part="editor"></div>${this.#renderBottombar()}`;
|
|
147
348
|
}
|
|
148
349
|
update() {
|
|
149
350
|
if (!this.#editor) {
|
|
@@ -153,7 +354,24 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
153
354
|
this.#applyConfig();
|
|
154
355
|
}
|
|
155
356
|
}
|
|
357
|
+
#clickHandler = (e) => {
|
|
358
|
+
const btn = e.target?.closest("[data-action]");
|
|
359
|
+
if (btn) {
|
|
360
|
+
const action = btn.getAttribute("data-action");
|
|
361
|
+
if (action)
|
|
362
|
+
this.#handleBarAction(action);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
connectedCallback() {
|
|
366
|
+
super.connectedCallback();
|
|
367
|
+
this.shadowRoot.addEventListener("click", this.#clickHandler);
|
|
368
|
+
}
|
|
156
369
|
disconnectedCallback() {
|
|
370
|
+
this.shadowRoot.removeEventListener("click", this.#clickHandler);
|
|
371
|
+
if (this.#copyTimer) {
|
|
372
|
+
clearTimeout(this.#copyTimer);
|
|
373
|
+
this.#copyTimer = null;
|
|
374
|
+
}
|
|
157
375
|
if (this.#editor) {
|
|
158
376
|
this.#pendingValue = this.#editor.state.doc.toString();
|
|
159
377
|
this.#editor.destroy();
|
|
@@ -161,6 +379,110 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
161
379
|
}
|
|
162
380
|
super.disconnectedCallback();
|
|
163
381
|
}
|
|
382
|
+
#renderTopbar() {
|
|
383
|
+
const badge = this.language ? `<span class="lang-badge">${LANG_BADGES[this.language] ?? this.language.toUpperCase()}</span>` : "";
|
|
384
|
+
const t = this.title;
|
|
385
|
+
const title = t ? `<span class="topbar-title">${t}</span>` : "";
|
|
386
|
+
return `
|
|
387
|
+
<div class="topbar" part="topbar">
|
|
388
|
+
<slot name="topbar">
|
|
389
|
+
<div class="topbar-left">${badge}${title}</div>
|
|
390
|
+
<div class="topbar-right">
|
|
391
|
+
<button class="bar-btn" data-action="undo" title="Undo">${ICON_UNDO}</button>
|
|
392
|
+
<button class="bar-btn" data-action="redo" title="Redo">${ICON_REDO}</button>
|
|
393
|
+
<button class="bar-btn" data-action="wrap" title="Toggle line wrap">${ICON_WRAP}</button>
|
|
394
|
+
<button class="bar-btn" data-action="copy" title="Copy">${ICON_COPY}</button>
|
|
395
|
+
<button class="bar-btn" data-action="fullscreen" title="Toggle fullscreen">${this.#isFullscreen ? ICON_EXIT_FULLSCREEN : ICON_FULLSCREEN}</button>
|
|
396
|
+
</div>
|
|
397
|
+
</slot>
|
|
398
|
+
</div>
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
#renderBottombar() {
|
|
402
|
+
const langName = this.language ? LANG_NAMES[this.language] ?? this.language : "";
|
|
403
|
+
return `
|
|
404
|
+
<div class="bottombar" part="bottombar">
|
|
405
|
+
<slot name="bottombar">
|
|
406
|
+
<div class="bottombar-left">
|
|
407
|
+
<span class="cursor-pos">Ln ${this.#cursorLine}, Col ${this.#cursorCol}</span>
|
|
408
|
+
<span class="line-count">${this.#lineCount} lines</span>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="bottombar-right">
|
|
411
|
+
<span>UTF-8</span>
|
|
412
|
+
${langName ? `<span>${langName}</span>` : ""}
|
|
413
|
+
</div>
|
|
414
|
+
</slot>
|
|
415
|
+
</div>
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
#handleBarAction(action) {
|
|
419
|
+
switch (action) {
|
|
420
|
+
case "undo":
|
|
421
|
+
if (this.#editor)
|
|
422
|
+
undo(this.#editor);
|
|
423
|
+
break;
|
|
424
|
+
case "redo":
|
|
425
|
+
if (this.#editor)
|
|
426
|
+
redo(this.#editor);
|
|
427
|
+
break;
|
|
428
|
+
case "wrap":
|
|
429
|
+
this.wrap = !this.wrap;
|
|
430
|
+
break;
|
|
431
|
+
case "copy":
|
|
432
|
+
this.#handleCopy();
|
|
433
|
+
break;
|
|
434
|
+
case "fullscreen":
|
|
435
|
+
this.#toggleFullscreen();
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async#handleCopy() {
|
|
440
|
+
const value = this.value;
|
|
441
|
+
try {
|
|
442
|
+
await navigator.clipboard.writeText(value);
|
|
443
|
+
} catch {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
this.emit("copy", { value });
|
|
447
|
+
this.#showCopyFeedback();
|
|
448
|
+
}
|
|
449
|
+
#showCopyFeedback() {
|
|
450
|
+
const btn = this.shadowRoot?.querySelector('[data-action="copy"]');
|
|
451
|
+
if (!btn)
|
|
452
|
+
return;
|
|
453
|
+
if (this.#copyTimer)
|
|
454
|
+
clearTimeout(this.#copyTimer);
|
|
455
|
+
btn.innerHTML = ICON_CHECK;
|
|
456
|
+
btn.setAttribute("title", "Copied!");
|
|
457
|
+
this.#copyTimer = setTimeout(() => {
|
|
458
|
+
btn.innerHTML = ICON_COPY;
|
|
459
|
+
btn.setAttribute("title", "Copy");
|
|
460
|
+
this.#copyTimer = null;
|
|
461
|
+
}, 2000);
|
|
462
|
+
}
|
|
463
|
+
#toggleFullscreen() {
|
|
464
|
+
this.#isFullscreen = !this.#isFullscreen;
|
|
465
|
+
this.classList.toggle("fullscreen", this.#isFullscreen);
|
|
466
|
+
const btn = this.shadowRoot?.querySelector('[data-action="fullscreen"]');
|
|
467
|
+
if (btn) {
|
|
468
|
+
btn.innerHTML = this.#isFullscreen ? ICON_EXIT_FULLSCREEN : ICON_FULLSCREEN;
|
|
469
|
+
btn.setAttribute("title", this.#isFullscreen ? "Exit fullscreen" : "Toggle fullscreen");
|
|
470
|
+
}
|
|
471
|
+
this.emit("fullscreen", { active: this.#isFullscreen });
|
|
472
|
+
}
|
|
473
|
+
#updateCursorInfo(state) {
|
|
474
|
+
const pos = state.selection.main.head;
|
|
475
|
+
const line = state.doc.lineAt(pos);
|
|
476
|
+
this.#cursorLine = line.number;
|
|
477
|
+
this.#cursorCol = pos - line.from + 1;
|
|
478
|
+
this.#lineCount = state.doc.lines;
|
|
479
|
+
const cursorEl = this.shadowRoot?.querySelector(".cursor-pos");
|
|
480
|
+
const lineCountEl = this.shadowRoot?.querySelector(".line-count");
|
|
481
|
+
if (cursorEl)
|
|
482
|
+
cursorEl.textContent = `Ln ${this.#cursorLine}, Col ${this.#cursorCol}`;
|
|
483
|
+
if (lineCountEl)
|
|
484
|
+
lineCountEl.textContent = `${this.#lineCount} lines`;
|
|
485
|
+
}
|
|
164
486
|
async#mountEditor() {
|
|
165
487
|
const host = this.shadowRoot?.querySelector(".cm-host");
|
|
166
488
|
if (!host || this.#editor)
|
|
@@ -181,6 +503,9 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
181
503
|
if (update.docChanged) {
|
|
182
504
|
this.emit("input", { value: update.state.doc.toString() });
|
|
183
505
|
}
|
|
506
|
+
if (update.docChanged || update.selectionSet) {
|
|
507
|
+
this.#updateCursorInfo(update.state);
|
|
508
|
+
}
|
|
184
509
|
}),
|
|
185
510
|
EditorView.domEventHandlers({
|
|
186
511
|
blur: () => {
|
|
@@ -193,6 +518,7 @@ class ElDmCodeEngine extends BaseElement {
|
|
|
193
518
|
parent: host,
|
|
194
519
|
root: this.shadowRoot
|
|
195
520
|
});
|
|
521
|
+
this.#updateCursorInfo(this.#editor.state);
|
|
196
522
|
}
|
|
197
523
|
async#applyConfig() {
|
|
198
524
|
if (!this.#editor)
|
|
@@ -239,5 +565,5 @@ export {
|
|
|
239
565
|
ElDmCodeEngine
|
|
240
566
|
};
|
|
241
567
|
|
|
242
|
-
//# debugId=
|
|
568
|
+
//# debugId=6C8C69AE44633C3064756E2164756E21
|
|
243
569
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/el-dm-code-engine.ts", "../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Code Engine Element\n *\n * A lightweight code editor backed by @duskmoon-dev/code-engine (CodeMirror 6 fork).\n * Behaves like a native <input> or <textarea>: the `value` attribute sets the initial\n * content, the `value` property always reflects the current content, and the element\n * fires `input` on every change and `change` on blur.\n *\n * @element el-dm-code-engine\n *\n * @attr {string} value - Initial editor content (read once at mount, like <input>)\n * @attr {string} language - Language name for syntax highlighting (e.g. \"javascript\", \"css\")\n * @attr {boolean} readonly - Whether the editor is read-only\n * @attr {string} theme - Editor theme: \"duskmoon\" | \"sunshine\" | \"moonlight\" | \"one-dark\"\n * @attr {boolean} wrap - Enable line wrapping\n *\n * @prop {string} value - Gets or sets current editor content\n *\n * @method focus() - Focuses the editor\n * @method getValue() - Returns current editor content\n * @method setValue(value: string) - Sets editor content programmatically\n *\n * @fires input - Fired on every document change, detail: { value: string }\n * @fires change - Fired when editor loses focus, detail: { value: string }\n *\n * @csspart editor - The CodeMirror mount container\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-base';\nimport type { Extension } from '@duskmoon-dev/code-engine';\nimport { EditorView } from '@duskmoon-dev/code-engine/view';\nimport { EditorState, Compartment } from '@duskmoon-dev/code-engine/state';\nimport { basicSetup } from '@duskmoon-dev/code-engine/setup';\n\n// ── Static theme imports (all 4 are small; avoids runtime bare-specifier issues) ──\nimport * as _duskmoonTheme from '@duskmoon-dev/code-engine/theme/duskmoon';\nimport * as _sunshineTheme from '@duskmoon-dev/code-engine/theme/sunshine';\nimport * as _moonlightTheme from '@duskmoon-dev/code-engine/theme/moonlight';\nimport * as _oneDarkTheme from '@duskmoon-dev/code-engine/theme/one-dark';\n\nfunction extractExt(mod: Record<string, unknown>): Extension {\n const v =\n mod.default ??\n Object.values(mod).find((x) => typeof x === 'function' || (x && typeof x !== 'string'));\n // Theme modules export a factory function (e.g. duskMoon(options?))\n if (typeof v === 'function') return (v as () => Extension)() as Extension;\n return (v ?? []) as Extension;\n}\n\nconst THEMES: Record<string, Extension> = {\n duskmoon: extractExt(_duskmoonTheme as unknown as Record<string, unknown>),\n sunshine: extractExt(_sunshineTheme as unknown as Record<string, unknown>),\n moonlight: extractExt(_moonlightTheme as unknown as Record<string, unknown>),\n 'one-dark': extractExt(_oneDarkTheme as unknown as Record<string, unknown>),\n};\n\n// ── Language loaders (literal import paths so bundlers can resolve them) ──\n\nfunction langLoader(\n importFn: () => Promise<Record<string, unknown>>,\n opts?: Record<string, unknown>,\n): () => Promise<Extension | null> {\n return async () => {\n const mod = await importFn();\n const factory = mod.default ?? Object.values(mod).find((v) => typeof v === 'function');\n if (typeof factory === 'function') {\n return (opts ? factory(opts) : factory()) as Extension;\n }\n return factory as Extension | null;\n };\n}\n\nconst LANG_LOADERS: Record<string, () => Promise<Extension | null>> = {\n javascript: langLoader(() => import('@duskmoon-dev/code-engine/lang/javascript')),\n typescript: langLoader(() => import('@duskmoon-dev/code-engine/lang/javascript'), {\n typescript: true,\n }),\n css: langLoader(() => import('@duskmoon-dev/code-engine/lang/css')),\n html: langLoader(() => import('@duskmoon-dev/code-engine/lang/html')),\n json: langLoader(() => import('@duskmoon-dev/code-engine/lang/json')),\n python: langLoader(() => import('@duskmoon-dev/code-engine/lang/python')),\n markdown: langLoader(() => import('@duskmoon-dev/code-engine/lang/markdown')),\n xml: langLoader(() => import('@duskmoon-dev/code-engine/lang/xml')),\n sql: langLoader(() => import('@duskmoon-dev/code-engine/lang/sql')),\n rust: langLoader(() => import('@duskmoon-dev/code-engine/lang/rust')),\n go: langLoader(() => import('@duskmoon-dev/code-engine/lang/go')),\n java: langLoader(() => import('@duskmoon-dev/code-engine/lang/java')),\n cpp: langLoader(() => import('@duskmoon-dev/code-engine/lang/cpp')),\n php: langLoader(() => import('@duskmoon-dev/code-engine/lang/php')),\n yaml: langLoader(() => import('@duskmoon-dev/code-engine/lang/yaml')),\n sass: langLoader(() => import('@duskmoon-dev/code-engine/lang/sass')),\n less: langLoader(() => import('@duskmoon-dev/code-engine/lang/less')),\n elixir: langLoader(() => import('@duskmoon-dev/code-engine/lang/elixir')),\n erlang: langLoader(() => import('@duskmoon-dev/code-engine/lang/erlang')),\n heex: langLoader(() => import('@duskmoon-dev/code-engine/lang/heex')),\n dart: langLoader(() => import('@duskmoon-dev/code-engine/lang/dart')),\n zig: langLoader(() => import('@duskmoon-dev/code-engine/lang/zig')),\n vue: langLoader(() => import('@duskmoon-dev/code-engine/lang/vue')),\n angular: langLoader(() => import('@duskmoon-dev/code-engine/lang/angular')),\n liquid: langLoader(() => import('@duskmoon-dev/code-engine/lang/liquid')),\n jinja: langLoader(() => import('@duskmoon-dev/code-engine/lang/jinja')),\n wast: langLoader(() => import('@duskmoon-dev/code-engine/lang/wast')),\n lezer: langLoader(() => import('@duskmoon-dev/code-engine/lang/lezer')),\n caddyfile: langLoader(() => import('@duskmoon-dev/code-engine/lang/caddyfile')),\n};\nexport type CodeEngineTheme = 'duskmoon' | 'sunshine' | 'moonlight' | 'one-dark';\n\nconst styles = css`\n :host {\n display: block;\n min-height: 200px;\n font-family: var(\n --dm-font-mono,\n ui-monospace,\n 'Cascadia Code',\n 'Source Code Pro',\n Menlo,\n Consolas,\n 'DejaVu Sans Mono',\n monospace\n );\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .cm-host {\n height: 100%;\n }\n\n .cm-host .cm-editor {\n height: 100%;\n }\n\n .cm-host .cm-editor.cm-focused {\n outline: none;\n }\n`;\n\nexport class ElDmCodeEngine extends BaseElement {\n static properties = {\n language: { type: String, reflect: true },\n readonly: { type: Boolean, reflect: true },\n theme: { type: String, reflect: true, default: 'duskmoon' },\n wrap: { type: Boolean, reflect: true },\n };\n\n declare language: string;\n declare readonly: boolean;\n declare theme: CodeEngineTheme;\n declare wrap: boolean;\n\n #editor: EditorView | null = null;\n #pendingValue: string | null = null;\n\n readonly #languageCompartment = new Compartment();\n readonly #readonlyCompartment = new Compartment();\n readonly #themeCompartment = new Compartment();\n readonly #wrapCompartment = new Compartment();\n\n readonly #langCache = new Map<string, Extension | null>();\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n /**\n * Current editor content. Getter always returns live document text.\n * Setter updates the editor document (or queues the value if called before mount).\n */\n get value(): string {\n return (\n this.#editor?.state.doc.toString() ?? this.#pendingValue ?? this.getAttribute('value') ?? ''\n );\n }\n\n set value(v: string) {\n if (this.#editor) {\n this.#editor.dispatch({\n changes: { from: 0, to: this.#editor.state.doc.length, insert: v },\n });\n } else {\n this.#pendingValue = v;\n }\n }\n\n /** Focus the editor. */\n focus(): void {\n this.#editor?.focus();\n }\n\n /** Returns current editor content. Equivalent to reading the `value` property. */\n getValue(): string {\n return this.value;\n }\n\n /** Sets editor content programmatically. Equivalent to assigning the `value` property. */\n setValue(v: string): void {\n this.value = v;\n }\n\n protected render(): string {\n return '<div class=\"cm-host\" part=\"editor\"></div>';\n }\n\n protected update(): void {\n if (!this.#editor) {\n // First render: create the container then mount the editor.\n super.update();\n void this.#mountEditor();\n } else {\n // Editor already mounted: reconfigure via compartment transactions.\n void this.#applyConfig();\n }\n }\n\n disconnectedCallback(): void {\n // Save current content so it survives a DOM move/reconnect.\n if (this.#editor) {\n this.#pendingValue = this.#editor.state.doc.toString();\n this.#editor.destroy();\n this.#editor = null;\n }\n super.disconnectedCallback();\n }\n\n // ── Private helpers ──────────────────────────────────────────────────\n\n async #mountEditor(): Promise<void> {\n const host = this.shadowRoot?.querySelector('.cm-host');\n if (!host || this.#editor) return;\n\n const initialDoc = this.#pendingValue ?? this.getAttribute('value') ?? '';\n this.#pendingValue = null;\n\n const langExt = await this.#loadLanguage(this.language);\n\n this.#editor = new EditorView({\n state: EditorState.create({\n doc: initialDoc,\n extensions: [\n basicSetup,\n this.#languageCompartment.of(langExt ?? []),\n this.#readonlyCompartment.of(EditorState.readOnly.of(this.readonly ?? false)),\n this.#themeCompartment.of(THEMES[this.theme] ?? []),\n this.#wrapCompartment.of(this.wrap ? EditorView.lineWrapping : []),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n this.emit('input', { value: update.state.doc.toString() });\n }\n }),\n EditorView.domEventHandlers({\n blur: () => {\n this.emit('change', { value: this.#editor!.state.doc.toString() });\n return false;\n },\n }),\n ],\n }),\n parent: host,\n root: this.shadowRoot,\n });\n }\n\n async #applyConfig(): Promise<void> {\n if (!this.#editor) return;\n\n const langExt = await this.#loadLanguage(this.language);\n\n this.#editor.dispatch({\n effects: [\n this.#languageCompartment.reconfigure(langExt ?? []),\n this.#readonlyCompartment.reconfigure(EditorState.readOnly.of(this.readonly ?? false)),\n this.#themeCompartment.reconfigure(THEMES[this.theme] ?? []),\n this.#wrapCompartment.reconfigure(this.wrap ? EditorView.lineWrapping : []),\n ],\n });\n }\n\n async #loadLanguage(name: string): Promise<Extension | null> {\n if (!name) return null;\n if (this.#langCache.has(name)) return this.#langCache.get(name)!;\n\n const loader = LANG_LOADERS[name];\n if (!loader) {\n this.#langCache.set(name, null);\n return null;\n }\n\n try {\n const ext = await loader();\n this.#langCache.set(name, ext);\n return ext;\n } catch {\n this.#langCache.set(name, null);\n return null;\n }\n }\n}\n",
|
|
5
|
+
"/**\n * DuskMoon Code Engine Element\n *\n * A lightweight code editor backed by @duskmoon-dev/code-engine (CodeMirror 6 fork).\n * Behaves like a native <input> or <textarea>: the `value` attribute sets the initial\n * content, the `value` property always reflects the current content, and the element\n * fires `input` on every change and `change` on blur.\n *\n * @element el-dm-code-engine\n *\n * @attr {string} value - Initial editor content (read once at mount, like <input>)\n * @attr {string} language - Language name for syntax highlighting (e.g. \"javascript\", \"css\")\n * @attr {boolean} readonly - Whether the editor is read-only\n * @attr {string} theme - Editor theme: \"duskmoon\" | \"sunshine\" | \"moonlight\" | \"one-dark\"\n * @attr {boolean} wrap - Enable line wrapping\n *\n * @prop {string} value - Gets or sets current editor content\n *\n * @method focus() - Focuses the editor\n * @method getValue() - Returns current editor content\n * @method setValue(value: string) - Sets editor content programmatically\n *\n * @fires input - Fired on every document change, detail: { value: string }\n * @fires change - Fired when editor loses focus, detail: { value: string }\n *\n * @csspart editor - The CodeMirror mount container\n *\n * @attr {boolean} show-topbar - Show the topbar\n * @attr {boolean} show-bottombar - Show the bottombar\n * @attr {string} title - Title shown in topbar (e.g. filename)\n *\n * @fires copy - Fired when copy button is clicked, detail: { value: string }\n * @fires fullscreen - Fired when fullscreen is toggled, detail: { active: boolean }\n *\n * @csspart topbar - The topbar container\n * @csspart bottombar - The bottombar container\n *\n * @slot topbar - Custom topbar content (replaces default)\n * @slot bottombar - Custom bottombar content (replaces default)\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-base';\nimport type { Extension } from '@duskmoon-dev/code-engine';\nimport { EditorView } from '@duskmoon-dev/code-engine/view';\nimport { EditorState, Compartment } from '@duskmoon-dev/code-engine/state';\nimport { basicSetup } from '@duskmoon-dev/code-engine/setup';\nimport { undo, redo } from '@duskmoon-dev/code-engine/commands';\n\n// ── Static theme imports (all 4 are small; avoids runtime bare-specifier issues) ──\nimport * as _duskmoonTheme from '@duskmoon-dev/code-engine/theme/duskmoon';\nimport * as _sunshineTheme from '@duskmoon-dev/code-engine/theme/sunshine';\nimport * as _moonlightTheme from '@duskmoon-dev/code-engine/theme/moonlight';\nimport * as _oneDarkTheme from '@duskmoon-dev/code-engine/theme/one-dark';\n\nfunction extractExt(mod: Record<string, unknown>): Extension {\n const v =\n mod.default ??\n Object.values(mod).find((x) => typeof x === 'function' || (x && typeof x !== 'string'));\n // Theme modules export a factory function (e.g. duskMoon(options?))\n if (typeof v === 'function') return (v as () => Extension)() as Extension;\n return (v ?? []) as Extension;\n}\n\nconst THEMES: Record<string, Extension> = {\n duskmoon: extractExt(_duskmoonTheme as unknown as Record<string, unknown>),\n sunshine: extractExt(_sunshineTheme as unknown as Record<string, unknown>),\n moonlight: extractExt(_moonlightTheme as unknown as Record<string, unknown>),\n 'one-dark': extractExt(_oneDarkTheme as unknown as Record<string, unknown>),\n};\n\n// ── Language loaders (literal import paths so bundlers can resolve them) ──\n\nfunction langLoader(\n importFn: () => Promise<Record<string, unknown>>,\n opts?: Record<string, unknown>,\n): () => Promise<Extension | null> {\n return async () => {\n const mod = await importFn();\n const factory = mod.default ?? Object.values(mod).find((v) => typeof v === 'function');\n if (typeof factory === 'function') {\n return (opts ? factory(opts) : factory()) as Extension;\n }\n return factory as Extension | null;\n };\n}\n\nconst LANG_LOADERS: Record<string, () => Promise<Extension | null>> = {\n javascript: langLoader(() => import('@duskmoon-dev/code-engine/lang/javascript')),\n typescript: langLoader(() => import('@duskmoon-dev/code-engine/lang/javascript'), {\n typescript: true,\n }),\n css: langLoader(() => import('@duskmoon-dev/code-engine/lang/css')),\n html: langLoader(() => import('@duskmoon-dev/code-engine/lang/html')),\n json: langLoader(() => import('@duskmoon-dev/code-engine/lang/json')),\n python: langLoader(() => import('@duskmoon-dev/code-engine/lang/python')),\n markdown: langLoader(() => import('@duskmoon-dev/code-engine/lang/markdown')),\n xml: langLoader(() => import('@duskmoon-dev/code-engine/lang/xml')),\n sql: langLoader(() => import('@duskmoon-dev/code-engine/lang/sql')),\n rust: langLoader(() => import('@duskmoon-dev/code-engine/lang/rust')),\n go: langLoader(() => import('@duskmoon-dev/code-engine/lang/go')),\n java: langLoader(() => import('@duskmoon-dev/code-engine/lang/java')),\n cpp: langLoader(() => import('@duskmoon-dev/code-engine/lang/cpp')),\n php: langLoader(() => import('@duskmoon-dev/code-engine/lang/php')),\n yaml: langLoader(() => import('@duskmoon-dev/code-engine/lang/yaml')),\n sass: langLoader(() => import('@duskmoon-dev/code-engine/lang/sass')),\n less: langLoader(() => import('@duskmoon-dev/code-engine/lang/less')),\n elixir: langLoader(() => import('@duskmoon-dev/code-engine/lang/elixir')),\n erlang: langLoader(() => import('@duskmoon-dev/code-engine/lang/erlang')),\n heex: langLoader(() => import('@duskmoon-dev/code-engine/lang/heex')),\n dart: langLoader(() => import('@duskmoon-dev/code-engine/lang/dart')),\n zig: langLoader(() => import('@duskmoon-dev/code-engine/lang/zig')),\n vue: langLoader(() => import('@duskmoon-dev/code-engine/lang/vue')),\n angular: langLoader(() => import('@duskmoon-dev/code-engine/lang/angular')),\n liquid: langLoader(() => import('@duskmoon-dev/code-engine/lang/liquid')),\n jinja: langLoader(() => import('@duskmoon-dev/code-engine/lang/jinja')),\n wast: langLoader(() => import('@duskmoon-dev/code-engine/lang/wast')),\n lezer: langLoader(() => import('@duskmoon-dev/code-engine/lang/lezer')),\n caddyfile: langLoader(() => import('@duskmoon-dev/code-engine/lang/caddyfile')),\n};\n\nconst LANG_BADGES: Record<string, string> = {\n javascript: 'JS',\n typescript: 'TS',\n python: 'PY',\n rust: 'RS',\n go: 'GO',\n java: 'JAVA',\n cpp: 'C++',\n html: 'HTML',\n css: 'CSS',\n json: 'JSON',\n markdown: 'MD',\n sql: 'SQL',\n yaml: 'YAML',\n xml: 'XML',\n php: 'PHP',\n elixir: 'EX',\n erlang: 'ERL',\n heex: 'HEEX',\n dart: 'DART',\n zig: 'ZIG',\n vue: 'VUE',\n angular: 'NG',\n sass: 'SASS',\n less: 'LESS',\n wast: 'WAST',\n lezer: 'LEZER',\n caddyfile: 'CADDY',\n jinja: 'JINJA',\n liquid: 'LIQUID',\n};\n\nconst LANG_NAMES: Record<string, string> = {\n javascript: 'JavaScript',\n typescript: 'TypeScript',\n python: 'Python',\n rust: 'Rust',\n go: 'Go',\n java: 'Java',\n cpp: 'C++',\n html: 'HTML',\n css: 'CSS',\n json: 'JSON',\n markdown: 'Markdown',\n sql: 'SQL',\n yaml: 'YAML',\n xml: 'XML',\n php: 'PHP',\n elixir: 'Elixir',\n erlang: 'Erlang',\n heex: 'HEEx',\n dart: 'Dart',\n zig: 'Zig',\n vue: 'Vue',\n angular: 'Angular',\n sass: 'Sass',\n less: 'Less',\n wast: 'WAT',\n lezer: 'Lezer',\n caddyfile: 'Caddyfile',\n jinja: 'Jinja',\n liquid: 'Liquid',\n};\n\n// ── SVG Icons (14×14, stroke-based) ──\n\nconst ICON_UNDO = `<svg viewBox=\"0 0 24 24\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>`;\n\nconst ICON_REDO = `<svg viewBox=\"0 0 24 24\"><polyline points=\"23 4 23 10 17 10\"/><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\"/></svg>`;\n\nconst ICON_WRAP = `<svg viewBox=\"0 0 24 24\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><path d=\"M3 12h15a3 3 0 1 1 0 6h-4\"/><polyline points=\"16 16 14 18 16 20\"/><line x1=\"3\" y1=\"18\" x2=\"10\" y2=\"18\"/></svg>`;\n\nconst ICON_COPY = `<svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>`;\n\nconst ICON_CHECK = `<svg viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\nconst ICON_FULLSCREEN = `<svg viewBox=\"0 0 24 24\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>`;\n\nconst ICON_EXIT_FULLSCREEN = `<svg viewBox=\"0 0 24 24\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>`;\n\nexport type CodeEngineTheme = 'duskmoon' | 'sunshine' | 'moonlight' | 'one-dark';\n\nconst styles = css`\n :host {\n display: block;\n min-height: 200px;\n font-family: var(\n --dm-font-mono,\n ui-monospace,\n 'Cascadia Code',\n 'Source Code Pro',\n Menlo,\n Consolas,\n 'DejaVu Sans Mono',\n monospace\n );\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .cm-host {\n height: 100%;\n }\n\n .cm-host .cm-editor {\n height: 100%;\n }\n\n .cm-host .cm-editor.cm-focused {\n outline: none;\n }\n\n /* ── Topbar ────────────────────────────────────────── */\n\n .topbar {\n display: none;\n }\n\n :host([show-topbar]) .topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n border-bottom: 1px solid var(--dm-border, #e0e0e0);\n background: var(--dm-surface-container, #f0f0f0);\n font-size: 0.75rem;\n color: var(--dm-on-surface, #1a1a1a);\n }\n\n .topbar-left {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n min-width: 0;\n }\n\n .topbar-right {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n }\n\n .lang-badge {\n padding: 0.0625rem 0.375rem;\n border-radius: var(--dm-radius-sm, 0.25rem);\n background: var(--dm-primary, #6750a4);\n color: var(--dm-on-primary, #fff);\n font-size: 0.625rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.025em;\n }\n\n .topbar-title {\n opacity: 0.7;\n font-size: 0.6875rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .bar-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--dm-radius-sm, 0.25rem);\n background: transparent;\n color: var(--dm-on-surface-variant, #555);\n font-family: inherit;\n font-size: 0.75rem;\n cursor: pointer;\n transition:\n background 0.15s,\n color 0.15s;\n }\n\n .bar-btn:hover {\n background: var(--dm-surface-container-high, #e0e0e0);\n color: var(--dm-on-surface, #1a1a1a);\n }\n\n .bar-btn svg {\n width: 14px;\n height: 14px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n }\n\n /* ── Bottombar ─────────────────────────────────────── */\n\n .bottombar {\n display: none;\n }\n\n :host([show-bottombar]) .bottombar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.25rem 0.75rem;\n border-top: 1px solid var(--dm-border, #e0e0e0);\n background: var(--dm-surface-container-high, #e0e0e0);\n font-size: 0.625rem;\n color: var(--dm-on-surface-variant, #555);\n }\n\n .bottombar-left,\n .bottombar-right {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n }\n\n /* ── Fullscreen ────────────────────────────────────── */\n\n :host(.fullscreen) {\n position: fixed !important;\n inset: 0 !important;\n z-index: 9999 !important;\n min-height: 100vh !important;\n display: flex;\n flex-direction: column;\n }\n\n :host(.fullscreen) .cm-host {\n flex: 1;\n }\n`;\n\nexport class ElDmCodeEngine extends BaseElement {\n static properties = {\n language: { type: String, reflect: true },\n readonly: { type: Boolean, reflect: true },\n theme: { type: String, reflect: true, default: 'duskmoon' },\n wrap: { type: Boolean, reflect: true },\n showTopbar: { type: Boolean, reflect: true },\n showBottombar: { type: Boolean, reflect: true },\n title: { type: String, reflect: true },\n };\n\n declare language: string;\n declare readonly: boolean;\n declare theme: CodeEngineTheme;\n declare wrap: boolean;\n declare showTopbar: boolean;\n declare showBottombar: boolean;\n declare title: string;\n\n #editor: EditorView | null = null;\n #pendingValue: string | null = null;\n\n readonly #languageCompartment = new Compartment();\n readonly #readonlyCompartment = new Compartment();\n readonly #themeCompartment = new Compartment();\n readonly #wrapCompartment = new Compartment();\n\n readonly #langCache = new Map<string, Extension | null>();\n\n #cursorLine = 1;\n #cursorCol = 1;\n #lineCount = 0;\n #isFullscreen = false;\n #copyTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n /**\n * Current editor content. Getter always returns live document text.\n * Setter updates the editor document (or queues the value if called before mount).\n */\n get value(): string {\n return (\n this.#editor?.state.doc.toString() ?? this.#pendingValue ?? this.getAttribute('value') ?? ''\n );\n }\n\n set value(v: string) {\n if (this.#editor) {\n this.#editor.dispatch({\n changes: { from: 0, to: this.#editor.state.doc.length, insert: v },\n });\n } else {\n this.#pendingValue = v;\n }\n }\n\n /** Focus the editor. */\n focus(): void {\n this.#editor?.focus();\n }\n\n /** Returns current editor content. Equivalent to reading the `value` property. */\n getValue(): string {\n return this.value;\n }\n\n /** Sets editor content programmatically. Equivalent to assigning the `value` property. */\n setValue(v: string): void {\n this.value = v;\n }\n\n protected render(): string {\n return `${this.#renderTopbar()}<div class=\"cm-host\" part=\"editor\"></div>${this.#renderBottombar()}`;\n }\n\n protected update(): void {\n if (!this.#editor) {\n // First render: create the container then mount the editor.\n super.update();\n void this.#mountEditor();\n } else {\n // Editor already mounted: reconfigure via compartment transactions.\n void this.#applyConfig();\n }\n }\n\n #clickHandler = (e: Event) => {\n const btn = (e.target as Element)?.closest('[data-action]');\n if (btn) {\n const action = btn.getAttribute('data-action');\n if (action) this.#handleBarAction(action);\n }\n };\n\n connectedCallback(): void {\n super.connectedCallback();\n this.shadowRoot!.addEventListener('click', this.#clickHandler);\n }\n\n disconnectedCallback(): void {\n this.shadowRoot!.removeEventListener('click', this.#clickHandler);\n if (this.#copyTimer) {\n clearTimeout(this.#copyTimer);\n this.#copyTimer = null;\n }\n // Save current content so it survives a DOM move/reconnect.\n if (this.#editor) {\n this.#pendingValue = this.#editor.state.doc.toString();\n this.#editor.destroy();\n this.#editor = null;\n }\n super.disconnectedCallback();\n }\n\n // ── Bar rendering ───────────────────────────────────────────────────\n\n #renderTopbar(): string {\n const badge = this.language\n ? `<span class=\"lang-badge\">${LANG_BADGES[this.language] ?? this.language.toUpperCase()}</span>`\n : '';\n const t = (this as unknown as { title: string }).title;\n const title = t ? `<span class=\"topbar-title\">${t}</span>` : '';\n return `\n <div class=\"topbar\" part=\"topbar\">\n <slot name=\"topbar\">\n <div class=\"topbar-left\">${badge}${title}</div>\n <div class=\"topbar-right\">\n <button class=\"bar-btn\" data-action=\"undo\" title=\"Undo\">${ICON_UNDO}</button>\n <button class=\"bar-btn\" data-action=\"redo\" title=\"Redo\">${ICON_REDO}</button>\n <button class=\"bar-btn\" data-action=\"wrap\" title=\"Toggle line wrap\">${ICON_WRAP}</button>\n <button class=\"bar-btn\" data-action=\"copy\" title=\"Copy\">${ICON_COPY}</button>\n <button class=\"bar-btn\" data-action=\"fullscreen\" title=\"Toggle fullscreen\">${this.#isFullscreen ? ICON_EXIT_FULLSCREEN : ICON_FULLSCREEN}</button>\n </div>\n </slot>\n </div>\n `;\n }\n\n #renderBottombar(): string {\n const langName = this.language ? (LANG_NAMES[this.language] ?? this.language) : '';\n return `\n <div class=\"bottombar\" part=\"bottombar\">\n <slot name=\"bottombar\">\n <div class=\"bottombar-left\">\n <span class=\"cursor-pos\">Ln ${this.#cursorLine}, Col ${this.#cursorCol}</span>\n <span class=\"line-count\">${this.#lineCount} lines</span>\n </div>\n <div class=\"bottombar-right\">\n <span>UTF-8</span>\n ${langName ? `<span>${langName}</span>` : ''}\n </div>\n </slot>\n </div>\n `;\n }\n\n // ── Action handlers ─────────────────────────────────────────────────\n\n #handleBarAction(action: string): void {\n switch (action) {\n case 'undo':\n if (this.#editor) undo(this.#editor);\n break;\n case 'redo':\n if (this.#editor) redo(this.#editor);\n break;\n case 'wrap':\n this.wrap = !this.wrap;\n break;\n case 'copy':\n void this.#handleCopy();\n break;\n case 'fullscreen':\n this.#toggleFullscreen();\n break;\n }\n }\n\n async #handleCopy(): Promise<void> {\n const value = this.value;\n try {\n await navigator.clipboard.writeText(value);\n } catch {\n return; // Clipboard API unavailable or denied\n }\n this.emit('copy', { value });\n this.#showCopyFeedback();\n }\n\n #showCopyFeedback(): void {\n const btn = this.shadowRoot?.querySelector('[data-action=\"copy\"]');\n if (!btn) return;\n if (this.#copyTimer) clearTimeout(this.#copyTimer);\n btn.innerHTML = ICON_CHECK;\n btn.setAttribute('title', 'Copied!');\n this.#copyTimer = setTimeout(() => {\n btn.innerHTML = ICON_COPY;\n btn.setAttribute('title', 'Copy');\n this.#copyTimer = null;\n }, 2000);\n }\n\n #toggleFullscreen(): void {\n this.#isFullscreen = !this.#isFullscreen;\n this.classList.toggle('fullscreen', this.#isFullscreen);\n // Update fullscreen button icon\n const btn = this.shadowRoot?.querySelector('[data-action=\"fullscreen\"]');\n if (btn) {\n btn.innerHTML = this.#isFullscreen ? ICON_EXIT_FULLSCREEN : ICON_FULLSCREEN;\n btn.setAttribute('title', this.#isFullscreen ? 'Exit fullscreen' : 'Toggle fullscreen');\n }\n this.emit('fullscreen', { active: this.#isFullscreen });\n }\n\n // ── Cursor tracking ────────────────────────────────────────────────\n\n #updateCursorInfo(state: EditorState): void {\n const pos = state.selection.main.head;\n const line = state.doc.lineAt(pos);\n this.#cursorLine = line.number;\n this.#cursorCol = pos - line.from + 1;\n this.#lineCount = state.doc.lines;\n\n // Update bottombar spans directly (avoid full re-render)\n const cursorEl = this.shadowRoot?.querySelector('.cursor-pos');\n const lineCountEl = this.shadowRoot?.querySelector('.line-count');\n if (cursorEl) cursorEl.textContent = `Ln ${this.#cursorLine}, Col ${this.#cursorCol}`;\n if (lineCountEl) lineCountEl.textContent = `${this.#lineCount} lines`;\n }\n\n // ── Private helpers ──────────────────────────────────────────────────\n\n async #mountEditor(): Promise<void> {\n const host = this.shadowRoot?.querySelector('.cm-host');\n if (!host || this.#editor) return;\n\n const initialDoc = this.#pendingValue ?? this.getAttribute('value') ?? '';\n this.#pendingValue = null;\n\n const langExt = await this.#loadLanguage(this.language);\n\n this.#editor = new EditorView({\n state: EditorState.create({\n doc: initialDoc,\n extensions: [\n basicSetup,\n this.#languageCompartment.of(langExt ?? []),\n this.#readonlyCompartment.of(EditorState.readOnly.of(this.readonly ?? false)),\n this.#themeCompartment.of(THEMES[this.theme] ?? []),\n this.#wrapCompartment.of(this.wrap ? EditorView.lineWrapping : []),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n this.emit('input', { value: update.state.doc.toString() });\n }\n if (update.docChanged || update.selectionSet) {\n this.#updateCursorInfo(update.state);\n }\n }),\n EditorView.domEventHandlers({\n blur: () => {\n this.emit('change', { value: this.#editor!.state.doc.toString() });\n return false;\n },\n }),\n ],\n }),\n parent: host,\n root: this.shadowRoot,\n });\n\n this.#updateCursorInfo(this.#editor.state);\n }\n\n async #applyConfig(): Promise<void> {\n if (!this.#editor) return;\n\n const langExt = await this.#loadLanguage(this.language);\n\n this.#editor.dispatch({\n effects: [\n this.#languageCompartment.reconfigure(langExt ?? []),\n this.#readonlyCompartment.reconfigure(EditorState.readOnly.of(this.readonly ?? false)),\n this.#themeCompartment.reconfigure(THEMES[this.theme] ?? []),\n this.#wrapCompartment.reconfigure(this.wrap ? EditorView.lineWrapping : []),\n ],\n });\n }\n\n async #loadLanguage(name: string): Promise<Extension | null> {\n if (!name) return null;\n if (this.#langCache.has(name)) return this.#langCache.get(name)!;\n\n const loader = LANG_LOADERS[name];\n if (!loader) {\n this.#langCache.set(name, null);\n return null;\n }\n\n try {\n const ext = await loader();\n this.#langCache.set(name, ext);\n return ext;\n } catch {\n this.#langCache.set(name, null);\n return null;\n }\n }\n}\n",
|
|
6
6
|
"/**\n * @duskmoon-dev/el-code-engine\n *\n * DuskMoon Code Engine custom element — a lightweight code editor\n * backed by @duskmoon-dev/code-engine (CodeMirror 6 fork).\n */\n\nimport { ElDmCodeEngine } from './el-dm-code-engine.js';\n\nexport { ElDmCodeEngine };\nexport type { CodeEngineTheme } from './el-dm-code-engine.js';\n\n/**\n * Register the el-dm-code-engine custom element.\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-code-engine';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-code-engine')) {\n customElements.define('el-dm-code-engine', ElDmCodeEngine);\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;;;;;;;
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "6C8C69AE44633C3064756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|