@embedpdf/plugin-selection 3.0.0-next.2 → 3.0.0-next.4

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/dist/index.cjs CHANGED
@@ -2,251 +2,81 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _embedpdf_core = require("@embedpdf/core");
3
3
  let _embedpdf_plugin_interaction = require("@embedpdf/plugin-interaction");
4
4
  let _embedpdf_core_geometry = require("@embedpdf/core-geometry");
5
+ let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
5
6
  //#region src/geometry.ts
6
7
  /**
7
- * The pure selection geometry: engine text geometry (PDF user space, y-up) →
8
- * viewer content space (y-down), glyph hit-testing, word/line expansion, and the
9
- * line-merge. No DOM, no engine, no React.
8
+ * The selection plugin's COORDINATE SEAM — and nothing else.
10
9
  *
11
- * The non-trivial bits are ports of battle-tested algorithms, adapted to v3's
12
- * content-space `Rect {x,y,width,height}`:
13
- * - `glyphAt` — PDFium `GetIndexAtPos` (exact tight box, then a tolerance pass).
14
- * - `rectsForRange` — Chromium `pdfium_range.cc` `MergeAdjacentRects` (one rect per line).
15
- * - `expandToWord/Line` — Chromium `OnMultipleClick` (double = word, triple = line).
16
- * The PDF↔content y-flip is the geometry package's `pageGeometry` (the one place
17
- * that math lives), never hand-rolled here.
10
+ * Segmentation, hit-testing, and word/line expansion live in the canonical
11
+ * engine-core text layout (`@embedpdf/engine-core` `text/layout`): a text
12
+ * range has exactly one segmentation whether it came from a drag or a
13
+ * search. This module only converts between the viewer's content space
14
+ * (y-down, crop-relative) and the engine's PDF space (y-up) — pointer
15
+ * points inward, canonical segments outward. The PDF↔content y-flip is the
16
+ * geometry package's `pageGeometry` (the one place that math lives), never
17
+ * hand-rolled here.
18
18
  */
19
- const FLAG_EMPTY = 2;
20
- const isBoundary = (flags) => (flags & 3) !== 0;
21
- const isEmpty = (flags) => (flags & FLAG_EMPTY) !== 0;
22
- const toContent = (m, b) => (0, _embedpdf_core_geometry.applyRect)(m, {
23
- x: b.left,
24
- y: b.bottom,
25
- width: b.right - b.left,
26
- height: b.top - b.bottom
27
- });
28
19
  /**
29
- * Flatten a page's text geometry into content-space glyphs + runs. Rotation does
30
- * NOT enter here — the overlay rides the page's CSS rotation, so glyphs stay in
31
- * un-rotated content space (the crop-relative y-flip is the only conversion).
20
+ * Build a page's selection geometry. Page display rotation does NOT enter
21
+ * here — the overlay rides the page's CSS rotation, so content space stays
22
+ * un-rotated (the crop-relative y-flip is the only conversion; zoom = 1,
23
+ * the viewer's zoom is applied later by `PageTransform.pageToContent`).
32
24
  */
33
- function buildPageText(snapshot, crop, rotation, userUnit) {
25
+ function buildSelectionPageGeometry(snapshot, crop, rotation, userUnit) {
34
26
  const { pdfToContent } = (0, _embedpdf_core_geometry.pageGeometry)({
35
27
  crop,
36
28
  rotation,
37
29
  userUnit
38
30
  }, 1);
39
- const glyphs = [];
40
- const runs = [];
41
- for (const run of snapshot.runs) {
42
- const start = glyphs.length;
43
- for (const g of run.glyphs) glyphs.push({
44
- loose: toContent(pdfToContent, g.looseBox),
45
- tight: g.tightBox ? toContent(pdfToContent, g.tightBox) : void 0,
46
- flags: g.flags
47
- });
48
- runs.push({
49
- start,
50
- count: run.glyphs.length,
51
- rect: toContent(pdfToContent, run.rect),
52
- fontSize: run.fontSize
53
- });
54
- }
55
31
  return {
56
- glyphs,
57
- runs
32
+ layout: (0, _embedpdf_engine_core_runtime.buildPageTextLayout)(snapshot),
33
+ toContent: pdfToContent,
34
+ fromContent: (0, _embedpdf_core_geometry.invert)(pdfToContent)
58
35
  };
59
36
  }
60
- function avgGlyphHeight(text) {
61
- let total = 0;
62
- let count = 0;
63
- for (const g of text.glyphs) {
64
- if (isEmpty(g.flags)) continue;
65
- total += g.loose.height;
66
- count++;
67
- }
68
- return count === 0 ? 0 : total / count;
37
+ /** A content-space pointer point in the layout's PDF space. */
38
+ function contentPointToPdf(geom, p) {
39
+ return (0, _embedpdf_core_geometry.applyPoint)(geom.fromContent, p);
69
40
  }
70
41
  /**
71
- * The glyph index at a content-space point, or null when nothing is near (so the
72
- * caller can show the pointer cursor off-text). PDFium `GetIndexAtPos`: exact
73
- * tight-box containment first, then a tolerance pass (closest by Manhattan
74
- * distance within `toleranceFactor × average glyph height`).
42
+ * A canonical PDF quad in content space. Engine quads carry frame-geometric
43
+ * slot semantics (`p1..p4` = upper-start, upper-end, lower-start,
44
+ * lower-end) — a trusted producer, so the y-flip maps corners straight onto
45
+ * their names; imported-annotation normalization does NOT apply here.
75
46
  */
76
- function glyphAt(text, p, toleranceFactor = 1.5) {
77
- for (const run of text.runs) {
78
- const r = run.rect;
79
- if (p.x < r.x || p.x > r.x + r.width || p.y < r.y || p.y > r.y + r.height) continue;
80
- for (let i = 0; i < run.count; i++) {
81
- const b = text.glyphs[run.start + i].tight ?? text.glyphs[run.start + i].loose;
82
- if (p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height) return run.start + i;
83
- }
84
- }
85
- if (toleranceFactor <= 0) return null;
86
- const half = avgGlyphHeight(text) * toleranceFactor / 2;
87
- let best = -1;
88
- let bestDist = Infinity;
89
- for (const run of text.runs) {
90
- const r = run.rect;
91
- if (p.y < r.y - half || p.y > r.y + r.height + half || p.x < r.x - half || p.x > r.x + r.width + half) continue;
92
- for (let i = 0; i < run.count; i++) {
93
- const g = text.glyphs[run.start + i];
94
- if (isEmpty(g.flags)) continue;
95
- const b = g.tight ?? g.loose;
96
- if (p.x < b.x - half || p.x > b.x + b.width + half || p.y < b.y - half || p.y > b.y + b.height + half) continue;
97
- const dx = Math.min(Math.abs(p.x - b.x), Math.abs(p.x - (b.x + b.width)));
98
- const dy = Math.min(Math.abs(p.y - b.y), Math.abs(p.y - (b.y + b.height)));
99
- if (dx + dy < bestDist) {
100
- bestDist = dx + dy;
101
- best = run.start + i;
102
- }
103
- }
104
- }
105
- return best >= 0 ? best : null;
106
- }
107
- /** Double-click: the word around `i` (walk to space/empty glyphs both ways). */
108
- function expandToWord(text, i) {
109
- const n = text.glyphs.length;
110
- if (i < 0 || i >= n) return [i, i];
111
- let from = i;
112
- while (from > 0 && !isBoundary(text.glyphs[from - 1].flags)) from--;
113
- let to = i;
114
- while (to < n - 1 && !isBoundary(text.glyphs[to + 1].flags)) to++;
115
- return [from, to];
116
- }
117
- /** Triple-click: the full visual line — runs whose vertical extent overlaps the anchor run's. */
118
- function expandToLine(text, i) {
119
- const ri = text.runs.findIndex((r) => i >= r.start && i < r.start + r.count);
120
- if (ri < 0) return [i, i];
121
- const anchor = text.runs[ri];
122
- const top = anchor.rect.y;
123
- const bottom = anchor.rect.y + anchor.rect.height;
124
- let from = anchor.start;
125
- let to = anchor.start + anchor.count - 1;
126
- for (let r = ri - 1; r >= 0; r--) {
127
- const run = text.runs[r];
128
- if (isZero(run.rect)) continue;
129
- if (!overlapV(run.rect.y, run.rect.y + run.rect.height, top, bottom)) break;
130
- from = run.start;
131
- }
132
- for (let r = ri + 1; r < text.runs.length; r++) {
133
- const run = text.runs[r];
134
- if (isZero(run.rect)) continue;
135
- if (!overlapV(run.rect.y, run.rect.y + run.rect.height, top, bottom)) break;
136
- to = run.start + run.count - 1;
137
- }
138
- return [from, to];
139
- }
140
- const CHAR_DISTANCE_FACTOR = 2.5;
141
- const FONT_SIZE_RATIO_THRESHOLD = 1.5;
142
- const VERTICAL_OVERLAP_THRESHOLD = .8;
143
- const LINE_OVERLAP_THRESHOLD = .5;
144
- function rectsForRange(text, from, to) {
145
- const lo = Math.max(0, Math.min(from, to));
146
- const hi = Math.min(text.glyphs.length - 1, Math.max(from, to));
147
- const subRuns = [];
148
- for (const run of text.runs) {
149
- const runEnd = run.start + run.count - 1;
150
- if (runEnd < lo || run.start > hi) continue;
151
- const s = Math.max(lo, run.start);
152
- const e = Math.min(hi, runEnd);
153
- let minX = Infinity;
154
- let maxX = -Infinity;
155
- let minY = Infinity;
156
- let maxY = -Infinity;
157
- let charCount = 0;
158
- let widthSum = 0;
159
- let prevRight = -Infinity;
160
- const flush = () => {
161
- if (minX !== Infinity && charCount > 0) subRuns.push({
162
- rect: {
163
- x: minX,
164
- y: minY,
165
- width: maxX - minX,
166
- height: maxY - minY
167
- },
168
- charCount,
169
- fontSize: run.fontSize
170
- });
171
- minX = Infinity;
172
- maxX = -Infinity;
173
- minY = Infinity;
174
- maxY = -Infinity;
175
- charCount = 0;
176
- widthSum = 0;
177
- prevRight = -Infinity;
178
- };
179
- for (let gi = s; gi <= e; gi++) {
180
- const g = text.glyphs[gi];
181
- if (isEmpty(g.flags)) continue;
182
- const b = g.loose;
183
- if (charCount > 0 && prevRight > -Infinity) {
184
- const avg = widthSum / charCount;
185
- if (avg > 0 && Math.abs(b.x - prevRight) > CHAR_DISTANCE_FACTOR * avg) flush();
186
- }
187
- minX = Math.min(minX, b.x);
188
- maxX = Math.max(maxX, b.x + b.width);
189
- minY = Math.min(minY, b.y);
190
- maxY = Math.max(maxY, b.y + b.height);
191
- charCount++;
192
- widthSum += b.width;
193
- prevRight = b.x + b.width;
194
- }
195
- flush();
196
- }
197
- return mergeAdjacentRects(subRuns);
198
- }
199
- function mergeAdjacentRects(runs) {
200
- const out = [];
201
- let prev = null;
202
- let cur = null;
203
- for (const run of runs) {
204
- if (prev && cur && shouldMerge(prev, run)) cur = (0, _embedpdf_core_geometry.boundsOfRects)([cur, run.rect]);
205
- else {
206
- if (cur) out.push(cur);
207
- cur = run.rect;
208
- }
209
- prev = run;
210
- }
211
- if (cur && cur.width > 0 && cur.height > 0) out.push(cur);
212
- return out;
213
- }
214
- function shouldMerge(a, b) {
215
- if (a.fontSize != null && b.fontSize != null && a.fontSize > 0 && b.fontSize > 0) {
216
- if (Math.max(a.fontSize, b.fontSize) / Math.min(a.fontSize, b.fontSize) > FONT_SIZE_RATIO_THRESHOLD) return false;
217
- }
218
- if (verticalOverlap(a.rect, b.rect) < VERTICAL_OVERLAP_THRESHOLD) return false;
219
- const aw = a.rect.width / a.charCount;
220
- const bw = b.rect.width / b.charCount;
221
- const aL = a.rect.x - aw;
222
- const aR = a.rect.x + a.rect.width + aw;
223
- const bL = b.rect.x - bw;
224
- return aL < b.rect.x + b.rect.width + bw && aR > bL;
225
- }
226
- function verticalOverlap(a, b) {
227
- if (a.height <= 0 || b.height <= 0) return 0;
228
- const u = Math.max(a.y + a.height, b.y + b.height) - Math.min(a.y, b.y);
229
- if (u === a.height || u === b.height) return 1;
230
- return Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)) / u;
47
+ function toContentTextQuad(geom, quad) {
48
+ const pt = (p) => (0, _embedpdf_core_geometry.applyPoint)(geom.toContent, p);
49
+ return {
50
+ upperStart: pt(quad.p1),
51
+ upperEnd: pt(quad.p2),
52
+ lowerStart: pt(quad.p3),
53
+ lowerEnd: pt(quad.p4)
54
+ };
231
55
  }
232
- const isZero = (r) => r.width === 0 && r.height === 0;
233
- function overlapV(top1, bottom1, top2, bottom2) {
234
- const u = Math.max(bottom1, bottom2) - Math.min(top1, top2);
235
- if (u === 0) return false;
236
- return Math.max(0, Math.min(bottom1, bottom2) - Math.max(top1, top2)) / u >= LINE_OVERLAP_THRESHOLD;
56
+ /** A canonical segment in content space (`rect` recomputed from the mapped
57
+ * quad so the pair can never drift). */
58
+ function toContentSegment(geom, segment) {
59
+ const quad = toContentTextQuad(geom, segment.quad);
60
+ return {
61
+ quad,
62
+ rect: (0, _embedpdf_core_geometry.textQuadBounds)(quad),
63
+ advance: segment.advance
64
+ };
237
65
  }
238
66
  //#endregion
239
67
  //#region src/capability.ts
240
- const EMPTY = [];
68
+ const EMPTY_SEGMENTS = [];
241
69
  /**
242
70
  * The selection capability. The reducer state holds the selection range, the
243
- * derived content-space rects (per page), and per-page loaded flags; the (large,
244
- * non-serializable) per-page text geometry is cached HERE in the closure.
71
+ * derived content-space segments (per page), and per-page loaded flags; the
72
+ * (large, non-serializable) per-page canonical layout is cached HERE in the
73
+ * closure. Segmentation itself is the engine-core text layout — selection
74
+ * owns gestures and state, never how glyphs become lines.
245
75
  *
246
- * Selection is cross-page: glyphs are ordered globally by (pageIndex, glyph), so
247
- * a drag from page 2 into page 4 selects the tail of 2, all of 3, and the head of
248
- * 4. `recompute` rebuilds the merged line rects for every loaded page in the span
249
- * and re-runs whenever a mid-span page finishes loading.
76
+ * Selection is cross-page: glyphs are ordered globally by (pageIndex, glyph),
77
+ * so a drag from page 2 into page 4 selects the tail of 2, all of 3, and the
78
+ * head of 4. `recompute` rebuilds the merged line segments for every loaded
79
+ * page in the span and re-runs whenever a mid-span page finishes loading.
250
80
  */
251
81
  function createSelectionCapability(ctx) {
252
82
  const cache = /* @__PURE__ */ new Map();
@@ -256,6 +86,7 @@ function createSelectionCapability(ctx) {
256
86
  const fireChange = () => changeCbs.forEach((cb) => cb());
257
87
  const pageIndexOf = (pon) => ctx.document()?.pages.findIndex((p) => p.pageObjectNumber === pon) ?? -1;
258
88
  const ponAtIndex = (i) => ctx.document()?.pages[i]?.pageObjectNumber;
89
+ const glyphAt = (geom, point) => (0, _embedpdf_engine_core_runtime.textGlyphAt)(geom.layout, contentPointToPdf(geom, point));
259
90
  function orderedEnds(sel) {
260
91
  const ai = pageIndexOf(sel.anchor.pon);
261
92
  const fi = pageIndexOf(sel.focus.pon);
@@ -270,18 +101,32 @@ function createSelectionCapability(ctx) {
270
101
  };
271
102
  }
272
103
  function endpointFor(ptr, which) {
273
- const rects = ctx.getState().rects[ptr.pon] ?? EMPTY;
274
- if (!rects.length) return null;
104
+ const segments = ctx.getState().segments[ptr.pon] ?? EMPTY_SEGMENTS;
105
+ if (!segments.length) return null;
106
+ const segment = which === "start" ? segments[0] : segments[segments.length - 1];
107
+ const geom = cache.get(ptr.pon);
108
+ const cell = geom ? (0, _embedpdf_engine_core_runtime.textGlyphQuad)(geom.layout, ptr.glyph) : null;
109
+ if (geom && cell) {
110
+ const glyphQuad = toContentTextQuad(geom, cell);
111
+ return {
112
+ pon: ptr.pon,
113
+ glyphQuad,
114
+ advance: segment.advance,
115
+ rect: (0, _embedpdf_core_geometry.textQuadBounds)(glyphQuad)
116
+ };
117
+ }
275
118
  return {
276
119
  pon: ptr.pon,
277
- rect: which === "start" ? rects[0] : rects[rects.length - 1]
120
+ glyphQuad: segment.quad,
121
+ advance: segment.advance,
122
+ rect: segment.rect
278
123
  };
279
124
  }
280
125
  function snapshot() {
281
- const { selection, rects } = ctx.getState();
282
- const pages = Object.keys(rects).map(Number).filter((pon) => (rects[pon]?.length ?? 0) > 0).map((pon) => ({
126
+ const { selection, segments } = ctx.getState();
127
+ const pages = Object.keys(segments).map(Number).filter((pon) => (segments[pon]?.length ?? 0) > 0).map((pon) => ({
283
128
  pon,
284
- rects: rects[pon]
129
+ segments: segments[pon]
285
130
  }));
286
131
  if (!selection) return {
287
132
  pages,
@@ -305,7 +150,7 @@ function createSelectionCapability(ctx) {
305
150
  pending.add(pon);
306
151
  doc.page(pon).geometry.read().then((snapshot) => {
307
152
  pending.delete(pon);
308
- cache.set(pon, buildPageText(snapshot, layout.boxes.crop, layout.rotation, layout.userUnit));
153
+ cache.set(pon, buildSelectionPageGeometry(snapshot, layout.boxes.crop, layout.rotation, layout.userUnit));
309
154
  ctx.dispatch({
310
155
  type: "PAGE_LOADED",
311
156
  pon
@@ -321,30 +166,32 @@ function createSelectionCapability(ctx) {
321
166
  const si = pageIndexOf(start.pon);
322
167
  const ei = pageIndexOf(end.pon);
323
168
  if (si < 0 || ei < 0) return;
324
- const rects = {};
169
+ const segments = {};
325
170
  for (let i = si; i <= ei; i++) {
326
171
  const pon = ponAtIndex(i);
327
172
  if (pon == null) continue;
328
- const text = cache.get(pon);
329
- if (!text) {
173
+ const geom = cache.get(pon);
174
+ if (!geom) {
330
175
  ensurePage(pon);
331
176
  continue;
332
177
  }
333
- rects[pon] = rectsForRange(text, i === si ? start.glyph : 0, i === ei ? end.glyph : text.glyphs.length - 1);
178
+ const from = i === si ? start.glyph : 0;
179
+ const to = i === ei ? end.glyph : geom.layout.glyphs.length - 1;
180
+ segments[pon] = (0, _embedpdf_engine_core_runtime.textSegmentsForRange)(geom.layout, from, to - from + 1).map((s) => toContentSegment(geom, s));
334
181
  }
335
182
  ctx.dispatch({
336
183
  type: "SET",
337
184
  selection: sel,
338
- rects
185
+ segments
339
186
  });
340
187
  fireChange();
341
188
  }
342
189
  function selectSpan(pon, point, expand) {
343
- const text = cache.get(pon);
344
- if (!text) return;
345
- const i = glyphAt(text, point);
190
+ const geom = cache.get(pon);
191
+ if (!geom) return;
192
+ const i = glyphAt(geom, point);
346
193
  if (i == null) return;
347
- const [from, to] = expand === "word" ? expandToWord(text, i) : expandToLine(text, i);
194
+ const [from, to] = expand === "word" ? (0, _embedpdf_engine_core_runtime.expandTextRangeToWord)(geom.layout, i) : (0, _embedpdf_engine_core_runtime.expandTextRangeToLine)(geom.layout, i);
348
195
  recompute({
349
196
  anchor: {
350
197
  pon,
@@ -360,13 +207,13 @@ function createSelectionCapability(ctx) {
360
207
  ensurePage,
361
208
  isLoaded: (pon) => !!ctx.getState().loaded[pon],
362
209
  isOverText: (pon, point) => {
363
- const text = cache.get(pon);
364
- return text ? glyphAt(text, point) != null : false;
210
+ const geom = cache.get(pon);
211
+ return geom ? glyphAt(geom, point) != null : false;
365
212
  },
366
213
  beginAt: (pon, point) => {
367
- const text = cache.get(pon);
368
- if (!text) return false;
369
- const i = glyphAt(text, point);
214
+ const geom = cache.get(pon);
215
+ if (!geom) return false;
216
+ const i = glyphAt(geom, point);
370
217
  if (i == null) return false;
371
218
  recompute({
372
219
  anchor: {
@@ -385,12 +232,12 @@ function createSelectionCapability(ctx) {
385
232
  extendTo: (pon, point) => {
386
233
  const cur = ctx.getState().selection;
387
234
  if (!cur) return;
388
- const text = cache.get(pon);
389
- if (!text) {
235
+ const geom = cache.get(pon);
236
+ if (!geom) {
390
237
  ensurePage(pon);
391
238
  return;
392
239
  }
393
- const i = glyphAt(text, point);
240
+ const i = glyphAt(geom, point);
394
241
  if (i == null) return;
395
242
  recompute({
396
243
  anchor: cur.anchor,
@@ -406,11 +253,12 @@ function createSelectionCapability(ctx) {
406
253
  fireChange();
407
254
  },
408
255
  snapshot,
409
- rectsForPage: (pon) => ctx.getState().rects[pon] ?? EMPTY,
256
+ segmentsForPage: (pon) => ctx.getState().segments[pon] ?? EMPTY_SEGMENTS,
257
+ rectsForPage: (pon) => (ctx.getState().segments[pon] ?? EMPTY_SEGMENTS).map((s) => s.rect),
410
258
  hasSelection: () => ctx.getState().selection != null,
411
259
  selectedPages: () => {
412
- const { rects } = ctx.getState();
413
- return Object.keys(rects).map(Number).filter((pon) => (rects[pon]?.length ?? 0) > 0);
260
+ const { segments } = ctx.getState();
261
+ return Object.keys(segments).map(Number).filter((pon) => (segments[pon]?.length ?? 0) > 0);
414
262
  },
415
263
  onChange: (cb) => {
416
264
  changeCbs.add(cb);
@@ -504,7 +352,7 @@ function createTextSelectHandler(selection, interaction) {
504
352
  //#region src/reducer.ts
505
353
  const initialSelectionState = {
506
354
  selection: null,
507
- rects: {},
355
+ segments: {},
508
356
  loaded: {},
509
357
  highlightHidden: false
510
358
  };
@@ -520,12 +368,12 @@ const selectionReducer = (state, action) => {
520
368
  case "SET": return {
521
369
  ...state,
522
370
  selection: action.selection,
523
- rects: action.rects
371
+ segments: action.segments
524
372
  };
525
- case "CLEAR": return state.selection === null && Object.keys(state.rects).length === 0 ? state : {
373
+ case "CLEAR": return state.selection === null && Object.keys(state.segments).length === 0 ? state : {
526
374
  ...state,
527
375
  selection: null,
528
- rects: {}
376
+ segments: {}
529
377
  };
530
378
  case "SET_HIGHLIGHT_HIDDEN": return state.highlightHidden === action.hidden ? state : {
531
379
  ...state,
@@ -561,15 +409,14 @@ const selectionPlugin = () => (0, _embedpdf_core.definePlugin)({
561
409
  });
562
410
  //#endregion
563
411
  exports.SelectionToken = SelectionToken;
564
- exports.buildPageText = buildPageText;
412
+ exports.buildSelectionPageGeometry = buildSelectionPageGeometry;
413
+ exports.contentPointToPdf = contentPointToPdf;
565
414
  exports.createSelectionCapability = createSelectionCapability;
566
415
  exports.createTextSelectHandler = createTextSelectHandler;
567
- exports.expandToLine = expandToLine;
568
- exports.expandToWord = expandToWord;
569
- exports.glyphAt = glyphAt;
570
416
  exports.initialSelectionState = initialSelectionState;
571
- exports.rectsForRange = rectsForRange;
572
417
  exports.selectionPlugin = selectionPlugin;
573
418
  exports.selectionReducer = selectionReducer;
419
+ exports.toContentSegment = toContentSegment;
420
+ exports.toContentTextQuad = toContentTextQuad;
574
421
 
575
422
  //# sourceMappingURL=index.cjs.map