@pyreon/code 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/lib/analysis/index.js.html +5406 -0
  3. package/lib/dist-B5vB-rif.js +3904 -0
  4. package/lib/dist-B5vB-rif.js.map +1 -0
  5. package/lib/dist-BAfzu5eu.js +1428 -0
  6. package/lib/dist-BAfzu5eu.js.map +1 -0
  7. package/lib/dist-BLlV_D16.js +1166 -0
  8. package/lib/dist-BLlV_D16.js.map +1 -0
  9. package/lib/dist-BNmKLTu8.js +373 -0
  10. package/lib/dist-BNmKLTu8.js.map +1 -0
  11. package/lib/dist-BZtTlC1J.js +692 -0
  12. package/lib/dist-BZtTlC1J.js.map +1 -0
  13. package/lib/dist-CTDqGIAf.js +856 -0
  14. package/lib/dist-CTDqGIAf.js.map +1 -0
  15. package/lib/dist-CTPisNZp.js +83 -0
  16. package/lib/dist-CTPisNZp.js.map +1 -0
  17. package/lib/dist-Ce2tvOxv.js +379 -0
  18. package/lib/dist-Ce2tvOxv.js.map +1 -0
  19. package/lib/dist-CttF0OTv.js +465 -0
  20. package/lib/dist-CttF0OTv.js.map +1 -0
  21. package/lib/dist-DS2tluW9.js +818 -0
  22. package/lib/dist-DS2tluW9.js.map +1 -0
  23. package/lib/dist-DUNx9ldu.js +460 -0
  24. package/lib/dist-DUNx9ldu.js.map +1 -0
  25. package/lib/dist-Dej_yf3k.js +473 -0
  26. package/lib/dist-Dej_yf3k.js.map +1 -0
  27. package/lib/dist-DshStUxU.js +283 -0
  28. package/lib/dist-DshStUxU.js.map +1 -0
  29. package/lib/dist-qTrOe7xY.js +461 -0
  30. package/lib/dist-qTrOe7xY.js.map +1 -0
  31. package/lib/dist-v09vikKr.js +2421 -0
  32. package/lib/dist-v09vikKr.js.map +1 -0
  33. package/lib/index.js +915 -0
  34. package/lib/index.js.map +1 -0
  35. package/lib/types/dist.d.ts +798 -0
  36. package/lib/types/dist.d.ts.map +1 -0
  37. package/lib/types/dist10.d.ts +67 -0
  38. package/lib/types/dist10.d.ts.map +1 -0
  39. package/lib/types/dist11.d.ts +126 -0
  40. package/lib/types/dist11.d.ts.map +1 -0
  41. package/lib/types/dist12.d.ts +21 -0
  42. package/lib/types/dist12.d.ts.map +1 -0
  43. package/lib/types/dist13.d.ts +404 -0
  44. package/lib/types/dist13.d.ts.map +1 -0
  45. package/lib/types/dist14.d.ts +292 -0
  46. package/lib/types/dist14.d.ts.map +1 -0
  47. package/lib/types/dist15.d.ts +132 -0
  48. package/lib/types/dist15.d.ts.map +1 -0
  49. package/lib/types/dist2.d.ts +15 -0
  50. package/lib/types/dist2.d.ts.map +1 -0
  51. package/lib/types/dist3.d.ts +106 -0
  52. package/lib/types/dist3.d.ts.map +1 -0
  53. package/lib/types/dist4.d.ts +67 -0
  54. package/lib/types/dist4.d.ts.map +1 -0
  55. package/lib/types/dist5.d.ts +95 -0
  56. package/lib/types/dist5.d.ts.map +1 -0
  57. package/lib/types/dist6.d.ts +330 -0
  58. package/lib/types/dist6.d.ts.map +1 -0
  59. package/lib/types/dist7.d.ts +15 -0
  60. package/lib/types/dist7.d.ts.map +1 -0
  61. package/lib/types/dist8.d.ts +15 -0
  62. package/lib/types/dist8.d.ts.map +1 -0
  63. package/lib/types/dist9.d.ts +635 -0
  64. package/lib/types/dist9.d.ts.map +1 -0
  65. package/lib/types/index.d.ts +852 -0
  66. package/lib/types/index.d.ts.map +1 -0
  67. package/lib/types/index2.d.ts +347 -0
  68. package/lib/types/index2.d.ts.map +1 -0
  69. package/package.json +79 -0
  70. package/src/components/code-editor.tsx +42 -0
  71. package/src/components/diff-editor.tsx +97 -0
  72. package/src/components/tabbed-editor.tsx +86 -0
  73. package/src/editor.ts +652 -0
  74. package/src/index.ts +52 -0
  75. package/src/languages.ts +77 -0
  76. package/src/minimap.ts +160 -0
  77. package/src/tabbed-editor.ts +231 -0
  78. package/src/tests/code.test.ts +505 -0
  79. package/src/themes.ts +87 -0
  80. package/src/types.ts +253 -0
@@ -0,0 +1,852 @@
1
+ import { bracketMatching, defaultHighlightStyle, foldGutter, foldKeymap, indentOnInput, indentUnit, syntaxHighlighting } from "@codemirror/language";
2
+ import { MergeView } from "@codemirror/merge";
3
+ import { Compartment, EditorState } from "@codemirror/state";
4
+ import { Decoration, EditorView, GutterMarker, ViewPlugin, crosshairCursor, drawSelection, dropCursor, gutter, highlightActiveLine, highlightActiveLineGutter, keymap, lineNumbers, placeholder, rectangularSelection } from "@codemirror/view";
5
+ import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from "@codemirror/autocomplete";
6
+ import { defaultKeymap, history, historyKeymap, indentWithTab, redo, undo } from "@codemirror/commands";
7
+ import { lintKeymap, setDiagnostics } from "@codemirror/lint";
8
+ import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
9
+ import { computed, effect, signal } from "@pyreon/reactivity";
10
+
11
+ //#region \0rolldown/runtime.js
12
+
13
+ function h(type, props, ...children) {
14
+ return {
15
+ type,
16
+ props: props ?? EMPTY_PROPS,
17
+ children: normalizeChildren(children),
18
+ key: props?.key ?? null
19
+ };
20
+ }
21
+ function normalizeChildren(children) {
22
+ for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
23
+ return children;
24
+ }
25
+ function flattenChildren(children) {
26
+ const result = [];
27
+ for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));else result.push(child);
28
+ return result;
29
+ }
30
+ /**
31
+ * JSX automatic runtime.
32
+ *
33
+ * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
34
+ * rewrites JSX to imports from this file automatically:
35
+ * <div class="x" /> → jsx("div", { class: "x" })
36
+ */
37
+ function jsx(type, props, key) {
38
+ const {
39
+ children,
40
+ ...rest
41
+ } = props;
42
+ const propsWithKey = key != null ? {
43
+ ...rest,
44
+ key
45
+ } : rest;
46
+ if (typeof type === "function") return h(type, children !== void 0 ? {
47
+ ...propsWithKey,
48
+ children
49
+ } : propsWithKey);
50
+ return h(type, propsWithKey, ...(children === void 0 ? [] : Array.isArray(children) ? children : [children]));
51
+ }
52
+ //#endregion
53
+ //#region src/components/code-editor.tsx
54
+ /**
55
+ * Code editor component — mounts a CodeMirror 6 instance.
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * const editor = createEditor({
60
+ * value: 'const x = 1',
61
+ * language: 'typescript',
62
+ * theme: 'dark',
63
+ * })
64
+ *
65
+ * <CodeEditor instance={editor} style="height: 400px" />
66
+ * ```
67
+ */
68
+ function CodeEditor(props) {
69
+ const {
70
+ instance
71
+ } = props;
72
+ const containerRef = el => {
73
+ if (!el) return;
74
+ const mountable = instance;
75
+ if (mountable._mount) mountable._mount(el);
76
+ };
77
+ const baseStyle = `width: 100%; height: 100%; overflow: hidden; ${props.style ?? ""}`;
78
+ return /* @__PURE__ */jsx("div", {
79
+ ref: containerRef,
80
+ class: `pyreon-code-editor ${props.class ?? ""}`,
81
+ style: baseStyle
82
+ });
83
+ }
84
+
85
+ //#endregion
86
+ //#region src/languages.ts
87
+ /**
88
+ * Language extension loaders — lazy-loaded on demand.
89
+ * Only the requested language is imported, keeping the initial bundle small.
90
+ */
91
+
92
+ /**
93
+ * Load a language extension. Returns cached if already loaded.
94
+ * Language grammars are lazy-imported — zero cost until used.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const ext = await loadLanguage('typescript')
99
+ * ```
100
+ */
101
+ async function loadLanguage(language) {
102
+ const cached = loaded.get(language);
103
+ if (cached) return cached;
104
+ const loader = languageLoaders[language];
105
+ if (!loader) return [];
106
+ try {
107
+ const ext = await loader();
108
+ loaded.set(language, ext);
109
+ return ext;
110
+ } catch {
111
+ return [];
112
+ }
113
+ }
114
+ /**
115
+ * Get available languages.
116
+ */
117
+ function getAvailableLanguages() {
118
+ return Object.keys(languageLoaders);
119
+ }
120
+
121
+ //#endregion
122
+ //#region src/themes.ts
123
+ /**
124
+ * Light theme — clean, minimal.
125
+ */
126
+
127
+ /**
128
+ * Resolve a theme value to a CodeMirror extension.
129
+ */
130
+ function resolveTheme(theme) {
131
+ if (theme === "light") return lightTheme;
132
+ if (theme === "dark") return darkTheme;
133
+ return theme;
134
+ }
135
+
136
+ //#endregion
137
+ //#region src/components/diff-editor.tsx
138
+ /**
139
+ * Side-by-side or inline diff editor using @codemirror/merge.
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * <DiffEditor
144
+ * original="const x = 1"
145
+ * modified="const x = 2"
146
+ * language="typescript"
147
+ * theme="dark"
148
+ * style="height: 400px"
149
+ * />
150
+ * ```
151
+ */
152
+ function DiffEditor(props) {
153
+ const {
154
+ original,
155
+ modified,
156
+ language = "plain",
157
+ theme = "light",
158
+ readOnly = true,
159
+ inline = false
160
+ } = props;
161
+ const containerRef = async el => {
162
+ if (!el) return;
163
+ const langExt = await loadLanguage(language);
164
+ const themeExt = resolveTheme(theme);
165
+ const extensions = [syntaxHighlighting(defaultHighlightStyle, {
166
+ fallback: true
167
+ }), langExt, themeExt, EditorView.editable.of(!readOnly), EditorState.readOnly.of(readOnly)];
168
+ const originalText = typeof original === "string" ? original : original();
169
+ const modifiedText = typeof modified === "string" ? modified : modified();
170
+ el.innerHTML = "";
171
+ if (inline) new MergeView({
172
+ a: {
173
+ doc: originalText,
174
+ extensions
175
+ },
176
+ b: {
177
+ doc: modifiedText,
178
+ extensions
179
+ },
180
+ parent: el,
181
+ collapseUnchanged: {
182
+ margin: 3,
183
+ minSize: 4
184
+ }
185
+ });else new MergeView({
186
+ a: {
187
+ doc: originalText,
188
+ extensions
189
+ },
190
+ b: {
191
+ doc: modifiedText,
192
+ extensions
193
+ },
194
+ parent: el,
195
+ collapseUnchanged: {
196
+ margin: 3,
197
+ minSize: 4
198
+ }
199
+ });
200
+ };
201
+ const baseStyle = `width: 100%; height: 100%; overflow: hidden; ${props.style ?? ""}`;
202
+ return /* @__PURE__ */jsx("div", {
203
+ ref: containerRef,
204
+ class: `pyreon-diff-editor ${props.class ?? ""}`,
205
+ style: baseStyle
206
+ });
207
+ }
208
+
209
+ //#endregion
210
+ //#region src/components/tabbed-editor.tsx
211
+ /**
212
+ * Tabbed code editor component — renders tab bar + editor.
213
+ * Headless styling — the tab bar is a plain div with button tabs.
214
+ * Consumers can style via CSS classes.
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * const editor = createTabbedEditor({
219
+ * tabs: [
220
+ * { name: 'index.ts', language: 'typescript', value: 'const x = 1' },
221
+ * { name: 'style.css', language: 'css', value: '.app { }' },
222
+ * ],
223
+ * theme: 'dark',
224
+ * })
225
+ *
226
+ * <TabbedEditor instance={editor} style="height: 500px" />
227
+ * ```
228
+ */
229
+ function TabbedEditor(props) {
230
+ const {
231
+ instance
232
+ } = props;
233
+ const containerStyle = `display: flex; flex-direction: column; width: 100%; height: 100%; ${props.style ?? ""}`;
234
+ const tabBarStyle = "display: flex; overflow-x: auto; background: #f1f5f9; border-bottom: 1px solid #e2e8f0; min-height: 34px; flex-shrink: 0;";
235
+ return /* @__PURE__ */jsxs("div", {
236
+ class: `pyreon-tabbed-editor ${props.class ?? ""}`,
237
+ style: containerStyle,
238
+ children: [() => {
239
+ const tabs = instance.tabs();
240
+ const activeId = instance.activeTabId();
241
+ return /* @__PURE__ */jsx("div", {
242
+ class: "pyreon-tabbed-editor-tabs",
243
+ style: tabBarStyle,
244
+ children: tabs.map(tab => {
245
+ const id = tab.id ?? tab.name;
246
+ const isActive = id === activeId;
247
+ const tabStyle = `display: flex; align-items: center; gap: 6px; padding: 6px 12px; border: none; background: ${isActive ? "white" : "transparent"}; border-bottom: ${isActive ? "2px solid #3b82f6" : "2px solid transparent"}; cursor: pointer; font-size: 13px; color: ${isActive ? "#1e293b" : "#64748b"}; white-space: nowrap; position: relative; font-family: inherit;`;
248
+ return /* @__PURE__ */jsxs("button", {
249
+ type: "button",
250
+ class: `pyreon-tab ${isActive ? "active" : ""} ${tab.modified ? "modified" : ""}`,
251
+ style: tabStyle,
252
+ onClick: () => instance.switchTab(id),
253
+ children: [/* @__PURE__ */jsx("span", {
254
+ children: tab.name
255
+ }), tab.modified && /* @__PURE__ */jsx("span", {
256
+ style: "width: 6px; height: 6px; border-radius: 50%; background: #f59e0b; flex-shrink: 0;",
257
+ title: "Modified"
258
+ }), tab.closable !== false && /* @__PURE__ */jsx("span", {
259
+ style: "font-size: 14px; line-height: 1; opacity: 0.5; cursor: pointer; padding: 0 2px; margin-left: 2px;",
260
+ title: "Close",
261
+ onClick: e => {
262
+ e.stopPropagation();
263
+ instance.closeTab(id);
264
+ },
265
+ children: "×"
266
+ })]
267
+ }, id);
268
+ })
269
+ });
270
+ }, /* @__PURE__ */jsx("div", {
271
+ style: "flex: 1; min-height: 0;",
272
+ children: /* @__PURE__ */jsx(CodeEditor, {
273
+ instance: instance.editor
274
+ })
275
+ })]
276
+ });
277
+ }
278
+
279
+ //#endregion
280
+ //#region src/minimap.ts
281
+ /**
282
+ * Canvas-based minimap extension for CodeMirror 6.
283
+ * Renders a scaled-down overview of the document on the right side.
284
+ */
285
+
286
+ function createMinimapCanvas() {
287
+ const canvas = document.createElement("canvas");
288
+ canvas.style.cssText = `position: absolute; right: 0; top: 0; width: ${MINIMAP_WIDTH}px; height: 100%; cursor: pointer; z-index: 5;`;
289
+ canvas.width = MINIMAP_WIDTH * 2;
290
+ return canvas;
291
+ }
292
+ function renderMinimap(canvas, view) {
293
+ const ctx = canvas.getContext("2d");
294
+ if (!ctx) return;
295
+ const doc = view.state.doc;
296
+ const totalLines = doc.lines;
297
+ const height = canvas.clientHeight;
298
+ canvas.height = height * 2;
299
+ const isDark = view.dom.classList.contains("cm-dark");
300
+ const bg = isDark ? MINIMAP_BG : MINIMAP_BG_LIGHT;
301
+ const textColor = isDark ? TEXT_COLOR : TEXT_COLOR_LIGHT;
302
+ const scale = 2;
303
+ ctx.setTransform(scale, 0, 0, scale, 0, 0);
304
+ ctx.fillStyle = bg;
305
+ ctx.fillRect(0, 0, MINIMAP_WIDTH, height);
306
+ const contentHeight = totalLines * LINE_HEIGHT;
307
+ const scrollFraction = contentHeight > height ? view.scrollDOM.scrollTop / (view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight || 1) : 0;
308
+ const offset = contentHeight > height ? scrollFraction * (contentHeight - height) : 0;
309
+ ctx.fillStyle = textColor;
310
+ const startLine = Math.max(1, Math.floor(offset / LINE_HEIGHT));
311
+ const endLine = Math.min(totalLines, startLine + Math.ceil(height / LINE_HEIGHT) + 1);
312
+ for (let i = startLine; i <= endLine; i++) {
313
+ const line = doc.line(i);
314
+ const y = (i - 1) * LINE_HEIGHT - offset;
315
+ if (y < -LINE_HEIGHT || y > height) continue;
316
+ const text = line.text;
317
+ let x = 4;
318
+ for (let j = 0; j < Math.min(text.length, 60); j++) {
319
+ if (text[j] !== " " && text[j] !== " ") ctx.fillRect(x, y, CHAR_WIDTH, 1.5);
320
+ x += CHAR_WIDTH;
321
+ }
322
+ }
323
+ const viewportTop = view.scrollDOM.scrollTop;
324
+ const viewportHeight = view.scrollDOM.clientHeight;
325
+ const docHeight = view.scrollDOM.scrollHeight || 1;
326
+ const vpY = viewportTop / docHeight * Math.min(contentHeight, height);
327
+ const vpH = viewportHeight / docHeight * Math.min(contentHeight, height);
328
+ ctx.fillStyle = VIEWPORT_COLOR;
329
+ ctx.fillRect(0, vpY, MINIMAP_WIDTH, vpH);
330
+ ctx.strokeStyle = VIEWPORT_BORDER;
331
+ ctx.lineWidth = 1;
332
+ ctx.strokeRect(.5, vpY + .5, MINIMAP_WIDTH - 1, vpH - 1);
333
+ }
334
+ /**
335
+ * CodeMirror 6 minimap extension.
336
+ * Renders a canvas-based code overview on the right side of the editor.
337
+ *
338
+ * @example
339
+ * ```ts
340
+ * import { minimapExtension } from '@pyreon/code'
341
+ * // Add to editor extensions
342
+ * ```
343
+ */
344
+ function minimapExtension() {
345
+ return [ViewPlugin.fromClass(class {
346
+ canvas;
347
+ view;
348
+ animFrame = null;
349
+ constructor(view) {
350
+ this.view = view;
351
+ this.canvas = createMinimapCanvas();
352
+ view.dom.style.position = "relative";
353
+ view.dom.appendChild(this.canvas);
354
+ this.canvas.addEventListener("click", e => {
355
+ const rect = this.canvas.getBoundingClientRect();
356
+ const scrollTarget = (e.clientY - rect.top) / rect.height * (view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight);
357
+ view.scrollDOM.scrollTo({
358
+ top: scrollTarget,
359
+ behavior: "smooth"
360
+ });
361
+ });
362
+ this.render();
363
+ }
364
+ render() {
365
+ renderMinimap(this.canvas, this.view);
366
+ }
367
+ update(update) {
368
+ if (update.docChanged || update.viewportChanged || update.geometryChanged) {
369
+ if (this.animFrame) cancelAnimationFrame(this.animFrame);
370
+ this.animFrame = requestAnimationFrame(() => this.render());
371
+ }
372
+ }
373
+ destroy() {
374
+ if (this.animFrame) cancelAnimationFrame(this.animFrame);
375
+ this.canvas.remove();
376
+ }
377
+ }), EditorView.theme({
378
+ ".cm-scroller": {
379
+ paddingRight: `${MINIMAP_WIDTH + 8}px`
380
+ }
381
+ })];
382
+ }
383
+
384
+ //#endregion
385
+ //#region src/editor.ts
386
+ /**
387
+ * Create a reactive code editor instance.
388
+ *
389
+ * The editor state (value, language, theme, cursor, selection) is backed
390
+ * by signals. The CodeMirror EditorView is created when mounted via
391
+ * the `<CodeEditor>` component.
392
+ *
393
+ * @param config - Editor configuration
394
+ * @returns A reactive EditorInstance
395
+ *
396
+ * @example
397
+ * ```tsx
398
+ * const editor = createEditor({
399
+ * value: 'const x = 1',
400
+ * language: 'typescript',
401
+ * theme: 'dark',
402
+ * })
403
+ *
404
+ * editor.value() // reactive
405
+ * editor.value.set('new') // updates editor
406
+ *
407
+ * <CodeEditor instance={editor} />
408
+ * ```
409
+ */
410
+ function createEditor(config = {}) {
411
+ const {
412
+ value: initialValue = "",
413
+ language: initialLanguage = "plain",
414
+ theme: initialTheme = "light",
415
+ lineNumbers: showLineNumbers = true,
416
+ readOnly: initialReadOnly = false,
417
+ foldGutter: showFoldGutter = true,
418
+ bracketMatching: enableBracketMatching = true,
419
+ autocomplete: enableAutocomplete = true,
420
+ search: _enableSearch = true,
421
+ highlightIndentGuides: enableIndentGuides = true,
422
+ vim: enableVim = false,
423
+ emacs: enableEmacs = false,
424
+ tabSize: configTabSize = 2,
425
+ lineWrapping: enableLineWrapping = false,
426
+ placeholder: placeholderText,
427
+ minimap: enableMinimap = false,
428
+ extensions: userExtensions = [],
429
+ onChange
430
+ } = config;
431
+ const value = signal(initialValue);
432
+ const language = signal(initialLanguage);
433
+ const theme = signal(initialTheme);
434
+ const readOnly = signal(initialReadOnly);
435
+ const focused = signal(false);
436
+ const view = signal(null);
437
+ const docVersion = signal(0);
438
+ const languageCompartment = new Compartment();
439
+ const themeCompartment = new Compartment();
440
+ const readOnlyCompartment = new Compartment();
441
+ const extraKeymapCompartment = new Compartment();
442
+ const keyModeCompartment = new Compartment();
443
+ const cursor = computed(() => {
444
+ docVersion();
445
+ const v = view.peek();
446
+ if (!v) return {
447
+ line: 1,
448
+ col: 1
449
+ };
450
+ const pos = v.state.selection.main.head;
451
+ const line = v.state.doc.lineAt(pos);
452
+ return {
453
+ line: line.number,
454
+ col: pos - line.from + 1
455
+ };
456
+ });
457
+ const selection = computed(() => {
458
+ docVersion();
459
+ const v = view.peek();
460
+ if (!v) return {
461
+ from: 0,
462
+ to: 0,
463
+ text: ""
464
+ };
465
+ const sel = v.state.selection.main;
466
+ return {
467
+ from: sel.from,
468
+ to: sel.to,
469
+ text: v.state.sliceDoc(sel.from, sel.to)
470
+ };
471
+ });
472
+ const lineCount = computed(() => {
473
+ docVersion();
474
+ const v = view.peek();
475
+ return v ? v.state.doc.lines : initialValue.split("\n").length;
476
+ });
477
+ const lineHighlights = /* @__PURE__ */new Map();
478
+ const lineHighlightField = ViewPlugin.fromClass(class {
479
+ decorations;
480
+ constructor(editorView) {
481
+ this.decorations = this.buildDecos(editorView);
482
+ }
483
+ buildDecos(editorView) {
484
+ const ranges = [];
485
+ for (const [lineNum, cls] of lineHighlights) if (lineNum >= 1 && lineNum <= editorView.state.doc.lines) {
486
+ const lineInfo = editorView.state.doc.line(lineNum);
487
+ ranges.push({
488
+ from: lineInfo.from,
489
+ deco: Decoration.line({
490
+ class: cls
491
+ })
492
+ });
493
+ }
494
+ return Decoration.set(ranges.sort((a, b) => a.from - b.from).map(d => d.deco.range(d.from)));
495
+ }
496
+ update(upd) {
497
+ if (upd.docChanged || upd.viewportChanged) this.decorations = this.buildDecos(upd.view);
498
+ }
499
+ }, {
500
+ decorations: plugin => plugin.decorations
501
+ });
502
+ const gutterMarkers = /* @__PURE__ */new Map();
503
+ class CustomGutterMarker extends GutterMarker {
504
+ markerText;
505
+ markerTitle;
506
+ markerClass;
507
+ constructor(opts) {
508
+ super();
509
+ this.markerText = opts.text ?? "";
510
+ this.markerTitle = opts.title ?? "";
511
+ this.markerClass = opts.class ?? "";
512
+ }
513
+ toDOM() {
514
+ const el = document.createElement("span");
515
+ el.textContent = this.markerText;
516
+ el.title = this.markerTitle;
517
+ if (this.markerClass) el.className = this.markerClass;
518
+ el.style.cssText = "cursor: pointer; display: inline-block; width: 100%; text-align: center;";
519
+ return el;
520
+ }
521
+ }
522
+ const gutterMarkerExtension = gutter({
523
+ class: "pyreon-code-gutter-markers",
524
+ lineMarker: (gutterView, line) => {
525
+ const lineNo = gutterView.state.doc.lineAt(line.from).number;
526
+ const marker = gutterMarkers.get(lineNo);
527
+ if (!marker) return null;
528
+ return new CustomGutterMarker(marker);
529
+ },
530
+ initialSpacer: () => new CustomGutterMarker({
531
+ text: " "
532
+ })
533
+ });
534
+ function buildExtensions(langExt) {
535
+ const exts = [history(), drawSelection(), dropCursor(), rectangularSelection(), crosshairCursor(), highlightActiveLine(), highlightActiveLineGutter(), highlightSelectionMatches(), indentOnInput(), syntaxHighlighting(defaultHighlightStyle, {
536
+ fallback: true
537
+ }), indentUnit.of(" ".repeat(configTabSize)), keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap, ...foldKeymap, ...completionKeymap, ...lintKeymap, indentWithTab]), languageCompartment.of(langExt), themeCompartment.of(resolveTheme(initialTheme)), readOnlyCompartment.of(EditorState.readOnly.of(initialReadOnly)), extraKeymapCompartment.of([]), keyModeCompartment.of([]), EditorView.updateListener.of(update => {
538
+ if (update.docChanged) {
539
+ const newValue = update.state.doc.toString();
540
+ if (newValue !== value.peek()) {
541
+ value.set(newValue);
542
+ onChange?.(newValue);
543
+ }
544
+ docVersion.update(v => v + 1);
545
+ }
546
+ if (update.selectionSet) docVersion.update(v => v + 1);
547
+ if (update.focusChanged) focused.set(update.view.hasFocus);
548
+ })];
549
+ if (showLineNumbers) exts.push(lineNumbers());
550
+ if (showFoldGutter) exts.push(foldGutter());
551
+ if (enableBracketMatching) exts.push(bracketMatching(), closeBrackets());
552
+ if (enableAutocomplete) exts.push(autocompletion());
553
+ if (enableLineWrapping) exts.push(EditorView.lineWrapping);
554
+ if (enableIndentGuides) exts.push(EditorView.theme({
555
+ ".cm-line": {
556
+ backgroundImage: "linear-gradient(to right, #e5e7eb 1px, transparent 1px)",
557
+ backgroundSize: `${configTabSize}ch 100%`,
558
+ backgroundPosition: "0 0"
559
+ }
560
+ }));
561
+ if (placeholderText) exts.push(placeholder(placeholderText));
562
+ if (enableMinimap) exts.push(minimapExtension());
563
+ exts.push(lineHighlightField);
564
+ exts.push(gutterMarkerExtension);
565
+ exts.push(...userExtensions);
566
+ return exts;
567
+ }
568
+ let mounted = false;
569
+ async function mount(parent) {
570
+ if (mounted) return;
571
+ const extensions = buildExtensions(await loadLanguage(language.peek()));
572
+ const editorView = new EditorView({
573
+ state: EditorState.create({
574
+ doc: value.peek(),
575
+ extensions
576
+ }),
577
+ parent
578
+ });
579
+ view.set(editorView);
580
+ mounted = true;
581
+ effect(() => {
582
+ const val = value();
583
+ const v = view.peek();
584
+ if (!v) return;
585
+ const current = v.state.doc.toString();
586
+ if (val !== current) v.dispatch({
587
+ changes: {
588
+ from: 0,
589
+ to: current.length,
590
+ insert: val
591
+ }
592
+ });
593
+ });
594
+ effect(() => {
595
+ const lang = language();
596
+ const v = view.peek();
597
+ if (!v) return;
598
+ loadLanguage(lang).then(ext => {
599
+ v.dispatch({
600
+ effects: languageCompartment.reconfigure(ext)
601
+ });
602
+ });
603
+ });
604
+ effect(() => {
605
+ const t = theme();
606
+ const v = view.peek();
607
+ if (!v) return;
608
+ v.dispatch({
609
+ effects: themeCompartment.reconfigure(resolveTheme(t))
610
+ });
611
+ });
612
+ effect(() => {
613
+ const ro = readOnly();
614
+ const v = view.peek();
615
+ if (!v) return;
616
+ v.dispatch({
617
+ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(ro))
618
+ });
619
+ });
620
+ }
621
+ function focus() {
622
+ view.peek()?.focus();
623
+ }
624
+ function insert(text) {
625
+ const v = view.peek();
626
+ if (!v) return;
627
+ const pos = v.state.selection.main.head;
628
+ v.dispatch({
629
+ changes: {
630
+ from: pos,
631
+ insert: text
632
+ }
633
+ });
634
+ }
635
+ function replaceSelection(text) {
636
+ const v = view.peek();
637
+ if (!v) return;
638
+ v.dispatch(v.state.replaceSelection(text));
639
+ }
640
+ function select(from, to) {
641
+ const v = view.peek();
642
+ if (!v) return;
643
+ v.dispatch({
644
+ selection: {
645
+ anchor: from,
646
+ head: to
647
+ }
648
+ });
649
+ }
650
+ function selectAll() {
651
+ const v = view.peek();
652
+ if (!v) return;
653
+ v.dispatch({
654
+ selection: {
655
+ anchor: 0,
656
+ head: v.state.doc.length
657
+ }
658
+ });
659
+ }
660
+ function goToLine(line) {
661
+ const v = view.peek();
662
+ if (!v) return;
663
+ const lineInfo = v.state.doc.line(Math.min(Math.max(1, line), v.state.doc.lines));
664
+ v.dispatch({
665
+ selection: {
666
+ anchor: lineInfo.from
667
+ },
668
+ scrollIntoView: true
669
+ });
670
+ v.focus();
671
+ }
672
+ function undo$1() {
673
+ const v = view.peek();
674
+ if (v) undo(v);
675
+ }
676
+ function redo$1() {
677
+ const v = view.peek();
678
+ if (v) redo(v);
679
+ }
680
+ function foldAll() {
681
+ const v = view.peek();
682
+ if (!v) return;
683
+ const {
684
+ foldAll: foldAllCmd
685
+ } = __require("@codemirror/language");
686
+ foldAllCmd(v);
687
+ }
688
+ function unfoldAll() {
689
+ const v = view.peek();
690
+ if (!v) return;
691
+ const {
692
+ unfoldAll: unfoldAllCmd
693
+ } = __require("@codemirror/language");
694
+ unfoldAllCmd(v);
695
+ }
696
+ function setDiagnostics$1(diagnostics) {
697
+ const v = view.peek();
698
+ if (!v) return;
699
+ v.dispatch(setDiagnostics(v.state, diagnostics.map(d => ({
700
+ from: d.from,
701
+ to: d.to,
702
+ severity: d.severity === "hint" ? "info" : d.severity,
703
+ message: d.message,
704
+ source: d.source
705
+ }))));
706
+ }
707
+ function clearDiagnostics() {
708
+ const v = view.peek();
709
+ if (!v) return;
710
+ v.dispatch(setDiagnostics(v.state, []));
711
+ }
712
+ function highlightLine(line, className) {
713
+ lineHighlights.set(line, className);
714
+ const v = view.peek();
715
+ if (v) v.dispatch({
716
+ effects: []
717
+ });
718
+ }
719
+ function clearLineHighlights() {
720
+ lineHighlights.clear();
721
+ const v = view.peek();
722
+ if (v) v.dispatch({
723
+ effects: []
724
+ });
725
+ }
726
+ function setGutterMarker(line, marker) {
727
+ gutterMarkers.set(line, marker);
728
+ const v = view.peek();
729
+ if (v) v.dispatch({
730
+ effects: []
731
+ });
732
+ }
733
+ function clearGutterMarkers() {
734
+ gutterMarkers.clear();
735
+ const v = view.peek();
736
+ if (v) v.dispatch({
737
+ effects: []
738
+ });
739
+ }
740
+ const customKeybindings = [];
741
+ function addKeybinding(key, handler) {
742
+ customKeybindings.push({
743
+ key,
744
+ run: () => {
745
+ handler();
746
+ return true;
747
+ }
748
+ });
749
+ const v = view.peek();
750
+ if (!v) return;
751
+ v.dispatch({
752
+ effects: extraKeymapCompartment.reconfigure(keymap.of(customKeybindings))
753
+ });
754
+ }
755
+ function getLine(line) {
756
+ const v = view.peek();
757
+ if (!v) return "";
758
+ const clamped = Math.min(Math.max(1, line), v.state.doc.lines);
759
+ return v.state.doc.line(clamped).text;
760
+ }
761
+ function getWordAtCursor() {
762
+ const v = view.peek();
763
+ if (!v) return "";
764
+ const pos = v.state.selection.main.head;
765
+ const line = v.state.doc.lineAt(pos);
766
+ const col = pos - line.from;
767
+ const text = line.text;
768
+ let start = col;
769
+ let end = col;
770
+ while (start > 0 && /\w/.test(text[start - 1])) start--;
771
+ while (end < text.length && /\w/.test(text[end])) end++;
772
+ return text.slice(start, end);
773
+ }
774
+ function scrollTo(pos) {
775
+ const v = view.peek();
776
+ if (!v) return;
777
+ v.dispatch({
778
+ effects: EditorView.scrollIntoView(pos, {
779
+ y: "center"
780
+ })
781
+ });
782
+ }
783
+ async function loadKeyMode() {
784
+ const v = view.peek();
785
+ if (!v) return;
786
+ const vimPkg = "@replit/codemirror-vim";
787
+ const emacsPkg = "@replit/codemirror-emacs";
788
+ if (enableVim) try {
789
+ const mod = await import(/* @vite-ignore */
790
+ vimPkg);
791
+ v.dispatch({
792
+ effects: keyModeCompartment.reconfigure(mod.vim())
793
+ });
794
+ } catch {}
795
+ if (enableEmacs) try {
796
+ const mod = await import(/* @vite-ignore */
797
+ emacsPkg);
798
+ v.dispatch({
799
+ effects: keyModeCompartment.reconfigure(mod.emacs())
800
+ });
801
+ } catch {}
802
+ }
803
+ function dispose() {
804
+ const v = view.peek();
805
+ if (v) {
806
+ v.destroy();
807
+ view.set(null);
808
+ mounted = false;
809
+ }
810
+ }
811
+ return {
812
+ value,
813
+ language,
814
+ theme,
815
+ readOnly,
816
+ cursor,
817
+ selection,
818
+ lineCount,
819
+ focused,
820
+ view,
821
+ focus,
822
+ insert,
823
+ replaceSelection,
824
+ select,
825
+ selectAll,
826
+ goToLine,
827
+ undo: undo$1,
828
+ redo: redo$1,
829
+ foldAll,
830
+ unfoldAll,
831
+ setDiagnostics: setDiagnostics$1,
832
+ clearDiagnostics,
833
+ highlightLine,
834
+ clearLineHighlights,
835
+ setGutterMarker,
836
+ clearGutterMarkers,
837
+ addKeybinding,
838
+ getLine,
839
+ getWordAtCursor,
840
+ scrollTo,
841
+ config,
842
+ dispose,
843
+ _mount: async parent => {
844
+ await mount(parent);
845
+ await loadKeyMode();
846
+ }
847
+ };
848
+ }
849
+
850
+ //#endregion
851
+ export { CodeEditor, DiffEditor, TabbedEditor, createEditor, darkTheme, getAvailableLanguages, lightTheme, loadLanguage, minimapExtension, resolveTheme, __exportAll as t };
852
+ //# sourceMappingURL=index.d.ts.map