@markput/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/index.css +1 -0
- package/index.d.ts +261 -0
- package/index.js +1755 -0
- package/package.json +32 -0
package/index.js
ADDED
|
@@ -0,0 +1,1755 @@
|
|
|
1
|
+
import "./index.css";
|
|
2
|
+
import { jsx as m, Fragment as oe, jsxs as ie } from "react/jsx-runtime";
|
|
3
|
+
import ae, { createContext as ce, useContext as N, useEffect as x, useState as A, useRef as I, memo as W, useCallback as R, useImperativeHandle as le, useMemo as ue, forwardRef as de } from "react";
|
|
4
|
+
function he(t) {
|
|
5
|
+
return t ? Object.fromEntries(
|
|
6
|
+
Object.entries(t).map(([e, n]) => e.startsWith("data") && e.length > 4 && e[4] === e[4].toUpperCase() ? [`data-${e.slice(4).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`, n] : [e, n])
|
|
7
|
+
) : {};
|
|
8
|
+
}
|
|
9
|
+
const fe = {
|
|
10
|
+
container: "div",
|
|
11
|
+
span: "span"
|
|
12
|
+
};
|
|
13
|
+
function Z(t, e) {
|
|
14
|
+
return e.props.slots?.[t] ? e.props.slots[t] : fe[t];
|
|
15
|
+
}
|
|
16
|
+
function J(t, e) {
|
|
17
|
+
const n = e.props.slotProps?.[t];
|
|
18
|
+
return n ? he(n) : void 0;
|
|
19
|
+
}
|
|
20
|
+
function ge(...t) {
|
|
21
|
+
return t.filter(Boolean).join(" ") || void 0;
|
|
22
|
+
}
|
|
23
|
+
function pe(...t) {
|
|
24
|
+
const e = t.reduce((n, s) => (s && Object.assign(n, s), n), {});
|
|
25
|
+
return Object.keys(e).length > 0 ? e : void 0;
|
|
26
|
+
}
|
|
27
|
+
function Q(t) {
|
|
28
|
+
if (t == null) throw new Error("Value must be a non nullable!");
|
|
29
|
+
}
|
|
30
|
+
var k = /* @__PURE__ */ ((t) => (t.UP = "ArrowUp", t.DOWN = "ArrowDown", t.LEFT = "ArrowLeft", t.RIGHT = "ArrowRight", t.END = "End", t.HOME = "Home", t.PAGE_DOWN = "PageDown", t.PAGE_UP = "PageUp", t.ENTER = "Enter", t.TAB = "Tab", t.SPACE = " ", t.BACKSPACE = "Backspace", t.DELETE = "Delete", t.COMMA = ",", t.ESC = "Escape", t))(k || {});
|
|
31
|
+
const me = "@", ve = "@[__value__](__meta__)", y = {
|
|
32
|
+
Value: "__value__",
|
|
33
|
+
Meta: "__meta__",
|
|
34
|
+
Nested: "__nested__"
|
|
35
|
+
}, w = {
|
|
36
|
+
Value: "value",
|
|
37
|
+
Meta: "meta",
|
|
38
|
+
Nested: "nested"
|
|
39
|
+
};
|
|
40
|
+
function xe(t, e) {
|
|
41
|
+
const { segments: n, gapTypes: s, counts: r, valueGapIndices: o } = ye(t);
|
|
42
|
+
ke(r, t);
|
|
43
|
+
const i = r.value === 2, { segments: a, gapTypes: c } = i ? Se(n, s, o) : { segments: n, gapTypes: s };
|
|
44
|
+
return {
|
|
45
|
+
markup: t,
|
|
46
|
+
index: e,
|
|
47
|
+
segments: a,
|
|
48
|
+
gapTypes: c,
|
|
49
|
+
hasNested: r.nested === 1,
|
|
50
|
+
hasTwoValues: i,
|
|
51
|
+
segmentGlobalIndices: Array.from({ length: a.length })
|
|
52
|
+
// Will be populated by MarkupRegistry
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function ye(t) {
|
|
56
|
+
const e = [], n = [], s = [], r = {
|
|
57
|
+
value: 0,
|
|
58
|
+
meta: 0,
|
|
59
|
+
nested: 0
|
|
60
|
+
}, o = [], i = [w.Value, w.Meta, w.Nested];
|
|
61
|
+
for (const l of i) {
|
|
62
|
+
const h = j[l];
|
|
63
|
+
let u = t.indexOf(h);
|
|
64
|
+
for (; u !== -1; )
|
|
65
|
+
o.push({ type: l, position: u }), u = t.indexOf(h, u + h.length);
|
|
66
|
+
}
|
|
67
|
+
o.sort((l, h) => l.position - h.position);
|
|
68
|
+
let a = 0;
|
|
69
|
+
for (const l of o) {
|
|
70
|
+
const h = t.substring(a, l.position);
|
|
71
|
+
h.length > 0 && e.push(h), n.push(l.type), r[l.type]++, l.type === w.Value && s.push(n.length - 1), a = l.position + j[l.type].length;
|
|
72
|
+
}
|
|
73
|
+
const c = t.substring(a);
|
|
74
|
+
return c.length > 0 && e.push(c), {
|
|
75
|
+
segments: e,
|
|
76
|
+
gapTypes: n,
|
|
77
|
+
counts: r,
|
|
78
|
+
valueGapIndices: s
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function ke(t, e) {
|
|
82
|
+
const n = [
|
|
83
|
+
{ count: t.value, max: 2, name: y.Value },
|
|
84
|
+
{ count: t.meta, max: 1, name: y.Meta },
|
|
85
|
+
{ count: t.nested, max: 1, name: y.Nested }
|
|
86
|
+
];
|
|
87
|
+
for (const { count: s, max: r, name: o } of n)
|
|
88
|
+
if (s > r)
|
|
89
|
+
throw new Error(`Invalid markup: "${e}". Max ${r} "${o}" placeholders, got ${s}`);
|
|
90
|
+
if (t.value === 0 && t.nested === 0)
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Invalid markup: "${e}". Need at least one "${y.Value}" or "${y.Nested}"`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const j = {
|
|
96
|
+
[w.Value]: y.Value,
|
|
97
|
+
[w.Meta]: y.Meta,
|
|
98
|
+
[w.Nested]: y.Nested
|
|
99
|
+
};
|
|
100
|
+
function Se(t, e, n) {
|
|
101
|
+
if (n.length !== 2)
|
|
102
|
+
return { segments: t, gapTypes: e };
|
|
103
|
+
const [s, r] = n, o = [], i = t[s], a = t[s + 1];
|
|
104
|
+
i && a && o.push(u(i, a, t[s + 2]));
|
|
105
|
+
for (let d = s + 2; d < r; d++)
|
|
106
|
+
o.push(t[d]);
|
|
107
|
+
const c = t[r], l = t[r + 1];
|
|
108
|
+
c && l && o.push(u(c, l, t[r + 2]));
|
|
109
|
+
const h = e.filter((d) => d !== w.Value);
|
|
110
|
+
return { segments: o, gapTypes: h };
|
|
111
|
+
function u(d, g, E) {
|
|
112
|
+
if (!E) return [d, g, ""];
|
|
113
|
+
const L = E.charAt(0), re = L && !g.includes(L) && !E.startsWith(d) ? L : "";
|
|
114
|
+
return [d, g, re];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
class Ce {
|
|
118
|
+
markups;
|
|
119
|
+
descriptors;
|
|
120
|
+
/** Deduplicated list of unique segment definitions (static strings or dynamic patterns) */
|
|
121
|
+
segments = [];
|
|
122
|
+
/** Map from first segment index to descriptors that start with this segment (for O(1) lookup) */
|
|
123
|
+
firstSegmentIndexMap = /* @__PURE__ */ new Map();
|
|
124
|
+
constructor(e) {
|
|
125
|
+
this.markups = e;
|
|
126
|
+
const n = /* @__PURE__ */ new Map();
|
|
127
|
+
this.descriptors = e.map((s, r) => {
|
|
128
|
+
if (s === void 0)
|
|
129
|
+
return null;
|
|
130
|
+
const o = xe(s, r);
|
|
131
|
+
return o.segments.forEach((i, a) => {
|
|
132
|
+
this.processSegment(o, i, a, n);
|
|
133
|
+
}), this.addToFirstSegmentIndexMap(o), o;
|
|
134
|
+
}).filter((s) => s !== null);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Adds a descriptor to the firstSegmentIndexMap using its first segment's global index
|
|
138
|
+
*/
|
|
139
|
+
addToFirstSegmentIndexMap(e) {
|
|
140
|
+
const n = e.segmentGlobalIndices[0];
|
|
141
|
+
if (n === void 0) return;
|
|
142
|
+
const s = this.firstSegmentIndexMap.get(n);
|
|
143
|
+
s ? s.push(e) : this.firstSegmentIndexMap.set(n, [e]);
|
|
144
|
+
}
|
|
145
|
+
processSegment(e, n, s, r) {
|
|
146
|
+
const o = this.getSegmentKey(n);
|
|
147
|
+
if (!o) return;
|
|
148
|
+
this.registerSegment(n, o, r);
|
|
149
|
+
const i = r.get(o);
|
|
150
|
+
if (e.segmentGlobalIndices[s] = i, typeof n != "string") {
|
|
151
|
+
const [a, c] = n;
|
|
152
|
+
a && this.registerSegment(a, a, r), c && this.registerSegment(c, c, r);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
registerSegment(e, n, s) {
|
|
156
|
+
if (!s.has(n)) {
|
|
157
|
+
const r = this.segments.length;
|
|
158
|
+
this.segments.push(e), s.set(n, r);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Gets a unique key for a segment definition
|
|
163
|
+
* For static segments (strings), returns the string itself if non-empty
|
|
164
|
+
* For dynamic segments (arrays), returns before|after|exclusions if before or after is non-empty
|
|
165
|
+
* Returns empty string for segments that should be ignored
|
|
166
|
+
*/
|
|
167
|
+
getSegmentKey(e) {
|
|
168
|
+
if (typeof e == "string")
|
|
169
|
+
return e;
|
|
170
|
+
const [n, s, r] = e;
|
|
171
|
+
return n || s ? `${n}|${s}|${r}` : "";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function K(t, e) {
|
|
175
|
+
if (!e)
|
|
176
|
+
return t;
|
|
177
|
+
let n = 5381;
|
|
178
|
+
for (let s = 0; s < e.length; s++)
|
|
179
|
+
n = n * 33 ^ e.charCodeAt(s);
|
|
180
|
+
return n = n >>> 0, t * 1e6 + (n & 1048575);
|
|
181
|
+
}
|
|
182
|
+
class we {
|
|
183
|
+
constructor(e, n) {
|
|
184
|
+
if (this.descriptor = e, this.expectedSegmentIndex = 1, this.start = n.start, this.end = n.end, e.segments.length === 1 && (this.expectedSegmentIndex = NaN, this.gaps.value = { start: this.start, end: this.end }), e.hasTwoValues && n.captured) {
|
|
185
|
+
this.captured = n.captured;
|
|
186
|
+
const s = n.start + n.value.indexOf(n.captured), r = s + n.captured.length;
|
|
187
|
+
this.gaps.value = { start: s, end: r };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
gaps = {};
|
|
191
|
+
/** Captured value from first dynamic segment (for hasTwoValues patterns) */
|
|
192
|
+
captured;
|
|
193
|
+
/**
|
|
194
|
+
* Index of expected next segment:
|
|
195
|
+
* - >= 0: active, waiting for segment at this index
|
|
196
|
+
* - NaN: completed successfully
|
|
197
|
+
* - -1: invalid, should be discarded
|
|
198
|
+
*/
|
|
199
|
+
expectedSegmentIndex;
|
|
200
|
+
start;
|
|
201
|
+
end;
|
|
202
|
+
/**
|
|
203
|
+
* Check if the match is invalid and should be discarded
|
|
204
|
+
*/
|
|
205
|
+
get isInvalid() {
|
|
206
|
+
return this.expectedSegmentIndex === -1;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Check if the pattern is completed (computed property)
|
|
210
|
+
*/
|
|
211
|
+
get isCompleted() {
|
|
212
|
+
return isNaN(this.expectedSegmentIndex);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if the match is waiting for the last segment (high priority)
|
|
216
|
+
*/
|
|
217
|
+
get isAwaitingLastSegment() {
|
|
218
|
+
return this.expectedSegmentIndex === this.descriptor.segments.length - 1;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get the next expected segment index
|
|
222
|
+
*/
|
|
223
|
+
get nextSegment() {
|
|
224
|
+
if (this.isCompleted || this.isInvalid)
|
|
225
|
+
return;
|
|
226
|
+
const e = this.descriptor.segmentGlobalIndices[this.expectedSegmentIndex], n = this.descriptor.segments[this.expectedSegmentIndex];
|
|
227
|
+
if (typeof n == "object" && this.descriptor.hasTwoValues && this.captured && this.isAwaitingLastSegment) {
|
|
228
|
+
const [s, r] = n, o = s + this.captured + r;
|
|
229
|
+
return K(e, o);
|
|
230
|
+
}
|
|
231
|
+
return e;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Update state with new segment by setting gap positions
|
|
235
|
+
*/
|
|
236
|
+
processNext(e) {
|
|
237
|
+
const n = this.end, s = e.start, r = this.descriptor.gapTypes[this.expectedSegmentIndex - 1];
|
|
238
|
+
if (s < n) {
|
|
239
|
+
this.expectedSegmentIndex = -1;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this.gaps[r] = { start: n, end: s }, this.end = e.end, this.expectedSegmentIndex++, this.expectedSegmentIndex >= this.descriptor.segments.length && (this.expectedSegmentIndex = NaN);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Checks if this match conflicts with another match (overlaps and cannot nest properly)
|
|
246
|
+
*/
|
|
247
|
+
conflictsWith(e) {
|
|
248
|
+
return !e || this.start >= e.end ? !1 : !e.descriptor.hasNested || e.gaps.nested === void 0 ? !0 : !(this.start >= e.gaps.nested.start && this.end <= e.gaps.nested.end);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
class Te {
|
|
252
|
+
constructor(e) {
|
|
253
|
+
this.registry = e;
|
|
254
|
+
}
|
|
255
|
+
pendingStates = /* @__PURE__ */ new Map();
|
|
256
|
+
completingStates = /* @__PURE__ */ new Map();
|
|
257
|
+
completedStates = [];
|
|
258
|
+
/** Main method that converts found segments into structured matches */
|
|
259
|
+
process(e) {
|
|
260
|
+
this.pendingStates.clear(), this.completingStates.clear(), this.completedStates.length = 0;
|
|
261
|
+
for (const n of e)
|
|
262
|
+
this.processWaitingStates(n), this.tryStartNewStates(n);
|
|
263
|
+
return this.completedStates.map((n) => n.match);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Process states waiting for this segment
|
|
267
|
+
* Try states by priority until one is valid, keeping rejected states for later attempts
|
|
268
|
+
* Process completing states first (higher priority), then pending states
|
|
269
|
+
* Process only one state per call
|
|
270
|
+
*/
|
|
271
|
+
processWaitingStates(e) {
|
|
272
|
+
const n = this.dequeueWaitingMatch(e);
|
|
273
|
+
if (n && (n.processNext(e), !n.isInvalid)) {
|
|
274
|
+
if (n.isCompleted) return this.addToCompleted(n);
|
|
275
|
+
this.addToWaiting(n);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
tryStartNewStates(e) {
|
|
279
|
+
this.registry.firstSegmentIndexMap.get(e.index)?.forEach((n) => {
|
|
280
|
+
const s = new we(n, e);
|
|
281
|
+
if (!s.isInvalid) {
|
|
282
|
+
if (s.isCompleted) return this.addToCompleted(s);
|
|
283
|
+
this.addToWaiting(s);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Gets the next waiting match for the given segment
|
|
289
|
+
* Uses value-specific index for dynamic segments, base index for static segments
|
|
290
|
+
*/
|
|
291
|
+
dequeueWaitingMatch(e) {
|
|
292
|
+
const n = e.captured ? K(e.index, e.value) : e.index, s = this.completingStates.get(n);
|
|
293
|
+
if (s?.length) return s.pop();
|
|
294
|
+
const r = this.pendingStates.get(n);
|
|
295
|
+
if (r?.length) return r.pop();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Adds a state to the waiting list for the next expected segment
|
|
299
|
+
*/
|
|
300
|
+
addToWaiting(e) {
|
|
301
|
+
const n = e.nextSegment;
|
|
302
|
+
if (e.isAwaitingLastSegment) {
|
|
303
|
+
const s = this.completingStates.get(n) || [];
|
|
304
|
+
s.length === 0 && this.completingStates.set(n, s), s.push(e);
|
|
305
|
+
} else {
|
|
306
|
+
const s = this.pendingStates.get(n) || [];
|
|
307
|
+
s.length === 0 && this.pendingStates.set(n, s), s.push(e);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Add match to position-indexed array, replacing any existing match at the same position
|
|
312
|
+
* Uses binary search to find insertion point and maintains sorted order
|
|
313
|
+
* Relies on processing order to determine which match to keep
|
|
314
|
+
*/
|
|
315
|
+
addToCompleted(e) {
|
|
316
|
+
const n = e.start;
|
|
317
|
+
let s = 0, r = this.completedStates.length;
|
|
318
|
+
for (; s < r; ) {
|
|
319
|
+
const o = Math.floor((s + r) / 2);
|
|
320
|
+
this.completedStates[o].position < n ? s = o + 1 : r = o;
|
|
321
|
+
}
|
|
322
|
+
s < this.completedStates.length && this.completedStates[s].position === n ? this.completedStates[s].match = e : this.completedStates.splice(s, 0, { position: n, match: e });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const b = (t) => t.replace(/[.*+?^${}()|[\]\\\\]/g, "\\$&");
|
|
326
|
+
function Ee(t, e, n) {
|
|
327
|
+
const s = b(t), r = b(e), o = b(e + n);
|
|
328
|
+
return `${s}([^${o}]+?)${r}`;
|
|
329
|
+
}
|
|
330
|
+
class be {
|
|
331
|
+
staticRegex;
|
|
332
|
+
staticToIndex;
|
|
333
|
+
dynamicRegex;
|
|
334
|
+
dynamicEntries;
|
|
335
|
+
dynamicIndices;
|
|
336
|
+
constructor(e) {
|
|
337
|
+
this.initializeDual(e);
|
|
338
|
+
}
|
|
339
|
+
initializeDual(e) {
|
|
340
|
+
const n = [], s = [], r = /* @__PURE__ */ new Map();
|
|
341
|
+
if (e.forEach((o, i) => {
|
|
342
|
+
typeof o == "string" ? (n.push(o), r.set(o, i)) : s.push(o);
|
|
343
|
+
}), n.length > 0) {
|
|
344
|
+
const i = [...n].sort((a, c) => c.length - a.length).map(b);
|
|
345
|
+
this.staticRegex = new RegExp(`(?:${i.join("|")})`, "gu"), this.staticToIndex = r;
|
|
346
|
+
}
|
|
347
|
+
if (s.length > 0) {
|
|
348
|
+
const o = /* @__PURE__ */ new Set(), i = [];
|
|
349
|
+
s.forEach((a) => {
|
|
350
|
+
const c = e.indexOf(a);
|
|
351
|
+
if (typeof a == "string")
|
|
352
|
+
i.push({ index: c, pattern: b(a), definition: a });
|
|
353
|
+
else {
|
|
354
|
+
const [l, h, u] = a;
|
|
355
|
+
o.add(c);
|
|
356
|
+
const g = Ee(l, h, u).replace("(", `(?<content${c}>`);
|
|
357
|
+
i.push({ index: c, pattern: g, definition: a });
|
|
358
|
+
}
|
|
359
|
+
}), i.sort((a, c) => {
|
|
360
|
+
const l = typeof a.definition == "string" ? a.definition.length : a.pattern.length;
|
|
361
|
+
return (typeof c.definition == "string" ? c.definition.length : c.pattern.length) - l;
|
|
362
|
+
}), this.dynamicEntries = i, this.dynamicIndices = o, this.dynamicRegex = new RegExp(i.map((a, c) => `(?<seg${c}>${a.pattern})`).join("|"), "gu");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
search(e) {
|
|
366
|
+
const n = [], s = [];
|
|
367
|
+
if (this.staticRegex && this.staticToIndex)
|
|
368
|
+
for (const o of e.matchAll(this.staticRegex)) {
|
|
369
|
+
const i = this.staticToIndex.get(o[0]);
|
|
370
|
+
i !== void 0 && n.push({
|
|
371
|
+
index: i,
|
|
372
|
+
start: o.index,
|
|
373
|
+
end: o.index + o[0].length,
|
|
374
|
+
value: o[0]
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (this.dynamicRegex && this.dynamicEntries && this.dynamicIndices)
|
|
378
|
+
for (const o of e.matchAll(this.dynamicRegex)) {
|
|
379
|
+
const i = o[0], a = o.index;
|
|
380
|
+
let c, l;
|
|
381
|
+
if (o.groups) {
|
|
382
|
+
for (let h = 0; h < this.dynamicEntries.length; h++)
|
|
383
|
+
if (o.groups[`seg${h}`] !== void 0) {
|
|
384
|
+
c = this.dynamicEntries[h].index, this.dynamicIndices.has(c) && (l = o.groups[`content${c}`]);
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
c !== void 0 && s.push({
|
|
389
|
+
index: c,
|
|
390
|
+
start: a,
|
|
391
|
+
end: a + i.length,
|
|
392
|
+
value: i,
|
|
393
|
+
captured: l
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const r = [...s];
|
|
397
|
+
for (const o of n)
|
|
398
|
+
s.some(
|
|
399
|
+
(a) => o.start < a.end && o.end > a.start
|
|
400
|
+
) || r.push(o);
|
|
401
|
+
return r.sort((o, i) => o.start - i.start), r;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const Y = (t, e = 0, n = t.length) => ({
|
|
405
|
+
type: "text",
|
|
406
|
+
content: t.substring(e, n),
|
|
407
|
+
position: { start: e, end: n }
|
|
408
|
+
});
|
|
409
|
+
class Ie {
|
|
410
|
+
// Instance fields - only what's needed for single pass
|
|
411
|
+
input;
|
|
412
|
+
// ===== PUBLIC API =====
|
|
413
|
+
/**
|
|
414
|
+
* Builds nested token tree from pre-processed matches in a single pass
|
|
415
|
+
*
|
|
416
|
+
* Algorithm:
|
|
417
|
+
* 1. Iterate through matches in order
|
|
418
|
+
* 2. For each match:
|
|
419
|
+
* - Close any parents whose content ends before this match
|
|
420
|
+
* - Skip matches that conflict with the last accepted match
|
|
421
|
+
* - Add text token before this match
|
|
422
|
+
* - Create mark token and push to appropriate parent
|
|
423
|
+
* - If match has nested content, push to active parents stack
|
|
424
|
+
* 3. After all matches, close remaining parents and add final text
|
|
425
|
+
*
|
|
426
|
+
* Complexity: O(M) where M is number of matches
|
|
427
|
+
* Memory: O(D) for active parents stack where D is nesting depth
|
|
428
|
+
*/
|
|
429
|
+
build(e, n) {
|
|
430
|
+
return this.input = n, e.length === 0 ? [this.createTextToken(0, n.length)] : this.buildSinglePass(e);
|
|
431
|
+
}
|
|
432
|
+
// ===== SINGLE-PASS ALGORITHM =====
|
|
433
|
+
/**
|
|
434
|
+
* Builds token tree in a single pass through matches
|
|
435
|
+
*
|
|
436
|
+
* This is the core algorithm that processes matches sequentially,
|
|
437
|
+
* maintaining a stack of active parents and creating tokens directly.
|
|
438
|
+
*/
|
|
439
|
+
buildSinglePass(e) {
|
|
440
|
+
const n = [], s = [];
|
|
441
|
+
let r = null, o = 0;
|
|
442
|
+
for (const i of e) {
|
|
443
|
+
if (r && i.conflictsWith(r))
|
|
444
|
+
continue;
|
|
445
|
+
for (r = i; s.length > 0; ) {
|
|
446
|
+
const l = s[s.length - 1], h = this.getContentBounds(l.match);
|
|
447
|
+
if (h.end <= i.start)
|
|
448
|
+
this.finalizeParent(l, h.end), s.pop(), s.length > 0 ? s[s.length - 1].token.children.push(l.token) : n.push(l.token);
|
|
449
|
+
else
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
const a = s.length > 0 ? s[s.length - 1] : null;
|
|
453
|
+
if (a) {
|
|
454
|
+
const l = this.createTextToken(a.textPos, i.start);
|
|
455
|
+
a.token.children.push(l), a.textPos = i.end;
|
|
456
|
+
} else {
|
|
457
|
+
const l = this.createTextToken(o, i.start);
|
|
458
|
+
n.push(l), o = i.end;
|
|
459
|
+
}
|
|
460
|
+
const c = this.createMarkToken(i);
|
|
461
|
+
if (this.hasNestedContent(i)) {
|
|
462
|
+
const l = this.getContentBounds(i);
|
|
463
|
+
s.push({
|
|
464
|
+
match: i,
|
|
465
|
+
token: c,
|
|
466
|
+
textPos: l.start
|
|
467
|
+
});
|
|
468
|
+
} else
|
|
469
|
+
a ? a.token.children.push(c) : n.push(c);
|
|
470
|
+
}
|
|
471
|
+
for (; s.length > 0; ) {
|
|
472
|
+
const i = s.pop(), a = this.getContentBounds(i.match);
|
|
473
|
+
this.finalizeParent(i, a.end), s.length > 0 ? s[s.length - 1].token.children.push(i.token) : n.push(i.token);
|
|
474
|
+
}
|
|
475
|
+
return n.push(this.createTextToken(o, this.input.length)), n;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Finalizes a parent token by adding final text token if needed
|
|
479
|
+
*/
|
|
480
|
+
finalizeParent(e, n) {
|
|
481
|
+
const s = this.createTextToken(e.textPos, n);
|
|
482
|
+
e.token.children.push(s), e.token.children.some((o) => o.type === "mark") || (e.token.children = []);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Creates a mark token from a match (without children - those are added later)
|
|
486
|
+
*/
|
|
487
|
+
createMarkToken(e) {
|
|
488
|
+
const n = this.extractSubstring(e.gaps.value?.start, e.gaps.value?.end), s = this.extractSubstring(e.gaps.nested?.start, e.gaps.nested?.end), r = this.extractSubstring(e.gaps.meta?.start, e.gaps.meta?.end), o = s || void 0, i = e.gaps.meta !== void 0 ? r : void 0, a = n || o || "";
|
|
489
|
+
return {
|
|
490
|
+
type: "mark",
|
|
491
|
+
content: this.input.substring(e.start, e.end),
|
|
492
|
+
children: [],
|
|
493
|
+
// Will be populated if match has nested content
|
|
494
|
+
descriptor: e.descriptor,
|
|
495
|
+
value: a,
|
|
496
|
+
meta: i,
|
|
497
|
+
position: { start: e.start, end: e.end },
|
|
498
|
+
nested: this.createNestedInfo(e, o)
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
// ===== UTILITY METHODS =====
|
|
502
|
+
/**
|
|
503
|
+
* Gets the content boundaries for a match
|
|
504
|
+
* Priority: nested content if present, otherwise value content
|
|
505
|
+
*/
|
|
506
|
+
getContentBounds(e) {
|
|
507
|
+
return e.gaps.nested ? e.gaps.nested : e.gaps.value ? e.gaps.value : {
|
|
508
|
+
start: e.start,
|
|
509
|
+
end: e.start
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Checks if a match has nested content capability
|
|
514
|
+
*/
|
|
515
|
+
hasNestedContent(e) {
|
|
516
|
+
return e.gaps.nested !== void 0;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Extracts substring safely, returns empty string if positions are undefined
|
|
520
|
+
*/
|
|
521
|
+
extractSubstring(e, n) {
|
|
522
|
+
return e !== void 0 && n !== void 0 ? this.input.substring(e, n) : "";
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Creates a text token for a range in the input
|
|
526
|
+
*/
|
|
527
|
+
createTextToken(e, n) {
|
|
528
|
+
return Y(this.input, e, n);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Creates nested info object if nested content exists
|
|
532
|
+
*/
|
|
533
|
+
createNestedInfo(e, n) {
|
|
534
|
+
if (!(!n || e.gaps.nested === void 0))
|
|
535
|
+
return {
|
|
536
|
+
content: n,
|
|
537
|
+
start: e.gaps.nested.start,
|
|
538
|
+
end: e.gaps.nested.end
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function $(t, e) {
|
|
543
|
+
let n = t;
|
|
544
|
+
return e.value !== void 0 && (n = n.replaceAll(y.Value, e.value)), e.meta !== void 0 && (n = n.replaceAll(y.Meta, e.meta)), e.nested !== void 0 && (n = n.replaceAll(y.Nested, e.nested)), n;
|
|
545
|
+
}
|
|
546
|
+
function T(t) {
|
|
547
|
+
let e = "";
|
|
548
|
+
for (const n of t) {
|
|
549
|
+
if (n.type === "text") {
|
|
550
|
+
e += n.content;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const s = n.descriptor.markup, r = s.includes(y.Nested) ? n.children.length > 0 ? T(n.children) : n.nested?.content : void 0;
|
|
554
|
+
e += $(s, {
|
|
555
|
+
value: n.value,
|
|
556
|
+
meta: n.meta,
|
|
557
|
+
nested: r
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
return e;
|
|
561
|
+
}
|
|
562
|
+
function G(t, e) {
|
|
563
|
+
let n = "";
|
|
564
|
+
for (const s of t)
|
|
565
|
+
if (s.type === "text")
|
|
566
|
+
n += s.content;
|
|
567
|
+
else if (s.children.length > 0) {
|
|
568
|
+
const r = G(s.children, e), o = {
|
|
569
|
+
...s,
|
|
570
|
+
value: r
|
|
571
|
+
};
|
|
572
|
+
n += e(o);
|
|
573
|
+
} else
|
|
574
|
+
n += e(s);
|
|
575
|
+
return n;
|
|
576
|
+
}
|
|
577
|
+
class O {
|
|
578
|
+
registry;
|
|
579
|
+
segmentMatcher;
|
|
580
|
+
patternMatcher;
|
|
581
|
+
treeBuilder;
|
|
582
|
+
/**
|
|
583
|
+
* Creates a new Parser instance with the specified markup patterns
|
|
584
|
+
*
|
|
585
|
+
* @param markups - Array of markup pattern strings with placeholders (can include undefined values):
|
|
586
|
+
* - `__value__` - main content (plain text, no nesting)
|
|
587
|
+
* - `__meta__` - metadata (plain text, no nesting)
|
|
588
|
+
* - `__nested__` - content supporting nested structures
|
|
589
|
+
* - `undefined` - skipped, but original array indices are preserved for descriptor matching
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* ```typescript
|
|
593
|
+
* const parser = new Parser([
|
|
594
|
+
* '@[__value__](__meta__)', // @[label](value) - descriptor.index = 0
|
|
595
|
+
* undefined, // skipped
|
|
596
|
+
* '#[__nested__]', // #[nested content] - descriptor.index = 2
|
|
597
|
+
* '**__nested__**' // **bold text** - descriptor.index = 3
|
|
598
|
+
* ])
|
|
599
|
+
* ```
|
|
600
|
+
*/
|
|
601
|
+
constructor(e) {
|
|
602
|
+
this.registry = new Ce(e), this.segmentMatcher = new be(this.registry.segments), this.patternMatcher = new Te(this.registry), this.treeBuilder = new Ie();
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Parses text into tokens (static convenience method)
|
|
606
|
+
*
|
|
607
|
+
* @param value - Text to parse
|
|
608
|
+
* @param options - Options with markup patterns
|
|
609
|
+
* @returns Array of tokens (TextToken and MarkToken)
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* ```typescript
|
|
613
|
+
* const tokens = Parser.parse('Hello @[world]', {
|
|
614
|
+
* markup: ['@[__value__]']
|
|
615
|
+
* })
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
static parse(e, n) {
|
|
619
|
+
const s = n?.markup;
|
|
620
|
+
return !s || s.length === 0 ? [Y(e)] : new O(s).parse(e);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Converts tokens back to text (static convenience method)
|
|
624
|
+
*
|
|
625
|
+
* @param tokens - Array of tokens to convert
|
|
626
|
+
* @returns Reconstructed text string
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* const text = Parser.stringify(tokens)
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
static stringify(e) {
|
|
634
|
+
return T(e);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Parses text into a nested token tree
|
|
638
|
+
*
|
|
639
|
+
* This is the main parsing method. It processes the input text through
|
|
640
|
+
* three stages:
|
|
641
|
+
* 1. Segment matching - finds all markup segments (O(N + M))
|
|
642
|
+
* 2. Pattern matching - builds complete patterns from segments (O(M))
|
|
643
|
+
* 3. Tree building - constructs nested token tree (O(M·D))
|
|
644
|
+
*
|
|
645
|
+
* @param value - Text to parse
|
|
646
|
+
* @returns Array of tokens representing the parsed structure
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* ```typescript
|
|
650
|
+
* const parser = new Parser(['@[__value__](__meta__)'])
|
|
651
|
+
* const tokens = parser.parse('Hello @[world](test)')
|
|
652
|
+
* // Returns: [
|
|
653
|
+
* // TextToken('Hello '),
|
|
654
|
+
* // MarkToken('@[world](test)', value='world', meta='test'),
|
|
655
|
+
* // TextToken('')
|
|
656
|
+
* // ]
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
659
|
+
parse(e) {
|
|
660
|
+
const n = this.segmentMatcher.search(e), s = this.patternMatcher.process(n);
|
|
661
|
+
return this.treeBuilder.build(s, e);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Converts tokens back to the original text
|
|
665
|
+
*
|
|
666
|
+
* This is the inverse operation of parse(). It reconstructs the original
|
|
667
|
+
* text from a token tree, preserving all markup and structure.
|
|
668
|
+
*
|
|
669
|
+
* @param tokens - Array of tokens to convert
|
|
670
|
+
* @returns Reconstructed text string
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```typescript
|
|
674
|
+
* const text = 'Hello @[world](test)'
|
|
675
|
+
* const tokens = parser.parse(text)
|
|
676
|
+
* const reconstructed = parser.stringify(tokens)
|
|
677
|
+
* console.log(reconstructed === text) // true
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
stringify(e) {
|
|
681
|
+
return T(e);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Transforms annotated text by processing all mark tokens with a callback
|
|
685
|
+
*
|
|
686
|
+
* This method parses the text, recursively processes all MarkTokens
|
|
687
|
+
* (including nested ones) with the provided callback, and returns
|
|
688
|
+
* the transformed text.
|
|
689
|
+
*
|
|
690
|
+
* @param value - Annotated text to process
|
|
691
|
+
* @param callback - Function to transform each MarkToken
|
|
692
|
+
* @returns Transformed text
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* // Extract all values
|
|
697
|
+
* const text = '@[Hello](world) and #[tag]'
|
|
698
|
+
* const result = parser.transform(text, mark => mark.value)
|
|
699
|
+
* // Returns: 'Hello and tag'
|
|
700
|
+
*
|
|
701
|
+
* // Custom transformation
|
|
702
|
+
* const result = parser.transform(text, mark =>
|
|
703
|
+
* mark.meta ? `${mark.value}:${mark.meta}` : mark.value
|
|
704
|
+
* )
|
|
705
|
+
* // Returns: 'Hello:world and tag'
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
transform(e, n) {
|
|
709
|
+
const s = this.parse(e);
|
|
710
|
+
return G(s, n);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Escapes markup segments in the given text using backslash
|
|
714
|
+
*
|
|
715
|
+
* This method uses the registry's unique segments and escapes them by adding
|
|
716
|
+
* a backslash before each character of each segment, preventing them from being
|
|
717
|
+
* parsed as markup when the text is processed again.
|
|
718
|
+
*
|
|
719
|
+
* @param text - Text to escape segments in
|
|
720
|
+
* @returns Text with escaped segments
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* ```typescript
|
|
724
|
+
* const parser = new Parser(['**__nested__**', '@[__value__]'])
|
|
725
|
+
* const escaped = parser.escape('Hello **world** and @[user]')
|
|
726
|
+
* // Returns: 'Hello \*\*world\*\* and \@[user]'
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
escape(e) {
|
|
730
|
+
return this.registry.segments.filter((n) => typeof n == "string").sort((n, s) => s.length - n.length).reduce((n, s) => n.replaceAll(s, s.replace(/(.)/g, "\\$1")), e);
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Unescapes markup patterns in the given text
|
|
734
|
+
*
|
|
735
|
+
* This method removes escape characters from segments that were previously
|
|
736
|
+
* escaped using escape(), allowing the patterns to be parsed normally.
|
|
737
|
+
*
|
|
738
|
+
* @param text - Text to unescape patterns in
|
|
739
|
+
* @returns Text with unescaped patterns
|
|
740
|
+
*
|
|
741
|
+
* @example
|
|
742
|
+
* ```typescript
|
|
743
|
+
* const parser = new Parser(['**__nested__**', '@[__value__]'])
|
|
744
|
+
* const unescaped = parser.unescape('Hello \*\*world\*\* and \@[user]')
|
|
745
|
+
* // Returns: 'Hello **world** and @[user]'
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
unescape(e) {
|
|
749
|
+
return e.replaceAll(/\\(.)/g, "$1");
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function kt(t, e, n) {
|
|
753
|
+
if (!n.length) return t;
|
|
754
|
+
const s = new O(n).parse(t);
|
|
755
|
+
return G(s, e);
|
|
756
|
+
}
|
|
757
|
+
function Me(t = "", e = "") {
|
|
758
|
+
if (t === e) return {};
|
|
759
|
+
let n;
|
|
760
|
+
for (let r = 0; r < t.length; r++)
|
|
761
|
+
if (t[r] !== e[r]) {
|
|
762
|
+
n = r;
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
let s;
|
|
766
|
+
for (let r = 1; r <= t.length; r++)
|
|
767
|
+
if (t.at(-r) !== e.at(-r)) {
|
|
768
|
+
s = t.length - r + 1;
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
return { left: n, right: s };
|
|
772
|
+
}
|
|
773
|
+
function q(t, e) {
|
|
774
|
+
let n = -1, s = t.length;
|
|
775
|
+
for (; s - n > 1; ) {
|
|
776
|
+
const r = Math.round((n + s) / 2);
|
|
777
|
+
t[r] <= e ? n = r : s = r;
|
|
778
|
+
}
|
|
779
|
+
return t[n] == e && (s = n), [n, s].filter((r) => t[r] !== void 0);
|
|
780
|
+
}
|
|
781
|
+
function Pe(t, e) {
|
|
782
|
+
if (Object.is(t, e))
|
|
783
|
+
return !0;
|
|
784
|
+
if (typeof t != "object" || t === null || typeof e != "object" || e === null)
|
|
785
|
+
return !1;
|
|
786
|
+
const n = Object.keys(t);
|
|
787
|
+
if (n.length !== Object.keys(e).length)
|
|
788
|
+
return !1;
|
|
789
|
+
for (let s = 0; s < n.length; s++)
|
|
790
|
+
if (!Object.prototype.hasOwnProperty.call(e, n[s]) || !Object.is(t[n[s]], e[n[s]]))
|
|
791
|
+
return !1;
|
|
792
|
+
return !0;
|
|
793
|
+
}
|
|
794
|
+
function Ne(t, e, n, s) {
|
|
795
|
+
return t.slice(0, n) + e + t.slice(n + s.length);
|
|
796
|
+
}
|
|
797
|
+
function _(t, e) {
|
|
798
|
+
const s = {
|
|
799
|
+
prev: 2,
|
|
800
|
+
self: 1,
|
|
801
|
+
next: 0
|
|
802
|
+
}[t], { focus: r } = e.nodes, [o, , i] = e.tokens.splice(r.index - s, 3), a = (o.type === "text", o.content), c = (i.type === "text", i.content);
|
|
803
|
+
e.tokens = e.tokens.toSpliced(r.index - s, 0, {
|
|
804
|
+
type: "text",
|
|
805
|
+
content: a + c,
|
|
806
|
+
position: {
|
|
807
|
+
start: o.position.start,
|
|
808
|
+
end: i.position.end
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
let l = r;
|
|
812
|
+
for (let u = 0; u < s; u++)
|
|
813
|
+
l = l.prev;
|
|
814
|
+
const h = l.length;
|
|
815
|
+
e.recovery = { anchor: l.prev, caret: h }, e.props.onChange?.(T(e.tokens));
|
|
816
|
+
}
|
|
817
|
+
class Ae {
|
|
818
|
+
#t = /* @__PURE__ */ new Map();
|
|
819
|
+
send(e, n) {
|
|
820
|
+
this.#e(e).forEach((s) => s(n));
|
|
821
|
+
}
|
|
822
|
+
on(e, n) {
|
|
823
|
+
return this.#e(e).add(n), () => this.#e(e).delete(n);
|
|
824
|
+
}
|
|
825
|
+
#e(e) {
|
|
826
|
+
return this.#t.has(e) || this.#t.set(e, /* @__PURE__ */ new Set()), this.#t.get(e);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const p = {
|
|
830
|
+
STORE_UPDATED: Symbol(),
|
|
831
|
+
ClearTrigger: Symbol(),
|
|
832
|
+
CheckTrigger: Symbol(),
|
|
833
|
+
Change: Symbol(),
|
|
834
|
+
Parse: Symbol(),
|
|
835
|
+
Delete: Symbol(),
|
|
836
|
+
Select: Symbol()
|
|
837
|
+
};
|
|
838
|
+
class S {
|
|
839
|
+
static get isSelectedPosition() {
|
|
840
|
+
const e = window.getSelection();
|
|
841
|
+
if (e)
|
|
842
|
+
return e.isCollapsed;
|
|
843
|
+
}
|
|
844
|
+
static getCurrentPosition() {
|
|
845
|
+
return window.getSelection()?.anchorOffset ?? 0;
|
|
846
|
+
}
|
|
847
|
+
//TODO get span from state?
|
|
848
|
+
static getFocusedSpan() {
|
|
849
|
+
return window.getSelection()?.anchorNode?.textContent ?? "";
|
|
850
|
+
}
|
|
851
|
+
static getSelectedNode() {
|
|
852
|
+
const e = window.getSelection()?.anchorNode;
|
|
853
|
+
if (e) return e;
|
|
854
|
+
throw new Error("Anchor node of selection is not exists!");
|
|
855
|
+
}
|
|
856
|
+
//TODO add the returned type: "{left: CSSProperties["left"], top: CSSProperties["top"]}"?
|
|
857
|
+
static getAbsolutePosition() {
|
|
858
|
+
const e = window.getSelection()?.getRangeAt(0).getBoundingClientRect?.();
|
|
859
|
+
return e ? { left: e.left, top: e.top + e.height + 1 } : { left: 0, top: 0 };
|
|
860
|
+
}
|
|
861
|
+
static trySetIndex(e, n) {
|
|
862
|
+
try {
|
|
863
|
+
this.setIndex(e, n);
|
|
864
|
+
} catch (s) {
|
|
865
|
+
console.error(s);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
static setIndex(e, n) {
|
|
869
|
+
const s = window.getSelection();
|
|
870
|
+
if (!s?.anchorNode || !s.rangeCount) return;
|
|
871
|
+
const r = s.getRangeAt(0);
|
|
872
|
+
r?.setStart(e.firstChild || e, n), r?.setEnd(e.firstChild || e, n);
|
|
873
|
+
}
|
|
874
|
+
static getCaretIndex(e) {
|
|
875
|
+
let n = 0;
|
|
876
|
+
const s = window.getSelection();
|
|
877
|
+
if (!s?.rangeCount) return n;
|
|
878
|
+
const r = s.getRangeAt(0), o = r.cloneRange();
|
|
879
|
+
return o.selectNodeContents(e), o.setEnd(r.endContainer, r.endOffset), n = o.toString().length, n;
|
|
880
|
+
}
|
|
881
|
+
static setCaretToEnd(e) {
|
|
882
|
+
if (!e) return;
|
|
883
|
+
window.getSelection()?.setPosition(e, 1);
|
|
884
|
+
}
|
|
885
|
+
static getIndex() {
|
|
886
|
+
return window.getSelection()?.anchorOffset ?? NaN;
|
|
887
|
+
}
|
|
888
|
+
static setIndex1(e) {
|
|
889
|
+
const n = window.getSelection();
|
|
890
|
+
if (!n?.anchorNode || !n.rangeCount) return;
|
|
891
|
+
const s = n.getRangeAt(0);
|
|
892
|
+
s?.setStart(s.startContainer.firstChild || s.startContainer, e), s?.setEnd(s.startContainer.firstChild || s.startContainer, e);
|
|
893
|
+
}
|
|
894
|
+
setCaretRightTo(e, n) {
|
|
895
|
+
const r = window.getSelection()?.getRangeAt(0);
|
|
896
|
+
r?.setStart(r.endContainer, n), r?.setEnd(r.endContainer, n);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const Oe = new RegExp(/^\w*/);
|
|
900
|
+
class U {
|
|
901
|
+
span;
|
|
902
|
+
node;
|
|
903
|
+
dividedText;
|
|
904
|
+
constructor() {
|
|
905
|
+
const e = S.getCurrentPosition();
|
|
906
|
+
this.node = S.getSelectedNode(), this.span = S.getFocusedSpan(), this.dividedText = this.getDividedTextBy(e);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Find overlay match in text using provided options and trigger extractor.
|
|
910
|
+
* @template T - Type of option objects
|
|
911
|
+
* @param options - Array of options to search through
|
|
912
|
+
* @param getTrigger - Function that extracts trigger from each option
|
|
913
|
+
* @returns OverlayMatch with correct option type or undefined
|
|
914
|
+
*
|
|
915
|
+
* @example
|
|
916
|
+
* // React usage
|
|
917
|
+
* TriggerFinder.find(options, (opt) => opt.slotProps?.overlay?.trigger ?? '@')
|
|
918
|
+
*
|
|
919
|
+
* @example
|
|
920
|
+
* // Other framework usage
|
|
921
|
+
* TriggerFinder.find(vueOptions, (opt) => opt.overlay?.trigger ?? '@')
|
|
922
|
+
*/
|
|
923
|
+
static find(e, n) {
|
|
924
|
+
if (e && S.isSelectedPosition)
|
|
925
|
+
return new U().find(e, n);
|
|
926
|
+
}
|
|
927
|
+
getDividedTextBy(e) {
|
|
928
|
+
return { left: this.span.slice(0, e), right: this.span.slice(e) };
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Find overlay match in provided options.
|
|
932
|
+
* @template T - Type of option objects
|
|
933
|
+
* @param options - Array of options
|
|
934
|
+
* @param getTrigger - Function to extract trigger from each option
|
|
935
|
+
*/
|
|
936
|
+
find(e, n) {
|
|
937
|
+
for (let s = 0; s < e.length; s++) {
|
|
938
|
+
const r = e[s], o = n(r, s);
|
|
939
|
+
if (!o) continue;
|
|
940
|
+
const i = this.matchInTextVia(o);
|
|
941
|
+
if (i)
|
|
942
|
+
return {
|
|
943
|
+
value: i.word,
|
|
944
|
+
source: i.annotation,
|
|
945
|
+
index: i.index,
|
|
946
|
+
span: this.span,
|
|
947
|
+
node: this.node,
|
|
948
|
+
option: r
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
matchInTextVia(e = "@") {
|
|
953
|
+
const n = this.matchRightPart(), s = this.matchLeftPart(e);
|
|
954
|
+
if (s)
|
|
955
|
+
return {
|
|
956
|
+
word: s.word + n.word,
|
|
957
|
+
annotation: s.annotation + n.word,
|
|
958
|
+
index: s.index
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
matchRightPart() {
|
|
962
|
+
const { right: e } = this.dividedText;
|
|
963
|
+
return { word: e.match(Oe)?.[0] };
|
|
964
|
+
}
|
|
965
|
+
matchLeftPart(e) {
|
|
966
|
+
const n = this.makeTriggerRegex(e), { left: s } = this.dividedText, r = s.match(n);
|
|
967
|
+
if (!r) return;
|
|
968
|
+
const [o, i] = r;
|
|
969
|
+
return { word: i, annotation: o, index: r.index ?? 0 };
|
|
970
|
+
}
|
|
971
|
+
//TODO new overlayMatch option if (isSpaceBeforeRequired) append space check for not first words '\\s'
|
|
972
|
+
makeTriggerRegex(e) {
|
|
973
|
+
const n = b(e) + "(\\w*)$";
|
|
974
|
+
return new RegExp(n);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
class M {
|
|
978
|
+
#t;
|
|
979
|
+
#e;
|
|
980
|
+
get target() {
|
|
981
|
+
return this.#t;
|
|
982
|
+
}
|
|
983
|
+
set target(e) {
|
|
984
|
+
this.#t = e;
|
|
985
|
+
}
|
|
986
|
+
get next() {
|
|
987
|
+
return new M(this.target?.nextSibling, this.#e);
|
|
988
|
+
}
|
|
989
|
+
get prev() {
|
|
990
|
+
return new M(this.target?.previousSibling, this.#e);
|
|
991
|
+
}
|
|
992
|
+
get isSpan() {
|
|
993
|
+
return this.index % 2 === 0;
|
|
994
|
+
}
|
|
995
|
+
get isMark() {
|
|
996
|
+
return !this.isSpan;
|
|
997
|
+
}
|
|
998
|
+
get isEditable() {
|
|
999
|
+
return this.target?.isContentEditable ?? !1;
|
|
1000
|
+
}
|
|
1001
|
+
get isCaretAtBeginning() {
|
|
1002
|
+
return this.target ? S.getCaretIndex(this.target) === 0 : void 0;
|
|
1003
|
+
}
|
|
1004
|
+
get isCaretAtEnd() {
|
|
1005
|
+
return this.target ? S.getCaretIndex(this.target) === this.target.textContent?.length : void 0;
|
|
1006
|
+
}
|
|
1007
|
+
get index() {
|
|
1008
|
+
return this.target?.parentElement ? [...this.target.parentElement.children].indexOf(this.target) : -1;
|
|
1009
|
+
}
|
|
1010
|
+
get caret() {
|
|
1011
|
+
return this.target ? S.getCaretIndex(this.target) : -1;
|
|
1012
|
+
}
|
|
1013
|
+
set caret(e) {
|
|
1014
|
+
this.target && S.trySetIndex(this.target, e);
|
|
1015
|
+
}
|
|
1016
|
+
get length() {
|
|
1017
|
+
return this.target?.textContent?.length ?? -1;
|
|
1018
|
+
}
|
|
1019
|
+
get content() {
|
|
1020
|
+
return this.target?.textContent ?? "";
|
|
1021
|
+
}
|
|
1022
|
+
set content(e) {
|
|
1023
|
+
this.target && (this.target.textContent = e ?? "");
|
|
1024
|
+
}
|
|
1025
|
+
get head() {
|
|
1026
|
+
return this.#e.refs.container?.firstChild;
|
|
1027
|
+
}
|
|
1028
|
+
get tail() {
|
|
1029
|
+
return this.#e.refs.container?.lastChild;
|
|
1030
|
+
}
|
|
1031
|
+
get isFocused() {
|
|
1032
|
+
return this.target === document.activeElement;
|
|
1033
|
+
}
|
|
1034
|
+
constructor(e, n) {
|
|
1035
|
+
this.target = e, this.#e = n;
|
|
1036
|
+
}
|
|
1037
|
+
setCaretToEnd() {
|
|
1038
|
+
S.setCaretToEnd(this.target);
|
|
1039
|
+
}
|
|
1040
|
+
focus() {
|
|
1041
|
+
this.target?.focus();
|
|
1042
|
+
}
|
|
1043
|
+
clear() {
|
|
1044
|
+
this.target = void 0;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
class Le {
|
|
1048
|
+
#t = 1;
|
|
1049
|
+
#e = /* @__PURE__ */ new WeakMap();
|
|
1050
|
+
get(e) {
|
|
1051
|
+
return this.#e.has(e) ? this.#e.get(e) : (this.#e.set(e, this.#t), this.#t++);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
class B {
|
|
1055
|
+
// Utils domain
|
|
1056
|
+
bus = new Ae();
|
|
1057
|
+
key = new Le();
|
|
1058
|
+
// Config domain
|
|
1059
|
+
props;
|
|
1060
|
+
// Document domain
|
|
1061
|
+
tokens = [];
|
|
1062
|
+
parser;
|
|
1063
|
+
previousValue;
|
|
1064
|
+
// Navigation domain
|
|
1065
|
+
nodes = {
|
|
1066
|
+
focus: new M(void 0, this),
|
|
1067
|
+
input: new M(void 0, this)
|
|
1068
|
+
};
|
|
1069
|
+
recovery;
|
|
1070
|
+
// UI domain
|
|
1071
|
+
refs = {
|
|
1072
|
+
container: null,
|
|
1073
|
+
overlay: null,
|
|
1074
|
+
setContainer: (e) => {
|
|
1075
|
+
this.refs.container = e;
|
|
1076
|
+
},
|
|
1077
|
+
setOverlay: (e) => {
|
|
1078
|
+
this.refs.overlay = e;
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
selecting;
|
|
1082
|
+
// Overlay domain
|
|
1083
|
+
overlayMatch;
|
|
1084
|
+
constructor(e) {
|
|
1085
|
+
this.props = e;
|
|
1086
|
+
}
|
|
1087
|
+
static create = (e) => new Proxy(new B(e), { set: De });
|
|
1088
|
+
}
|
|
1089
|
+
const _e = /* @__PURE__ */ new Set(["bus", "refs", "nodes", "key"]);
|
|
1090
|
+
function De(t, e, n, s) {
|
|
1091
|
+
return _e.has(String(e)) ? !1 : (t[e] === n || (t[e] = n, t.bus.send(p.STORE_UPDATED, s)), !0);
|
|
1092
|
+
}
|
|
1093
|
+
const P = ce(void 0);
|
|
1094
|
+
P.displayName = "StoreContext";
|
|
1095
|
+
function v(t, e, n) {
|
|
1096
|
+
typeof t == "string" ? $e(t, e, n) : Re(t, e, n);
|
|
1097
|
+
}
|
|
1098
|
+
function Re(t, e, n) {
|
|
1099
|
+
const s = N(P);
|
|
1100
|
+
Q(s), x(() => s.bus.on(t, e), n);
|
|
1101
|
+
}
|
|
1102
|
+
function $e(t, e, n) {
|
|
1103
|
+
const s = N(P);
|
|
1104
|
+
x(() => (s?.refs.container?.addEventListener(t, e), () => s?.refs.container?.removeEventListener(t, e)), n);
|
|
1105
|
+
}
|
|
1106
|
+
function f(t, e) {
|
|
1107
|
+
const n = N(P);
|
|
1108
|
+
Q(n);
|
|
1109
|
+
const [s, r] = A(() => t?.(n));
|
|
1110
|
+
return v(
|
|
1111
|
+
p.STORE_UPDATED,
|
|
1112
|
+
() => {
|
|
1113
|
+
r((o) => {
|
|
1114
|
+
const i = t?.(n);
|
|
1115
|
+
return e && Pe(o, i) ? o : i;
|
|
1116
|
+
});
|
|
1117
|
+
},
|
|
1118
|
+
[]
|
|
1119
|
+
), t ? s : n;
|
|
1120
|
+
}
|
|
1121
|
+
const Ve = (t) => {
|
|
1122
|
+
const e = ae.createContext(void 0);
|
|
1123
|
+
e.displayName = t;
|
|
1124
|
+
const n = o(e), s = e.Provider;
|
|
1125
|
+
return [n, s, e];
|
|
1126
|
+
function o(i) {
|
|
1127
|
+
return () => {
|
|
1128
|
+
const a = N(i);
|
|
1129
|
+
if (a) return a;
|
|
1130
|
+
throw new Error(`The context ${i.displayName} didn't found!`);
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
}, [H, X] = Ve("NodeProvider");
|
|
1134
|
+
function ee(t, e, n, s) {
|
|
1135
|
+
const r = f((c) => t === "mark" ? c.props.Mark : c.props.Overlay), o = t === "mark" ? e?.mark : e?.overlay;
|
|
1136
|
+
let i;
|
|
1137
|
+
o !== void 0 ? typeof o == "function" ? i = o(n) : i = o : i = n ?? {};
|
|
1138
|
+
const a = i.slot || r || s;
|
|
1139
|
+
if (!a)
|
|
1140
|
+
throw new Error(
|
|
1141
|
+
`No ${t} component found. Provide either option.${t}.slot, global ${t === "mark" ? "Mark" : "Overlay"}, or a defaultComponent.`
|
|
1142
|
+
);
|
|
1143
|
+
return [a, i];
|
|
1144
|
+
}
|
|
1145
|
+
function Fe() {
|
|
1146
|
+
const t = H(), { options: e, key: n } = f((c) => ({ options: c.props.options, key: c.key }), !0), s = e?.[t.descriptor.index], r = t.children.map((c) => /* @__PURE__ */ m(z, { mark: c, isNested: !0 }, n.get(c))), o = {
|
|
1147
|
+
value: t.value,
|
|
1148
|
+
meta: t.meta,
|
|
1149
|
+
nested: t.nested?.content,
|
|
1150
|
+
children: t.children.length > 0 ? r : void 0
|
|
1151
|
+
}, [i, a] = ee("mark", s, o);
|
|
1152
|
+
return /* @__PURE__ */ m(i, { ...a });
|
|
1153
|
+
}
|
|
1154
|
+
const We = () => {
|
|
1155
|
+
const t = H(), e = I(null), { readOnly: n, SpanComponent: s, spanProps: r } = f(
|
|
1156
|
+
(o) => ({
|
|
1157
|
+
readOnly: o.props.readOnly,
|
|
1158
|
+
SpanComponent: Z("span", o),
|
|
1159
|
+
spanProps: J("span", o)
|
|
1160
|
+
}),
|
|
1161
|
+
!0
|
|
1162
|
+
);
|
|
1163
|
+
if (t.type !== "text")
|
|
1164
|
+
throw new Error("TextSpan component expects a TextToken");
|
|
1165
|
+
return /* @__PURE__ */ m(
|
|
1166
|
+
s,
|
|
1167
|
+
{
|
|
1168
|
+
...r,
|
|
1169
|
+
ref: e,
|
|
1170
|
+
contentEditable: !n,
|
|
1171
|
+
onPaste: Ge,
|
|
1172
|
+
suppressContentEditableWarning: !0,
|
|
1173
|
+
children: t.content
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1176
|
+
};
|
|
1177
|
+
function Ge(t) {
|
|
1178
|
+
t.preventDefault();
|
|
1179
|
+
const e = t.clipboardData.getData("text");
|
|
1180
|
+
document.execCommand("insertText", !1, e);
|
|
1181
|
+
}
|
|
1182
|
+
const z = W(({ mark: t, isNested: e = !1 }) => t.type === "mark" ? /* @__PURE__ */ m(X, { value: t, children: /* @__PURE__ */ m(Fe, {}) }) : e ? /* @__PURE__ */ m(oe, { children: t.content }) : /* @__PURE__ */ m(X, { value: t, children: /* @__PURE__ */ m(We, {}) }));
|
|
1183
|
+
z.displayName = "Token";
|
|
1184
|
+
const te = W(() => {
|
|
1185
|
+
const { className: t, style: e, tokens: n, bus: s, key: r, ContainerComponent: o, containerProps: i, refs: a } = f(
|
|
1186
|
+
(c) => ({
|
|
1187
|
+
className: c.props.className,
|
|
1188
|
+
style: c.props.style,
|
|
1189
|
+
tokens: c.tokens,
|
|
1190
|
+
bus: c.bus,
|
|
1191
|
+
key: c.key,
|
|
1192
|
+
refs: c.refs,
|
|
1193
|
+
ContainerComponent: Z("container", c),
|
|
1194
|
+
containerProps: J("container", c)
|
|
1195
|
+
}),
|
|
1196
|
+
!0
|
|
1197
|
+
);
|
|
1198
|
+
return v("input", () => s.send(p.Change), []), /* @__PURE__ */ m(o, { ref: a.setContainer, ...i, className: t, style: e, children: n.map((c) => /* @__PURE__ */ m(z, { mark: c }, r.get(c))) });
|
|
1199
|
+
});
|
|
1200
|
+
te.displayName = "Container";
|
|
1201
|
+
function Ue() {
|
|
1202
|
+
const { match: t, bus: e } = f(
|
|
1203
|
+
(n) => ({
|
|
1204
|
+
match: n.overlayMatch,
|
|
1205
|
+
bus: n.bus
|
|
1206
|
+
}),
|
|
1207
|
+
!0
|
|
1208
|
+
);
|
|
1209
|
+
x(() => {
|
|
1210
|
+
if (!t) return;
|
|
1211
|
+
const n = (s) => {
|
|
1212
|
+
s.key === k.ESC && e.send(p.ClearTrigger);
|
|
1213
|
+
};
|
|
1214
|
+
return window.addEventListener("keydown", n), () => window.removeEventListener("keydown", n);
|
|
1215
|
+
}, [t, e]);
|
|
1216
|
+
}
|
|
1217
|
+
function Be() {
|
|
1218
|
+
const t = f(), e = f((n) => n.overlayMatch);
|
|
1219
|
+
x(() => {
|
|
1220
|
+
if (!e) return;
|
|
1221
|
+
const n = (s) => {
|
|
1222
|
+
const r = s.target;
|
|
1223
|
+
t.refs.overlay?.contains(r) || t.refs.container?.contains(r) || t.bus.send(p.ClearTrigger);
|
|
1224
|
+
};
|
|
1225
|
+
return document.addEventListener("click", n), () => document.removeEventListener("click", n);
|
|
1226
|
+
}, [e]);
|
|
1227
|
+
}
|
|
1228
|
+
function C(t, e, n = []) {
|
|
1229
|
+
v(
|
|
1230
|
+
"keydown",
|
|
1231
|
+
(s) => {
|
|
1232
|
+
s.key === t && e(s);
|
|
1233
|
+
},
|
|
1234
|
+
n
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
function He() {
|
|
1238
|
+
const t = f();
|
|
1239
|
+
C(k.LEFT, e), C(k.RIGHT, n), C(k.DELETE, s), C(k.BACKSPACE, s), C(k.DELETE, o), C(k.BACKSPACE, r), v("keydown", i, []), v("paste", l, []), x(() => {
|
|
1240
|
+
const u = t.refs.container;
|
|
1241
|
+
if (!u) return;
|
|
1242
|
+
const d = (g) => {
|
|
1243
|
+
c(g);
|
|
1244
|
+
};
|
|
1245
|
+
return u.addEventListener("beforeinput", d, !0), () => u.removeEventListener("beforeinput", d, !0);
|
|
1246
|
+
}, []);
|
|
1247
|
+
function e(u) {
|
|
1248
|
+
const { focus: d } = t.nodes;
|
|
1249
|
+
if (d.isMark && !d.isEditable || d.isCaretAtBeginning) {
|
|
1250
|
+
const g = d.prev;
|
|
1251
|
+
g.focus(), g.isFocused || (g.prev.focus(), u.preventDefault()), d.setCaretToEnd();
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
function n(u) {
|
|
1255
|
+
const { focus: d } = t.nodes;
|
|
1256
|
+
if (d.isMark && !d.isEditable || d.isCaretAtEnd) {
|
|
1257
|
+
const g = d.next;
|
|
1258
|
+
g.focus(), g.isFocused || (g.next.focus(), u.preventDefault());
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
function s() {
|
|
1262
|
+
t.nodes.focus.isMark && _("self", t);
|
|
1263
|
+
}
|
|
1264
|
+
function r(u) {
|
|
1265
|
+
t.nodes.focus.isSpan && t.nodes.focus.isCaretAtBeginning && t.nodes.focus.prev.target && (u.preventDefault(), _("prev", t));
|
|
1266
|
+
}
|
|
1267
|
+
function o(u) {
|
|
1268
|
+
t.nodes.focus.isSpan && t.nodes.focus.isCaretAtEnd && t.nodes.focus.next.target && (u.preventDefault(), _("next", t));
|
|
1269
|
+
}
|
|
1270
|
+
function i(u) {
|
|
1271
|
+
if ((u.ctrlKey || u.metaKey) && u.code === "KeyA") {
|
|
1272
|
+
u.preventDefault();
|
|
1273
|
+
const d = window.getSelection(), g = t.refs.container?.firstChild, E = t.refs.container?.lastChild;
|
|
1274
|
+
if (!d || !g || !E) return;
|
|
1275
|
+
d.setBaseAndExtent(g, 0, E, 1), t.selecting = "all";
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function a() {
|
|
1279
|
+
const u = window.getSelection(), d = t.refs.container;
|
|
1280
|
+
if (!u?.rangeCount || !d?.firstChild || !d?.lastChild) return !1;
|
|
1281
|
+
try {
|
|
1282
|
+
const g = u.getRangeAt(0);
|
|
1283
|
+
return d.contains(g.startContainer) && d.contains(g.endContainer) && g.toString().length > 0;
|
|
1284
|
+
} catch {
|
|
1285
|
+
return !1;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
function c(u) {
|
|
1289
|
+
if (t.selecting !== "all" || !a()) {
|
|
1290
|
+
t.selecting === "all" && (t.selecting = void 0);
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
if (u.inputType === "insertFromPaste") return;
|
|
1294
|
+
u.preventDefault();
|
|
1295
|
+
const d = u.inputType.startsWith("delete") ? "" : u.data ?? "";
|
|
1296
|
+
h(d);
|
|
1297
|
+
}
|
|
1298
|
+
function l(u) {
|
|
1299
|
+
if (t.selecting !== "all" || !a()) {
|
|
1300
|
+
t.selecting === "all" && (t.selecting = void 0);
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
u.preventDefault();
|
|
1304
|
+
const d = u.clipboardData?.getData("text/plain") ?? "";
|
|
1305
|
+
h(d);
|
|
1306
|
+
}
|
|
1307
|
+
function h(u) {
|
|
1308
|
+
t.nodes.focus.target = null, t.selecting = void 0, t.props.onChange?.(u), t.props.value === void 0 && (t.tokens = t.parser?.parse(u) ?? [
|
|
1309
|
+
{
|
|
1310
|
+
type: "text",
|
|
1311
|
+
content: u,
|
|
1312
|
+
position: { start: 0, end: u.length }
|
|
1313
|
+
}
|
|
1314
|
+
]), queueMicrotask(() => {
|
|
1315
|
+
const d = t.refs.container?.firstChild;
|
|
1316
|
+
d && (t.recovery = {
|
|
1317
|
+
anchor: t.nodes.focus,
|
|
1318
|
+
caret: u.length
|
|
1319
|
+
}, d.focus());
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
function ze() {
|
|
1324
|
+
const t = f();
|
|
1325
|
+
v(
|
|
1326
|
+
p.Change,
|
|
1327
|
+
() => {
|
|
1328
|
+
const { onChange: e } = t.props;
|
|
1329
|
+
if (!t.nodes.focus.target) return;
|
|
1330
|
+
const n = t.tokens[t.nodes.focus.index];
|
|
1331
|
+
n.type === "text" ? n.content = t.nodes.focus.content : n.type === "mark" && (n.value = t.nodes.focus.content), e?.(T(t.tokens)), t.bus.send(p.Parse);
|
|
1332
|
+
},
|
|
1333
|
+
[]
|
|
1334
|
+
), v(
|
|
1335
|
+
p.Delete,
|
|
1336
|
+
({ token: e }) => {
|
|
1337
|
+
const { onChange: n } = t.props;
|
|
1338
|
+
t.tokens.splice(t.tokens.indexOf(e), 1), n?.(T(t.tokens));
|
|
1339
|
+
},
|
|
1340
|
+
[]
|
|
1341
|
+
), v(
|
|
1342
|
+
p.Select,
|
|
1343
|
+
(e) => {
|
|
1344
|
+
const { Mark: n, onChange: s } = t.props, {
|
|
1345
|
+
mark: r,
|
|
1346
|
+
match: { option: o, span: i, index: a, source: c }
|
|
1347
|
+
} = e, l = r.type === "mark" ? $(o.markup, {
|
|
1348
|
+
value: r.value,
|
|
1349
|
+
meta: r.meta
|
|
1350
|
+
}) : $(o.markup, {
|
|
1351
|
+
value: r.content
|
|
1352
|
+
}), h = Ne(i, l, a, c);
|
|
1353
|
+
if (t.recovery = n ? { caret: 0, anchor: t.nodes.input.next, isNext: !0 } : { caret: a + l.length, anchor: t.nodes.input }, t.nodes.input.target) {
|
|
1354
|
+
t.nodes.input.content = h;
|
|
1355
|
+
const u = t.tokens[t.nodes.input.index];
|
|
1356
|
+
u.type === "text" && (u.content = h), t.nodes.focus.target = t.nodes.input.target, t.nodes.input.clear(), s?.(T(t.tokens)), t.bus.send(p.Parse);
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
[]
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
const je = () => {
|
|
1363
|
+
const t = f();
|
|
1364
|
+
v("focusin", (e) => t.nodes.focus.target = e.target, []), v("focusout", (e) => t.nodes.focus.target = void 0, []);
|
|
1365
|
+
}, qe = () => {
|
|
1366
|
+
const t = f(), e = f((n) => n.tokens);
|
|
1367
|
+
v(
|
|
1368
|
+
"click",
|
|
1369
|
+
() => {
|
|
1370
|
+
e.length === 1 && e[0].type === "text" && e[0].content === "" && t.refs.container?.firstElementChild?.focus();
|
|
1371
|
+
},
|
|
1372
|
+
[e]
|
|
1373
|
+
);
|
|
1374
|
+
}, Xe = () => {
|
|
1375
|
+
const t = f(), e = f((s) => s.tokens), n = t.props.Mark ? [e] : void 0;
|
|
1376
|
+
x(() => {
|
|
1377
|
+
if (!t.recovery) return;
|
|
1378
|
+
const { anchor: s, caret: r, isNext: o } = t.recovery;
|
|
1379
|
+
switch (!0) {
|
|
1380
|
+
case (o && !s.target):
|
|
1381
|
+
t.nodes.focus.tail.focus();
|
|
1382
|
+
break;
|
|
1383
|
+
case o:
|
|
1384
|
+
s.prev.focus();
|
|
1385
|
+
break;
|
|
1386
|
+
case !s.target:
|
|
1387
|
+
t.nodes.focus.head.focus();
|
|
1388
|
+
break;
|
|
1389
|
+
default:
|
|
1390
|
+
s.next.focus();
|
|
1391
|
+
}
|
|
1392
|
+
t.nodes.focus.caret = r, t.recovery = void 0;
|
|
1393
|
+
}, n);
|
|
1394
|
+
};
|
|
1395
|
+
function Ze() {
|
|
1396
|
+
const t = f(), e = I(!1), n = I(null);
|
|
1397
|
+
x(() => {
|
|
1398
|
+
const s = (r) => {
|
|
1399
|
+
n.current = r.target, e.current = !0;
|
|
1400
|
+
};
|
|
1401
|
+
return document.addEventListener("mousedown", s), () => document.removeEventListener("mousedown", s);
|
|
1402
|
+
}, []), x(() => {
|
|
1403
|
+
const s = (r) => {
|
|
1404
|
+
const o = e.current, i = !t.refs.container?.contains(n.current) || n.current !== r.target, a = window.getSelection()?.containsNode(t.refs.container, !0);
|
|
1405
|
+
o && i && a && (t.selecting = "drag");
|
|
1406
|
+
};
|
|
1407
|
+
return document.addEventListener("mousemove", s), () => document.removeEventListener("mousemove", s);
|
|
1408
|
+
}, []), x(() => {
|
|
1409
|
+
const s = () => {
|
|
1410
|
+
e.current = !1, n.current = null, t.selecting = void 0;
|
|
1411
|
+
};
|
|
1412
|
+
return document.addEventListener("mouseup", s), () => document.removeEventListener("mouseup", s);
|
|
1413
|
+
}, []), Je();
|
|
1414
|
+
}
|
|
1415
|
+
function Je() {
|
|
1416
|
+
const t = f(), e = f((n) => n.selecting);
|
|
1417
|
+
x(() => {
|
|
1418
|
+
if (e !== "drag") return;
|
|
1419
|
+
const n = [...t.refs.container.children], s = n.map((r) => r.contentEditable);
|
|
1420
|
+
return n.forEach((r) => r.contentEditable = "false"), () => n.forEach((r, o) => r.contentEditable = s[o]);
|
|
1421
|
+
}, [e]);
|
|
1422
|
+
}
|
|
1423
|
+
function Qe() {
|
|
1424
|
+
const t = f(), e = R(() => {
|
|
1425
|
+
const n = t.props.showOverlayOn, s = "selectionChange";
|
|
1426
|
+
(n === s || n.includes(s)) && t.bus.send(p.CheckTrigger);
|
|
1427
|
+
}, []);
|
|
1428
|
+
v(
|
|
1429
|
+
"focusin",
|
|
1430
|
+
() => {
|
|
1431
|
+
document.addEventListener("selectionchange", e);
|
|
1432
|
+
},
|
|
1433
|
+
[]
|
|
1434
|
+
), v(
|
|
1435
|
+
"focusout",
|
|
1436
|
+
() => {
|
|
1437
|
+
document.removeEventListener("selectionchange", e);
|
|
1438
|
+
},
|
|
1439
|
+
[]
|
|
1440
|
+
), v(
|
|
1441
|
+
p.Change,
|
|
1442
|
+
() => {
|
|
1443
|
+
const n = t.props.showOverlayOn, s = "change";
|
|
1444
|
+
(n === s || n.includes(s)) && t.bus.send(p.CheckTrigger);
|
|
1445
|
+
},
|
|
1446
|
+
[]
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
const Ke = () => {
|
|
1450
|
+
const t = f();
|
|
1451
|
+
v(p.ClearTrigger, (e) => t.overlayMatch = void 0, []), v(
|
|
1452
|
+
p.CheckTrigger,
|
|
1453
|
+
(e) => t.overlayMatch = U.find(t.props.options, (n) => n.overlay?.trigger),
|
|
1454
|
+
[]
|
|
1455
|
+
);
|
|
1456
|
+
}, Ye = () => {
|
|
1457
|
+
const t = f(), e = I(!1), { value: n, options: s } = f(
|
|
1458
|
+
(r) => ({
|
|
1459
|
+
value: r.props.value,
|
|
1460
|
+
options: r.props.Mark ? r.props.options : void 0
|
|
1461
|
+
}),
|
|
1462
|
+
!0
|
|
1463
|
+
);
|
|
1464
|
+
x(() => {
|
|
1465
|
+
const r = s?.map((i) => i.markup);
|
|
1466
|
+
if (r && r.some(Boolean) ? t.parser = new O(r) : t.parser = void 0, e.current) {
|
|
1467
|
+
t.bus.send(p.Parse);
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const o = n ?? t.props.defaultValue ?? "";
|
|
1471
|
+
t.parser ? t.tokens = t.parser.parse(o) : t.tokens = [
|
|
1472
|
+
{
|
|
1473
|
+
type: "text",
|
|
1474
|
+
content: o,
|
|
1475
|
+
position: { start: 0, end: o.length }
|
|
1476
|
+
}
|
|
1477
|
+
], e.current = !0;
|
|
1478
|
+
}, [n, s]), v(
|
|
1479
|
+
p.Parse,
|
|
1480
|
+
() => {
|
|
1481
|
+
t.tokens = t.nodes.focus.target ? et(t) : tt(t);
|
|
1482
|
+
},
|
|
1483
|
+
[]
|
|
1484
|
+
);
|
|
1485
|
+
};
|
|
1486
|
+
function et(t) {
|
|
1487
|
+
const { focus: e } = t.nodes;
|
|
1488
|
+
if (!t.parser)
|
|
1489
|
+
return t.tokens;
|
|
1490
|
+
const n = t.parser.parse(e.content);
|
|
1491
|
+
return n.length === 1 ? t.tokens : t.tokens.toSpliced(e.index, 1, ...n);
|
|
1492
|
+
}
|
|
1493
|
+
function tt(t) {
|
|
1494
|
+
const {
|
|
1495
|
+
props: { value: e }
|
|
1496
|
+
} = t, n = nt(t), s = Me(t.previousValue, e);
|
|
1497
|
+
switch (t.previousValue = e, !0) {
|
|
1498
|
+
//Mark removing happen
|
|
1499
|
+
case (s.left && n.includes(s.left) && s.right && Math.abs(s.left - s.right) > 1): {
|
|
1500
|
+
const r = n.indexOf(s.left), o = D(t, r - 1, r);
|
|
1501
|
+
return t.tokens.toSpliced(r - 1, 2, ...o);
|
|
1502
|
+
}
|
|
1503
|
+
//Changing in label
|
|
1504
|
+
case s.left !== void 0: {
|
|
1505
|
+
const [r] = q(n, s.left), o = D(t, r);
|
|
1506
|
+
return o.length === 1 ? t.tokens : t.tokens.toSpliced(r, 1, ...o);
|
|
1507
|
+
}
|
|
1508
|
+
case s.right !== void 0: {
|
|
1509
|
+
const [r] = q(n, s.right), o = D(t, r);
|
|
1510
|
+
return o.length === 1 ? t.tokens : t.tokens.toSpliced(r, 1, ...o);
|
|
1511
|
+
}
|
|
1512
|
+
default:
|
|
1513
|
+
return ne(t, e ?? "");
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
function D(t, ...e) {
|
|
1517
|
+
let n = "";
|
|
1518
|
+
for (const s of e) {
|
|
1519
|
+
const r = t.tokens[s];
|
|
1520
|
+
n += r.content;
|
|
1521
|
+
}
|
|
1522
|
+
return ne(t, n);
|
|
1523
|
+
}
|
|
1524
|
+
function nt(t) {
|
|
1525
|
+
let e = 0;
|
|
1526
|
+
return t.tokens.map((n) => {
|
|
1527
|
+
const s = n.content.length;
|
|
1528
|
+
return e += s, e - s;
|
|
1529
|
+
}) ?? [];
|
|
1530
|
+
}
|
|
1531
|
+
function ne(t, e) {
|
|
1532
|
+
return t.parser ? t.parser.parse(e) : [
|
|
1533
|
+
{
|
|
1534
|
+
type: "text",
|
|
1535
|
+
content: e,
|
|
1536
|
+
position: { start: 0, end: e.length }
|
|
1537
|
+
}
|
|
1538
|
+
];
|
|
1539
|
+
}
|
|
1540
|
+
const st = (t) => ({
|
|
1541
|
+
get container() {
|
|
1542
|
+
return t.refs.container;
|
|
1543
|
+
},
|
|
1544
|
+
get overlay() {
|
|
1545
|
+
return t.refs.overlay;
|
|
1546
|
+
},
|
|
1547
|
+
focus() {
|
|
1548
|
+
t.nodes.focus.head?.focus();
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
function rt(t) {
|
|
1552
|
+
const e = f();
|
|
1553
|
+
le(t, () => st(e), [e]);
|
|
1554
|
+
}
|
|
1555
|
+
const ot = ({ inRef: t }) => (rt(t), ze(), Ye(), je(), He(), qe(), Xe(), Ke(), Qe(), Ue(), Be(), Ze(), null), it = "_Container_1lmfr_1", at = "_Suggestions_1lmfr_10", ct = "_suggestionActive_1lmfr_38", V = {
|
|
1556
|
+
Container: it,
|
|
1557
|
+
Suggestions: at,
|
|
1558
|
+
suggestionActive: ct
|
|
1559
|
+
}, lt = [
|
|
1560
|
+
{
|
|
1561
|
+
markup: ve,
|
|
1562
|
+
overlay: {
|
|
1563
|
+
trigger: me,
|
|
1564
|
+
data: []
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
], ut = ({ props: t, children: e }) => {
|
|
1568
|
+
const n = dt(t), [s] = A(() => B.create(n));
|
|
1569
|
+
return x(() => {
|
|
1570
|
+
s.props = n;
|
|
1571
|
+
}), /* @__PURE__ */ m(P.Provider, { value: s, children: e });
|
|
1572
|
+
};
|
|
1573
|
+
function dt(t) {
|
|
1574
|
+
const e = ge(V.Container, t.className, t.slotProps?.container?.className), n = pe(t.style, t.slotProps?.container?.style);
|
|
1575
|
+
return {
|
|
1576
|
+
value: t.value,
|
|
1577
|
+
defaultValue: t.defaultValue,
|
|
1578
|
+
onChange: t.onChange,
|
|
1579
|
+
readOnly: t.readOnly,
|
|
1580
|
+
options: t.options ?? lt,
|
|
1581
|
+
showOverlayOn: t.showOverlayOn ?? "change",
|
|
1582
|
+
className: e,
|
|
1583
|
+
style: n,
|
|
1584
|
+
Mark: t.Mark,
|
|
1585
|
+
Overlay: t.Overlay,
|
|
1586
|
+
slots: t.slots,
|
|
1587
|
+
slotProps: t.slotProps
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function ht() {
|
|
1591
|
+
const t = f(), e = f((o) => o.overlayMatch), n = S.getAbsolutePosition(), s = R(() => t.bus.send(p.ClearTrigger), []), r = R(
|
|
1592
|
+
(o) => {
|
|
1593
|
+
const i = {
|
|
1594
|
+
type: "mark",
|
|
1595
|
+
value: o.value,
|
|
1596
|
+
meta: o.meta,
|
|
1597
|
+
content: "",
|
|
1598
|
+
position: { start: e.index, end: e.index + e.span.length },
|
|
1599
|
+
descriptor: {
|
|
1600
|
+
index: 0,
|
|
1601
|
+
// TODO: get correct index
|
|
1602
|
+
markup: e.option.markup
|
|
1603
|
+
},
|
|
1604
|
+
// TODO: fix typing
|
|
1605
|
+
children: [],
|
|
1606
|
+
nested: void 0
|
|
1607
|
+
};
|
|
1608
|
+
t.bus.send(p.Select, { mark: i, match: e }), t.bus.send(p.ClearTrigger);
|
|
1609
|
+
},
|
|
1610
|
+
[e]
|
|
1611
|
+
);
|
|
1612
|
+
return { match: e, style: n, select: r, close: s, ref: { current: t.refs.overlay } };
|
|
1613
|
+
}
|
|
1614
|
+
const ft = () => {
|
|
1615
|
+
const { match: t, select: e, style: n, ref: s } = ht(), [r, o] = A(NaN), i = t.option.overlay?.data || [], a = ue(
|
|
1616
|
+
() => i.filter((l) => l.toLowerCase().indexOf(t.value.toLowerCase()) > -1),
|
|
1617
|
+
[t.value, i]
|
|
1618
|
+
), c = a.length;
|
|
1619
|
+
return C(
|
|
1620
|
+
k.UP,
|
|
1621
|
+
(l) => {
|
|
1622
|
+
l.preventDefault(), o((h) => isNaN(h) ? 0 : (c + (h - 1) % c) % c);
|
|
1623
|
+
},
|
|
1624
|
+
[c]
|
|
1625
|
+
), C(
|
|
1626
|
+
k.DOWN,
|
|
1627
|
+
(l) => {
|
|
1628
|
+
l.preventDefault(), o((h) => isNaN(h) ? 0 : (h + 1) % c);
|
|
1629
|
+
},
|
|
1630
|
+
[c]
|
|
1631
|
+
), C(
|
|
1632
|
+
k.ENTER,
|
|
1633
|
+
(l) => {
|
|
1634
|
+
l.preventDefault();
|
|
1635
|
+
const h = a[r];
|
|
1636
|
+
e({ value: h, meta: r.toString() });
|
|
1637
|
+
},
|
|
1638
|
+
[a, r]
|
|
1639
|
+
), a.length ? /* @__PURE__ */ m("ul", { ref: s, className: V.Suggestions, style: n, children: a.map((l, h) => {
|
|
1640
|
+
const u = h === r ? V.suggestionActive : void 0;
|
|
1641
|
+
return /* @__PURE__ */ m(
|
|
1642
|
+
"li",
|
|
1643
|
+
{
|
|
1644
|
+
ref: (d) => u && d?.scrollIntoView(!1),
|
|
1645
|
+
className: u,
|
|
1646
|
+
onClick: (d) => e({ value: l, meta: h.toString() }),
|
|
1647
|
+
children: l
|
|
1648
|
+
},
|
|
1649
|
+
l
|
|
1650
|
+
);
|
|
1651
|
+
}) }) : null;
|
|
1652
|
+
}, se = W(() => {
|
|
1653
|
+
const t = f(), e = f((o) => o.overlayMatch), n = f((o) => o.overlayMatch ? o.key.get(o.overlayMatch.option) : void 0), [s, r] = ee("overlay", e?.option, void 0, ft);
|
|
1654
|
+
if (x(() => {
|
|
1655
|
+
t.nodes.input.target = t.nodes.focus.target;
|
|
1656
|
+
}, [n]), n) return /* @__PURE__ */ m(s, { ...r ?? {} }, n);
|
|
1657
|
+
});
|
|
1658
|
+
se.displayName = "Whisper";
|
|
1659
|
+
const gt = (t, e) => /* @__PURE__ */ ie(ut, { props: t, children: [
|
|
1660
|
+
/* @__PURE__ */ m(te, {}),
|
|
1661
|
+
/* @__PURE__ */ m(se, {}),
|
|
1662
|
+
/* @__PURE__ */ m(ot, { inRef: e })
|
|
1663
|
+
] }), St = de(gt);
|
|
1664
|
+
function F(t, e, n = 0, s) {
|
|
1665
|
+
for (const r of t) {
|
|
1666
|
+
if (r === e) return { depth: n, parent: s };
|
|
1667
|
+
if (r.type === "mark") {
|
|
1668
|
+
const o = F(r.children, e, n + 1, r);
|
|
1669
|
+
if (o) return o;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
class pt {
|
|
1674
|
+
ref;
|
|
1675
|
+
#t;
|
|
1676
|
+
#e;
|
|
1677
|
+
readOnly;
|
|
1678
|
+
constructor(e) {
|
|
1679
|
+
this.ref = e.ref, this.#t = e.store, this.#e = e.token;
|
|
1680
|
+
}
|
|
1681
|
+
// ─── Data Properties ─────────────────────────────────────────────────────────
|
|
1682
|
+
/** Displayed text of the mark */
|
|
1683
|
+
get content() {
|
|
1684
|
+
return this.#e.content;
|
|
1685
|
+
}
|
|
1686
|
+
set content(e) {
|
|
1687
|
+
this.#e.content = e, this.#n();
|
|
1688
|
+
}
|
|
1689
|
+
/** Data value associated with the mark */
|
|
1690
|
+
get value() {
|
|
1691
|
+
return this.#e.value;
|
|
1692
|
+
}
|
|
1693
|
+
set value(e) {
|
|
1694
|
+
this.#e.value = e ?? "", this.#n();
|
|
1695
|
+
}
|
|
1696
|
+
/** Optional metadata for the mark */
|
|
1697
|
+
get meta() {
|
|
1698
|
+
return this.#e.meta;
|
|
1699
|
+
}
|
|
1700
|
+
set meta(e) {
|
|
1701
|
+
this.#e.meta = e, this.#n();
|
|
1702
|
+
}
|
|
1703
|
+
// ─── Navigation Properties ───────────────────────────────────────────────────
|
|
1704
|
+
/** Nesting depth (0 for root-level marks) */
|
|
1705
|
+
get depth() {
|
|
1706
|
+
return F(this.#t.tokens, this.#e).depth;
|
|
1707
|
+
}
|
|
1708
|
+
/** Whether this mark has nested children */
|
|
1709
|
+
get hasChildren() {
|
|
1710
|
+
return this.#e.children.length > 0;
|
|
1711
|
+
}
|
|
1712
|
+
/** Parent mark token (undefined for root-level marks) */
|
|
1713
|
+
get parent() {
|
|
1714
|
+
return F(this.#t.tokens, this.#e)?.parent;
|
|
1715
|
+
}
|
|
1716
|
+
/** Child tokens of this mark */
|
|
1717
|
+
get tokens() {
|
|
1718
|
+
return this.#e.children;
|
|
1719
|
+
}
|
|
1720
|
+
// ─── Mutation Methods ────────────────────────────────────────────────────────
|
|
1721
|
+
/** Update multiple properties in a single operation */
|
|
1722
|
+
change = (e) => {
|
|
1723
|
+
this.#e.content = e.content, this.#e.value = e.value ?? "", e.meta !== void 0 && (this.#e.meta = e.meta), this.#n();
|
|
1724
|
+
};
|
|
1725
|
+
/** Delete this mark from the editor */
|
|
1726
|
+
remove = () => this.#t.bus.send(p.Delete, { token: this.#e });
|
|
1727
|
+
// ─── Private ─────────────────────────────────────────────────────────────────
|
|
1728
|
+
#n() {
|
|
1729
|
+
this.#t.bus.send(p.Change, { node: this.#e });
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
const Ct = (t = {}) => {
|
|
1733
|
+
const e = f(), n = H(), s = I();
|
|
1734
|
+
if (n.type !== "mark")
|
|
1735
|
+
throw new Error("useMark can only be used with mark tokens");
|
|
1736
|
+
const [r] = A(() => new pt({ ref: s, store: e, token: n }));
|
|
1737
|
+
mt(s, t, n);
|
|
1738
|
+
const o = f((i) => i.props.readOnly);
|
|
1739
|
+
return x(() => {
|
|
1740
|
+
r.readOnly = o;
|
|
1741
|
+
}, [o]), r;
|
|
1742
|
+
};
|
|
1743
|
+
function mt(t, e, n) {
|
|
1744
|
+
x(() => {
|
|
1745
|
+
t.current && !e.controlled && (t.current.textContent = n.content);
|
|
1746
|
+
}, []);
|
|
1747
|
+
}
|
|
1748
|
+
export {
|
|
1749
|
+
St as MarkedInput,
|
|
1750
|
+
$ as annotate,
|
|
1751
|
+
kt as denote,
|
|
1752
|
+
v as useListener,
|
|
1753
|
+
Ct as useMark,
|
|
1754
|
+
ht as useOverlay
|
|
1755
|
+
};
|