@rcarls/rc-textarea 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +1311 -0
  2. package/dist/custom-elements.json +2103 -0
  3. package/dist/demo.css +85 -0
  4. package/dist/demo.js +40 -0
  5. package/dist/line-actions-controller-BsM6DJ0b.js +2334 -0
  6. package/dist/line-actions-controller-BsM6DJ0b.js.map +1 -0
  7. package/dist/rc-textarea-define.js +10 -0
  8. package/dist/rc-textarea-define.js.map +1 -0
  9. package/dist/rc-textarea.js +8 -0
  10. package/dist/rc-textarea.js.map +1 -0
  11. package/dist/types/packages/rc-textarea/src/blots.d.ts +39 -0
  12. package/dist/types/packages/rc-textarea/src/decoration.d.ts +65 -0
  13. package/dist/types/packages/rc-textarea/src/decoration.test.d.ts +1 -0
  14. package/dist/types/packages/rc-textarea/src/define.d.ts +1 -0
  15. package/dist/types/packages/rc-textarea/src/document.d.ts +19 -0
  16. package/dist/types/packages/rc-textarea/src/index.d.ts +7 -0
  17. package/dist/types/packages/rc-textarea/src/line-actions-controller.d.ts +91 -0
  18. package/dist/types/packages/rc-textarea/src/line-decorator.d.ts +57 -0
  19. package/dist/types/packages/rc-textarea/src/line-decorator.test.d.ts +1 -0
  20. package/dist/types/packages/rc-textarea/src/pattern-matcher.d.ts +23 -0
  21. package/dist/types/packages/rc-textarea/src/pattern-matcher.test.d.ts +1 -0
  22. package/dist/types/packages/rc-textarea/src/rc-textarea.d.ts +194 -0
  23. package/dist/types/packages/rc-textarea/src/rc-textarea.styles.d.ts +1 -0
  24. package/dist/types/packages/rc-textarea/src/rc-textarea.test.d.ts +0 -0
  25. package/dist/types/packages/rc-textarea/src/selection.d.ts +39 -0
  26. package/dist/types/packages/rc-textarea/src/test-helpers.d.ts +18 -0
  27. package/dist/types/packages/rc-textarea/src/types.d.ts +394 -0
  28. package/package.json +68 -0
@@ -0,0 +1,39 @@
1
+ import { Registry, ScrollBlot, BlockBlot, InlineBlot, EmbedBlot } from 'parchment';
2
+ import { MarkDecoration } from './types.ts';
3
+ export { Registry };
4
+ export declare class V2ScrollBlot extends ScrollBlot {
5
+ static blotName: string;
6
+ constructor(registry: Registry, domNode: HTMLDivElement);
7
+ update(_mutations: MutationRecord[], _context: Record<string, unknown>): void;
8
+ optimize(context: {
9
+ [key: string]: unknown;
10
+ }): void;
11
+ optimize(mutations: MutationRecord[], context: {
12
+ [key: string]: unknown;
13
+ }): void;
14
+ }
15
+ export declare class V2BlockBlot extends BlockBlot {
16
+ static blotName: string;
17
+ static tagName: string;
18
+ static className: string;
19
+ }
20
+ type InlineFormats = Pick<MarkDecoration, 'className' | 'bold' | 'italic' | 'color' | 'background' | 'underline' | 'underlineColor'>;
21
+ export declare class V2InlineBlot extends InlineBlot {
22
+ static blotName: string;
23
+ static tagName: string;
24
+ static className: string;
25
+ static create(formats?: InlineFormats): HTMLElement;
26
+ }
27
+ /**
28
+ * Apply `MarkDecoration` style properties to a `.v2-mark` span element as
29
+ * inline styles and extra CSS classes. Called by `V2InlineBlot.create()` and
30
+ * by external code that needs to style a span to match a decoration.
31
+ */
32
+ export declare function applyInlineFormats(el: HTMLSpanElement, formats: InlineFormats): void;
33
+ export declare class V2WidgetBlot extends EmbedBlot {
34
+ static blotName: string;
35
+ static tagName: string;
36
+ static className: string;
37
+ static create(): HTMLElement;
38
+ }
39
+ export declare function createRegistry(): Registry;
@@ -0,0 +1,65 @@
1
+ import { Decoration, MarkDecoration, LineDecoration, DecorationInput } from './types.ts';
2
+ export { generateId } from './types.ts';
3
+ /**
4
+ * The minimal description of a single contiguous edit:
5
+ * a deletion of `removed` characters at `start`, followed by an insertion of
6
+ * `inserted` characters at that same position.
7
+ */
8
+ interface Edit {
9
+ /** Character offset where the edit begins (inclusive). */
10
+ start: number;
11
+ /** Number of characters removed. */
12
+ removed: number;
13
+ /** Number of characters inserted. */
14
+ inserted: number;
15
+ }
16
+ /**
17
+ * Find the single contiguous edit region that transforms `oldValue` into
18
+ * `newValue` by trimming equal prefix and suffix.
19
+ *
20
+ * This is a heuristic ("longest common prefix/suffix"), not a full LCS diff.
21
+ * It assumes the browser makes one contiguous edit per input event, which holds
22
+ * true for normal typing, deletion, and paste.
23
+ */
24
+ export declare function findEdit(oldValue: string, newValue: string): Edit;
25
+ /**
26
+ * Remap all decorations in `decorations` from positions in `oldValue` to
27
+ * positions in `newValue`, assuming a single contiguous edit.
28
+ *
29
+ * - `MarkDecoration`: remapped via `remapMarkRange`; dropped if collapsed.
30
+ * - `LineDecoration`: dropped if its line falls within the deleted region;
31
+ * otherwise shifted by the line delta.
32
+ * - `WidgetDecoration`: treated like a zero-width mark at its offset.
33
+ *
34
+ * Returns a new array. The input array and its elements are not mutated.
35
+ */
36
+ export declare function mapDecorationsThroughChange(decorations: Decoration[], oldValue: string, newValue: string): Decoration[];
37
+ /**
38
+ * Heuristic: returns `true` when an edit is large enough that remapping
39
+ * decorations would likely produce incorrect results and it's better to
40
+ * clear them entirely.
41
+ *
42
+ * Threshold: the change affects > 50 characters **and** > 50% of the document.
43
+ * This covers paste of large blocks, select-all + type, and programmatic value
44
+ * sets with substantially different content. It deliberately ignores inserts
45
+ * into an empty document (no existing decorations to preserve).
46
+ */
47
+ export declare function isLargeChange(oldValue: string, edit: Edit): boolean;
48
+ /**
49
+ * Map decorations through a text change, or clear them entirely if the change
50
+ * is classified as "large" (see `isLargeChange`).
51
+ *
52
+ * This is the primary entry point used by the component on each input event.
53
+ */
54
+ export declare function mapOrClear(decorations: Decoration[], oldValue: string, newValue: string): Decoration[];
55
+ /**
56
+ * Add a single decoration to `map`, assigning it a fresh UUID as its `id`.
57
+ * Returns the assigned ID.
58
+ */
59
+ export declare function addDecoration(map: Map<string, Decoration>, input: DecorationInput): string;
60
+ /**
61
+ * Replace all decorations in `map` with a new set derived from `inputs`.
62
+ * Each input is assigned a fresh UUID.
63
+ */
64
+ export declare function setDecorations(map: Map<string, Decoration>, inputs: DecorationInput[]): void;
65
+ export type { MarkDecoration, LineDecoration };
@@ -0,0 +1 @@
1
+ export * from './index.js';
@@ -0,0 +1,19 @@
1
+ import { Decoration } from './types.ts';
2
+ export declare class V2Document {
3
+ private readonly scroll;
4
+ constructor(editorEl: HTMLDivElement);
5
+ /**
6
+ * Rebuild the document tree from `value` + `decorations`.
7
+ * Reuses existing `.v2-line` elements in place to preserve DOM identity
8
+ * across renders (fixes DevTools node tracking and mouse gesture continuity).
9
+ * Only the children of each line are replaced; the line element itself survives.
10
+ */
11
+ build(value: string, decorations: Decoration[]): void;
12
+ destroy(): void;
13
+ }
14
+ /**
15
+ * Extracts plain text directly from the editor's contenteditable DOM.
16
+ * Handles our structured .v2-line divs as well as browser-inserted markup
17
+ * from edits that happen between render frames.
18
+ */
19
+ export declare function extractEditorText(editorEl: HTMLElement): string;
@@ -0,0 +1,7 @@
1
+ export { RCTextarea } from './rc-textarea.ts';
2
+ export type { Decoration, DecorationInput, MarkDecoration, MarkDecorationStyle, LineDecoration, WidgetDecoration, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern, LineDecoratorPlugin, Token, } from './types.ts';
3
+ export { matchPatternResults } from './pattern-matcher.ts';
4
+ export { createLineDecoratorPlugin } from './line-decorator.ts';
5
+ export type { LineDecoratorPluginOptions } from './line-decorator.ts';
6
+ export { LineActionsController } from './line-actions-controller.ts';
7
+ export type { LineAction, LineActionsOptions } from './line-actions-controller.ts';
@@ -0,0 +1,91 @@
1
+ import { RCTextareaPluginAPI, DecorationInput } from './types.ts';
2
+ export interface LineAction {
3
+ id: string;
4
+ label: string;
5
+ /** data-icon attribute — host renders via iconify or similar */
6
+ icon?: string;
7
+ onClick: () => void;
8
+ }
9
+ export interface LineActionsOptions {
10
+ /** Where to place the inline widget. Default: 'append'. */
11
+ position?: 'append';
12
+ /**
13
+ * Create and return a DOM element for the given icon name (e.g. 'mdi-lock-outline').
14
+ * Called when `action.icon` is set. Return `null` to fall back to text label.
15
+ *
16
+ * @example — iconify web component
17
+ * ```ts
18
+ * createIcon: (name) => {
19
+ * const el = document.createElement('iconify-icon');
20
+ * el.setAttribute('icon', name);
21
+ * return el;
22
+ * }
23
+ * ```
24
+ */
25
+ createIcon?: (iconName: string) => HTMLElement | null;
26
+ /**
27
+ * Full control over button content. Called instead of the default icon/label
28
+ * logic. Use `createIcon` when you only need to swap the icon renderer —
29
+ * `renderButton` is for cases that require full button customisation.
30
+ */
31
+ renderButton?: (action: LineAction, btn: HTMLButtonElement) => void;
32
+ }
33
+ /**
34
+ * Helper class for plugins that need to show inline action buttons next to a
35
+ * line, or a floating popover panel anchored to the active line.
36
+ *
37
+ * Instantiate in `mount()`, call `getDecorations()` / `getDecorationsForLines()`
38
+ * from `update()`, and call `destroy()` from the plugin's `destroy()`.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * editor.usePlugin({
43
+ * mount(api) {
44
+ * this._controller = new LineActionsController(api);
45
+ * },
46
+ * update(value, api) {
47
+ * const lineIndex = LineActionsController.getActiveLineIndex(value, api.selectionStart);
48
+ * const decs = this._controller.getDecorations(lineIndex, myActions, value, 'inline');
49
+ * api.setDecorations(decs);
50
+ * },
51
+ * destroy() { this._controller.destroy(); },
52
+ * });
53
+ * ```
54
+ */
55
+ export declare class LineActionsController {
56
+ private readonly _api;
57
+ private readonly _options;
58
+ private _popover;
59
+ private _lastActionsKey;
60
+ private readonly _blurHandler;
61
+ constructor(api: RCTextareaPluginAPI, options?: LineActionsOptions);
62
+ /**
63
+ * Call from plugin's `update()`. Pass the line index, its actions, the full
64
+ * value, and the current render mode.
65
+ *
66
+ * - `'inline'`: returns a `WidgetDecoration[]` to merge into `setDecorations()`.
67
+ * - `'popover'`: returns `[]` and manages a floating panel as a side effect.
68
+ *
69
+ * Passing an empty actions array hides the popover and returns `[]`.
70
+ */
71
+ getDecorations(lineIndex: number, actions: LineAction[], value: string, renderMode: 'inline' | 'popover'): DecorationInput[];
72
+ /**
73
+ * Variant for `showOn: 'always'` — one entry per line with actions.
74
+ *
75
+ * In popover mode only the `activeLineIndex` line is shown; in inline mode
76
+ * every line with non-empty actions gets a widget.
77
+ */
78
+ getDecorationsForLines(lineActions: Map<number, LineAction[]>, value: string, renderMode: 'inline' | 'popover', activeLineIndex?: number): DecorationInput[];
79
+ /** Remove the floating panel and detach all listeners. Call from plugin `destroy()`. */
80
+ destroy(): void;
81
+ /** Derive the 0-based line index from a plain-text character offset. */
82
+ static getActiveLineIndex(value: string, selectionStart: number): number;
83
+ /**
84
+ * Character offset of the end of the given 0-based line (points at the
85
+ * newline character, or end-of-string for the last line).
86
+ */
87
+ private static _lineEndOffset;
88
+ private _showPopover;
89
+ private _positionPopover;
90
+ private _hidePopover;
91
+ }
@@ -0,0 +1,57 @@
1
+ import { DecorationInput, LineDecoratorPlugin, RCTextareaPlugin } from './types.ts';
2
+ /**
3
+ * Options for `createLineDecoratorPlugin`.
4
+ */
5
+ export interface LineDecoratorPluginOptions {
6
+ /**
7
+ * An array of subscriber setup functions. Each function receives a callback
8
+ * and should call it whenever an external value changes (e.g. a reactive
9
+ * signal). It may return an optional cleanup/unsubscribe function called on
10
+ * plugin destroy.
11
+ *
12
+ * This is intentionally framework-agnostic. For Solid.js signals:
13
+ * ```ts
14
+ * watch: [
15
+ * (cb) => createEffect(on(mySignal, cb, { defer: true })),
16
+ * ]
17
+ * ```
18
+ */
19
+ watch?: Array<(onChange: () => void) => (() => void) | void>;
20
+ /**
21
+ * Called once per update pass **after** all per-line decorations are built.
22
+ * Use this to merge in decorations that require the full document value
23
+ * (e.g. diagnostics from a whole-document parser).
24
+ */
25
+ extraDecorations?: (value: string) => DecorationInput[];
26
+ }
27
+ /**
28
+ * Factory that wraps a `LineDecoratorPlugin` in a full `RCTextareaPlugin`.
29
+ *
30
+ * Handles the boilerplate that every per-line decorator needs:
31
+ * - CSS injection via `api.adoptStyleSheet` on mount
32
+ * - `lineStart` offset bookkeeping when converting line-relative offsets to
33
+ * absolute document offsets
34
+ * - Subscribing to external change signals and calling `api.scheduleUpdate()`
35
+ * - Cleanup of subscribers on destroy
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const plugin = createLineDecoratorPlugin(
40
+ * {
41
+ * styles: '.kw { font-weight: bold; }',
42
+ * decorateLine(line) {
43
+ * const results: ReturnType<LineDecoratorPlugin['decorateLine']> = [];
44
+ * for (const m of line.matchAll(/\bfunction\b/g)) {
45
+ * results.push({ type: 'mark', from: m.index!, to: m.index! + m[0].length, className: 'kw' });
46
+ * }
47
+ * return results;
48
+ * },
49
+ * },
50
+ * {
51
+ * extraDecorations: (value) => getDiagnosticDecorations(value),
52
+ * },
53
+ * );
54
+ * editor.usePlugin(plugin);
55
+ * ```
56
+ */
57
+ export declare function createLineDecoratorPlugin(decorator: LineDecoratorPlugin, options?: LineDecoratorPluginOptions): RCTextareaPlugin;
@@ -0,0 +1,23 @@
1
+ import { TextPattern, MarkDecoration, LineDecoration } from './types.ts';
2
+ export type { TextPattern };
3
+ /**
4
+ * Run all `patterns` against `value` and collect every match as decorations.
5
+ *
6
+ * For each pattern:
7
+ * - If `captureGroups` is set, one `MarkDecoration` is emitted **per named
8
+ * capture group** instead of one for the whole match. Unmatched optional
9
+ * groups are silently skipped. The `d` flag (match indices) is added to
10
+ * the regex automatically.
11
+ * - Otherwise, one `MarkDecoration` covering the entire match is emitted,
12
+ * using the styling fields copied directly from the pattern.
13
+ * - If `createLineDecoration` is provided, it is called for every match
14
+ * (regardless of `captureGroups`) and may produce a paired `LineDecoration`.
15
+ *
16
+ * The global flag is added automatically if missing to ensure all occurrences
17
+ * are found and to avoid stateful `lastIndex` bugs on the caller's regex.
18
+ * Zero-length matches are skipped to prevent infinite loops.
19
+ */
20
+ export declare function matchPatternResults(value: string, patterns: TextPattern[]): {
21
+ markDecorations: Omit<MarkDecoration, 'id'>[];
22
+ lineDecorations: Omit<LineDecoration, 'id'>[];
23
+ };
@@ -0,0 +1,194 @@
1
+ import { LitElement, CSSResultGroup } from 'lit';
2
+ import { SavedSelection } from './selection.ts';
3
+ import { Decoration, DecorationInput, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern } from './types.ts';
4
+ /**
5
+ * Enhanced textarea with line decorations, gutter, and plugin API.
6
+ *
7
+ * @slot - Accepts a native `<textarea>` element for form wiring and progressive enhancement.
8
+ * @fires rc-textarea-change - Fired when the editor value changes
9
+ * @fires rc-textarea-blur - Fired when the editor loses focus
10
+ * @cssprop [--rc-textarea-border=1px solid ButtonBorder] - Border around the editor
11
+ * @cssprop [--rc-textarea-border-radius=2px] - Border radius of the editor
12
+ * @cssprop [--rc-textarea-background=Field] - Background color of the editor
13
+ * @cssprop [--rc-textarea-color=FieldText] - Text color; falls back through --rc-text
14
+ * @cssprop [--rc-textarea-font-family=monospace] - Font family
15
+ * @cssprop [--rc-textarea-font-size=1em] - Font size
16
+ * @cssprop [--rc-textarea-line-height=1.5] - Line height
17
+ * @cssprop [--rc-textarea-padding=0.5em] - Padding inside the editor area
18
+ * @cssprop [--rc-textarea-focus-outline=2px solid Highlight] - Focus ring outline
19
+ * @cssprop [--rc-textarea-caret-color=FieldText] - Caret color
20
+ * @cssprop [--rc-textarea-active-line-bg=transparent] - Active line highlight color
21
+ * @cssprop [--rc-textarea-gutter-bg=Canvas] - Gutter background color
22
+ * @cssprop [--rc-textarea-gutter-color=GrayText] - Gutter text color
23
+ * @cssprop [--rc-textarea-gutter-border=1px solid ButtonBorder] - Gutter right border
24
+ */
25
+ export declare class RCTextarea extends LitElement {
26
+ static styles: CSSResultGroup;
27
+ static shadowRootOptions: {
28
+ delegatesFocus: boolean;
29
+ clonable?: boolean;
30
+ customElementRegistry?: CustomElementRegistry;
31
+ mode: ShadowRootMode;
32
+ serializable?: boolean;
33
+ slotAssignment?: SlotAssignmentMode;
34
+ };
35
+ lineNumbers: boolean;
36
+ listNumbers: boolean;
37
+ gutter: boolean;
38
+ wordWrap: boolean;
39
+ autoGrow: boolean;
40
+ readOnly: boolean;
41
+ label: string | null;
42
+ /** Declarative plugin hook for framework integrations. */
43
+ get plugin(): RCTextareaPlugin | null;
44
+ /** Declarative plugin hook for framework integrations. */
45
+ set plugin(plugin: RCTextareaPlugin | null);
46
+ /**
47
+ * Reactive decorations — set from outside the component without registering a plugin.
48
+ * Ideal for reactive frameworks (Solid, React 19+, Vue 3) where decorations are
49
+ * computed as reactive state and passed directly as a property:
50
+ *
51
+ * ```tsx
52
+ * // Solid
53
+ * <rc-textarea decorations={decorations()} />
54
+ *
55
+ * // React 19+
56
+ * <rc-textarea decorations={decorations} />
57
+ * ```
58
+ *
59
+ * Merges with plugin decorations and pattern decorations on every render.
60
+ * Setting this property triggers a new render; setting it to `undefined` or `[]`
61
+ * clears any previously set external decorations.
62
+ */
63
+ get decorations(): DecorationInput[];
64
+ set decorations(value: DecorationInput[] | undefined);
65
+ private _value;
66
+ private _defaultValue;
67
+ private _initialValueResolved;
68
+ private _valueSetByHost;
69
+ private _document;
70
+ private _textareaRef;
71
+ /** Plugin-owned decorations (set via PluginAPI.setDecorations / addDecoration). */
72
+ protected _pluginDecorations: Map<string, Decoration>;
73
+ /** Pattern-generated decorations (rebuilt on each value change). */
74
+ private _patternDecorations;
75
+ /** Decorations set directly via the `decorations` property (reactive-framework-friendly). */
76
+ private _externalDecorations;
77
+ /** Registered patterns. */
78
+ private _patterns;
79
+ protected _plugin: RCTextareaPlugin | null;
80
+ private _pluginProperty;
81
+ protected _pluginApi: RCTextareaPluginAPI | null;
82
+ /** Sequence counter for async plugin safety (discard stale results). */
83
+ private _pluginSeq;
84
+ /** Stylesheets adopted into the shadow root by the active plugin. */
85
+ private _pluginSheets;
86
+ protected _savedSelection: SavedSelection | null;
87
+ private _rafHandle;
88
+ private _composing;
89
+ private _isRendering;
90
+ /** The currently highlighted `.v2-line` element (active line). */
91
+ private _activeLine;
92
+ /** Callbacks registered via PluginAPI.onCursorMove(). */
93
+ private _cursorCallbacks;
94
+ private _docSelectionChangeHandler;
95
+ /** Undo/redo stack — necessary because DOM rebuilds invalidate browser's native undo. */
96
+ private _undoStack;
97
+ private _undoIndex;
98
+ private _resizeObserver;
99
+ /** Cached per-line gutter labels from the last render — reused by ResizeObserver. */
100
+ private _gutterLabels;
101
+ get value(): string;
102
+ set value(v: string);
103
+ /** Initial uncontrolled editor value. */
104
+ get defaultValue(): string | undefined;
105
+ /** Initial uncontrolled editor value. */
106
+ set defaultValue(v: string | undefined);
107
+ connectedCallback(): void;
108
+ disconnectedCallback(): void;
109
+ firstUpdated(): void;
110
+ updated(changed: Map<string, unknown>): void;
111
+ render(): import('lit').TemplateResult<1>;
112
+ private _onSlotChange;
113
+ private _bindEditorEvents;
114
+ private _onInputEvent;
115
+ private _onInput;
116
+ private _onSelectionChange;
117
+ private _updateActiveLine;
118
+ private _updateActiveGutterCell;
119
+ private _onKeyDown;
120
+ protected _insertText(text: string): void;
121
+ /**
122
+ * Wrap the current selection with `prefix` and `suffix`.
123
+ * No-op when the selection is collapsed (no text selected).
124
+ *
125
+ * Uses the model path directly (not `execCommand`) because callers such as
126
+ * toolbar buttons move focus away from the editor before this fires, which
127
+ * clears the DOM selection. `_savedSelection` retains the last model-level
128
+ * anchor/focus offsets.
129
+ */
130
+ wrapSelection(prefix: string, suffix: string): void;
131
+ /**
132
+ * Replace the current selection with `text`.
133
+ * When the selection is collapsed this is equivalent to `insertText`.
134
+ */
135
+ replaceSelection(text: string): void;
136
+ private _onPaste;
137
+ /** Reset the undo/redo history. Call when the editor receives entirely new content. */
138
+ clearHistory(): void;
139
+ protected _pushUndo(sel: SavedSelection | null): void;
140
+ private _undo;
141
+ private _redo;
142
+ private _applyUndoEntry;
143
+ usePlugin(plugin: RCTextareaPlugin): void;
144
+ removePlugin(): void;
145
+ /**
146
+ * Build the `RCTextareaPluginAPI` object passed to the active plugin's
147
+ * `mount()` call. Each getter delegates to the component's live state so the
148
+ * API stays accurate across render frames without needing to be rebuilt.
149
+ */
150
+ private _buildPluginApi;
151
+ private _clearPluginSheets;
152
+ addPattern(pattern: Omit<TextPattern, 'id'>): string;
153
+ removePattern(id: string): void;
154
+ clearPatterns(): void;
155
+ protected _scheduleRender(): void;
156
+ private _performRender;
157
+ /**
158
+ * Compute the label string for each gutter cell based on the current gutter
159
+ * mode (`lineNumbers`, `listNumbers`, `gutter`) and any
160
+ * `LineDecoration.gutterContent` overrides in `allDecorations`.
161
+ *
162
+ * Returns one entry per line (same length as `value.split('\n')`):
163
+ * - `string` — the label to display
164
+ * - `null` — render an empty cell
165
+ */
166
+ private _computeGutterLabels;
167
+ /**
168
+ * Synchronize the pixel height of each gutter cell to match the
169
+ * corresponding `.v2-line` element in the editor.
170
+ *
171
+ * In non-word-wrap mode the gutter uses a uniform `line-height` mirrored
172
+ * from the editor's first line. In word-wrap mode each cell gets an explicit
173
+ * `height` so wrapped lines stay vertically aligned with their gutter label.
174
+ * Also copies the editor's computed `paddingTop`/`paddingBottom` to the
175
+ * gutter to compensate for browser UA overrides on contenteditable.
176
+ */
177
+ private _syncGutterHeights;
178
+ /**
179
+ * Add, remove, or update `.gutter-cell` spans so their count and text
180
+ * content match `labels`. When `labels` is provided the cached
181
+ * `_gutterLabels` is updated first; otherwise the cached labels are reused
182
+ * (called by the `ResizeObserver` without a new render pass).
183
+ */
184
+ private _syncGutter;
185
+ private _syncTypography;
186
+ protected _syncTextareaValue(fromUser?: boolean): void;
187
+ protected _dispatchChange(value: string): void;
188
+ private _getEditorEl;
189
+ }
190
+ declare global {
191
+ interface HTMLElementTagNameMap {
192
+ 'rc-textarea': RCTextarea;
193
+ }
194
+ }
@@ -0,0 +1 @@
1
+ export declare const styles: import('lit').CSSResult;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Cursor save/restore for rc-textarea's contenteditable editor.
3
+ *
4
+ * The text model is a plain string with \n line separators.
5
+ * WidgetDecoration spans (.v2-widget) are skipped — they are zero-width in
6
+ * the text model (not part of the value string).
7
+ * <br> elements (used to make empty lines editable) count as 0 characters.
8
+ */
9
+ export interface SavedSelection {
10
+ /** Anchor position as a character offset into the plain text value. */
11
+ anchorOffset: number;
12
+ /** Focus position as a character offset into the plain text value. */
13
+ focusOffset: number;
14
+ }
15
+ /**
16
+ * Converts a DOM (node, nodeOffset) pair inside `root` to a plain-text offset.
17
+ * Returns -1 if the target was not found inside root.
18
+ */
19
+ export declare function domToTextOffset(root: Element, targetNode: Node, targetNodeOffset: number): number;
20
+ interface DomPosition {
21
+ node: Node;
22
+ offset: number;
23
+ }
24
+ /**
25
+ * Converts a plain-text offset to a (node, nodeOffset) DOM position inside `root`.
26
+ * Returns null if offset is out of range.
27
+ */
28
+ export declare function textOffsetToDom(root: Element, targetOffset: number): DomPosition | null;
29
+ /**
30
+ * Captures the current browser selection as plain-text offsets relative to `root`.
31
+ * Returns null if there is no selection or the selection is outside `root`.
32
+ */
33
+ export declare function saveSelection(root: Element): SavedSelection | null;
34
+ /**
35
+ * Restores a previously saved selection inside `root`.
36
+ * Silently does nothing if the saved offsets are out of range.
37
+ */
38
+ export declare function restoreSelection(root: Element, saved: SavedSelection): void;
39
+ export {};
@@ -0,0 +1,18 @@
1
+ import { RCTextarea } from './rc-textarea.ts';
2
+ export declare function getEditor(host: RCTextarea): HTMLDivElement;
3
+ export declare function getGutterCells(host: RCTextarea): HTMLDivElement;
4
+ export declare function getSlottedTextarea(host: RCTextarea): HTMLTextAreaElement;
5
+ /**
6
+ * Wait one animation frame for _performRender to start, then flush one microtask
7
+ * tick so that synchronous plugin `update()` calls complete before the test asserts.
8
+ */
9
+ export declare function waitRender(): Promise<void>;
10
+ /**
11
+ * Dispatches a synthetic paste event carrying plain text onto `editor`.
12
+ *
13
+ * Using a plain Event + manual clipboardData override because `new ClipboardEvent`
14
+ * with a DataTransfer in the init dict is not reliable across browsers (Firefox
15
+ * ignores the init's clipboardData). The paste handler only calls
16
+ * `e.clipboardData?.getData('text/plain')`, so a minimal duck-typed mock is enough.
17
+ */
18
+ export declare function simulatePaste(editor: HTMLElement, text: string): void;