@rcarls/rc-textarea 0.6.0 → 0.7.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.
@@ -1,5 +1,5 @@
1
- import { R as e } from "./line-actions-controller-BcsnmSPS.js";
2
- import { L as o, c as s, m as c } from "./line-actions-controller-BcsnmSPS.js";
1
+ import { R as e } from "./line-actions-controller-DUdXQxMA.js";
2
+ import { L as o, c as s, m as c } from "./line-actions-controller-DUdXQxMA.js";
3
3
  customElements.get("rc-textarea") || customElements.define("rc-textarea", e);
4
4
  export {
5
5
  o as LineActionsController,
@@ -0,0 +1,5 @@
1
+ import { F as l } from "./folding-action-controller-sxM3IomW.js";
2
+ export {
3
+ l as FoldingActionController
4
+ };
5
+ //# sourceMappingURL=rc-textarea-folding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-textarea-folding.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,4 +1,4 @@
1
- import { L as t, R as r, c as o, m as s } from "./line-actions-controller-BcsnmSPS.js";
1
+ import { L as t, R as r, c as o, m as s } from "./line-actions-controller-DUdXQxMA.js";
2
2
  export {
3
3
  t as LineActionsController,
4
4
  r as RCTextarea,
@@ -32,6 +32,7 @@ export declare function findEdit(oldValue: string, newValue: string): Edit;
32
32
  * - `LineDecoration`: dropped if its line falls within the deleted region;
33
33
  * otherwise shifted by the line delta.
34
34
  * - `WidgetDecoration`: treated like a zero-width mark at its offset.
35
+ * - `FoldDecoration`: dropped; the owning plugin recalculates source ranges.
35
36
  *
36
37
  * Returns a new array. The input array and its elements are not mutated.
37
38
  */
@@ -1,6 +1,7 @@
1
1
  import { Decoration } from './types.js';
2
2
  export declare class RCDocument {
3
3
  private readonly scroll;
4
+ private readonly lineSignatures;
4
5
  constructor($documentRoot: HTMLDivElement);
5
6
  /**
6
7
  * Rebuild the document tree.
@@ -0,0 +1,46 @@
1
+ import { DecorationInput, RCTextareaPluginAPI } from './types.js';
2
+ export interface SourceFoldRange {
3
+ /** First source line hidden by the fold (one-based, inclusive). */
4
+ fromLine: number;
5
+ /** Last source line hidden by the fold (one-based, inclusive). */
6
+ toLine: number;
7
+ }
8
+ export interface FoldingAction {
9
+ /** Stable identity for the expansion state across plugin updates. */
10
+ key: string;
11
+ /** Name the region and its context; aria-expanded supplies its current state. */
12
+ label: string;
13
+ /** Source lines or a caller-owned panel. The caller renders panel widgets separately. */
14
+ target: SourceFoldRange | HTMLElement;
15
+ /** One-based gutter line. Omit to place the button in the trailing lane. */
16
+ gutterLine?: number;
17
+ /** Source offset for a trailing-lane button; required without gutterLine. */
18
+ offset?: number;
19
+ /** Initial state for a newly seen key. Defaults to true. */
20
+ initiallyExpanded?: boolean;
21
+ /** Add decorative content beside the gutter button. */
22
+ createGutterContent?: ($button: HTMLButtonElement) => HTMLElement;
23
+ /** Refresh the cached gutter content without replacing its focused button. */
24
+ updateGutterContent?: ($content: HTMLElement) => void;
25
+ }
26
+ export interface FoldingActionControllerOptions {
27
+ /** Render a decorative icon for the current expansion state. */
28
+ createTriggerIcon?: (expanded: boolean) => Element | null;
29
+ }
30
+ /** Reconcile independent, accessible folding actions without owning their content. */
31
+ export declare class FoldingActionController {
32
+ private readonly entries;
33
+ private readonly gutterGroupElements;
34
+ private readonly api;
35
+ private readonly options;
36
+ constructor(api: RCTextareaPluginAPI, options?: FoldingActionControllerOptions);
37
+ private updateIcon;
38
+ private applyState;
39
+ /** Return trigger and collapsed-range decorations; callers provide any panel widget. */
40
+ getDecorations(actions: readonly FoldingAction[]): DecorationInput[];
41
+ /** Get expansion state for an existing key. */
42
+ isExpanded(key: string): boolean | undefined;
43
+ /** Discard expansion state and return focus before removing a focused control. */
44
+ clear(): void;
45
+ private release;
46
+ }
@@ -0,0 +1,2 @@
1
+ export { FoldingActionController } from './folding-action-controller.js';
2
+ export type { FoldingAction, FoldingActionControllerOptions, SourceFoldRange, } from './folding-action-controller.js';
@@ -1,5 +1,5 @@
1
1
  export { RCTextarea } from './rc-textarea.js';
2
- export type { Decoration, DecorationInput, MarkDecoration, MarkDecorationStyle, LineDecoration, WidgetDecoration, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern, LineDecoratorPlugin, Token, } from './types.js';
2
+ export type { Decoration, DecorationInput, MarkDecoration, MarkDecorationStyle, LineDecoration, WidgetDecoration, FoldDecoration, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern, LineDecoratorPlugin, Token, } from './types.js';
3
3
  export { matchPatternResults } from './pattern-matcher.js';
4
4
  export { createLineDecoratorPlugin } from './line-decorator.js';
5
5
  export type { LineDecoratorPluginOptions } from './line-decorator.js';
@@ -13,8 +13,9 @@ export interface LineActionsOptions {
13
13
  /** Where to place the inline widget. Default: `'append'`. */
14
14
  position?: 'append';
15
15
  /**
16
- * Create and return a DOM element for the given icon name (e.g. `'mdi-lock-outline'`).
17
- * Called when `action.iconName` is set. Return `null` to fall back to text label.
16
+ * Create and return a DOM element for the given icon name (e.g. `'mdi-lock-outline'`),
17
+ * such as an `<svg>` or an icon custom element. Called when `action.iconName` is set.
18
+ * Return `null` to fall back to text label.
18
19
  *
19
20
  * @example
20
21
  * ```ts
@@ -25,7 +26,7 @@ export interface LineActionsOptions {
25
26
  * }
26
27
  * ```
27
28
  */
28
- createIcon?: (iconName: string) => HTMLElement | null;
29
+ createIcon?: (iconName: string) => Element | null;
29
30
  /**
30
31
  * Full control over button content. Called instead of the default icon/label
31
32
  * logic. Use `createIcon` when you only need to swap the icon renderer —
@@ -1,8 +1,8 @@
1
1
  import { LitElement, CSSResultGroup } from 'lit';
2
- import { NativeChildController } from '@rcarls/rc-common';
2
+ import { FlowController, LocaleController, NativeChildController } from '@rcarls/rc-common';
3
3
  import { RCDocument } from './document.js';
4
4
  import { SavedSelection } from './selection.js';
5
- import { Decoration, DecorationInput, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern } from './types.js';
5
+ import { Decoration, DecorationInput, RCTextareaPlugin, RCTextareaPluginAPI, TextPattern, FoldDecoration } from './types.js';
6
6
  declare global {
7
7
  interface HTMLElementTagNameMap {
8
8
  'rc-textarea': RCTextarea;
@@ -13,8 +13,14 @@ interface UndoEntry {
13
13
  anchorOffset: number;
14
14
  focusOffset: number;
15
15
  }
16
+ interface FoldBounds {
17
+ from: number;
18
+ to: number;
19
+ anchorEnd: number;
20
+ reveal: () => void;
21
+ }
16
22
  /**
17
- * Textarea wrapper with line decorations, gutter rendering, inline widgets, and plugin hooks.
23
+ * Textarea wrapper with line decorations, gutter rendering, folding actions, and plugin hooks.
18
24
  *
19
25
  * Provide a native `<textarea>` as the direct child when the component is
20
26
  * editable. Read-only displays may be driven from `value` or `defaultValue`.
@@ -38,6 +44,11 @@ interface UndoEntry {
38
44
  * });
39
45
  * ```
40
46
  *
47
+ * The slotted `<textarea>` describes the content: its `lang` and `spellcheck` are
48
+ * mirrored onto the editor, and its `dir` sets the editor's base direction (`auto`
49
+ * resolves from the first strong character of the value, else the host's direction).
50
+ * The host's own `lang` stays the interface language, which formats line numbers.
51
+ *
41
52
  * @slot - Accepts a native `<textarea>` element for form wiring.
42
53
  *
43
54
  * @fires rc-textarea-change - Fired when the field value changes
@@ -48,6 +59,8 @@ interface UndoEntry {
48
59
  * @csspart root - The outer flex container wrapping the gutter and editor area.
49
60
  * @csspart gutter - The gutter column container.
50
61
  * @csspart gutter-cells - The gutter's line-label list.
62
+ * @csspart fold-trigger - Native button created by a folding action; style its visual appearance without changing its target or keyboard behavior.
63
+ * @csspart fold-icon - Consumer-supplied icon inside a folding action button, when provided.
51
64
  * @csspart editor-area - The container positioning the editor over the slotted textarea.
52
65
  * @csspart editor - The contenteditable field surface.
53
66
  *
@@ -55,26 +68,46 @@ interface UndoEntry {
55
68
  * @attr gutter - Enable the gutter column without any built-in content.
56
69
  * @attr word-wrap - Wrap long lines within the field to prevent horizontal overflow.
57
70
  * @attr auto-grow - Allow the field to grow vertically with content to prevent vertical overflow.
58
- * @attr read-only - Disable editing. The field renders as a styled read-only display.
71
+ * @attr line-direction - `base` (default) gives every line the editor's base direction; `auto` gives
72
+ * each line the direction of its first strong character, holding lines without one to the base.
73
+ * @attr [data-readonly] - Reflected while the slotted `<textarea>` is `readonly`. The editor stays
74
+ * focusable and selectable, announces `aria-readonly`, and blocks edits.
59
75
  * @attr value - Current field value.
60
76
  * @attr default-value - Initial uncontrolled value.
61
77
  *
62
78
  * @cssprop [--rc-textarea-border=1px solid ButtonBorder] - Border around the field
63
79
  * @cssprop [--rc-textarea-border-radius=2px] - Border radius of the field
64
80
  * @cssprop [--rc-textarea-background=Field] - Background color of the field
65
- * @cssprop [--rc-textarea-color=FieldText] - Text color; falls back through --rc-text
81
+ * @cssprop [--rc-textarea-color=var(--rc-field-text, FieldText)] - Text color
66
82
  * @cssprop [--rc-textarea-font-family=monospace] - Font family
67
83
  * @cssprop [--rc-textarea-font-size=1em] - Font size
68
84
  * @cssprop [--rc-textarea-line-height=1.5] - Line height
69
- * @cssprop [--rc-textarea-padding=0.5em] - Padding inside the field area
85
+ * @cssprop [--rc-textarea-padding-block=0.5em] - Block padding of the editor and gutter; any `padding-block` value
86
+ * @cssprop [--rc-textarea-padding-inline=0.5em] - Inline padding on both sides of the editor and before the gutter; one length, which the active line highlight bleeds through
87
+ * @cssprop [--rc-textarea-gutter-min-inline-size=24px] - Minimum width of the gutter, including its padding
88
+ * @cssprop [--rc-textarea-trailing-size=2.5em] - Reserved inline space and minimum block size for trailing controls
70
89
  * @cssprop [--rc-textarea-focus-outline=2px solid Highlight] - Focus ring outline
71
90
  * @cssprop [--rc-textarea-caret-color=FieldText] - Caret color
72
- * @cssprop [--rc-textarea-active-line-bg=transparent] - Active line highlight color
91
+ * @cssprop [--rc-textarea-active-line-bg=transparent] - Active line highlight color, including editor and gutter padding
73
92
  * @cssprop [--rc-textarea-gutter-bg=Canvas] - Gutter background color
74
93
  * @cssprop [--rc-textarea-gutter-color=GrayText] - Gutter text color
75
94
  * @cssprop [--rc-textarea-gutter-border=1px solid ButtonBorder] - Gutter right border
76
95
  * @cssprop [--rc-textarea-gutter-font-family=var(--rc-textarea-font-family, monospace)] - Gutter font family
96
+ * @cssprop [--rc-textarea-gutter-content-gap=0.25em] - Gap between a retained line number and custom gutter content
77
97
  * @cssprop [--rc-textarea-gutter-padding-inline-end=0.75em] - Space between gutter labels and text
98
+ * @cssprop [--rc-line-actions-gap=2px] - Gap between inline line actions; popover actions default to 4px
99
+ * @cssprop [--rc-line-actions-margin-start=6px] - Inline-start margin before line actions
100
+ * @cssprop [--rc-line-action-padding=1px] - Inline line-action padding; popover actions default to 4px 6px
101
+ * @cssprop [--rc-line-action-border=none] - Line-action border; the popover surface defaults to 1px solid ButtonBorder
102
+ * @cssprop [--rc-line-action-border-radius=3px] - Line-action border radius; the popover surface defaults to 4px
103
+ * @cssprop [--rc-line-action-bg=transparent] - Line-action background; the popover surface defaults to Canvas
104
+ * @cssprop [--rc-line-action-color=inherit] - Line-action color; popover actions default to ButtonText
105
+ * @cssprop [--rc-line-action-font-size=1em] - Inline line-action font size; popover actions default to 0.85em
106
+ * @cssprop [--rc-line-action-opacity=0.4] - Resting opacity of inline line actions
107
+ * @cssprop [--rc-line-action-hover-opacity=1] - Hover and focus opacity of inline line actions
108
+ * @cssprop [--rc-line-action-hover-bg=transparent] - Hover and focus background; popover actions default to ButtonFace
109
+ * @cssprop [--rc-line-action-hover-color=inherit] - Hover and focus text color
110
+ * @cssprop [--rc-line-action-shadow=0 2px 8px color-mix(in srgb, CanvasText 15%, transparent)] - Line-action popover shadow
78
111
  */
79
112
  export declare class RCTextarea extends LitElement {
80
113
  static styles: CSSResultGroup;
@@ -99,8 +132,20 @@ export declare class RCTextarea extends LitElement {
99
132
  wordWrap: boolean;
100
133
  /** Allow the field to grow vertically with content to prevent vertical overflow. */
101
134
  autoGrow: boolean;
102
- /** Disable editing. The field renders as a styled read-only display. */
103
- readOnly: boolean;
135
+ /**
136
+ * How each line's direction is chosen. `base` gives every line the editor's
137
+ * base direction, which suits code. `auto` gives a line with a letter the
138
+ * direction of its first strong character, for prose that mixes scripts, and
139
+ * holds lines without one (empty, digits, punctuation) to the base direction.
140
+ */
141
+ lineDirection: 'auto' | 'base';
142
+ /**
143
+ * Whether the slotted `<textarea>` is `readonly`. The native control is the
144
+ * source of truth; set `readonly` on it to change this state.
145
+ */
146
+ get readOnly(): boolean;
147
+ private _readOnly;
148
+ private _textareaAttributeObserver;
104
149
  /** Declarative plugin hook for framework integrations. */
105
150
  get plugin(): RCTextareaPlugin | null;
106
151
  /** Declarative plugin hook for framework integrations. */
@@ -132,6 +177,9 @@ export declare class RCTextarea extends LitElement {
132
177
  protected _document: RCDocument | null;
133
178
  protected _$textareaRef: WeakRef<HTMLTextAreaElement> | null;
134
179
  protected readonly _textareaController: NativeChildController<HTMLTextAreaElement>;
180
+ protected readonly _flowController: FlowController;
181
+ /** The interface locale, which formats line numbers. */
182
+ protected readonly _localeController: LocaleController;
135
183
  /** Plugin-owned decorations */
136
184
  protected _pluginDecorations: Map<string, Decoration>;
137
185
  /** Pattern-generated decorations (rebuilt on each value change). */
@@ -150,6 +198,9 @@ export declare class RCTextarea extends LitElement {
150
198
  protected _savedSelection: SavedSelection | null;
151
199
  protected _rafHandle: number | null;
152
200
  protected _composing: boolean;
201
+ private _contentAttributesPendingRender;
202
+ /** Set by Escape: the next Tab moves focus instead of inserting `\t`. */
203
+ private _tabMovesFocus;
153
204
  protected _isRendering: boolean;
154
205
  /** The currently highlighted `.line` element (active line). */
155
206
  protected _$activeLine: HTMLElement | null;
@@ -160,8 +211,18 @@ export declare class RCTextarea extends LitElement {
160
211
  protected _undoStack: UndoEntry[];
161
212
  protected _undoIndex: number;
162
213
  protected _resizeObserver: ResizeObserver | null;
214
+ protected _gutterBlockAxis: 'x' | 'y' | null;
163
215
  /** Cached per-line gutter labels from the last render — reused by ResizeObserver. */
164
- protected _gutterLabels: (string | null)[];
216
+ protected _gutterLabels: (string | HTMLElement | null | {
217
+ number: string;
218
+ content: string | HTMLElement;
219
+ })[];
220
+ /** Gutter cells with explicitly accessible plugin controls. */
221
+ protected _accessibleGutterLines: Set<number>;
222
+ /** Collapsed source ranges from the most recent render. */
223
+ protected _foldedRanges: FoldDecoration[];
224
+ protected _foldBounds: FoldBounds[];
225
+ protected _hiddenGutterLines: Set<number>;
165
226
  /**
166
227
  * The current field value.
167
228
  *
@@ -178,18 +239,52 @@ export declare class RCTextarea extends LitElement {
178
239
  disconnectedCallback(): void;
179
240
  firstUpdated(): void;
180
241
  updated(changed: Map<string, unknown>): void;
181
- render(): import('lit').TemplateResult<1>;
242
+ private _observeTextarea;
243
+ /**
244
+ * Mirror the native textarea's content attributes onto the editor. `lang`
245
+ * picks glyphs, the spelling dictionary and word boundaries; `spellcheck`
246
+ * stays off unless the textarea sets it. The host's own `lang` is the
247
+ * interface language and is never copied.
248
+ */
249
+ protected _syncContentAttributes(): void;
250
+ /**
251
+ * Set the editor's base direction from the textarea's `dir`. `auto` reads
252
+ * the native control's directionality, including its LTR neutral fallback;
253
+ * without `dir` the editor inherits. Composition keeps the current direction
254
+ * so the IME session isn't disturbed.
255
+ */
256
+ protected _syncContentDirection(): void;
257
+ /** The textarea's `lang`, or `''` when it sets none. */
258
+ protected _contentLang(): string;
259
+ /** The editor's resolved base direction. */
260
+ protected _contentDirection(): 'ltr' | 'rtl';
261
+ /**
262
+ * Mark lines with no strong character so `line-direction="auto"` holds them
263
+ * to the base direction. Reads the model value, which composition text
264
+ * doesn't reach until `compositionend`, so a composing line keeps its mark.
265
+ */
266
+ protected _syncLineDirections(value: string): void;
267
+ /**
268
+ * Mirror the native textarea's `readonly` attribute: the editor stays a
269
+ * focusable, selectable textbox that announces `aria-readonly`, and edits
270
+ * are blocked by the existing input guards.
271
+ */
272
+ protected _syncReadOnly(): void;
273
+ render(): import('lit-html').TemplateResult<1>;
182
274
  protected _onSlotChange(): void;
183
275
  protected _setupTextarea($textarea: HTMLTextAreaElement | null, $previousTextarea?: HTMLTextAreaElement | null): void;
184
276
  protected _onTextareaInvalid: () => void;
185
277
  protected _bindEditorEvents($editor: HTMLElement): void;
186
278
  protected _onInputEvent: () => void;
187
279
  protected _onBeforeInput: (event: InputEvent) => void;
280
+ protected _revealFoldAtOffset(offset: number): boolean;
281
+ protected _revealFoldForEdit(inputType: string): boolean;
188
282
  protected _onInput(): void;
189
283
  protected _onSelectionChange: () => void;
190
284
  protected _updateActiveLine(): void;
191
285
  protected _updateActiveGutterCell(): void;
192
286
  protected _onKeyDown: (e: KeyboardEvent) => void;
287
+ protected _isWidgetEvent(event: Event): boolean;
193
288
  protected _insertText(text: string): void;
194
289
  /**
195
290
  * Wrap the current selection with `prefix` and `suffix`.
@@ -206,6 +301,7 @@ export declare class RCTextarea extends LitElement {
206
301
  * When the selection is collapsed this is equivalent to `insertText`.
207
302
  */
208
303
  replaceSelection(text: string): void;
304
+ protected _onCopy: (event: ClipboardEvent) => void;
209
305
  protected _onPaste: (e: ClipboardEvent) => void;
210
306
  /** Reset the undo/redo history. Call when the field receives entirely new content. */
211
307
  clearHistory(): void;
@@ -253,15 +349,17 @@ export declare class RCTextarea extends LitElement {
253
349
  * - `string` — the label to display
254
350
  * - `null` — render an empty cell
255
351
  */
256
- protected _computeGutterLabels(allDecorations: Decoration[], value?: string): (string | null)[];
352
+ protected _computeGutterLabels(allDecorations: Decoration[], value?: string): (string | HTMLElement | null | {
353
+ number: string;
354
+ content: string | HTMLElement;
355
+ })[];
257
356
  /**
258
- * Synchronize the pixel height of each gutter cell to match the
357
+ * Synchronize the block size of each gutter cell to match the
259
358
  * corresponding `.line` element in the editor.
260
359
  *
261
- * In non-word-wrap mode the gutter uses a uniform `line-height` mirrored
262
- * from the editor's first line. In word-wrap mode each cell gets an explicit
263
- * `height` so wrapped lines stay vertically aligned with their gutter label.
264
- * Also copies the editor's computed `paddingTop`/`paddingBottom` to the
360
+ * In non-word-wrap mode without block widgets the gutter uses a uniform
361
+ * line height. Wrapped lines and block widgets use per-line heights instead.
362
+ * Also copies the editor's computed block padding to the
265
363
  * gutter to compensate for browser UA overrides on contenteditable.
266
364
  */
267
365
  protected _syncGutterHeights(): void;
@@ -271,7 +369,10 @@ export declare class RCTextarea extends LitElement {
271
369
  * `_gutterLabels` is updated first; otherwise the cached labels are reused
272
370
  * (called by the `ResizeObserver` without a new render pass).
273
371
  */
274
- protected _syncGutter(labels?: (string | null)[]): void;
372
+ protected _syncGutter(labels?: (string | HTMLElement | null | {
373
+ number: string;
374
+ content: string | HTMLElement;
375
+ })[]): void;
275
376
  protected _syncLabel(): void;
276
377
  protected _syncTypography($textarea: HTMLTextAreaElement): void;
277
378
  protected _syncTextareaValue(fromUser?: boolean): void;
@@ -28,6 +28,8 @@ interface DomPosition {
28
28
  * Returns null if offset is out of range.
29
29
  */
30
30
  export declare function textOffsetToDom($root: Element, targetOffset: number): DomPosition | null;
31
+ /** Returns the current selection focus node when it belongs to `$root`. */
32
+ export declare function getSelectionFocusNode($root: Element): Node | null;
31
33
  /**
32
34
  * Captures the current browser selection as plain-text offsets relative to `$root`.
33
35
  *
@@ -61,13 +61,19 @@ export interface LineDecoration {
61
61
  /** Additional HTML attributes set directly on the line element. */
62
62
  attributes?: Record<string, string>;
63
63
  /**
64
- * Override the built-in gutter cell text for this line.
64
+ * Override the built-in gutter cell content for this line.
65
65
  *
66
66
  * - `string` — custom label (e.g. `"!"`, `"▶"`)
67
+ * - `HTMLElement` — plugin-owned marker or control; reuse its node between updates.
68
+ * Set `gutterAccessible` when it contains an interactive control.
67
69
  * - `null` — force empty cell (suppress built-in content)
68
70
  * - `undefined` — use the built-in mode default (`line-numbers`, `gutter`, etc.)
69
71
  */
70
- gutterContent?: string | null;
72
+ gutterContent?: string | HTMLElement | null;
73
+ /** Keep the built-in line number before custom gutter content when `line-numbers` is enabled. */
74
+ gutterLineNumber?: boolean;
75
+ /** Expose this gutter cell to assistive technology when it contains a control. */
76
+ gutterAccessible?: boolean;
71
77
  }
72
78
  /**
73
79
  * A non-editable DOM element inserted at a character offset.
@@ -81,17 +87,36 @@ export interface WidgetDecoration {
81
87
  type: 'widget';
82
88
  /** Character offset. Widget is placed before (or after) the character at this position. */
83
89
  offset: number;
84
- /** Factory called each render cycle. Must return a new element each time. */
90
+ /** Create widget content when its line rebuilds. With a stable key, content may be reused. */
85
91
  create(): HTMLElement;
86
92
  /** Whether to place the widget before or after the character at `offset`. Default: 'before'. */
87
93
  side?: 'before' | 'after';
94
+ /** Preserve this widget's line DOM while its key and decorations remain unchanged.
95
+ * Change the key when its content changes. Scope keys to the owning plugin.
96
+ */
97
+ key?: string;
98
+ /** Place the widget inline, below its logical line, or in a reserved trailing lane. */
99
+ layout?: 'inline' | 'block' | 'trailing';
100
+ /** Expose interactive or meaningful widget content to assistive technology. Default: false. */
101
+ accessible?: boolean;
102
+ }
103
+ /** Hide complete source lines while retaining their text and model offsets. */
104
+ export interface FoldDecoration {
105
+ id: string;
106
+ type: 'fold';
107
+ /** First hidden line, one-based and inclusive. */
108
+ fromLine: number;
109
+ /** Last hidden line, one-based and inclusive. */
110
+ toLine: number;
111
+ /** Reveal this range before a user edit or caret move enters its hidden lines. */
112
+ reveal(): void;
88
113
  }
89
114
  /** Decorations are visual formatting applied to the field content without affecting
90
115
  * the underlying plain text value.
91
116
  */
92
- export type Decoration = MarkDecoration | LineDecoration | WidgetDecoration;
117
+ export type Decoration = MarkDecoration | LineDecoration | WidgetDecoration | FoldDecoration;
93
118
  /** Input shape for creating a decoration — id-less variant of each decoration type. */
94
- export type DecorationInput = Omit<MarkDecoration, 'id'> | Omit<LineDecoration, 'id'> | Omit<WidgetDecoration, 'id'>;
119
+ export type DecorationInput = Omit<MarkDecoration, 'id'> | Omit<LineDecoration, 'id'> | Omit<WidgetDecoration, 'id'> | Omit<FoldDecoration, 'id'>;
95
120
  /**
96
121
  * A semantic token produced by an external tokenizer (lezer, tree-sitter, shiki, etc.).
97
122
  *
@@ -138,6 +163,19 @@ export interface RCTextareaPluginAPI {
138
163
  readonly selectionStart: number;
139
164
  /** Normalized selection end offset (always ≥ `selectionStart`). Equals `selectionStart` when the cursor is collapsed. */
140
165
  readonly selectionEnd: number;
166
+ /**
167
+ * The editor's resolved base direction, from the textarea's `dir` or the
168
+ * host's. Anchor caret-relative popups with this, not the host's flow.
169
+ */
170
+ readonly contentDirection: 'ltr' | 'rtl';
171
+ /** The content language: the textarea's `lang`, or `''` when it sets none. */
172
+ readonly contentLang: string;
173
+ /**
174
+ * The resolved direction of a 1-based line. Under `line-direction="auto"`
175
+ * that is the direction of the line's first strong character; otherwise,
176
+ * and for lines without one, it is `contentDirection`.
177
+ */
178
+ lineDirection(line: number): 'ltr' | 'rtl';
141
179
  /**
142
180
  * Returns the bounding rect of the cursor (caret) in viewport coordinates.
143
181
  *
@@ -150,8 +188,13 @@ export interface RCTextareaPluginAPI {
150
188
  /**
151
189
  * Returns the word boundaries at the current cursor position.
152
190
  *
153
- * @returns the word and its `[from, to)` offsets, or `null` if the cursor is
154
- * not on a word character (`\w` — alphanumeric + underscore)
191
+ * Words come from `Intl.Segmenter` for `contentLang` (UAX #29, with
192
+ * dictionary segmentation for Japanese and Thai), so `crème`, `don't` and
193
+ * `snake_case` are each one word. The word starting at the cursor wins over
194
+ * the one ending there.
195
+ *
196
+ * @returns the word and its `[from, to)` offsets, or `null` if the cursor
197
+ * touches no letter or digit
155
198
  */
156
199
  getWordAtCursor(): {
157
200
  word: string;
@@ -341,8 +384,8 @@ export interface RCTextareaPlugin {
341
384
  /** Called when the plugin is replaced or the element disconnects. */
342
385
  destroy?(): void;
343
386
  /**
344
- * Display-layer value transform — called before `update`/`highlight` in
345
- * read-only mode.
387
+ * Display-layer value transform — called before `update`/`highlight`
388
+ * while the slotted textarea is `readonly`.
346
389
  *
347
390
  * Return a non-null string to substitute it as the rendered
348
391
  * text passed to all subsequent hooks and to the document builder.
package/package.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.6.0",
7
- "description": "Textarea wrapper with line decorations, gutter rendering, inline widgets, and plugin hooks.",
6
+ "version": "0.7.0",
7
+ "description": "Textarea wrapper with line decorations, gutter rendering, folding actions, and plugin hooks.",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
@@ -29,6 +29,12 @@
29
29
  "types": "./dist/types/packages/rc-textarea/src/define.d.ts",
30
30
  "default": "./dist/rc-textarea-define.js"
31
31
  }
32
+ },
33
+ "./folding": {
34
+ "import": {
35
+ "types": "./dist/types/packages/rc-textarea/src/folding.d.ts",
36
+ "default": "./dist/rc-textarea-folding.js"
37
+ }
32
38
  }
33
39
  },
34
40
  "types": "./dist/types/packages/rc-textarea/src/index.d.ts",
@@ -45,7 +51,7 @@
45
51
  "test:browser:firefox": "vitest --run --project=firefox"
46
52
  },
47
53
  "dependencies": {
48
- "@rcarls/rc-common": "0.6.0",
54
+ "@rcarls/rc-common": "0.7.0",
49
55
  "parchment": "^3.0.0"
50
56
  },
51
57
  "devDependencies": {