@jhl548/duplicate-doc-core 0.1.2 → 0.1.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/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - Shared document, range, duplicate point, and editor snapshot types.
8
8
  - Similarity-based highlight color and CSS class helpers.
9
9
  - Range normalization and document filtering utilities.
10
+ - Plain-text highlight resolution helpers for checking whether a range can still render after edits.
10
11
  - Tiptap duplicate highlight extension.
11
12
  - Shared highlight styles via `@jhl548/duplicate-doc-core/style.css`.
12
13
 
@@ -25,7 +26,10 @@ import {
25
26
  type DuplicateHighlight,
26
27
  type NormalizedDocument
27
28
  } from "@jhl548/duplicate-doc-core";
29
+ import { hasResolvableHighlightInPlainText } from "@jhl548/duplicate-doc-core/highlight-resolution";
28
30
  import "@jhl548/duplicate-doc-core/style.css";
29
31
  ```
30
32
 
31
33
  This package is intended to be consumed directly by framework adapters such as `@jhl548/duplicate-doc-vue`, or by applications that need access to the shared duplicate document model.
34
+
35
+ Use `hasResolvableHighlightInPlainText(plainText, highlight)` from the pure `highlight-resolution` subpath before switching documents when the app needs to know whether at least one highlight range can still be resolved in the current edited text.
@@ -0,0 +1,10 @@
1
+ import type { DuplicateHighlight, TextRange } from "./types.js";
2
+ export interface ResolvedPlainTextRange {
3
+ start: number;
4
+ end: number;
5
+ matchedText: string;
6
+ source: "direct" | "fallback";
7
+ }
8
+ export declare function resolveTextRangeInPlainText(plainText: string, range: TextRange, expectedText?: string | undefined): ResolvedPlainTextRange | null;
9
+ export declare function isTextRangeResolvableInPlainText(plainText: string, range: TextRange, expectedText?: string | undefined): boolean;
10
+ export declare function hasResolvableHighlightInPlainText(plainText: string, highlight: DuplicateHighlight): boolean;
@@ -0,0 +1,54 @@
1
+ import { normalizeRange, normalizeRanges } from "./ranges.js";
2
+ function findClosestTextIndex(text, target, preferredIndex) {
3
+ if (!target) {
4
+ return -1;
5
+ }
6
+ let closestIndex = -1;
7
+ let closestDistance = Number.POSITIVE_INFINITY;
8
+ let searchIndex = text.indexOf(target);
9
+ while (searchIndex >= 0) {
10
+ const distance = Math.abs(searchIndex - preferredIndex);
11
+ if (distance < closestDistance) {
12
+ closestIndex = searchIndex;
13
+ closestDistance = distance;
14
+ }
15
+ searchIndex = text.indexOf(target, searchIndex + 1);
16
+ }
17
+ return closestIndex;
18
+ }
19
+ export function resolveTextRangeInPlainText(plainText, range, expectedText = range.matchedText) {
20
+ const normalizedRange = normalizeRange(range);
21
+ if (!normalizedRange) {
22
+ return null;
23
+ }
24
+ const directText = plainText.slice(normalizedRange.start, normalizedRange.end);
25
+ const isDirectRangeInBounds = normalizedRange.end <= plainText.length && directText.length > 0;
26
+ if (isDirectRangeInBounds && (!expectedText || directText === expectedText)) {
27
+ return {
28
+ start: normalizedRange.start,
29
+ end: normalizedRange.end,
30
+ matchedText: expectedText || directText,
31
+ source: "direct"
32
+ };
33
+ }
34
+ if (!expectedText) {
35
+ return null;
36
+ }
37
+ const fallbackStart = findClosestTextIndex(plainText, expectedText, normalizedRange.start);
38
+ if (fallbackStart < 0) {
39
+ return null;
40
+ }
41
+ return {
42
+ start: fallbackStart,
43
+ end: fallbackStart + expectedText.length,
44
+ matchedText: expectedText,
45
+ source: "fallback"
46
+ };
47
+ }
48
+ export function isTextRangeResolvableInPlainText(plainText, range, expectedText = range.matchedText) {
49
+ return Boolean(resolveTextRangeInPlainText(plainText, range, expectedText));
50
+ }
51
+ export function hasResolvableHighlightInPlainText(plainText, highlight) {
52
+ return normalizeRanges(highlight.ranges).some((range) => isTextRangeResolvableInPlainText(plainText, range, range.matchedText));
53
+ }
54
+ //# sourceMappingURL=highlightResolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlightResolution.js","sourceRoot":"","sources":["../src/highlightResolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU9D,SAAS,oBAAoB,CAAC,IAAY,EAAE,MAAc,EAAE,cAAsB;IAChF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,CAAC,CAAC;IACZ,CAAC;IAED,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,IAAI,eAAe,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC/C,IAAI,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvC,OAAO,WAAW,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,cAAc,CAAC,CAAC;QACxD,IAAI,QAAQ,GAAG,eAAe,EAAE,CAAC;YAC/B,YAAY,GAAG,WAAW,CAAC;YAC3B,eAAe,GAAG,QAAQ,CAAC;QAC7B,CAAC;QAED,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,SAAiB,EACjB,KAAgB,EAChB,YAAY,GAAG,KAAK,CAAC,WAAW;IAEhC,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;IAC/E,MAAM,qBAAqB,GAAG,eAAe,CAAC,GAAG,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/F,IAAI,qBAAqB,IAAI,CAAC,CAAC,YAAY,IAAI,UAAU,KAAK,YAAY,CAAC,EAAE,CAAC;QAC5E,OAAO;YACL,KAAK,EAAE,eAAe,CAAC,KAAK;YAC5B,GAAG,EAAE,eAAe,CAAC,GAAG;YACxB,WAAW,EAAE,YAAY,IAAI,UAAU;YACvC,MAAM,EAAE,QAAQ;SACjB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,aAAa,GAAG,oBAAoB,CAAC,SAAS,EAAE,YAAY,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;IAC3F,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,KAAK,EAAE,aAAa;QACpB,GAAG,EAAE,aAAa,GAAG,YAAY,CAAC,MAAM;QACxC,WAAW,EAAE,YAAY;QACzB,MAAM,EAAE,UAAU;KACnB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,SAAiB,EACjB,KAAgB,EAChB,YAAY,GAAG,KAAK,CAAC,WAAW;IAEhC,OAAO,OAAO,CAAC,2BAA2B,CAAC,SAAS,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,SAAiB,EACjB,SAA6B;IAE7B,OAAO,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACtD,gCAAgC,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CACtE,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export * from "./colors.js";
2
+ export * from "./highlightResolution.js";
3
+ export * from "./position.js";
2
4
  export * from "./ranges.js";
3
5
  export * from "./scroll.js";
6
+ export * from "./selection.js";
4
7
  export * from "./tiptapDuplicateHighlight.js";
5
8
  export * from "./types.js";
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  export * from "./colors.js";
2
+ export * from "./highlightResolution.js";
3
+ export * from "./position.js";
2
4
  export * from "./ranges.js";
3
5
  export * from "./scroll.js";
6
+ export * from "./selection.js";
4
7
  export * from "./tiptapDuplicateHighlight.js";
5
8
  export * from "./types.js";
6
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { DocumentSelectionChangePayload, RangeMapEntry } from "./types.js";
2
+ export interface PopupPosition {
3
+ top: number;
4
+ left: number;
5
+ placement: "above" | "below";
6
+ }
7
+ export interface SelectionPopupData {
8
+ documentId: string;
9
+ selection: DocumentSelectionChangePayload;
10
+ overlappingEntries: RangeMapEntry[];
11
+ }
12
+ export declare function calcSelectionPopupPosition(selRect: {
13
+ top: number;
14
+ bottom: number;
15
+ left: number;
16
+ right: number;
17
+ height: number;
18
+ }, viewportWidth: number, viewportHeight: number, popupRect?: {
19
+ width: number;
20
+ height: number;
21
+ }): PopupPosition;
22
+ export declare function getDomSelectionRect(): {
23
+ top: number;
24
+ bottom: number;
25
+ left: number;
26
+ right: number;
27
+ height: number;
28
+ } | null;
29
+ export declare function isDomSelectionInsideContainer(containerElement: HTMLElement): boolean;
@@ -0,0 +1,63 @@
1
+ export function calcSelectionPopupPosition(selRect, viewportWidth, viewportHeight, popupRect) {
2
+ const GAP = 8;
3
+ const popupWidth = popupRect?.width ?? 360;
4
+ const popupHeight = popupRect?.height ?? 200;
5
+ let left = selRect.left + (selRect.right - selRect.left) / 2 - popupWidth / 2;
6
+ left = Math.max(8, Math.min(left, viewportWidth - popupWidth - 8));
7
+ let top;
8
+ let placement;
9
+ const spaceBelow = viewportHeight - selRect.bottom;
10
+ const spaceAbove = selRect.top;
11
+ if (spaceBelow >= popupHeight + GAP || spaceBelow >= spaceAbove) {
12
+ placement = "below";
13
+ top = selRect.bottom + GAP;
14
+ }
15
+ else {
16
+ placement = "above";
17
+ top = selRect.top - popupHeight - GAP;
18
+ }
19
+ return { top, left, placement };
20
+ }
21
+ export function getDomSelectionRect() {
22
+ const domSelection = window.getSelection();
23
+ if (!domSelection || domSelection.isCollapsed || domSelection.rangeCount === 0) {
24
+ return null;
25
+ }
26
+ const domRange = domSelection.getRangeAt(0);
27
+ const rect = domRange.getBoundingClientRect();
28
+ if (rect.width === 0 && rect.height === 0) {
29
+ return null;
30
+ }
31
+ return {
32
+ top: rect.top,
33
+ bottom: rect.bottom,
34
+ left: rect.left,
35
+ right: rect.right,
36
+ height: rect.height
37
+ };
38
+ }
39
+ export function isDomSelectionInsideContainer(containerElement) {
40
+ const domSelection = window.getSelection();
41
+ if (!domSelection || domSelection.isCollapsed || domSelection.rangeCount === 0) {
42
+ return false;
43
+ }
44
+ const domRange = domSelection.getRangeAt(0);
45
+ const containerRect = containerElement.getBoundingClientRect();
46
+ if (domRange.startContainer) {
47
+ const node = domRange.startContainer;
48
+ if (!containerElement.contains(node.nodeType === Node.TEXT_NODE ? node.parentNode : node)) {
49
+ return false;
50
+ }
51
+ }
52
+ else {
53
+ return false;
54
+ }
55
+ const rect = domRange.getBoundingClientRect();
56
+ if (rect.width === 0 && rect.height === 0) {
57
+ return false;
58
+ }
59
+ const isHorizontallyVisible = rect.right > containerRect.left && rect.left < containerRect.right;
60
+ const isVerticallyVisible = rect.bottom > containerRect.top && rect.top < containerRect.bottom;
61
+ return isHorizontallyVisible && isVerticallyVisible;
62
+ }
63
+ //# sourceMappingURL=position.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { RangeMapEntry, TextRange } from "./types.js";
2
+ export interface SelectionRangeInfo {
3
+ selectedText: string;
4
+ from: number;
5
+ to: number;
6
+ empty: boolean;
7
+ plainTextOffset: {
8
+ start: number;
9
+ end: number;
10
+ } | null;
11
+ selectedRange: TextRange | null;
12
+ }
13
+ export declare function getSelectionRangeInfo(editor: {
14
+ state: {
15
+ selection: {
16
+ from: number;
17
+ to: number;
18
+ empty: boolean;
19
+ };
20
+ doc: {
21
+ textBetween: (from: number, to: number, separator: string | null, leafText?: string | null | ((leafNode: unknown) => string)) => string;
22
+ };
23
+ };
24
+ }): SelectionRangeInfo;
25
+ export declare function findOverlappingRangeMapEntries(rangeMap: RangeMapEntry[], plainTextOffset: {
26
+ start: number;
27
+ end: number;
28
+ }): RangeMapEntry[];
@@ -0,0 +1,39 @@
1
+ import { buildPlainTextPositionMap } from "./tiptapDuplicateHighlight.js";
2
+ export function getSelectionRangeInfo(editor) {
3
+ const { from, to, empty } = editor.state.selection;
4
+ const selectedText = editor.state.doc.textBetween(from, to, "\n");
5
+ let plainTextOffset = null;
6
+ let selectedRange = null;
7
+ if (!empty) {
8
+ const positions = buildPlainTextPositionMap(editor.state.doc);
9
+ const startIdx = positions.indexOf(from);
10
+ const lastIdx = positions.lastIndexOf(to - 1);
11
+ if (startIdx !== -1 && lastIdx !== -1 && lastIdx >= startIdx) {
12
+ const endIdx = lastIdx + 1;
13
+ plainTextOffset = { start: startIdx, end: endIdx };
14
+ selectedRange = {
15
+ start: startIdx,
16
+ end: endIdx,
17
+ matchedText: selectedText
18
+ };
19
+ }
20
+ }
21
+ return {
22
+ selectedText,
23
+ from,
24
+ to,
25
+ empty,
26
+ plainTextOffset,
27
+ selectedRange
28
+ };
29
+ }
30
+ export function findOverlappingRangeMapEntries(rangeMap, plainTextOffset) {
31
+ return rangeMap.filter((entry) => {
32
+ const entryStart = entry.textStart;
33
+ const entryEnd = entry.textEnd;
34
+ const selStart = plainTextOffset.start;
35
+ const selEnd = plainTextOffset.end;
36
+ return selStart < entryEnd && selEnd > entryStart;
37
+ });
38
+ }
39
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../src/selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAY1E,MAAM,UAAU,qBAAqB,CAAC,MAKrC;IACC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;IACnD,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAElE,IAAI,eAAe,GAA0C,IAAI,CAAC;IAClE,IAAI,aAAa,GAAqB,IAAI,CAAC;IAE3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,SAAS,GAAG,yBAAyB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAU,CAAC,CAAC;QAErE,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAE9C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7D,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;YAC3B,eAAe,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;YAEnD,aAAa,GAAG;gBACd,KAAK,EAAE,QAAQ;gBACf,GAAG,EAAE,MAAM;gBACX,WAAW,EAAE,YAAY;aAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,YAAY;QACZ,IAAI;QACJ,EAAE;QACF,KAAK;QACL,eAAe;QACf,aAAa;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,QAAyB,EACzB,eAA+C;IAE/C,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC;QACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC;QAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC;QACvC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC;QAEnC,OAAO,QAAQ,GAAG,QAAQ,IAAI,MAAM,GAAG,UAAU,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/types.d.ts CHANGED
@@ -89,3 +89,15 @@ export interface EditorChangePayload {
89
89
  plainText: string;
90
90
  json?: unknown;
91
91
  }
92
+ export interface DocumentSelectionChangePayload {
93
+ documentId: string;
94
+ selectedText: string;
95
+ from: number;
96
+ to: number;
97
+ empty: boolean;
98
+ plainTextOffset: {
99
+ start: number;
100
+ end: number;
101
+ } | null;
102
+ selectedRange: TextRange | null;
103
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhl548/duplicate-doc-core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,6 +11,10 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js"
13
13
  },
14
+ "./highlight-resolution": {
15
+ "types": "./dist/highlightResolution.d.ts",
16
+ "import": "./dist/highlightResolution.js"
17
+ },
14
18
  "./style.css": "./src/style.css"
15
19
  },
16
20
  "files": [
package/src/style.css CHANGED
@@ -29,3 +29,53 @@
29
29
  opacity: 0.62;
30
30
  border-bottom-style: dashed;
31
31
  }
32
+
33
+ .dupdoc-selection-popup {
34
+ position: fixed;
35
+ z-index: 1000;
36
+ background: #ffffff;
37
+ border: 1px solid rgba(15, 23, 42, 0.12);
38
+ border-radius: 12px;
39
+ box-shadow: 0 12px 40px rgba(15, 23, 42, 0.14);
40
+ overflow: auto;
41
+ }
42
+
43
+ .dupdoc-selection-popup__header {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: space-between;
47
+ padding: 10px 14px;
48
+ border-bottom: 1px solid rgba(15, 23, 42, 0.08);
49
+ font-size: 13px;
50
+ font-weight: 600;
51
+ color: #344054;
52
+ background: #f9fafb;
53
+ }
54
+
55
+ .dupdoc-selection-popup__close {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ width: 20px;
60
+ height: 20px;
61
+ border: 0;
62
+ border-radius: 4px;
63
+ color: #667085;
64
+ background: transparent;
65
+ cursor: pointer;
66
+ font-size: 14px;
67
+ }
68
+
69
+ .dupdoc-selection-popup__close:hover {
70
+ background: rgba(15, 23, 42, 0.06);
71
+ color: #1f2937;
72
+ }
73
+
74
+ .dupdoc-selection-popup__body {
75
+ padding: 12px 14px;
76
+ font-size: 13px;
77
+ line-height: 1.6;
78
+ color: #475467;
79
+ max-height: 60vh;
80
+ overflow: auto;
81
+ }