@salemcobalt/ui 0.2.0-rc.3 → 0.2.0-rc.31
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 +99 -21
- package/dist/codemirror-6REE3TUN.js +383 -0
- package/dist/codemirror-6REE3TUN.js.map +1 -0
- package/dist/editor.d.ts +55 -0
- package/dist/editor.js +174 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +1439 -62
- package/dist/index.js +2554 -88
- package/dist/index.js.map +1 -1
- package/dist/markdown-5O5GUVBJ.js +10 -0
- package/dist/markdown-5O5GUVBJ.js.map +1 -0
- package/dist/styles.css +3824 -38
- package/package.json +59 -6
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// src/components/TextEditor/codemirror.ts
|
|
4
|
+
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
|
5
|
+
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
6
|
+
import { Compartment, EditorState } from "@codemirror/state";
|
|
7
|
+
import { EditorView as EditorView2, keymap } from "@codemirror/view";
|
|
8
|
+
import { tags as t } from "@lezer/highlight";
|
|
9
|
+
|
|
10
|
+
// src/components/TextEditor/wordDiff.ts
|
|
11
|
+
import {
|
|
12
|
+
Facet,
|
|
13
|
+
StateEffect,
|
|
14
|
+
StateField
|
|
15
|
+
} from "@codemirror/state";
|
|
16
|
+
import {
|
|
17
|
+
Decoration,
|
|
18
|
+
EditorView,
|
|
19
|
+
ViewPlugin,
|
|
20
|
+
WidgetType
|
|
21
|
+
} from "@codemirror/view";
|
|
22
|
+
|
|
23
|
+
// src/components/TextEditor/diffParts.ts
|
|
24
|
+
import { diffLines, diffWordsWithSpace } from "diff";
|
|
25
|
+
var WORD_DIFF_MAX_CHANGE = 0.6;
|
|
26
|
+
var WORD_DIFF_MAX_CHARS = 4e3;
|
|
27
|
+
function pair(a, b) {
|
|
28
|
+
if (a.length + b.length <= WORD_DIFF_MAX_CHARS) {
|
|
29
|
+
const parts = diffWordsWithSpace(a, b).map((p) => ({
|
|
30
|
+
kind: p.added ? "added" : p.removed ? "removed" : "same",
|
|
31
|
+
text: p.value
|
|
32
|
+
}));
|
|
33
|
+
const changed = parts.reduce((n, p) => n + (p.kind === "same" ? 0 : p.text.length), 0);
|
|
34
|
+
if (changed / (a.length + b.length) <= WORD_DIFF_MAX_CHANGE) return parts;
|
|
35
|
+
}
|
|
36
|
+
return [
|
|
37
|
+
{ kind: "removed", text: a },
|
|
38
|
+
{ kind: "added", text: b }
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
var lines = (text) => text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
42
|
+
function hunk(a, b) {
|
|
43
|
+
const removed = lines(a);
|
|
44
|
+
const added = lines(b);
|
|
45
|
+
const n = Math.min(removed.length, added.length);
|
|
46
|
+
const out = [];
|
|
47
|
+
for (let i = 0; i < n; i++) out.push(...pair(removed[i], added[i]));
|
|
48
|
+
out.push({ kind: "removed", text: removed.slice(n).join("") });
|
|
49
|
+
out.push({ kind: "added", text: added.slice(n).join("") });
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function diffParts(base, text) {
|
|
53
|
+
const out = [];
|
|
54
|
+
const push = ({ kind, text: text2 }) => {
|
|
55
|
+
if (!text2) return;
|
|
56
|
+
const last = out.at(-1);
|
|
57
|
+
if (last?.kind === kind) last.text += text2;
|
|
58
|
+
else out.push({ kind, text: text2 });
|
|
59
|
+
};
|
|
60
|
+
const changes = diffLines(base, text);
|
|
61
|
+
for (let i = 0; i < changes.length; ) {
|
|
62
|
+
const c = changes[i];
|
|
63
|
+
if (!c.added && !c.removed) {
|
|
64
|
+
push({ kind: "same", text: c.value });
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
let removed = "";
|
|
69
|
+
let added = "";
|
|
70
|
+
for (; i < changes.length && (changes[i].added || changes[i].removed); i++) {
|
|
71
|
+
if (changes[i].added) added += changes[i].value;
|
|
72
|
+
else removed += changes[i].value;
|
|
73
|
+
}
|
|
74
|
+
hunk(removed, added).forEach(push);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/components/TextEditor/wordDiff.ts
|
|
80
|
+
var edges = /^(\s*)([\s\S]*?)(\s*)$/;
|
|
81
|
+
var hidden = (text) => Object.assign(document.createElement("span"), { className: "sc-visually-hidden", textContent: text });
|
|
82
|
+
var RemovedWidget = class extends WidgetType {
|
|
83
|
+
constructor(text, block, label) {
|
|
84
|
+
super();
|
|
85
|
+
this.text = text;
|
|
86
|
+
this.block = block;
|
|
87
|
+
this.label = label;
|
|
88
|
+
}
|
|
89
|
+
text;
|
|
90
|
+
block;
|
|
91
|
+
label;
|
|
92
|
+
eq(other) {
|
|
93
|
+
return other.text === this.text && other.block === this.block && other.label === this.label;
|
|
94
|
+
}
|
|
95
|
+
toDOM() {
|
|
96
|
+
const [, lead = "", core = "", trail = ""] = edges.exec(this.text) ?? [];
|
|
97
|
+
const wrap = document.createElement(this.block ? "div" : "span");
|
|
98
|
+
wrap.className = this.block ? "cm-sc-removed-lines" : "cm-sc-removed";
|
|
99
|
+
const del = document.createElement("del");
|
|
100
|
+
del.className = "sc-word-diff__removed";
|
|
101
|
+
del.append(hidden(`[${this.label}: `), core, hidden("]"));
|
|
102
|
+
wrap.append(lead, del, trail);
|
|
103
|
+
return wrap;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var LabelWidget = class extends WidgetType {
|
|
107
|
+
constructor(text) {
|
|
108
|
+
super();
|
|
109
|
+
this.text = text;
|
|
110
|
+
}
|
|
111
|
+
text;
|
|
112
|
+
eq(other) {
|
|
113
|
+
return other.text === this.text;
|
|
114
|
+
}
|
|
115
|
+
toDOM() {
|
|
116
|
+
return hidden(this.text);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var addedMark = Decoration.mark({ tagName: "ins", class: "sc-word-diff__added" });
|
|
120
|
+
function diffDecorations(doc, base, labels = {}) {
|
|
121
|
+
const { added = "added", removed = "removed" } = labels;
|
|
122
|
+
const ranges = [];
|
|
123
|
+
let pos = 0;
|
|
124
|
+
for (const part of diffParts(base, doc.toString())) {
|
|
125
|
+
const [, lead = "", core = ""] = edges.exec(part.text) ?? [];
|
|
126
|
+
if (part.kind === "same") pos += part.text.length;
|
|
127
|
+
else if (part.kind === "added") {
|
|
128
|
+
if (core) {
|
|
129
|
+
const from = pos + lead.length;
|
|
130
|
+
const to = from + core.length;
|
|
131
|
+
ranges.push(
|
|
132
|
+
Decoration.widget({ widget: new LabelWidget(`[${added}: `), side: -1, kind: "label" }).range(from),
|
|
133
|
+
addedMark.range(from, to),
|
|
134
|
+
Decoration.widget({ widget: new LabelWidget("]"), side: 1, kind: "label" }).range(to)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
pos += part.text.length;
|
|
138
|
+
} else if (core) {
|
|
139
|
+
if (!part.text.includes("\n")) {
|
|
140
|
+
const widget = new RemovedWidget(part.text, false, removed);
|
|
141
|
+
ranges.push(Decoration.widget({ widget, side: -1, kind: "removed" }).range(pos));
|
|
142
|
+
} else {
|
|
143
|
+
const line = doc.lineAt(pos);
|
|
144
|
+
const below = pos === line.to && line.to > line.from;
|
|
145
|
+
const widget = new RemovedWidget(part.text.replace(/^\n+|\n+$/g, ""), true, removed);
|
|
146
|
+
ranges.push(
|
|
147
|
+
Decoration.widget({ widget, side: below ? 1 : -1, block: true, kind: "removed" }).range(
|
|
148
|
+
below ? line.to : line.from
|
|
149
|
+
)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return Decoration.set(ranges, true);
|
|
155
|
+
}
|
|
156
|
+
var DEBOUNCE_MS = 300;
|
|
157
|
+
var config = Facet.define({
|
|
158
|
+
combine: (values) => values[0] ?? { base: "", live: false, labels: {} }
|
|
159
|
+
});
|
|
160
|
+
var setDiffFocus = StateEffect.define();
|
|
161
|
+
var refresh = StateEffect.define();
|
|
162
|
+
var compute = (state, focused) => {
|
|
163
|
+
const { base, labels } = state.facet(config);
|
|
164
|
+
return { decorations: diffDecorations(state.doc, base, labels), focused, stale: false };
|
|
165
|
+
};
|
|
166
|
+
var diffField = StateField.define({
|
|
167
|
+
create: (state) => compute(state, false),
|
|
168
|
+
update(value, tr) {
|
|
169
|
+
let focused = value.focused;
|
|
170
|
+
let refreshed = false;
|
|
171
|
+
for (const e of tr.effects) {
|
|
172
|
+
if (e.is(setDiffFocus)) focused = e.value;
|
|
173
|
+
if (e.is(refresh)) refreshed = true;
|
|
174
|
+
}
|
|
175
|
+
const next = tr.state.facet(config);
|
|
176
|
+
if (focused && !next.live) return { decorations: Decoration.none, focused, stale: false };
|
|
177
|
+
const configChanged = next !== tr.startState.facet(config);
|
|
178
|
+
if (focused && tr.docChanged && !refreshed && !configChanged)
|
|
179
|
+
return { decorations: value.decorations.map(tr.changes), focused, stale: true };
|
|
180
|
+
if (refreshed || configChanged || tr.docChanged || focused !== value.focused)
|
|
181
|
+
return compute(tr.state, focused);
|
|
182
|
+
return value;
|
|
183
|
+
},
|
|
184
|
+
// Block widgets have to come from state, not a view plugin.
|
|
185
|
+
provide: (field) => EditorView.decorations.from(field, (value) => value.decorations)
|
|
186
|
+
});
|
|
187
|
+
var debounce = ViewPlugin.fromClass(
|
|
188
|
+
class {
|
|
189
|
+
constructor(view) {
|
|
190
|
+
this.view = view;
|
|
191
|
+
}
|
|
192
|
+
view;
|
|
193
|
+
timer;
|
|
194
|
+
update(u) {
|
|
195
|
+
if (!u.docChanged || !u.state.field(diffField).stale) return;
|
|
196
|
+
clearTimeout(this.timer);
|
|
197
|
+
this.timer = setTimeout(() => this.view.dispatch({ effects: refresh.of(null) }), DEBOUNCE_MS);
|
|
198
|
+
}
|
|
199
|
+
destroy() {
|
|
200
|
+
clearTimeout(this.timer);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
var theme = EditorView.theme({
|
|
205
|
+
".cm-sc-removed-lines": { paddingInline: "var(--sc-spacing-sm)", whiteSpace: "pre-wrap" }
|
|
206
|
+
});
|
|
207
|
+
function wordDiff(base, options = {}) {
|
|
208
|
+
return [
|
|
209
|
+
config.of({ base, live: options.live ?? false, labels: options.labels ?? {} }),
|
|
210
|
+
diffField,
|
|
211
|
+
debounce,
|
|
212
|
+
EditorView.focusChangeEffect.of((_state, focusing) => setDiffFocus.of(focusing)),
|
|
213
|
+
theme
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/components/TextEditor/codemirror.ts
|
|
218
|
+
var languages = {
|
|
219
|
+
yaml: () => import("@codemirror/lang-yaml").then((m) => m.yaml()),
|
|
220
|
+
json: () => import("@codemirror/lang-json").then((m) => m.json()),
|
|
221
|
+
markdown: () => import("./markdown-5O5GUVBJ.js").then((m) => m.markdown())
|
|
222
|
+
};
|
|
223
|
+
var isProse = (language) => language === "markdown" || language === "text";
|
|
224
|
+
var TOUCH_BOTTOM_MARGIN = 96;
|
|
225
|
+
var coarse = () => typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
226
|
+
var theme2 = EditorView2.theme({
|
|
227
|
+
"&": {
|
|
228
|
+
border: "var(--sc-border-width-hairline) solid var(--sc-color-border-strong)",
|
|
229
|
+
borderRadius: "var(--sc-radius-md)",
|
|
230
|
+
background: "var(--sc-color-surface-default)",
|
|
231
|
+
color: "var(--sc-color-text-default)",
|
|
232
|
+
transition: "border-color var(--sc-duration-fast) var(--sc-ease-standard), background-color var(--sc-duration-fast) var(--sc-ease-standard)"
|
|
233
|
+
},
|
|
234
|
+
"&:hover:not([data-disabled])": { borderColor: "var(--sc-color-text-muted)" },
|
|
235
|
+
"&[data-invalid]": { borderColor: "var(--sc-color-danger-solid)" },
|
|
236
|
+
"&.cm-focused": {
|
|
237
|
+
outline: "var(--sc-border-width-focus) solid var(--sc-color-border-focus)",
|
|
238
|
+
outlineOffset: "0",
|
|
239
|
+
borderColor: "var(--sc-color-border-focus)"
|
|
240
|
+
},
|
|
241
|
+
"&[data-disabled]": {
|
|
242
|
+
background: "var(--sc-color-surface-sunken)",
|
|
243
|
+
color: "var(--sc-color-text-subtle)",
|
|
244
|
+
cursor: "not-allowed"
|
|
245
|
+
},
|
|
246
|
+
"&[data-disabled] .cm-content": { cursor: "not-allowed" },
|
|
247
|
+
".cm-scroller": {
|
|
248
|
+
fontFamily: "var(--sc-font-family-mono)",
|
|
249
|
+
lineHeight: "var(--sc-font-line-height-normal)"
|
|
250
|
+
},
|
|
251
|
+
".cm-content": {
|
|
252
|
+
padding: "var(--sc-spacing-xs) 0",
|
|
253
|
+
fontFamily: "var(--sc-font-family-mono)",
|
|
254
|
+
fontSize: "calc(var(--sc-font-size-sm) * 0.95)",
|
|
255
|
+
lineHeight: "var(--sc-font-line-height-normal)",
|
|
256
|
+
minBlockSize: "calc(var(--_rows, 3) * 1lh)",
|
|
257
|
+
caretColor: "var(--sc-color-text-default)"
|
|
258
|
+
},
|
|
259
|
+
".cm-line, .cm-sc-removed-lines": { paddingInline: "var(--sc-spacing-sm)" },
|
|
260
|
+
// Touch: iOS zooms the page into a focused field under 16px (as in field.css). Room under the last line, so a tap
|
|
261
|
+
// there isn't near Safari's collapsed toolbar, which takes taps at the bottom edge of the screen.
|
|
262
|
+
"@media (pointer: coarse)": {
|
|
263
|
+
".cm-content": {
|
|
264
|
+
fontSize: "max(16px, calc(var(--sc-font-size-sm) * 0.95))",
|
|
265
|
+
paddingBlockEnd: "var(--sc-spacing-xl)"
|
|
266
|
+
},
|
|
267
|
+
".cm-scroller": { scrollPaddingBlockEnd: `${TOUCH_BOTTOM_MARGIN}px` }
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
var highlight = HighlightStyle.define([
|
|
271
|
+
{ tag: [t.propertyName, t.definition(t.propertyName)], color: "var(--sc-color-accent-text)" },
|
|
272
|
+
{ tag: t.heading, color: "var(--sc-color-accent-text)", fontWeight: "var(--sc-font-weight-semibold)" },
|
|
273
|
+
{ tag: [t.string, t.special(t.string)], color: "var(--sc-color-info-text)" },
|
|
274
|
+
{ tag: [t.number, t.bool, t.null, t.atom], color: "var(--sc-color-warning-text)" },
|
|
275
|
+
{
|
|
276
|
+
tag: [t.comment, t.meta, t.punctuation, t.separator, t.processingInstruction],
|
|
277
|
+
color: "var(--sc-color-text-muted)"
|
|
278
|
+
},
|
|
279
|
+
{ tag: [t.link, t.url], color: "var(--sc-color-text-link)" },
|
|
280
|
+
{ tag: t.emphasis, fontStyle: "italic" },
|
|
281
|
+
{ tag: t.strong, fontWeight: "var(--sc-font-weight-semibold)" }
|
|
282
|
+
]);
|
|
283
|
+
var diffExtension = (o) => o.base != null ? wordDiff(o.base, { live: o.liveDiff }) : [];
|
|
284
|
+
function stateExtension(o) {
|
|
285
|
+
const flag = (on) => on ? "true" : "false";
|
|
286
|
+
return [
|
|
287
|
+
EditorView2.editable.of(!o.disabled),
|
|
288
|
+
EditorState.readOnly.of(o.disabled || o.readOnly),
|
|
289
|
+
EditorView2.editorAttributes.of({
|
|
290
|
+
...o.invalid ? { "data-invalid": "" } : {},
|
|
291
|
+
...o.disabled ? { "data-disabled": "" } : {},
|
|
292
|
+
...o.readOnly ? { "data-readonly": "" } : {}
|
|
293
|
+
}),
|
|
294
|
+
EditorView2.contentAttributes.of({
|
|
295
|
+
spellcheck: flag(isProse(o.language)),
|
|
296
|
+
autocorrect: isProse(o.language) ? "on" : "off",
|
|
297
|
+
autocapitalize: isProse(o.language) ? "sentences" : "off",
|
|
298
|
+
...o.invalid ? { "aria-invalid": "true" } : {},
|
|
299
|
+
...o.disabled ? { "aria-disabled": "true" } : {},
|
|
300
|
+
// Read-only stays focusable and selectable; inputmode none keeps the phone's keyboard down.
|
|
301
|
+
...o.readOnly ? { "aria-readonly": "true", inputmode: "none" } : {},
|
|
302
|
+
...o.attributes
|
|
303
|
+
})
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
var stateKey = (o) => JSON.stringify([o.language, o.disabled, o.readOnly, o.invalid, o.attributes]);
|
|
307
|
+
function mountEditor(parent, options, onChange) {
|
|
308
|
+
const diffSlot = new Compartment();
|
|
309
|
+
const languageSlot = new Compartment();
|
|
310
|
+
const stateSlot = new Compartment();
|
|
311
|
+
let current = options;
|
|
312
|
+
let destroyed = false;
|
|
313
|
+
const loadLanguage = (language) => {
|
|
314
|
+
if (language === "text") return view.dispatch({ effects: languageSlot.reconfigure([]) });
|
|
315
|
+
void languages[language]().then((extension) => {
|
|
316
|
+
if (!destroyed && current.language === language)
|
|
317
|
+
view.dispatch({ effects: languageSlot.reconfigure(extension) });
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
const view = new EditorView2({
|
|
321
|
+
parent,
|
|
322
|
+
state: EditorState.create({
|
|
323
|
+
doc: options.value,
|
|
324
|
+
extensions: [
|
|
325
|
+
history(),
|
|
326
|
+
keymap.of([...defaultKeymap, ...historyKeymap]),
|
|
327
|
+
EditorView2.lineWrapping,
|
|
328
|
+
EditorState.tabSize.of(2),
|
|
329
|
+
EditorView2.scrollMargins.of(() => coarse() ? { bottom: TOUCH_BOTTOM_MARGIN } : null),
|
|
330
|
+
theme2,
|
|
331
|
+
syntaxHighlighting(highlight),
|
|
332
|
+
// WordDiff's own classes, so the marks and widgets look like WordDiff family="mono".
|
|
333
|
+
EditorView2.contentAttributes.of({ class: "sc-word-diff", "data-family": "mono" }),
|
|
334
|
+
stateSlot.of(stateExtension(options)),
|
|
335
|
+
languageSlot.of([]),
|
|
336
|
+
diffSlot.of(diffExtension(options)),
|
|
337
|
+
EditorView2.updateListener.of((u) => {
|
|
338
|
+
if (u.docChanged) onChange(u.state.doc.toString());
|
|
339
|
+
})
|
|
340
|
+
]
|
|
341
|
+
})
|
|
342
|
+
});
|
|
343
|
+
loadLanguage(options.language);
|
|
344
|
+
return {
|
|
345
|
+
update(next) {
|
|
346
|
+
const prev = current;
|
|
347
|
+
current = next;
|
|
348
|
+
const effects = [];
|
|
349
|
+
const diffAdded = next.base != null && prev.base == null;
|
|
350
|
+
if (next.base !== prev.base || next.liveDiff !== prev.liveDiff)
|
|
351
|
+
effects.push(diffSlot.reconfigure(diffExtension(next)));
|
|
352
|
+
if (stateKey(next) !== stateKey(prev)) effects.push(stateSlot.reconfigure(stateExtension(next)));
|
|
353
|
+
const doc = view.state.doc;
|
|
354
|
+
const changes = next.value !== doc.toString() ? { from: 0, to: doc.length, insert: next.value } : void 0;
|
|
355
|
+
if (effects.length || changes) view.dispatch(changes ? { effects, changes } : { effects });
|
|
356
|
+
if (diffAdded && view.hasFocus) view.dispatch({ effects: setDiffFocus.of(true) });
|
|
357
|
+
if (next.language !== prev.language) loadLanguage(next.language);
|
|
358
|
+
},
|
|
359
|
+
focus(anchor, head) {
|
|
360
|
+
if (current.disabled) return;
|
|
361
|
+
if (anchor != null) {
|
|
362
|
+
const max = view.state.doc.length;
|
|
363
|
+
view.dispatch({ selection: { anchor: Math.min(anchor, max), head: Math.min(head ?? anchor, max) } });
|
|
364
|
+
}
|
|
365
|
+
view.focus();
|
|
366
|
+
},
|
|
367
|
+
selectLine(line) {
|
|
368
|
+
if (current.disabled) return;
|
|
369
|
+
const doc = view.state.doc;
|
|
370
|
+
const { from, to } = doc.line(Math.min(Math.max(Math.trunc(line), 1), doc.lines));
|
|
371
|
+
view.dispatch({ selection: { anchor: from, head: to }, scrollIntoView: true });
|
|
372
|
+
view.focus();
|
|
373
|
+
},
|
|
374
|
+
destroy() {
|
|
375
|
+
destroyed = true;
|
|
376
|
+
view.destroy();
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
export {
|
|
381
|
+
mountEditor
|
|
382
|
+
};
|
|
383
|
+
//# sourceMappingURL=codemirror-6REE3TUN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/TextEditor/codemirror.ts","../src/components/TextEditor/wordDiff.ts","../src/components/TextEditor/diffParts.ts"],"sourcesContent":["// The lazily loaded half of TextEditor: everything that imports CodeMirror. Each language is its own chunk too.\nimport { defaultKeymap, history, historyKeymap } from '@codemirror/commands';\nimport { HighlightStyle, syntaxHighlighting } from '@codemirror/language';\nimport { Compartment, EditorState, type Extension } from '@codemirror/state';\nimport { EditorView, keymap } from '@codemirror/view';\nimport { tags as t } from '@lezer/highlight';\nimport { setDiffFocus, wordDiff } from './wordDiff';\n\nexport type TextEditorLanguage = 'yaml' | 'json' | 'markdown' | 'text';\n\nexport interface EditorOptions {\n value: string;\n /** The text to diff against. No base, no diff. */\n base: string | undefined;\n /** Keep showing changes while focused, recomputed after a pause in typing. Off: hidden until blur. */\n liveDiff: boolean;\n language: TextEditorLanguage;\n disabled: boolean;\n readOnly: boolean;\n invalid: boolean;\n /** Attributes for the editable element: id and ARIA wiring from the React side. */\n attributes: Record<string, string>;\n}\n\nexport interface EditorHandle {\n update(next: EditorOptions): void;\n /** Focuses the editor, optionally with a selection (from the Textarea it replaced). */\n focus(anchor?: number, head?: number): void;\n /** Selects line `line` (1-based, clamped to the document), scrolls it into view and focuses the editor. */\n selectLine(line: number): void;\n destroy(): void;\n}\n\nconst languages: Record<Exclude<TextEditorLanguage, 'text'>, () => Promise<Extension>> = {\n yaml: () => import('@codemirror/lang-yaml').then((m) => m.yaml()),\n json: () => import('@codemirror/lang-json').then((m) => m.json()),\n markdown: () => import('./markdown').then((m) => m.markdown()),\n};\n\n/** Prose gets the phone's autocorrect, capitals and spellcheck; YAML and JSON don't (like Textarea variant=\"code\"). */\nconst isProse = (language: TextEditorLanguage) => language === 'markdown' || language === 'text';\n\n/** Touch screens: keep the caret line this far above the bottom of the viewport, clear of Safari's toolbar. */\nconst TOUCH_BOTTOM_MARGIN = 96;\nconst coarse = () => typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches;\n\n// One theme, all tokens: the box and its states match .sc-input.sc-textarea[data-variant='code'].\nconst theme = EditorView.theme({\n '&': {\n border: 'var(--sc-border-width-hairline) solid var(--sc-color-border-strong)',\n borderRadius: 'var(--sc-radius-md)',\n background: 'var(--sc-color-surface-default)',\n color: 'var(--sc-color-text-default)',\n transition:\n 'border-color var(--sc-duration-fast) var(--sc-ease-standard), background-color var(--sc-duration-fast) var(--sc-ease-standard)',\n },\n '&:hover:not([data-disabled])': { borderColor: 'var(--sc-color-text-muted)' },\n '&[data-invalid]': { borderColor: 'var(--sc-color-danger-solid)' },\n '&.cm-focused': {\n outline: 'var(--sc-border-width-focus) solid var(--sc-color-border-focus)',\n outlineOffset: '0',\n borderColor: 'var(--sc-color-border-focus)',\n },\n '&[data-disabled]': {\n background: 'var(--sc-color-surface-sunken)',\n color: 'var(--sc-color-text-subtle)',\n cursor: 'not-allowed',\n },\n '&[data-disabled] .cm-content': { cursor: 'not-allowed' },\n '.cm-scroller': {\n fontFamily: 'var(--sc-font-family-mono)',\n lineHeight: 'var(--sc-font-line-height-normal)',\n },\n '.cm-content': {\n padding: 'var(--sc-spacing-xs) 0',\n fontFamily: 'var(--sc-font-family-mono)',\n fontSize: 'calc(var(--sc-font-size-sm) * 0.95)',\n lineHeight: 'var(--sc-font-line-height-normal)',\n minBlockSize: 'calc(var(--_rows, 3) * 1lh)',\n caretColor: 'var(--sc-color-text-default)',\n },\n '.cm-line, .cm-sc-removed-lines': { paddingInline: 'var(--sc-spacing-sm)' },\n // Touch: iOS zooms the page into a focused field under 16px (as in field.css). Room under the last line, so a tap\n // there isn't near Safari's collapsed toolbar, which takes taps at the bottom edge of the screen.\n '@media (pointer: coarse)': {\n '.cm-content': {\n fontSize: 'max(16px, calc(var(--sc-font-size-sm) * 0.95))',\n paddingBlockEnd: 'var(--sc-spacing-xl)',\n },\n '.cm-scroller': { scrollPaddingBlockEnd: `${TOUCH_BOTTOM_MARGIN}px` },\n },\n});\n\nconst highlight = HighlightStyle.define([\n { tag: [t.propertyName, t.definition(t.propertyName)], color: 'var(--sc-color-accent-text)' },\n { tag: t.heading, color: 'var(--sc-color-accent-text)', fontWeight: 'var(--sc-font-weight-semibold)' },\n { tag: [t.string, t.special(t.string)], color: 'var(--sc-color-info-text)' },\n { tag: [t.number, t.bool, t.null, t.atom], color: 'var(--sc-color-warning-text)' },\n {\n tag: [t.comment, t.meta, t.punctuation, t.separator, t.processingInstruction],\n color: 'var(--sc-color-text-muted)',\n },\n { tag: [t.link, t.url], color: 'var(--sc-color-text-link)' },\n { tag: t.emphasis, fontStyle: 'italic' },\n { tag: t.strong, fontWeight: 'var(--sc-font-weight-semibold)' },\n]);\n\nconst diffExtension = (o: EditorOptions) => (o.base != null ? wordDiff(o.base, { live: o.liveDiff }) : []);\n\n/** Editable state, attributes on the editor and on the content: everything that follows the props but the diff. */\nfunction stateExtension(o: EditorOptions): Extension {\n const flag = (on: boolean) => (on ? 'true' : 'false');\n return [\n EditorView.editable.of(!o.disabled),\n EditorState.readOnly.of(o.disabled || o.readOnly),\n EditorView.editorAttributes.of({\n ...(o.invalid ? { 'data-invalid': '' } : {}),\n ...(o.disabled ? { 'data-disabled': '' } : {}),\n ...(o.readOnly ? { 'data-readonly': '' } : {}),\n }),\n EditorView.contentAttributes.of({\n spellcheck: flag(isProse(o.language)),\n autocorrect: isProse(o.language) ? 'on' : 'off',\n autocapitalize: isProse(o.language) ? 'sentences' : 'off',\n ...(o.invalid ? { 'aria-invalid': 'true' } : {}),\n ...(o.disabled ? { 'aria-disabled': 'true' } : {}),\n // Read-only stays focusable and selectable; inputmode none keeps the phone's keyboard down.\n ...(o.readOnly ? { 'aria-readonly': 'true', inputmode: 'none' } : {}),\n ...o.attributes,\n }),\n ];\n}\nconst stateKey = (o: EditorOptions) =>\n JSON.stringify([o.language, o.disabled, o.readOnly, o.invalid, o.attributes]);\n\n/** Mounts a CodeMirror view in `parent`. `onChange` gets the full text after every document change. */\nexport function mountEditor(\n parent: HTMLElement,\n options: EditorOptions,\n onChange: (value: string) => void,\n): EditorHandle {\n const diffSlot = new Compartment();\n const languageSlot = new Compartment();\n const stateSlot = new Compartment();\n let current = options;\n let destroyed = false;\n\n const loadLanguage = (language: TextEditorLanguage) => {\n if (language === 'text') return view.dispatch({ effects: languageSlot.reconfigure([]) });\n void languages[language]().then((extension) => {\n if (!destroyed && current.language === language)\n view.dispatch({ effects: languageSlot.reconfigure(extension) });\n });\n };\n\n const view = new EditorView({\n parent,\n state: EditorState.create({\n doc: options.value,\n extensions: [\n history(),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n EditorView.lineWrapping,\n EditorState.tabSize.of(2),\n EditorView.scrollMargins.of(() => (coarse() ? { bottom: TOUCH_BOTTOM_MARGIN } : null)),\n theme,\n syntaxHighlighting(highlight),\n // WordDiff's own classes, so the marks and widgets look like WordDiff family=\"mono\".\n EditorView.contentAttributes.of({ class: 'sc-word-diff', 'data-family': 'mono' }),\n stateSlot.of(stateExtension(options)),\n languageSlot.of([]),\n diffSlot.of(diffExtension(options)),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChange(u.state.doc.toString());\n }),\n ],\n }),\n });\n loadLanguage(options.language);\n\n return {\n update(next) {\n const prev = current;\n current = next;\n const effects = [];\n const diffAdded = next.base != null && prev.base == null;\n if (next.base !== prev.base || next.liveDiff !== prev.liveDiff)\n effects.push(diffSlot.reconfigure(diffExtension(next)));\n if (stateKey(next) !== stateKey(prev)) effects.push(stateSlot.reconfigure(stateExtension(next)));\n const doc = view.state.doc;\n const changes =\n next.value !== doc.toString() ? { from: 0, to: doc.length, insert: next.value } : undefined;\n if (effects.length || changes) view.dispatch(changes ? { effects, changes } : { effects });\n // A diff turned on while focused starts out believing it's blurred.\n if (diffAdded && view.hasFocus) view.dispatch({ effects: setDiffFocus.of(true) });\n if (next.language !== prev.language) loadLanguage(next.language);\n },\n focus(anchor, head) {\n if (current.disabled) return;\n if (anchor != null) {\n const max = view.state.doc.length;\n view.dispatch({ selection: { anchor: Math.min(anchor, max), head: Math.min(head ?? anchor, max) } });\n }\n view.focus();\n },\n selectLine(line) {\n if (current.disabled) return;\n const doc = view.state.doc;\n const { from, to } = doc.line(Math.min(Math.max(Math.trunc(line), 1), doc.lines));\n view.dispatch({ selection: { anchor: from, head: to }, scrollIntoView: true });\n view.focus();\n },\n destroy() {\n destroyed = true;\n view.destroy();\n },\n };\n}\n","/**\n * Inline word diffs inside CodeMirror 6 (job-radar docs/design/word-diff-editor.md, option (b), phase 2).\n *\n * The current document is diffed against a `base` string (`diffParts`), and the parts become decorations:\n * - added text is a mark (`<ins class=\"sc-word-diff__added\">`) over the document range, between visually hidden\n * \"[added: \" and \"]\", as WordDiff reads;\n * - each removed run is a widget, so it isn't part of the document: the caret steps over it, and it can't be edited\n * or copied. Inside a line it's an inline `<del>` at the position it was removed from. A run that holds a line\n * break is a block of `<del>` lines above the line it was removed from, so it never breaks a line in two.\n * The classes are WordDiff's, and the content carries `class=\"sc-word-diff\" data-family=\"mono\"`, so it looks the\n * same as WordDiff from `@salemcobalt/ui/styles.css`. Not `@codemirror/merge` (see the design doc).\n *\n * With `live` off (the default) the diff hides while the editor has focus, so nothing moves under the caret, and\n * shows again on blur. With `live` on it stays, mapped through each edit, and recomputes `DEBOUNCE_MS` after the\n * last one.\n */\nimport {\n Facet,\n StateEffect,\n StateField,\n type EditorState,\n type Extension,\n type Range,\n type Text,\n} from '@codemirror/state';\nimport {\n Decoration,\n EditorView,\n ViewPlugin,\n WidgetType,\n type DecorationSet,\n type ViewUpdate,\n} from '@codemirror/view';\nimport { diffParts } from './diffParts';\n\nconst edges = /^(\\s*)([\\s\\S]*?)(\\s*)$/;\n\nconst hidden = (text: string) =>\n Object.assign(document.createElement('span'), { className: 'sc-visually-hidden', textContent: text });\n\nclass RemovedWidget extends WidgetType {\n constructor(\n readonly text: string,\n readonly block: boolean,\n readonly label: string,\n ) {\n super();\n }\n override eq(other: RemovedWidget) {\n return other.text === this.text && other.block === this.block && other.label === this.label;\n }\n toDOM() {\n // Like WordDiff's Change: edge whitespace outside the <del>, so the tint hugs the words.\n const [, lead = '', core = '', trail = ''] = edges.exec(this.text) ?? [];\n const wrap = document.createElement(this.block ? 'div' : 'span');\n wrap.className = this.block ? 'cm-sc-removed-lines' : 'cm-sc-removed';\n const del = document.createElement('del');\n del.className = 'sc-word-diff__removed';\n del.append(hidden(`[${this.label}: `), core, hidden(']'));\n wrap.append(lead, del, trail);\n return wrap;\n }\n}\n\n/** Visually hidden \"[added: \" / \"]\" around an added mark, for screen readers. */\nclass LabelWidget extends WidgetType {\n constructor(readonly text: string) {\n super();\n }\n override eq(other: LabelWidget) {\n return other.text === this.text;\n }\n toDOM() {\n return hidden(this.text);\n }\n}\n\nexport interface DiffLabels {\n /** Screen reader word around added text: \"[added: …]\". Default \"added\". */\n added?: string | undefined;\n /** Screen reader word around removed text: \"[removed: …]\". Default \"removed\". */\n removed?: string | undefined;\n}\n\nconst addedMark = Decoration.mark({ tagName: 'ins', class: 'sc-word-diff__added' });\n\n/** The decorations for `doc` against `base`: added marks with their labels, and removed widgets, sorted. */\nexport function diffDecorations(doc: Text, base: string, labels: DiffLabels = {}): DecorationSet {\n const { added = 'added', removed = 'removed' } = labels;\n const ranges: Range<Decoration>[] = [];\n let pos = 0;\n for (const part of diffParts(base, doc.toString())) {\n const [, lead = '', core = ''] = edges.exec(part.text) ?? [];\n if (part.kind === 'same') pos += part.text.length;\n else if (part.kind === 'added') {\n if (core) {\n const from = pos + lead.length;\n const to = from + core.length;\n ranges.push(\n Decoration.widget({ widget: new LabelWidget(`[${added}: `), side: -1, kind: 'label' }).range(from),\n addedMark.range(from, to),\n Decoration.widget({ widget: new LabelWidget(']'), side: 1, kind: 'label' }).range(to),\n );\n }\n pos += part.text.length;\n } else if (core) {\n if (!part.text.includes('\\n')) {\n const widget = new RemovedWidget(part.text, false, removed);\n ranges.push(Decoration.widget({ widget, side: -1, kind: 'removed' }).range(pos));\n } else {\n // A run with a line break is whole lines next to the line it was removed from: below it when removed from\n // its end, above it otherwise. Never inline, so it can't break a line in two.\n const line = doc.lineAt(pos);\n const below = pos === line.to && line.to > line.from;\n const widget = new RemovedWidget(part.text.replace(/^\\n+|\\n+$/g, ''), true, removed);\n ranges.push(\n Decoration.widget({ widget, side: below ? 1 : -1, block: true, kind: 'removed' }).range(\n below ? line.to : line.from,\n ),\n );\n }\n }\n }\n return Decoration.set(ranges, true);\n}\n\ninterface DiffConfig {\n base: string;\n live: boolean;\n labels: DiffLabels;\n}\ninterface DiffState {\n decorations: DecorationSet;\n focused: boolean;\n /** Mapped through edits since the last diff; the debounce will recompute. */\n stale: boolean;\n}\n\n/** Quiet time after the last edit before a live diff recomputes. */\nexport const DEBOUNCE_MS = 300;\n\nconst config = Facet.define<DiffConfig, DiffConfig>({\n combine: (values) => values[0] ?? { base: '', live: false, labels: {} },\n});\n/** The editor gained (true) or lost focus. Exported so tests can focus a bare EditorState. */\nexport const setDiffFocus = StateEffect.define<boolean>();\nconst refresh = StateEffect.define<null>();\n\nconst compute = (state: EditorState, focused: boolean): DiffState => {\n const { base, labels } = state.facet(config);\n return { decorations: diffDecorations(state.doc, base, labels), focused, stale: false };\n};\n\nconst diffField = StateField.define<DiffState>({\n create: (state) => compute(state, false),\n update(value, tr) {\n let focused = value.focused;\n let refreshed = false;\n for (const e of tr.effects) {\n if (e.is(setDiffFocus)) focused = e.value;\n if (e.is(refresh)) refreshed = true;\n }\n const next = tr.state.facet(config);\n // Not live: nothing moves under the caret while typing. The diff shows again on blur.\n if (focused && !next.live) return { decorations: Decoration.none, focused, stale: false };\n const configChanged = next !== tr.startState.facet(config);\n // Live and typing: keep the old marks where they were; the debounce recomputes.\n if (focused && tr.docChanged && !refreshed && !configChanged)\n return { decorations: value.decorations.map(tr.changes), focused, stale: true };\n if (refreshed || configChanged || tr.docChanged || focused !== value.focused)\n return compute(tr.state, focused);\n return value;\n },\n // Block widgets have to come from state, not a view plugin.\n provide: (field) => EditorView.decorations.from(field, (value) => value.decorations),\n});\n\n// Schedules the recompute of a stale (live) diff, restarting on every edit.\nconst debounce = ViewPlugin.fromClass(\n class {\n timer: ReturnType<typeof setTimeout> | undefined;\n constructor(readonly view: EditorView) {}\n update(u: ViewUpdate) {\n if (!u.docChanged || !u.state.field(diffField).stale) return;\n clearTimeout(this.timer);\n this.timer = setTimeout(() => this.view.dispatch({ effects: refresh.of(null) }), DEBOUNCE_MS);\n }\n destroy() {\n clearTimeout(this.timer);\n }\n },\n);\n\nconst theme = EditorView.theme({\n '.cm-sc-removed-lines': { paddingInline: 'var(--sc-spacing-sm)', whiteSpace: 'pre-wrap' },\n});\n\n/**\n * Shows the document's changes against `base` inline. Reconfigure it (in a Compartment) to change the base, `live`\n * or the labels; a new configuration recomputes at once.\n */\nexport function wordDiff(base: string, options: { live?: boolean; labels?: DiffLabels } = {}): Extension {\n return [\n config.of({ base, live: options.live ?? false, labels: options.labels ?? {} }),\n diffField,\n debounce,\n EditorView.focusChangeEffect.of((_state, focusing) => setDiffFocus.of(focusing)),\n theme,\n ];\n}\n\n/** Test seam: the decorations in `state`, or none when the extension isn't on. */\nexport const wordDiffDecorations = (state: EditorState): DecorationSet =>\n state.field(diffField, false)?.decorations ?? Decoration.none;\n","/**\n * The word-diff engine (job-radar docs/design/word-diff-editor.md, \"Engine\"): lines first (Myers, `diffLines`),\n * then each run of changed lines paired line by line, with `diffWordsWithSpace` on each pair. A pair that changed\n * mostly, or is large, shows as the whole line removed and added; lines left over on either side are whole lines.\n * Pairing per line keeps every word-level change inside one line, so a removed run never spans a line break.\n * No CodeMirror here, so a plain Textarea + WordDiff can use it too.\n */\nimport { diffLines, diffWordsWithSpace } from 'diff';\nimport type { WordDiffPart } from '../WordDiff/WordDiff';\n\n/** Above this share of changed characters, a changed line shows as a whole line removed and added. */\nexport const WORD_DIFF_MAX_CHANGE = 0.6;\n/** Above this many characters (both sides), a changed line shows whole, without a word pass. */\nexport const WORD_DIFF_MAX_CHARS = 4000;\n\n/** One changed line against its replacement, as word parts, or whole when a word diff wouldn't help. */\nfunction pair(a: string, b: string): WordDiffPart[] {\n if (a.length + b.length <= WORD_DIFF_MAX_CHARS) {\n const parts = diffWordsWithSpace(a, b).map((p): WordDiffPart => ({\n kind: p.added ? 'added' : p.removed ? 'removed' : 'same',\n text: p.value,\n }));\n const changed = parts.reduce((n, p) => n + (p.kind === 'same' ? 0 : p.text.length), 0);\n if (changed / (a.length + b.length) <= WORD_DIFF_MAX_CHANGE) return parts;\n }\n return [\n { kind: 'removed', text: a },\n { kind: 'added', text: b },\n ];\n}\n\nconst lines = (text: string) => text.match(/[^\\n]*\\n|[^\\n]+$/g) ?? [];\n\n/** A run of changed lines: pairs first, then the lines left over on either side, whole. */\nfunction hunk(a: string, b: string): WordDiffPart[] {\n const removed = lines(a);\n const added = lines(b);\n const n = Math.min(removed.length, added.length);\n const out: WordDiffPart[] = [];\n for (let i = 0; i < n; i++) out.push(...pair(removed[i]!, added[i]!));\n out.push({ kind: 'removed', text: removed.slice(n).join('') });\n out.push({ kind: 'added', text: added.slice(n).join('') });\n return out;\n}\n\n/** `base` → `text` as WordDiff parts. Joining the `same` and `added` texts gives `text` exactly. */\nexport function diffParts(base: string, text: string): WordDiffPart[] {\n const out: WordDiffPart[] = [];\n const push = ({ kind, text }: WordDiffPart) => {\n if (!text) return;\n const last = out.at(-1);\n if (last?.kind === kind) last.text += text;\n else out.push({ kind, text });\n };\n const changes = diffLines(base, text);\n for (let i = 0; i < changes.length;) {\n const c = changes[i]!;\n if (!c.added && !c.removed) {\n push({ kind: 'same', text: c.value });\n i++;\n continue;\n }\n let removed = '';\n let added = '';\n for (; i < changes.length && (changes[i]!.added || changes[i]!.removed); i++) {\n if (changes[i]!.added) added += changes[i]!.value;\n else removed += changes[i]!.value;\n }\n hunk(removed, added).forEach(push);\n }\n return out;\n}\n"],"mappings":"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","names":["EditorView","text","theme","EditorView"]}
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, Ref } from 'react';
|
|
3
|
+
|
|
4
|
+
type TextEditorLanguage = 'yaml' | 'json' | 'markdown' | 'text';
|
|
5
|
+
|
|
6
|
+
/** What `ref` gives you: focus and line selection, on the Textarea before CodeMirror loads and on CodeMirror after. */
|
|
7
|
+
interface TextEditorHandle {
|
|
8
|
+
/** Focuses the editor. */
|
|
9
|
+
focus(): void;
|
|
10
|
+
/** Selects line `line` (1-based, clamped to the text), scrolls it into view and focuses the editor. */
|
|
11
|
+
selectLine(line: number): void;
|
|
12
|
+
}
|
|
13
|
+
interface TextEditorProps {
|
|
14
|
+
/** Required. The visible label. */
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
/** Help text under the editor, wired with `aria-describedby`. */
|
|
17
|
+
description?: ReactNode | undefined;
|
|
18
|
+
/** The error message. When given the editor is invalid and the message is announced. */
|
|
19
|
+
error?: ReactNode | undefined;
|
|
20
|
+
/** The text. Controlled: feed `onValueChange`'s value back. */
|
|
21
|
+
value: string;
|
|
22
|
+
onValueChange: (value: string) => void;
|
|
23
|
+
/** The text to compare against, such as the saved version. With a base, changes show inline; without, no diff. */
|
|
24
|
+
base?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Syntax highlighting, loaded per language. `markdown` and `text` (default) keep the phone's autocorrect,
|
|
27
|
+
* capitals and spellcheck; `yaml` and `json` turn them off.
|
|
28
|
+
*/
|
|
29
|
+
language?: TextEditorLanguage | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Keep showing changes while the editor has focus, updated 300ms after the last keystroke. Default false: the
|
|
32
|
+
* diff hides while you type, so nothing moves under the caret, and shows again on blur.
|
|
33
|
+
*/
|
|
34
|
+
liveDiff?: boolean | undefined;
|
|
35
|
+
disabled?: boolean | undefined;
|
|
36
|
+
/** Focusable and selectable, not editable. The phone's keyboard stays down. */
|
|
37
|
+
readOnly?: boolean | undefined;
|
|
38
|
+
/** Minimum visible lines; the editor grows with its content. Default 8. */
|
|
39
|
+
rows?: number | undefined;
|
|
40
|
+
/** The editable element's id (the Textarea's before CodeMirror loads). */
|
|
41
|
+
id?: string | undefined;
|
|
42
|
+
/** Class on the wrapper. */
|
|
43
|
+
className?: string | undefined;
|
|
44
|
+
/** Focus and select a line from outside, such as "Show line 12". */
|
|
45
|
+
ref?: Ref<TextEditorHandle> | undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A code and text editor on CodeMirror 6, with inline word diffs against `base`. Client-only: the server and the
|
|
49
|
+
* first client render show a code `<textarea>` in the same field chrome, and CodeMirror replaces it once its chunk
|
|
50
|
+
* has loaded, keeping focus and the selection.
|
|
51
|
+
*/
|
|
52
|
+
declare function TextEditor({ label, description, error, value, onValueChange, base, language, liveDiff, disabled, readOnly, rows, id: idProp, className, ref, }: TextEditorProps): react.JSX.Element;
|
|
53
|
+
|
|
54
|
+
export { TextEditor };
|
|
55
|
+
export type { TextEditorHandle, TextEditorLanguage, TextEditorProps };
|