@pyreon/code 0.11.5 → 0.11.7
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/README.md +18 -18
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +4 -4
- package/package.json +28 -28
- package/src/components/code-editor.tsx +4 -4
- package/src/components/diff-editor.tsx +17 -17
- package/src/components/tabbed-editor.tsx +8 -8
- package/src/editor.ts +38 -38
- package/src/index.ts +8 -8
- package/src/languages.ts +19 -19
- package/src/minimap.ts +16 -16
- package/src/tabbed-editor.ts +11 -11
- package/src/tests/code.test.ts +193 -193
- package/src/themes.ts +48 -48
- package/src/types.ts +27 -27
package/src/editor.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
closeBrackets,
|
|
4
4
|
closeBracketsKeymap,
|
|
5
5
|
completionKeymap,
|
|
6
|
-
} from
|
|
6
|
+
} from '@codemirror/autocomplete'
|
|
7
7
|
import {
|
|
8
8
|
redo as cmRedo,
|
|
9
9
|
undo as cmUndo,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
history,
|
|
12
12
|
historyKeymap,
|
|
13
13
|
indentWithTab,
|
|
14
|
-
} from
|
|
14
|
+
} from '@codemirror/commands'
|
|
15
15
|
import {
|
|
16
16
|
bracketMatching,
|
|
17
17
|
defaultHighlightStyle,
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
indentOnInput,
|
|
21
21
|
indentUnit,
|
|
22
22
|
syntaxHighlighting,
|
|
23
|
-
} from
|
|
24
|
-
import { setDiagnostics as cmSetDiagnostics, lintKeymap } from
|
|
25
|
-
import { highlightSelectionMatches, searchKeymap } from
|
|
26
|
-
import { Compartment, EditorState, type Extension } from
|
|
23
|
+
} from '@codemirror/language'
|
|
24
|
+
import { setDiagnostics as cmSetDiagnostics, lintKeymap } from '@codemirror/lint'
|
|
25
|
+
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
|
26
|
+
import { Compartment, EditorState, type Extension } from '@codemirror/state'
|
|
27
27
|
import {
|
|
28
28
|
GutterMarker as CMGutterMarker,
|
|
29
29
|
crosshairCursor,
|
|
@@ -41,12 +41,12 @@ import {
|
|
|
41
41
|
rectangularSelection,
|
|
42
42
|
ViewPlugin,
|
|
43
43
|
type ViewUpdate,
|
|
44
|
-
} from
|
|
45
|
-
import { computed, effect, signal } from
|
|
46
|
-
import { loadLanguage } from
|
|
47
|
-
import { minimapExtension } from
|
|
48
|
-
import { resolveTheme } from
|
|
49
|
-
import type { EditorConfig, EditorInstance, EditorLanguage, EditorTheme } from
|
|
44
|
+
} from '@codemirror/view'
|
|
45
|
+
import { computed, effect, signal } from '@pyreon/reactivity'
|
|
46
|
+
import { loadLanguage } from './languages'
|
|
47
|
+
import { minimapExtension } from './minimap'
|
|
48
|
+
import { resolveTheme } from './themes'
|
|
49
|
+
import type { EditorConfig, EditorInstance, EditorLanguage, EditorTheme } from './types'
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Create a reactive code editor instance.
|
|
@@ -74,9 +74,9 @@ import type { EditorConfig, EditorInstance, EditorLanguage, EditorTheme } from "
|
|
|
74
74
|
*/
|
|
75
75
|
export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
76
76
|
const {
|
|
77
|
-
value: initialValue =
|
|
78
|
-
language: initialLanguage =
|
|
79
|
-
theme: initialTheme =
|
|
77
|
+
value: initialValue = '',
|
|
78
|
+
language: initialLanguage = 'plain',
|
|
79
|
+
theme: initialTheme = 'light',
|
|
80
80
|
lineNumbers: showLineNumbers = true,
|
|
81
81
|
readOnly: initialReadOnly = false,
|
|
82
82
|
foldGutter: showFoldGutter = true,
|
|
@@ -128,7 +128,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
128
128
|
const selection = computed(() => {
|
|
129
129
|
docVersion()
|
|
130
130
|
const v = view.peek()
|
|
131
|
-
if (!v) return { from: 0, to: 0, text:
|
|
131
|
+
if (!v) return { from: 0, to: 0, text: '' }
|
|
132
132
|
const sel = v.state.selection.main
|
|
133
133
|
return {
|
|
134
134
|
from: sel.from,
|
|
@@ -140,7 +140,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
140
140
|
const lineCount = computed(() => {
|
|
141
141
|
docVersion()
|
|
142
142
|
const v = view.peek()
|
|
143
|
-
return v ? v.state.doc.lines : initialValue.split(
|
|
143
|
+
return v ? v.state.doc.lines : initialValue.split('\n').length
|
|
144
144
|
})
|
|
145
145
|
|
|
146
146
|
// ── Line highlight support ──────────────────────────────────────────
|
|
@@ -191,30 +191,30 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
191
191
|
|
|
192
192
|
constructor(opts: { class?: string; text?: string; title?: string }) {
|
|
193
193
|
super()
|
|
194
|
-
this.markerText = opts.text ??
|
|
195
|
-
this.markerTitle = opts.title ??
|
|
196
|
-
this.markerClass = opts.class ??
|
|
194
|
+
this.markerText = opts.text ?? ''
|
|
195
|
+
this.markerTitle = opts.title ?? ''
|
|
196
|
+
this.markerClass = opts.class ?? ''
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
override toDOM() {
|
|
200
|
-
const el = document.createElement(
|
|
200
|
+
const el = document.createElement('span')
|
|
201
201
|
el.textContent = this.markerText
|
|
202
202
|
el.title = this.markerTitle
|
|
203
203
|
if (this.markerClass) el.className = this.markerClass
|
|
204
|
-
el.style.cssText =
|
|
204
|
+
el.style.cssText = 'cursor: pointer; display: inline-block; width: 100%; text-align: center;'
|
|
205
205
|
return el
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
const gutterMarkerExtension = gutter({
|
|
210
|
-
class:
|
|
210
|
+
class: 'pyreon-code-gutter-markers',
|
|
211
211
|
lineMarker: (gutterView, line) => {
|
|
212
212
|
const lineNo = gutterView.state.doc.lineAt(line.from).number
|
|
213
213
|
const marker = gutterMarkers.get(lineNo)
|
|
214
214
|
if (!marker) return null
|
|
215
215
|
return new CustomGutterMarker(marker)
|
|
216
216
|
},
|
|
217
|
-
initialSpacer: () => new CustomGutterMarker({ text:
|
|
217
|
+
initialSpacer: () => new CustomGutterMarker({ text: ' ' }),
|
|
218
218
|
})
|
|
219
219
|
|
|
220
220
|
// ── Build extensions ─────────────────────────────────────────────────
|
|
@@ -232,7 +232,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
232
232
|
highlightSelectionMatches(),
|
|
233
233
|
indentOnInput(),
|
|
234
234
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
235
|
-
indentUnit.of(
|
|
235
|
+
indentUnit.of(' '.repeat(configTabSize)),
|
|
236
236
|
|
|
237
237
|
// Keymaps
|
|
238
238
|
keymap.of([
|
|
@@ -283,10 +283,10 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
283
283
|
if (enableIndentGuides) {
|
|
284
284
|
exts.push(
|
|
285
285
|
EditorView.theme({
|
|
286
|
-
|
|
287
|
-
backgroundImage:
|
|
286
|
+
'.cm-line': {
|
|
287
|
+
backgroundImage: 'linear-gradient(to right, #e5e7eb 1px, transparent 1px)',
|
|
288
288
|
backgroundSize: `${configTabSize}ch 100%`,
|
|
289
|
-
backgroundPosition:
|
|
289
|
+
backgroundPosition: '0 0',
|
|
290
290
|
},
|
|
291
291
|
}),
|
|
292
292
|
)
|
|
@@ -425,20 +425,20 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
425
425
|
function foldAll(): void {
|
|
426
426
|
const v = view.peek()
|
|
427
427
|
if (!v) return
|
|
428
|
-
const { foldAll: foldAllCmd } = require(
|
|
428
|
+
const { foldAll: foldAllCmd } = require('@codemirror/language')
|
|
429
429
|
foldAllCmd(v)
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
function unfoldAll(): void {
|
|
433
433
|
const v = view.peek()
|
|
434
434
|
if (!v) return
|
|
435
|
-
const { unfoldAll: unfoldAllCmd } = require(
|
|
435
|
+
const { unfoldAll: unfoldAllCmd } = require('@codemirror/language')
|
|
436
436
|
unfoldAllCmd(v)
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
// ── Diagnostics ────────────────────────────────────────────────────
|
|
440
440
|
|
|
441
|
-
function setDiagnostics(diagnostics: import(
|
|
441
|
+
function setDiagnostics(diagnostics: import('./types').Diagnostic[]): void {
|
|
442
442
|
const v = view.peek()
|
|
443
443
|
if (!v) return
|
|
444
444
|
v.dispatch(
|
|
@@ -447,7 +447,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
447
447
|
diagnostics.map((d) => ({
|
|
448
448
|
from: d.from,
|
|
449
449
|
to: d.to,
|
|
450
|
-
severity: d.severity ===
|
|
450
|
+
severity: d.severity === 'hint' ? 'info' : d.severity,
|
|
451
451
|
message: d.message,
|
|
452
452
|
...(d.source != null ? { source: d.source } : {}),
|
|
453
453
|
})),
|
|
@@ -478,7 +478,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
478
478
|
|
|
479
479
|
// ── Gutter markers ────────────────────────────────────────────────
|
|
480
480
|
|
|
481
|
-
function setGutterMarker(line: number, marker: import(
|
|
481
|
+
function setGutterMarker(line: number, marker: import('./types').GutterMarker): void {
|
|
482
482
|
gutterMarkers.set(line, marker)
|
|
483
483
|
const v = view.peek()
|
|
484
484
|
if (v) v.dispatch({ effects: [] })
|
|
@@ -513,14 +513,14 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
513
513
|
|
|
514
514
|
function getLine(line: number): string {
|
|
515
515
|
const v = view.peek()
|
|
516
|
-
if (!v) return
|
|
516
|
+
if (!v) return ''
|
|
517
517
|
const clamped = Math.min(Math.max(1, line), v.state.doc.lines)
|
|
518
518
|
return v.state.doc.line(clamped).text
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
function getWordAtCursor(): string {
|
|
522
522
|
const v = view.peek()
|
|
523
|
-
if (!v) return
|
|
523
|
+
if (!v) return ''
|
|
524
524
|
const pos = v.state.selection.main.head
|
|
525
525
|
const line = v.state.doc.lineAt(pos)
|
|
526
526
|
const col = pos - line.from
|
|
@@ -539,7 +539,7 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
539
539
|
const v = view.peek()
|
|
540
540
|
if (!v) return
|
|
541
541
|
v.dispatch({
|
|
542
|
-
effects: EditorView.scrollIntoView(pos, { y:
|
|
542
|
+
effects: EditorView.scrollIntoView(pos, { y: 'center' }),
|
|
543
543
|
})
|
|
544
544
|
}
|
|
545
545
|
|
|
@@ -550,8 +550,8 @@ export function createEditor(config: EditorConfig = {}): EditorInstance {
|
|
|
550
550
|
if (!v) return
|
|
551
551
|
|
|
552
552
|
// Use string concat to prevent Vite from statically analyzing these optional imports
|
|
553
|
-
const vimPkg =
|
|
554
|
-
const emacsPkg =
|
|
553
|
+
const vimPkg = '@replit/codemirror-' + 'vim'
|
|
554
|
+
const emacsPkg = '@replit/codemirror-' + 'emacs'
|
|
555
555
|
|
|
556
556
|
if (enableVim) {
|
|
557
557
|
try {
|
package/src/index.ts
CHANGED
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
// Components
|
|
27
|
-
export { CodeEditor } from
|
|
28
|
-
export { DiffEditor } from
|
|
29
|
-
export { TabbedEditor } from
|
|
27
|
+
export { CodeEditor } from './components/code-editor'
|
|
28
|
+
export { DiffEditor } from './components/diff-editor'
|
|
29
|
+
export { TabbedEditor } from './components/tabbed-editor'
|
|
30
30
|
// Core
|
|
31
|
-
export { createEditor } from
|
|
31
|
+
export { createEditor } from './editor'
|
|
32
32
|
// Languages
|
|
33
|
-
export { getAvailableLanguages, loadLanguage } from
|
|
33
|
+
export { getAvailableLanguages, loadLanguage } from './languages'
|
|
34
34
|
// Minimap
|
|
35
|
-
export { minimapExtension } from
|
|
35
|
+
export { minimapExtension } from './minimap'
|
|
36
36
|
// Themes
|
|
37
|
-
export { darkTheme, lightTheme, resolveTheme } from
|
|
37
|
+
export { darkTheme, lightTheme, resolveTheme } from './themes'
|
|
38
38
|
|
|
39
39
|
// Types
|
|
40
40
|
export type {
|
|
@@ -49,4 +49,4 @@ export type {
|
|
|
49
49
|
TabbedEditorConfig,
|
|
50
50
|
TabbedEditorInstance,
|
|
51
51
|
TabbedEditorProps,
|
|
52
|
-
} from
|
|
52
|
+
} from './types'
|
package/src/languages.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import type { Extension } from
|
|
2
|
-
import type { EditorLanguage } from
|
|
1
|
+
import type { Extension } from '@codemirror/state'
|
|
2
|
+
import type { EditorLanguage } from './types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Language extension loaders — lazy-loaded on demand.
|
|
6
6
|
* Only the requested language is imported, keeping the initial bundle small.
|
|
7
7
|
*/
|
|
8
8
|
const languageLoaders: Record<EditorLanguage, () => Promise<Extension>> = {
|
|
9
|
-
javascript: () => import(
|
|
9
|
+
javascript: () => import('@codemirror/lang-javascript').then((m) => m.javascript()),
|
|
10
10
|
typescript: () =>
|
|
11
|
-
import(
|
|
12
|
-
jsx: () => import(
|
|
11
|
+
import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),
|
|
12
|
+
jsx: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ jsx: true })),
|
|
13
13
|
tsx: () =>
|
|
14
|
-
import(
|
|
14
|
+
import('@codemirror/lang-javascript').then((m) =>
|
|
15
15
|
m.javascript({ typescript: true, jsx: true }),
|
|
16
16
|
),
|
|
17
|
-
html: () => import(
|
|
18
|
-
css: () => import(
|
|
19
|
-
json: () => import(
|
|
20
|
-
markdown: () => import(
|
|
21
|
-
python: () => import(
|
|
22
|
-
rust: () => import(
|
|
23
|
-
sql: () => import(
|
|
24
|
-
xml: () => import(
|
|
25
|
-
yaml: () => import(
|
|
26
|
-
cpp: () => import(
|
|
27
|
-
java: () => import(
|
|
28
|
-
go: () => import(
|
|
29
|
-
php: () => import(
|
|
17
|
+
html: () => import('@codemirror/lang-html').then((m) => m.html()),
|
|
18
|
+
css: () => import('@codemirror/lang-css').then((m) => m.css()),
|
|
19
|
+
json: () => import('@codemirror/lang-json').then((m) => m.json()),
|
|
20
|
+
markdown: () => import('@codemirror/lang-markdown').then((m) => m.markdown()),
|
|
21
|
+
python: () => import('@codemirror/lang-python').then((m) => m.python()),
|
|
22
|
+
rust: () => import('@codemirror/lang-rust').then((m) => m.rust()),
|
|
23
|
+
sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),
|
|
24
|
+
xml: () => import('@codemirror/lang-xml').then((m) => m.xml()),
|
|
25
|
+
yaml: () => import('@codemirror/lang-yaml').then((m) => m.yaml()),
|
|
26
|
+
cpp: () => import('@codemirror/lang-cpp').then((m) => m.cpp()),
|
|
27
|
+
java: () => import('@codemirror/lang-java').then((m) => m.java()),
|
|
28
|
+
go: () => import('@codemirror/lang-go').then((m) => m.go()),
|
|
29
|
+
php: () => import('@codemirror/lang-php').then((m) => m.php()),
|
|
30
30
|
ruby: () => Promise.resolve([]),
|
|
31
31
|
shell: () => Promise.resolve([]),
|
|
32
32
|
plain: () => Promise.resolve([]),
|
package/src/minimap.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Extension } from
|
|
2
|
-
import { EditorView, ViewPlugin, type ViewUpdate } from
|
|
1
|
+
import type { Extension } from '@codemirror/state'
|
|
2
|
+
import { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Canvas-based minimap extension for CodeMirror 6.
|
|
@@ -9,22 +9,22 @@ import { EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view"
|
|
|
9
9
|
const MINIMAP_WIDTH = 80
|
|
10
10
|
const CHAR_WIDTH = 1.2
|
|
11
11
|
const LINE_HEIGHT = 2.5
|
|
12
|
-
const MINIMAP_BG =
|
|
13
|
-
const MINIMAP_BG_LIGHT =
|
|
14
|
-
const TEXT_COLOR =
|
|
15
|
-
const TEXT_COLOR_LIGHT =
|
|
16
|
-
const VIEWPORT_COLOR =
|
|
17
|
-
const VIEWPORT_BORDER =
|
|
12
|
+
const MINIMAP_BG = '#1e1e2e'
|
|
13
|
+
const MINIMAP_BG_LIGHT = '#f8fafc'
|
|
14
|
+
const TEXT_COLOR = '#585b70'
|
|
15
|
+
const TEXT_COLOR_LIGHT = '#94a3b8'
|
|
16
|
+
const VIEWPORT_COLOR = 'rgba(59, 130, 246, 0.15)'
|
|
17
|
+
const VIEWPORT_BORDER = 'rgba(59, 130, 246, 0.4)'
|
|
18
18
|
|
|
19
19
|
function createMinimapCanvas(): HTMLCanvasElement {
|
|
20
|
-
const canvas = document.createElement(
|
|
20
|
+
const canvas = document.createElement('canvas')
|
|
21
21
|
canvas.style.cssText = `position: absolute; right: 0; top: 0; width: ${MINIMAP_WIDTH}px; height: 100%; cursor: pointer; z-index: 5;`
|
|
22
22
|
canvas.width = MINIMAP_WIDTH * 2 // retina
|
|
23
23
|
return canvas
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function renderMinimap(canvas: HTMLCanvasElement, view: EditorView): void {
|
|
27
|
-
const ctx = canvas.getContext(
|
|
27
|
+
const ctx = canvas.getContext('2d')
|
|
28
28
|
if (!ctx) return
|
|
29
29
|
|
|
30
30
|
const doc = view.state.doc
|
|
@@ -32,7 +32,7 @@ function renderMinimap(canvas: HTMLCanvasElement, view: EditorView): void {
|
|
|
32
32
|
const height = canvas.clientHeight
|
|
33
33
|
canvas.height = height * 2 // retina
|
|
34
34
|
|
|
35
|
-
const isDark = view.dom.classList.contains(
|
|
35
|
+
const isDark = view.dom.classList.contains('cm-dark')
|
|
36
36
|
const bg = isDark ? MINIMAP_BG : MINIMAP_BG_LIGHT
|
|
37
37
|
const textColor = isDark ? TEXT_COLOR : TEXT_COLOR_LIGHT
|
|
38
38
|
|
|
@@ -64,7 +64,7 @@ function renderMinimap(canvas: HTMLCanvasElement, view: EditorView): void {
|
|
|
64
64
|
const text = line.text
|
|
65
65
|
let x = 4
|
|
66
66
|
for (let j = 0; j < Math.min(text.length, 60); j++) {
|
|
67
|
-
if (text[j] !==
|
|
67
|
+
if (text[j] !== ' ' && text[j] !== '\t') {
|
|
68
68
|
ctx.fillRect(x, y, CHAR_WIDTH, 1.5)
|
|
69
69
|
}
|
|
70
70
|
x += CHAR_WIDTH
|
|
@@ -107,17 +107,17 @@ export function minimapExtension(): Extension {
|
|
|
107
107
|
constructor(view: EditorView) {
|
|
108
108
|
this.view = view
|
|
109
109
|
this.canvas = createMinimapCanvas()
|
|
110
|
-
view.dom.style.position =
|
|
110
|
+
view.dom.style.position = 'relative'
|
|
111
111
|
view.dom.appendChild(this.canvas)
|
|
112
112
|
|
|
113
113
|
// Click to scroll
|
|
114
|
-
this.canvas.addEventListener(
|
|
114
|
+
this.canvas.addEventListener('click', (e) => {
|
|
115
115
|
const rect = this.canvas.getBoundingClientRect()
|
|
116
116
|
const clickY = e.clientY - rect.top
|
|
117
117
|
const fraction = clickY / rect.height
|
|
118
118
|
const scrollTarget =
|
|
119
119
|
fraction * (view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight)
|
|
120
|
-
view.scrollDOM.scrollTo({ top: scrollTarget, behavior:
|
|
120
|
+
view.scrollDOM.scrollTo({ top: scrollTarget, behavior: 'smooth' })
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
this.render()
|
|
@@ -142,7 +142,7 @@ export function minimapExtension(): Extension {
|
|
|
142
142
|
),
|
|
143
143
|
// Add padding on the right for the minimap
|
|
144
144
|
EditorView.theme({
|
|
145
|
-
|
|
145
|
+
'.cm-scroller': {
|
|
146
146
|
paddingRight: `${MINIMAP_WIDTH + 8}px`,
|
|
147
147
|
},
|
|
148
148
|
}),
|
package/src/tabbed-editor.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { computed, signal } from
|
|
2
|
-
import { createEditor } from
|
|
3
|
-
import type { EditorLanguage, Tab, TabbedEditorConfig, TabbedEditorInstance } from
|
|
1
|
+
import { computed, signal } from '@pyreon/reactivity'
|
|
2
|
+
import { createEditor } from './editor'
|
|
3
|
+
import type { EditorLanguage, Tab, TabbedEditorConfig, TabbedEditorInstance } from './types'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Create a tabbed code editor — multiple files with tab management.
|
|
@@ -42,7 +42,7 @@ export function createTabbedEditor(config: TabbedEditorConfig = {}): TabbedEdito
|
|
|
42
42
|
// ── State ──────────────────────────────────────────────────────────────
|
|
43
43
|
|
|
44
44
|
const tabs = signal<Tab[]>(tabsWithIds)
|
|
45
|
-
const activeTabId = signal(tabsWithIds[0]?.id ??
|
|
45
|
+
const activeTabId = signal(tabsWithIds[0]?.id ?? '')
|
|
46
46
|
|
|
47
47
|
// Content cache — stores each tab's current content
|
|
48
48
|
const contentCache = new Map<string, string>()
|
|
@@ -59,8 +59,8 @@ export function createTabbedEditor(config: TabbedEditorConfig = {}): TabbedEdito
|
|
|
59
59
|
Object.entries(editorConfig).filter(([_, v]) => v !== undefined),
|
|
60
60
|
)
|
|
61
61
|
const editor = createEditor({
|
|
62
|
-
value: firstTab?.value ??
|
|
63
|
-
language: (firstTab?.language ??
|
|
62
|
+
value: firstTab?.value ?? '',
|
|
63
|
+
language: (firstTab?.language ?? 'plain') as EditorLanguage,
|
|
64
64
|
...(theme != null ? { theme } : {}),
|
|
65
65
|
...filteredConfig,
|
|
66
66
|
onChange: (value) => {
|
|
@@ -106,7 +106,7 @@ export function createTabbedEditor(config: TabbedEditorConfig = {}): TabbedEdito
|
|
|
106
106
|
// Restore target tab content
|
|
107
107
|
const cached = contentCache.get(id)
|
|
108
108
|
editor.value.set(cached ?? tab.value)
|
|
109
|
-
editor.language.set((tab.language ??
|
|
109
|
+
editor.language.set((tab.language ?? 'plain') as EditorLanguage)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function openTab(tab: Tab): void {
|
|
@@ -145,8 +145,8 @@ export function createTabbedEditor(config: TabbedEditorConfig = {}): TabbedEdito
|
|
|
145
145
|
const nextTab = remaining[nextIndex]
|
|
146
146
|
if (nextTab) switchTab(nextTab.id ?? nextTab.name)
|
|
147
147
|
} else {
|
|
148
|
-
activeTabId.set(
|
|
149
|
-
editor.value.set(
|
|
148
|
+
activeTabId.set('')
|
|
149
|
+
editor.value.set('')
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -183,8 +183,8 @@ export function createTabbedEditor(config: TabbedEditorConfig = {}): TabbedEdito
|
|
|
183
183
|
const first = remaining[0]
|
|
184
184
|
if (first) switchTab(first.id ?? first.name)
|
|
185
185
|
} else {
|
|
186
|
-
activeTabId.set(
|
|
187
|
-
editor.value.set(
|
|
186
|
+
activeTabId.set('')
|
|
187
|
+
editor.value.set('')
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|