@coze-editor/react-components 0.1.0-alpha.09ffeb
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/LICENSE +21 -0
- package/dist/esm/index.js +1596 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +160 -0
- package/dist/index.d.ts +160 -0
- package/dist/index.js +1596 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1596 @@
|
|
|
1
|
+
// src/cursor-mirror/index.tsx
|
|
2
|
+
import {
|
|
3
|
+
forwardRef,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import { autoUpdate } from "@floating-ui/dom";
|
|
11
|
+
import { useEditor, useInjector } from "@coze-editor/react";
|
|
12
|
+
import {
|
|
13
|
+
elementAtPosition,
|
|
14
|
+
positionElementLayer,
|
|
15
|
+
SelectionSide
|
|
16
|
+
} from "@coze-editor/extensions";
|
|
17
|
+
var CursorMirror = forwardRef(function CursorMirror2({ side, onChange, onVisibleChange }, ref) {
|
|
18
|
+
const injector = useInjector();
|
|
19
|
+
const editor = useEditor();
|
|
20
|
+
const [dom] = useState(() => document.createElement("div"));
|
|
21
|
+
const domRef = useRef(dom);
|
|
22
|
+
domRef.current = dom;
|
|
23
|
+
const onChangeRef = useRef(onChange);
|
|
24
|
+
onChangeRef.current = onChange;
|
|
25
|
+
const onVisibleChangeRef = useRef(onVisibleChange);
|
|
26
|
+
onVisibleChangeRef.current = onVisibleChange;
|
|
27
|
+
useImperativeHandle(ref, () => domRef.current);
|
|
28
|
+
useLayoutEffect(
|
|
29
|
+
() => injector.inject([
|
|
30
|
+
elementAtPosition.of({
|
|
31
|
+
dom: domRef.current,
|
|
32
|
+
pos: side
|
|
33
|
+
}),
|
|
34
|
+
positionElementLayer
|
|
35
|
+
]),
|
|
36
|
+
[injector, side]
|
|
37
|
+
);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const floating = document.createElement("div");
|
|
40
|
+
document.body.appendChild(floating);
|
|
41
|
+
const dispose = autoUpdate(
|
|
42
|
+
domRef.current,
|
|
43
|
+
floating,
|
|
44
|
+
() => {
|
|
45
|
+
if (typeof onChangeRef.current === "function") {
|
|
46
|
+
onChangeRef.current();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{ animationFrame: true }
|
|
50
|
+
);
|
|
51
|
+
return () => {
|
|
52
|
+
document.body.removeChild(floating);
|
|
53
|
+
dispose();
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!editor) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const view = editor.$view;
|
|
61
|
+
const observer = new IntersectionObserver(
|
|
62
|
+
(entries) => {
|
|
63
|
+
entries.forEach((entry) => {
|
|
64
|
+
if (typeof onVisibleChangeRef.current === "function") {
|
|
65
|
+
onVisibleChangeRef.current(entry.isIntersecting);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
root: view.scrollDOM,
|
|
71
|
+
threshold: 0
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
observer.observe(domRef.current);
|
|
75
|
+
return () => {
|
|
76
|
+
observer.disconnect();
|
|
77
|
+
};
|
|
78
|
+
}, [editor]);
|
|
79
|
+
return null;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// src/position-mirror/index.tsx
|
|
83
|
+
import {
|
|
84
|
+
forwardRef as forwardRef2,
|
|
85
|
+
useEffect as useEffect2,
|
|
86
|
+
useImperativeHandle as useImperativeHandle2,
|
|
87
|
+
useLayoutEffect as useLayoutEffect2,
|
|
88
|
+
useRef as useRef2,
|
|
89
|
+
useState as useState2
|
|
90
|
+
} from "react";
|
|
91
|
+
import { autoUpdate as autoUpdate2 } from "@floating-ui/dom";
|
|
92
|
+
import { useEditor as useEditor2, useInjector as useInjector2 } from "@coze-editor/react";
|
|
93
|
+
import {
|
|
94
|
+
elementAtPosition as elementAtPosition2,
|
|
95
|
+
positionElementLayer as positionElementLayer2
|
|
96
|
+
} from "@coze-editor/extensions";
|
|
97
|
+
var PositionMirror = forwardRef2(function CursorMirror3({ position, onChange, onVisibleChange }, ref) {
|
|
98
|
+
const injector = useInjector2();
|
|
99
|
+
const editor = useEditor2();
|
|
100
|
+
const [dom] = useState2(() => {
|
|
101
|
+
const el = document.createElement("div");
|
|
102
|
+
el.classList.add("cm-position-mirror");
|
|
103
|
+
return el;
|
|
104
|
+
});
|
|
105
|
+
const domRef = useRef2(dom);
|
|
106
|
+
domRef.current = dom;
|
|
107
|
+
const onChangeRef = useRef2(onChange);
|
|
108
|
+
onChangeRef.current = onChange;
|
|
109
|
+
const onVisibleChangeRef = useRef2(onVisibleChange);
|
|
110
|
+
onVisibleChangeRef.current = onVisibleChange;
|
|
111
|
+
useImperativeHandle2(ref, () => domRef.current);
|
|
112
|
+
useLayoutEffect2(
|
|
113
|
+
() => injector.inject([
|
|
114
|
+
elementAtPosition2.of({
|
|
115
|
+
dom: domRef.current,
|
|
116
|
+
pos: position
|
|
117
|
+
}),
|
|
118
|
+
positionElementLayer2
|
|
119
|
+
]),
|
|
120
|
+
[injector, position]
|
|
121
|
+
);
|
|
122
|
+
useEffect2(() => {
|
|
123
|
+
const floating = document.createElement("div");
|
|
124
|
+
document.body.appendChild(floating);
|
|
125
|
+
const dispose = autoUpdate2(
|
|
126
|
+
domRef.current,
|
|
127
|
+
floating,
|
|
128
|
+
() => {
|
|
129
|
+
if (typeof onChangeRef.current === "function") {
|
|
130
|
+
onChangeRef.current();
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{ animationFrame: true }
|
|
134
|
+
);
|
|
135
|
+
return () => {
|
|
136
|
+
document.body.removeChild(floating);
|
|
137
|
+
dispose();
|
|
138
|
+
};
|
|
139
|
+
}, []);
|
|
140
|
+
useEffect2(() => {
|
|
141
|
+
if (!editor) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const view = editor.$view;
|
|
145
|
+
const observer = new IntersectionObserver(
|
|
146
|
+
(entries) => {
|
|
147
|
+
entries.forEach((entry) => {
|
|
148
|
+
if (typeof onVisibleChangeRef.current === "function") {
|
|
149
|
+
onVisibleChangeRef.current(entry.isIntersecting);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
root: view.scrollDOM,
|
|
155
|
+
threshold: 0
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
observer.observe(domRef.current);
|
|
159
|
+
return () => {
|
|
160
|
+
observer.disconnect();
|
|
161
|
+
};
|
|
162
|
+
}, [editor]);
|
|
163
|
+
return null;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// src/cursor-inlay-hint/index.tsx
|
|
167
|
+
import { createPortal } from "react-dom";
|
|
168
|
+
import { useLayoutEffect as useLayoutEffect3 } from "react";
|
|
169
|
+
import { useHTMLElement } from "@coze-editor/react-hooks";
|
|
170
|
+
import { useInjector as useInjector3 } from "@coze-editor/react";
|
|
171
|
+
import { Decoration, EditorView, WidgetType } from "@codemirror/view";
|
|
172
|
+
var InlayHintWidget = class extends WidgetType {
|
|
173
|
+
constructor(element) {
|
|
174
|
+
super();
|
|
175
|
+
this.element = element;
|
|
176
|
+
}
|
|
177
|
+
eq(other) {
|
|
178
|
+
return this.element === other.element;
|
|
179
|
+
}
|
|
180
|
+
toDOM() {
|
|
181
|
+
return this.element;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
function CursorInlayHint({ children }) {
|
|
185
|
+
const injector = useInjector3();
|
|
186
|
+
const element = useHTMLElement("span");
|
|
187
|
+
useLayoutEffect3(
|
|
188
|
+
() => injector.inject([
|
|
189
|
+
EditorView.decorations.compute(
|
|
190
|
+
["selection"],
|
|
191
|
+
(state) => Decoration.set([
|
|
192
|
+
Decoration.widget({
|
|
193
|
+
widget: new InlayHintWidget(element),
|
|
194
|
+
side: 1
|
|
195
|
+
}).range(state.selection.main.head)
|
|
196
|
+
])
|
|
197
|
+
)
|
|
198
|
+
]),
|
|
199
|
+
[injector]
|
|
200
|
+
);
|
|
201
|
+
return createPortal(children, element);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/markers/markers.tsx
|
|
205
|
+
import { useCallback, useLayoutEffect as useLayoutEffect4 } from "react";
|
|
206
|
+
import { useStateField } from "@coze-editor/react-hooks";
|
|
207
|
+
import { useInjector as useInjector4 } from "@coze-editor/react";
|
|
208
|
+
import { mergeableMarkers } from "@coze-editor/extensions";
|
|
209
|
+
function Markers({ markers }) {
|
|
210
|
+
const injector = useInjector4();
|
|
211
|
+
const field = useStateField(useCallback(() => markers, [markers]));
|
|
212
|
+
useLayoutEffect4(
|
|
213
|
+
() => injector.inject([field, mergeableMarkers.from(field)]),
|
|
214
|
+
[injector, field]
|
|
215
|
+
);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/markers/diagnostic-markers.tsx
|
|
220
|
+
import React, { useLayoutEffect as useLayoutEffect5, useMemo } from "react";
|
|
221
|
+
import hash from "hash-sum";
|
|
222
|
+
import { useInjector as useInjector5 } from "@coze-editor/react";
|
|
223
|
+
import { EditorView as EditorView2 } from "@codemirror/view";
|
|
224
|
+
function DiagnosticMarkers({ markers }) {
|
|
225
|
+
const theme = useMemo(() => {
|
|
226
|
+
const colors = getColors(markers);
|
|
227
|
+
return colors.reduce((memo, color) => {
|
|
228
|
+
const className = getClassNameFromColor(color);
|
|
229
|
+
memo[`& .${className}`] = {
|
|
230
|
+
backgroundPosition: "left bottom",
|
|
231
|
+
backgroundRepeat: "repeat-x",
|
|
232
|
+
paddingBottom: "0.7px",
|
|
233
|
+
backgroundImage: underline(color)
|
|
234
|
+
};
|
|
235
|
+
return memo;
|
|
236
|
+
}, {});
|
|
237
|
+
}, [markers]);
|
|
238
|
+
const injector = useInjector5();
|
|
239
|
+
useLayoutEffect5(
|
|
240
|
+
() => injector.inject([EditorView2.theme(theme)]),
|
|
241
|
+
[injector, theme]
|
|
242
|
+
);
|
|
243
|
+
const finalMarkers = useMemo(
|
|
244
|
+
() => markers.map((marker) => {
|
|
245
|
+
const className = getClassNameFromColor(marker.color ?? "red");
|
|
246
|
+
return {
|
|
247
|
+
from: marker.from,
|
|
248
|
+
to: marker.to,
|
|
249
|
+
className
|
|
250
|
+
};
|
|
251
|
+
}),
|
|
252
|
+
[markers]
|
|
253
|
+
);
|
|
254
|
+
return /* @__PURE__ */ React.createElement(Markers, { markers: finalMarkers });
|
|
255
|
+
}
|
|
256
|
+
var isString = (v) => Boolean(v);
|
|
257
|
+
function getColors(markers) {
|
|
258
|
+
return [
|
|
259
|
+
.../* @__PURE__ */ new Set([
|
|
260
|
+
...markers.map((marker) => marker.color).filter((v) => isString(v)),
|
|
261
|
+
"red"
|
|
262
|
+
])
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
function getClassNameFromColor(color) {
|
|
266
|
+
return `cm-sdk-diagnostic-marker-${hash(color)}`;
|
|
267
|
+
}
|
|
268
|
+
function svg(content, attrs) {
|
|
269
|
+
return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`;
|
|
270
|
+
}
|
|
271
|
+
function underline(color) {
|
|
272
|
+
return svg(
|
|
273
|
+
`<path d="m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>`,
|
|
274
|
+
'width="6" height="3"'
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/gutter/gutter.tsx
|
|
279
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
280
|
+
import React2, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
281
|
+
import { useHydrateAtoms } from "jotai/utils";
|
|
282
|
+
import { Provider, useAtomValue } from "jotai";
|
|
283
|
+
import {
|
|
284
|
+
useCompartment,
|
|
285
|
+
useHTMLElement as useHTMLElement2,
|
|
286
|
+
useInjectorEffect,
|
|
287
|
+
useLatest
|
|
288
|
+
} from "@coze-editor/react-hooks";
|
|
289
|
+
import { useEditor as useEditor3 } from "@coze-editor/react";
|
|
290
|
+
import { gutter as leftGutter } from "@codemirror/view";
|
|
291
|
+
import { Facet as Facet3, RangeSet as RangeSet2 } from "@codemirror/state";
|
|
292
|
+
|
|
293
|
+
// src/gutter/right-gutter.ts
|
|
294
|
+
import {
|
|
295
|
+
MapMode,
|
|
296
|
+
Facet,
|
|
297
|
+
RangeValue,
|
|
298
|
+
RangeSet
|
|
299
|
+
} from "@codemirror/state";
|
|
300
|
+
import { EditorView as EditorView3, ViewPlugin, BlockType, Direction } from "@codemirror/view";
|
|
301
|
+
var GutterMarker = class extends RangeValue {
|
|
302
|
+
/// @internal
|
|
303
|
+
compare(other) {
|
|
304
|
+
return this == other || this.constructor == other.constructor && this.eq(other);
|
|
305
|
+
}
|
|
306
|
+
/// Compare this marker to another marker of the same type.
|
|
307
|
+
eq(other) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
/// This property can be used to add CSS classes to the gutter
|
|
311
|
+
/// element that contains this marker.
|
|
312
|
+
elementClass;
|
|
313
|
+
/// Called if the marker has a `toDOM` method and its representation
|
|
314
|
+
/// was removed from a gutter.
|
|
315
|
+
destroy(dom) {
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
GutterMarker.prototype.elementClass = "";
|
|
319
|
+
GutterMarker.prototype.toDOM = void 0;
|
|
320
|
+
GutterMarker.prototype.mapMode = MapMode.TrackBefore;
|
|
321
|
+
GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
|
|
322
|
+
GutterMarker.prototype.point = true;
|
|
323
|
+
var gutterLineClass = Facet.define();
|
|
324
|
+
var gutterWidgetClass = Facet.define();
|
|
325
|
+
var defaults = {
|
|
326
|
+
class: "",
|
|
327
|
+
renderEmptyElements: false,
|
|
328
|
+
elementStyle: "",
|
|
329
|
+
markers: () => RangeSet.empty,
|
|
330
|
+
lineMarker: () => null,
|
|
331
|
+
widgetMarker: () => null,
|
|
332
|
+
lineMarkerChange: null,
|
|
333
|
+
initialSpacer: null,
|
|
334
|
+
updateSpacer: null,
|
|
335
|
+
domEventHandlers: {}
|
|
336
|
+
};
|
|
337
|
+
var activeGutters = Facet.define();
|
|
338
|
+
function gutter(config) {
|
|
339
|
+
return [gutters(), activeGutters.of({ ...defaults, ...config })];
|
|
340
|
+
}
|
|
341
|
+
var unfixGutters = Facet.define({
|
|
342
|
+
combine: (values) => values.some((x) => x)
|
|
343
|
+
});
|
|
344
|
+
function gutters(config) {
|
|
345
|
+
const result = [
|
|
346
|
+
gutterView,
|
|
347
|
+
// right gutter theme
|
|
348
|
+
EditorView3.baseTheme({
|
|
349
|
+
".cm-right-gutters": {
|
|
350
|
+
flexShrink: 0,
|
|
351
|
+
display: "flex",
|
|
352
|
+
height: "100%",
|
|
353
|
+
boxSizing: "border-box",
|
|
354
|
+
insetInlineStart: 0,
|
|
355
|
+
zIndex: 200
|
|
356
|
+
},
|
|
357
|
+
".cm-right-gutter": {
|
|
358
|
+
display: "flex !important",
|
|
359
|
+
// Necessary -- prevents margin collapsing
|
|
360
|
+
flexDirection: "column",
|
|
361
|
+
flexShrink: 0,
|
|
362
|
+
boxSizing: "border-box",
|
|
363
|
+
minHeight: "100%",
|
|
364
|
+
overflow: "hidden"
|
|
365
|
+
},
|
|
366
|
+
".cm-rightGutterElement": {
|
|
367
|
+
boxSizing: "border-box"
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
];
|
|
371
|
+
if (config && config.fixed === false) result.push(unfixGutters.of(true));
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
var gutterView = ViewPlugin.fromClass(class {
|
|
375
|
+
constructor(view) {
|
|
376
|
+
this.view = view;
|
|
377
|
+
this.prevViewport = view.viewport;
|
|
378
|
+
this.dom = document.createElement("div");
|
|
379
|
+
this.dom.className = "cm-right-gutters";
|
|
380
|
+
this.dom.setAttribute("aria-hidden", "true");
|
|
381
|
+
this.dom.style.minHeight = this.view.contentHeight / this.view.scaleY + "px";
|
|
382
|
+
this.gutters = view.state.facet(activeGutters).map((conf) => new SingleGutterView(view, conf));
|
|
383
|
+
for (const gutter2 of this.gutters) this.dom.appendChild(gutter2.dom);
|
|
384
|
+
this.fixed = !view.state.facet(unfixGutters);
|
|
385
|
+
if (this.fixed) {
|
|
386
|
+
this.dom.style.position = "sticky";
|
|
387
|
+
}
|
|
388
|
+
this.syncGutters(false);
|
|
389
|
+
view.scrollDOM.appendChild(this.dom);
|
|
390
|
+
}
|
|
391
|
+
gutters;
|
|
392
|
+
dom;
|
|
393
|
+
fixed;
|
|
394
|
+
prevViewport;
|
|
395
|
+
update(update) {
|
|
396
|
+
if (this.updateGutters(update)) {
|
|
397
|
+
const vpA = this.prevViewport, vpB = update.view.viewport;
|
|
398
|
+
const vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
|
|
399
|
+
this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
|
|
400
|
+
}
|
|
401
|
+
if (update.geometryChanged) {
|
|
402
|
+
this.dom.style.minHeight = this.view.contentHeight / this.view.scaleY + "px";
|
|
403
|
+
}
|
|
404
|
+
if (this.view.state.facet(unfixGutters) != !this.fixed) {
|
|
405
|
+
this.fixed = !this.fixed;
|
|
406
|
+
this.dom.style.position = this.fixed ? "sticky" : "";
|
|
407
|
+
}
|
|
408
|
+
this.prevViewport = update.view.viewport;
|
|
409
|
+
}
|
|
410
|
+
syncGutters(detach) {
|
|
411
|
+
const before = this.dom.previousSibling;
|
|
412
|
+
if (detach) this.dom.remove();
|
|
413
|
+
const lineClasses = RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
|
|
414
|
+
let classSet = [];
|
|
415
|
+
const contexts = this.gutters.map((gutter2) => new UpdateContext(gutter2, this.view.viewport, -this.view.documentPadding.top));
|
|
416
|
+
for (const line of this.view.viewportLineBlocks) {
|
|
417
|
+
if (classSet.length) classSet = [];
|
|
418
|
+
if (Array.isArray(line.type)) {
|
|
419
|
+
let first = true;
|
|
420
|
+
for (const b of line.type) {
|
|
421
|
+
if (b.type == BlockType.Text && first) {
|
|
422
|
+
advanceCursor(lineClasses, classSet, b.from);
|
|
423
|
+
for (const cx of contexts) cx.line(this.view, b, classSet);
|
|
424
|
+
first = false;
|
|
425
|
+
} else if (b.widget) {
|
|
426
|
+
for (const cx of contexts) cx.widget(this.view, b);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} else if (line.type == BlockType.Text) {
|
|
430
|
+
advanceCursor(lineClasses, classSet, line.from);
|
|
431
|
+
for (const cx of contexts) cx.line(this.view, line, classSet);
|
|
432
|
+
} else if (line.widget) {
|
|
433
|
+
for (const cx of contexts) cx.widget(this.view, line);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const cx of contexts) cx.finish();
|
|
437
|
+
if (detach) {
|
|
438
|
+
this.view.scrollDOM.insertBefore(this.dom, (before == null ? void 0 : before.nextSibling) ?? null);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
updateGutters(update) {
|
|
442
|
+
const prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
|
|
443
|
+
let change = update.docChanged || update.heightChanged || update.viewportChanged || !RangeSet.eq(
|
|
444
|
+
update.startState.facet(gutterLineClass),
|
|
445
|
+
update.state.facet(gutterLineClass),
|
|
446
|
+
update.view.viewport.from,
|
|
447
|
+
update.view.viewport.to
|
|
448
|
+
);
|
|
449
|
+
if (prev == cur) {
|
|
450
|
+
for (const gutter2 of this.gutters) if (gutter2.update(update)) change = true;
|
|
451
|
+
} else {
|
|
452
|
+
change = true;
|
|
453
|
+
const gutters2 = [];
|
|
454
|
+
for (const conf of cur) {
|
|
455
|
+
const known = prev.indexOf(conf);
|
|
456
|
+
if (known < 0) {
|
|
457
|
+
gutters2.push(new SingleGutterView(this.view, conf));
|
|
458
|
+
} else {
|
|
459
|
+
this.gutters[known].update(update);
|
|
460
|
+
gutters2.push(this.gutters[known]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
for (const g of this.gutters) {
|
|
464
|
+
g.dom.remove();
|
|
465
|
+
if (gutters2.indexOf(g) < 0) g.destroy();
|
|
466
|
+
}
|
|
467
|
+
for (const g of gutters2) this.dom.appendChild(g.dom);
|
|
468
|
+
this.gutters = gutters2;
|
|
469
|
+
}
|
|
470
|
+
return change;
|
|
471
|
+
}
|
|
472
|
+
destroy() {
|
|
473
|
+
for (const view of this.gutters) view.destroy();
|
|
474
|
+
this.dom.remove();
|
|
475
|
+
}
|
|
476
|
+
}, {
|
|
477
|
+
provide: (plugin) => EditorView3.scrollMargins.of((view) => {
|
|
478
|
+
const value = view.plugin(plugin);
|
|
479
|
+
if (!value || value.gutters.length == 0 || !value.fixed) return null;
|
|
480
|
+
return view.textDirection == Direction.LTR ? { left: value.dom.offsetWidth * view.scaleX } : { right: value.dom.offsetWidth * view.scaleX };
|
|
481
|
+
})
|
|
482
|
+
});
|
|
483
|
+
function asArray(val) {
|
|
484
|
+
return Array.isArray(val) ? val : [val];
|
|
485
|
+
}
|
|
486
|
+
function advanceCursor(cursor, collect, pos) {
|
|
487
|
+
while (cursor.value && cursor.from <= pos) {
|
|
488
|
+
if (cursor.from == pos) collect.push(cursor.value);
|
|
489
|
+
cursor.next();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
var UpdateContext = class {
|
|
493
|
+
constructor(gutter2, viewport, height) {
|
|
494
|
+
this.gutter = gutter2;
|
|
495
|
+
this.height = height;
|
|
496
|
+
this.cursor = RangeSet.iter(gutter2.markers, viewport.from);
|
|
497
|
+
}
|
|
498
|
+
cursor;
|
|
499
|
+
i = 0;
|
|
500
|
+
addElement(view, block, markers) {
|
|
501
|
+
const { gutter: gutter2 } = this, above = (block.top - this.height) / view.scaleY, height = block.height / view.scaleY;
|
|
502
|
+
if (this.i == gutter2.elements.length) {
|
|
503
|
+
const newElt = new GutterElement(view, height, above, markers);
|
|
504
|
+
gutter2.elements.push(newElt);
|
|
505
|
+
gutter2.dom.appendChild(newElt.dom);
|
|
506
|
+
} else {
|
|
507
|
+
gutter2.elements[this.i].update(view, height, above, markers);
|
|
508
|
+
}
|
|
509
|
+
this.height = block.bottom;
|
|
510
|
+
this.i++;
|
|
511
|
+
}
|
|
512
|
+
line(view, line, extraMarkers) {
|
|
513
|
+
let localMarkers = [];
|
|
514
|
+
advanceCursor(this.cursor, localMarkers, line.from);
|
|
515
|
+
if (extraMarkers.length) localMarkers = localMarkers.concat(extraMarkers);
|
|
516
|
+
const forLine = this.gutter.config.lineMarker(view, line, localMarkers);
|
|
517
|
+
if (forLine) localMarkers.unshift(forLine);
|
|
518
|
+
const gutter2 = this.gutter;
|
|
519
|
+
if (localMarkers.length == 0 && !gutter2.config.renderEmptyElements) return;
|
|
520
|
+
this.addElement(view, line, localMarkers);
|
|
521
|
+
}
|
|
522
|
+
widget(view, block) {
|
|
523
|
+
const marker = this.gutter.config.widgetMarker(view, block.widget, block);
|
|
524
|
+
let markers = marker ? [marker] : null;
|
|
525
|
+
for (const cls of view.state.facet(gutterWidgetClass)) {
|
|
526
|
+
const marker2 = cls(view, block.widget, block);
|
|
527
|
+
if (marker2) (markers || (markers = [])).push(marker2);
|
|
528
|
+
}
|
|
529
|
+
if (markers) this.addElement(view, block, markers);
|
|
530
|
+
}
|
|
531
|
+
finish() {
|
|
532
|
+
const gutter2 = this.gutter;
|
|
533
|
+
while (gutter2.elements.length > this.i) {
|
|
534
|
+
const last = gutter2.elements.pop();
|
|
535
|
+
gutter2.dom.removeChild(last.dom);
|
|
536
|
+
last.destroy();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
var SingleGutterView = class {
|
|
541
|
+
constructor(view, config) {
|
|
542
|
+
this.view = view;
|
|
543
|
+
this.config = config;
|
|
544
|
+
this.dom = document.createElement("div");
|
|
545
|
+
this.dom.className = "cm-right-gutter" + (this.config.class ? " " + this.config.class : "");
|
|
546
|
+
for (const prop in config.domEventHandlers) {
|
|
547
|
+
this.dom.addEventListener(prop, (event) => {
|
|
548
|
+
let target = event.target, y;
|
|
549
|
+
if (target != this.dom && this.dom.contains(target)) {
|
|
550
|
+
while (target.parentNode != this.dom) target = target.parentNode;
|
|
551
|
+
const rect = target.getBoundingClientRect();
|
|
552
|
+
y = (rect.top + rect.bottom) / 2;
|
|
553
|
+
} else {
|
|
554
|
+
y = event.clientY;
|
|
555
|
+
}
|
|
556
|
+
const line = view.lineBlockAtHeight(y - view.documentTop);
|
|
557
|
+
if (config.domEventHandlers[prop](view, line, event)) event.preventDefault();
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
this.markers = asArray(config.markers(view));
|
|
561
|
+
if (config.initialSpacer) {
|
|
562
|
+
this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
|
|
563
|
+
this.dom.appendChild(this.spacer.dom);
|
|
564
|
+
this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
dom;
|
|
568
|
+
elements = [];
|
|
569
|
+
markers;
|
|
570
|
+
spacer = null;
|
|
571
|
+
update(update) {
|
|
572
|
+
const prevMarkers = this.markers;
|
|
573
|
+
this.markers = asArray(this.config.markers(update.view));
|
|
574
|
+
if (this.spacer && this.config.updateSpacer) {
|
|
575
|
+
const updated = this.config.updateSpacer(this.spacer.markers[0], update);
|
|
576
|
+
if (updated != this.spacer.markers[0]) this.spacer.update(update.view, 0, 0, [updated]);
|
|
577
|
+
}
|
|
578
|
+
const vp = update.view.viewport;
|
|
579
|
+
return !RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) || (this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
|
|
580
|
+
}
|
|
581
|
+
destroy() {
|
|
582
|
+
for (const elt of this.elements) elt.destroy();
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
var GutterElement = class {
|
|
586
|
+
dom;
|
|
587
|
+
height = -1;
|
|
588
|
+
above = 0;
|
|
589
|
+
markers = [];
|
|
590
|
+
constructor(view, height, above, markers) {
|
|
591
|
+
this.dom = document.createElement("div");
|
|
592
|
+
this.dom.className = "cm-rightGutterElement";
|
|
593
|
+
this.update(view, height, above, markers);
|
|
594
|
+
}
|
|
595
|
+
update(view, height, above, markers) {
|
|
596
|
+
if (this.height != height) {
|
|
597
|
+
this.height = height;
|
|
598
|
+
this.dom.style.height = height + "px";
|
|
599
|
+
}
|
|
600
|
+
if (this.above != above)
|
|
601
|
+
this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
|
|
602
|
+
if (!sameMarkers(this.markers, markers)) this.setMarkers(view, markers);
|
|
603
|
+
}
|
|
604
|
+
setMarkers(view, markers) {
|
|
605
|
+
let cls = "cm-rightGutterElement", domPos = this.dom.firstChild;
|
|
606
|
+
for (let iNew = 0, iOld = 0; ; ) {
|
|
607
|
+
let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
|
|
608
|
+
if (marker) {
|
|
609
|
+
const c = marker.elementClass;
|
|
610
|
+
if (c) cls += " " + c;
|
|
611
|
+
for (let i = iOld; i < this.markers.length; i++)
|
|
612
|
+
if (this.markers[i].compare(marker)) {
|
|
613
|
+
skipTo = i;
|
|
614
|
+
matched = true;
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
skipTo = this.markers.length;
|
|
619
|
+
}
|
|
620
|
+
while (iOld < skipTo) {
|
|
621
|
+
const next = this.markers[iOld++];
|
|
622
|
+
if (next.toDOM) {
|
|
623
|
+
next.destroy(domPos);
|
|
624
|
+
const after = domPos.nextSibling;
|
|
625
|
+
domPos.remove();
|
|
626
|
+
domPos = after;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (!marker) break;
|
|
630
|
+
if (marker.toDOM) {
|
|
631
|
+
if (matched) domPos = domPos.nextSibling;
|
|
632
|
+
else this.dom.insertBefore(marker.toDOM(view), domPos);
|
|
633
|
+
}
|
|
634
|
+
if (matched) iOld++;
|
|
635
|
+
}
|
|
636
|
+
this.dom.className = cls;
|
|
637
|
+
this.markers = markers;
|
|
638
|
+
}
|
|
639
|
+
destroy() {
|
|
640
|
+
this.setMarkers(null, []);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
function sameMarkers(a, b) {
|
|
644
|
+
if (a.length != b.length) return false;
|
|
645
|
+
for (let i = 0; i < a.length; i++) if (!a[i].compare(b[i])) return false;
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
var activeLineGutterMarker = new class extends GutterMarker {
|
|
649
|
+
elementClass = "cm-activeLineRightGutter";
|
|
650
|
+
}();
|
|
651
|
+
var activeLineGutterHighlighter = gutterLineClass.compute(["selection"], (state) => {
|
|
652
|
+
const marks = [];
|
|
653
|
+
let last = -1;
|
|
654
|
+
for (const range of state.selection.ranges) {
|
|
655
|
+
const linePos = state.doc.lineAt(range.head).from;
|
|
656
|
+
if (linePos > last) {
|
|
657
|
+
last = linePos;
|
|
658
|
+
marks.push(activeLineGutterMarker.range(linePos));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return RangeSet.of(marks);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// src/gutter/atoms.ts
|
|
665
|
+
import { atom } from "jotai";
|
|
666
|
+
import { Facet as Facet2 } from "@codemirror/state";
|
|
667
|
+
var facetAtom = atom(Facet2.define());
|
|
668
|
+
|
|
669
|
+
// src/gutter/gutter.tsx
|
|
670
|
+
var DOMGutterLineMarker = class extends GutterMarker {
|
|
671
|
+
constructor(dom) {
|
|
672
|
+
super();
|
|
673
|
+
this.dom = dom;
|
|
674
|
+
}
|
|
675
|
+
eq(other) {
|
|
676
|
+
return this.dom === other.dom;
|
|
677
|
+
}
|
|
678
|
+
toDOM() {
|
|
679
|
+
return this.dom;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
function HydrateAtoms({ markers, children }) {
|
|
683
|
+
useHydrateAtoms([[facetAtom, markers]]);
|
|
684
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, children);
|
|
685
|
+
}
|
|
686
|
+
var GutterPlacement = /* @__PURE__ */ ((GutterPlacement2) => {
|
|
687
|
+
GutterPlacement2["Left"] = "left";
|
|
688
|
+
GutterPlacement2["Right"] = "right";
|
|
689
|
+
return GutterPlacement2;
|
|
690
|
+
})(GutterPlacement || {});
|
|
691
|
+
function Gutter({
|
|
692
|
+
defaultClassName,
|
|
693
|
+
placement = "left" /* Left */,
|
|
694
|
+
children
|
|
695
|
+
}) {
|
|
696
|
+
const [markersFacet] = useState3(
|
|
697
|
+
() => Facet3.define()
|
|
698
|
+
);
|
|
699
|
+
useInjectorEffect(
|
|
700
|
+
(injector) => {
|
|
701
|
+
const gutter2 = placement === "right" /* Right */ ? gutter : leftGutter;
|
|
702
|
+
return injector.inject([
|
|
703
|
+
gutter2({
|
|
704
|
+
class: defaultClassName ?? "",
|
|
705
|
+
markers(view) {
|
|
706
|
+
const specs = view.state.facet(markersFacet);
|
|
707
|
+
const { lines } = view.state.doc;
|
|
708
|
+
let markers = RangeSet2.empty;
|
|
709
|
+
for (const spec of specs) {
|
|
710
|
+
const { lineNumber } = spec;
|
|
711
|
+
if (lineNumber < 1 || lineNumber > lines) {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
const line = view.state.doc.line(spec.lineNumber);
|
|
715
|
+
markers = markers.update({
|
|
716
|
+
add: [new DOMGutterLineMarker(spec.dom).range(line.from)],
|
|
717
|
+
sort: true
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
return markers;
|
|
721
|
+
}
|
|
722
|
+
})
|
|
723
|
+
]);
|
|
724
|
+
},
|
|
725
|
+
[placement]
|
|
726
|
+
);
|
|
727
|
+
return /* @__PURE__ */ React2.createElement(Provider, null, /* @__PURE__ */ React2.createElement(HydrateAtoms, { markers: markersFacet }, children));
|
|
728
|
+
}
|
|
729
|
+
function GutterLineMarker({
|
|
730
|
+
lineNumber,
|
|
731
|
+
children
|
|
732
|
+
}) {
|
|
733
|
+
const facet = useAtomValue(facetAtom);
|
|
734
|
+
const editor = useEditor3();
|
|
735
|
+
const compartment = useCompartment();
|
|
736
|
+
const elementRef = useLatest(useHTMLElement2());
|
|
737
|
+
useInjectorEffect((injector) => injector.inject([compartment.of([])]), []);
|
|
738
|
+
useEffect3(() => {
|
|
739
|
+
if (!editor) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
editor.$view.dispatch({
|
|
743
|
+
effects: compartment.reconfigure(
|
|
744
|
+
facet.of({
|
|
745
|
+
lineNumber,
|
|
746
|
+
dom: elementRef.current
|
|
747
|
+
})
|
|
748
|
+
)
|
|
749
|
+
});
|
|
750
|
+
}, [editor, compartment, lineNumber]);
|
|
751
|
+
return createPortal2(children, elementRef.current);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/fold-gutter/index.tsx
|
|
755
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
756
|
+
import React3, {
|
|
757
|
+
useEffect as useEffect4,
|
|
758
|
+
useState as useState4
|
|
759
|
+
} from "react";
|
|
760
|
+
import { v4 } from "@lukeed/uuid";
|
|
761
|
+
import { disposeAll } from "@coze-editor/utils";
|
|
762
|
+
import {
|
|
763
|
+
useInjectorEffect as useInjectorEffect2,
|
|
764
|
+
useLatest as useLatest2,
|
|
765
|
+
usePortalConnector
|
|
766
|
+
} from "@coze-editor/react-hooks";
|
|
767
|
+
import { useInjector as useInjector6 } from "@coze-editor/react";
|
|
768
|
+
import { EditorView as EditorView4 } from "@codemirror/view";
|
|
769
|
+
import { Prec } from "@codemirror/state";
|
|
770
|
+
import { codeFolding, foldGutter } from "@codemirror/language";
|
|
771
|
+
function FluentTriangleDown12Filled(props) {
|
|
772
|
+
return /* @__PURE__ */ React3.createElement(
|
|
773
|
+
"svg",
|
|
774
|
+
{
|
|
775
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
776
|
+
width: "8px",
|
|
777
|
+
height: "8px",
|
|
778
|
+
viewBox: "0 0 12 12",
|
|
779
|
+
...props
|
|
780
|
+
},
|
|
781
|
+
/* @__PURE__ */ React3.createElement(
|
|
782
|
+
"path",
|
|
783
|
+
{
|
|
784
|
+
fill: "currentColor",
|
|
785
|
+
d: "M5.214 10.541a.903.903 0 0 0 1.572 0l4.092-7.169C11.226 2.762 10.789 2 10.09 2H1.91c-.698 0-1.135.762-.787 1.372z"
|
|
786
|
+
}
|
|
787
|
+
)
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
function FluentTriangleRight12Filled(props) {
|
|
791
|
+
return /* @__PURE__ */ React3.createElement(
|
|
792
|
+
"svg",
|
|
793
|
+
{
|
|
794
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
795
|
+
width: "8px",
|
|
796
|
+
height: "8px",
|
|
797
|
+
viewBox: "0 0 12 12",
|
|
798
|
+
...props
|
|
799
|
+
},
|
|
800
|
+
/* @__PURE__ */ React3.createElement(
|
|
801
|
+
"path",
|
|
802
|
+
{
|
|
803
|
+
fill: "currentColor",
|
|
804
|
+
d: "M10.541 6.786a.903.903 0 0 0 0-1.572L3.372 1.122C2.762.774 2 1.211 2 1.91v8.182c0 .698.762 1.135 1.372.787z"
|
|
805
|
+
}
|
|
806
|
+
)
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
function defaultRenderMarker(open) {
|
|
810
|
+
return open ? /* @__PURE__ */ React3.createElement(FluentTriangleDown12Filled, null) : /* @__PURE__ */ React3.createElement(FluentTriangleRight12Filled, null);
|
|
811
|
+
}
|
|
812
|
+
function defaultRenderPlaceholder() {
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
815
|
+
function FoldGutter({
|
|
816
|
+
renderMarker = defaultRenderMarker,
|
|
817
|
+
renderPlaceholder = defaultRenderPlaceholder,
|
|
818
|
+
opacityTransition = false,
|
|
819
|
+
opacityTransitionDuration = 300
|
|
820
|
+
}) {
|
|
821
|
+
const connector = usePortalConnector({ sync: true });
|
|
822
|
+
const connectorRef = useLatest2(connector);
|
|
823
|
+
const renderMarkerRef = useLatest2(renderMarker);
|
|
824
|
+
const renderPlaceholderRef = useLatest2(renderPlaceholder);
|
|
825
|
+
const [opacity, setOpacity] = useState4(0);
|
|
826
|
+
const setOpacityRef = useLatest2(setOpacity);
|
|
827
|
+
const injector = useInjector6();
|
|
828
|
+
const { Portal } = connector;
|
|
829
|
+
useEffect4(() => {
|
|
830
|
+
if (!opacityTransition || !opacityTransitionDuration) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
return injector.inject([
|
|
834
|
+
EditorView4.theme({
|
|
835
|
+
".cm-foldGutter": {
|
|
836
|
+
transition: `opacity ${opacityTransitionDuration / 1e3}s ease`
|
|
837
|
+
}
|
|
838
|
+
})
|
|
839
|
+
]);
|
|
840
|
+
}, [injector, opacityTransition, opacityTransitionDuration]);
|
|
841
|
+
useInjectorEffect2(
|
|
842
|
+
(injector2) => {
|
|
843
|
+
if (!opacityTransition) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
return injector2.inject([
|
|
847
|
+
Prec.lowest(
|
|
848
|
+
EditorView4.theme({
|
|
849
|
+
".cm-foldGutter": {
|
|
850
|
+
opacity
|
|
851
|
+
}
|
|
852
|
+
})
|
|
853
|
+
)
|
|
854
|
+
]);
|
|
855
|
+
},
|
|
856
|
+
[opacityTransition, opacity]
|
|
857
|
+
);
|
|
858
|
+
useInjectorEffect2((injector2) => {
|
|
859
|
+
const disposes = [];
|
|
860
|
+
return disposeAll([
|
|
861
|
+
injector2.inject([
|
|
862
|
+
Prec.low(
|
|
863
|
+
EditorView4.theme({
|
|
864
|
+
".cm-foldGutter .cm-gutterElement": {
|
|
865
|
+
display: "flex",
|
|
866
|
+
alignItems: "center",
|
|
867
|
+
cursor: "pointer"
|
|
868
|
+
}
|
|
869
|
+
})
|
|
870
|
+
),
|
|
871
|
+
foldGutter({
|
|
872
|
+
markerDOM(open) {
|
|
873
|
+
const dom = document.createElement("span");
|
|
874
|
+
dom.setAttribute("aria-expanded", open ? "true" : "false");
|
|
875
|
+
const id = v4();
|
|
876
|
+
connectorRef.current.connect(
|
|
877
|
+
id,
|
|
878
|
+
createPortal3(renderMarkerRef.current(open), dom)
|
|
879
|
+
);
|
|
880
|
+
disposes.push(() => {
|
|
881
|
+
connectorRef.current.disconnect(id);
|
|
882
|
+
});
|
|
883
|
+
return dom;
|
|
884
|
+
},
|
|
885
|
+
domEventHandlers: {
|
|
886
|
+
mouseenter() {
|
|
887
|
+
setOpacityRef.current(1);
|
|
888
|
+
return true;
|
|
889
|
+
},
|
|
890
|
+
mouseleave() {
|
|
891
|
+
setOpacityRef.current(0);
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}),
|
|
896
|
+
codeFolding({
|
|
897
|
+
placeholderDOM(view, onclick) {
|
|
898
|
+
const dom = document.createElement("span");
|
|
899
|
+
const id = v4();
|
|
900
|
+
connectorRef.current.connect(
|
|
901
|
+
id,
|
|
902
|
+
createPortal3(renderPlaceholderRef.current(), dom)
|
|
903
|
+
);
|
|
904
|
+
disposes.push(() => {
|
|
905
|
+
connectorRef.current.disconnect(id);
|
|
906
|
+
});
|
|
907
|
+
dom.addEventListener("click", onclick, false);
|
|
908
|
+
disposes.push(() => {
|
|
909
|
+
dom.removeEventListener("click", onclick, false);
|
|
910
|
+
});
|
|
911
|
+
return dom;
|
|
912
|
+
}
|
|
913
|
+
})
|
|
914
|
+
]),
|
|
915
|
+
...disposes
|
|
916
|
+
]);
|
|
917
|
+
}, []);
|
|
918
|
+
return /* @__PURE__ */ React3.createElement(Portal, null);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// src/placeholder/index.tsx
|
|
922
|
+
import { createPortal as createPortal4 } from "react-dom";
|
|
923
|
+
import { useHTMLElement as useHTMLElement3, useInjectorEffect as useInjectorEffect3 } from "@coze-editor/react-hooks";
|
|
924
|
+
import {
|
|
925
|
+
placeholder,
|
|
926
|
+
activeLinePlaceholder
|
|
927
|
+
} from "@coze-editor/extension-placeholder";
|
|
928
|
+
function Placeholder({ children }) {
|
|
929
|
+
const element = useHTMLElement3("span");
|
|
930
|
+
useInjectorEffect3(
|
|
931
|
+
(injector) => injector.inject([placeholder(() => element)]),
|
|
932
|
+
[]
|
|
933
|
+
);
|
|
934
|
+
return createPortal4(children, element);
|
|
935
|
+
}
|
|
936
|
+
function ActiveLinePlaceholder({
|
|
937
|
+
children
|
|
938
|
+
}) {
|
|
939
|
+
const element = useHTMLElement3("span");
|
|
940
|
+
useInjectorEffect3(
|
|
941
|
+
(injector) => injector.inject([activeLinePlaceholder(() => element)]),
|
|
942
|
+
[]
|
|
943
|
+
);
|
|
944
|
+
return createPortal4(children, element);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// src/ref-element/index.tsx
|
|
948
|
+
import { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle3 } from "react";
|
|
949
|
+
import { useLatest as useLatest3 } from "@coze-editor/react-hooks";
|
|
950
|
+
var RefElement = forwardRef3(({ element }, ref) => {
|
|
951
|
+
const domRef = useLatest3(element);
|
|
952
|
+
useImperativeHandle3(ref, () => domRef.current);
|
|
953
|
+
return null;
|
|
954
|
+
});
|
|
955
|
+
RefElement.displayName = "RefElement";
|
|
956
|
+
|
|
957
|
+
// src/line-widget/index.tsx
|
|
958
|
+
import { createPortal as createPortal5 } from "react-dom";
|
|
959
|
+
import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect6, useRef as useRef3 } from "react";
|
|
960
|
+
import { useHTMLElement as useHTMLElement4 } from "@coze-editor/react-hooks";
|
|
961
|
+
import {
|
|
962
|
+
useEditor as useEditor4,
|
|
963
|
+
useInjector as useInjector7
|
|
964
|
+
} from "@coze-editor/react";
|
|
965
|
+
|
|
966
|
+
// src/line-widget/cursor-widget/state.ts
|
|
967
|
+
import { EditorView as EditorView5 } from "@codemirror/view";
|
|
968
|
+
import {
|
|
969
|
+
StateField,
|
|
970
|
+
StateEffect
|
|
971
|
+
} from "@codemirror/state";
|
|
972
|
+
|
|
973
|
+
// src/line-widget/cursor-widget/widget-decoration.ts
|
|
974
|
+
import { Decoration as Decoration2, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
975
|
+
var CustomDivWidget = class extends WidgetType3 {
|
|
976
|
+
customDOM;
|
|
977
|
+
constructor(customDOM) {
|
|
978
|
+
super();
|
|
979
|
+
this.customDOM = customDOM;
|
|
980
|
+
}
|
|
981
|
+
toDOM() {
|
|
982
|
+
const div = document.createElement("div");
|
|
983
|
+
div.className = "cursor-widget-wrapper";
|
|
984
|
+
div.setAttribute("style", "caret-color: initial;");
|
|
985
|
+
if (this.customDOM) {
|
|
986
|
+
div.appendChild(this.customDOM);
|
|
987
|
+
}
|
|
988
|
+
return div;
|
|
989
|
+
}
|
|
990
|
+
eq(widget) {
|
|
991
|
+
return widget.customDOM === this.customDOM;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
var createBlockWidget = (config, pos) => {
|
|
995
|
+
if (typeof pos !== "number") {
|
|
996
|
+
return Decoration2.none;
|
|
997
|
+
}
|
|
998
|
+
return Decoration2.set(
|
|
999
|
+
Decoration2.widget({
|
|
1000
|
+
widget: new CustomDivWidget(config.createDOM()),
|
|
1001
|
+
side: config.side,
|
|
1002
|
+
block: true,
|
|
1003
|
+
config
|
|
1004
|
+
}).range(pos)
|
|
1005
|
+
);
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// src/line-widget/cursor-widget/state.ts
|
|
1009
|
+
var setWidgetEffect = StateEffect.define();
|
|
1010
|
+
var getPos = (state, num) => state.doc.line(num).from;
|
|
1011
|
+
var cursorWidgetState = (facet) => StateField.define({
|
|
1012
|
+
create(state) {
|
|
1013
|
+
return createBlockWidget(
|
|
1014
|
+
facet.config,
|
|
1015
|
+
typeof facet.config.lineNumber === "number" ? getPos(state, facet.config.lineNumber) : void 0
|
|
1016
|
+
);
|
|
1017
|
+
},
|
|
1018
|
+
update(widget, tr) {
|
|
1019
|
+
tr.effects.forEach((effect) => {
|
|
1020
|
+
if (effect.is(setWidgetEffect) && effect.value.id === facet.id) {
|
|
1021
|
+
widget = createBlockWidget(
|
|
1022
|
+
{
|
|
1023
|
+
...facet.config,
|
|
1024
|
+
side: effect.value.side,
|
|
1025
|
+
lineNumber: effect.value.lineNumber
|
|
1026
|
+
},
|
|
1027
|
+
typeof effect.value.lineNumber === "number" ? getPos(tr.state, effect.value.lineNumber) : void 0
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
return widget.update({
|
|
1032
|
+
filter: (from, to) => 0 <= from && to <= tr.state.doc.length
|
|
1033
|
+
});
|
|
1034
|
+
},
|
|
1035
|
+
provide: (f) => EditorView5.decorations.from(f)
|
|
1036
|
+
});
|
|
1037
|
+
var setWidgetLineNumber = (id, lineNumber, side) => ({
|
|
1038
|
+
effects: [setWidgetEffect.of({ lineNumber, id, side })]
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
// src/line-widget/cursor-widget/common.ts
|
|
1042
|
+
var nextID = 1;
|
|
1043
|
+
var ConfigFacet = class {
|
|
1044
|
+
constructor(config) {
|
|
1045
|
+
this.config = config;
|
|
1046
|
+
}
|
|
1047
|
+
id = nextID++;
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
// src/line-widget/cursor-widget/index.ts
|
|
1051
|
+
var cursorBlockWidget = (config) => {
|
|
1052
|
+
const facet = new ConfigFacet(config);
|
|
1053
|
+
return {
|
|
1054
|
+
setWidgetLineNumber: (lineNumber, side) => setWidgetLineNumber(facet.id, lineNumber, side),
|
|
1055
|
+
extension: [cursorWidgetState(facet)]
|
|
1056
|
+
};
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
// src/line-widget/index.tsx
|
|
1060
|
+
function LineWidget({ children, lineNumber, side }) {
|
|
1061
|
+
const setWidgetLineNumberRef = useRef3(null);
|
|
1062
|
+
const injector = useInjector7();
|
|
1063
|
+
const editor = useEditor4();
|
|
1064
|
+
useEffect5(() => {
|
|
1065
|
+
var _a;
|
|
1066
|
+
if (setWidgetLineNumberRef.current && (editor == null ? void 0 : editor.$view)) {
|
|
1067
|
+
(_a = editor.$view) == null ? void 0 : _a.dispatch(setWidgetLineNumberRef.current(lineNumber, side));
|
|
1068
|
+
}
|
|
1069
|
+
}, [lineNumber, side]);
|
|
1070
|
+
const element = useHTMLElement4("span");
|
|
1071
|
+
useLayoutEffect6(() => {
|
|
1072
|
+
const { extension, setWidgetLineNumber: setWidgetLineNumber2 } = cursorBlockWidget({
|
|
1073
|
+
side,
|
|
1074
|
+
lineNumber,
|
|
1075
|
+
createDOM: () => element
|
|
1076
|
+
});
|
|
1077
|
+
setWidgetLineNumberRef.current = setWidgetLineNumber2;
|
|
1078
|
+
return injector.inject(extension);
|
|
1079
|
+
}, [injector]);
|
|
1080
|
+
return createPortal5(children, element);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/diff-view/index.tsx
|
|
1084
|
+
import { useLayoutEffect as useLayoutEffect7 } from "react";
|
|
1085
|
+
import { useInjector as useInjector8 } from "@coze-editor/react";
|
|
1086
|
+
|
|
1087
|
+
// src/diff-view/extension.ts
|
|
1088
|
+
import { EditorView as EditorView6 } from "@codemirror/view";
|
|
1089
|
+
import { EditorState } from "@codemirror/state";
|
|
1090
|
+
import { unifiedMergeView } from "@codemirror/merge";
|
|
1091
|
+
var diffView = (props) => {
|
|
1092
|
+
const { original = "" } = props;
|
|
1093
|
+
return [
|
|
1094
|
+
unifiedMergeView({
|
|
1095
|
+
original,
|
|
1096
|
+
gutter: false
|
|
1097
|
+
}),
|
|
1098
|
+
EditorState.phrases.of({
|
|
1099
|
+
Accept: "^Y",
|
|
1100
|
+
Reject: "^N"
|
|
1101
|
+
}),
|
|
1102
|
+
EditorView6.theme({
|
|
1103
|
+
".cm-deletedChunk": {
|
|
1104
|
+
paddingLeft: "2px",
|
|
1105
|
+
backgroundColor: "rgba(220, 68, 50, 0.2)"
|
|
1106
|
+
},
|
|
1107
|
+
".cm-deletedChunk .cm-chunkButtons": {
|
|
1108
|
+
position: "static",
|
|
1109
|
+
display: "flex"
|
|
1110
|
+
},
|
|
1111
|
+
".cm-deletedChunk .cm-chunkButtons button": {
|
|
1112
|
+
flex: "1",
|
|
1113
|
+
margin: "0px"
|
|
1114
|
+
},
|
|
1115
|
+
".cm-deletedChunk .cm-chunkButtons button:first-child": {
|
|
1116
|
+
marginRight: "2px"
|
|
1117
|
+
},
|
|
1118
|
+
".cm-deletedChunk del": {
|
|
1119
|
+
textDecoration: "none",
|
|
1120
|
+
backgroundColor: "rgba(220, 68, 50, 0.4)"
|
|
1121
|
+
},
|
|
1122
|
+
"&.cm-merge-b .cm-changedLine": {
|
|
1123
|
+
backgroundColor: "rgba(33, 170, 33, 0.2)"
|
|
1124
|
+
},
|
|
1125
|
+
".cm-insertedLine .cm-changedText": {
|
|
1126
|
+
background: "none",
|
|
1127
|
+
backgroundColor: "rgba(33, 170, 33, 0.4)"
|
|
1128
|
+
}
|
|
1129
|
+
})
|
|
1130
|
+
];
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// src/diff-view/index.tsx
|
|
1134
|
+
function DiffView({ original }) {
|
|
1135
|
+
const injector = useInjector8();
|
|
1136
|
+
useLayoutEffect7(() => injector.inject(diffView({ original })), []);
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// src/mention/extension.ts
|
|
1141
|
+
import { FacetCombineStrategy } from "@coze-editor/utils";
|
|
1142
|
+
import {
|
|
1143
|
+
Facet as Facet4,
|
|
1144
|
+
MapMode as MapMode2,
|
|
1145
|
+
StateField as StateField2
|
|
1146
|
+
} from "@codemirror/state";
|
|
1147
|
+
|
|
1148
|
+
// src/mention/is.ts
|
|
1149
|
+
function hasTriggerCharacters(options) {
|
|
1150
|
+
return Array.isArray(options.triggerCharacters);
|
|
1151
|
+
}
|
|
1152
|
+
function hasTrigger(options) {
|
|
1153
|
+
return typeof options.trigger === "function";
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/mention/extension.ts
|
|
1157
|
+
var validForReg = /^[\u4e00-\u9fa5a-zA-Z0-9_']*$/;
|
|
1158
|
+
function defaultValidFor(text) {
|
|
1159
|
+
return validForReg.test(text);
|
|
1160
|
+
}
|
|
1161
|
+
var fields = Facet4.define();
|
|
1162
|
+
function mention(options) {
|
|
1163
|
+
const mentionConfig = Facet4.define({
|
|
1164
|
+
combine: FacetCombineStrategy.Last
|
|
1165
|
+
});
|
|
1166
|
+
const field = StateField2.define({
|
|
1167
|
+
create() {
|
|
1168
|
+
return {
|
|
1169
|
+
show: false,
|
|
1170
|
+
triggerContext: void 0
|
|
1171
|
+
};
|
|
1172
|
+
},
|
|
1173
|
+
update(value, tr) {
|
|
1174
|
+
const options2 = tr.startState.facet(mentionConfig);
|
|
1175
|
+
if (!options2) {
|
|
1176
|
+
return value;
|
|
1177
|
+
}
|
|
1178
|
+
const { search = true, onOpenChange, onSearch, onTrigger } = options2;
|
|
1179
|
+
let { show } = value;
|
|
1180
|
+
let triggerContext = void 0;
|
|
1181
|
+
if (tr.docChanged) {
|
|
1182
|
+
if (hasTriggerCharacters(options2)) {
|
|
1183
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
|
1184
|
+
const insertString = inserted.toString();
|
|
1185
|
+
if (fromA === toA && options2.triggerCharacters.includes(insertString)) {
|
|
1186
|
+
triggerContext = {
|
|
1187
|
+
from: fromB,
|
|
1188
|
+
to: toB,
|
|
1189
|
+
triggerCharacter: insertString,
|
|
1190
|
+
cursorPosition: tr.state.selection.main.head
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
} else if (hasTrigger(options2)) {
|
|
1195
|
+
triggerContext = options2.trigger(tr);
|
|
1196
|
+
}
|
|
1197
|
+
if (triggerContext && typeof onTrigger === "function") {
|
|
1198
|
+
onTrigger({
|
|
1199
|
+
triggerContext: {
|
|
1200
|
+
from: triggerContext.from,
|
|
1201
|
+
to: triggerContext.to,
|
|
1202
|
+
triggerCharacter: triggerContext.triggerCharacter
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
const isSelectUserEvent = tr.isUserEvent("select");
|
|
1208
|
+
const newValue = {
|
|
1209
|
+
show: value.show,
|
|
1210
|
+
triggerContext: value.triggerContext ? { ...value.triggerContext } : void 0
|
|
1211
|
+
};
|
|
1212
|
+
if (triggerContext) {
|
|
1213
|
+
newValue.triggerContext = triggerContext;
|
|
1214
|
+
show = true;
|
|
1215
|
+
if (typeof onSearch === "function") {
|
|
1216
|
+
onSearch({
|
|
1217
|
+
value: ""
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
} else if (tr.docChanged && value.triggerContext && search) {
|
|
1221
|
+
if (newValue.triggerContext) {
|
|
1222
|
+
const newFrom = tr.changes.mapPos(
|
|
1223
|
+
value.triggerContext.from,
|
|
1224
|
+
1,
|
|
1225
|
+
MapMode2.TrackAfter
|
|
1226
|
+
);
|
|
1227
|
+
const newTo = tr.changes.mapPos(
|
|
1228
|
+
value.triggerContext.to,
|
|
1229
|
+
1,
|
|
1230
|
+
MapMode2.Simple
|
|
1231
|
+
);
|
|
1232
|
+
if (typeof newFrom === "number" && typeof newTo === "number") {
|
|
1233
|
+
newValue.triggerContext.from = newFrom;
|
|
1234
|
+
newValue.triggerContext.to = newTo;
|
|
1235
|
+
} else {
|
|
1236
|
+
show = false;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (show === true) {
|
|
1240
|
+
const validFor = typeof search === "object" ? search.validFor : defaultValidFor;
|
|
1241
|
+
const from = value.triggerContext.cursorPosition;
|
|
1242
|
+
const to = tr.state.selection.main.head;
|
|
1243
|
+
if (to >= from) {
|
|
1244
|
+
const text = tr.state.sliceDoc(from, to);
|
|
1245
|
+
if (validFor(text, from, to, tr.state)) {
|
|
1246
|
+
show = true;
|
|
1247
|
+
if (typeof onSearch === "function") {
|
|
1248
|
+
onSearch({
|
|
1249
|
+
value: text
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1253
|
+
show = false;
|
|
1254
|
+
}
|
|
1255
|
+
} else {
|
|
1256
|
+
show = false;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
} else if (isSelectUserEvent) {
|
|
1260
|
+
show = false;
|
|
1261
|
+
}
|
|
1262
|
+
if (show === false) {
|
|
1263
|
+
newValue.triggerContext = void 0;
|
|
1264
|
+
}
|
|
1265
|
+
if (show !== value.show) {
|
|
1266
|
+
newValue.show = show;
|
|
1267
|
+
if (typeof onOpenChange === "function") {
|
|
1268
|
+
onOpenChange({
|
|
1269
|
+
value: show,
|
|
1270
|
+
state: tr.state,
|
|
1271
|
+
triggerContext: newValue.triggerContext ? {
|
|
1272
|
+
from: newValue.triggerContext.from,
|
|
1273
|
+
to: newValue.triggerContext.to,
|
|
1274
|
+
triggerCharacter: newValue.triggerContext.triggerCharacter
|
|
1275
|
+
} : void 0
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return newValue;
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
return [mentionConfig.of(options), field, fields.of(field)];
|
|
1283
|
+
}
|
|
1284
|
+
function getCurrentMentionReplaceRange(state) {
|
|
1285
|
+
const allFields = state.facet(fields);
|
|
1286
|
+
for (const field of allFields) {
|
|
1287
|
+
const fieldState = state.field(field, false);
|
|
1288
|
+
if (fieldState && fieldState.triggerContext && typeof fieldState.triggerContext.from === "number" && typeof fieldState.triggerContext.to === "number") {
|
|
1289
|
+
return {
|
|
1290
|
+
from: fieldState.triggerContext.from,
|
|
1291
|
+
to: fieldState.triggerContext.to
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// src/mention/react.tsx
|
|
1298
|
+
import { useInjectorEffect as useInjectorEffect4, useLatest as useLatest4 } from "@coze-editor/react-hooks";
|
|
1299
|
+
function Mention(props) {
|
|
1300
|
+
const propsRef = useLatest4(props);
|
|
1301
|
+
useInjectorEffect4((injector) => {
|
|
1302
|
+
const sharedOptions = {
|
|
1303
|
+
search: props.search,
|
|
1304
|
+
onOpenChange(...args) {
|
|
1305
|
+
if (typeof propsRef.current.onOpenChange === "function") {
|
|
1306
|
+
return propsRef.current.onOpenChange(...args);
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1309
|
+
onSearch(...args) {
|
|
1310
|
+
if (typeof propsRef.current.onSearch === "function") {
|
|
1311
|
+
return propsRef.current.onSearch(...args);
|
|
1312
|
+
}
|
|
1313
|
+
},
|
|
1314
|
+
onTrigger(...args) {
|
|
1315
|
+
if (typeof propsRef.current.onTrigger === "function") {
|
|
1316
|
+
return propsRef.current.onTrigger(...args);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
return injector.inject([
|
|
1321
|
+
mention(
|
|
1322
|
+
hasTrigger(props) ? {
|
|
1323
|
+
...sharedOptions,
|
|
1324
|
+
trigger(tr) {
|
|
1325
|
+
if (hasTrigger(propsRef.current)) {
|
|
1326
|
+
return propsRef.current.trigger(tr);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
} : {
|
|
1330
|
+
...sharedOptions,
|
|
1331
|
+
triggerCharacters: props.triggerCharacters ?? []
|
|
1332
|
+
}
|
|
1333
|
+
)
|
|
1334
|
+
]);
|
|
1335
|
+
});
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// src/embeded-line-view/react.tsx
|
|
1340
|
+
import { createPortal as createPortal6 } from "react-dom";
|
|
1341
|
+
import { forwardRef as forwardRef4, useImperativeHandle as useImperativeHandle4 } from "react";
|
|
1342
|
+
import {
|
|
1343
|
+
useHTMLElement as useHTMLElement5,
|
|
1344
|
+
useInjectorEffect as useInjectorEffect5,
|
|
1345
|
+
useLatest as useLatest5
|
|
1346
|
+
} from "@coze-editor/react-hooks";
|
|
1347
|
+
import { useEditor as useEditor5 } from "@coze-editor/react";
|
|
1348
|
+
import { Decoration as Decoration3, EditorView as EditorView7, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1349
|
+
import { MapMode as MapMode3, StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
|
|
1350
|
+
var DOMWidget = class extends WidgetType4 {
|
|
1351
|
+
constructor(options) {
|
|
1352
|
+
super();
|
|
1353
|
+
this.options = options;
|
|
1354
|
+
}
|
|
1355
|
+
toDOM() {
|
|
1356
|
+
return this.options.dom;
|
|
1357
|
+
}
|
|
1358
|
+
eq(other) {
|
|
1359
|
+
return this.options.dom === other.options.dom;
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
var EmbededLineViewSide = /* @__PURE__ */ ((EmbededLineViewSide2) => {
|
|
1363
|
+
EmbededLineViewSide2["Before"] = "before";
|
|
1364
|
+
EmbededLineViewSide2["After"] = "after";
|
|
1365
|
+
return EmbededLineViewSide2;
|
|
1366
|
+
})(EmbededLineViewSide || {});
|
|
1367
|
+
var updateEffect = StateEffect2.define();
|
|
1368
|
+
var EmbededLineView = forwardRef4(function EmbededLineView2({ children }, ref) {
|
|
1369
|
+
const dom = useHTMLElement5("div");
|
|
1370
|
+
const domRef = useLatest5(dom);
|
|
1371
|
+
const editor = useEditor5();
|
|
1372
|
+
const editorRef = useLatest5(editor);
|
|
1373
|
+
useImperativeHandle4(ref, () => ({
|
|
1374
|
+
update({ visible, line, side }) {
|
|
1375
|
+
var _a;
|
|
1376
|
+
(_a = editorRef.current) == null ? void 0 : _a.$view.dispatch({
|
|
1377
|
+
effects: updateEffect.of({
|
|
1378
|
+
visible,
|
|
1379
|
+
line: line ?? 1,
|
|
1380
|
+
side: side ?? "before" /* Before */
|
|
1381
|
+
})
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
}));
|
|
1385
|
+
useInjectorEffect5((injector) => {
|
|
1386
|
+
const field = StateField3.define({
|
|
1387
|
+
create() {
|
|
1388
|
+
return {
|
|
1389
|
+
position: 0,
|
|
1390
|
+
side: "before" /* Before */,
|
|
1391
|
+
decorations: Decoration3.none
|
|
1392
|
+
};
|
|
1393
|
+
},
|
|
1394
|
+
update(value, tr) {
|
|
1395
|
+
const newPosition = tr.changes.mapPos(
|
|
1396
|
+
value.position,
|
|
1397
|
+
-1,
|
|
1398
|
+
MapMode3.Simple
|
|
1399
|
+
);
|
|
1400
|
+
let newValue = value;
|
|
1401
|
+
if (typeof newPosition === "number" && newPosition !== value.position) {
|
|
1402
|
+
const docLine = tr.state.doc.lineAt(newPosition);
|
|
1403
|
+
let decoPos = 0;
|
|
1404
|
+
if (value.side === "after" /* After */) {
|
|
1405
|
+
decoPos = docLine.to;
|
|
1406
|
+
} else {
|
|
1407
|
+
decoPos = docLine.from;
|
|
1408
|
+
}
|
|
1409
|
+
newValue = {
|
|
1410
|
+
...value,
|
|
1411
|
+
position: newPosition,
|
|
1412
|
+
decorations: Decoration3.set([
|
|
1413
|
+
Decoration3.widget({
|
|
1414
|
+
widget: new DOMWidget({
|
|
1415
|
+
dom: domRef.current
|
|
1416
|
+
}),
|
|
1417
|
+
block: true,
|
|
1418
|
+
side: value.side === "after" /* After */ ? 1 : -1
|
|
1419
|
+
}).range(decoPos)
|
|
1420
|
+
])
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
for (const effect of tr.effects) {
|
|
1424
|
+
if (effect.is(updateEffect)) {
|
|
1425
|
+
const { visible, line, side } = effect.value;
|
|
1426
|
+
if (!visible) {
|
|
1427
|
+
return {
|
|
1428
|
+
position: newValue.position,
|
|
1429
|
+
side,
|
|
1430
|
+
decorations: Decoration3.none
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
if (line < 1 || line > tr.startState.doc.lines) {
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
const docLine = tr.startState.doc.line(line);
|
|
1437
|
+
let decoPos = 0;
|
|
1438
|
+
if (side === "after" /* After */) {
|
|
1439
|
+
decoPos = docLine.to;
|
|
1440
|
+
} else {
|
|
1441
|
+
decoPos = docLine.from;
|
|
1442
|
+
}
|
|
1443
|
+
return {
|
|
1444
|
+
position: decoPos,
|
|
1445
|
+
side,
|
|
1446
|
+
decorations: Decoration3.set([
|
|
1447
|
+
Decoration3.widget({
|
|
1448
|
+
widget: new DOMWidget({
|
|
1449
|
+
dom: domRef.current
|
|
1450
|
+
}),
|
|
1451
|
+
block: true,
|
|
1452
|
+
side: newValue.side === "after" /* After */ ? 1 : -1
|
|
1453
|
+
}).range(decoPos)
|
|
1454
|
+
])
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
return newValue;
|
|
1459
|
+
},
|
|
1460
|
+
provide(f) {
|
|
1461
|
+
return EditorView7.decorations.compute(
|
|
1462
|
+
[f],
|
|
1463
|
+
(state) => state.field(f).decorations
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
return injector.inject([field]);
|
|
1468
|
+
}, []);
|
|
1469
|
+
return createPortal6(children, dom);
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
// src/prefix-element/react.tsx
|
|
1473
|
+
import { createPortal as createPortal7 } from "react-dom";
|
|
1474
|
+
import { useLayoutEffect as useLayoutEffect8 } from "react";
|
|
1475
|
+
import { useHTMLElement as useHTMLElement6, useLatest as useLatest6 } from "@coze-editor/react-hooks";
|
|
1476
|
+
import { useInjector as useInjector9 } from "@coze-editor/react";
|
|
1477
|
+
import { Decoration as Decoration4, EditorView as EditorView8, keymap, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1478
|
+
|
|
1479
|
+
// src/prefix-element/dom.ts
|
|
1480
|
+
var scratchRange;
|
|
1481
|
+
function textRange(node, from, to = from) {
|
|
1482
|
+
const range = scratchRange || (scratchRange = document.createRange());
|
|
1483
|
+
range.setEnd(node, to);
|
|
1484
|
+
range.setStart(node, from);
|
|
1485
|
+
return range;
|
|
1486
|
+
}
|
|
1487
|
+
function flattenRect(rect, left) {
|
|
1488
|
+
const x = left ? rect.left : rect.right;
|
|
1489
|
+
return { left: x, right: x, top: rect.top, bottom: rect.bottom };
|
|
1490
|
+
}
|
|
1491
|
+
function clientRectsFor(dom) {
|
|
1492
|
+
if (dom.nodeType == 3) {
|
|
1493
|
+
return textRange(dom, 0, dom.nodeValue.length).getClientRects();
|
|
1494
|
+
} else if (dom.nodeType == 1) {
|
|
1495
|
+
return dom.getClientRects();
|
|
1496
|
+
} else {
|
|
1497
|
+
return [];
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// src/prefix-element/react.tsx
|
|
1502
|
+
var PrefixElementWidget = class extends WidgetType5 {
|
|
1503
|
+
constructor(content) {
|
|
1504
|
+
super();
|
|
1505
|
+
this.content = content;
|
|
1506
|
+
}
|
|
1507
|
+
toDOM(view) {
|
|
1508
|
+
const wrap = document.createElement("span");
|
|
1509
|
+
wrap.className = "cm-prefix-element";
|
|
1510
|
+
wrap.style.cssText = "height: 0;";
|
|
1511
|
+
wrap.appendChild(
|
|
1512
|
+
typeof this.content === "string" ? document.createTextNode(this.content) : typeof this.content === "function" ? this.content(view) : this.content
|
|
1513
|
+
);
|
|
1514
|
+
if (typeof this.content === "string") {
|
|
1515
|
+
wrap.setAttribute("aria-label", `prefix ${this.content}`);
|
|
1516
|
+
} else {
|
|
1517
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
1518
|
+
}
|
|
1519
|
+
return wrap;
|
|
1520
|
+
}
|
|
1521
|
+
coordsAt(dom) {
|
|
1522
|
+
const rects = dom.firstChild ? clientRectsFor(dom.firstChild) : [];
|
|
1523
|
+
if (!rects.length) {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
const style = window.getComputedStyle(dom.parentNode);
|
|
1527
|
+
const rect = flattenRect(rects[0], style.direction != "rtl");
|
|
1528
|
+
const lineHeight = parseInt(style.lineHeight);
|
|
1529
|
+
if (rect.bottom - rect.top > lineHeight * 1.5) {
|
|
1530
|
+
return {
|
|
1531
|
+
left: rect.left,
|
|
1532
|
+
right: rect.right,
|
|
1533
|
+
top: rect.top,
|
|
1534
|
+
bottom: rect.top + lineHeight
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
return rect;
|
|
1538
|
+
}
|
|
1539
|
+
ignoreEvent() {
|
|
1540
|
+
return false;
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
function PrefixElement({ deletable, onDelete, children }) {
|
|
1544
|
+
const element = useHTMLElement6("span");
|
|
1545
|
+
const latestElement = useLatest6(element);
|
|
1546
|
+
const latestDeletable = useLatest6(deletable);
|
|
1547
|
+
const latestOnDelete = useLatest6(onDelete);
|
|
1548
|
+
const injector = useInjector9();
|
|
1549
|
+
useLayoutEffect8(() => {
|
|
1550
|
+
function run(view) {
|
|
1551
|
+
if (view.state.selection.main.empty && view.state.selection.main.head === 0 && latestDeletable.current === true && typeof latestOnDelete.current === "function") {
|
|
1552
|
+
latestOnDelete.current();
|
|
1553
|
+
}
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
return injector.inject([
|
|
1557
|
+
EditorView8.decorations.of(
|
|
1558
|
+
Decoration4.set([
|
|
1559
|
+
Decoration4.widget({
|
|
1560
|
+
side: -100,
|
|
1561
|
+
block: false,
|
|
1562
|
+
widget: new PrefixElementWidget(() => latestElement.current)
|
|
1563
|
+
}).range(0)
|
|
1564
|
+
])
|
|
1565
|
+
),
|
|
1566
|
+
keymap.of([
|
|
1567
|
+
{ key: "Backspace", shift: run, run },
|
|
1568
|
+
{ key: "Meta-Backspace", shift: run, run }
|
|
1569
|
+
])
|
|
1570
|
+
]);
|
|
1571
|
+
}, []);
|
|
1572
|
+
return createPortal7(children, element);
|
|
1573
|
+
}
|
|
1574
|
+
export {
|
|
1575
|
+
ActiveLinePlaceholder,
|
|
1576
|
+
CursorInlayHint,
|
|
1577
|
+
CursorMirror,
|
|
1578
|
+
DiagnosticMarkers,
|
|
1579
|
+
DiffView,
|
|
1580
|
+
EmbededLineView,
|
|
1581
|
+
EmbededLineViewSide,
|
|
1582
|
+
FoldGutter,
|
|
1583
|
+
Gutter,
|
|
1584
|
+
GutterLineMarker,
|
|
1585
|
+
GutterPlacement,
|
|
1586
|
+
LineWidget,
|
|
1587
|
+
Markers,
|
|
1588
|
+
Mention,
|
|
1589
|
+
Placeholder,
|
|
1590
|
+
PositionMirror,
|
|
1591
|
+
PrefixElement,
|
|
1592
|
+
RefElement,
|
|
1593
|
+
SelectionSide,
|
|
1594
|
+
getCurrentMentionReplaceRange
|
|
1595
|
+
};
|
|
1596
|
+
//# sourceMappingURL=index.js.map
|