rayo-editor 0.0.1

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.
Files changed (43) hide show
  1. package/README.md +563 -0
  2. package/dist/components/BlogEditor.d.ts +4 -0
  3. package/dist/components/BlogEditor.d.ts.map +1 -0
  4. package/dist/components/DiffOverlay.d.ts +4 -0
  5. package/dist/components/DiffOverlay.d.ts.map +1 -0
  6. package/dist/components/ImageGenerationLoader.d.ts +5 -0
  7. package/dist/components/ImageGenerationLoader.d.ts.map +1 -0
  8. package/dist/components/RayoEditor.d.ts +74 -0
  9. package/dist/components/RayoEditor.d.ts.map +1 -0
  10. package/dist/components/ReviewButtons.d.ts +4 -0
  11. package/dist/components/ReviewButtons.d.ts.map +1 -0
  12. package/dist/components/TitleTextarea.d.ts +4 -0
  13. package/dist/components/TitleTextarea.d.ts.map +1 -0
  14. package/dist/components/index.d.ts +7 -0
  15. package/dist/components/index.d.ts.map +1 -0
  16. package/dist/hooks/index.d.ts +3 -0
  17. package/dist/hooks/index.d.ts.map +1 -0
  18. package/dist/hooks/useContentProcessing.d.ts +83 -0
  19. package/dist/hooks/useContentProcessing.d.ts.map +1 -0
  20. package/dist/hooks/useEditorDiff.d.ts +65 -0
  21. package/dist/hooks/useEditorDiff.d.ts.map +1 -0
  22. package/dist/index.d.ts +213 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.mjs +1011 -0
  25. package/dist/index.umd.js +32 -0
  26. package/dist/styles.css +1150 -0
  27. package/dist/types/diff.types.d.ts +84 -0
  28. package/dist/types/diff.types.d.ts.map +1 -0
  29. package/dist/types/editor.types.d.ts +59 -0
  30. package/dist/types/editor.types.d.ts.map +1 -0
  31. package/dist/utils/contentProcessing.d.ts +81 -0
  32. package/dist/utils/contentProcessing.d.ts.map +1 -0
  33. package/dist/utils/diffDetection.d.ts +85 -0
  34. package/dist/utils/diffDetection.d.ts.map +1 -0
  35. package/dist/utils/errorHandling.d.ts +57 -0
  36. package/dist/utils/errorHandling.d.ts.map +1 -0
  37. package/dist/utils/imageHandling.d.ts +52 -0
  38. package/dist/utils/imageHandling.d.ts.map +1 -0
  39. package/dist/utils/index.d.ts +6 -0
  40. package/dist/utils/index.d.ts.map +1 -0
  41. package/dist/utils/proximityMatching.d.ts +80 -0
  42. package/dist/utils/proximityMatching.d.ts.map +1 -0
  43. package/package.json +70 -0
@@ -0,0 +1,84 @@
1
+ export interface DiffRange {
2
+ from: number;
3
+ to: number;
4
+ }
5
+ export interface DiffRect {
6
+ top: number;
7
+ left: number;
8
+ width: number;
9
+ bottom: number;
10
+ right: number;
11
+ }
12
+ export interface DiffPair {
13
+ redRange?: DiffRange;
14
+ greenRange?: DiffRange;
15
+ rect: DiffRect;
16
+ lastGreenRect: DiffRect;
17
+ isTextOnly?: boolean;
18
+ isImageDeletion?: boolean;
19
+ isImageInsertion?: boolean;
20
+ isImageReplacement?: boolean;
21
+ isTableDeletion?: boolean;
22
+ isTableInsertion?: boolean;
23
+ isTableReplacement?: boolean;
24
+ isCodeBlockDeletion?: boolean;
25
+ isCodeBlockInsertion?: boolean;
26
+ isCodeBlockReplacement?: boolean;
27
+ includesImageDeletion?: boolean;
28
+ includesImageInsertion?: boolean;
29
+ isImageReplacedByText?: boolean;
30
+ isTextReplacedByImage?: boolean;
31
+ includesTableDeletion?: boolean;
32
+ includesTableInsertion?: boolean;
33
+ isTableReplacedByText?: boolean;
34
+ isTextReplacedByTable?: boolean;
35
+ tableIsBeforeText?: boolean;
36
+ includesCodeBlockDeletion?: boolean;
37
+ includesCodeBlockInsertion?: boolean;
38
+ isCodeBlockReplacedByText?: boolean;
39
+ isTextReplacedByCode?: boolean;
40
+ codeBlockIsBeforeText?: boolean;
41
+ includesTextInsertion?: boolean;
42
+ imageIsLastInPair?: boolean;
43
+ firstGreenBlockEnd?: number;
44
+ newImagesCount?: number;
45
+ }
46
+ export interface DiffMarker {
47
+ type: 'insertion' | 'deletion' | 'highlight';
48
+ color?: string;
49
+ from?: number;
50
+ to?: number;
51
+ }
52
+ export interface DiffMarkerResult {
53
+ hasDiffs: boolean;
54
+ markers: DiffMarker[];
55
+ }
56
+ export interface ImageOperation {
57
+ pos: number;
58
+ nodeSize: number;
59
+ rect: DiffRect;
60
+ pendingDelete?: boolean;
61
+ pendingInsert?: boolean;
62
+ }
63
+ export interface TableOperation {
64
+ pos: number;
65
+ nodeSize: number;
66
+ rect: DiffRect;
67
+ pendingDelete?: boolean;
68
+ pendingInsert?: boolean;
69
+ }
70
+ export interface CodeBlockOperation {
71
+ pos: number;
72
+ nodeSize: number;
73
+ rect: DiffRect;
74
+ language?: string;
75
+ pendingDelete?: boolean;
76
+ pendingInsert?: boolean;
77
+ }
78
+ export interface HighlightInfo {
79
+ from: number;
80
+ to: number;
81
+ color: '#c7f0d6ff' | '#fecaca';
82
+ rect: DiffRect;
83
+ }
84
+ //# sourceMappingURL=diff.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.types.d.ts","sourceRoot":"","sources":["../../src/types/diff.types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { RefObject } from 'react';
2
+ import { DiffRange, DiffPair } from './diff.types';
3
+ type Editor = any;
4
+ export interface BlogSimpleEditorRef {
5
+ getEditor: () => Editor | null;
6
+ }
7
+ export interface RayoEditorProps {
8
+ content: string;
9
+ title: string;
10
+ onChange: (content: string) => void;
11
+ onTitleChange?: (title: string) => void;
12
+ isLoading: boolean;
13
+ isStreaming?: boolean;
14
+ isAgentThinking?: boolean;
15
+ readOnly?: boolean;
16
+ focusMode?: boolean;
17
+ pendingChanges?: boolean;
18
+ showDiffs?: boolean;
19
+ hideReviewUI?: boolean;
20
+ editedLinesCount?: number;
21
+ onAcceptChanges?: () => void;
22
+ onRejectChanges?: () => void;
23
+ onAcceptSingleChange?: (greenRange: DiffRange, redRange?: DiffRange) => void;
24
+ onRejectSingleChange?: (greenRange: DiffRange, redRange?: DiffRange) => void;
25
+ onDiffPairsChange?: (diffPairs: DiffPair[]) => void;
26
+ featuredImageUrl?: string;
27
+ onEditFeaturedImage?: () => void;
28
+ isGeneratingImage?: boolean;
29
+ editorRef: RefObject<BlogSimpleEditorRef>;
30
+ onAriScoreChange?: (score: number) => void;
31
+ disableAutoScroll?: boolean;
32
+ onUserScrollChange?: (isScrolledUp: boolean) => void;
33
+ showToolbarAnimation?: boolean;
34
+ streamingPhase?: string;
35
+ }
36
+ export interface TitleTextareaProps {
37
+ title: string;
38
+ onTitleChange?: (title: string) => void;
39
+ readOnly?: boolean;
40
+ }
41
+ export interface DiffOverlayProps {
42
+ diffPairs: DiffPair[];
43
+ overlayHoleRect: {
44
+ top: number;
45
+ bottom: number;
46
+ } | null;
47
+ onPairHover?: (index: number) => void;
48
+ onPairClick?: (index: number) => void;
49
+ }
50
+ export interface ReviewButtonsProps {
51
+ onAccept: () => void;
52
+ onReject: () => void;
53
+ position: {
54
+ top: number;
55
+ left: number;
56
+ };
57
+ }
58
+ export {};
59
+ //# sourceMappingURL=editor.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.types.d.ts","sourceRoot":"","sources":["../../src/types/editor.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGnD,KAAK,MAAM,GAAG,GAAG,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAE9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAGxC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IAGpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7E,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7E,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAGpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,SAAS,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC"}
@@ -0,0 +1,81 @@
1
+ import { DiffRange } from '../types/diff.types';
2
+ /**
3
+ * Merges consecutive or overlapping ranges into single ranges
4
+ *
5
+ * Sorts ranges by start position and combines any that touch or overlap.
6
+ * Useful for consolidating multiple small diffs into larger blocks.
7
+ *
8
+ * @param ranges - Array of DiffRange objects to merge
9
+ * @returns Array of merged DiffRange objects (sorted by position)
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const ranges = [
14
+ * { from: 0, to: 5 },
15
+ * { from: 5, to: 10 }, // consecutive
16
+ * { from: 8, to: 15 } // overlapping
17
+ * ];
18
+ * const merged = mergeConsecutiveRanges(ranges);
19
+ * // Returns: [{ from: 0, to: 15 }]
20
+ * ```
21
+ *
22
+ * @example
23
+ * Optimize diff highlighting:
24
+ * ```tsx
25
+ * const diffRanges = detectAndExtractDiffs(content);
26
+ * const optimized = mergeConsecutiveRanges(diffRanges);
27
+ * renderHighlights(optimized);
28
+ * ```
29
+ */
30
+ export declare function mergeConsecutiveRanges(ranges: DiffRange[]): DiffRange[];
31
+ /**
32
+ * Extracts text content from a given range within a string
33
+ *
34
+ * Simple substring extraction based on position range.
35
+ * Returns empty string if range is invalid or out of bounds.
36
+ *
37
+ * @param content - The full text content
38
+ * @param range - The range defining start and end positions
39
+ * @returns The extracted text substring
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * const text = 'Hello World';
44
+ * const range = { from: 0, to: 5 };
45
+ * extractTextContent(text, range); // 'Hello'
46
+ * ```
47
+ */
48
+ export declare function extractTextContent(content: string, range: DiffRange): string;
49
+ /**
50
+ * Optimizes ranges by merging consecutive ranges and filtering out empty ranges
51
+ *
52
+ * Performs two optimizations:
53
+ * 1. Filters out empty ranges where from === to
54
+ * 2. Merges all consecutive/overlapping ranges
55
+ *
56
+ * Use before rendering to ensure efficient diff highlighting.
57
+ *
58
+ * @param ranges - Array of DiffRange objects to optimize
59
+ * @returns Array of optimized DiffRange objects (minimal, non-overlapping)
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * const ranges = [
64
+ * { from: 0, to: 0 }, // empty - filtered
65
+ * { from: 5, to: 10 },
66
+ * { from: 10, to: 15 } // consecutive - merged
67
+ * ];
68
+ * const optimized = optimizeRanges(ranges);
69
+ * // Returns: [{ from: 5, to: 15 }]
70
+ * ```
71
+ *
72
+ * @example
73
+ * Before rendering highlights:
74
+ * ```tsx
75
+ * const detected = extractDiffRanges(content);
76
+ * const optimized = optimizeRanges(detected);
77
+ * return <DiffOverlay ranges={optimized} />;
78
+ * ```
79
+ */
80
+ export declare function optimizeRanges(ranges: DiffRange[]): DiffRange[];
81
+ //# sourceMappingURL=contentProcessing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentProcessing.d.ts","sourceRoot":"","sources":["../../src/utils/contentProcessing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CA6BvE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAM/D"}
@@ -0,0 +1,85 @@
1
+ import { DiffMarkerResult } from '../types/diff.types';
2
+ /**
3
+ * Detect diff markers in HTML content
4
+ *
5
+ * Identifies insertions, deletions, and highlights using multiple detection methods:
6
+ * - <ins> and <del> HTML tags
7
+ * - data-color attributes for green (#c7f0d6ff) and red (#fecaca) highlights
8
+ * - data-pending-insert and data-pending-delete attributes
9
+ *
10
+ * @param content - HTML content to scan for diff markers
11
+ * @returns Result object with hasDiffs flag and array of markers found
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const content = '<ins>new text</ins> and <del>old text</del>';
16
+ * const { hasDiffs, markers } = detectDiffMarkers(content);
17
+ * console.log(hasDiffs); // true
18
+ * console.log(markers.length); // 2
19
+ * ```
20
+ *
21
+ * @example
22
+ * Check for specific highlight colors:
23
+ * ```tsx
24
+ * const { hasDiffs, markers } = detectDiffMarkers(content);
25
+ * const greenMarkers = markers.filter(m => m.color === '#c7f0d6ff');
26
+ * const redMarkers = markers.filter(m => m.color === '#fecaca');
27
+ * ```
28
+ */
29
+ export declare const detectDiffMarkers: (content: string) => DiffMarkerResult;
30
+ /**
31
+ * Normalize diff text for consistent comparison
32
+ *
33
+ * Performs Unicode normalization and standardization:
34
+ * - Removes diacritics (é → e, ñ → n)
35
+ * - Normalizes dash variants (em-dash, en-dash → hyphen)
36
+ * - Standardizes whitespace (non-breaking space, em-space → regular space)
37
+ * - Normalizes quote variants (curly quotes → straight quotes)
38
+ * - Removes zero-width characters
39
+ * - Collapses multiple spaces into single space
40
+ *
41
+ * @param text - Text to normalize
42
+ * @returns Normalized text suitable for comparison
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const text = 'Café — hello\u00A0world';
47
+ * const normalized = normalizeDiffText(text);
48
+ * console.log(normalized); // 'Cafe - hello world'
49
+ * ```
50
+ *
51
+ * @example
52
+ * Compare diff texts reliably:
53
+ * ```tsx
54
+ * const oldText = 'Résumé – 2026';
55
+ * const newText = 'Resume - 2026';
56
+ * normalizeDiffText(oldText) === normalizeDiffText(newText); // true
57
+ * ```
58
+ */
59
+ export declare const normalizeDiffText: (text: string) => string;
60
+ /**
61
+ * Extract diff ranges from HTML content
62
+ *
63
+ * Parses content to find all diff ranges and returns them as position pairs.
64
+ * Can filter by type to get only insertions, deletions, or all changes.
65
+ *
66
+ * @param _content - HTML content to extract ranges from
67
+ * @param _type - Optional filter: 'all' (default), 'green' (additions), 'red' (deletions)
68
+ * @returns Array of {from, to} ranges indicating diff locations
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * const content = '<ins>new</ins> text <del>old</del>';
73
+ * const ranges = extractDiffRanges(content);
74
+ * // Returns positions of insertions and deletions
75
+ * ```
76
+ *
77
+ * @example
78
+ * Filter by type:
79
+ * ```tsx
80
+ * const additions = extractDiffRanges(content, 'green');
81
+ * const deletions = extractDiffRanges(content, 'red');
82
+ * ```
83
+ */
84
+ export declare const extractDiffRanges: (_content: string) => never[];
85
+ //# sourceMappingURL=diffDetection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffDetection.d.ts","sourceRoot":"","sources":["../../src/utils/diffDetection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,gBAAgB,EAAc,MAAM,oBAAoB,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,KAAG,gBA2DnD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAsBhD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,YAGjD,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Custom error class for diff processing failures
3
+ *
4
+ * Extends Error with additional context information for better debugging.
5
+ * Useful for distinguishing diff processing errors from other exceptions.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * try {
10
+ * processComplexDiff(content);
11
+ * } catch (error) {
12
+ * if (error instanceof DiffProcessingError) {
13
+ * console.error('Diff error:', error.message);
14
+ * console.error('Context:', error.context);
15
+ * }
16
+ * }
17
+ * ```
18
+ */
19
+ export declare class DiffProcessingError extends Error {
20
+ context?: any | undefined;
21
+ /**
22
+ * Create a new DiffProcessingError
23
+ * @param message - Error message
24
+ * @param context - Additional context object for debugging
25
+ */
26
+ constructor(message: string, context?: any | undefined);
27
+ }
28
+ /**
29
+ * Safely execute a function with error handling and fallback
30
+ *
31
+ * Wraps function execution in try-catch and returns fallback value on error.
32
+ * Logs warnings but doesn't throw, making it safe for non-critical operations.
33
+ *
34
+ * @template T - The return type of the function
35
+ * @param fn - Function to execute safely
36
+ * @param fallback - Fallback value if function throws
37
+ * @returns Result of fn() or fallback value
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * const result = safeExecute(
42
+ * () => expensiveOperation(data),
43
+ * defaultValue
44
+ * );
45
+ * ```
46
+ *
47
+ * @example
48
+ * Safe diff detection:
49
+ * ```tsx
50
+ * const markers = safeExecute(
51
+ * () => detectDiffMarkers(content),
52
+ * { hasDiffs: false, markers: [] }
53
+ * );
54
+ * ```
55
+ */
56
+ export declare const safeExecute: <T>(fn: () => T, fallback: T) => T;
57
+ //# sourceMappingURL=errorHandling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandling.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAMR,OAAO,CAAC,EAAE,GAAG;IALjD;;;;OAIG;gBACS,OAAO,EAAE,MAAM,EAAS,OAAO,CAAC,EAAE,GAAG,YAAA;CAIlD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,UAAU,CAAC,KAAG,CAOzD,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { ImageOperation, DiffPair } from '../types/diff.types';
2
+ /**
3
+ * Group consecutive images in content
4
+ *
5
+ * Identifies images that appear consecutively in content and groups them together.
6
+ * Useful for batch processing image changes or detecting image sequences.
7
+ *
8
+ * @param images - Array of ImageOperation objects
9
+ * @returns Array of groups, where each group contains consecutive images
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const images = [
14
+ * { pos: 0, nodeSize: 10, rect: {...} },
15
+ * { pos: 10, nodeSize: 12, rect: {...} }, // consecutive
16
+ * { pos: 25, nodeSize: 10, rect: {...} } // separate group
17
+ * ];
18
+ * const groups = groupConsecutiveImages(images);
19
+ * // Returns: [[image1, image2], [image3]]
20
+ * ```
21
+ */
22
+ export declare const groupConsecutiveImages: (images: ImageOperation[]) => ImageOperation[][];
23
+ /**
24
+ * Match image replacements in diff pairs
25
+ *
26
+ * Identifies images that were deleted and replaced with new images.
27
+ * Matches insertions immediately following deletions as replacements.
28
+ * Handles multiple image replacements in sequence.
29
+ *
30
+ * @param deletions - Array of deleted ImageOperation objects
31
+ * @param insertions - Array of inserted ImageOperation objects
32
+ * @returns Array of DiffPair objects representing image changes
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const deleted = [{ pos: 0, nodeSize: 10, rect: {...} }];
37
+ * const inserted = [{ pos: 10, nodeSize: 12, rect: {...} }];
38
+ * const pairs = matchImageReplacements(deleted, inserted);
39
+ * // Returns: [{ redRange, greenRange, isImageReplacement: true }]
40
+ * ```
41
+ *
42
+ * @example
43
+ * Handle image deletion without replacement:
44
+ * ```tsx
45
+ * const deleted = [{ pos: 0, nodeSize: 10, rect: {...} }];
46
+ * const inserted: ImageOperation[] = [];
47
+ * const pairs = matchImageReplacements(deleted, inserted);
48
+ * // Returns: [{ redRange, greenRange: undefined, isImageDeletion: true }]
49
+ * ```
50
+ */
51
+ export declare const matchImageReplacements: (deletions: ImageOperation[], insertions: ImageOperation[]) => DiffPair[];
52
+ //# sourceMappingURL=imageHandling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageHandling.d.ts","sourceRoot":"","sources":["../../src/utils/imageHandling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,cAAc,EAAE,KAAG,cAAc,EAAE,EA0BjF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,sBAAsB,GACjC,WAAW,cAAc,EAAE,EAC3B,YAAY,cAAc,EAAE,KAC3B,QAAQ,EA4CV,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { detectDiffMarkers, normalizeDiffText, extractDiffRanges } from './diffDetection';
2
+ export { mergeConsecutiveRanges, extractTextContent, optimizeRanges } from './contentProcessing';
3
+ export { groupConsecutiveImages, matchImageReplacements } from './imageHandling';
4
+ export { calculateProximity, findOwnerTextPair, groupConsecutiveItems } from './proximityMatching';
5
+ export { DiffProcessingError, safeExecute } from './errorHandling';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { DiffRange, DiffPair } from '../types/diff.types';
2
+ /**
3
+ * Calculate proximity score between two text ranges
4
+ *
5
+ * Calculates the minimum distance between two ranges in characters.
6
+ * Returns 0 if ranges are adjacent or overlapping, larger values indicate
7
+ * greater distance.
8
+ *
9
+ * @param range1 - First text range
10
+ * @param range2 - Second text range
11
+ * @returns Distance in characters (0 means adjacent/overlapping)
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const range1 = { from: 0, to: 10 };
16
+ * const range2 = { from: 10, to: 20 };
17
+ * calculateProximity(range1, range2); // 0 - adjacent
18
+ *
19
+ * const range3 = { from: 100, to: 110 };
20
+ * calculateProximity(range1, range3); // 90 - far apart
21
+ * ```
22
+ */
23
+ export declare const calculateProximity: (range1: DiffRange, range2: DiffRange) => number;
24
+ /**
25
+ * Find the best matching diff pair for a given block position
26
+ *
27
+ * Searches for the diff pair closest to the specified block position.
28
+ * Useful for associating non-text blocks (images, tables) with their
29
+ * corresponding text changes.
30
+ *
31
+ * @param blockPos - Position of the block to match
32
+ * @param _blockEndPos - End position of the block (unused)
33
+ * @param textPairs - Array of DiffPair objects to search
34
+ * @returns The closest DiffPair or null if none found
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const pairs = [
39
+ * { redRange: { from: 0, to: 10 }, greenRange: { from: 0, to: 15 } },
40
+ * { redRange: { from: 50, to: 60 }, greenRange: { from: 50, to: 65 } }
41
+ * ];
42
+ * const imagePos = 52;
43
+ * const owner = findOwnerTextPair(imagePos, 62, pairs);
44
+ * // Returns the second pair as it's closest to imagePos
45
+ * ```
46
+ */
47
+ export declare const findOwnerTextPair: (blockPos: number, _blockEndPos: number, textPairs: DiffPair[]) => DiffPair | null;
48
+ /**
49
+ * Group consecutive items by position
50
+ *
51
+ * Groups items that appear consecutively in content based on pos and nodeSize.
52
+ * Items are consecutive if: item.pos === previous.pos + previous.nodeSize
53
+ *
54
+ * @template T - Item type (must have pos and nodeSize properties)
55
+ * @param items - Array of items to group
56
+ * @returns Array of groups, each containing consecutive items
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * const items = [
61
+ * { pos: 0, nodeSize: 10 },
62
+ * { pos: 10, nodeSize: 5 }, // consecutive
63
+ * { pos: 20, nodeSize: 10 } // separate group
64
+ * ];
65
+ * const groups = groupConsecutiveItems(items);
66
+ * // Returns: [[item1, item2], [item3]]
67
+ * ```
68
+ *
69
+ * @example
70
+ * Group items by position:
71
+ * ```typescript
72
+ * const items = [{ pos: 5, nodeSize: 20 }, { pos: 25, nodeSize: 15 }];
73
+ * const groups = groupConsecutiveItems(items);
74
+ * ```
75
+ */
76
+ export declare const groupConsecutiveItems: <T extends {
77
+ pos: number;
78
+ nodeSize: number;
79
+ }>(items: T[]) => T[][];
80
+ //# sourceMappingURL=proximityMatching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proximityMatching.d.ts","sourceRoot":"","sources":["../../src/utils/proximityMatching.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,SAAS,KAAG,MAIzE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,MAAM,EAChB,cAAc,MAAM,EACpB,WAAW,QAAQ,EAAE,KACpB,QAAQ,GAAG,IAqBb,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC/E,OAAO,CAAC,EAAE,KACT,CAAC,EAAE,EAqBL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "rayo-editor",
3
+ "version": "0.0.1",
4
+ "description": "Standalone npm module for Rayo Blog Editor",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.umd.js"
14
+ },
15
+ "./dist/styles.css": "./dist/styles.css"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "dev": "vite",
22
+ "build": "tsc && vite build && node scripts/copy-styles.js",
23
+ "test": "vitest",
24
+ "test:ui": "vitest --ui",
25
+ "test:coverage": "vitest --coverage",
26
+ "lint": "eslint src --ext .ts,.tsx",
27
+ "type-check": "tsc --noEmit"
28
+ },
29
+ "keywords": [
30
+ "rayo",
31
+ "editor",
32
+ "blog",
33
+ "rich-text"
34
+ ],
35
+ "author": "Rayo Team",
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "@tiptap/react": "^3.20.1"
39
+ },
40
+ "devDependencies": {
41
+ "@testing-library/jest-dom": "^6.0.0",
42
+ "@testing-library/react": "^14.0.0",
43
+ "@types/node": "^20.0.0",
44
+ "@types/react": "^18.0.0",
45
+ "@types/react-dom": "^18.0.0",
46
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
47
+ "@typescript-eslint/parser": "^6.0.0",
48
+ "@vitejs/plugin-react": "^4.0.0",
49
+ "@vitest/coverage-v8": "^0.34.6",
50
+ "@vitest/ui": "^0.34.0",
51
+ "eslint": "^8.0.0",
52
+ "jsdom": "^28.1.0",
53
+ "typescript": "^5.0.0",
54
+ "vite": "^4.0.0",
55
+ "vite-plugin-dts": "^4.5.4",
56
+ "vitest": "^0.34.0"
57
+ },
58
+ "peerDependencies": {
59
+ "react": "^18.0.0",
60
+ "react-dom": "^18.0.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "react": {
64
+ "optional": false
65
+ },
66
+ "react-dom": {
67
+ "optional": false
68
+ }
69
+ }
70
+ }