dyelight 1.2.0 → 1.2.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.
- package/dist/index.d.mts +102 -91
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React$1 from "react";
|
|
2
2
|
|
|
3
3
|
//#region src/builder.d.ts
|
|
4
4
|
/**
|
|
@@ -26,12 +26,12 @@ declare const HighlightBuilder: {
|
|
|
26
26
|
characters: (chars: Array<{
|
|
27
27
|
className?: string;
|
|
28
28
|
index: number;
|
|
29
|
-
style?: React.CSSProperties;
|
|
29
|
+
style?: React$1.CSSProperties;
|
|
30
30
|
}>) => {
|
|
31
31
|
className: string | undefined;
|
|
32
32
|
end: number;
|
|
33
33
|
start: number;
|
|
34
|
-
style: React.CSSProperties | undefined;
|
|
34
|
+
style: React$1.CSSProperties | undefined;
|
|
35
35
|
}[];
|
|
36
36
|
/**
|
|
37
37
|
* Creates line highlights for entire lines
|
|
@@ -80,11 +80,11 @@ declare const HighlightBuilder: {
|
|
|
80
80
|
* );
|
|
81
81
|
* ```
|
|
82
82
|
*/
|
|
83
|
-
pattern: (text: string, pattern: RegExp | string, className?: string, style?: React.CSSProperties) => {
|
|
83
|
+
pattern: (text: string, pattern: RegExp | string, className?: string, style?: React$1.CSSProperties) => {
|
|
84
84
|
className: string | undefined;
|
|
85
85
|
end: number;
|
|
86
86
|
start: number;
|
|
87
|
-
style: React.CSSProperties | undefined;
|
|
87
|
+
style: React$1.CSSProperties | undefined;
|
|
88
88
|
}[];
|
|
89
89
|
/**
|
|
90
90
|
* Creates character range highlights using absolute positions
|
|
@@ -108,12 +108,12 @@ declare const HighlightBuilder: {
|
|
|
108
108
|
className?: string;
|
|
109
109
|
end: number;
|
|
110
110
|
start: number;
|
|
111
|
-
style?: React.CSSProperties;
|
|
111
|
+
style?: React$1.CSSProperties;
|
|
112
112
|
}>) => {
|
|
113
113
|
className: string | undefined;
|
|
114
114
|
end: number;
|
|
115
115
|
start: number;
|
|
116
|
-
style: React.CSSProperties | undefined;
|
|
116
|
+
style: React$1.CSSProperties | undefined;
|
|
117
117
|
}[];
|
|
118
118
|
/**
|
|
119
119
|
* Highlights text between specific start and end positions
|
|
@@ -133,11 +133,11 @@ declare const HighlightBuilder: {
|
|
|
133
133
|
* );
|
|
134
134
|
* ```
|
|
135
135
|
*/
|
|
136
|
-
selection: (start: number, end: number, className?: string, style?: React.CSSProperties) => {
|
|
136
|
+
selection: (start: number, end: number, className?: string, style?: React$1.CSSProperties) => {
|
|
137
137
|
className: string | undefined;
|
|
138
138
|
end: number;
|
|
139
139
|
start: number;
|
|
140
|
-
style: React.CSSProperties | undefined;
|
|
140
|
+
style: React$1.CSSProperties | undefined;
|
|
141
141
|
}[];
|
|
142
142
|
/**
|
|
143
143
|
* Highlights entire words that match specific terms
|
|
@@ -164,11 +164,11 @@ declare const HighlightBuilder: {
|
|
|
164
164
|
* );
|
|
165
165
|
* ```
|
|
166
166
|
*/
|
|
167
|
-
words: (text: string, words: string[], className?: string, style?: React.CSSProperties) => {
|
|
167
|
+
words: (text: string, words: string[], className?: string, style?: React$1.CSSProperties) => {
|
|
168
168
|
className: string | undefined;
|
|
169
169
|
end: number;
|
|
170
170
|
start: number;
|
|
171
|
-
style: React.CSSProperties | undefined;
|
|
171
|
+
style: React$1.CSSProperties | undefined;
|
|
172
172
|
}[];
|
|
173
173
|
};
|
|
174
174
|
//#endregion
|
|
@@ -189,7 +189,7 @@ type CharacterRange = {
|
|
|
189
189
|
/** Optional CSS class name to apply to the highlighted range */className?: string; /** Zero-based end index in the entire text (exclusive) */
|
|
190
190
|
end: number; /** Zero-based start index in the entire text (inclusive) */
|
|
191
191
|
start: number; /** Optional inline styles to apply to the highlighted range */
|
|
192
|
-
style?: React.CSSProperties;
|
|
192
|
+
style?: React$1.CSSProperties;
|
|
193
193
|
};
|
|
194
194
|
/**
|
|
195
195
|
* Props for the DyeLight component
|
|
@@ -225,7 +225,7 @@ type CharacterRange = {
|
|
|
225
225
|
* };
|
|
226
226
|
* ```
|
|
227
227
|
*/
|
|
228
|
-
interface DyeLightProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
|
|
228
|
+
interface DyeLightProps extends Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
|
|
229
229
|
/** CSS class name for the textarea element */
|
|
230
230
|
className?: string;
|
|
231
231
|
/** CSS class name for the container wrapper element */
|
|
@@ -294,76 +294,14 @@ type DyeLightRef = {
|
|
|
294
294
|
select: () => void; /** Sets the selection range in the textarea */
|
|
295
295
|
setSelectionRange: (start: number, end: number) => void; /** Sets the value of the textarea programmatically */
|
|
296
296
|
setValue: (value: string) => void; /** Scrolls the character position into view with an optional pixel offset */
|
|
297
|
-
scrollToPosition: (pos: number, offset?: number, behavior?: ScrollBehavior) => void;
|
|
297
|
+
scrollToPosition: (pos: number, offset?: number, behavior?: ScrollBehavior) => void;
|
|
298
|
+
/**
|
|
299
|
+
* Exports AI-optimized debug report as JSON string (requires debug mode)
|
|
300
|
+
* The report includes value deduplication - large text values are stored once
|
|
301
|
+
* in a valueRegistry and referenced as <REF:value_N> to reduce JSON size
|
|
302
|
+
*/
|
|
298
303
|
exportForAI: () => string;
|
|
299
304
|
};
|
|
300
|
-
//#endregion
|
|
301
|
-
//#region src/DyeLight.d.ts
|
|
302
|
-
/**
|
|
303
|
-
* @fileoverview DyeLight - A React textarea component with advanced text highlighting capabilities
|
|
304
|
-
*
|
|
305
|
-
* This component provides a textarea with overlay-based text highlighting that supports:
|
|
306
|
-
* - Character-level highlighting using absolute text positions
|
|
307
|
-
* - Line-level highlighting with CSS classes or color values
|
|
308
|
-
* - Automatic height adjustment based on content
|
|
309
|
-
* - Synchronized scrolling between textarea and highlight layer
|
|
310
|
-
* - Both controlled and uncontrolled usage patterns
|
|
311
|
-
* - RTL text direction support
|
|
312
|
-
*/
|
|
313
|
-
/**
|
|
314
|
-
* Creates a line element with optional highlighting
|
|
315
|
-
*/
|
|
316
|
-
declare const createLineElement: (content: React.ReactNode, lineIndex: number, lineHighlight?: string) => React.ReactElement;
|
|
317
|
-
/**
|
|
318
|
-
* Renders a single line with character-level highlights and optional line-level highlighting
|
|
319
|
-
*/
|
|
320
|
-
declare const renderHighlightedLine: (line: string, lineIndex: number, ranges: Array<{
|
|
321
|
-
absoluteStart: number;
|
|
322
|
-
className?: string;
|
|
323
|
-
end: number;
|
|
324
|
-
start: number;
|
|
325
|
-
style?: React.CSSProperties;
|
|
326
|
-
}>, lineHighlight?: string) => React.ReactElement;
|
|
327
|
-
/**
|
|
328
|
-
* A textarea component with support for highlighting character ranges using absolute positions
|
|
329
|
-
* and optional line-level highlighting. Perfect for syntax highlighting, error indication,
|
|
330
|
-
* and text annotation without the complexity of line-based positioning.
|
|
331
|
-
*/
|
|
332
|
-
declare const DyeLight: React.ForwardRefExoticComponent<DyeLightProps & React.RefAttributes<DyeLightRef>>;
|
|
333
|
-
//#endregion
|
|
334
|
-
//#region src/domUtils.d.ts
|
|
335
|
-
/**
|
|
336
|
-
* @fileoverview DOM utility functions for textarea manipulation
|
|
337
|
-
*
|
|
338
|
-
* This module provides utility functions for working with DOM elements,
|
|
339
|
-
* specifically focused on textarea auto-resizing functionality used by
|
|
340
|
-
* the DyeLight component.
|
|
341
|
-
*/
|
|
342
|
-
/**
|
|
343
|
-
* Automatically resizes a textarea element to fit its content
|
|
344
|
-
*
|
|
345
|
-
* This function adjusts the textarea height to match its scroll height,
|
|
346
|
-
* effectively removing scrollbars when the content fits and expanding
|
|
347
|
-
* the textarea as content is added. The height is first set to 'auto'
|
|
348
|
-
* to allow the element to shrink if content is removed.
|
|
349
|
-
*
|
|
350
|
-
* @param textArea - The HTML textarea element to resize
|
|
351
|
-
* @example
|
|
352
|
-
* ```ts
|
|
353
|
-
* const textarea = document.querySelector('textarea');
|
|
354
|
-
* if (textarea) {
|
|
355
|
-
* autoResize(textarea);
|
|
356
|
-
* }
|
|
357
|
-
*
|
|
358
|
-
* // Or in an event handler:
|
|
359
|
-
* const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
360
|
-
* autoResize(e.target);
|
|
361
|
-
* };
|
|
362
|
-
* ```
|
|
363
|
-
*/
|
|
364
|
-
declare const autoResize: (textArea: HTMLTextAreaElement) => void;
|
|
365
|
-
//#endregion
|
|
366
|
-
//#region src/telemetry.d.ts
|
|
367
305
|
/**
|
|
368
306
|
* Represents a single telemetry event with full context
|
|
369
307
|
*/
|
|
@@ -376,7 +314,7 @@ type AITelemetryEvent = {
|
|
|
376
314
|
description: string; /** Event-specific data payload */
|
|
377
315
|
data: Record<string, unknown>; /** Complete state snapshot at the time of this event */
|
|
378
316
|
stateSnapshot: {
|
|
379
|
-
/** Actual value in the textarea DOM element */textareaValue: string; /** Value in React state */
|
|
317
|
+
/** Actual value in the textarea DOM element (may be <REF:value_N> if deduplicated) */textareaValue: string; /** Value in React state (may be <REF:value_N> if deduplicated) */
|
|
380
318
|
reactValue: string; /** Whether DOM and React state are synchronized */
|
|
381
319
|
valuesMatch: boolean; /** Current height of the textarea in pixels */
|
|
382
320
|
textareaHeight: number | undefined; /** Whether the component is in controlled mode */
|
|
@@ -398,8 +336,7 @@ type AIDebugReport = {
|
|
|
398
336
|
durationMs: number;
|
|
399
337
|
}; /** Browser user agent string */
|
|
400
338
|
browser: string; /** Platform identifier */
|
|
401
|
-
platform: string;
|
|
402
|
-
reactVersion: string;
|
|
339
|
+
platform: string;
|
|
403
340
|
}; /** Analysis summary with detected issues */
|
|
404
341
|
summary: {
|
|
405
342
|
/** High-level description of findings */description: string; /** List of detected issues sorted by severity */
|
|
@@ -421,8 +358,8 @@ type AIDebugReport = {
|
|
|
421
358
|
}>; /** All state mutations in chronological order */
|
|
422
359
|
stateChanges: Array<{
|
|
423
360
|
/** When the change occurred */timestamp: string; /** Type of state change */
|
|
424
|
-
type: string; /** Previous value */
|
|
425
|
-
before: unknown; /** New value */
|
|
361
|
+
type: string; /** Previous value (may be <REF:value_N>) */
|
|
362
|
+
before: unknown; /** New value (may be <REF:value_N>) */
|
|
426
363
|
after: unknown; /** Whether this change was unexpected */
|
|
427
364
|
unexpected: boolean;
|
|
428
365
|
}>; /** Synchronization operations between textarea and overlay */
|
|
@@ -435,7 +372,7 @@ type AIDebugReport = {
|
|
|
435
372
|
}; /** Complete chronological list of all events */
|
|
436
373
|
events: AITelemetryEvent[]; /** Final state at time of export */
|
|
437
374
|
finalState: {
|
|
438
|
-
/** Current textarea DOM value */textareaValue: string; /** Current React state value */
|
|
375
|
+
/** Current textarea DOM value (may be <REF:value_N>) */textareaValue: string; /** Current React state value (may be <REF:value_N>) */
|
|
439
376
|
reactValue: string; /** Whether values are synchronized */
|
|
440
377
|
inSync: boolean; /** Current height in pixels */
|
|
441
378
|
height: number | undefined; /** Current scroll position */
|
|
@@ -445,7 +382,82 @@ type AIDebugReport = {
|
|
|
445
382
|
}; /** Current highlights configuration */
|
|
446
383
|
highlights: unknown[];
|
|
447
384
|
};
|
|
385
|
+
/**
|
|
386
|
+
* Value registry for deduplicated text values
|
|
387
|
+
* Large text values (>1000 chars) are stored here once and referenced as <REF:value_N>
|
|
388
|
+
* This dramatically reduces JSON size when the same large text appears in many events
|
|
389
|
+
*/
|
|
390
|
+
valueRegistry: {
|
|
391
|
+
[key: string]: string;
|
|
392
|
+
};
|
|
448
393
|
};
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/DyeLight.d.ts
|
|
396
|
+
/**
|
|
397
|
+
* @fileoverview DyeLight - A React textarea component with advanced text highlighting capabilities
|
|
398
|
+
*
|
|
399
|
+
* This component provides a textarea with overlay-based text highlighting that supports:
|
|
400
|
+
* - Character-level highlighting using absolute text positions
|
|
401
|
+
* - Line-level highlighting with CSS classes or color values
|
|
402
|
+
* - Automatic height adjustment based on content
|
|
403
|
+
* - Synchronized scrolling between textarea and highlight layer
|
|
404
|
+
* - Both controlled and uncontrolled usage patterns
|
|
405
|
+
* - RTL text direction support
|
|
406
|
+
*/
|
|
407
|
+
/**
|
|
408
|
+
* Creates a line element with optional highlighting
|
|
409
|
+
*/
|
|
410
|
+
declare const createLineElement: (content: React$1.ReactNode, lineIndex: number, lineHighlight?: string) => React$1.ReactElement;
|
|
411
|
+
/**
|
|
412
|
+
* Renders a single line with character-level highlights and optional line-level highlighting
|
|
413
|
+
*/
|
|
414
|
+
declare const renderHighlightedLine: (line: string, lineIndex: number, ranges: Array<{
|
|
415
|
+
absoluteStart: number;
|
|
416
|
+
className?: string;
|
|
417
|
+
end: number;
|
|
418
|
+
start: number;
|
|
419
|
+
style?: React$1.CSSProperties;
|
|
420
|
+
}>, lineHighlight?: string) => React$1.ReactElement;
|
|
421
|
+
/**
|
|
422
|
+
* A textarea component with support for highlighting character ranges using absolute positions
|
|
423
|
+
* and optional line-level highlighting. Perfect for syntax highlighting, error indication,
|
|
424
|
+
* and text annotation without the complexity of line-based positioning.
|
|
425
|
+
*/
|
|
426
|
+
declare const DyeLight: React$1.ForwardRefExoticComponent<DyeLightProps & React$1.RefAttributes<DyeLightRef>>;
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/domUtils.d.ts
|
|
429
|
+
/**
|
|
430
|
+
* @fileoverview DOM utility functions for textarea manipulation
|
|
431
|
+
*
|
|
432
|
+
* This module provides utility functions for working with DOM elements,
|
|
433
|
+
* specifically focused on textarea auto-resizing functionality used by
|
|
434
|
+
* the DyeLight component.
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* Automatically resizes a textarea element to fit its content
|
|
438
|
+
*
|
|
439
|
+
* This function adjusts the textarea height to match its scroll height,
|
|
440
|
+
* effectively removing scrollbars when the content fits and expanding
|
|
441
|
+
* the textarea as content is added. The height is first set to 'auto'
|
|
442
|
+
* to allow the element to shrink if content is removed.
|
|
443
|
+
*
|
|
444
|
+
* @param textArea - The HTML textarea element to resize
|
|
445
|
+
* @example
|
|
446
|
+
* ```ts
|
|
447
|
+
* const textarea = document.querySelector('textarea');
|
|
448
|
+
* if (textarea) {
|
|
449
|
+
* autoResize(textarea);
|
|
450
|
+
* }
|
|
451
|
+
*
|
|
452
|
+
* // Or in an event handler:
|
|
453
|
+
* const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
454
|
+
* autoResize(e.target);
|
|
455
|
+
* };
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
declare const autoResize: (textArea: HTMLTextAreaElement) => void;
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/telemetry.d.ts
|
|
449
461
|
/**
|
|
450
462
|
* AI-optimized telemetry collector for the DyeLight component.
|
|
451
463
|
* Records events with full context and generates comprehensive debug reports
|
|
@@ -457,6 +469,7 @@ declare class AIOptimizedTelemetry {
|
|
|
457
469
|
private enabled;
|
|
458
470
|
private lastEventTimestamp;
|
|
459
471
|
private issueRegistry;
|
|
472
|
+
private valueRegistry;
|
|
460
473
|
/**
|
|
461
474
|
* Creates a new telemetry instance
|
|
462
475
|
* @param enabled Whether telemetry collection is initially enabled
|
|
@@ -493,20 +506,18 @@ declare class AIOptimizedTelemetry {
|
|
|
493
506
|
* @param currentValue Current React state value
|
|
494
507
|
* @param textareaHeight Current textarea height
|
|
495
508
|
* @param highlights Current highlights configuration
|
|
496
|
-
* @param lineHighlights Current line highlights configuration
|
|
497
509
|
* @returns Complete debug report
|
|
498
510
|
*/
|
|
499
|
-
generateAIReport(textareaRef: React.RefObject<HTMLTextAreaElement | null>, currentValue: string | undefined, textareaHeight: number | undefined, highlights: unknown[]
|
|
511
|
+
generateAIReport(textareaRef: React.RefObject<HTMLTextAreaElement | null>, currentValue: string | undefined, textareaHeight: number | undefined, highlights: unknown[]): AIDebugReport;
|
|
500
512
|
/**
|
|
501
513
|
* Exports telemetry data in AI-optimized format with instructions
|
|
502
514
|
* @param textareaRef Reference to the textarea element
|
|
503
515
|
* @param currentValue Current React state value
|
|
504
516
|
* @param textareaHeight Current textarea height
|
|
505
517
|
* @param highlights Current highlights configuration
|
|
506
|
-
* @param lineHighlights Current line highlights configuration
|
|
507
518
|
* @returns JSON string containing debug report and AI instructions
|
|
508
519
|
*/
|
|
509
|
-
exportForAI(textareaRef: React.RefObject<HTMLTextAreaElement | null>, currentValue: string | undefined, textareaHeight: number | undefined, highlights: unknown[]
|
|
520
|
+
exportForAI(textareaRef: React.RefObject<HTMLTextAreaElement | null>, currentValue: string | undefined, textareaHeight: number | undefined, highlights: unknown[]): string;
|
|
510
521
|
/**
|
|
511
522
|
* Clears all recorded events and issue registry
|
|
512
523
|
*/
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/builder.ts","../src/types.ts","../src/DyeLight.tsx","../src/domUtils.ts","../src/telemetry.ts"],"mappings":";;;;;;;cAca,gBAAA;EA+FwE;;;;;;;;;;;;;;;;;sBA7E7D,KAAA;IAAQ,SAAA;IAAoB,KAAA;IAAe,KAAA,GAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/builder.ts","../src/types.ts","../src/DyeLight.tsx","../src/domUtils.ts","../src/telemetry.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),n=[],r=0;return t.forEach((e,i)=>{n.push(r),r+=e.length+(i<t.length-1?1:0)}),{lineStarts:n,lines:t}},m=(e,t)=>{for(let n=t.length-1;n>=0;n--)if(e>=t[n])return{char:e-t[n],line:n};return{char:0,line:0}},h=e=>/^(#|rgb|hsl|var\(--.*?\)|transparent|currentColor|inherit|initial|unset)/i.test(e)||/^[a-z]+$/i.test(e),g=(e,t,n,r)=>{let{lines:i,lineStarts:a}=p(e),o={};return t.forEach(e=>{let t=m(e.start,a),n=m(e.end-1,a);for(let r=t.line;r<=n.line;r++){o[r]||(o[r]=[]);let t=a[r],n=Math.max(e.start-t,0),s=Math.min(e.end-t,i[r].length);s>n&&o[r].push({absoluteStart:e.start,className:e.className,end:s,start:n,style:e.style})}}),i.map((e,t)=>{let i=n[t];return r(e,t,o[t]||[],i)})},_=(e,t,n,r)=>a(()=>g(e,t,n,r),[e,t,n,r]),v=(e,t)=>{if(!e||!t)return;let{scrollLeft:n,scrollTop:r}=e;t.scrollTop=r,t.scrollLeft=n},y=(e,t,n=getComputedStyle)=>{if(!e||!t)return;let r=n(e);t.style.padding=r.padding,t.style.fontSize=r.fontSize,t.style.fontFamily=r.fontFamily,t.style.lineHeight=r.lineHeight,t.style.letterSpacing=r.letterSpacing,t.style.wordSpacing=r.wordSpacing,t.style.textIndent=r.textIndent,t.style.whiteSpace=r.whiteSpace,t.style.wordBreak=r.wordBreak,t.style.overflowWrap=r.overflowWrap,t.style.tabSize=r.tabSize,t.style.borderTopWidth=r.borderTopWidth,t.style.borderRightWidth=r.borderRightWidth,t.style.borderBottomWidth=r.borderBottomWidth,t.style.borderLeftWidth=r.borderLeftWidth,t.style.borderTopStyle=r.borderTopStyle,t.style.borderRightStyle=r.borderRightStyle,t.style.borderBottomStyle=r.borderBottomStyle,t.style.borderLeftStyle=r.borderLeftStyle,t.style.borderColor=`transparent`;let i=parseFloat(r.borderLeftWidth)||0,a=parseFloat(r.borderRightWidth)||0,o=e.offsetWidth-e.clientWidth-i-a;if(o>0)if(r.direction===`rtl`){let e=parseFloat(r.paddingLeft)||0;t.style.paddingLeft=`${e+o}px`}else{let e=parseFloat(r.paddingRight)||0;t.style.paddingRight=`${e+o}px`}},b=(e,t,r,i,a)=>{let s=o(null);return{highlightLayerRef:s,syncScroll:n(n=>{let o={scrollLeft:s.current?.scrollLeft,scrollTop:s.current?.scrollTop};v(n.current,s.current);let c={scrollLeft:s.current?.scrollLeft,scrollTop:s.current?.scrollTop};e?.record(`syncScroll`,`sync`,{after:c,before:o,changed:o.scrollTop!==c.scrollTop||o.scrollLeft!==c.scrollLeft},t??n,r?.()??``,i?.(),a??!1)},[e,t,r,i,a]),syncStyles:n(n=>{if(!n.current||!s.current)return;let o=getComputedStyle(n.current),c=n.current.offsetWidth-n.current.clientWidth-(parseFloat(o.borderLeftWidth)||0)-(parseFloat(o.borderRightWidth)||0);y(n.current,s.current),e?.record(`syncStyles`,`sync`,{direction:o.direction,fontSize:o.fontSize,padding:o.padding,scrollbarWidth:c},t??n,r?.()??``,i?.(),a??!1)},[e,t,r,i,a])}},x=(e,t,n,r,i)=>{if(!e)return;let a=e.value;a!==t&&(n||r(a),i?.(a))},S=(e,t,n,r,i)=>{e!==t&&(n||r(e),i?.(e))},C=(e,t,n,r,i)=>{e&&(e.value=t,n||r(t),i?.(t))},w=(e,t=``,i,a,c,l)=>{let u=o(null),[d,f]=s(e??t),p=e!==void 0,m=p?e??``:d,h=c??u,g=n(()=>{x(h.current,m,p,f,i)},[m,p,i,h]),_=n(e=>{let t=e.target.value;a?.record(`onChange`,`user`,{lengthDelta:t.length-m.length,newValue:t,previousValue:m,valueLength:t.length},h,m,l?.(),p),S(t,m,p,f,i)},[m,p,i,a,h,l]),v=n(e=>{a?.record(`setValue`,`state`,{newValue:e,previousValue:m},h,m,l?.(),p),C(h.current,e,p,f,i)},[p,i,a,h,l,m]);return r(()=>{g()},[g]),r(()=>{p&&h.current&&h.current.value!==m&&(h.current.value=m)},[p,m,h]),r(()=>{h.current&&h.current.value!==m&&a?.record(`valueMismatch`,`state`,{domValue:h.current.value,stateValue:m},h,m,l?.(),p)},[m,p,a,h,l]),{currentValue:m,handleChange:_,setValue:v,textareaRef:h}},T={display:`block`,position:`relative`,width:`100%`},E={background:`transparent`,boxSizing:`border-box`,caretColor:`#000000`,color:`transparent`,minHeight:`auto`,position:`relative`,width:`100%`,zIndex:2},D={border:`0px none transparent`,bottom:0,boxSizing:`border-box`,color:`inherit`,fontFamily:`inherit`,fontSize:`inherit`,left:0,lineHeight:`inherit`,margin:0,overflow:`hidden`,pointerEvents:`none`,position:`absolute`,right:0,top:0,whiteSpace:`pre-wrap`,wordWrap:`break-word`,zIndex:1};var O=class{events=[];maxEvents=1e3;enabled=!1;lastEventTimestamp=null;issueRegistry=new Map;constructor(e=!1,t=1e3){this.enabled=e,this.maxEvents=t}setEnabled(e){this.enabled=e}record(e,t,n,r,i,a,o){if(!this.enabled)return;let s=Date.now(),c=this.lastEventTimestamp?s-this.lastEventTimestamp:null,l=r.current?.value??``,u=i??``,d=l===u,f=[];if(d||(f.push(`State mismatch: DOM="${l}" vs React="${u}"`),this.issueRegistry.set(`state_mismatch`,(this.issueRegistry.get(`state_mismatch`)??0)+1)),c!==null&&c<5&&(f.push(`Rapid event: ${c}ms since last event`),this.issueRegistry.set(`rapid_events`,(this.issueRegistry.get(`rapid_events`)??0)+1)),t===`user`&&e===`onChange`){let e=Math.abs(l.length-u.length);e>100&&(f.push(`Large paste detected: ${e} characters changed`),this.issueRegistry.set(`large_paste`,(this.issueRegistry.get(`large_paste`)??0)+1))}let p={anomalies:f,category:t,data:n,description:this.generateDescription(e,t,n),stateSnapshot:{isControlled:o,reactValue:u,textareaHeight:a,textareaValue:l,valuesMatch:d},timeSinceLastEvent:c,timestamp:s,timestampISO:new Date(s).toISOString(),type:e};this.events.push(p),this.lastEventTimestamp=s,this.events.length>this.maxEvents&&(this.events=this.events.slice(-this.maxEvents))}generateDescription(e,t,n){switch(e){case`onChange`:return`User typed/pasted text. New length: ${n.valueLength}`;case`autoResize`:return`Textarea auto-resized from ${n.beforeHeight}px to ${n.afterHeight}px`;case`syncScroll`:return`Synchronized scroll position to top:${n.after?.scrollTop??0}`;case`syncStyles`:return`Synchronized styles (padding, font, borders) between textarea and overlay`;case`valueMismatch`:return`CRITICAL: DOM value differs from React state`;case`setValue`:return`Programmatic value change via ref.setValue()`;case`snapshot`:return`Periodic state snapshot for debugging`;default:return`${t} event: ${e}`}}generateAIReport(t,n,r,i,a){let o=this.events[0],s=this.events[this.events.length-1],c=s&&o?s.timestamp-o.timestamp:0,l=[];for(let[e,t]of this.issueRegistry.entries()){let n=this.events.map((t,n)=>t.anomalies.some(t=>t.includes(e))?n:-1).filter(e=>e!==-1),r=n.length>0?this.events[n[0]].timestampISO:``,i=`info`,a=e;e===`state_mismatch`?(i=`critical`,a=`State desynchronization between DOM and React detected`):e===`rapid_events`?(i=t>10?`warning`:`info`,a=`Rapid successive events detected (possible race condition)`):e===`large_paste`&&(i=`warning`,a=`Large paste operations detected (may trigger sync issues)`),l.push({firstOccurrence:r,issue:a,occurrenceCount:t,relatedEvents:n,severity:i})}l.sort((e,t)=>{let n={critical:0,info:2,warning:1};return n[e.severity]-n[t.severity]});let u=[],d=this.events.filter(e=>e.type===`autoResize`),f=this.events.filter(e=>e.type===`onChange`);d.length>f.length*2&&u.push(`Excessive resize operations (${d.length} resizes vs ${f.length} value changes). May indicate infinite ResizeObserver loop.`);let p=this.events.filter(e=>e.category===`sync`);p.length>this.events.length*.3&&u.push(`High frequency of sync operations (${p.length}/${this.events.length} events). May indicate layout thrashing.`);let m=[];l.some(e=>e.issue.includes(`desynchronization`))&&m.push(`State desynchronization detected. Check if multiple onChange handlers are bound or if external code is modifying the textarea DOM directly.`),u.some(e=>e.includes(`ResizeObserver`))&&m.push(`Possible infinite ResizeObserver loop. Ensure resize operations are debounced with requestAnimationFrame.`),l.some(e=>e.issue.includes(`race condition`))&&m.push(`Rapid events detected. Consider debouncing onChange handlers or checking for double-bound event listeners.`);let h={stateChanges:this.events.filter(e=>e.category===`state`).map(e=>({after:e.data.newValue,before:e.data.previousValue,timestamp:e.timestampISO,type:e.type,unexpected:e.anomalies.length>0})),syncOperations:this.events.filter(e=>e.category===`sync`).map(e=>({details:e.data,operation:e.type,success:!e.anomalies.length,timestamp:e.timestampISO})),userActions:this.events.filter(e=>e.category===`user`).map(e=>({action:e.description,resultingStateChanges:this.events.filter(t=>t.timestamp>e.timestamp&&t.timestamp<e.timestamp+100&&t.category===`state`).length,timestamp:e.timestampISO}))};return{events:this.events,finalState:{height:r,highlights:i,inSync:(t.current?.value??``)===(n??``),reactValue:n??``,scrollPosition:{left:t.current?.scrollLeft??0,top:t.current?.scrollTop??0},textareaValue:t.current?.value??``},metadata:{browser:navigator.userAgent,componentVersion:`1.1.3`,generatedAt:new Date().toISOString(),platform:navigator.platform,reactVersion:e.version??`unknown`,timespan:{durationMs:c,end:s?.timestampISO??``,start:o?.timestampISO??``},totalEvents:this.events.length},summary:{description:l.length>0?`Found ${l.length} issue(s) during ${this.events.length} recorded events over ${(c/1e3).toFixed(1)}s`:`No issues detected during ${this.events.length} events over ${(c/1e3).toFixed(1)}s`,detectedIssues:l,recommendations:m,suspiciousPatterns:u},timeline:h}}exportForAI(e,t,n,r,i){let a=this.generateAIReport(e,t,n,r,i);return JSON.stringify({aiInstructions:{commonIssues:{rapidEvents:`Events firing <5ms apart - possible race condition or double-bound handlers`,resizeLoop:`Excessive resize operations - likely infinite ResizeObserver loop`,stateMismatch:`DOM value differs from React state - likely caused by multiple onChange handlers or direct DOM manipulation`},keyFields:{"events[].anomalies":`Issues detected at each event`,"events[].stateSnapshot":`Full state at each event for comparison`,finalState:`Current state at time of export`,"summary.detectedIssues":`Automatically detected problems, sorted by severity`,"timeline.stateChanges":`Chronological view of all state mutations`},purpose:`This is a debug report from the DyeLight React component. A textarea with highlighting overlay is experiencing synchronization issues.`,yourTask:[`1. Review the summary.detectedIssues for critical problems`,`2. Examine timeline.stateChanges for unexpected state transitions`,`3. Look for patterns in events where stateSnapshot.valuesMatch is false`,`4. Check for rapid events (<5ms apart) that might indicate race conditions`,`5. Identify the root cause and suggest specific code fixes`]},debugReport:a},null,2)}clear(){this.events=[],this.issueRegistry.clear(),this.lastEventTimestamp=null}};const k=(e,t,n)=>{if(!n)return c(`div`,{children:e},t);let r=h(n);return c(`div`,{className:r?void 0:n,style:r?{backgroundColor:n}:void 0,children:e},t)},A=(e,t,n,r)=>{if(n.length===0)return k(e||`\xA0`,t,r);let i=n.toSorted((e,t)=>e.start-t.start),a=[],o=0;if(i.forEach((n,r)=>{let{className:i,end:s,start:l,style:u}=n,d=Math.max(0,Math.min(l,e.length)),f=Math.max(d,Math.min(s,e.length));if(f<=o)return;let p=Math.max(d,o);if(p>o){let t=e.slice(o,p);t&&a.push(t)}if(f>p){let o=e.slice(p,f);a.push(c(`span`,{className:i,style:u,"data-range-start":n.absoluteStart,children:o},`highlight-${t}-${r.toString()}`))}o=Math.max(o,f)}),o<e.length){let t=e.slice(o);t&&a.push(t)}return k(a.length===0?`\xA0`:a,t,r)},j=t(({className:e=``,containerClassName:t=``,defaultValue:s=``,dir:u=`ltr`,enableAutoResize:d=!0,highlights:p=[],lineHighlights:m={},onChange:h,rows:g=4,style:v,value:y,debug:x=!1,debugMaxEvents:S=1e3,...C},k)=>{let j=o(null),M=o(void 0),N=a(()=>new O(x,S),[x,S]);r(()=>{N.setEnabled(x)},[x,N]);let P=y!==void 0,F=n(()=>M.current,[]),{currentValue:I,handleChange:L,setValue:R,textareaRef:z}=w(y,s,h,N,void 0,F),B=n(()=>I,[I]),{handleAutoResize:V,textareaHeight:H}=f(d,N,z,B,P);r(()=>{M.current=H},[H]);let{highlightLayerRef:U,syncScroll:W,syncStyles:G}=b(N,z,B,F,P),K=_(I,p,m,A),q=n(e=>{L(e),V(e.target)},[L,V]),J=n(e=>{R(e),z.current&&V(z.current)},[R,V,z]);r(()=>{},[]);let Y=n(()=>{W(z)},[W,z]);r(()=>{if(!x)return;let e=setInterval(()=>{N.record(`snapshot`,`system`,{},z,I,H,P)},1e3);return()=>clearInterval(e)},[x,N,z,I,H,P]),i(k,()=>({blur:()=>z.current?.blur(),exportForAI:()=>N.exportForAI(z,I,H,p,m),focus:()=>z.current?.focus(),getValue:()=>I,scrollToPosition:(e,t=40,n=`auto`)=>{if(U.current&&z.current){let r=U.current.querySelector(`[data-range-start="${e.toString()}"]`);if(r instanceof HTMLElement){let e=z.current,i=r.offsetTop;n===`smooth`?e.scrollTo({behavior:`smooth`,top:i-t}):e.scrollTop=i-t}}},select:()=>z.current?.select(),setSelectionRange:(e,t)=>z.current?.setSelectionRange(e,t),setValue:J}),[I,J,U,z,N,H,p,m]),r(()=>{z.current&&d&&V(z.current),G(z)},[I,V,d,G,z]),r(()=>{if(!z.current)return;let e=z.current,t,n=new ResizeObserver(()=>{cancelAnimationFrame(t),t=requestAnimationFrame(()=>{G(z),d&&V(e)})});return n.observe(e),()=>{n.disconnect(),cancelAnimationFrame(t)}},[z,G,V,d]);let X={...E,color:I?`transparent`:`inherit`,height:H?`${H}px`:void 0,resize:d?`none`:`vertical`},Z={...D,direction:u,height:H?`${H}px`:void 0,padding:z.current?getComputedStyle(z.current).padding:`8px 12px`};return l(`div`,{className:t,ref:j,style:{...T,...v},children:[c(`div`,{"aria-hidden":`true`,ref:U,style:Z,children:K}),c(`textarea`,{className:e,dir:u,onChange:q,onScroll:Y,ref:z,rows:g,style:X,value:I,...C})]})});j.displayName=`DyeLight`;export{O as AIOptimizedTelemetry,j as DyeLight,u as HighlightBuilder,d as autoResize,k as createLineElement,A as renderHighlightedLine};
|
|
1
|
+
import{forwardRef as e,useCallback as t,useEffect as n,useImperativeHandle as r,useMemo as i,useRef as a,useState as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";const l={characters:e=>e.map(({className:e,index:t,style:n})=>({className:e,end:t+1,start:t,style:n})),lines:e=>{let t={};return e.forEach(({className:e,color:n,line:r})=>{t[r]=e||n||``}),t},pattern:(e,t,n,r)=>{let i=typeof t==`string`?new RegExp(t,`g`):new RegExp(t.source,`g`);return Array.from(e.matchAll(i)).map(e=>({className:n,end:e.index+e[0].length,start:e.index,style:r}))},ranges:e=>e.map(({className:e,end:t,start:n,style:r})=>({className:e,end:t,start:n,style:r})),selection:(e,t,n,r)=>[{className:n,end:t,start:e,style:r}],words:(e,t,n,r)=>{let i=RegExp(`\\b(${t.join(`|`)})\\b`,`g`);return l.pattern(e,i,n,r)}},u=e=>{let t=getComputedStyle(e),n=parseFloat(t.borderTopWidth)||0,r=parseFloat(t.borderBottomWidth)||0;e.style.height=`auto`;let i=e.scrollHeight,a=n+r,o=a>2?i+a:i;e.style.height=`${o}px`},d=(e,n,r,i,a)=>{let[s,c]=o();return{handleAutoResize:t(t=>{if(!e)return;let o=t.scrollHeight;u(t);let s=t.scrollHeight;n?.record(`autoResize`,`system`,{afterHeight:s,beforeHeight:o,changed:o!==s,textLength:t.value.length},r??{current:t},i?.()??t.value,s,a??!1),c(t.scrollHeight)},[e,n,r,i,a]),textareaHeight:s}},f=e=>{let t=e.split(`
|
|
2
|
+
`),n=[],r=0;return t.forEach((e,i)=>{n.push(r),r+=e.length+(i<t.length-1?1:0)}),{lineStarts:n,lines:t}},p=(e,t)=>{for(let n=t.length-1;n>=0;n--)if(e>=t[n])return{char:e-t[n],line:n};return{char:0,line:0}},m=e=>/^(#|rgb|hsl|var\(--.*?\)|transparent|currentColor|inherit|initial|unset)/i.test(e)||/^[a-z]+$/i.test(e),h=(e,t,n,r)=>{let{lines:i,lineStarts:a}=f(e),o={};return t.forEach(e=>{let t=p(e.start,a),n=p(e.end-1,a);for(let r=t.line;r<=n.line;r++){o[r]||(o[r]=[]);let t=a[r],n=Math.max(e.start-t,0),s=Math.min(e.end-t,i[r].length);s>n&&o[r].push({absoluteStart:e.start,className:e.className,end:s,start:n,style:e.style})}}),i.map((e,t)=>{let i=n[t];return r(e,t,o[t]||[],i)})},g=(e,t,n,r)=>i(()=>h(e,t,n,r),[e,t,n,r]),_=(e,t)=>{if(!e||!t)return;let{scrollLeft:n,scrollTop:r}=e;t.scrollTop=r,t.scrollLeft=n},v=(e,t,n=getComputedStyle)=>{if(!e||!t)return;let r=n(e);t.style.padding=r.padding,t.style.fontSize=r.fontSize,t.style.fontFamily=r.fontFamily,t.style.lineHeight=r.lineHeight,t.style.letterSpacing=r.letterSpacing,t.style.wordSpacing=r.wordSpacing,t.style.textIndent=r.textIndent,t.style.whiteSpace=r.whiteSpace,t.style.wordBreak=r.wordBreak,t.style.overflowWrap=r.overflowWrap,t.style.tabSize=r.tabSize,t.style.borderTopWidth=r.borderTopWidth,t.style.borderRightWidth=r.borderRightWidth,t.style.borderBottomWidth=r.borderBottomWidth,t.style.borderLeftWidth=r.borderLeftWidth,t.style.borderTopStyle=r.borderTopStyle,t.style.borderRightStyle=r.borderRightStyle,t.style.borderBottomStyle=r.borderBottomStyle,t.style.borderLeftStyle=r.borderLeftStyle,t.style.borderColor=`transparent`;let i=parseFloat(r.borderLeftWidth)||0,a=parseFloat(r.borderRightWidth)||0,o=e.offsetWidth-e.clientWidth-i-a;if(o>0)if(r.direction===`rtl`){let e=parseFloat(r.paddingLeft)||0;t.style.paddingLeft=`${e+o}px`}else{let e=parseFloat(r.paddingRight)||0;t.style.paddingRight=`${e+o}px`}},y=(e,n,r,i,o)=>{let s=a(null);return{highlightLayerRef:s,syncScroll:t(t=>{let a={scrollLeft:s.current?.scrollLeft,scrollTop:s.current?.scrollTop};_(t.current,s.current);let c={scrollLeft:s.current?.scrollLeft,scrollTop:s.current?.scrollTop};e?.record(`syncScroll`,`sync`,{after:c,before:a,changed:a.scrollTop!==c.scrollTop||a.scrollLeft!==c.scrollLeft},n??t,r?.()??``,i?.(),o??!1)},[e,n,r,i,o]),syncStyles:t(t=>{if(!t.current||!s.current)return;let a=getComputedStyle(t.current),c=t.current.offsetWidth-t.current.clientWidth-(parseFloat(a.borderLeftWidth)||0)-(parseFloat(a.borderRightWidth)||0);v(t.current,s.current),e?.record(`syncStyles`,`sync`,{direction:a.direction,fontSize:a.fontSize,padding:a.padding,scrollbarWidth:c},n??t,r?.()??``,i?.(),o??!1)},[e,n,r,i,o])}},b=(e,t,n,r,i)=>{if(!e)return;let a=e.value;a!==t&&(n||r(a),i?.(a))},x=(e,t,n,r,i,a)=>{e!==t&&(n&&a&&a.value!==e&&(a.value=e),n||r(e),i?.(e))},S=(e,t,n,r,i)=>{e&&(e.value=t,n||r(t),i?.(t))},C=(e,r=``,i,s,c,l)=>{let u=a(null),[d,f]=o(e??r),p=e!==void 0,m=p?e??``:d,h=c??u,g=t(()=>{b(h.current,m,p,f,i)},[m,p,i,h]),_=t(e=>{let t=e.target.value;s?.record(`onChange`,`user`,{lengthDelta:t.length-m.length,newValue:t,previousValue:m,valueLength:t.length},h,m,l?.(),p),x(t,m,p,f,i,h.current)},[m,p,i,s,h,l]),v=t(e=>{s?.record(`setValue`,`state`,{newValue:e,previousValue:m},h,m,l?.(),p),S(h.current,e,p,f,i)},[p,i,s,h,l,m]);return n(()=>{g()},[g]),n(()=>{p&&h.current&&h.current.value!==m&&(h.current.value=m)},[p,m,h]),n(()=>{h.current&&h.current.value!==m&&s?.record(`valueMismatch`,`state`,{domValue:h.current.value,stateValue:m},h,m,l?.(),p)},[m,p,s,h,l]),{currentValue:m,handleChange:_,setValue:v,textareaRef:h}},w={display:`block`,position:`relative`,width:`100%`},T={background:`transparent`,boxSizing:`border-box`,caretColor:`#000000`,color:`transparent`,minHeight:`auto`,position:`relative`,width:`100%`,zIndex:2},E={border:`0px none transparent`,bottom:0,boxSizing:`border-box`,color:`inherit`,fontFamily:`inherit`,fontSize:`inherit`,left:0,lineHeight:`inherit`,margin:0,overflow:`hidden`,pointerEvents:`none`,position:`absolute`,right:0,top:0,whiteSpace:`pre-wrap`,wordWrap:`break-word`,zIndex:1};function D(e,t){let n=[];for(let[r,i]of e.entries()){let e={large_paste:`Large paste detected`,rapid_events:`Rapid event`,state_mismatch:`State mismatch`}[r]??r,a=t.map((t,n)=>t.anomalies.some(t=>t.includes(e))?n:-1).filter(e=>e!==-1),o=a.length>0?t[a[0]].timestampISO:``,s=`info`,c=r;r===`state_mismatch`?(s=`critical`,c=`State desynchronization between DOM and React detected`):r===`rapid_events`?(s=i>10?`warning`:`info`,c=`Rapid successive events detected (possible race condition)`):r===`large_paste`&&(s=`warning`,c=`Large paste operations detected (may trigger sync issues)`),n.push({firstOccurrence:o,issue:c,occurrenceCount:i,relatedEvents:a,severity:s})}return n.sort((e,t)=>{let n={critical:0,info:2,warning:1};return n[e.severity]-n[t.severity]}),n}function O(e){let t=[],n=e.filter(e=>e.type===`autoResize`),r=e.filter(e=>e.type===`onChange`);n.length>r.length*2&&t.push(`Excessive resize operations (${n.length} resizes vs ${r.length} value changes). May indicate infinite ResizeObserver loop.`);let i=e.filter(e=>e.category===`sync`);return i.length>e.length*.3&&t.push(`High frequency of sync operations (${i.length}/${e.length} events). May indicate layout thrashing.`),t}function k(e,t){let n=[];return e.some(e=>e.issue.includes(`desynchronization`))&&n.push(`State desynchronization detected. Check if multiple onChange handlers are bound or if external code is modifying the textarea DOM directly.`),t.some(e=>e.includes(`ResizeObserver`))&&n.push(`Possible infinite ResizeObserver loop. Ensure resize operations are debounced with requestAnimationFrame.`),e.some(e=>e.issue.includes(`race condition`))&&n.push(`Rapid events detected. Consider debouncing onChange handlers or checking for double-bound event listeners.`),e.some(e=>e.issue.includes(`Large paste`))&&n.push(`Large paste operations detected. Ensure e.preventDefault() is called BEFORE reading clipboard data in your onPaste handler to prevent the browser from inserting raw content into the DOM.`),n}function A(e){return{stateChanges:e.filter(e=>e.category===`state`).map(e=>({after:e.data.newValue,before:e.data.previousValue,timestamp:e.timestampISO,type:e.type,unexpected:e.anomalies.length>0})),syncOperations:e.filter(e=>e.category===`sync`).map(e=>({details:e.data,operation:e.type,success:!e.anomalies.length,timestamp:e.timestampISO})),userActions:e.filter(e=>e.category===`user`).map(t=>({action:t.description,resultingStateChanges:e.filter(e=>e.timestamp>t.timestamp&&e.timestamp<t.timestamp+100&&e.category===`state`).length,timestamp:t.timestampISO}))}}var j=class{registry=new Map;reverseRegistry=new Map;nextId=0;threshold=1e3;store(e){if(e.length<=this.threshold)return e;if(this.reverseRegistry.has(e))return this.reverseRegistry.get(e);let t=`<REF:value_${this.nextId}>`;return this.registry.set(t,e),this.reverseRegistry.set(e,t),this.nextId++,t}getRegistry(){return Object.fromEntries(this.registry)}clear(){this.registry.clear(),this.reverseRegistry.clear(),this.nextId=0}},M=class{events=[];maxEvents=1e3;enabled=!1;lastEventTimestamp=null;issueRegistry=new Map;valueRegistry=new j;constructor(e=!1,t=1e3){this.enabled=e,this.maxEvents=t}setEnabled(e){this.enabled=e}record(e,t,n,r,i,a,o){if(!this.enabled)return;let s=Date.now(),c=this.lastEventTimestamp?s-this.lastEventTimestamp:null,l=r.current?.value??``,u=i??``,d=l===u,f=[];if(d||(f.push(`State mismatch: DOM="${l.slice(0,50)}..." vs React="${u.slice(0,50)}..."`),this.issueRegistry.set(`state_mismatch`,(this.issueRegistry.get(`state_mismatch`)??0)+1)),c!==null&&c<5&&(f.push(`Rapid event: ${c}ms since last event`),this.issueRegistry.set(`rapid_events`,(this.issueRegistry.get(`rapid_events`)??0)+1)),t===`user`&&e===`onChange`){let e=Math.abs(l.length-u.length);e>100&&(f.push(`Large paste detected: ${e} characters changed`),this.issueRegistry.set(`large_paste`,(this.issueRegistry.get(`large_paste`)??0)+1))}let p=this.valueRegistry.store(l),m=this.valueRegistry.store(u),h={anomalies:f,category:t,data:n,description:this.generateDescription(e,t,n),stateSnapshot:{isControlled:o,reactValue:m,textareaHeight:a,textareaValue:p,valuesMatch:d},timeSinceLastEvent:c,timestamp:s,timestampISO:new Date(s).toISOString(),type:e};this.events.push(h),this.lastEventTimestamp=s,this.events.length>this.maxEvents&&(this.events=this.events.slice(-this.maxEvents))}generateDescription(e,t,n){switch(e){case`onChange`:return`User typed/pasted text. New length: ${n.valueLength}`;case`autoResize`:return`Textarea auto-resized from ${n.beforeHeight}px to ${n.afterHeight}px`;case`syncScroll`:return`Synchronized scroll position to top:${n.after?.scrollTop??0}`;case`syncStyles`:return`Synchronized styles (padding, font, borders) between textarea and overlay`;case`valueMismatch`:return`CRITICAL: DOM value differs from React state`;case`setValue`:return`Programmatic value change via ref.setValue()`;case`snapshot`:return`Periodic state snapshot for debugging`;default:return`${t} event: ${e}`}}generateAIReport(e,t,n,r){let i=this.events[0],a=this.events[this.events.length-1],o=a&&i?a.timestamp-i.timestamp:0,s=D(this.issueRegistry,this.events),c=O(this.events),l=k(s,c),u=A(this.events),d=this.valueRegistry.store(e.current?.value??``),f=this.valueRegistry.store(t??``);return{events:this.events,finalState:{height:n,highlights:r,inSync:(e.current?.value??``)===(t??``),reactValue:f,scrollPosition:{left:e.current?.scrollLeft??0,top:e.current?.scrollTop??0},textareaValue:d},metadata:{browser:navigator.userAgent,componentVersion:`1.1.3`,generatedAt:new Date().toISOString(),platform:navigator.platform,timespan:{durationMs:o,end:a?.timestampISO??``,start:i?.timestampISO??``},totalEvents:this.events.length},summary:{description:s.length>0?`Found ${s.length} issue(s) during ${this.events.length} recorded events over ${(o/1e3).toFixed(1)}s`:`No issues detected during ${this.events.length} events over ${(o/1e3).toFixed(1)}s`,detectedIssues:s,recommendations:l,suspiciousPatterns:c},timeline:u,valueRegistry:this.valueRegistry.getRegistry()}}exportForAI(e,t,n,r){let i=this.generateAIReport(e,t,n,r);return JSON.stringify({aiInstructions:{commonIssues:{largePaste:`Large paste operations where e.preventDefault() is called AFTER clipboard data is read - browser inserts raw text into DOM before React state updates`,rapidEvents:`Events firing <5ms apart - possible race condition or double-bound handlers`,resizeLoop:`Excessive resize operations - likely infinite ResizeObserver loop`,stateMismatch:`DOM value differs from React state - likely caused by multiple onChange handlers or direct DOM manipulation`},keyFields:{"events[].anomalies":`Issues detected at each event`,"events[].stateSnapshot":`Full state at each event for comparison`,finalState:`Current state at time of export`,"summary.detectedIssues":`Automatically detected problems, sorted by severity`,"timeline.stateChanges":`Chronological view of all state mutations`,valueRegistry:`Deduplicated text values - references like <REF:value_0> point to entries here`},purpose:`This is a debug report from the DyeLight React component. A textarea with highlighting overlay is experiencing synchronization issues.`,valueDeduplication:`IMPORTANT: Text values longer than 1000 characters are stored in valueRegistry and referenced as <REF:value_N>. When you see <REF:value_0> in stateSnapshot.textareaValue, look up the actual value in debugReport.valueRegistry["<REF:value_0>"]. This optimization prevents the JSON from becoming huge when users paste large blocks of text. Example: If textareaValue is "<REF:value_0>" and valueRegistry is {"<REF:value_0>": "...40KB of text..."}, then the actual textarea value is that 40KB text.`,yourTask:[`1. Review the summary.detectedIssues for critical problems`,`2. Examine timeline.stateChanges for unexpected state transitions`,`3. Look for patterns in events where stateSnapshot.valuesMatch is false`,`4. Check for rapid events (<5ms apart) that might indicate race conditions`,`5. If you see <REF:value_N> references, look them up in valueRegistry`,`6. Identify the root cause and suggest specific code fixes`]},debugReport:i},null,2)}clear(){this.events=[],this.issueRegistry.clear(),this.lastEventTimestamp=null,this.valueRegistry.clear()}};const N=(e,t,n)=>{if(!n)return s(`div`,{children:e},t);let r=m(n);return s(`div`,{className:r?void 0:n,style:r?{backgroundColor:n}:void 0,children:e},t)},P=(e,t,n,r)=>{if(n.length===0)return N(e||`\xA0`,t,r);let i=n.toSorted((e,t)=>e.start-t.start),a=[],o=0;if(i.forEach((n,r)=>{let{className:i,end:c,start:l,style:u}=n,d=Math.max(0,Math.min(l,e.length)),f=Math.max(d,Math.min(c,e.length));if(f<=o)return;let p=Math.max(d,o);if(p>o){let t=e.slice(o,p);t&&a.push(t)}if(f>p){let o=e.slice(p,f);a.push(s(`span`,{className:i,style:u,"data-range-start":n.absoluteStart,children:o},`highlight-${t}-${r.toString()}`))}o=Math.max(o,f)}),o<e.length){let t=e.slice(o);t&&a.push(t)}return N(a.length===0?`\xA0`:a,t,r)},F=e(({className:e=``,containerClassName:o=``,defaultValue:l=``,dir:u=`ltr`,enableAutoResize:f=!0,highlights:p=[],lineHighlights:m={},onChange:h,rows:_=4,style:v,value:b,debug:x=!1,debugMaxEvents:S=1e3,...D},O)=>{let k=a(null),A=a(void 0),j=i(()=>new M(x,S),[x,S]);n(()=>{j.setEnabled(x)},[x,j]);let N=b!==void 0,F=t(()=>A.current,[]),{currentValue:I,handleChange:L,setValue:R,textareaRef:z}=C(b,l,h,j,void 0,F),B=t(()=>I,[I]),{handleAutoResize:V,textareaHeight:H}=d(f,j,z,B,N);n(()=>{A.current=H},[H]);let{highlightLayerRef:U,syncScroll:W,syncStyles:G}=y(j,z,B,F,N),K=g(I,p,m,P),q=t(e=>{L(e),V(e.target)},[L,V]),J=t(e=>{R(e),z.current&&V(z.current)},[R,V,z]);n(()=>{},[]);let Y=t(()=>{W(z)},[W,z]);n(()=>{if(!x)return;let e=setInterval(()=>{j.record(`snapshot`,`system`,{},z,I,H,N)},1e3);return()=>clearInterval(e)},[x,j,z,I,H,N]),r(O,()=>({blur:()=>z.current?.blur(),exportForAI:()=>j.exportForAI(z,I,H,p),focus:()=>z.current?.focus(),getValue:()=>I,scrollToPosition:(e,t=40,n=`auto`)=>{if(U.current&&z.current){let r=U.current.querySelector(`[data-range-start="${e.toString()}"]`);if(r instanceof HTMLElement){let e=z.current,i=r.offsetTop;n===`smooth`?e.scrollTo({behavior:`smooth`,top:i-t}):e.scrollTop=i-t}}},select:()=>z.current?.select(),setSelectionRange:(e,t)=>z.current?.setSelectionRange(e,t),setValue:J}),[I,J,U,z,j,H,p]),n(()=>{z.current&&f&&V(z.current),G(z)},[I,V,f,G,z]),n(()=>{if(!z.current)return;let e=z.current,t,n=new ResizeObserver(()=>{cancelAnimationFrame(t),t=requestAnimationFrame(()=>{G(z),f&&V(e)})});return n.observe(e),()=>{n.disconnect(),cancelAnimationFrame(t)}},[z,G,V,f]);let X={...T,color:I?`transparent`:`inherit`,height:H?`${H}px`:void 0,resize:f?`none`:`vertical`},Z={...E,direction:u,height:H?`${H}px`:void 0,padding:z.current?getComputedStyle(z.current).padding:`8px 12px`};return c(`div`,{className:o,ref:k,style:{...w,...v},children:[s(`div`,{"aria-hidden":`true`,ref:U,style:Z,children:K}),s(`textarea`,{className:e,dir:u,onChange:q,onScroll:Y,ref:z,rows:_,style:X,value:I,...D})]})});F.displayName=`DyeLight`;export{M as AIOptimizedTelemetry,F as DyeLight,l as HighlightBuilder,u as autoResize,N as createLineElement,P as renderHighlightedLine};
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/builder.ts","../src/domUtils.ts","../src/hooks/useAutoResize.ts","../src/textUtils.ts","../src/hooks/useHighlightedContent.ts","../src/hooks/useHighlightSync.ts","../src/hooks/useTextareaValue.ts","../src/styles.ts","../src/telemetry.ts","../src/DyeLight.tsx"],"sourcesContent":["/**\n * @fileoverview HighlightBuilder - Utility functions for creating highlight objects\n *\n * This module provides a convenient API for building highlight configurations\n * for the DyeLight component. It includes methods for creating character-level\n * highlights, line-level highlights, pattern-based highlights, and more.\n */\n\nimport type React from 'react';\n\n/**\n * Utility functions for building highlight objects for the DyeLight component\n * Provides a fluent API for creating various types of text highlights\n */\nexport const HighlightBuilder = {\n /**\n * Creates highlights for individual characters using absolute positions\n * @param chars - Array of character highlight configurations\n * @param chars[].index - Zero-based character index in the text\n * @param chars[].className - Optional CSS class name to apply\n * @param chars[].style - Optional inline styles to apply\n * @returns Array of character range highlights\n * @example\n * ```tsx\n * // Highlight characters at positions 5, 10, and 15\n * const highlights = HighlightBuilder.characters([\n * { index: 5, className: 'highlight-error' },\n * { index: 10, className: 'highlight-warning' },\n * { index: 15, style: { backgroundColor: 'yellow' } }\n * ]);\n * ```\n */\n characters: (chars: Array<{ className?: string; index: number; style?: React.CSSProperties }>) => {\n return chars.map(({ className, index, style }) => ({ className, end: index + 1, start: index, style }));\n },\n\n /**\n * Creates line highlights for entire lines\n * @param lines - Array of line highlight configurations\n * @param lines[].line - Zero-based line number\n * @param lines[].className - Optional CSS class name to apply to the line\n * @param lines[].color - Optional color value (CSS color, hex, rgb, etc.)\n * @returns Object mapping line numbers to highlight values\n * @example\n * ```tsx\n * // Highlight lines 0 and 2 with different styles\n * const lineHighlights = HighlightBuilder.lines([\n * { line: 0, className: 'error-line' },\n * { line: 2, color: '#ffff00' }\n * ]);\n * ```\n */\n lines: (lines: Array<{ className?: string; color?: string; line: number }>) => {\n const result: { [lineNumber: number]: string } = {};\n lines.forEach(({ className, color, line }) => {\n result[line] = className || color || '';\n });\n return result;\n },\n\n /**\n * Highlights text matching a pattern using absolute positions\n * @param text - The text to search within\n * @param pattern - Regular expression or string pattern to match\n * @param className - Optional CSS class name to apply to matches\n * @param style - Optional inline styles to apply to matches\n * @returns Array of character range highlights for all matches\n * @example\n * ```tsx\n * // Highlight all JavaScript keywords\n * const highlights = HighlightBuilder.pattern(\n * code,\n * /\\b(function|const|let|var|if|else|for|while)\\b/g,\n * 'keyword-highlight'\n * );\n *\n * // Highlight all email addresses\n * const emailHighlights = HighlightBuilder.pattern(\n * text,\n * /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g,\n * 'email-highlight'\n * );\n * ```\n */\n pattern: (text: string, pattern: RegExp | string, className?: string, style?: React.CSSProperties) => {\n const regex = typeof pattern === 'string' ? new RegExp(pattern, 'g') : new RegExp(pattern.source, 'g');\n const matches = Array.from(text.matchAll(regex));\n\n return matches.map((match) => ({ className, end: match.index! + match[0].length, start: match.index!, style }));\n },\n\n /**\n * Creates character range highlights using absolute positions\n * @param ranges - Array of character range configurations\n * @param ranges[].start - Zero-based start index (inclusive)\n * @param ranges[].end - Zero-based end index (exclusive)\n * @param ranges[].className - Optional CSS class name to apply\n * @param ranges[].style - Optional inline styles to apply\n * @returns Array of character range highlights\n * @example\n * ```tsx\n * // Highlight specific ranges in the text\n * const highlights = HighlightBuilder.ranges([\n * { start: 0, end: 5, className: 'title-highlight' },\n * { start: 10, end: 20, style: { backgroundColor: 'yellow' } },\n * { start: 25, end: 30, className: 'error-highlight' }\n * ]);\n * ```\n */\n ranges: (ranges: Array<{ className?: string; end: number; start: number; style?: React.CSSProperties }>) => {\n return ranges.map(({ className, end, start, style }) => ({ className, end, start, style }));\n },\n\n /**\n * Highlights text between specific start and end positions\n * Convenience method for highlighting a single selection range\n * @param start - Zero-based start index (inclusive)\n * @param end - Zero-based end index (exclusive)\n * @param className - Optional CSS class name to apply\n * @param style - Optional inline styles to apply\n * @returns Array containing a single character range highlight\n * @example\n * ```tsx\n * // Highlight a selection from position 10 to 25\n * const selectionHighlight = HighlightBuilder.selection(\n * 10,\n * 25,\n * 'selection-highlight'\n * );\n * ```\n */\n selection: (start: number, end: number, className?: string, style?: React.CSSProperties) => {\n return [{ className, end, start, style }];\n },\n\n /**\n * Highlights entire words that match specific terms\n * @param text - The text to search within\n * @param words - Array of words to highlight\n * @param className - Optional CSS class name to apply to matched words\n * @param style - Optional inline styles to apply to matched words\n * @returns Array of character range highlights for all matched words\n * @example\n * ```tsx\n * // Highlight specific programming keywords\n * const highlights = HighlightBuilder.words(\n * sourceCode,\n * ['function', 'const', 'let', 'var', 'return'],\n * 'keyword'\n * );\n *\n * // Highlight important terms with custom styling\n * const termHighlights = HighlightBuilder.words(\n * document,\n * ['TODO', 'FIXME', 'NOTE'],\n * undefined,\n * { backgroundColor: 'orange', fontWeight: 'bold' }\n * );\n * ```\n */\n words: (text: string, words: string[], className?: string, style?: React.CSSProperties) => {\n const pattern = new RegExp(`\\\\b(${words.join('|')})\\\\b`, 'g');\n return HighlightBuilder.pattern(text, pattern, className, style);\n },\n};\n","/**\n * @fileoverview DOM utility functions for textarea manipulation\n *\n * This module provides utility functions for working with DOM elements,\n * specifically focused on textarea auto-resizing functionality used by\n * the DyeLight component.\n */\n\n/**\n * Automatically resizes a textarea element to fit its content\n *\n * This function adjusts the textarea height to match its scroll height,\n * effectively removing scrollbars when the content fits and expanding\n * the textarea as content is added. The height is first set to 'auto'\n * to allow the element to shrink if content is removed.\n *\n * @param textArea - The HTML textarea element to resize\n * @example\n * ```ts\n * const textarea = document.querySelector('textarea');\n * if (textarea) {\n * autoResize(textarea);\n * }\n *\n * // Or in an event handler:\n * const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n * autoResize(e.target);\n * };\n * ```\n */\nexport const autoResize = (textArea: HTMLTextAreaElement) => {\n const computedStyle = getComputedStyle(textArea);\n const borderTop = parseFloat(computedStyle.borderTopWidth) || 0;\n const borderBottom = parseFloat(computedStyle.borderBottomWidth) || 0;\n\n // Reset height to auto to force accurate scrollHeight calculation (shrink if needed)\n textArea.style.height = 'auto';\n\n const scrollHeight = textArea.scrollHeight;\n const totalBorderHeight = borderTop + borderBottom;\n\n // Only add border compensation if borders are significant (> 2px), otherwise trust scrollHeight\n // This handles browser differences in how scrollHeight reports when box-sizing is border-box\n const finalHeight = totalBorderHeight > 2 ? scrollHeight + totalBorderHeight : scrollHeight;\n\n // Set height including borders for border-box\n textArea.style.height = `${finalHeight}px`;\n};\n","/**\n * @fileoverview Hook for managing textarea auto-resize functionality\n */\n\nimport { useCallback, useState } from 'react';\nimport { autoResize } from '@/domUtils';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Creates an auto-resize handler function\n * @param enableAutoResize Whether auto-resize is enabled\n * @param setTextareaHeight Function to update textarea height state\n * @param resize The resize function to use (defaults to autoResize)\n * @returns Handler function that resizes the textarea\n */\nexport const createAutoResizeHandler = (\n enableAutoResize: boolean,\n setTextareaHeight: (height: number | undefined) => void,\n resize: (element: HTMLTextAreaElement) => void = autoResize,\n) => {\n return (element: HTMLTextAreaElement) => {\n if (!enableAutoResize) {\n return;\n }\n\n resize(element);\n setTextareaHeight(element.scrollHeight);\n };\n};\n\n/**\n * Hook for managing textarea auto-resize functionality\n * Automatically adjusts textarea height based on content\n *\n * @param enableAutoResize Whether to enable automatic resizing\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional textarea reference for telemetry\n * @param getCurrentValue Optional function to get current value for telemetry\n * @param isControlled Whether the component is in controlled mode\n * @returns Object containing resize handler and current height\n */\nexport const useAutoResize = (\n enableAutoResize: boolean,\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement>,\n getCurrentValue?: () => string,\n isControlled?: boolean,\n) => {\n const [textareaHeight, setTextareaHeight] = useState<number | undefined>();\n\n /**\n * Handles automatic resizing of the textarea based on content\n * Records telemetry data if telemetry is enabled\n */\n const handleAutoResize = useCallback(\n (element: HTMLTextAreaElement) => {\n if (!enableAutoResize) {\n return;\n }\n\n const beforeHeight = element.scrollHeight;\n\n autoResize(element);\n\n const afterHeight = element.scrollHeight;\n\n telemetry?.record(\n 'autoResize',\n 'system',\n { afterHeight, beforeHeight, changed: beforeHeight !== afterHeight, textLength: element.value.length },\n textareaRef ?? { current: element },\n getCurrentValue?.() ?? element.value,\n afterHeight,\n isControlled ?? false,\n );\n\n setTextareaHeight(element.scrollHeight);\n },\n [enableAutoResize, telemetry, textareaRef, getCurrentValue, isControlled],\n );\n\n return { handleAutoResize, textareaHeight };\n};\n","/**\n * @fileoverview Text utility functions for position calculations and text processing\n *\n * This module provides utilities for converting between absolute text positions\n * and line-relative positions, as well as other text processing functions used\n * by the DyeLight component for highlight positioning.\n */\n\n/**\n * Analyzes text content and returns line information with position mappings\n * @param text - The text content to analyze\n * @returns Object containing lines array and line start positions\n * @returns returns.lines - Array of individual lines (without newline characters)\n * @returns returns.lineStarts - Array of absolute positions where each line starts\n * @example\n * ```ts\n * const text = \"Hello\\nWorld\\nTest\";\n * const { lines, lineStarts } = getLinePositions(text);\n * // lines: [\"Hello\", \"World\", \"Test\"]\n * // lineStarts: [0, 6, 12]\n * ```\n */\nexport const getLinePositions = (text: string) => {\n const lines = text.split('\\n');\n const lineStarts: number[] = [];\n let position = 0;\n\n lines.forEach((line, index) => {\n lineStarts.push(position);\n position += line.length + (index < lines.length - 1 ? 1 : 0); // +1 for \\n except last line\n });\n\n return { lineStarts, lines };\n};\n\n/**\n * Converts an absolute text position to line-relative coordinates\n * @param absolutePos - Zero-based absolute position in the entire text\n * @param lineStarts - Array of line start positions (from getLinePositions)\n * @returns Object containing line number and character position within that line\n * @returns returns.line - Zero-based line number\n * @returns returns.char - Zero-based character position within the line\n * @example\n * ```ts\n * const text = \"Hello\\nWorld\\nTest\";\n * const { lineStarts } = getLinePositions(text);\n * const pos = absoluteToLinePos(8, lineStarts);\n * // pos: { line: 1, char: 2 } (the 'r' in \"World\")\n * ```\n */\nexport const absoluteToLinePos = (absolutePos: number, lineStarts: number[]) => {\n for (let i = lineStarts.length - 1; i >= 0; i--) {\n if (absolutePos >= lineStarts[i]) {\n return { char: absolutePos - lineStarts[i], line: i };\n }\n }\n return { char: 0, line: 0 };\n};\n\n/**\n * Determines if a string value represents a CSS color value\n * @param value - The string value to test\n * @returns True if the value appears to be a color value, false otherwise\n * @example\n * ```ts\n * isColorValue('#ff0000'); // true\n * isColorValue('rgb(255, 0, 0)'); // true\n * isColorValue('red'); // true\n * isColorValue('my-css-class'); // false (contains hyphens)\n * isColorValue('transparent'); // true\n * isColorValue('var(--primary-color)'); // true\n * ```\n */\nexport const isColorValue = (value: string): boolean => {\n return (\n /^(#|rgb|hsl|var\\(--.*?\\)|transparent|currentColor|inherit|initial|unset)/i.test(value) ||\n /^[a-z]+$/i.test(value)\n );\n};\n","import { useMemo } from 'react';\nimport { absoluteToLinePos, getLinePositions } from '@/textUtils';\nimport type { CharacterRange } from '@/types';\n\nexport const computeHighlightedContent = (\n text: string,\n highlights: CharacterRange[],\n lineHighlights: { [lineNumber: number]: string },\n renderHighlightedLine: (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n ) => React.ReactElement,\n) => {\n const { lines, lineStarts } = getLinePositions(text);\n\n const highlightsByLine: {\n [lineIndex: number]: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>;\n } = {};\n\n highlights.forEach((highlight) => {\n const startPos = absoluteToLinePos(highlight.start, lineStarts);\n const endPos = absoluteToLinePos(highlight.end - 1, lineStarts);\n\n for (let lineIndex = startPos.line; lineIndex <= endPos.line; lineIndex++) {\n if (!highlightsByLine[lineIndex]) {\n highlightsByLine[lineIndex] = [];\n }\n\n const lineStart = lineStarts[lineIndex];\n\n const rangeStart = Math.max(highlight.start - lineStart, 0);\n const rangeEnd = Math.min(highlight.end - lineStart, lines[lineIndex].length);\n\n if (rangeEnd > rangeStart) {\n highlightsByLine[lineIndex].push({\n absoluteStart: highlight.start,\n className: highlight.className,\n end: rangeEnd,\n start: rangeStart,\n style: highlight.style,\n });\n }\n }\n });\n\n return lines.map((line, lineIndex) => {\n const lineHighlight = lineHighlights[lineIndex];\n const ranges = highlightsByLine[lineIndex] || [];\n\n return renderHighlightedLine(line, lineIndex, ranges, lineHighlight);\n });\n};\n\n/**\n * Hook for computing highlighted content from text and highlight ranges\n */\nexport const useHighlightedContent = (\n text: string,\n highlights: CharacterRange[],\n lineHighlights: { [lineNumber: number]: string },\n renderHighlightedLine: (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n ) => React.ReactElement,\n) => {\n /**\n * Computes the highlighted content by processing text and highlight ranges\n * Groups highlights by line and renders each line with appropriate highlighting\n */\n const highlightedContent = useMemo(\n () => computeHighlightedContent(text, highlights, lineHighlights, renderHighlightedLine),\n [text, highlights, lineHighlights, renderHighlightedLine],\n );\n\n return highlightedContent;\n};\n","/**\n * @fileoverview Hook for managing highlight layer synchronization\n */\n\nimport { useCallback, useRef } from 'react';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Synchronizes scroll positions between textarea and highlight layer\n * @param textarea The textarea element\n * @param highlightLayer The highlight overlay element\n */\nexport const syncScrollPositions = (\n textarea: Pick<HTMLTextAreaElement, 'scrollLeft' | 'scrollTop'> | null,\n highlightLayer: Pick<HTMLDivElement, 'scrollLeft' | 'scrollTop'> | null,\n) => {\n if (!textarea || !highlightLayer) {\n return;\n }\n\n const { scrollLeft, scrollTop } = textarea;\n highlightLayer.scrollTop = scrollTop;\n highlightLayer.scrollLeft = scrollLeft;\n};\n\n/**\n * Synchronizes styles between textarea and highlight layer for pixel-perfect alignment\n * @param textarea The textarea element\n * @param highlightLayer The highlight overlay element\n * @param computeStyle Function to compute styles (defaults to getComputedStyle)\n */\nexport const syncHighlightStyles = (\n textarea: HTMLTextAreaElement | null,\n highlightLayer: HTMLDivElement | null,\n computeStyle: (element: Element) => CSSStyleDeclaration = getComputedStyle,\n) => {\n if (!textarea || !highlightLayer) {\n return;\n }\n\n const computedStyle = computeStyle(textarea);\n\n highlightLayer.style.padding = computedStyle.padding;\n highlightLayer.style.fontSize = computedStyle.fontSize;\n highlightLayer.style.fontFamily = computedStyle.fontFamily;\n highlightLayer.style.lineHeight = computedStyle.lineHeight;\n highlightLayer.style.letterSpacing = computedStyle.letterSpacing;\n highlightLayer.style.wordSpacing = computedStyle.wordSpacing;\n highlightLayer.style.textIndent = computedStyle.textIndent;\n\n highlightLayer.style.whiteSpace = computedStyle.whiteSpace;\n highlightLayer.style.wordBreak = computedStyle.wordBreak;\n highlightLayer.style.overflowWrap = computedStyle.overflowWrap;\n highlightLayer.style.tabSize = computedStyle.tabSize;\n\n highlightLayer.style.borderTopWidth = computedStyle.borderTopWidth;\n highlightLayer.style.borderRightWidth = computedStyle.borderRightWidth;\n highlightLayer.style.borderBottomWidth = computedStyle.borderBottomWidth;\n highlightLayer.style.borderLeftWidth = computedStyle.borderLeftWidth;\n highlightLayer.style.borderTopStyle = computedStyle.borderTopStyle;\n highlightLayer.style.borderRightStyle = computedStyle.borderRightStyle;\n highlightLayer.style.borderBottomStyle = computedStyle.borderBottomStyle;\n highlightLayer.style.borderLeftStyle = computedStyle.borderLeftStyle;\n highlightLayer.style.borderColor = 'transparent';\n\n const borderLeft = parseFloat(computedStyle.borderLeftWidth) || 0;\n const borderRight = parseFloat(computedStyle.borderRightWidth) || 0;\n const scrollbarWidth = textarea.offsetWidth - textarea.clientWidth - borderLeft - borderRight;\n\n if (scrollbarWidth > 0) {\n const isRTL = computedStyle.direction === 'rtl';\n if (isRTL) {\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n highlightLayer.style.paddingLeft = `${paddingLeft + scrollbarWidth}px`;\n } else {\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n highlightLayer.style.paddingRight = `${paddingRight + scrollbarWidth}px`;\n }\n }\n};\n\n/**\n * Hook for managing highlight layer synchronization\n * Provides functions to sync scroll and styles between textarea and overlay\n *\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional textarea reference for telemetry\n * @param getCurrentValue Optional function to get current value for telemetry\n * @param getHeight Optional function to get current height for telemetry\n * @param isControlled Whether the component is in controlled mode\n * @returns Object containing highlight layer ref and sync functions\n */\nexport const useHighlightSync = (\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement>,\n getCurrentValue?: () => string,\n getHeight?: () => number | undefined,\n isControlled?: boolean,\n) => {\n const highlightLayerRef = useRef<HTMLDivElement>(null);\n\n /**\n * Synchronizes scroll position between textarea and highlight layer\n * @param textareaRefParam The textarea ref to sync from\n */\n const syncScroll = useCallback(\n (textareaRefParam: React.RefObject<HTMLTextAreaElement | null>) => {\n const before = {\n scrollLeft: highlightLayerRef.current?.scrollLeft,\n scrollTop: highlightLayerRef.current?.scrollTop,\n };\n\n syncScrollPositions(textareaRefParam.current, highlightLayerRef.current);\n\n const after = {\n scrollLeft: highlightLayerRef.current?.scrollLeft,\n scrollTop: highlightLayerRef.current?.scrollTop,\n };\n\n telemetry?.record(\n 'syncScroll',\n 'sync',\n {\n after,\n before,\n changed: before.scrollTop !== after.scrollTop || before.scrollLeft !== after.scrollLeft,\n },\n textareaRef ?? textareaRefParam,\n getCurrentValue?.() ?? '',\n getHeight?.(),\n isControlled ?? false,\n );\n },\n [telemetry, textareaRef, getCurrentValue, getHeight, isControlled],\n );\n\n /**\n * Synchronizes styles between textarea and highlight layer\n * @param textareaRefParam The textarea ref to sync from\n */\n const syncStyles = useCallback(\n (textareaRefParam: React.RefObject<HTMLTextAreaElement | null>) => {\n if (!textareaRefParam.current || !highlightLayerRef.current) {\n return;\n }\n\n const computedStyle = getComputedStyle(textareaRefParam.current);\n const scrollbarWidth =\n textareaRefParam.current.offsetWidth -\n textareaRefParam.current.clientWidth -\n (parseFloat(computedStyle.borderLeftWidth) || 0) -\n (parseFloat(computedStyle.borderRightWidth) || 0);\n\n syncHighlightStyles(textareaRefParam.current, highlightLayerRef.current);\n\n telemetry?.record(\n 'syncStyles',\n 'sync',\n {\n direction: computedStyle.direction,\n fontSize: computedStyle.fontSize,\n padding: computedStyle.padding,\n scrollbarWidth,\n },\n textareaRef ?? textareaRefParam,\n getCurrentValue?.() ?? '',\n getHeight?.(),\n isControlled ?? false,\n );\n },\n [telemetry, textareaRef, getCurrentValue, getHeight, isControlled],\n );\n\n return { highlightLayerRef, syncScroll, syncStyles };\n};\n","/**\n * @fileoverview Hook for managing textarea value state and synchronization\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Synchronizes the textarea DOM value with React state\n * @param textarea The textarea DOM element\n * @param currentValue Current React state value\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const syncValueWithDOM = (\n textarea: HTMLTextAreaElement | null,\n currentValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n) => {\n if (!textarea) {\n return;\n }\n\n const domValue = textarea.value;\n if (domValue !== currentValue) {\n if (!isControlled) {\n setInternalValue(domValue);\n }\n onChange?.(domValue);\n }\n};\n\n/**\n * Handles value changes from user input\n * @param newValue New value from user input\n * @param currentValue Current React state value\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const handleChangeValue = (\n newValue: string,\n currentValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n) => {\n if (newValue === currentValue) {\n return;\n }\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n};\n\n/**\n * Applies a programmatic value change via setValue\n * @param textarea The textarea DOM element\n * @param newValue New value to set\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const applySetValue = (\n textarea: HTMLTextAreaElement | null,\n newValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n) => {\n if (!textarea) {\n return;\n }\n\n textarea.value = newValue;\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n};\n\n/**\n * Hook for managing textarea value state and synchronization\n * Handles both controlled and uncontrolled modes, plus programmatic changes\n *\n * @param value Controlled value (optional)\n * @param defaultValue Default value for uncontrolled mode\n * @param onChange Callback when value changes\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional external textarea ref\n * @param getHeight Optional function to get current textarea height\n * @returns Object containing current value, change handler, setValue function, and textarea ref\n */\nexport const useTextareaValue = (\n value?: string,\n defaultValue = '',\n onChange?: (value: string) => void,\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement>,\n getHeight?: () => number | undefined,\n) => {\n const internalTextareaRef = useRef<HTMLTextAreaElement>(null);\n const [internalValue, setInternalValue] = useState(value ?? defaultValue);\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? (value ?? '') : internalValue;\n const actualTextareaRef = textareaRef ?? internalTextareaRef;\n\n const syncValueWithDOMCallback = useCallback(() => {\n syncValueWithDOM(actualTextareaRef.current, currentValue, isControlled, setInternalValue, onChange);\n }, [currentValue, isControlled, onChange, actualTextareaRef]);\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n\n telemetry?.record(\n 'onChange',\n 'user',\n {\n lengthDelta: newValue.length - currentValue.length,\n newValue,\n previousValue: currentValue,\n valueLength: newValue.length,\n },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n\n handleChangeValue(newValue, currentValue, isControlled, setInternalValue, onChange);\n },\n [currentValue, isControlled, onChange, telemetry, actualTextareaRef, getHeight],\n );\n\n const setValue = useCallback(\n (newValue: string) => {\n telemetry?.record(\n 'setValue',\n 'state',\n { newValue, previousValue: currentValue },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n\n applySetValue(actualTextareaRef.current, newValue, isControlled, setInternalValue, onChange);\n },\n [isControlled, onChange, telemetry, actualTextareaRef, getHeight, currentValue],\n );\n\n useEffect(() => {\n syncValueWithDOMCallback();\n }, [syncValueWithDOMCallback]);\n\n useEffect(() => {\n if (isControlled && actualTextareaRef.current && actualTextareaRef.current.value !== currentValue) {\n actualTextareaRef.current.value = currentValue;\n }\n }, [isControlled, currentValue, actualTextareaRef]);\n\n useEffect(() => {\n if (actualTextareaRef.current && actualTextareaRef.current.value !== currentValue) {\n telemetry?.record(\n 'valueMismatch',\n 'state',\n { domValue: actualTextareaRef.current.value, stateValue: currentValue },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n }\n }, [currentValue, isControlled, telemetry, actualTextareaRef, getHeight]);\n\n return { currentValue, handleChange, setValue, textareaRef: actualTextareaRef };\n};\n","/**\n * @fileoverview Default styles for DyeLight component elements\n *\n * This module contains the default CSS-in-JS styles used by the DyeLight component\n * for its container, textarea, and highlight layer elements. These styles ensure\n * proper layering, positioning, and visual alignment between the textarea and\n * its highlight overlay.\n */\n\nimport type React from 'react';\n\n/**\n * Default styles for the main container element that wraps the entire component\n * Creates a positioned container that serves as the reference for absolutely positioned children\n */\nexport const DEFAULT_CONTAINER_STYLE: React.CSSProperties = {\n /** Ensures the container behaves as a block-level element */\n display: 'block',\n /** Enables absolute positioning for child elements */\n position: 'relative',\n /** Takes full width of parent container */\n width: '100%',\n};\n\n/**\n * Default styles for the textarea element\n * Makes the textarea transparent while maintaining its functionality and positioning\n */\nexport const DEFAULT_BASE_STYLE: React.CSSProperties = {\n /** Transparent background to show highlights underneath */\n background: 'transparent',\n /** Ensures consistent box model calculations */\n boxSizing: 'border-box',\n /** Maintains visible text cursor */\n caretColor: '#000000',\n /** Transparent text to reveal highlights while keeping cursor and selection */\n color: 'transparent',\n /** Prevents unwanted height constraints */\n minHeight: 'auto',\n /** Positioned above the highlight layer */\n position: 'relative',\n /** Takes full width of container */\n width: '100%',\n /** Ensures textarea appears above highlight layer */\n zIndex: 2,\n};\n\n/**\n * Default styles for the highlight layer that renders behind the textarea\n * Positioned absolutely to overlay perfectly with the textarea content\n */\nexport const DEFAULT_HIGHLIGHT_LAYER_STYLE: React.CSSProperties = {\n /** Transparent border to match textarea default border - now synced dynamically */\n border: '0px none transparent',\n /** Stretch to fill container bottom */\n bottom: 0,\n /** Consistent box model with textarea */\n boxSizing: 'border-box',\n /** Inherit text color from parent */\n color: 'inherit',\n /** Match textarea font family */\n fontFamily: 'inherit',\n /** Match textarea font size */\n fontSize: 'inherit',\n /** Stretch to fill container left */\n left: 0,\n /** Match textarea line height */\n lineHeight: 'inherit',\n /** Remove default margins */\n margin: 0,\n /** Hide scrollbars on highlight layer */\n overflow: 'hidden',\n /** Prevent highlight layer from capturing mouse events */\n pointerEvents: 'none',\n /** Positioned absolutely within container */\n position: 'absolute',\n /** Stretch to fill container right */\n right: 0,\n /** Stretch to fill container top */\n top: 0,\n /** Preserve whitespace and line breaks like textarea */\n whiteSpace: 'pre-wrap',\n /** Break long words like textarea */\n wordWrap: 'break-word',\n /** Ensure highlight layer appears behind textarea */\n zIndex: 1,\n};\n","/**\n * @fileoverview AI-Optimized Telemetry System for DyeLight\n *\n * This format is specifically designed to be parsed by LLMs for debugging.\n * It includes rich context, clear narratives, and structured anomaly detection.\n */\n\nimport React from 'react';\n\n/**\n * Represents a single telemetry event with full context\n */\nexport type AITelemetryEvent = {\n /** Unix timestamp in milliseconds */\n timestamp: number;\n /** ISO 8601 formatted timestamp */\n timestampISO: string;\n /** Milliseconds since the last event, or null for first event */\n timeSinceLastEvent: number | null;\n /** Event type identifier */\n type: string;\n /** Event category for grouping and analysis */\n category: 'state' | 'dom' | 'sync' | 'user' | 'system';\n /** Human-readable description of the event */\n description: string;\n /** Event-specific data payload */\n data: Record<string, unknown>;\n /** Complete state snapshot at the time of this event */\n stateSnapshot: {\n /** Actual value in the textarea DOM element */\n textareaValue: string;\n /** Value in React state */\n reactValue: string;\n /** Whether DOM and React state are synchronized */\n valuesMatch: boolean;\n /** Current height of the textarea in pixels */\n textareaHeight: number | undefined;\n /** Whether the component is in controlled mode */\n isControlled: boolean;\n };\n /** Detected anomalies or issues at this event */\n anomalies: string[];\n};\n\n/**\n * Complete AI-readable debug report structure\n */\nexport type AIDebugReport = {\n /** Metadata about the report and environment */\n metadata: {\n /** When this report was generated */\n generatedAt: string;\n /** DyeLight version */\n componentVersion: string;\n /** Total number of events recorded */\n totalEvents: number;\n /** Time range covered by events */\n timespan: {\n /** ISO timestamp of first event */\n start: string;\n /** ISO timestamp of last event */\n end: string;\n /** Duration in milliseconds */\n durationMs: number;\n };\n /** Browser user agent string */\n browser: string;\n /** Platform identifier */\n platform: string;\n /** React version */\n reactVersion: string;\n };\n\n /** Analysis summary with detected issues */\n summary: {\n /** High-level description of findings */\n description: string;\n /** List of detected issues sorted by severity */\n detectedIssues: Array<{\n /** Issue severity level */\n severity: 'critical' | 'warning' | 'info';\n /** Issue description */\n issue: string;\n /** When this issue first occurred */\n firstOccurrence: string;\n /** How many times this issue occurred */\n occurrenceCount: number;\n /** Event indices where this issue occurred */\n relatedEvents: number[];\n }>;\n /** Suspicious patterns detected in event sequences */\n suspiciousPatterns: string[];\n /** Recommended actions to resolve issues */\n recommendations: string[];\n };\n\n /** Different views of the event timeline */\n timeline: {\n /** User interactions and their impacts */\n userActions: Array<{\n /** When the action occurred */\n timestamp: string;\n /** What the user did */\n action: string;\n /** Number of state changes triggered */\n resultingStateChanges: number;\n }>;\n /** All state mutations in chronological order */\n stateChanges: Array<{\n /** When the change occurred */\n timestamp: string;\n /** Type of state change */\n type: string;\n /** Previous value */\n before: unknown;\n /** New value */\n after: unknown;\n /** Whether this change was unexpected */\n unexpected: boolean;\n }>;\n /** Synchronization operations between textarea and overlay */\n syncOperations: Array<{\n /** When the sync occurred */\n timestamp: string;\n /** Type of synchronization */\n operation: string;\n /** Whether sync was successful */\n success: boolean;\n /** Additional sync details */\n details: Record<string, unknown>;\n }>;\n };\n\n /** Complete chronological list of all events */\n events: AITelemetryEvent[];\n\n /** Final state at time of export */\n finalState: {\n /** Current textarea DOM value */\n textareaValue: string;\n /** Current React state value */\n reactValue: string;\n /** Whether values are synchronized */\n inSync: boolean;\n /** Current height in pixels */\n height: number | undefined;\n /** Current scroll position */\n scrollPosition: { top: number; left: number };\n /** Current highlights configuration */\n highlights: unknown[];\n };\n};\n\n/**\n * AI-optimized telemetry collector for the DyeLight component.\n * Records events with full context and generates comprehensive debug reports\n * that can be analyzed by AI language models.\n */\nexport class AIOptimizedTelemetry {\n private events: AITelemetryEvent[] = [];\n private maxEvents = 1000;\n private enabled = false;\n private lastEventTimestamp: number | null = null;\n private issueRegistry = new Map<string, number>();\n\n /**\n * Creates a new telemetry instance\n * @param enabled Whether telemetry collection is initially enabled\n * @param maxEvents Maximum number of events to retain in memory\n */\n constructor(enabled = false, maxEvents = 1000) {\n this.enabled = enabled;\n this.maxEvents = maxEvents;\n }\n\n /**\n * Enable or disable telemetry collection\n * @param enabled Whether to enable telemetry\n */\n setEnabled(enabled: boolean) {\n this.enabled = enabled;\n }\n\n /**\n * Records an event with full context for AI analysis\n * @param type Event type identifier\n * @param category Event category for grouping\n * @param data Event-specific data\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param isControlled Whether component is in controlled mode\n */\n record(\n type: string,\n category: 'state' | 'dom' | 'sync' | 'user' | 'system',\n data: Record<string, unknown>,\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n isControlled: boolean,\n ) {\n if (!this.enabled) {\n return;\n }\n\n const now = Date.now();\n const timeSinceLastEvent = this.lastEventTimestamp ? now - this.lastEventTimestamp : null;\n\n const textareaValue = textareaRef.current?.value ?? '';\n const reactValue = currentValue ?? '';\n const valuesMatch = textareaValue === reactValue;\n\n const anomalies: string[] = [];\n\n if (!valuesMatch) {\n anomalies.push(`State mismatch: DOM=\"${textareaValue}\" vs React=\"${reactValue}\"`);\n this.issueRegistry.set('state_mismatch', (this.issueRegistry.get('state_mismatch') ?? 0) + 1);\n }\n\n if (timeSinceLastEvent !== null && timeSinceLastEvent < 5) {\n anomalies.push(`Rapid event: ${timeSinceLastEvent}ms since last event`);\n this.issueRegistry.set('rapid_events', (this.issueRegistry.get('rapid_events') ?? 0) + 1);\n }\n\n if (category === 'user' && type === 'onChange') {\n const lengthDelta = Math.abs(textareaValue.length - reactValue.length);\n if (lengthDelta > 100) {\n anomalies.push(`Large paste detected: ${lengthDelta} characters changed`);\n this.issueRegistry.set('large_paste', (this.issueRegistry.get('large_paste') ?? 0) + 1);\n }\n }\n\n const event: AITelemetryEvent = {\n anomalies,\n category,\n data,\n description: this.generateDescription(type, category, data),\n stateSnapshot: { isControlled, reactValue, textareaHeight, textareaValue, valuesMatch },\n timeSinceLastEvent,\n timestamp: now,\n timestampISO: new Date(now).toISOString(),\n type,\n };\n\n this.events.push(event);\n this.lastEventTimestamp = now;\n\n if (this.events.length > this.maxEvents) {\n this.events = this.events.slice(-this.maxEvents);\n }\n }\n\n /**\n * Generates a human-readable description for an event\n * @param type Event type\n * @param category Event category\n * @param data Event data\n * @returns Human-readable description\n */\n private generateDescription(type: string, category: string, data: Record<string, unknown>): string {\n switch (type) {\n case 'onChange':\n return `User typed/pasted text. New length: ${data.valueLength}`;\n case 'autoResize':\n return `Textarea auto-resized from ${data.beforeHeight}px to ${data.afterHeight}px`;\n case 'syncScroll':\n return `Synchronized scroll position to top:${(data as any).after?.scrollTop ?? 0}`;\n case 'syncStyles':\n return `Synchronized styles (padding, font, borders) between textarea and overlay`;\n case 'valueMismatch':\n return `CRITICAL: DOM value differs from React state`;\n case 'setValue':\n return `Programmatic value change via ref.setValue()`;\n case 'snapshot':\n return `Periodic state snapshot for debugging`;\n default:\n return `${category} event: ${type}`;\n }\n }\n\n /**\n * Generates a comprehensive AI-readable debug report\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param highlights Current highlights configuration\n * @param lineHighlights Current line highlights configuration\n * @returns Complete debug report\n */\n generateAIReport(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n highlights: unknown[],\n lineHighlights: Record<number, string>,\n ): AIDebugReport {\n const firstEvent = this.events[0];\n const lastEvent = this.events[this.events.length - 1];\n const timespan = lastEvent && firstEvent ? lastEvent.timestamp - firstEvent.timestamp : 0;\n\n const detectedIssues: AIDebugReport['summary']['detectedIssues'] = [];\n\n for (const [issueType, count] of this.issueRegistry.entries()) {\n const relatedEvents = this.events\n .map((e, i) => (e.anomalies.some((a) => a.includes(issueType)) ? i : -1))\n .filter((i) => i !== -1);\n\n const firstOccurrence = relatedEvents.length > 0 ? this.events[relatedEvents[0]].timestampISO : '';\n\n let severity: 'critical' | 'warning' | 'info' = 'info';\n let issue = issueType;\n\n if (issueType === 'state_mismatch') {\n severity = 'critical';\n issue = 'State desynchronization between DOM and React detected';\n } else if (issueType === 'rapid_events') {\n severity = count > 10 ? 'warning' : 'info';\n issue = 'Rapid successive events detected (possible race condition)';\n } else if (issueType === 'large_paste') {\n severity = 'warning';\n issue = 'Large paste operations detected (may trigger sync issues)';\n }\n\n detectedIssues.push({ firstOccurrence, issue, occurrenceCount: count, relatedEvents, severity });\n }\n\n detectedIssues.sort((a, b) => {\n const severityOrder = { critical: 0, info: 2, warning: 1 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n\n const suspiciousPatterns: string[] = [];\n\n const resizeEvents = this.events.filter((e) => e.type === 'autoResize');\n const valueChanges = this.events.filter((e) => e.type === 'onChange');\n if (resizeEvents.length > valueChanges.length * 2) {\n suspiciousPatterns.push(\n `Excessive resize operations (${resizeEvents.length} resizes vs ${valueChanges.length} value changes). ` +\n `May indicate infinite ResizeObserver loop.`,\n );\n }\n\n const syncEvents = this.events.filter((e) => e.category === 'sync');\n if (syncEvents.length > this.events.length * 0.3) {\n suspiciousPatterns.push(\n `High frequency of sync operations (${syncEvents.length}/${this.events.length} events). ` +\n `May indicate layout thrashing.`,\n );\n }\n\n const recommendations: string[] = [];\n\n if (detectedIssues.some((i) => i.issue.includes('desynchronization'))) {\n recommendations.push(\n 'State desynchronization detected. Check if multiple onChange handlers are bound or ' +\n 'if external code is modifying the textarea DOM directly.',\n );\n }\n\n if (suspiciousPatterns.some((p) => p.includes('ResizeObserver'))) {\n recommendations.push(\n 'Possible infinite ResizeObserver loop. Ensure resize operations are debounced with requestAnimationFrame.',\n );\n }\n\n if (detectedIssues.some((i) => i.issue.includes('race condition'))) {\n recommendations.push(\n 'Rapid events detected. Consider debouncing onChange handlers or checking for double-bound event listeners.',\n );\n }\n\n const timeline = {\n stateChanges: this.events\n .filter((e) => e.category === 'state')\n .map((e) => ({\n after: e.data.newValue,\n before: e.data.previousValue,\n timestamp: e.timestampISO,\n type: e.type,\n unexpected: e.anomalies.length > 0,\n })),\n\n syncOperations: this.events\n .filter((e) => e.category === 'sync')\n .map((e) => ({\n details: e.data,\n operation: e.type,\n success: !e.anomalies.length,\n timestamp: e.timestampISO,\n })),\n userActions: this.events\n .filter((e) => e.category === 'user')\n .map((e) => ({\n action: e.description,\n resultingStateChanges: this.events.filter(\n (se) =>\n se.timestamp > e.timestamp && se.timestamp < e.timestamp + 100 && se.category === 'state',\n ).length,\n timestamp: e.timestampISO,\n })),\n };\n\n return {\n events: this.events,\n\n finalState: {\n height: textareaHeight,\n highlights,\n inSync: (textareaRef.current?.value ?? '') === (currentValue ?? ''),\n reactValue: currentValue ?? '',\n scrollPosition: {\n left: textareaRef.current?.scrollLeft ?? 0,\n top: textareaRef.current?.scrollTop ?? 0,\n },\n textareaValue: textareaRef.current?.value ?? '',\n },\n metadata: {\n browser: navigator.userAgent,\n componentVersion: '1.1.3',\n generatedAt: new Date().toISOString(),\n platform: navigator.platform,\n reactVersion: (React as any).version ?? 'unknown',\n timespan: {\n durationMs: timespan,\n end: lastEvent?.timestampISO ?? '',\n start: firstEvent?.timestampISO ?? '',\n },\n totalEvents: this.events.length,\n },\n\n summary: {\n description:\n detectedIssues.length > 0\n ? `Found ${detectedIssues.length} issue(s) during ${this.events.length} recorded events over ${(timespan / 1000).toFixed(1)}s`\n : `No issues detected during ${this.events.length} events over ${(timespan / 1000).toFixed(1)}s`,\n detectedIssues,\n recommendations,\n suspiciousPatterns,\n },\n\n timeline,\n };\n }\n\n /**\n * Exports telemetry data in AI-optimized format with instructions\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param highlights Current highlights configuration\n * @param lineHighlights Current line highlights configuration\n * @returns JSON string containing debug report and AI instructions\n */\n exportForAI(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n highlights: unknown[],\n lineHighlights: Record<number, string>,\n ): string {\n const report = this.generateAIReport(textareaRef, currentValue, textareaHeight, highlights, lineHighlights);\n\n return JSON.stringify(\n {\n aiInstructions: {\n commonIssues: {\n rapidEvents: 'Events firing <5ms apart - possible race condition or double-bound handlers',\n resizeLoop: 'Excessive resize operations - likely infinite ResizeObserver loop',\n stateMismatch:\n 'DOM value differs from React state - likely caused by multiple onChange handlers or direct DOM manipulation',\n },\n keyFields: {\n 'events[].anomalies': 'Issues detected at each event',\n 'events[].stateSnapshot': 'Full state at each event for comparison',\n finalState: 'Current state at time of export',\n 'summary.detectedIssues': 'Automatically detected problems, sorted by severity',\n 'timeline.stateChanges': 'Chronological view of all state mutations',\n },\n purpose:\n 'This is a debug report from the DyeLight React component. ' +\n 'A textarea with highlighting overlay is experiencing synchronization issues.',\n yourTask: [\n '1. Review the summary.detectedIssues for critical problems',\n '2. Examine timeline.stateChanges for unexpected state transitions',\n '3. Look for patterns in events where stateSnapshot.valuesMatch is false',\n '4. Check for rapid events (<5ms apart) that might indicate race conditions',\n '5. Identify the root cause and suggest specific code fixes',\n ],\n },\n debugReport: report,\n },\n null,\n 2,\n );\n }\n\n /**\n * Clears all recorded events and issue registry\n */\n clear() {\n this.events = [];\n this.issueRegistry.clear();\n this.lastEventTimestamp = null;\n }\n}\n","import type React from 'react';\nimport { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';\nimport { useAutoResize } from './hooks/useAutoResize';\nimport { useHighlightedContent } from './hooks/useHighlightedContent';\nimport { useHighlightSync } from './hooks/useHighlightSync';\nimport { useTextareaValue } from './hooks/useTextareaValue';\nimport { DEFAULT_BASE_STYLE, DEFAULT_CONTAINER_STYLE, DEFAULT_HIGHLIGHT_LAYER_STYLE } from './styles';\nimport { AIOptimizedTelemetry } from './telemetry';\nimport { isColorValue } from './textUtils';\nimport type { DyeLightProps, DyeLightRef } from './types';\n\n/**\n * @fileoverview DyeLight - A React textarea component with advanced text highlighting capabilities\n *\n * This component provides a textarea with overlay-based text highlighting that supports:\n * - Character-level highlighting using absolute text positions\n * - Line-level highlighting with CSS classes or color values\n * - Automatic height adjustment based on content\n * - Synchronized scrolling between textarea and highlight layer\n * - Both controlled and uncontrolled usage patterns\n * - RTL text direction support\n */\n\n/**\n * Creates a line element with optional highlighting\n */\nexport const createLineElement = (\n content: React.ReactNode,\n lineIndex: number,\n lineHighlight?: string,\n): React.ReactElement => {\n if (!lineHighlight) {\n return <div key={lineIndex}>{content}</div>;\n }\n\n const isColor = isColorValue(lineHighlight);\n return (\n <div\n className={isColor ? undefined : lineHighlight}\n key={lineIndex}\n style={isColor ? { backgroundColor: lineHighlight } : undefined}\n >\n {content}\n </div>\n );\n};\n\n/**\n * Renders a single line with character-level highlights and optional line-level highlighting\n */\nexport const renderHighlightedLine = (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n): React.ReactElement => {\n if (ranges.length === 0) {\n const content = line || '\\u00A0';\n return createLineElement(content, lineIndex, lineHighlight);\n }\n\n const sortedRanges = ranges.toSorted((a, b) => a.start - b.start);\n\n const result: React.ReactNode[] = [];\n let lastIndex = 0;\n\n sortedRanges.forEach((range, idx) => {\n const { className, end, start, style: rangeStyle } = range;\n\n const clampedStart = Math.max(0, Math.min(start, line.length));\n const clampedEnd = Math.max(clampedStart, Math.min(end, line.length));\n\n if (clampedEnd <= lastIndex) {\n return;\n }\n\n const effectiveStart = Math.max(clampedStart, lastIndex);\n\n if (effectiveStart > lastIndex) {\n const textBefore = line.slice(lastIndex, effectiveStart);\n if (textBefore) {\n result.push(textBefore);\n }\n }\n\n if (clampedEnd > effectiveStart) {\n const highlightedText = line.slice(effectiveStart, clampedEnd);\n result.push(\n <span\n className={className}\n key={`highlight-${lineIndex}-${idx.toString()}`}\n style={rangeStyle}\n data-range-start={range.absoluteStart}\n >\n {highlightedText}\n </span>,\n );\n }\n\n lastIndex = Math.max(lastIndex, clampedEnd);\n });\n\n if (lastIndex < line.length) {\n const textAfter = line.slice(lastIndex);\n if (textAfter) {\n result.push(textAfter);\n }\n }\n\n const content = result.length === 0 ? '\\u00A0' : result;\n return createLineElement(content, lineIndex, lineHighlight);\n};\n\n/**\n * A textarea component with support for highlighting character ranges using absolute positions\n * and optional line-level highlighting. Perfect for syntax highlighting, error indication,\n * and text annotation without the complexity of line-based positioning.\n */\nexport const DyeLight = forwardRef<DyeLightRef, DyeLightProps>(\n (\n {\n className = '',\n containerClassName = '',\n defaultValue = '',\n dir = 'ltr',\n enableAutoResize = true,\n highlights = [],\n lineHighlights = {},\n onChange,\n rows = 4,\n style,\n value,\n debug = false,\n debugMaxEvents = 1000,\n ...props\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const textareaHeightRef = useRef<number | undefined>(undefined);\n\n const telemetry = useMemo(() => new AIOptimizedTelemetry(debug, debugMaxEvents), [debug, debugMaxEvents]);\n\n useEffect(() => {\n telemetry.setEnabled(debug);\n }, [debug, telemetry]);\n\n const isControlled = value !== undefined;\n const getHeight = useCallback(() => textareaHeightRef.current, []);\n\n const { currentValue, handleChange, setValue, textareaRef } = useTextareaValue(\n value,\n defaultValue,\n onChange,\n telemetry,\n undefined,\n getHeight,\n );\n\n const getCurrentValue = useCallback(() => currentValue, [currentValue]);\n\n const { handleAutoResize, textareaHeight } = useAutoResize(\n enableAutoResize,\n telemetry,\n textareaRef,\n getCurrentValue,\n isControlled,\n );\n\n useEffect(() => {\n textareaHeightRef.current = textareaHeight;\n }, [textareaHeight]);\n\n const { highlightLayerRef, syncScroll, syncStyles } = useHighlightSync(\n telemetry,\n textareaRef,\n getCurrentValue,\n getHeight,\n isControlled,\n );\n\n const highlightedContent = useHighlightedContent(\n currentValue,\n highlights,\n lineHighlights,\n renderHighlightedLine,\n );\n\n const handleChangeWithResize = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n handleChange(e);\n handleAutoResize(e.target);\n },\n [handleChange, handleAutoResize],\n );\n\n const setValueWithResize = useCallback(\n (newValue: string) => {\n setValue(newValue);\n if (textareaRef.current) {\n handleAutoResize(textareaRef.current);\n }\n },\n [setValue, handleAutoResize, textareaRef],\n );\n\n useEffect(() => {}, []);\n\n const handleScroll = useCallback(() => {\n syncScroll(textareaRef);\n }, [syncScroll, textareaRef]);\n\n useEffect(() => {\n if (!debug) {\n return;\n }\n\n const intervalId = setInterval(() => {\n telemetry.record('snapshot', 'system', {}, textareaRef, currentValue, textareaHeight, isControlled);\n }, 1000);\n\n return () => clearInterval(intervalId);\n }, [debug, telemetry, textareaRef, currentValue, textareaHeight, isControlled]);\n\n useImperativeHandle(\n ref,\n () => ({\n blur: () => textareaRef.current?.blur(),\n exportForAI: () => {\n return telemetry.exportForAI(textareaRef, currentValue, textareaHeight, highlights, lineHighlights);\n },\n focus: () => textareaRef.current?.focus(),\n getValue: () => currentValue,\n scrollToPosition: (pos: number, offset = 40, behavior: ScrollBehavior = 'auto') => {\n if (highlightLayerRef.current && textareaRef.current) {\n const span = highlightLayerRef.current.querySelector(`[data-range-start=\"${pos.toString()}\"]`);\n if (span instanceof HTMLElement) {\n const textarea = textareaRef.current;\n const spanTop = span.offsetTop;\n if (behavior === 'smooth') {\n textarea.scrollTo({ behavior: 'smooth', top: spanTop - offset });\n } else {\n textarea.scrollTop = spanTop - offset;\n }\n }\n }\n },\n select: () => textareaRef.current?.select(),\n setSelectionRange: (start: number, end: number) => textareaRef.current?.setSelectionRange(start, end),\n setValue: setValueWithResize,\n }),\n [\n currentValue,\n setValueWithResize,\n highlightLayerRef,\n textareaRef,\n telemetry,\n textareaHeight,\n highlights,\n lineHighlights,\n ],\n );\n\n useEffect(() => {\n if (textareaRef.current && enableAutoResize) {\n handleAutoResize(textareaRef.current);\n }\n syncStyles(textareaRef);\n }, [currentValue, handleAutoResize, enableAutoResize, syncStyles, textareaRef]);\n\n useEffect(() => {\n if (!textareaRef.current) {\n return;\n }\n\n const textarea = textareaRef.current;\n let rafId: number;\n\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n syncStyles(textareaRef);\n if (enableAutoResize) {\n handleAutoResize(textarea);\n }\n });\n });\n\n observer.observe(textarea);\n return () => {\n observer.disconnect();\n cancelAnimationFrame(rafId);\n };\n }, [textareaRef, syncStyles, handleAutoResize, enableAutoResize]);\n\n const baseTextareaStyle: React.CSSProperties = {\n ...DEFAULT_BASE_STYLE,\n color: currentValue ? 'transparent' : 'inherit',\n height: textareaHeight ? `${textareaHeight}px` : undefined,\n resize: enableAutoResize ? 'none' : 'vertical',\n };\n\n const highlightLayerStyle: React.CSSProperties = {\n ...DEFAULT_HIGHLIGHT_LAYER_STYLE,\n direction: dir,\n height: textareaHeight ? `${textareaHeight}px` : undefined,\n padding: textareaRef.current ? getComputedStyle(textareaRef.current).padding : '8px 12px',\n };\n\n return (\n <div className={containerClassName} ref={containerRef} style={{ ...DEFAULT_CONTAINER_STYLE, ...style }}>\n <div aria-hidden=\"true\" ref={highlightLayerRef} style={highlightLayerStyle}>\n {highlightedContent}\n </div>\n\n <textarea\n className={className}\n dir={dir}\n onChange={handleChangeWithResize}\n onScroll={handleScroll}\n ref={textareaRef}\n rows={rows}\n style={baseTextareaStyle}\n value={currentValue}\n {...props}\n />\n </div>\n );\n },\n);\n\nDyeLight.displayName = 'DyeLight';\n"],"mappings":"wLAcA,MAAa,EAAmB,CAkB5B,WAAa,GACF,EAAM,KAAK,CAAE,YAAW,QAAO,YAAa,CAAE,YAAW,IAAK,EAAQ,EAAG,MAAO,EAAO,QAAO,EAAE,CAmB3G,MAAQ,GAAuE,CAC3E,IAAM,EAA2C,EAAE,CAInD,OAHA,EAAM,SAAS,CAAE,YAAW,QAAO,UAAW,CAC1C,EAAO,GAAQ,GAAa,GAAS,IACvC,CACK,GA2BX,SAAU,EAAc,EAA0B,EAAoB,IAAgC,CAClG,IAAM,EAAQ,OAAO,GAAY,SAAW,IAAI,OAAO,EAAS,IAAI,CAAG,IAAI,OAAO,EAAQ,OAAQ,IAAI,CAGtG,OAFgB,MAAM,KAAK,EAAK,SAAS,EAAM,CAAC,CAEjC,IAAK,IAAW,CAAE,YAAW,IAAK,EAAM,MAAS,EAAM,GAAG,OAAQ,MAAO,EAAM,MAAQ,QAAO,EAAE,EAqBnH,OAAS,GACE,EAAO,KAAK,CAAE,YAAW,MAAK,QAAO,YAAa,CAAE,YAAW,MAAK,QAAO,QAAO,EAAE,CAqB/F,WAAY,EAAe,EAAa,EAAoB,IACjD,CAAC,CAAE,YAAW,MAAK,QAAO,QAAO,CAAC,CA4B7C,OAAQ,EAAc,EAAiB,EAAoB,IAAgC,CACvF,IAAM,EAAc,OAAO,OAAO,EAAM,KAAK,IAAI,CAAC,MAAO,IAAI,CAC7D,OAAO,EAAiB,QAAQ,EAAM,EAAS,EAAW,EAAM,EAEvE,CCtIY,EAAc,GAAkC,CACzD,IAAM,EAAgB,iBAAiB,EAAS,CAC1C,EAAY,WAAW,EAAc,eAAe,EAAI,EACxD,EAAe,WAAW,EAAc,kBAAkB,EAAI,EAGpE,EAAS,MAAM,OAAS,OAExB,IAAM,EAAe,EAAS,aACxB,EAAoB,EAAY,EAIhC,EAAc,EAAoB,EAAI,EAAe,EAAoB,EAG/E,EAAS,MAAM,OAAS,GAAG,EAAY,KCL9B,GACT,EACA,EACA,EACA,EACA,IACC,CACD,GAAM,CAAC,EAAgB,GAAqB,GAA8B,CAiC1E,MAAO,CAAE,iBA3BgB,EACpB,GAAiC,CAC9B,GAAI,CAAC,EACD,OAGJ,IAAM,EAAe,EAAQ,aAE7B,EAAW,EAAQ,CAEnB,IAAM,EAAc,EAAQ,aAE5B,GAAW,OACP,aACA,SACA,CAAE,cAAa,eAAc,QAAS,IAAiB,EAAa,WAAY,EAAQ,MAAM,OAAQ,CACtG,GAAe,CAAE,QAAS,EAAS,CACnC,KAAmB,EAAI,EAAQ,MAC/B,EACA,GAAgB,GACnB,CAED,EAAkB,EAAQ,aAAa,EAE3C,CAAC,EAAkB,EAAW,EAAa,EAAiB,EAAa,CAC5E,CAE0B,iBAAgB,EC3DlC,EAAoB,GAAiB,CAC9C,IAAM,EAAQ,EAAK,MAAM;EAAK,CACxB,EAAuB,EAAE,CAC3B,EAAW,EAOf,OALA,EAAM,SAAS,EAAM,IAAU,CAC3B,EAAW,KAAK,EAAS,CACzB,GAAY,EAAK,QAAU,EAAQ,EAAM,OAAS,EAAI,EAAI,IAC5D,CAEK,CAAE,aAAY,QAAO,EAkBnB,GAAqB,EAAqB,IAAyB,CAC5E,IAAK,IAAI,EAAI,EAAW,OAAS,EAAG,GAAK,EAAG,IACxC,GAAI,GAAe,EAAW,GAC1B,MAAO,CAAE,KAAM,EAAc,EAAW,GAAI,KAAM,EAAG,CAG7D,MAAO,CAAE,KAAM,EAAG,KAAM,EAAG,EAiBlB,EAAgB,GAErB,4EAA4E,KAAK,EAAM,EACvF,YAAY,KAAK,EAAM,CCxElB,GACT,EACA,EACA,EACA,IAYC,CACD,GAAM,CAAE,QAAO,cAAe,EAAiB,EAAK,CAE9C,EAQF,EAAE,CA4BN,OA1BA,EAAW,QAAS,GAAc,CAC9B,IAAM,EAAW,EAAkB,EAAU,MAAO,EAAW,CACzD,EAAS,EAAkB,EAAU,IAAM,EAAG,EAAW,CAE/D,IAAK,IAAI,EAAY,EAAS,KAAM,GAAa,EAAO,KAAM,IAAa,CAClE,EAAiB,KAClB,EAAiB,GAAa,EAAE,EAGpC,IAAM,EAAY,EAAW,GAEvB,EAAa,KAAK,IAAI,EAAU,MAAQ,EAAW,EAAE,CACrD,EAAW,KAAK,IAAI,EAAU,IAAM,EAAW,EAAM,GAAW,OAAO,CAEzE,EAAW,GACX,EAAiB,GAAW,KAAK,CAC7B,cAAe,EAAU,MACzB,UAAW,EAAU,UACrB,IAAK,EACL,MAAO,EACP,MAAO,EAAU,MACpB,CAAC,GAGZ,CAEK,EAAM,KAAK,EAAM,IAAc,CAClC,IAAM,EAAgB,EAAe,GAGrC,OAAO,EAAsB,EAAM,EAFpB,EAAiB,IAAc,EAAE,CAEM,EAAc,EACtE,EAMO,GACT,EACA,EACA,EACA,IAiB2B,MACjB,EAA0B,EAAM,EAAY,EAAgB,EAAsB,CACxF,CAAC,EAAM,EAAY,EAAgB,EAAsB,CAC5D,CClFQ,GACT,EACA,IACC,CACD,GAAI,CAAC,GAAY,CAAC,EACd,OAGJ,GAAM,CAAE,aAAY,aAAc,EAClC,EAAe,UAAY,EAC3B,EAAe,WAAa,GASnB,GACT,EACA,EACA,EAA0D,mBACzD,CACD,GAAI,CAAC,GAAY,CAAC,EACd,OAGJ,IAAM,EAAgB,EAAa,EAAS,CAE5C,EAAe,MAAM,QAAU,EAAc,QAC7C,EAAe,MAAM,SAAW,EAAc,SAC9C,EAAe,MAAM,WAAa,EAAc,WAChD,EAAe,MAAM,WAAa,EAAc,WAChD,EAAe,MAAM,cAAgB,EAAc,cACnD,EAAe,MAAM,YAAc,EAAc,YACjD,EAAe,MAAM,WAAa,EAAc,WAEhD,EAAe,MAAM,WAAa,EAAc,WAChD,EAAe,MAAM,UAAY,EAAc,UAC/C,EAAe,MAAM,aAAe,EAAc,aAClD,EAAe,MAAM,QAAU,EAAc,QAE7C,EAAe,MAAM,eAAiB,EAAc,eACpD,EAAe,MAAM,iBAAmB,EAAc,iBACtD,EAAe,MAAM,kBAAoB,EAAc,kBACvD,EAAe,MAAM,gBAAkB,EAAc,gBACrD,EAAe,MAAM,eAAiB,EAAc,eACpD,EAAe,MAAM,iBAAmB,EAAc,iBACtD,EAAe,MAAM,kBAAoB,EAAc,kBACvD,EAAe,MAAM,gBAAkB,EAAc,gBACrD,EAAe,MAAM,YAAc,cAEnC,IAAM,EAAa,WAAW,EAAc,gBAAgB,EAAI,EAC1D,EAAc,WAAW,EAAc,iBAAiB,EAAI,EAC5D,EAAiB,EAAS,YAAc,EAAS,YAAc,EAAa,EAElF,GAAI,EAAiB,EAEjB,GADc,EAAc,YAAc,MAC/B,CACP,IAAM,EAAc,WAAW,EAAc,YAAY,EAAI,EAC7D,EAAe,MAAM,YAAc,GAAG,EAAc,EAAe,QAChE,CACH,IAAM,EAAe,WAAW,EAAc,aAAa,EAAI,EAC/D,EAAe,MAAM,aAAe,GAAG,EAAe,EAAe,MAgBpE,GACT,EACA,EACA,EACA,EACA,IACC,CACD,IAAM,EAAoB,EAAuB,KAAK,CA0EtD,MAAO,CAAE,oBAAmB,WApET,EACd,GAAkE,CAC/D,IAAM,EAAS,CACX,WAAY,EAAkB,SAAS,WACvC,UAAW,EAAkB,SAAS,UACzC,CAED,EAAoB,EAAiB,QAAS,EAAkB,QAAQ,CAExE,IAAM,EAAQ,CACV,WAAY,EAAkB,SAAS,WACvC,UAAW,EAAkB,SAAS,UACzC,CAED,GAAW,OACP,aACA,OACA,CACI,QACA,SACA,QAAS,EAAO,YAAc,EAAM,WAAa,EAAO,aAAe,EAAM,WAChF,CACD,GAAe,EACf,KAAmB,EAAI,GACvB,KAAa,CACb,GAAgB,GACnB,EAEL,CAAC,EAAW,EAAa,EAAiB,EAAW,EAAa,CACrE,CAuCuC,WAjCrB,EACd,GAAkE,CAC/D,GAAI,CAAC,EAAiB,SAAW,CAAC,EAAkB,QAChD,OAGJ,IAAM,EAAgB,iBAAiB,EAAiB,QAAQ,CAC1D,EACF,EAAiB,QAAQ,YACzB,EAAiB,QAAQ,aACxB,WAAW,EAAc,gBAAgB,EAAI,IAC7C,WAAW,EAAc,iBAAiB,EAAI,GAEnD,EAAoB,EAAiB,QAAS,EAAkB,QAAQ,CAExE,GAAW,OACP,aACA,OACA,CACI,UAAW,EAAc,UACzB,SAAU,EAAc,SACxB,QAAS,EAAc,QACvB,iBACH,CACD,GAAe,EACf,KAAmB,EAAI,GACvB,KAAa,CACb,GAAgB,GACnB,EAEL,CAAC,EAAW,EAAa,EAAiB,EAAW,EAAa,CACrE,CAEmD,EC9J3C,GACT,EACA,EACA,EACA,EACA,IACC,CACD,GAAI,CAAC,EACD,OAGJ,IAAM,EAAW,EAAS,MACtB,IAAa,IACR,GACD,EAAiB,EAAS,CAE9B,IAAW,EAAS,GAYf,GACT,EACA,EACA,EACA,EACA,IACC,CACG,IAAa,IAIZ,GACD,EAAiB,EAAS,CAG9B,IAAW,EAAS,GAWX,GACT,EACA,EACA,EACA,EACA,IACC,CACI,IAIL,EAAS,MAAQ,EAEZ,GACD,EAAiB,EAAS,CAG9B,IAAW,EAAS,GAeX,GACT,EACA,EAAe,GACf,EACA,EACA,EACA,IACC,CACD,IAAM,EAAsB,EAA4B,KAAK,CACvD,CAAC,EAAe,GAAoB,EAAS,GAAS,EAAa,CAEnE,EAAe,IAAU,IAAA,GACzB,EAAe,EAAgB,GAAS,GAAM,EAC9C,EAAoB,GAAe,EAEnC,EAA2B,MAAkB,CAC/C,EAAiB,EAAkB,QAAS,EAAc,EAAc,EAAkB,EAAS,EACpG,CAAC,EAAc,EAAc,EAAU,EAAkB,CAAC,CAEvD,EAAe,EAChB,GAA8C,CAC3C,IAAM,EAAW,EAAE,OAAO,MAE1B,GAAW,OACP,WACA,OACA,CACI,YAAa,EAAS,OAAS,EAAa,OAC5C,WACA,cAAe,EACf,YAAa,EAAS,OACzB,CACD,EACA,EACA,KAAa,CACb,EACH,CAED,EAAkB,EAAU,EAAc,EAAc,EAAkB,EAAS,EAEvF,CAAC,EAAc,EAAc,EAAU,EAAW,EAAmB,EAAU,CAClF,CAEK,EAAW,EACZ,GAAqB,CAClB,GAAW,OACP,WACA,QACA,CAAE,WAAU,cAAe,EAAc,CACzC,EACA,EACA,KAAa,CACb,EACH,CAED,EAAc,EAAkB,QAAS,EAAU,EAAc,EAAkB,EAAS,EAEhG,CAAC,EAAc,EAAU,EAAW,EAAmB,EAAW,EAAa,CAClF,CA0BD,OAxBA,MAAgB,CACZ,GAA0B,EAC3B,CAAC,EAAyB,CAAC,CAE9B,MAAgB,CACR,GAAgB,EAAkB,SAAW,EAAkB,QAAQ,QAAU,IACjF,EAAkB,QAAQ,MAAQ,IAEvC,CAAC,EAAc,EAAc,EAAkB,CAAC,CAEnD,MAAgB,CACR,EAAkB,SAAW,EAAkB,QAAQ,QAAU,GACjE,GAAW,OACP,gBACA,QACA,CAAE,SAAU,EAAkB,QAAQ,MAAO,WAAY,EAAc,CACvE,EACA,EACA,KAAa,CACb,EACH,EAEN,CAAC,EAAc,EAAc,EAAW,EAAmB,EAAU,CAAC,CAElE,CAAE,eAAc,eAAc,WAAU,YAAa,EAAmB,EC1KtE,EAA+C,CAExD,QAAS,QAET,SAAU,WAEV,MAAO,OACV,CAMY,EAA0C,CAEnD,WAAY,cAEZ,UAAW,aAEX,WAAY,UAEZ,MAAO,cAEP,UAAW,OAEX,SAAU,WAEV,MAAO,OAEP,OAAQ,EACX,CAMY,EAAqD,CAE9D,OAAQ,uBAER,OAAQ,EAER,UAAW,aAEX,MAAO,UAEP,WAAY,UAEZ,SAAU,UAEV,KAAM,EAEN,WAAY,UAEZ,OAAQ,EAER,SAAU,SAEV,cAAe,OAEf,SAAU,WAEV,MAAO,EAEP,IAAK,EAEL,WAAY,WAEZ,SAAU,aAEV,OAAQ,EACX,CCwED,IAAa,EAAb,KAAkC,CAC9B,OAAqC,EAAE,CACvC,UAAoB,IACpB,QAAkB,GAClB,mBAA4C,KAC5C,cAAwB,IAAI,IAO5B,YAAY,EAAU,GAAO,EAAY,IAAM,CAC3C,KAAK,QAAU,EACf,KAAK,UAAY,EAOrB,WAAW,EAAkB,CACzB,KAAK,QAAU,EAanB,OACI,EACA,EACA,EACA,EACA,EACA,EACA,EACF,CACE,GAAI,CAAC,KAAK,QACN,OAGJ,IAAM,EAAM,KAAK,KAAK,CAChB,EAAqB,KAAK,mBAAqB,EAAM,KAAK,mBAAqB,KAE/E,EAAgB,EAAY,SAAS,OAAS,GAC9C,EAAa,GAAgB,GAC7B,EAAc,IAAkB,EAEhC,EAAsB,EAAE,CAY9B,GAVK,IACD,EAAU,KAAK,wBAAwB,EAAc,cAAc,EAAW,GAAG,CACjF,KAAK,cAAc,IAAI,kBAAmB,KAAK,cAAc,IAAI,iBAAiB,EAAI,GAAK,EAAE,EAG7F,IAAuB,MAAQ,EAAqB,IACpD,EAAU,KAAK,gBAAgB,EAAmB,qBAAqB,CACvE,KAAK,cAAc,IAAI,gBAAiB,KAAK,cAAc,IAAI,eAAe,EAAI,GAAK,EAAE,EAGzF,IAAa,QAAU,IAAS,WAAY,CAC5C,IAAM,EAAc,KAAK,IAAI,EAAc,OAAS,EAAW,OAAO,CAClE,EAAc,MACd,EAAU,KAAK,yBAAyB,EAAY,qBAAqB,CACzE,KAAK,cAAc,IAAI,eAAgB,KAAK,cAAc,IAAI,cAAc,EAAI,GAAK,EAAE,EAI/F,IAAM,EAA0B,CAC5B,YACA,WACA,OACA,YAAa,KAAK,oBAAoB,EAAM,EAAU,EAAK,CAC3D,cAAe,CAAE,eAAc,aAAY,iBAAgB,gBAAe,cAAa,CACvF,qBACA,UAAW,EACX,aAAc,IAAI,KAAK,EAAI,CAAC,aAAa,CACzC,OACH,CAED,KAAK,OAAO,KAAK,EAAM,CACvB,KAAK,mBAAqB,EAEtB,KAAK,OAAO,OAAS,KAAK,YAC1B,KAAK,OAAS,KAAK,OAAO,MAAM,CAAC,KAAK,UAAU,EAWxD,oBAA4B,EAAc,EAAkB,EAAuC,CAC/F,OAAQ,EAAR,CACI,IAAK,WACD,MAAO,uCAAuC,EAAK,cACvD,IAAK,aACD,MAAO,8BAA8B,EAAK,aAAa,QAAQ,EAAK,YAAY,IACpF,IAAK,aACD,MAAO,uCAAwC,EAAa,OAAO,WAAa,IACpF,IAAK,aACD,MAAO,4EACX,IAAK,gBACD,MAAO,+CACX,IAAK,WACD,MAAO,+CACX,IAAK,WACD,MAAO,wCACX,QACI,MAAO,GAAG,EAAS,UAAU,KAazC,iBACI,EACA,EACA,EACA,EACA,EACa,CACb,IAAM,EAAa,KAAK,OAAO,GACzB,EAAY,KAAK,OAAO,KAAK,OAAO,OAAS,GAC7C,EAAW,GAAa,EAAa,EAAU,UAAY,EAAW,UAAY,EAElF,EAA6D,EAAE,CAErE,IAAK,GAAM,CAAC,EAAW,KAAU,KAAK,cAAc,SAAS,CAAE,CAC3D,IAAM,EAAgB,KAAK,OACtB,KAAK,EAAG,IAAO,EAAE,UAAU,KAAM,GAAM,EAAE,SAAS,EAAU,CAAC,CAAG,EAAI,GAAI,CACxE,OAAQ,GAAM,IAAM,GAAG,CAEtB,EAAkB,EAAc,OAAS,EAAI,KAAK,OAAO,EAAc,IAAI,aAAe,GAE5F,EAA4C,OAC5C,EAAQ,EAER,IAAc,kBACd,EAAW,WACX,EAAQ,0DACD,IAAc,gBACrB,EAAW,EAAQ,GAAK,UAAY,OACpC,EAAQ,8DACD,IAAc,gBACrB,EAAW,UACX,EAAQ,6DAGZ,EAAe,KAAK,CAAE,kBAAiB,QAAO,gBAAiB,EAAO,gBAAe,WAAU,CAAC,CAGpG,EAAe,MAAM,EAAG,IAAM,CAC1B,IAAM,EAAgB,CAAE,SAAU,EAAG,KAAM,EAAG,QAAS,EAAG,CAC1D,OAAO,EAAc,EAAE,UAAY,EAAc,EAAE,WACrD,CAEF,IAAM,EAA+B,EAAE,CAEjC,EAAe,KAAK,OAAO,OAAQ,GAAM,EAAE,OAAS,aAAa,CACjE,EAAe,KAAK,OAAO,OAAQ,GAAM,EAAE,OAAS,WAAW,CACjE,EAAa,OAAS,EAAa,OAAS,GAC5C,EAAmB,KACf,gCAAgC,EAAa,OAAO,cAAc,EAAa,OAAO,6DAEzF,CAGL,IAAM,EAAa,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,OAAO,CAC/D,EAAW,OAAS,KAAK,OAAO,OAAS,IACzC,EAAmB,KACf,sCAAsC,EAAW,OAAO,GAAG,KAAK,OAAO,OAAO,0CAEjF,CAGL,IAAM,EAA4B,EAAE,CAEhC,EAAe,KAAM,GAAM,EAAE,MAAM,SAAS,oBAAoB,CAAC,EACjE,EAAgB,KACZ,8IAEH,CAGD,EAAmB,KAAM,GAAM,EAAE,SAAS,iBAAiB,CAAC,EAC5D,EAAgB,KACZ,4GACH,CAGD,EAAe,KAAM,GAAM,EAAE,MAAM,SAAS,iBAAiB,CAAC,EAC9D,EAAgB,KACZ,6GACH,CAGL,IAAM,EAAW,CACb,aAAc,KAAK,OACd,OAAQ,GAAM,EAAE,WAAa,QAAQ,CACrC,IAAK,IAAO,CACT,MAAO,EAAE,KAAK,SACd,OAAQ,EAAE,KAAK,cACf,UAAW,EAAE,aACb,KAAM,EAAE,KACR,WAAY,EAAE,UAAU,OAAS,EACpC,EAAE,CAEP,eAAgB,KAAK,OAChB,OAAQ,GAAM,EAAE,WAAa,OAAO,CACpC,IAAK,IAAO,CACT,QAAS,EAAE,KACX,UAAW,EAAE,KACb,QAAS,CAAC,EAAE,UAAU,OACtB,UAAW,EAAE,aAChB,EAAE,CACP,YAAa,KAAK,OACb,OAAQ,GAAM,EAAE,WAAa,OAAO,CACpC,IAAK,IAAO,CACT,OAAQ,EAAE,YACV,sBAAuB,KAAK,OAAO,OAC9B,GACG,EAAG,UAAY,EAAE,WAAa,EAAG,UAAY,EAAE,UAAY,KAAO,EAAG,WAAa,QACzF,CAAC,OACF,UAAW,EAAE,aAChB,EAAE,CACV,CAED,MAAO,CACH,OAAQ,KAAK,OAEb,WAAY,CACR,OAAQ,EACR,aACA,QAAS,EAAY,SAAS,OAAS,OAAS,GAAgB,IAChE,WAAY,GAAgB,GAC5B,eAAgB,CACZ,KAAM,EAAY,SAAS,YAAc,EACzC,IAAK,EAAY,SAAS,WAAa,EAC1C,CACD,cAAe,EAAY,SAAS,OAAS,GAChD,CACD,SAAU,CACN,QAAS,UAAU,UACnB,iBAAkB,QAClB,YAAa,IAAI,MAAM,CAAC,aAAa,CACrC,SAAU,UAAU,SACpB,aAAe,EAAc,SAAW,UACxC,SAAU,CACN,WAAY,EACZ,IAAK,GAAW,cAAgB,GAChC,MAAO,GAAY,cAAgB,GACtC,CACD,YAAa,KAAK,OAAO,OAC5B,CAED,QAAS,CACL,YACI,EAAe,OAAS,EAClB,SAAS,EAAe,OAAO,mBAAmB,KAAK,OAAO,OAAO,yBAAyB,EAAW,KAAM,QAAQ,EAAE,CAAC,GAC1H,6BAA6B,KAAK,OAAO,OAAO,gBAAgB,EAAW,KAAM,QAAQ,EAAE,CAAC,GACtG,iBACA,kBACA,qBACH,CAED,WACH,CAYL,YACI,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EAAS,KAAK,iBAAiB,EAAa,EAAc,EAAgB,EAAY,EAAe,CAE3G,OAAO,KAAK,UACR,CACI,eAAgB,CACZ,aAAc,CACV,YAAa,8EACb,WAAY,oEACZ,cACI,8GACP,CACD,UAAW,CACP,qBAAsB,gCACtB,yBAA0B,0CAC1B,WAAY,kCACZ,yBAA0B,sDAC1B,wBAAyB,4CAC5B,CACD,QACI,yIAEJ,SAAU,CACN,6DACA,oEACA,0EACA,6EACA,6DACH,CACJ,CACD,YAAa,EAChB,CACD,KACA,EACH,CAML,OAAQ,CACJ,KAAK,OAAS,EAAE,CAChB,KAAK,cAAc,OAAO,CAC1B,KAAK,mBAAqB,OC7dlC,MAAa,GACT,EACA,EACA,IACqB,CACrB,GAAI,CAAC,EACD,OAAO,EAAC,MAAA,CAAA,SAAqB,EAAA,CAAZ,EAA0B,CAG/C,IAAM,EAAU,EAAa,EAAc,CAC3C,OACI,EAAC,MAAA,CACG,UAAW,EAAU,IAAA,GAAY,EAEjC,MAAO,EAAU,CAAE,gBAAiB,EAAe,CAAG,IAAA,YAErD,GAHI,EAIH,EAOD,GACT,EACA,EACA,EAOA,IACqB,CACrB,GAAI,EAAO,SAAW,EAElB,OAAO,EADS,GAAQ,OACU,EAAW,EAAc,CAG/D,IAAM,EAAe,EAAO,UAAU,EAAG,IAAM,EAAE,MAAQ,EAAE,MAAM,CAE3D,EAA4B,EAAE,CAChC,EAAY,EAsChB,GApCA,EAAa,SAAS,EAAO,IAAQ,CACjC,GAAM,CAAE,YAAW,MAAK,QAAO,MAAO,GAAe,EAE/C,EAAe,KAAK,IAAI,EAAG,KAAK,IAAI,EAAO,EAAK,OAAO,CAAC,CACxD,EAAa,KAAK,IAAI,EAAc,KAAK,IAAI,EAAK,EAAK,OAAO,CAAC,CAErE,GAAI,GAAc,EACd,OAGJ,IAAM,EAAiB,KAAK,IAAI,EAAc,EAAU,CAExD,GAAI,EAAiB,EAAW,CAC5B,IAAM,EAAa,EAAK,MAAM,EAAW,EAAe,CACpD,GACA,EAAO,KAAK,EAAW,CAI/B,GAAI,EAAa,EAAgB,CAC7B,IAAM,EAAkB,EAAK,MAAM,EAAgB,EAAW,CAC9D,EAAO,KACH,EAAC,OAAA,CACc,YAEX,MAAO,EACP,mBAAkB,EAAM,uBAEvB,GAJI,aAAa,EAAU,GAAG,EAAI,UAAU,GAK1C,CACV,CAGL,EAAY,KAAK,IAAI,EAAW,EAAW,EAC7C,CAEE,EAAY,EAAK,OAAQ,CACzB,IAAM,EAAY,EAAK,MAAM,EAAU,CACnC,GACA,EAAO,KAAK,EAAU,CAK9B,OAAO,EADS,EAAO,SAAW,EAAI,OAAW,EACf,EAAW,EAAc,EAQlD,EAAW,GAEhB,CACI,YAAY,GACZ,qBAAqB,GACrB,eAAe,GACf,MAAM,MACN,mBAAmB,GACnB,aAAa,EAAE,CACf,iBAAiB,EAAE,CACnB,WACA,OAAO,EACP,QACA,QACA,QAAQ,GACR,iBAAiB,IACjB,GAAG,GAEP,IACC,CACD,IAAM,EAAe,EAAuB,KAAK,CAC3C,EAAoB,EAA2B,IAAA,GAAU,CAEzD,EAAY,MAAc,IAAI,EAAqB,EAAO,EAAe,CAAE,CAAC,EAAO,EAAe,CAAC,CAEzG,MAAgB,CACZ,EAAU,WAAW,EAAM,EAC5B,CAAC,EAAO,EAAU,CAAC,CAEtB,IAAM,EAAe,IAAU,IAAA,GACzB,EAAY,MAAkB,EAAkB,QAAS,EAAE,CAAC,CAE5D,CAAE,eAAc,eAAc,WAAU,eAAgB,EAC1D,EACA,EACA,EACA,EACA,IAAA,GACA,EACH,CAEK,EAAkB,MAAkB,EAAc,CAAC,EAAa,CAAC,CAEjE,CAAE,mBAAkB,kBAAmB,EACzC,EACA,EACA,EACA,EACA,EACH,CAED,MAAgB,CACZ,EAAkB,QAAU,GAC7B,CAAC,EAAe,CAAC,CAEpB,GAAM,CAAE,oBAAmB,aAAY,cAAe,EAClD,EACA,EACA,EACA,EACA,EACH,CAEK,EAAqB,EACvB,EACA,EACA,EACA,EACH,CAEK,EAAyB,EAC1B,GAA8C,CAC3C,EAAa,EAAE,CACf,EAAiB,EAAE,OAAO,EAE9B,CAAC,EAAc,EAAiB,CACnC,CAEK,EAAqB,EACtB,GAAqB,CAClB,EAAS,EAAS,CACd,EAAY,SACZ,EAAiB,EAAY,QAAQ,EAG7C,CAAC,EAAU,EAAkB,EAAY,CAC5C,CAED,MAAgB,GAAI,EAAE,CAAC,CAEvB,IAAM,EAAe,MAAkB,CACnC,EAAW,EAAY,EACxB,CAAC,EAAY,EAAY,CAAC,CAE7B,MAAgB,CACZ,GAAI,CAAC,EACD,OAGJ,IAAM,EAAa,gBAAkB,CACjC,EAAU,OAAO,WAAY,SAAU,EAAE,CAAE,EAAa,EAAc,EAAgB,EAAa,EACpG,IAAK,CAER,UAAa,cAAc,EAAW,EACvC,CAAC,EAAO,EAAW,EAAa,EAAc,EAAgB,EAAa,CAAC,CAE/E,EACI,OACO,CACH,SAAY,EAAY,SAAS,MAAM,CACvC,gBACW,EAAU,YAAY,EAAa,EAAc,EAAgB,EAAY,EAAe,CAEvG,UAAa,EAAY,SAAS,OAAO,CACzC,aAAgB,EAChB,kBAAmB,EAAa,EAAS,GAAI,EAA2B,SAAW,CAC/E,GAAI,EAAkB,SAAW,EAAY,QAAS,CAClD,IAAM,EAAO,EAAkB,QAAQ,cAAc,sBAAsB,EAAI,UAAU,CAAC,IAAI,CAC9F,GAAI,aAAgB,YAAa,CAC7B,IAAM,EAAW,EAAY,QACvB,EAAU,EAAK,UACjB,IAAa,SACb,EAAS,SAAS,CAAE,SAAU,SAAU,IAAK,EAAU,EAAQ,CAAC,CAEhE,EAAS,UAAY,EAAU,KAK/C,WAAc,EAAY,SAAS,QAAQ,CAC3C,mBAAoB,EAAe,IAAgB,EAAY,SAAS,kBAAkB,EAAO,EAAI,CACrG,SAAU,EACb,EACD,CACI,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACH,CACJ,CAED,MAAgB,CACR,EAAY,SAAW,GACvB,EAAiB,EAAY,QAAQ,CAEzC,EAAW,EAAY,EACxB,CAAC,EAAc,EAAkB,EAAkB,EAAY,EAAY,CAAC,CAE/E,MAAgB,CACZ,GAAI,CAAC,EAAY,QACb,OAGJ,IAAM,EAAW,EAAY,QACzB,EAEE,EAAW,IAAI,mBAAqB,CACtC,qBAAqB,EAAM,CAC3B,EAAQ,0BAA4B,CAChC,EAAW,EAAY,CACnB,GACA,EAAiB,EAAS,EAEhC,EACJ,CAGF,OADA,EAAS,QAAQ,EAAS,KACb,CACT,EAAS,YAAY,CACrB,qBAAqB,EAAM,GAEhC,CAAC,EAAa,EAAY,EAAkB,EAAiB,CAAC,CAEjE,IAAM,EAAyC,CAC3C,GAAG,EACH,MAAO,EAAe,cAAgB,UACtC,OAAQ,EAAiB,GAAG,EAAe,IAAM,IAAA,GACjD,OAAQ,EAAmB,OAAS,WACvC,CAEK,EAA2C,CAC7C,GAAG,EACH,UAAW,EACX,OAAQ,EAAiB,GAAG,EAAe,IAAM,IAAA,GACjD,QAAS,EAAY,QAAU,iBAAiB,EAAY,QAAQ,CAAC,QAAU,WAClF,CAED,OACI,EAAC,MAAA,CAAI,UAAW,EAAoB,IAAK,EAAc,MAAO,CAAE,GAAG,EAAyB,GAAG,EAAO,WAClG,EAAC,MAAA,CAAI,cAAY,OAAO,IAAK,EAAmB,MAAO,WAClD,GACC,CAEN,EAAC,WAAA,CACc,YACN,MACL,SAAU,EACV,SAAU,EACV,IAAK,EACC,OACN,MAAO,EACP,MAAO,EACP,GAAI,GACN,CAAA,EACA,EAGjB,CAED,EAAS,YAAc"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/builder.ts","../src/domUtils.ts","../src/hooks/useAutoResize.ts","../src/textUtils.ts","../src/hooks/useHighlightedContent.ts","../src/hooks/useHighlightSync.ts","../src/hooks/useTextareaValue.ts","../src/styles.ts","../src/telemetry.ts","../src/DyeLight.tsx"],"sourcesContent":["/**\n * @fileoverview HighlightBuilder - Utility functions for creating highlight objects\n *\n * This module provides a convenient API for building highlight configurations\n * for the DyeLight component. It includes methods for creating character-level\n * highlights, line-level highlights, pattern-based highlights, and more.\n */\n\nimport type React from 'react';\n\n/**\n * Utility functions for building highlight objects for the DyeLight component\n * Provides a fluent API for creating various types of text highlights\n */\nexport const HighlightBuilder = {\n /**\n * Creates highlights for individual characters using absolute positions\n * @param chars - Array of character highlight configurations\n * @param chars[].index - Zero-based character index in the text\n * @param chars[].className - Optional CSS class name to apply\n * @param chars[].style - Optional inline styles to apply\n * @returns Array of character range highlights\n * @example\n * ```tsx\n * // Highlight characters at positions 5, 10, and 15\n * const highlights = HighlightBuilder.characters([\n * { index: 5, className: 'highlight-error' },\n * { index: 10, className: 'highlight-warning' },\n * { index: 15, style: { backgroundColor: 'yellow' } }\n * ]);\n * ```\n */\n characters: (chars: Array<{ className?: string; index: number; style?: React.CSSProperties }>) => {\n return chars.map(({ className, index, style }) => ({ className, end: index + 1, start: index, style }));\n },\n\n /**\n * Creates line highlights for entire lines\n * @param lines - Array of line highlight configurations\n * @param lines[].line - Zero-based line number\n * @param lines[].className - Optional CSS class name to apply to the line\n * @param lines[].color - Optional color value (CSS color, hex, rgb, etc.)\n * @returns Object mapping line numbers to highlight values\n * @example\n * ```tsx\n * // Highlight lines 0 and 2 with different styles\n * const lineHighlights = HighlightBuilder.lines([\n * { line: 0, className: 'error-line' },\n * { line: 2, color: '#ffff00' }\n * ]);\n * ```\n */\n lines: (lines: Array<{ className?: string; color?: string; line: number }>) => {\n const result: { [lineNumber: number]: string } = {};\n lines.forEach(({ className, color, line }) => {\n result[line] = className || color || '';\n });\n return result;\n },\n\n /**\n * Highlights text matching a pattern using absolute positions\n * @param text - The text to search within\n * @param pattern - Regular expression or string pattern to match\n * @param className - Optional CSS class name to apply to matches\n * @param style - Optional inline styles to apply to matches\n * @returns Array of character range highlights for all matches\n * @example\n * ```tsx\n * // Highlight all JavaScript keywords\n * const highlights = HighlightBuilder.pattern(\n * code,\n * /\\b(function|const|let|var|if|else|for|while)\\b/g,\n * 'keyword-highlight'\n * );\n *\n * // Highlight all email addresses\n * const emailHighlights = HighlightBuilder.pattern(\n * text,\n * /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g,\n * 'email-highlight'\n * );\n * ```\n */\n pattern: (text: string, pattern: RegExp | string, className?: string, style?: React.CSSProperties) => {\n const regex = typeof pattern === 'string' ? new RegExp(pattern, 'g') : new RegExp(pattern.source, 'g');\n const matches = Array.from(text.matchAll(regex));\n\n return matches.map((match) => ({ className, end: match.index! + match[0].length, start: match.index!, style }));\n },\n\n /**\n * Creates character range highlights using absolute positions\n * @param ranges - Array of character range configurations\n * @param ranges[].start - Zero-based start index (inclusive)\n * @param ranges[].end - Zero-based end index (exclusive)\n * @param ranges[].className - Optional CSS class name to apply\n * @param ranges[].style - Optional inline styles to apply\n * @returns Array of character range highlights\n * @example\n * ```tsx\n * // Highlight specific ranges in the text\n * const highlights = HighlightBuilder.ranges([\n * { start: 0, end: 5, className: 'title-highlight' },\n * { start: 10, end: 20, style: { backgroundColor: 'yellow' } },\n * { start: 25, end: 30, className: 'error-highlight' }\n * ]);\n * ```\n */\n ranges: (ranges: Array<{ className?: string; end: number; start: number; style?: React.CSSProperties }>) => {\n return ranges.map(({ className, end, start, style }) => ({ className, end, start, style }));\n },\n\n /**\n * Highlights text between specific start and end positions\n * Convenience method for highlighting a single selection range\n * @param start - Zero-based start index (inclusive)\n * @param end - Zero-based end index (exclusive)\n * @param className - Optional CSS class name to apply\n * @param style - Optional inline styles to apply\n * @returns Array containing a single character range highlight\n * @example\n * ```tsx\n * // Highlight a selection from position 10 to 25\n * const selectionHighlight = HighlightBuilder.selection(\n * 10,\n * 25,\n * 'selection-highlight'\n * );\n * ```\n */\n selection: (start: number, end: number, className?: string, style?: React.CSSProperties) => {\n return [{ className, end, start, style }];\n },\n\n /**\n * Highlights entire words that match specific terms\n * @param text - The text to search within\n * @param words - Array of words to highlight\n * @param className - Optional CSS class name to apply to matched words\n * @param style - Optional inline styles to apply to matched words\n * @returns Array of character range highlights for all matched words\n * @example\n * ```tsx\n * // Highlight specific programming keywords\n * const highlights = HighlightBuilder.words(\n * sourceCode,\n * ['function', 'const', 'let', 'var', 'return'],\n * 'keyword'\n * );\n *\n * // Highlight important terms with custom styling\n * const termHighlights = HighlightBuilder.words(\n * document,\n * ['TODO', 'FIXME', 'NOTE'],\n * undefined,\n * { backgroundColor: 'orange', fontWeight: 'bold' }\n * );\n * ```\n */\n words: (text: string, words: string[], className?: string, style?: React.CSSProperties) => {\n const pattern = new RegExp(`\\\\b(${words.join('|')})\\\\b`, 'g');\n return HighlightBuilder.pattern(text, pattern, className, style);\n },\n};\n","/**\n * @fileoverview DOM utility functions for textarea manipulation\n *\n * This module provides utility functions for working with DOM elements,\n * specifically focused on textarea auto-resizing functionality used by\n * the DyeLight component.\n */\n\n/**\n * Automatically resizes a textarea element to fit its content\n *\n * This function adjusts the textarea height to match its scroll height,\n * effectively removing scrollbars when the content fits and expanding\n * the textarea as content is added. The height is first set to 'auto'\n * to allow the element to shrink if content is removed.\n *\n * @param textArea - The HTML textarea element to resize\n * @example\n * ```ts\n * const textarea = document.querySelector('textarea');\n * if (textarea) {\n * autoResize(textarea);\n * }\n *\n * // Or in an event handler:\n * const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n * autoResize(e.target);\n * };\n * ```\n */\nexport const autoResize = (textArea: HTMLTextAreaElement) => {\n const computedStyle = getComputedStyle(textArea);\n const borderTop = parseFloat(computedStyle.borderTopWidth) || 0;\n const borderBottom = parseFloat(computedStyle.borderBottomWidth) || 0;\n\n // Reset height to auto to force accurate scrollHeight calculation (shrink if needed)\n textArea.style.height = 'auto';\n\n const scrollHeight = textArea.scrollHeight;\n const totalBorderHeight = borderTop + borderBottom;\n\n // Only add border compensation if borders are significant (> 2px), otherwise trust scrollHeight\n // This handles browser differences in how scrollHeight reports when box-sizing is border-box\n const finalHeight = totalBorderHeight > 2 ? scrollHeight + totalBorderHeight : scrollHeight;\n\n // Set height including borders for border-box\n textArea.style.height = `${finalHeight}px`;\n};\n","/**\n * @fileoverview Hook for managing textarea auto-resize functionality\n */\n\nimport { useCallback, useState } from 'react';\nimport { autoResize } from '@/domUtils';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Creates an auto-resize handler function\n * @param enableAutoResize Whether auto-resize is enabled\n * @param setTextareaHeight Function to update textarea height state\n * @param resize The resize function to use (defaults to autoResize)\n * @returns Handler function that resizes the textarea\n */\nexport const createAutoResizeHandler = (\n enableAutoResize: boolean,\n setTextareaHeight: (height: number | undefined) => void,\n resize: (element: HTMLTextAreaElement) => void = autoResize,\n) => {\n return (element: HTMLTextAreaElement) => {\n if (!enableAutoResize) {\n return;\n }\n\n resize(element);\n setTextareaHeight(element.scrollHeight);\n };\n};\n\n/**\n * Hook for managing textarea auto-resize functionality\n * Automatically adjusts textarea height based on content\n *\n * @param enableAutoResize Whether to enable automatic resizing\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional textarea reference for telemetry\n * @param getCurrentValue Optional function to get current value for telemetry\n * @param isControlled Whether the component is in controlled mode\n * @returns Object containing resize handler and current height\n */\nexport const useAutoResize = (\n enableAutoResize: boolean,\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n getCurrentValue?: () => string,\n isControlled?: boolean,\n) => {\n const [textareaHeight, setTextareaHeight] = useState<number | undefined>();\n\n /**\n * Handles automatic resizing of the textarea based on content\n * Records telemetry data if telemetry is enabled\n */\n const handleAutoResize = useCallback(\n (element: HTMLTextAreaElement) => {\n if (!enableAutoResize) {\n return;\n }\n\n const beforeHeight = element.scrollHeight;\n\n autoResize(element);\n\n const afterHeight = element.scrollHeight;\n\n telemetry?.record(\n 'autoResize',\n 'system',\n { afterHeight, beforeHeight, changed: beforeHeight !== afterHeight, textLength: element.value.length },\n textareaRef ?? { current: element },\n getCurrentValue?.() ?? element.value,\n afterHeight,\n isControlled ?? false,\n );\n\n setTextareaHeight(element.scrollHeight);\n },\n [enableAutoResize, telemetry, textareaRef, getCurrentValue, isControlled],\n );\n\n return { handleAutoResize, textareaHeight };\n};\n","/**\n * @fileoverview Text utility functions for position calculations and text processing\n *\n * This module provides utilities for converting between absolute text positions\n * and line-relative positions, as well as other text processing functions used\n * by the DyeLight component for highlight positioning.\n */\n\n/**\n * Analyzes text content and returns line information with position mappings\n * @param text - The text content to analyze\n * @returns Object containing lines array and line start positions\n * @returns returns.lines - Array of individual lines (without newline characters)\n * @returns returns.lineStarts - Array of absolute positions where each line starts\n * @example\n * ```ts\n * const text = \"Hello\\nWorld\\nTest\";\n * const { lines, lineStarts } = getLinePositions(text);\n * // lines: [\"Hello\", \"World\", \"Test\"]\n * // lineStarts: [0, 6, 12]\n * ```\n */\nexport const getLinePositions = (text: string) => {\n const lines = text.split('\\n');\n const lineStarts: number[] = [];\n let position = 0;\n\n lines.forEach((line, index) => {\n lineStarts.push(position);\n position += line.length + (index < lines.length - 1 ? 1 : 0); // +1 for \\n except last line\n });\n\n return { lineStarts, lines };\n};\n\n/**\n * Converts an absolute text position to line-relative coordinates\n * @param absolutePos - Zero-based absolute position in the entire text\n * @param lineStarts - Array of line start positions (from getLinePositions)\n * @returns Object containing line number and character position within that line\n * @returns returns.line - Zero-based line number\n * @returns returns.char - Zero-based character position within the line\n * @example\n * ```ts\n * const text = \"Hello\\nWorld\\nTest\";\n * const { lineStarts } = getLinePositions(text);\n * const pos = absoluteToLinePos(8, lineStarts);\n * // pos: { line: 1, char: 2 } (the 'r' in \"World\")\n * ```\n */\nexport const absoluteToLinePos = (absolutePos: number, lineStarts: number[]) => {\n for (let i = lineStarts.length - 1; i >= 0; i--) {\n if (absolutePos >= lineStarts[i]) {\n return { char: absolutePos - lineStarts[i], line: i };\n }\n }\n return { char: 0, line: 0 };\n};\n\n/**\n * Determines if a string value represents a CSS color value\n * @param value - The string value to test\n * @returns True if the value appears to be a color value, false otherwise\n * @example\n * ```ts\n * isColorValue('#ff0000'); // true\n * isColorValue('rgb(255, 0, 0)'); // true\n * isColorValue('red'); // true\n * isColorValue('my-css-class'); // false (contains hyphens)\n * isColorValue('transparent'); // true\n * isColorValue('var(--primary-color)'); // true\n * ```\n */\nexport const isColorValue = (value: string): boolean => {\n return (\n /^(#|rgb|hsl|var\\(--.*?\\)|transparent|currentColor|inherit|initial|unset)/i.test(value) ||\n /^[a-z]+$/i.test(value)\n );\n};\n","import { useMemo } from 'react';\nimport { absoluteToLinePos, getLinePositions } from '@/textUtils';\nimport type { CharacterRange } from '@/types';\n\nexport const computeHighlightedContent = (\n text: string,\n highlights: CharacterRange[],\n lineHighlights: { [lineNumber: number]: string },\n renderHighlightedLine: (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n ) => React.ReactElement,\n) => {\n const { lines, lineStarts } = getLinePositions(text);\n\n const highlightsByLine: {\n [lineIndex: number]: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>;\n } = {};\n\n highlights.forEach((highlight) => {\n const startPos = absoluteToLinePos(highlight.start, lineStarts);\n const endPos = absoluteToLinePos(highlight.end - 1, lineStarts);\n\n for (let lineIndex = startPos.line; lineIndex <= endPos.line; lineIndex++) {\n if (!highlightsByLine[lineIndex]) {\n highlightsByLine[lineIndex] = [];\n }\n\n const lineStart = lineStarts[lineIndex];\n\n const rangeStart = Math.max(highlight.start - lineStart, 0);\n const rangeEnd = Math.min(highlight.end - lineStart, lines[lineIndex].length);\n\n if (rangeEnd > rangeStart) {\n highlightsByLine[lineIndex].push({\n absoluteStart: highlight.start,\n className: highlight.className,\n end: rangeEnd,\n start: rangeStart,\n style: highlight.style,\n });\n }\n }\n });\n\n return lines.map((line, lineIndex) => {\n const lineHighlight = lineHighlights[lineIndex];\n const ranges = highlightsByLine[lineIndex] || [];\n\n return renderHighlightedLine(line, lineIndex, ranges, lineHighlight);\n });\n};\n\n/**\n * Hook for computing highlighted content from text and highlight ranges\n */\nexport const useHighlightedContent = (\n text: string,\n highlights: CharacterRange[],\n lineHighlights: { [lineNumber: number]: string },\n renderHighlightedLine: (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n ) => React.ReactElement,\n) => {\n /**\n * Computes the highlighted content by processing text and highlight ranges\n * Groups highlights by line and renders each line with appropriate highlighting\n */\n const highlightedContent = useMemo(\n () => computeHighlightedContent(text, highlights, lineHighlights, renderHighlightedLine),\n [text, highlights, lineHighlights, renderHighlightedLine],\n );\n\n return highlightedContent;\n};\n","/**\n * @fileoverview Hook for managing highlight layer synchronization\n */\n\nimport { useCallback, useRef } from 'react';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Synchronizes scroll positions between textarea and highlight layer\n * @param textarea The textarea element\n * @param highlightLayer The highlight overlay element\n */\nexport const syncScrollPositions = (\n textarea: Pick<HTMLTextAreaElement, 'scrollLeft' | 'scrollTop'> | null,\n highlightLayer: Pick<HTMLDivElement, 'scrollLeft' | 'scrollTop'> | null,\n) => {\n if (!textarea || !highlightLayer) {\n return;\n }\n\n const { scrollLeft, scrollTop } = textarea;\n highlightLayer.scrollTop = scrollTop;\n highlightLayer.scrollLeft = scrollLeft;\n};\n\n/**\n * Synchronizes styles between textarea and highlight layer for pixel-perfect alignment\n * @param textarea The textarea element\n * @param highlightLayer The highlight overlay element\n * @param computeStyle Function to compute styles (defaults to getComputedStyle)\n */\nexport const syncHighlightStyles = (\n textarea: HTMLTextAreaElement | null,\n highlightLayer: HTMLDivElement | null,\n computeStyle: (element: Element) => CSSStyleDeclaration = getComputedStyle,\n) => {\n if (!textarea || !highlightLayer) {\n return;\n }\n\n const computedStyle = computeStyle(textarea);\n\n highlightLayer.style.padding = computedStyle.padding;\n highlightLayer.style.fontSize = computedStyle.fontSize;\n highlightLayer.style.fontFamily = computedStyle.fontFamily;\n highlightLayer.style.lineHeight = computedStyle.lineHeight;\n highlightLayer.style.letterSpacing = computedStyle.letterSpacing;\n highlightLayer.style.wordSpacing = computedStyle.wordSpacing;\n highlightLayer.style.textIndent = computedStyle.textIndent;\n\n highlightLayer.style.whiteSpace = computedStyle.whiteSpace;\n highlightLayer.style.wordBreak = computedStyle.wordBreak;\n highlightLayer.style.overflowWrap = computedStyle.overflowWrap;\n highlightLayer.style.tabSize = computedStyle.tabSize;\n\n highlightLayer.style.borderTopWidth = computedStyle.borderTopWidth;\n highlightLayer.style.borderRightWidth = computedStyle.borderRightWidth;\n highlightLayer.style.borderBottomWidth = computedStyle.borderBottomWidth;\n highlightLayer.style.borderLeftWidth = computedStyle.borderLeftWidth;\n highlightLayer.style.borderTopStyle = computedStyle.borderTopStyle;\n highlightLayer.style.borderRightStyle = computedStyle.borderRightStyle;\n highlightLayer.style.borderBottomStyle = computedStyle.borderBottomStyle;\n highlightLayer.style.borderLeftStyle = computedStyle.borderLeftStyle;\n highlightLayer.style.borderColor = 'transparent';\n\n const borderLeft = parseFloat(computedStyle.borderLeftWidth) || 0;\n const borderRight = parseFloat(computedStyle.borderRightWidth) || 0;\n const scrollbarWidth = textarea.offsetWidth - textarea.clientWidth - borderLeft - borderRight;\n\n if (scrollbarWidth > 0) {\n const isRTL = computedStyle.direction === 'rtl';\n if (isRTL) {\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n highlightLayer.style.paddingLeft = `${paddingLeft + scrollbarWidth}px`;\n } else {\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n highlightLayer.style.paddingRight = `${paddingRight + scrollbarWidth}px`;\n }\n }\n};\n\n/**\n * Hook for managing highlight layer synchronization\n * Provides functions to sync scroll and styles between textarea and overlay\n *\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional textarea reference for telemetry\n * @param getCurrentValue Optional function to get current value for telemetry\n * @param getHeight Optional function to get current height for telemetry\n * @param isControlled Whether the component is in controlled mode\n * @returns Object containing highlight layer ref and sync functions\n */\nexport const useHighlightSync = (\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n getCurrentValue?: () => string,\n getHeight?: () => number | undefined,\n isControlled?: boolean,\n) => {\n const highlightLayerRef = useRef<HTMLDivElement>(null);\n\n /**\n * Synchronizes scroll position between textarea and highlight layer\n * @param textareaRefParam The textarea ref to sync from\n */\n const syncScroll = useCallback(\n (textareaRefParam: React.RefObject<HTMLTextAreaElement | null>) => {\n const before = {\n scrollLeft: highlightLayerRef.current?.scrollLeft,\n scrollTop: highlightLayerRef.current?.scrollTop,\n };\n\n syncScrollPositions(textareaRefParam.current, highlightLayerRef.current);\n\n const after = {\n scrollLeft: highlightLayerRef.current?.scrollLeft,\n scrollTop: highlightLayerRef.current?.scrollTop,\n };\n\n telemetry?.record(\n 'syncScroll',\n 'sync',\n {\n after,\n before,\n changed: before.scrollTop !== after.scrollTop || before.scrollLeft !== after.scrollLeft,\n },\n textareaRef ?? textareaRefParam,\n getCurrentValue?.() ?? '',\n getHeight?.(),\n isControlled ?? false,\n );\n },\n [telemetry, textareaRef, getCurrentValue, getHeight, isControlled],\n );\n\n /**\n * Synchronizes styles between textarea and highlight layer\n * @param textareaRefParam The textarea ref to sync from\n */\n const syncStyles = useCallback(\n (textareaRefParam: React.RefObject<HTMLTextAreaElement | null>) => {\n if (!textareaRefParam.current || !highlightLayerRef.current) {\n return;\n }\n\n const computedStyle = getComputedStyle(textareaRefParam.current);\n const scrollbarWidth =\n textareaRefParam.current.offsetWidth -\n textareaRefParam.current.clientWidth -\n (parseFloat(computedStyle.borderLeftWidth) || 0) -\n (parseFloat(computedStyle.borderRightWidth) || 0);\n\n syncHighlightStyles(textareaRefParam.current, highlightLayerRef.current);\n\n telemetry?.record(\n 'syncStyles',\n 'sync',\n {\n direction: computedStyle.direction,\n fontSize: computedStyle.fontSize,\n padding: computedStyle.padding,\n scrollbarWidth,\n },\n textareaRef ?? textareaRefParam,\n getCurrentValue?.() ?? '',\n getHeight?.(),\n isControlled ?? false,\n );\n },\n [telemetry, textareaRef, getCurrentValue, getHeight, isControlled],\n );\n\n return { highlightLayerRef, syncScroll, syncStyles };\n};\n","/**\n * @fileoverview Hook for managing textarea value state and synchronization\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { AIOptimizedTelemetry } from '../telemetry';\n\n/**\n * Synchronizes the textarea DOM value with React state\n * @param textarea The textarea DOM element\n * @param currentValue Current React state value\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const syncValueWithDOM = (\n textarea: HTMLTextAreaElement | null,\n currentValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n) => {\n if (!textarea) {\n return;\n }\n\n const domValue = textarea.value;\n if (domValue !== currentValue) {\n if (!isControlled) {\n setInternalValue(domValue);\n }\n onChange?.(domValue);\n }\n};\n\n/**\n * Handles value changes from user input\n * @param newValue New value from user input\n * @param currentValue Current React state value\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const handleChangeValue = (\n newValue: string,\n currentValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n textarea?: HTMLTextAreaElement | null,\n) => {\n if (newValue === currentValue) {\n return;\n }\n\n if (isControlled && textarea && textarea.value !== newValue) {\n textarea.value = newValue;\n }\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n};\n\n/**\n * Applies a programmatic value change via setValue\n * @param textarea The textarea DOM element\n * @param newValue New value to set\n * @param isControlled Whether the component is in controlled mode\n * @param setInternalValue Function to update internal state\n * @param onChange Optional onChange callback\n */\nexport const applySetValue = (\n textarea: HTMLTextAreaElement | null,\n newValue: string,\n isControlled: boolean,\n setInternalValue: (value: string) => void,\n onChange?: (value: string) => void,\n) => {\n if (!textarea) {\n return;\n }\n\n textarea.value = newValue;\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n};\n\n/**\n * Hook for managing textarea value state and synchronization\n * Handles both controlled and uncontrolled modes, plus programmatic changes\n *\n * @param value Controlled value (optional)\n * @param defaultValue Default value for uncontrolled mode\n * @param onChange Callback when value changes\n * @param telemetry Optional telemetry collector for debugging\n * @param textareaRef Optional external textarea ref\n * @param getHeight Optional function to get current textarea height\n * @returns Object containing current value, change handler, setValue function, and textarea ref\n */\nexport const useTextareaValue = (\n value?: string,\n defaultValue = '',\n onChange?: (value: string) => void,\n telemetry?: AIOptimizedTelemetry,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n getHeight?: () => number | undefined,\n) => {\n const internalTextareaRef = useRef<HTMLTextAreaElement | null>(null);\n const [internalValue, setInternalValue] = useState(value ?? defaultValue);\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? (value ?? '') : internalValue;\n const actualTextareaRef = textareaRef ?? internalTextareaRef;\n\n const syncValueWithDOMCallback = useCallback(() => {\n syncValueWithDOM(actualTextareaRef.current, currentValue, isControlled, setInternalValue, onChange);\n }, [currentValue, isControlled, onChange, actualTextareaRef]);\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n\n telemetry?.record(\n 'onChange',\n 'user',\n {\n lengthDelta: newValue.length - currentValue.length,\n newValue,\n previousValue: currentValue,\n valueLength: newValue.length,\n },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n\n handleChangeValue(\n newValue,\n currentValue,\n isControlled,\n setInternalValue,\n onChange,\n actualTextareaRef.current,\n );\n },\n [currentValue, isControlled, onChange, telemetry, actualTextareaRef, getHeight],\n );\n\n const setValue = useCallback(\n (newValue: string) => {\n telemetry?.record(\n 'setValue',\n 'state',\n { newValue, previousValue: currentValue },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n\n applySetValue(actualTextareaRef.current, newValue, isControlled, setInternalValue, onChange);\n },\n [isControlled, onChange, telemetry, actualTextareaRef, getHeight, currentValue],\n );\n\n useEffect(() => {\n syncValueWithDOMCallback();\n }, [syncValueWithDOMCallback]);\n\n useEffect(() => {\n if (isControlled && actualTextareaRef.current && actualTextareaRef.current.value !== currentValue) {\n actualTextareaRef.current.value = currentValue;\n }\n }, [isControlled, currentValue, actualTextareaRef]);\n\n useEffect(() => {\n if (actualTextareaRef.current && actualTextareaRef.current.value !== currentValue) {\n telemetry?.record(\n 'valueMismatch',\n 'state',\n { domValue: actualTextareaRef.current.value, stateValue: currentValue },\n actualTextareaRef,\n currentValue,\n getHeight?.(),\n isControlled,\n );\n }\n }, [currentValue, isControlled, telemetry, actualTextareaRef, getHeight]);\n\n return { currentValue, handleChange, setValue, textareaRef: actualTextareaRef };\n};\n","/**\n * @fileoverview Default styles for DyeLight component elements\n *\n * This module contains the default CSS-in-JS styles used by the DyeLight component\n * for its container, textarea, and highlight layer elements. These styles ensure\n * proper layering, positioning, and visual alignment between the textarea and\n * its highlight overlay.\n */\n\nimport type React from 'react';\n\n/**\n * Default styles for the main container element that wraps the entire component\n * Creates a positioned container that serves as the reference for absolutely positioned children\n */\nexport const DEFAULT_CONTAINER_STYLE: React.CSSProperties = {\n /** Ensures the container behaves as a block-level element */\n display: 'block',\n /** Enables absolute positioning for child elements */\n position: 'relative',\n /** Takes full width of parent container */\n width: '100%',\n};\n\n/**\n * Default styles for the textarea element\n * Makes the textarea transparent while maintaining its functionality and positioning\n */\nexport const DEFAULT_BASE_STYLE: React.CSSProperties = {\n /** Transparent background to show highlights underneath */\n background: 'transparent',\n /** Ensures consistent box model calculations */\n boxSizing: 'border-box',\n /** Maintains visible text cursor */\n caretColor: '#000000',\n /** Transparent text to reveal highlights while keeping cursor and selection */\n color: 'transparent',\n /** Prevents unwanted height constraints */\n minHeight: 'auto',\n /** Positioned above the highlight layer */\n position: 'relative',\n /** Takes full width of container */\n width: '100%',\n /** Ensures textarea appears above highlight layer */\n zIndex: 2,\n};\n\n/**\n * Default styles for the highlight layer that renders behind the textarea\n * Positioned absolutely to overlay perfectly with the textarea content\n */\nexport const DEFAULT_HIGHLIGHT_LAYER_STYLE: React.CSSProperties = {\n /** Transparent border to match textarea default border - now synced dynamically */\n border: '0px none transparent',\n /** Stretch to fill container bottom */\n bottom: 0,\n /** Consistent box model with textarea */\n boxSizing: 'border-box',\n /** Inherit text color from parent */\n color: 'inherit',\n /** Match textarea font family */\n fontFamily: 'inherit',\n /** Match textarea font size */\n fontSize: 'inherit',\n /** Stretch to fill container left */\n left: 0,\n /** Match textarea line height */\n lineHeight: 'inherit',\n /** Remove default margins */\n margin: 0,\n /** Hide scrollbars on highlight layer */\n overflow: 'hidden',\n /** Prevent highlight layer from capturing mouse events */\n pointerEvents: 'none',\n /** Positioned absolutely within container */\n position: 'absolute',\n /** Stretch to fill container right */\n right: 0,\n /** Stretch to fill container top */\n top: 0,\n /** Preserve whitespace and line breaks like textarea */\n whiteSpace: 'pre-wrap',\n /** Break long words like textarea */\n wordWrap: 'break-word',\n /** Ensure highlight layer appears behind textarea */\n zIndex: 1,\n};\n","import type { AIDebugReport, AITelemetryEvent } from './types';\n\n/**\n * Detects issues from the issue registry and creates structured issue records\n */\nfunction detectIssues(\n issueRegistry: Map<string, number>,\n events: AITelemetryEvent[],\n): AIDebugReport['summary']['detectedIssues'] {\n const detectedIssues: AIDebugReport['summary']['detectedIssues'] = [];\n for (const [issueType, count] of issueRegistry.entries()) {\n const issueIndicators: Record<string, string> = {\n large_paste: 'Large paste detected',\n rapid_events: 'Rapid event',\n state_mismatch: 'State mismatch',\n };\n const indicator = issueIndicators[issueType] ?? issueType;\n const relatedEvents = events\n .map((e, i) => (e.anomalies.some((a) => a.includes(indicator)) ? i : -1))\n .filter((i) => i !== -1);\n const firstOccurrence = relatedEvents.length > 0 ? events[relatedEvents[0]].timestampISO : '';\n let severity: 'critical' | 'warning' | 'info' = 'info';\n let issue = issueType;\n if (issueType === 'state_mismatch') {\n severity = 'critical';\n issue = 'State desynchronization between DOM and React detected';\n } else if (issueType === 'rapid_events') {\n severity = count > 10 ? 'warning' : 'info';\n issue = 'Rapid successive events detected (possible race condition)';\n } else if (issueType === 'large_paste') {\n severity = 'warning';\n issue = 'Large paste operations detected (may trigger sync issues)';\n }\n detectedIssues.push({ firstOccurrence, issue, occurrenceCount: count, relatedEvents, severity });\n }\n detectedIssues.sort((a, b) => {\n const severityOrder = { critical: 0, info: 2, warning: 1 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n return detectedIssues;\n}\n\n/**\n * Detects suspicious patterns in the event sequence\n */\nfunction detectSuspiciousPatterns(events: AITelemetryEvent[]): string[] {\n const suspiciousPatterns: string[] = [];\n\n const resizeEvents = events.filter((e) => e.type === 'autoResize');\n const valueChanges = events.filter((e) => e.type === 'onChange');\n if (resizeEvents.length > valueChanges.length * 2) {\n suspiciousPatterns.push(\n `Excessive resize operations (${resizeEvents.length} resizes vs ${valueChanges.length} value changes). ` +\n `May indicate infinite ResizeObserver loop.`,\n );\n }\n\n const syncEvents = events.filter((e) => e.category === 'sync');\n if (syncEvents.length > events.length * 0.3) {\n suspiciousPatterns.push(\n `High frequency of sync operations (${syncEvents.length}/${events.length} events). ` +\n `May indicate layout thrashing.`,\n );\n }\n\n return suspiciousPatterns;\n}\n\n/**\n * Generates recommendations based on detected issues and patterns\n */\nfunction generateRecommendations(\n detectedIssues: AIDebugReport['summary']['detectedIssues'],\n suspiciousPatterns: string[],\n): string[] {\n const recommendations: string[] = [];\n\n if (detectedIssues.some((i) => i.issue.includes('desynchronization'))) {\n recommendations.push(\n 'State desynchronization detected. Check if multiple onChange handlers are bound or ' +\n 'if external code is modifying the textarea DOM directly.',\n );\n }\n\n if (suspiciousPatterns.some((p) => p.includes('ResizeObserver'))) {\n recommendations.push(\n 'Possible infinite ResizeObserver loop. Ensure resize operations are debounced with requestAnimationFrame.',\n );\n }\n\n if (detectedIssues.some((i) => i.issue.includes('race condition'))) {\n recommendations.push(\n 'Rapid events detected. Consider debouncing onChange handlers or checking for double-bound event listeners.',\n );\n }\n\n if (detectedIssues.some((i) => i.issue.includes('Large paste'))) {\n recommendations.push(\n 'Large paste operations detected. Ensure e.preventDefault() is called BEFORE reading clipboard data ' +\n 'in your onPaste handler to prevent the browser from inserting raw content into the DOM.',\n );\n }\n\n return recommendations;\n}\n\n/**\n * Builds the timeline view from events\n */\nfunction buildTimeline(events: AITelemetryEvent[]): AIDebugReport['timeline'] {\n return {\n stateChanges: events\n .filter((e) => e.category === 'state')\n .map((e) => ({\n after: e.data.newValue,\n before: e.data.previousValue,\n timestamp: e.timestampISO,\n type: e.type,\n unexpected: e.anomalies.length > 0,\n })),\n\n syncOperations: events\n .filter((e) => e.category === 'sync')\n .map((e) => ({\n details: e.data,\n operation: e.type,\n success: !e.anomalies.length,\n timestamp: e.timestampISO,\n })),\n\n userActions: events\n .filter((e) => e.category === 'user')\n .map((e) => ({\n action: e.description,\n resultingStateChanges: events.filter(\n (se) => se.timestamp > e.timestamp && se.timestamp < e.timestamp + 100 && se.category === 'state',\n ).length,\n timestamp: e.timestampISO,\n })),\n };\n}\n\n/**\n * Registry for deduplicating large text values\n * Stores values >1000 chars once and returns a reference key\n */\nclass ValueRegistry {\n private registry = new Map<string, string>();\n private reverseRegistry = new Map<string, string>();\n private nextId = 0;\n private readonly threshold = 1000; // Only deduplicate values >1KB\n\n /**\n * Stores a value and returns either the value itself (if small) or a reference\n */\n store(value: string): string {\n // Don't deduplicate small values\n if (value.length <= this.threshold) {\n return value;\n }\n\n // Check if we've already stored this exact value\n if (this.reverseRegistry.has(value)) {\n return this.reverseRegistry.get(value)!;\n }\n\n // Store new value\n const ref = `<REF:value_${this.nextId}>`;\n this.registry.set(ref, value);\n this.reverseRegistry.set(value, ref);\n this.nextId++;\n\n return ref;\n }\n\n /**\n * Gets the actual registry for export\n */\n getRegistry(): { [key: string]: string } {\n return Object.fromEntries(this.registry);\n }\n\n /**\n * Clears the registry\n */\n clear() {\n this.registry.clear();\n this.reverseRegistry.clear();\n this.nextId = 0;\n }\n}\n\n/**\n * AI-optimized telemetry collector for the DyeLight component.\n * Records events with full context and generates comprehensive debug reports\n * that can be analyzed by AI language models.\n */\nexport class AIOptimizedTelemetry {\n private events: AITelemetryEvent[] = [];\n private maxEvents = 1000;\n private enabled = false;\n private lastEventTimestamp: number | null = null;\n private issueRegistry = new Map<string, number>();\n private valueRegistry = new ValueRegistry();\n\n /**\n * Creates a new telemetry instance\n * @param enabled Whether telemetry collection is initially enabled\n * @param maxEvents Maximum number of events to retain in memory\n */\n constructor(enabled = false, maxEvents = 1000) {\n this.enabled = enabled;\n this.maxEvents = maxEvents;\n }\n\n /**\n * Enable or disable telemetry collection\n * @param enabled Whether to enable telemetry\n */\n setEnabled(enabled: boolean) {\n this.enabled = enabled;\n }\n\n /**\n * Records an event with full context for AI analysis\n * @param type Event type identifier\n * @param category Event category for grouping\n * @param data Event-specific data\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param isControlled Whether component is in controlled mode\n */\n record(\n type: string,\n category: 'state' | 'dom' | 'sync' | 'user' | 'system',\n data: Record<string, unknown>,\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n isControlled: boolean,\n ) {\n if (!this.enabled) {\n return;\n }\n\n const now = Date.now();\n const timeSinceLastEvent = this.lastEventTimestamp ? now - this.lastEventTimestamp : null;\n\n const textareaValue = textareaRef.current?.value ?? '';\n const reactValue = currentValue ?? '';\n const valuesMatch = textareaValue === reactValue;\n\n const anomalies: string[] = [];\n\n if (!valuesMatch) {\n anomalies.push(\n `State mismatch: DOM=\"${textareaValue.slice(0, 50)}...\" vs React=\"${reactValue.slice(0, 50)}...\"`,\n );\n this.issueRegistry.set('state_mismatch', (this.issueRegistry.get('state_mismatch') ?? 0) + 1);\n }\n\n if (timeSinceLastEvent !== null && timeSinceLastEvent < 5) {\n anomalies.push(`Rapid event: ${timeSinceLastEvent}ms since last event`);\n this.issueRegistry.set('rapid_events', (this.issueRegistry.get('rapid_events') ?? 0) + 1);\n }\n\n if (category === 'user' && type === 'onChange') {\n const lengthDelta = Math.abs(textareaValue.length - reactValue.length);\n if (lengthDelta > 100) {\n anomalies.push(`Large paste detected: ${lengthDelta} characters changed`);\n this.issueRegistry.set('large_paste', (this.issueRegistry.get('large_paste') ?? 0) + 1);\n }\n }\n\n // Deduplicate large values using the registry\n const deduplicatedTextareaValue = this.valueRegistry.store(textareaValue);\n const deduplicatedReactValue = this.valueRegistry.store(reactValue);\n\n const event: AITelemetryEvent = {\n anomalies,\n category,\n data,\n description: this.generateDescription(type, category, data),\n stateSnapshot: {\n isControlled,\n reactValue: deduplicatedReactValue,\n textareaHeight,\n textareaValue: deduplicatedTextareaValue,\n valuesMatch,\n },\n timeSinceLastEvent,\n timestamp: now,\n timestampISO: new Date(now).toISOString(),\n type,\n };\n\n this.events.push(event);\n this.lastEventTimestamp = now;\n\n if (this.events.length > this.maxEvents) {\n this.events = this.events.slice(-this.maxEvents);\n }\n }\n\n /**\n * Generates a human-readable description for an event\n * @param type Event type\n * @param category Event category\n * @param data Event data\n * @returns Human-readable description\n */\n private generateDescription(type: string, category: string, data: Record<string, unknown>): string {\n switch (type) {\n case 'onChange':\n return `User typed/pasted text. New length: ${data.valueLength}`;\n case 'autoResize':\n return `Textarea auto-resized from ${data.beforeHeight}px to ${data.afterHeight}px`;\n case 'syncScroll':\n return `Synchronized scroll position to top:${(data as any).after?.scrollTop ?? 0}`;\n case 'syncStyles':\n return `Synchronized styles (padding, font, borders) between textarea and overlay`;\n case 'valueMismatch':\n return `CRITICAL: DOM value differs from React state`;\n case 'setValue':\n return `Programmatic value change via ref.setValue()`;\n case 'snapshot':\n return `Periodic state snapshot for debugging`;\n default:\n return `${category} event: ${type}`;\n }\n }\n\n /**\n * Generates a comprehensive AI-readable debug report\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param highlights Current highlights configuration\n * @returns Complete debug report\n */\n generateAIReport(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n highlights: unknown[],\n ): AIDebugReport {\n const firstEvent = this.events[0];\n const lastEvent = this.events[this.events.length - 1];\n const timespan = lastEvent && firstEvent ? lastEvent.timestamp - firstEvent.timestamp : 0;\n\n const detectedIssues = detectIssues(this.issueRegistry, this.events);\n const suspiciousPatterns = detectSuspiciousPatterns(this.events);\n const recommendations = generateRecommendations(detectedIssues, suspiciousPatterns);\n const timeline = buildTimeline(this.events);\n\n // Deduplicate final state values\n const finalTextareaValue = this.valueRegistry.store(textareaRef.current?.value ?? '');\n const finalReactValue = this.valueRegistry.store(currentValue ?? '');\n\n return {\n events: this.events,\n\n finalState: {\n height: textareaHeight,\n highlights,\n inSync: (textareaRef.current?.value ?? '') === (currentValue ?? ''),\n reactValue: finalReactValue,\n scrollPosition: {\n left: textareaRef.current?.scrollLeft ?? 0,\n top: textareaRef.current?.scrollTop ?? 0,\n },\n textareaValue: finalTextareaValue,\n },\n\n metadata: {\n browser: navigator.userAgent,\n componentVersion: '1.1.3',\n generatedAt: new Date().toISOString(),\n platform: navigator.platform,\n timespan: {\n durationMs: timespan,\n end: lastEvent?.timestampISO ?? '',\n start: firstEvent?.timestampISO ?? '',\n },\n totalEvents: this.events.length,\n },\n\n summary: {\n description:\n detectedIssues.length > 0\n ? `Found ${detectedIssues.length} issue(s) during ${this.events.length} recorded events over ${(timespan / 1000).toFixed(1)}s`\n : `No issues detected during ${this.events.length} events over ${(timespan / 1000).toFixed(1)}s`,\n detectedIssues,\n recommendations,\n suspiciousPatterns,\n },\n\n timeline,\n\n valueRegistry: this.valueRegistry.getRegistry(),\n };\n }\n\n /**\n * Exports telemetry data in AI-optimized format with instructions\n * @param textareaRef Reference to the textarea element\n * @param currentValue Current React state value\n * @param textareaHeight Current textarea height\n * @param highlights Current highlights configuration\n * @returns JSON string containing debug report and AI instructions\n */\n exportForAI(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n currentValue: string | undefined,\n textareaHeight: number | undefined,\n highlights: unknown[],\n ): string {\n const report = this.generateAIReport(textareaRef, currentValue, textareaHeight, highlights);\n\n return JSON.stringify(\n {\n aiInstructions: {\n commonIssues: {\n largePaste:\n 'Large paste operations where e.preventDefault() is called AFTER clipboard data is read - ' +\n 'browser inserts raw text into DOM before React state updates',\n rapidEvents: 'Events firing <5ms apart - possible race condition or double-bound handlers',\n resizeLoop: 'Excessive resize operations - likely infinite ResizeObserver loop',\n stateMismatch:\n 'DOM value differs from React state - likely caused by multiple onChange handlers or direct DOM manipulation',\n },\n keyFields: {\n 'events[].anomalies': 'Issues detected at each event',\n 'events[].stateSnapshot': 'Full state at each event for comparison',\n finalState: 'Current state at time of export',\n 'summary.detectedIssues': 'Automatically detected problems, sorted by severity',\n 'timeline.stateChanges': 'Chronological view of all state mutations',\n valueRegistry: 'Deduplicated text values - references like <REF:value_0> point to entries here',\n },\n purpose:\n 'This is a debug report from the DyeLight React component. ' +\n 'A textarea with highlighting overlay is experiencing synchronization issues.',\n valueDeduplication:\n 'IMPORTANT: Text values longer than 1000 characters are stored in valueRegistry and referenced as <REF:value_N>. ' +\n 'When you see <REF:value_0> in stateSnapshot.textareaValue, look up the actual value in debugReport.valueRegistry[\"<REF:value_0>\"]. ' +\n 'This optimization prevents the JSON from becoming huge when users paste large blocks of text. ' +\n 'Example: If textareaValue is \"<REF:value_0>\" and valueRegistry is {\"<REF:value_0>\": \"...40KB of text...\"}, ' +\n 'then the actual textarea value is that 40KB text.',\n yourTask: [\n '1. Review the summary.detectedIssues for critical problems',\n '2. Examine timeline.stateChanges for unexpected state transitions',\n '3. Look for patterns in events where stateSnapshot.valuesMatch is false',\n '4. Check for rapid events (<5ms apart) that might indicate race conditions',\n '5. If you see <REF:value_N> references, look them up in valueRegistry',\n '6. Identify the root cause and suggest specific code fixes',\n ],\n },\n debugReport: report,\n },\n null,\n 2,\n );\n }\n\n /**\n * Clears all recorded events and issue registry\n */\n clear() {\n this.events = [];\n this.issueRegistry.clear();\n this.lastEventTimestamp = null;\n this.valueRegistry.clear();\n }\n}\n","import type React from 'react';\nimport { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';\nimport { useAutoResize } from './hooks/useAutoResize';\nimport { useHighlightedContent } from './hooks/useHighlightedContent';\nimport { useHighlightSync } from './hooks/useHighlightSync';\nimport { useTextareaValue } from './hooks/useTextareaValue';\nimport { DEFAULT_BASE_STYLE, DEFAULT_CONTAINER_STYLE, DEFAULT_HIGHLIGHT_LAYER_STYLE } from './styles';\nimport { AIOptimizedTelemetry } from './telemetry';\nimport { isColorValue } from './textUtils';\nimport type { DyeLightProps, DyeLightRef } from './types';\n\n/**\n * @fileoverview DyeLight - A React textarea component with advanced text highlighting capabilities\n *\n * This component provides a textarea with overlay-based text highlighting that supports:\n * - Character-level highlighting using absolute text positions\n * - Line-level highlighting with CSS classes or color values\n * - Automatic height adjustment based on content\n * - Synchronized scrolling between textarea and highlight layer\n * - Both controlled and uncontrolled usage patterns\n * - RTL text direction support\n */\n\n/**\n * Creates a line element with optional highlighting\n */\nexport const createLineElement = (\n content: React.ReactNode,\n lineIndex: number,\n lineHighlight?: string,\n): React.ReactElement => {\n if (!lineHighlight) {\n return <div key={lineIndex}>{content}</div>;\n }\n\n const isColor = isColorValue(lineHighlight);\n return (\n <div\n className={isColor ? undefined : lineHighlight}\n key={lineIndex}\n style={isColor ? { backgroundColor: lineHighlight } : undefined}\n >\n {content}\n </div>\n );\n};\n\n/**\n * Renders a single line with character-level highlights and optional line-level highlighting\n */\nexport const renderHighlightedLine = (\n line: string,\n lineIndex: number,\n ranges: Array<{\n absoluteStart: number;\n className?: string;\n end: number;\n start: number;\n style?: React.CSSProperties;\n }>,\n lineHighlight?: string,\n): React.ReactElement => {\n if (ranges.length === 0) {\n const content = line || '\\u00A0';\n return createLineElement(content, lineIndex, lineHighlight);\n }\n\n const sortedRanges = ranges.toSorted((a, b) => a.start - b.start);\n\n const result: React.ReactNode[] = [];\n let lastIndex = 0;\n\n sortedRanges.forEach((range, idx) => {\n const { className, end, start, style: rangeStyle } = range;\n\n const clampedStart = Math.max(0, Math.min(start, line.length));\n const clampedEnd = Math.max(clampedStart, Math.min(end, line.length));\n\n if (clampedEnd <= lastIndex) {\n return;\n }\n\n const effectiveStart = Math.max(clampedStart, lastIndex);\n\n if (effectiveStart > lastIndex) {\n const textBefore = line.slice(lastIndex, effectiveStart);\n if (textBefore) {\n result.push(textBefore);\n }\n }\n\n if (clampedEnd > effectiveStart) {\n const highlightedText = line.slice(effectiveStart, clampedEnd);\n result.push(\n <span\n className={className}\n key={`highlight-${lineIndex}-${idx.toString()}`}\n style={rangeStyle}\n data-range-start={range.absoluteStart}\n >\n {highlightedText}\n </span>,\n );\n }\n\n lastIndex = Math.max(lastIndex, clampedEnd);\n });\n\n if (lastIndex < line.length) {\n const textAfter = line.slice(lastIndex);\n if (textAfter) {\n result.push(textAfter);\n }\n }\n\n const content = result.length === 0 ? '\\u00A0' : result;\n return createLineElement(content, lineIndex, lineHighlight);\n};\n\n/**\n * A textarea component with support for highlighting character ranges using absolute positions\n * and optional line-level highlighting. Perfect for syntax highlighting, error indication,\n * and text annotation without the complexity of line-based positioning.\n */\nexport const DyeLight = forwardRef<DyeLightRef, DyeLightProps>(\n (\n {\n className = '',\n containerClassName = '',\n defaultValue = '',\n dir = 'ltr',\n enableAutoResize = true,\n highlights = [],\n lineHighlights = {},\n onChange,\n rows = 4,\n style,\n value,\n debug = false,\n debugMaxEvents = 1000,\n ...props\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const textareaHeightRef = useRef<number | undefined>(undefined);\n\n const telemetry = useMemo(() => new AIOptimizedTelemetry(debug, debugMaxEvents), [debug, debugMaxEvents]);\n\n useEffect(() => {\n telemetry.setEnabled(debug);\n }, [debug, telemetry]);\n\n const isControlled = value !== undefined;\n const getHeight = useCallback(() => textareaHeightRef.current, []);\n\n const { currentValue, handleChange, setValue, textareaRef } = useTextareaValue(\n value,\n defaultValue,\n onChange,\n telemetry,\n undefined,\n getHeight,\n );\n\n const getCurrentValue = useCallback(() => currentValue, [currentValue]);\n\n const { handleAutoResize, textareaHeight } = useAutoResize(\n enableAutoResize,\n telemetry,\n textareaRef,\n getCurrentValue,\n isControlled,\n );\n\n useEffect(() => {\n textareaHeightRef.current = textareaHeight;\n }, [textareaHeight]);\n\n const { highlightLayerRef, syncScroll, syncStyles } = useHighlightSync(\n telemetry,\n textareaRef,\n getCurrentValue,\n getHeight,\n isControlled,\n );\n\n const highlightedContent = useHighlightedContent(\n currentValue,\n highlights,\n lineHighlights,\n renderHighlightedLine,\n );\n\n const handleChangeWithResize = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n handleChange(e);\n handleAutoResize(e.target);\n },\n [handleChange, handleAutoResize],\n );\n\n const setValueWithResize = useCallback(\n (newValue: string) => {\n setValue(newValue);\n if (textareaRef.current) {\n handleAutoResize(textareaRef.current);\n }\n },\n [setValue, handleAutoResize, textareaRef],\n );\n\n useEffect(() => {}, []);\n\n const handleScroll = useCallback(() => {\n syncScroll(textareaRef);\n }, [syncScroll, textareaRef]);\n\n useEffect(() => {\n if (!debug) {\n return;\n }\n\n const intervalId = setInterval(() => {\n telemetry.record('snapshot', 'system', {}, textareaRef, currentValue, textareaHeight, isControlled);\n }, 1000);\n\n return () => clearInterval(intervalId);\n }, [debug, telemetry, textareaRef, currentValue, textareaHeight, isControlled]);\n\n useImperativeHandle(\n ref,\n () => ({\n blur: () => textareaRef.current?.blur(),\n exportForAI: () => {\n return telemetry.exportForAI(textareaRef, currentValue, textareaHeight, highlights);\n },\n focus: () => textareaRef.current?.focus(),\n getValue: () => currentValue,\n scrollToPosition: (pos: number, offset = 40, behavior: ScrollBehavior = 'auto') => {\n if (highlightLayerRef.current && textareaRef.current) {\n const span = highlightLayerRef.current.querySelector(`[data-range-start=\"${pos.toString()}\"]`);\n if (span instanceof HTMLElement) {\n const textarea = textareaRef.current;\n const spanTop = span.offsetTop;\n if (behavior === 'smooth') {\n textarea.scrollTo({ behavior: 'smooth', top: spanTop - offset });\n } else {\n textarea.scrollTop = spanTop - offset;\n }\n }\n }\n },\n select: () => textareaRef.current?.select(),\n setSelectionRange: (start: number, end: number) => textareaRef.current?.setSelectionRange(start, end),\n setValue: setValueWithResize,\n }),\n [\n currentValue,\n setValueWithResize,\n highlightLayerRef,\n textareaRef,\n telemetry,\n textareaHeight,\n highlights,\n ],\n );\n\n useEffect(() => {\n if (textareaRef.current && enableAutoResize) {\n handleAutoResize(textareaRef.current);\n }\n syncStyles(textareaRef);\n }, [currentValue, handleAutoResize, enableAutoResize, syncStyles, textareaRef]);\n\n useEffect(() => {\n if (!textareaRef.current) {\n return;\n }\n\n const textarea = textareaRef.current;\n let rafId: number;\n\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n syncStyles(textareaRef);\n if (enableAutoResize) {\n handleAutoResize(textarea);\n }\n });\n });\n\n observer.observe(textarea);\n return () => {\n observer.disconnect();\n cancelAnimationFrame(rafId);\n };\n }, [textareaRef, syncStyles, handleAutoResize, enableAutoResize]);\n\n const baseTextareaStyle: React.CSSProperties = {\n ...DEFAULT_BASE_STYLE,\n color: currentValue ? 'transparent' : 'inherit',\n height: textareaHeight ? `${textareaHeight}px` : undefined,\n resize: enableAutoResize ? 'none' : 'vertical',\n };\n\n const highlightLayerStyle: React.CSSProperties = {\n ...DEFAULT_HIGHLIGHT_LAYER_STYLE,\n direction: dir,\n height: textareaHeight ? `${textareaHeight}px` : undefined,\n padding: textareaRef.current ? getComputedStyle(textareaRef.current).padding : '8px 12px',\n };\n\n return (\n <div className={containerClassName} ref={containerRef} style={{ ...DEFAULT_CONTAINER_STYLE, ...style }}>\n <div aria-hidden=\"true\" ref={highlightLayerRef} style={highlightLayerStyle}>\n {highlightedContent}\n </div>\n\n <textarea\n className={className}\n dir={dir}\n onChange={handleChangeWithResize}\n onScroll={handleScroll}\n ref={textareaRef}\n rows={rows}\n style={baseTextareaStyle}\n value={currentValue}\n {...props}\n />\n </div>\n );\n },\n);\n\nDyeLight.displayName = 'DyeLight';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
"description": "A lightweight TypeScript React component to allow highlighting characters in textareas.",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"@biomejs/biome": "^2.3.13",
|
|
8
|
-
"@storybook/addon-docs": "^10.2.
|
|
9
|
-
"@storybook/react-vite": "^10.2.
|
|
8
|
+
"@storybook/addon-docs": "^10.2.4",
|
|
9
|
+
"@storybook/react-vite": "^10.2.4",
|
|
10
10
|
"@testing-library/dom": "^10.4.1",
|
|
11
11
|
"@testing-library/react": "^16.3.2",
|
|
12
|
-
"@types/bun": "^1.3.
|
|
13
|
-
"@types/react": "^19.2.
|
|
12
|
+
"@types/bun": "^1.3.8",
|
|
13
|
+
"@types/react": "^19.2.10",
|
|
14
14
|
"@types/react-dom": "^19.2.3",
|
|
15
|
-
"semantic-release": "^25.0.
|
|
16
|
-
"storybook": "^10.2.
|
|
15
|
+
"semantic-release": "^25.0.3",
|
|
16
|
+
"storybook": "^10.2.4",
|
|
17
17
|
"tsdown": "^0.20.1",
|
|
18
18
|
"vite-tsconfig-paths": "^6.0.5"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
|
-
"bun": ">=1.3.
|
|
21
|
+
"bun": ">=1.3.8",
|
|
22
22
|
"node": ">=24.x"
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"main": "./dist/index.mjs",
|
|
44
44
|
"module": "./dist/index.mjs",
|
|
45
45
|
"name": "dyelight",
|
|
46
|
-
"packageManager": "bun@1.3.
|
|
46
|
+
"packageManager": "bun@1.3.8",
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"react": "^19.2.4",
|
|
49
49
|
"react-dom": "^19.2.4"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"url": "https://github.com/ragaeeb/dyelight.git"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
|
-
"build": "tsdown",
|
|
56
|
+
"build": "tsc -p tsconfig.build.json --noEmit && tsdown",
|
|
57
57
|
"format": "biome format --write .",
|
|
58
58
|
"lint": "biome lint .",
|
|
59
59
|
"storybook": "storybook dev -p 6006 --no-open",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
},
|
|
62
62
|
"type": "module",
|
|
63
63
|
"types": "./dist/index.d.mts",
|
|
64
|
-
"version": "1.2.
|
|
64
|
+
"version": "1.2.1"
|
|
65
65
|
}
|