@coze-editor/react-components 0.1.0-alpha.0fd19e

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.
@@ -0,0 +1,1475 @@
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
+ const { search = true, onOpenChange, onSearch } = options2;
1176
+ let { show } = value;
1177
+ let triggerContext = void 0;
1178
+ if (tr.docChanged) {
1179
+ if (hasTriggerCharacters(options2)) {
1180
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
1181
+ const insertString = inserted.toString();
1182
+ if (fromA === toA && options2.triggerCharacters.includes(insertString)) {
1183
+ triggerContext = {
1184
+ from: fromB,
1185
+ to: toB,
1186
+ triggerCharacter: insertString,
1187
+ cursorPosition: tr.state.selection.main.head
1188
+ };
1189
+ }
1190
+ });
1191
+ } else if (hasTrigger(options2)) {
1192
+ triggerContext = options2.trigger(tr);
1193
+ }
1194
+ }
1195
+ const isSelectUserEvent = tr.isUserEvent("select");
1196
+ const newValue = {
1197
+ show: value.show,
1198
+ triggerContext: value.triggerContext ? { ...value.triggerContext } : void 0
1199
+ };
1200
+ if (triggerContext) {
1201
+ newValue.triggerContext = triggerContext;
1202
+ show = true;
1203
+ if (typeof onSearch === "function") {
1204
+ onSearch({
1205
+ value: ""
1206
+ });
1207
+ }
1208
+ } else if (tr.docChanged && value.triggerContext && search) {
1209
+ if (newValue.triggerContext) {
1210
+ const newFrom = tr.changes.mapPos(
1211
+ value.triggerContext.from,
1212
+ 1,
1213
+ MapMode2.TrackAfter
1214
+ );
1215
+ const newTo = tr.changes.mapPos(
1216
+ value.triggerContext.to,
1217
+ 1,
1218
+ MapMode2.Simple
1219
+ );
1220
+ if (typeof newFrom === "number" && typeof newTo === "number") {
1221
+ newValue.triggerContext.from = newFrom;
1222
+ newValue.triggerContext.to = newTo;
1223
+ } else {
1224
+ show = false;
1225
+ }
1226
+ }
1227
+ if (show === true) {
1228
+ const validFor = typeof search === "object" ? search.validFor : defaultValidFor;
1229
+ const from = value.triggerContext.cursorPosition;
1230
+ const to = tr.state.selection.main.head;
1231
+ if (to >= from) {
1232
+ const text = tr.state.sliceDoc(from, to);
1233
+ if (validFor(text, from, to, tr.state)) {
1234
+ show = true;
1235
+ if (typeof onSearch === "function") {
1236
+ onSearch({
1237
+ value: text
1238
+ });
1239
+ }
1240
+ } else {
1241
+ show = false;
1242
+ }
1243
+ } else {
1244
+ show = false;
1245
+ }
1246
+ }
1247
+ } else if (isSelectUserEvent) {
1248
+ show = false;
1249
+ }
1250
+ if (show === false) {
1251
+ newValue.triggerContext = void 0;
1252
+ }
1253
+ if (show !== value.show) {
1254
+ newValue.show = show;
1255
+ if (typeof onOpenChange === "function") {
1256
+ onOpenChange({
1257
+ value: show,
1258
+ state: tr.state,
1259
+ triggerContext: newValue.triggerContext ? {
1260
+ from: newValue.triggerContext.from,
1261
+ to: newValue.triggerContext.to,
1262
+ triggerCharacter: newValue.triggerContext.triggerCharacter
1263
+ } : void 0
1264
+ });
1265
+ }
1266
+ }
1267
+ return newValue;
1268
+ }
1269
+ });
1270
+ return [mentionConfig.of(options), field, fields.of(field)];
1271
+ }
1272
+ function getCurrentMentionReplaceRange(state) {
1273
+ const allFields = state.facet(fields);
1274
+ for (const field of allFields) {
1275
+ const fieldState = state.field(field, false);
1276
+ if (fieldState && fieldState.triggerContext && typeof fieldState.triggerContext.from === "number" && typeof fieldState.triggerContext.to === "number") {
1277
+ return {
1278
+ from: fieldState.triggerContext.from,
1279
+ to: fieldState.triggerContext.to
1280
+ };
1281
+ }
1282
+ }
1283
+ }
1284
+
1285
+ // src/mention/react.tsx
1286
+ import { useInjectorEffect as useInjectorEffect4, useLatest as useLatest4 } from "@coze-editor/react-hooks";
1287
+ function Mention(props) {
1288
+ const propsRef = useLatest4(props);
1289
+ useInjectorEffect4((injector) => {
1290
+ const sharedOptions = {
1291
+ search: props.search,
1292
+ onOpenChange(...args) {
1293
+ if (typeof propsRef.current.onOpenChange === "function") {
1294
+ return propsRef.current.onOpenChange(...args);
1295
+ }
1296
+ },
1297
+ onSearch(...args) {
1298
+ if (typeof propsRef.current.onSearch === "function") {
1299
+ return propsRef.current.onSearch(...args);
1300
+ }
1301
+ }
1302
+ };
1303
+ return injector.inject([
1304
+ mention(
1305
+ hasTrigger(props) ? {
1306
+ ...sharedOptions,
1307
+ trigger(tr) {
1308
+ if (hasTrigger(propsRef.current)) {
1309
+ return propsRef.current.trigger(tr);
1310
+ }
1311
+ }
1312
+ } : {
1313
+ ...sharedOptions,
1314
+ triggerCharacters: props.triggerCharacters ?? []
1315
+ }
1316
+ )
1317
+ ]);
1318
+ });
1319
+ return null;
1320
+ }
1321
+
1322
+ // src/embeded-line-view/react.tsx
1323
+ import { createPortal as createPortal6 } from "react-dom";
1324
+ import { forwardRef as forwardRef4, useImperativeHandle as useImperativeHandle4 } from "react";
1325
+ import {
1326
+ useHTMLElement as useHTMLElement5,
1327
+ useInjectorEffect as useInjectorEffect5,
1328
+ useLatest as useLatest5
1329
+ } from "@coze-editor/react-hooks";
1330
+ import { useEditor as useEditor5 } from "@coze-editor/react";
1331
+ import { Decoration as Decoration3, EditorView as EditorView7, WidgetType as WidgetType4 } from "@codemirror/view";
1332
+ import { MapMode as MapMode3, StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
1333
+ var DOMWidget = class extends WidgetType4 {
1334
+ constructor(options) {
1335
+ super();
1336
+ this.options = options;
1337
+ }
1338
+ toDOM() {
1339
+ return this.options.dom;
1340
+ }
1341
+ eq(other) {
1342
+ return this.options.dom === other.options.dom;
1343
+ }
1344
+ };
1345
+ var EmbededLineViewSide = /* @__PURE__ */ ((EmbededLineViewSide2) => {
1346
+ EmbededLineViewSide2["Before"] = "before";
1347
+ EmbededLineViewSide2["After"] = "after";
1348
+ return EmbededLineViewSide2;
1349
+ })(EmbededLineViewSide || {});
1350
+ var updateEffect = StateEffect2.define();
1351
+ var EmbededLineView = forwardRef4(function EmbededLineView2({ children }, ref) {
1352
+ const dom = useHTMLElement5("div");
1353
+ const domRef = useLatest5(dom);
1354
+ const editor = useEditor5();
1355
+ const editorRef = useLatest5(editor);
1356
+ useImperativeHandle4(ref, () => ({
1357
+ update({ visible, line, side }) {
1358
+ var _a;
1359
+ (_a = editorRef.current) == null ? void 0 : _a.$view.dispatch({
1360
+ effects: updateEffect.of({
1361
+ visible,
1362
+ line: line ?? 1,
1363
+ side: side ?? "before" /* Before */
1364
+ })
1365
+ });
1366
+ }
1367
+ }));
1368
+ useInjectorEffect5((injector) => {
1369
+ const field = StateField3.define({
1370
+ create() {
1371
+ return {
1372
+ position: 0,
1373
+ side: "before" /* Before */,
1374
+ decorations: Decoration3.none
1375
+ };
1376
+ },
1377
+ update(value, tr) {
1378
+ const newPosition = tr.changes.mapPos(
1379
+ value.position,
1380
+ -1,
1381
+ MapMode3.Simple
1382
+ );
1383
+ let newValue = value;
1384
+ if (typeof newPosition === "number" && newPosition !== value.position) {
1385
+ const docLine = tr.state.doc.lineAt(newPosition);
1386
+ let decoPos = 0;
1387
+ if (value.side === "after" /* After */) {
1388
+ decoPos = docLine.to;
1389
+ } else {
1390
+ decoPos = docLine.from;
1391
+ }
1392
+ newValue = {
1393
+ ...value,
1394
+ position: newPosition,
1395
+ decorations: Decoration3.set([
1396
+ Decoration3.widget({
1397
+ widget: new DOMWidget({
1398
+ dom: domRef.current
1399
+ }),
1400
+ block: true,
1401
+ side: value.side === "after" /* After */ ? 1 : -1
1402
+ }).range(decoPos)
1403
+ ])
1404
+ };
1405
+ }
1406
+ for (const effect of tr.effects) {
1407
+ if (effect.is(updateEffect)) {
1408
+ const { visible, line, side } = effect.value;
1409
+ if (!visible) {
1410
+ return {
1411
+ position: newValue.position,
1412
+ side,
1413
+ decorations: Decoration3.none
1414
+ };
1415
+ }
1416
+ if (line < 1 || line > tr.startState.doc.lines) {
1417
+ continue;
1418
+ }
1419
+ const docLine = tr.startState.doc.line(line);
1420
+ let decoPos = 0;
1421
+ if (side === "after" /* After */) {
1422
+ decoPos = docLine.to;
1423
+ } else {
1424
+ decoPos = docLine.from;
1425
+ }
1426
+ return {
1427
+ position: decoPos,
1428
+ side,
1429
+ decorations: Decoration3.set([
1430
+ Decoration3.widget({
1431
+ widget: new DOMWidget({
1432
+ dom: domRef.current
1433
+ }),
1434
+ block: true,
1435
+ side: newValue.side === "after" /* After */ ? 1 : -1
1436
+ }).range(decoPos)
1437
+ ])
1438
+ };
1439
+ }
1440
+ }
1441
+ return newValue;
1442
+ },
1443
+ provide(f) {
1444
+ return EditorView7.decorations.compute(
1445
+ [f],
1446
+ (state) => state.field(f).decorations
1447
+ );
1448
+ }
1449
+ });
1450
+ return injector.inject([field]);
1451
+ }, []);
1452
+ return createPortal6(children, dom);
1453
+ });
1454
+ export {
1455
+ ActiveLinePlaceholder,
1456
+ CursorInlayHint,
1457
+ CursorMirror,
1458
+ DiagnosticMarkers,
1459
+ DiffView,
1460
+ EmbededLineView,
1461
+ EmbededLineViewSide,
1462
+ FoldGutter,
1463
+ Gutter,
1464
+ GutterLineMarker,
1465
+ GutterPlacement,
1466
+ LineWidget,
1467
+ Markers,
1468
+ Mention,
1469
+ Placeholder,
1470
+ PositionMirror,
1471
+ RefElement,
1472
+ SelectionSide,
1473
+ getCurrentMentionReplaceRange
1474
+ };
1475
+ //# sourceMappingURL=index.js.map