@vscode/markdown-editor 0.0.2-5 → 0.0.2-51

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 (36) hide show
  1. package/README.md +26 -0
  2. package/dist/_observables/observableInternal/index.d.ts +1 -1
  3. package/dist/_observables/observableInternal/logging/consoleObservableLogger.d.ts +1 -1
  4. package/dist/_observables/observableInternal/logging/debugger/devToolsLogger.d.ts +1 -1
  5. package/dist/_observables/observableInternal/logging/logging.d.ts +1 -1
  6. package/dist/_observables/observableInternal/reactions/{autorun.d.ts → createEffect.d.ts} +11 -3
  7. package/dist/config-BGeaJqWk.js +286 -0
  8. package/dist/config-BGeaJqWk.js.map +1 -0
  9. package/dist/config.d.ts +10 -0
  10. package/dist/config.js +6 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/index.d.ts +1456 -56
  13. package/dist/index.js +6067 -1924
  14. package/dist/index.js.map +1 -1
  15. package/dist/markdown-editor.css +1 -0
  16. package/dist/observables.js +45 -43
  17. package/dist/observables.js.map +1 -1
  18. package/dist/{runOnChange-C00UIwqQ.js → runOnChange-e7FtCqOQ.js} +115 -111
  19. package/dist/runOnChange-e7FtCqOQ.js.map +1 -0
  20. package/dist/stringEdit-CVDbCUBY.js +215 -0
  21. package/dist/stringEdit-CVDbCUBY.js.map +1 -0
  22. package/dist/web-editors.d.ts +130 -0
  23. package/dist/web-editors.js +6185 -0
  24. package/dist/web-editors.js.map +1 -0
  25. package/package.json +48 -9
  26. package/src/contrib/comments/commentInput.css +150 -0
  27. package/src/contrib/comments/comments.css +129 -0
  28. package/src/contrib/commentsVscode/vscodeCommentWidgetV2.css +171 -0
  29. package/src/contrib/find/find.css +347 -0
  30. package/src/view/editor.css +522 -23
  31. package/src/view/themes/default.css +12 -3
  32. package/src/view/themes/github.css +13 -8
  33. package/src/view/themes/vscode-default.css +398 -0
  34. package/src/view/themes/vscode-github.css +395 -0
  35. package/dist/runOnChange-C00UIwqQ.js.map +0 -1
  36. /package/dist/_observables/observableInternal/reactions/{autorunImpl.d.ts → createEffectImpl.d.ts} +0 -0
package/dist/index.d.ts CHANGED
@@ -6,10 +6,27 @@ import { ISettableObservable } from './_observables/index';
6
6
  import { ITransaction } from './_observables/index';
7
7
  import { MonarchTokenizer } from 'monaco-editor/esm/vs/editor/standalone/common/monarch/monarchLexer.js';
8
8
 
9
+ declare interface AddedItem {
10
+ readonly kind: 'added';
11
+ readonly node: AstNode;
12
+ readonly modifiedStart: number;
13
+ readonly insertedLocal: readonly AnnotatedRange[];
14
+ }
15
+
16
+ /**
17
+ * A word/character-level highlight inside a single block, in that block's
18
+ * *local* coordinate space (`0` = block start). `inserted` ranges live on a
19
+ * modified/added block, `deleted` ranges on an original/removed block.
20
+ */
21
+ declare interface AnnotatedRange {
22
+ readonly range: OffsetRange;
23
+ readonly kind: 'inserted' | 'deleted';
24
+ }
25
+
9
26
  /** Every concrete node kind, for exhaustive consumer-side dispatch. */
10
- export declare type AnyAstNode = TextAstNode | MarkerAstNode | GlueAstNode | ThematicBreakAstNode | StrongAstNode | EmphasisAstNode | StrikethroughAstNode | InlineCodeAstNode | InlineMathAstNode | LinkAstNode | ImageAstNode | HeadingAstNode | ParagraphAstNode | CodeBlockAstNode | MathBlockAstNode | BlockQuoteAstNode | ListAstNode | ListItemAstNode | TableAstNode | TableRowAstNode | TableCellAstNode | DocumentAstNode;
27
+ export declare type AnyAstNode = TextAstNode | MarkerAstNode | GlueAstNode | ThematicBreakAstNode | StrongAstNode | EmphasisAstNode | StrikethroughAstNode | InlineCodeAstNode | InlineMathAstNode | LinkAstNode | ImageAstNode | HeadingAstNode | ParagraphAstNode | FrontMatterAstNode | CodeBlockAstNode | MathBlockAstNode | BlockQuoteAstNode | ListAstNode | ListItemAstNode | TableAstNode | TableRowAstNode | TableCellAstNode | DocumentAstNode | UnhandledBlockAstNode;
11
28
 
12
- declare type AnyViewData = DocumentViewData | BlockViewData | InlineViewData | ListItemViewData | TableRowViewData | TableCellViewData | MarkerViewData | GlueViewData;
29
+ declare type AnyViewData = DocumentViewData | BlockViewData | InlineViewData | ListItemViewData | TableRowViewData | TableCellViewData | MarkerViewData | GlueViewData | DiffHunkViewData | DiffDecorationViewData;
13
30
 
14
31
  export declare abstract class AstNode {
15
32
  abstract readonly kind: string;
@@ -71,7 +88,7 @@ export declare class AsyncClipboardStrategy implements IClipboardStrategy {
71
88
  connect(context: IClipboardContext): IDisposable;
72
89
  }
73
90
 
74
- export declare type BlockAstNode = HeadingAstNode | ParagraphAstNode | CodeBlockAstNode | MathBlockAstNode | ThematicBreakAstNode | BlockQuoteAstNode | ListAstNode | TableAstNode;
91
+ export declare type BlockAstNode = HeadingAstNode | ParagraphAstNode | FrontMatterAstNode | CodeBlockAstNode | MathBlockAstNode | ThematicBreakAstNode | BlockQuoteAstNode | ListAstNode | TableAstNode | UnhandledBlockAstNode;
75
92
 
76
93
  /**
77
94
  * A block-level node. Every block may carry a {@link leadingTrivia} glue — the
@@ -92,7 +109,7 @@ declare abstract class BlockAstNodeBase extends AstNode {
92
109
  /**
93
110
  * One block's place in the rendered document.
94
111
  *
95
- * `height` is in CSS pixels. It is either a real DOM measurement
112
+ * Geometry is in editor-local CSS pixels. `height` is either a real DOM measurement
96
113
  * (`isMeasured: true`) or an estimate produced when the block is not
97
114
  * currently mounted (`isMeasured: false`). Estimates exist so virtual
98
115
  * rendering can size the scroll container without mounting every block.
@@ -107,6 +124,13 @@ export declare interface BlockMeasurement {
107
124
  readonly block: BlockAstNode;
108
125
  readonly absoluteStart: number;
109
126
  readonly height: number;
127
+ /** Local border box when mounted and measured. */
128
+ readonly rect: Rect2D | undefined;
129
+ /** Local horizontal padding-box clip when this block scrolls horizontally. */
130
+ readonly viewportClip: {
131
+ readonly left: number;
132
+ readonly right: number;
133
+ } | undefined;
110
134
  readonly isMeasured: boolean;
111
135
  readonly visualLineMap: VisualLineMap | undefined;
112
136
  readonly viewNode: ViewNode | undefined;
@@ -130,7 +154,14 @@ declare class BlockQuoteViewData {
130
154
  constructor(ast: BlockQuoteAstNode, content: readonly AnyViewData[]);
131
155
  }
132
156
 
133
- declare type BlockViewData = HeadingViewData | ParagraphViewData | CodeBlockViewData | MathBlockViewData | ThematicBreakViewData | BlockQuoteViewData | ListViewData | TableViewData;
157
+ /**
158
+ * All blocks whose source range intersects `[start, endExclusive]`. A
159
+ * collapsed range (start === endExclusive) matches the block containing
160
+ * that offset (with the same boundary rule as {@link findBlockAtOffset}).
161
+ */
162
+ export declare function blocksIntersecting(doc: DocumentAstNode, start: SourceOffset, endExclusive: SourceOffset): BlockAstNode[];
163
+
164
+ declare type BlockViewData = HeadingViewData | ParagraphViewData | FrontMatterViewData | CodeBlockViewData | MathBlockViewData | ThematicBreakViewData | BlockQuoteViewData | ListViewData | TableViewData | UnhandledBlockViewData;
134
165
 
135
166
  /**
136
167
  * Base view node for everything the editor renders, generic over the
@@ -151,6 +182,17 @@ export declare class BlockViewNode<T extends AnyViewData = AnyViewData> extends
151
182
  constructor(data: T, dom: globalThis.Node, children: readonly ViewNode[]);
152
183
  get block(): BlockAstNode;
153
184
  get element(): HTMLElement;
185
+ /**
186
+ * The horizontal scroll viewport for selection/caret clipping
187
+ * ({@link blockViewportClip}). For most blocks the scroller *is*
188
+ * {@link element} — a code / math / unhandled block's `element` is the very
189
+ * `overflow-x: auto` box that scrolls. A table is the exception: its
190
+ * `element` stays the inner `<table>` (so the active/markers classes and
191
+ * `.md-table` theme styling are unaffected), but the box that actually
192
+ * scrolls is the wrapping `.md-table-wrapper`, so {@link TableViewNode}
193
+ * overrides this to return that wrapper.
194
+ */
195
+ get scrollElement(): HTMLElement;
154
196
  /**
155
197
  * Whether this already-built node can stand in for `data` unchanged. The
156
198
  * builder preserves view-data identity for any subtree whose ast and
@@ -174,9 +216,10 @@ export declare interface BlockViewOptions {
174
216
  /**
175
217
  * Opens a link's URL. Called when the user activates a link: a plain click
176
218
  * while the link's block is inactive (rendered), or a Ctrl/Cmd+click while it
177
- * is active (source shown). Defaults to `window.open(url, '_blank')`.
219
+ * is active (source shown). Return `false` to use the anchor's native
220
+ * navigation behavior.
178
221
  */
179
- readonly onOpenLink?: (url: string, event: MouseEvent) => void;
222
+ readonly onOpenLink?: (url: string, event: MouseEvent) => false | void;
180
223
  /**
181
224
  * Colours fenced code blocks. When set, a code block's content is rendered
182
225
  * as a sequence of token spans instead of one plain text node. This is the
@@ -196,6 +239,22 @@ export declare interface BlockViewOptions {
196
239
  * `katexEditableIdentifiers.ts`).
197
240
  */
198
241
  readonly renderMath?: (request: MathRenderRequest) => MathRendering | undefined;
242
+ /**
243
+ * Pluggable factory for an in-place, interactive editor that replaces the
244
+ * *rendered* (inactive) form of a fenced code block — see
245
+ * {@link IEmbeddedCodeEditor}. When it returns an editor for the block's
246
+ * language, that editor's element is mounted instead of the highlighted
247
+ * code, and content flows both ways as string edits. Returning `undefined`
248
+ * falls back to the default rendering. EXPERIMENTAL.
249
+ */
250
+ readonly embeddedCodeEditorFactory?: IEmbeddedCodeEditorFactory;
251
+ /**
252
+ * Called when an {@link IEmbeddedCodeEditor} edits its content. `contentEdit`
253
+ * is in the block's *content* coordinates; the host translates it to a
254
+ * document edit (via {@link CodeBlockAstNode.codeOffset} and the block's
255
+ * offset) and applies it to the model.
256
+ */
257
+ readonly onEmbeddedCodeEditorEdit?: (block: CodeBlockAstNode, contentEdit: StringEdit) => void;
199
258
  }
200
259
 
201
260
  export declare class CodeBlockAstNode extends BlockAstNodeBase {
@@ -271,10 +330,243 @@ export declare class CodeBlockViewNode extends BlockViewNode<CodeBlockViewData>
271
330
  * its predecessor's subscription explicitly.
272
331
  */
273
332
  private _snapshotSub;
333
+ /**
334
+ * An in-place interactive editor (e.g. an iframe) mounted instead of the
335
+ * rendered code. Like {@link _session} it is adopted from `previous` across
336
+ * rebuilds so the underlying editor keeps its state, and must be disposed
337
+ * manually (a node reused as `previous` is never {@link dispose}d).
338
+ */
339
+ private _embeddedEditor;
274
340
  constructor(data: CodeBlockViewData, options: BlockViewOptions | undefined, previous: ViewNode | undefined);
275
341
  dispose(): void;
276
342
  }
277
343
 
344
+ /** A persistent comment anchored to a source range. */
345
+ declare interface Comment_2 {
346
+ readonly id: string;
347
+ /** Source range the comment refers to (its highlighted region). */
348
+ readonly range: OffsetRange;
349
+ /** The comment text. */
350
+ readonly body: string;
351
+ /** Display name of the author, if any. */
352
+ readonly author?: string;
353
+ /** Creation time (epoch ms), used to render a relative timestamp. */
354
+ readonly createdAt?: number;
355
+ }
356
+ export { Comment_2 as Comment }
357
+
358
+ /**
359
+ * A self-contained comment input box — a rounded panel with an auto-growing
360
+ * textarea and a send button, styled after the gdocs/Word "add a comment"
361
+ * affordance.
362
+ *
363
+ * This widget is *positioning-agnostic*: it only owns its own DOM and state.
364
+ * A host (the comment-mode controller, or a fixture) mounts {@link element}
365
+ * wherever it likes and is responsible for placing it relative to a selection.
366
+ *
367
+ * State is observable-driven (no framework): {@link value} reflects the live
368
+ * textarea content; submit/cancel are reported through the option callbacks.
369
+ */
370
+ export declare class CommentInputWidget extends Disposable {
371
+ private readonly _options?;
372
+ readonly element: HTMLElement;
373
+ private readonly _textarea;
374
+ private readonly _measure;
375
+ private readonly _submitButton;
376
+ private readonly _value;
377
+ /** Live, untrimmed textarea content. */
378
+ get value(): IObservable<string>;
379
+ /** The raw textarea, exposed so a host can move focus into it (e.g. on Tab). */
380
+ get inputElement(): HTMLTextAreaElement;
381
+ constructor(_options?: CommentInputWidgetOptions | undefined);
382
+ /** Move focus into the textarea (caret at the end). */
383
+ focus(): void;
384
+ /** Replace the textarea content. */
385
+ setText(text: string): void;
386
+ /** Clear the textarea. */
387
+ clear(): void;
388
+ private _submit;
389
+ private _autoSize;
390
+ }
391
+
392
+ export declare interface CommentInputWidgetOptions {
393
+ /** Placeholder shown while the textarea is empty. Defaults to "Add Comment". */
394
+ readonly placeholder?: string;
395
+ /** Called after the textarea changes size. */
396
+ readonly onDidChangeSize?: () => void;
397
+ /**
398
+ * Called when the user submits a non-empty comment (Enter or the send
399
+ * button). The text is trimmed; never called with an empty string.
400
+ */
401
+ readonly onSubmit?: (text: string) => void;
402
+ /** Called when the user dismisses the input (Escape). */
403
+ readonly onCancel?: () => void;
404
+ }
405
+
406
+ /**
407
+ * Comment mode — a gdocs/Word-style "add a comment" affordance layered on top of
408
+ * the editor *without modifying it*. It reads the editor's public observables
409
+ * ({@link EditorModel.readonlyMode}, {@link EditorModel.selection}) and the
410
+ * exposed {@link EditorView.caretRect} geometry, and mounts a
411
+ * {@link CommentInputWidget} into {@link EditorView.overlayContainer}.
412
+ *
413
+ * Behaviour:
414
+ * - Only active in read-only mode (the "review" view).
415
+ * - When a user-created selection is non-empty, the input box appears next to
416
+ * the caret (the selection's active end) but does NOT take focus, so keyboard
417
+ * selection keeps working. Programmatic selections such as find matches do
418
+ * not summon it. Press Tab to move focus into the box, then type.
419
+ * - The box appears on mouse-up, not mid-drag, so it doesn't flicker/jump
420
+ * while a selection is being dragged out (keyboard selection shows at once).
421
+ * - While the box has focus or holds a draft it is frozen in place (selection
422
+ * changes, drags and clicks no longer move it). It is dismissed by Escape,
423
+ * by submitting, or by blurring an empty box.
424
+ * - The editor's logical caret geometry remains available for anchoring in
425
+ * read-only mode even though the painted caret is hidden. While the box has
426
+ * focus, `.md-comment-active` also suppresses the painted caret in any mode.
427
+ */
428
+ export declare class CommentModeController extends Disposable {
429
+ private readonly _model;
430
+ private readonly _view;
431
+ private readonly _options?;
432
+ private static _isCommentableSelectionSource;
433
+ private readonly _widget;
434
+ private readonly _gap;
435
+ private _visible;
436
+ private _anchorX;
437
+ private _pinnedRange;
438
+ /**
439
+ * The range a comment was just submitted for. The box stays hidden for it
440
+ * until the selection changes, so submitting doesn't immediately re-summon an
441
+ * empty box on the still-selected text.
442
+ */
443
+ private _submittedRange;
444
+ constructor(_model: EditorModel, _view: EditorView, _options?: CommentModeControllerOptions | undefined);
445
+ private _update;
446
+ private _show;
447
+ private _layoutHorizontally;
448
+ /** Force-hide and clear the box (used by Escape and submit). */
449
+ private _hide;
450
+ /**
451
+ * Hide unless the user is engaged with the box: it has focus or holds a
452
+ * non-empty draft. This preserves in-progress text and keeps a focused box
453
+ * open (it is dismissed explicitly via Escape/submit, or by blurring it).
454
+ */
455
+ private _autoHide;
456
+ private _widgetHasFocus;
457
+ /**
458
+ * The visible viewport (client coords) used for the flip-above decision: the
459
+ * nearest scrollable ancestor of the editor. `.md-editor` itself spans the
460
+ * full document height and never clips, so measuring against it would always
461
+ * report room below. Falls back to the window when nothing scrolls.
462
+ */
463
+ private _getViewportRect;
464
+ private _hideAndRefocus;
465
+ private _submit;
466
+ }
467
+
468
+ export declare interface CommentModeControllerOptions {
469
+ /** Called when the user submits a comment for the current selection. */
470
+ readonly onSubmit?: (submission: CommentSubmission) => void;
471
+ /** Gap (px) between the bottom of the selection and the top of the input box. */
472
+ readonly gap?: number;
473
+ }
474
+
475
+ /** Human-readable labels (for pickers / dropdowns). */
476
+ export declare const COMMENTS_DESIGN_LABELS: Record<CommentsDesign, string>;
477
+
478
+ /** design id → presenter factory over a shared CommentsModel + EditorView. */
479
+ export declare const COMMENTS_DESIGNS: Record<CommentsDesign, CommentsPresenterFactory>;
480
+
481
+ /** The available comment rendering designs. */
482
+ export declare type CommentsDesign = 'connected' | 'vscode' | 'vscode-v2';
483
+
484
+ /**
485
+ * Seedable store of {@link Comment}s for the comment-mode contribution. Owns the
486
+ * comment list and the shared hover state; it has no opinion on rendering or
487
+ * persistence — a host seeds it via {@link set}/{@link add} and observes
488
+ * {@link comments}.
489
+ */
490
+ export declare class CommentsModel {
491
+ private readonly _comments;
492
+ /** Monotonic counter for ids of comments created via {@link create}. */
493
+ private _sequence;
494
+ /** The current comments, in insertion order. */
495
+ get comments(): IObservable<readonly Comment_2[]>;
496
+ /**
497
+ * The comment currently hovered (by its card or its highlight), or
498
+ * `undefined`. Shared so the card and the highlight can react together.
499
+ */
500
+ readonly hoveredId: ISettableObservable<string | undefined>;
501
+ /** Replace the whole comment set. */
502
+ set(comments: readonly Comment_2[]): void;
503
+ /**
504
+ * Create a comment from a user submission and append it, generating its `id`
505
+ * and `createdAt` here so id/time allocation stays the store's concern (the
506
+ * UI only supplies the range and text). Returns the created comment.
507
+ */
508
+ create(input: {
509
+ range: OffsetRange;
510
+ body: string;
511
+ author?: string;
512
+ }): Comment_2;
513
+ /** Append a comment. */
514
+ add(comment: Comment_2): void;
515
+ /** Remove a comment by id. */
516
+ remove(id: string): void;
517
+ }
518
+
519
+ /** Optional, design-specific context a presenter may use (ignored by others). */
520
+ export declare interface CommentsPresenterContext {
521
+ /** Light/dark hint for the token-wrapped VS Code widget designs. */
522
+ readonly theme?: 'light' | 'dark';
523
+ /**
524
+ * Resolves a source offset to a 1-based line number, for designs that show
525
+ * line info (the VS Code V1 card). Optional because the model itself carries
526
+ * no text; the host (which owns the source) supplies it.
527
+ */
528
+ readonly resolveLine?: (offset: number) => number;
529
+ }
530
+
531
+ /** Builds a presenter for a given model + editor view. */
532
+ export declare type CommentsPresenterFactory = (model: CommentsModel, view: EditorView, context?: CommentsPresenterContext) => ICommentsPresenter;
533
+
534
+ /** A comment the user submitted, with the source range it was anchored to. */
535
+ export declare interface CommentSubmission {
536
+ readonly text: string;
537
+ readonly range: OffsetRange;
538
+ }
539
+
540
+ /**
541
+ * Renders persistent comments as a gdocs-style side rail: each comment's range
542
+ * is highlighted (reusing the editor's selection geometry via
543
+ * {@link EditorView.rangeRects}), a leader line curves from the bottom of that
544
+ * highlight to a card stacked in the right rail, and cards never overlap.
545
+ *
546
+ * Everything is mounted into {@link EditorView.overlayContainer} so it shares
547
+ * the selection/caret coordinate space and scrolls with the document. When the
548
+ * editor's natural right margin is too narrow for the rail, the view reserves
549
+ * proportional space by padding the editor on the right — but only while there
550
+ * are comments.
551
+ */
552
+ export declare class CommentsView extends Disposable {
553
+ private readonly _model;
554
+ private readonly _view;
555
+ private readonly _layer;
556
+ private readonly _entries;
557
+ constructor(_model: CommentsModel, _view: EditorView);
558
+ private _createLayer;
559
+ private _update;
560
+ /** Create/update/remove per-comment DOM to match `comments`. */
561
+ private _reconcile;
562
+ private _createEntry;
563
+ private _fillCard;
564
+ private _disposeEntry;
565
+ /** Position highlights, cards (stacked) and leader lines. */
566
+ private _layout;
567
+ private _applyHover;
568
+ }
569
+
278
570
  /**
279
571
  * A {@link MonacoSyntaxHighlighter} preloaded with a handful of common Monarch
280
572
  * grammars (plus the usual short aliases). Unknown languages fall back to an
@@ -292,6 +584,8 @@ export declare interface CursorCommandContext {
292
584
  readonly selection: Selection_2;
293
585
  readonly document: DocumentAstNode;
294
586
  readonly activeBlock: BlockAstNode | undefined;
587
+ readonly markerVisibleBlocks: ReadonlySet<BlockAstNode>;
588
+ readonly wordNavigationConfig: WordNavigationConfig;
295
589
  }
296
590
 
297
591
  export declare const cursorDocumentEnd: CursorCommand;
@@ -300,6 +594,8 @@ export declare const cursorDocumentStart: CursorCommand;
300
594
 
301
595
  export declare const cursorDown: VisualCursorCommand;
302
596
 
597
+ export declare type CursorKeyboardAction = 'left' | 'right' | 'up' | 'down' | 'wordLeft' | 'wordRight' | 'visualLineStart' | 'visualLineEnd' | 'logicalLineStart' | 'logicalLineEnd' | 'documentStart' | 'documentEnd';
598
+
303
599
  export declare const cursorLeft: CursorCommand;
304
600
 
305
601
  export declare const cursorLineEnd: CursorCommand;
@@ -329,15 +625,27 @@ export declare const cursorUp: VisualCursorCommand;
329
625
  * derived subscribed.
330
626
  */
331
627
  export declare class CursorView extends Disposable {
332
- private readonly _parent;
333
628
  readonly element: HTMLElement;
334
629
  readonly rendering: IObservable<CursorViewRendering>;
335
- constructor(_parent: HTMLElement, options: CursorViewOptions);
630
+ constructor(options: CursorViewOptions);
336
631
  }
337
632
 
338
633
  export declare interface CursorViewOptions {
339
634
  readonly offset: IObservable<SourceOffset | undefined>;
340
635
  readonly visualLineMap: IObservable<VisualLineMap>;
636
+ /**
637
+ * The mounted blocks, used to hide the caret when it sits at an offset that
638
+ * has been scrolled out of its (horizontally scrolling) block's viewport —
639
+ * matching how the selection is clipped there.
640
+ */
641
+ readonly blocks?: IObservable<readonly SelectionBlock[]>;
642
+ /**
643
+ * When set, the caret is drawn over the transient empty paragraph instead
644
+ * of at {@link offset} — its editor-local rect comes straight
645
+ * from that synthetic element's geometry, since it has no visual-line-map
646
+ * entry. Takes priority over the normal offset-based placement.
647
+ */
648
+ readonly pendingCaretRect?: IObservable<Rect2D | undefined>;
341
649
  }
342
650
 
343
651
  export declare class CursorViewRendering {
@@ -347,18 +655,100 @@ export declare class CursorViewRendering {
347
655
  constructor(offset: SourceOffset, visible: boolean, rect: Rect2D);
348
656
  }
349
657
 
658
+ export declare const cursorVisualLineEnd: VisualCursorCommand;
659
+
660
+ export declare const cursorVisualLineStart: VisualCursorCommand;
661
+
350
662
  export declare const cursorWordLeft: CursorCommand;
351
663
 
352
664
  export declare const cursorWordRight: CursorCommand;
353
665
 
666
+ export declare const DEFAULT_INDENTATION_CONFIG: IndentationConfig;
667
+
668
+ export declare const DEFAULT_WORD_NAVIGATION_CONFIG: WordNavigationConfig;
669
+
670
+ export declare const DEFAULT_WORD_SEPARATORS = "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?";
671
+
354
672
  export declare const deleteLeft: EditCommand;
355
673
 
674
+ export declare const deleteLineLeft: EditCommand;
675
+
676
+ export declare const deleteLineRight: EditCommand;
677
+
356
678
  export declare const deleteRight: EditCommand;
357
679
 
358
680
  export declare const deleteWordLeft: EditCommand;
359
681
 
360
682
  export declare const deleteWordRight: EditCommand;
361
683
 
684
+ /**
685
+ * A read-only "removed" decoration: an original block rendered (red) above its
686
+ * place in the modified document, occupying vertical space like a view-zone but
687
+ * contributing **zero** source length, so the editor's source mapping stays the
688
+ * modified document and editing is unaffected. Used for `removed` and the
689
+ * original side of a `replaced` block in editor diff mode.
690
+ */
691
+ declare class DiffDecorationViewData {
692
+ readonly ast: AstNode;
693
+ readonly side: BlockViewData;
694
+ readonly deletedRanges: readonly DiffHighlightRange[];
695
+ /** True when the whole block was removed: solid red band, no word rects. */
696
+ readonly whole: boolean;
697
+ /** Absolute offset of this block in the *original* document. */
698
+ readonly originalStart: number;
699
+ readonly kind = "diffDecoration";
700
+ constructor(ast: AstNode, side: BlockViewData, deletedRanges: readonly DiffHighlightRange[],
701
+ /** True when the whole block was removed: solid red band, no word rects. */
702
+ whole: boolean,
703
+ /** Absolute offset of this block in the *original* document. */
704
+ originalStart: number);
705
+ }
706
+
707
+ /** A word/character highlight inside one diff side, in block-local coords. */
708
+ declare interface DiffHighlightRange {
709
+ readonly range: OffsetRange;
710
+ readonly kind: 'inserted' | 'deleted';
711
+ }
712
+
713
+ /**
714
+ * A changed block rendered as its original form stacked over its modified form
715
+ * (either side may be absent for a pure deletion/insertion). It is itself a
716
+ * document child the renderer mounts like a block; its {@link ast} is the
717
+ * surviving side's ast, used only for view-node identity/reuse.
718
+ */
719
+ declare class DiffHunkViewData {
720
+ readonly ast: AstNode;
721
+ readonly original: DiffSideViewData | undefined;
722
+ readonly modified: DiffSideViewData | undefined;
723
+ readonly kind = "diffHunk";
724
+ constructor(ast: AstNode, original: DiffSideViewData | undefined, modified: DiffSideViewData | undefined);
725
+ }
726
+
727
+ /**
728
+ * The recursive classification of a diff. Each item describes one aligned
729
+ * position in the merged document:
730
+ *
731
+ * - `unchanged` — render the (modified) node once, neutral.
732
+ * - `added` — exists only in the modified document (green).
733
+ * - `removed` — exists only in the original document (red).
734
+ * - `replaced` — a *leaf* block changed in place → render original over
735
+ * modified, with word-level {@link AnnotatedRange}s on each.
736
+ * - `nested` — a *container* changed → render it once and diff its
737
+ * {@link NestedItem.children} recursively.
738
+ *
739
+ * Offsets ({@link UnchangedItem.modifiedStart} etc.) are absolute in their
740
+ * respective documents, so a renderer/visualizer can slice the source text.
741
+ */
742
+ declare type DiffItem = UnchangedItem | AddedItem | RemovedItem | ReplacedItem | NestedItem;
743
+
744
+ /** One side (original or modified) of a {@link DiffHunkViewData}. */
745
+ declare interface DiffSideViewData {
746
+ readonly view: BlockViewData;
747
+ /** Render in active form (markers/whitespace visible). */
748
+ readonly active: boolean;
749
+ readonly ranges: readonly DiffHighlightRange[];
750
+ }
751
+
362
752
  export declare class DocumentAstNode extends AstNode {
363
753
  readonly content: readonly (BlockAstNode | GlueAstNode)[];
364
754
  readonly kind = "document";
@@ -383,13 +773,21 @@ declare interface DocumentBlockViewData {
383
773
  readonly view: BlockViewData;
384
774
  }
385
775
 
386
- /** A mounted document child: a block or a run of inter-block glue. */
776
+ /** A mounted document child: a block, a run of inter-block glue, the
777
+ * transient empty paragraph (see {@link PendingParagraphViewData}), or a
778
+ * {@link DiffHunkViewData diff hunk} (stacked original/modified blocks). */
387
779
  declare interface DocumentChildViewData {
388
780
  readonly absoluteStart: number;
389
781
  /** For a block: selection reaches it. For glue: always false (unowned, hidden). */
390
782
  readonly isActive: boolean;
391
- readonly view: BlockViewData | GlueViewData;
392
- readonly kind: 'block' | 'glue';
783
+ readonly view: BlockViewData | GlueViewData | PendingParagraphViewData | DiffHunkViewData | DiffDecorationViewData;
784
+ readonly kind: 'block' | 'glue' | 'pendingParagraph' | 'diffHunk' | 'diffDecoration';
785
+ /**
786
+ * Diff mode: how this (modified) block changed. `added` = a whole new block
787
+ * (strong green band, no inline rects); `modified` = a partial change (light
788
+ * band + inline rects on the changed words).
789
+ */
790
+ readonly diffKind?: 'added' | 'modified';
393
791
  }
394
792
 
395
793
  /**
@@ -445,6 +843,8 @@ declare class DocumentViewData {
445
843
  */
446
844
  export declare class DocumentViewNode extends ViewNode {
447
845
  readonly blocks: readonly DocumentBlock[];
846
+ /** The transient empty-paragraph element, when one is armed. */
847
+ readonly pendingElement?: HTMLElement | undefined;
448
848
  static create(viewData: DocumentViewData, options: BlockViewOptions | undefined, previous: DocumentViewNode | undefined): DocumentViewNode;
449
849
  private constructor();
450
850
  /** The stable content element this document mounts its children into. */
@@ -466,6 +866,26 @@ export declare type EditCommand = (ctx: CursorCommandContext) => {
466
866
  readonly selection: Selection_2;
467
867
  } | undefined;
468
868
 
869
+ export declare type EditKeyboardAction = 'deleteLeft' | 'deleteRight' | 'deleteWordLeft' | 'deleteWordRight' | 'deleteLineLeft' | 'deleteLineRight';
870
+
871
+ declare interface EditorCommandDefinition {
872
+ readonly id: `markdown.editor.${string}`;
873
+ readonly title: string;
874
+ readonly action: EditorKeyboardAction;
875
+ readonly keybindings: readonly EditorCommandKeybinding[];
876
+ /**
877
+ * Local commands must execute synchronously in the webview instead of being
878
+ * forwarded through the VS Code keybinding service.
879
+ */
880
+ readonly routing?: 'host' | 'local';
881
+ }
882
+
883
+ declare interface EditorCommandKeybinding {
884
+ readonly key: string;
885
+ readonly modifiers?: KeyboardModifiers;
886
+ readonly platforms?: readonly KeyboardPlatform[];
887
+ }
888
+
469
889
  /**
470
890
  * Translates raw browser input (mouse, keyboard, EditContext) into model
471
891
  * mutations. Knows about DOM event types but never reads/writes the DOM
@@ -474,25 +894,71 @@ export declare type EditCommand = (ctx: CursorCommandContext) => {
474
894
  *
475
895
  * Owns the only non-derivable controller state:
476
896
  * - `_desiredColumn` — sticky column for up/down navigation
897
+ * - `_clickCount` / `_lastPointerDown` — multi-click detection for pointer
898
+ * input, since `pointerdown` events (unlike `mousedown`) don't populate
899
+ * `detail` with a click count.
477
900
  */
478
901
  export declare class EditorController extends Disposable {
479
902
  private readonly _model;
480
903
  private readonly _view;
904
+ readonly findController: FindController | undefined;
481
905
  private _desiredColumn;
906
+ private readonly _keyboardPlatform;
907
+ private readonly _keyboardProfile;
908
+ private readonly _forwardedKeyboardProfile;
909
+ private readonly _historyStrategy;
910
+ private readonly _indentation;
911
+ private readonly _tabFocusStatus;
912
+ private _tabMovesFocus;
913
+ /** Running click count for the current multi-click sequence (1, 2, 3, …). */
914
+ private _clickCount;
915
+ /** Timestamp and position of the previous pointer-down, for multi-click detection. */
916
+ private _lastPointerDown;
482
917
  constructor(_model: EditorModel, _view: EditorView, options?: EditorControllerOptions);
483
918
  private readonly _handleTextUpdate;
484
- private readonly _handleMouseDown;
919
+ private readonly _handlePointerDown;
485
920
  private _makeCursorContext;
486
921
  private _makeVisualCursorContext;
487
922
  private _executeCursorCommand;
488
923
  private _executeEditCommand;
924
+ private _runUndoableEdit;
489
925
  private _executeVisualCursorCommand;
926
+ private _setUserSelection;
490
927
  /** Move the cursor down one visual line (Arrow Down). */
491
928
  cursorDown(extend?: boolean): void;
492
929
  /** Move the cursor up one visual line (Arrow Up). */
493
930
  cursorUp(extend?: boolean): void;
494
931
  private _selectedText;
932
+ private readonly _updateModifierState;
933
+ private readonly _clearModifierState;
934
+ /**
935
+ * Drop any native DOM selection over the rendered text.
936
+ *
937
+ * The editor paints selection from `model.selection`, so a browser
938
+ * selection there is always spurious: nothing reads it (copy/cut read the
939
+ * model, hit-testing uses the measured layout) and nothing clears it, so it
940
+ * lingers as a second highlight even after the caret moves away.
941
+ * {@link isCaretMotionKey} stops the common source synchronously; this is
942
+ * the backstop for the rest of the browser's editing commands, which are
943
+ * platform- and version-specific and cannot be enumerated (Shift+PageDown
944
+ * and macOS Shift+Ctrl+B both reach one today).
945
+ *
946
+ * Scoped twice so it only ever discards selections the editor owns: the
947
+ * range must touch the rendered text (overlays such as comment widgets sit
948
+ * beside it and stay selectable), and input focus must still be inside this
949
+ * editor (so a host find-in-page, which selects while its own input is
950
+ * focused, is left alone).
951
+ */
952
+ private readonly _discardNativeSelection;
495
953
  private readonly _handleKeyDown;
954
+ executeCommand(command: EditorCommandDefinition): void;
955
+ private _executeKeyboardAction;
956
+ /**
957
+ * Context-aware Enter: splits / line-breaks via {@link insertSmartEnter}, or
958
+ * arms a transient empty paragraph when at the end of a paragraph.
959
+ */
960
+ private _smartEnter;
961
+ private _registerTabFocusAccessibility;
496
962
  }
497
963
 
498
964
  /** Options for an {@link EditorController}. */
@@ -503,10 +969,79 @@ export declare interface EditorControllerOptions {
503
969
  * strategy (e.g. `AsyncClipboardStrategy`) in hosts that swallow them.
504
970
  */
505
971
  readonly clipboardStrategy?: IClipboardStrategy;
972
+ /**
973
+ * Where undo and redo are executed: `LocalHistoryStrategy` for a
974
+ * self-contained editor, or a strategy that forwards to the host's own
975
+ * document history. Left unset, the chords are passed on to the host.
976
+ */
977
+ readonly historyStrategy?: IHistoryStrategy;
978
+ readonly keyboardPlatform?: KeyboardPlatform;
979
+ readonly keyboardProfile?: KeyboardProfile;
980
+ /**
981
+ * Bindings owned by the host. Matching events have their browser default
982
+ * suppressed but continue propagating so the host keybinding service sees them.
983
+ */
984
+ readonly forwardedKeyboardProfile?: KeyboardProfile;
985
+ /** Tab-stop settings used outside semantic list indentation. */
986
+ readonly indentation?: IndentationConfig;
987
+ readonly find?: false;
506
988
  }
507
989
 
990
+ /**
991
+ * The editor overlay's local CSS-pixel coordinate space.
992
+ *
993
+ * Browser geometry and pointer APIs expose viewport client coordinates. This
994
+ * boundary converts them immediately into the coordinate system shared by the
995
+ * editor content and its overlays. Range rectangles are axis-aligned, so the
996
+ * current implementation deliberately supports positive axis-aligned scale and
997
+ * translation only.
998
+ */
999
+ export declare class EditorCoordinateSpace {
1000
+ private readonly _getLocalToClientMatrix;
1001
+ static forSvgOverlay(overlay: SVGSVGElement): EditorCoordinateSpace;
1002
+ private constructor();
1003
+ capture(): EditorCoordinateTransform;
1004
+ }
1005
+
1006
+ /** A stable coordinate conversion captured for one measurement operation. */
1007
+ export declare class EditorCoordinateTransform {
1008
+ private readonly _localToClient;
1009
+ private readonly _clientToLocal;
1010
+ constructor(_localToClient: DOMMatrix);
1011
+ toLocalPoint(point: Pick<Point2D, 'x' | 'y'>): Point2D;
1012
+ toClientPoint(point: Pick<Point2D, 'x' | 'y'>): Point2D;
1013
+ toLocalRect(rect: Pick<DOMRectReadOnly, 'left' | 'top' | 'width' | 'height'>): Rect2D;
1014
+ toClientRect(rect: Pick<Rect2D, 'left' | 'top' | 'width' | 'height'>): Rect2D;
1015
+ private _convertRect;
1016
+ }
1017
+
1018
+ export declare type EditorKeyboardAction = {
1019
+ readonly kind: 'cursor';
1020
+ readonly command: CursorKeyboardAction;
1021
+ readonly extend: boolean;
1022
+ } | {
1023
+ readonly kind: 'edit';
1024
+ readonly command: EditKeyboardAction;
1025
+ } | {
1026
+ readonly kind: 'history';
1027
+ readonly command: HistoryKeyboardAction;
1028
+ } | {
1029
+ readonly kind: 'tab';
1030
+ readonly command: TabKeyboardAction;
1031
+ } | {
1032
+ readonly kind: 'toggleTabFocus';
1033
+ } | {
1034
+ readonly kind: 'selectAll';
1035
+ } | {
1036
+ readonly kind: 'enter';
1037
+ readonly command: 'smartEnter' | 'insertParagraph' | 'insertHardLineBreak';
1038
+ };
1039
+
508
1040
  export declare class EditorModel {
509
1041
  private readonly _parser;
1042
+ private readonly _sourceEditListeners;
1043
+ private readonly _sourceTextIds;
1044
+ private _lastSourceTextId;
510
1045
  /**
511
1046
  * The most recent edit applied to {@link sourceText}, used by
512
1047
  * {@link document} to let the parser link incrementally edited code
@@ -515,11 +1050,45 @@ export declare class EditorModel {
515
1050
  */
516
1051
  private _pendingEdit;
517
1052
  readonly sourceText: ISettableObservable<StringValue, void>;
1053
+ readonly wordNavigationConfig: ISettableObservable<WordNavigationConfig, void>;
1054
+ /**
1055
+ * Read-only mode. When `true`, the editor never reveals a block's source
1056
+ * markers (markdown special characters like `**`, `#`, list bullets, code
1057
+ * fences, `$…$`) — every block stays in its clean rendered form regardless
1058
+ * of where the caret/selection is — and source-mutating edits are ignored.
1059
+ * Plain text selection still works everywhere (so the user can copy). The
1060
+ * default (`false`) is the normal editing mode where the active block
1061
+ * reveals its markers.
1062
+ */
1063
+ readonly readonlyMode: ISettableObservable<boolean, void>;
518
1064
  /**
519
1065
  * The current selection, or `undefined` when the editor has no caret
520
1066
  * (e.g. an inactive/unfocused rendering).
521
1067
  */
522
1068
  readonly selection: ISettableObservable<Selection_2 | undefined, void>;
1069
+ readonly selectionSource: ISettableObservable<SelectionSource, void>;
1070
+ /**
1071
+ * Whether a Ctrl/Cmd modifier is currently held. Set by the controller from
1072
+ * live keyboard state; the view reads it to show the link-open affordance
1073
+ * (underline + pointer cursor) only while a Ctrl/Cmd+click would open a link
1074
+ * whose block is active.
1075
+ */
1076
+ readonly ctrlOrMetaDown: ISettableObservable<boolean, void>;
1077
+ /**
1078
+ * Whether a pointer-driven selection drag is currently in progress. Set by
1079
+ * the controller between the pointer-down that starts the drag and the
1080
+ * pointer-up/cancel that ends it. Contributions read it to defer UI that
1081
+ * would otherwise flicker mid-drag (e.g. the comment input box appears only
1082
+ * once the drag ends).
1083
+ */
1084
+ readonly isSelecting: ISettableObservable<boolean, void>;
1085
+ /**
1086
+ * Gutter markers (source-control style change indicators) painted in the
1087
+ * left gutter. Each entry maps a source {@link OffsetRange} to a change kind
1088
+ * — see {@link GutterMarker}. Purely decorative: markers never affect the
1089
+ * parsed {@link document}, selection, or layout. Empty by default.
1090
+ */
1091
+ readonly gutterMarkers: ISettableObservable<readonly GutterMarker[], void>;
523
1092
  /**
524
1093
  * Forces the rendered active-block set. `undefined` (the default)
525
1094
  * derives the set from the current selection range (see
@@ -528,6 +1097,13 @@ export declare class EditorModel {
528
1097
  * collapsed/inactive rendering.
529
1098
  */
530
1099
  readonly activeBlocksOverride: ISettableObservable<readonly BlockAstNode[] | typeof NO_ACTIVE_BLOCKS | undefined, void>;
1100
+ /**
1101
+ * The transient empty-paragraph editing state, or `undefined` when none is
1102
+ * armed. See {@link PendingParagraph}. This is *not* document data — it is
1103
+ * cleared by any source edit and lives only between the Enter that armed it
1104
+ * and the next keystroke.
1105
+ */
1106
+ readonly pendingParagraph: ISettableObservable<PendingParagraph | undefined, void>;
531
1107
  readonly cursorOffset: IObservableWithChange<number | undefined, void>;
532
1108
  /**
533
1109
  * The parsed document. Threads the previous document into the parser so
@@ -551,10 +1127,62 @@ export declare class EditorModel {
551
1127
  * collapsed this is a one-element set holding {@link activeBlock}.
552
1128
  */
553
1129
  readonly activeBlocks: IObservableWithChange<Set<BlockAstNode>, void>;
554
- applyEdit(edit: StringEdit): void;
1130
+ /**
1131
+ * The baseline document to diff against. When set, the editor renders in
1132
+ * diff mode: the modified document ({@link document}) stays editable, while
1133
+ * the baseline's removed/changed blocks are shown as read-only decorations.
1134
+ * `undefined` (the default) renders normally.
1135
+ */
1136
+ readonly baseline: ISettableObservable<StringValue | undefined, void>;
1137
+ private readonly _baselineDocument;
1138
+ /**
1139
+ * The diff of {@link baseline} → {@link document}, or `undefined` when no
1140
+ * baseline is set. The view renders the {@link DiffItem}s as stacked
1141
+ * decorations; `insertedRanges` (modified-side change spans) drive the green
1142
+ * word-level highlight.
1143
+ */
1144
+ readonly diff: IObservableWithChange< {
1145
+ items: DiffItem[];
1146
+ insertedRanges: OffsetRange[];
1147
+ changedBlocks: Set<BlockAstNode>;
1148
+ } | undefined, void>;
1149
+ readonly markerVisibleBlocks: IObservableWithChange<Set<BlockAstNode>, void>;
1150
+ onWillApplySourceEdit(listener: (event: SourceEditEvent) => void): IDisposable;
1151
+ /** Returns a stable per-object identity without retaining the source text. */
1152
+ getSourceTextId(sourceText: StringValue): number;
1153
+ /**
1154
+ * Arm a {@link PendingParagraph} at the given gap, minting a fresh synthetic
1155
+ * AST node for it, and park the caret at the gap start. No source edit is
1156
+ * applied — the blank line exists only in the view until it is materialized.
1157
+ */
1158
+ armPendingParagraph(req: {
1159
+ anchorBlock: BlockAstNode;
1160
+ replaceRange: OffsetRange;
1161
+ atEof: boolean;
1162
+ }): void;
1163
+ /** Discard the pending paragraph (if any) without touching the source. */
1164
+ cancelPendingParagraph(): void;
1165
+ /**
1166
+ * Replace the source with an authoritative value from the host, mapping the
1167
+ * selection through the changed span and atomically discarding transient
1168
+ * state anchored to the previous parse.
1169
+ */
1170
+ replaceSourceText(text: StringValue): void;
1171
+ /**
1172
+ * Turn the pending paragraph into real source: rewrite its gap so the typed
1173
+ * `text` becomes its own paragraph, separated from its neighbours by blank
1174
+ * lines, and place the caret after the inserted text.
1175
+ */
1176
+ materializePendingParagraph(text: string): void;
1177
+ applyEdit(edit: StringEdit, selection?: Selection_2): void;
555
1178
  applyEditForSelection(edit: StringEdit): void;
1179
+ private _applySourceEdit;
1180
+ private _identifySourceEdit;
1181
+ private _emitWillApplySourceEdit;
556
1182
  }
557
1183
 
1184
+ export declare type EditorOverlayPosition = 'top-chrome' | 'below-selection' | 'above-decorations';
1185
+
558
1186
  /**
559
1187
  * Pure-render view of an {@link EditorModel}.
560
1188
  *
@@ -584,6 +1212,8 @@ export declare class EditorView extends Disposable {
584
1212
  readonly element: HTMLElement;
585
1213
  readonly editContext: EditContext;
586
1214
  readonly measuredLayout: MeasuredLayoutModel;
1215
+ readonly coordinateSpace: EditorCoordinateSpace;
1216
+ readonly forcedMarkerVisibleBlocks: ISettableObservable<ReadonlySet<BlockAstNode>, void>;
587
1217
  /**
588
1218
  * Inner container that holds the rendered document and the cursor/selection
589
1219
  * overlays. The outer {@link element} spans the full width; this container
@@ -593,6 +1223,11 @@ export declare class EditorView extends Disposable {
593
1223
  private readonly _contentContainer;
594
1224
  private readonly _cursorView;
595
1225
  private readonly _selectionView;
1226
+ private readonly _gutterMarkersView;
1227
+ private readonly _diffHighlightsView;
1228
+ private _readonlyToggleButton;
1229
+ private readonly _editContextSuspensions;
1230
+ private readonly _revealOcclusions;
596
1231
  /**
597
1232
  * The mounted block sequence, in source order. Rebuilt (not mutated) each
598
1233
  * frame by {@link DocumentViewNode.create}; the view just swaps one
@@ -612,20 +1247,146 @@ export declare class EditorView extends Disposable {
612
1247
  /** The current view-data tree (AST overlaid with selection flags), for debugging. */
613
1248
  private readonly _viewData;
614
1249
  get viewData(): IObservable<DocumentViewData | undefined>;
1250
+ /**
1251
+ * Whether the editor is genuinely focused: focus rests somewhere inside the
1252
+ * editor subtree *and* its window is focused. Mirrored onto the root as
1253
+ * `.md-focused`, which gates the painted caret — the blinking cursor is only
1254
+ * shown while this is `true`, so it never blinks in an unfocused editor or
1255
+ * after the window loses focus. Only the caret's visibility is affected; the
1256
+ * logical selection and caret geometry ({@link caretRect}) are unchanged.
1257
+ */
1258
+ private readonly _focused;
1259
+ get focused(): IObservable<boolean>;
1260
+ /**
1261
+ * Caret rect (editor-local coordinates) for the transient empty paragraph, or
1262
+ * `undefined` when none is armed. Set each frame from the synthetic
1263
+ * paragraph element's geometry and fed to the {@link CursorView}, which has
1264
+ * no visual-line-map entry to place the caret from otherwise.
1265
+ */
1266
+ private readonly _pendingCaretRect;
615
1267
  /**
616
1268
  * The block cache projected for views (selection painting) that need to
617
1269
  * react to mount/unmount. Derived from {@link _document}, so it stays in
618
1270
  * lock-step without any manual bookkeeping.
619
1271
  */
620
1272
  private readonly _selectionBlocksObs;
1273
+ /**
1274
+ * The caret rect (zero width) at the selection's active end, in
1275
+ * {@link overlayContainer}-local coordinates, or `undefined` when there is no
1276
+ * caret. This is the same geometry the editor paints its cursor from, so
1277
+ * contributions (e.g. comment mode) can anchor an overlay to the active end of
1278
+ * the selection — where the user's cursor is — without re-deriving geometry.
1279
+ */
1280
+ private readonly _caretRect;
1281
+ get caretRect(): IObservable<Rect2D | undefined>;
1282
+ /**
1283
+ * The container that establishes the positioning context for the editor's
1284
+ * overlays (cursor, selection, gutter). Contributions mount their own
1285
+ * absolutely-positioned overlays here so they share the coordinate space of
1286
+ * {@link caretRect}.
1287
+ */
1288
+ get overlayContainer(): HTMLElement;
1289
+ /**
1290
+ * Selection-style rectangles covering `range`, in {@link overlayContainer}-
1291
+ * local coordinates — the same geometry the live selection paints. Exposed so
1292
+ * contributions (e.g. persistent comments) can highlight arbitrary ranges and
1293
+ * anchor overlays to them. Recomputes when the measured layout changes.
1294
+ */
1295
+ rangeRects(range: OffsetRange): IObservable<readonly SelectionRect[]>;
621
1296
  constructor(_model: EditorModel, _options?: EditorViewOptions | undefined);
1297
+ /**
1298
+ * Mirrors the model's live Ctrl/Cmd state onto the editor root as
1299
+ * `.md-mod-down` so CSS can show the link-open underline and pointer cursor
1300
+ * only while a click would actually open the link: an inactive link opens on
1301
+ * a plain click, but an active link only opens with the modifier held.
1302
+ */
1303
+ private _setupModifierTracking;
1304
+ /**
1305
+ * Tracks whether the editor is genuinely focused and mirrors it onto the
1306
+ * root as `.md-focused` so CSS can gate the painted caret. "Focused" means
1307
+ * focus rests somewhere inside the editor subtree *and* the window itself is
1308
+ * focused; either condition failing (focus moving elsewhere, or the window
1309
+ * losing focus) hides the blinking caret while leaving the logical selection
1310
+ * and caret geometry intact.
1311
+ */
1312
+ private _setupFocusTracking;
1313
+ /**
1314
+ * Renders the edit/read-only mode toggle. It flips the model's
1315
+ * {@link EditorModel.readonlyMode}: when locked (read-only) every block stays
1316
+ * in its clean rendered form (no markdown markers revealed) and edits are
1317
+ * ignored, while text selection still works. The control lives in a
1318
+ * zero-height *sticky* host inside the centered content container, so the
1319
+ * lock follows the content's right edge and remains pinned as the document
1320
+ * scrolls. The current mode is also mirrored onto the root as `.md-readonly`
1321
+ * for any CSS hooks.
1322
+ */
1323
+ private _setupReadonlyToggle;
1324
+ /** Draws attention to the mode toggle after text input is attempted while locked. */
1325
+ showReadonlyEditingAttempt(): void;
622
1326
  focus(): void;
1327
+ mountOverlay(element: HTMLElement | SVGSVGElement, position: EditorOverlayPosition): IDisposable;
1328
+ /** Registers floating editor chrome that should count as covering a range during reveal. */
1329
+ registerRevealOcclusion(element: Element): IDisposable;
1330
+ /**
1331
+ * Temporarily detaches the root {@link EditContext} while focus is inside
1332
+ * nested editor chrome. Chromium otherwise reclaims focus from non-text
1333
+ * controls inside the EditContext host, breaking keyboard access to controls
1334
+ * such as the find actions and read-only toggle.
1335
+ */
1336
+ suspendEditContextWhileFocused(element: HTMLElement): IDisposable;
1337
+ revealRangeInCenterIfOutsideViewport(range: OffsetRange, behavior?: ScrollBehavior): IDisposable;
1338
+ /**
1339
+ * Samples the ambient focus state that decides whether taking focus on open
1340
+ * would steal it from an explicit user target: whether the window is focused
1341
+ * and whether focus is still unclaimed (no active element, or the `<body>`
1342
+ * fallback).
1343
+ */
1344
+ private _sampleAutoFocusEnvironment;
1345
+ /**
1346
+ * One-shot guarded focus attempt: focuses the editor only if doing so will
1347
+ * not steal focus from an explicit user target — the window must already be
1348
+ * focused and no other element may have claimed focus yet. Returns whether
1349
+ * focus was taken. A no-op for a background window or when the user has
1350
+ * already focused something else. {@link autoFocusOnOpen} builds the
1351
+ * open-time behavior on top of this primitive.
1352
+ */
1353
+ tryAutoFocus(): boolean;
1354
+ /**
1355
+ * Focuses the editor when it opens without ever stealing focus from an
1356
+ * explicit user target. Tries once immediately; if the window is not focused
1357
+ * yet — a common open-time race where the editor is mounted before the host
1358
+ * routes focus to its window — the guarded attempt is deferred to the next
1359
+ * time the window gains focus and re-evaluated then. The deferral is
1360
+ * one-shot, so a later, unrelated window refocus never grabs focus, and the
1361
+ * re-check still respects any target the user has claimed in the meantime.
1362
+ */
1363
+ autoFocusOnOpen(): void;
1364
+ /**
1365
+ * Own point→offset resolution. When `true` (the default),
1366
+ * {@link resolveOffsetFromPoint} ignores the platform DOM hit-test
1367
+ * (`caretPositionFromPoint`) and snaps the point to the nearest offset purely
1368
+ * from the rendered {@link VisualLineMap} geometry — picking the nearest
1369
+ * visual line by `y`, then the nearest offset on it by `x`. Because a table
1370
+ * row's cells share one horizontal line band, this makes the whole width of a
1371
+ * row resolve into that row (rather than only the cell boxes), with no visible
1372
+ * layout change. It also lets a drag keep extending toward off-viewport points
1373
+ * (e.g. the pointer leaving the window), which the platform hit-test cannot
1374
+ * resolve. Set to `false` to fall back to the platform DOM hit-test.
1375
+ */
1376
+ readonly geometricHitTest: ISettableObservable<boolean, void>;
623
1377
  /**
624
1378
  * Client coordinates → absolute source offset (any block). Used during
625
1379
  * drag to keep extending the selection even when the pointer leaves the
626
- * original block.
1380
+ * original block. Honours {@link geometricHitTest}.
627
1381
  */
628
1382
  resolveOffsetFromPoint(point: Point2D): SourceOffset | undefined;
1383
+ /**
1384
+ * Resolve table-cell hits that have no measurable text run. Empty cells map
1385
+ * from their own box instead of snapping to a neighboring cell; element-only
1386
+ * content (for example an inactive image) maps through the hit element's view
1387
+ * node. Text-bearing cells keep the normal pixel-precise line-map/DOM path.
1388
+ */
1389
+ private _resolveTableCellOffset;
629
1390
  /**
630
1391
  * Whether a client point falls on the rendered document content, as
631
1392
  * opposed to the surrounding editor padding (the green area). Uses DOM
@@ -635,6 +1396,13 @@ export declare class EditorView extends Disposable {
635
1396
  * have `pointer-events: none`, so the hit-test sees through them.
636
1397
  */
637
1398
  isPointInContent(point: Point2D): boolean;
1399
+ /**
1400
+ * Whether `node` sits inside the rendered source text — the region whose
1401
+ * selection this editor paints itself from `model.selection`. Overlays
1402
+ * anchored beside the text (comment widgets and the like) are *not* part of
1403
+ * it and keep their own native selection behaviour.
1404
+ */
1405
+ containsRenderedContent(node: Node | null | undefined): boolean;
638
1406
  private readonly _renderAutorun;
639
1407
  /** Current mounted blocks, or empty before the first render. */
640
1408
  private get _blocks();
@@ -644,6 +1412,18 @@ export declare class EditorView extends Disposable {
644
1412
  * is not read here, so there is no feedback loop into the render autorun.
645
1413
  */
646
1414
  private _publishMeasurements;
1415
+ /**
1416
+ * Paint the diff highlights via the CSS Custom Highlight API: green over the
1417
+ * inserted/changed modified ranges (mapped on the document's own DOM), and
1418
+ * red over each {@link DiffDecorationViewNode}'s deleted ranges (mapped on
1419
+ * the decoration's own subtree). No DOM is mutated, so reconciliation and
1420
+ * editing are unaffected.
1421
+ */
1422
+ private _paintDiff;
1423
+ private _clearDiff;
1424
+ private _syncEditContextAttachment;
1425
+ private _revealRange;
1426
+ private _isRevealOccluded;
647
1427
  }
648
1428
 
649
1429
  export declare interface EditorViewOptions extends BlockViewOptions {
@@ -654,6 +1434,12 @@ export declare interface EditorViewOptions extends BlockViewOptions {
654
1434
  * only) unless a theme class is supplied.
655
1435
  */
656
1436
  readonly classNames?: readonly string[];
1437
+ /**
1438
+ * Whether to render the sticky edit/read-only toggle at the top-right edge
1439
+ * of the content. Defaults to `true`; set to `false` to omit it (e.g. in
1440
+ * fixtures that focus on selection rendering).
1441
+ */
1442
+ readonly showReadonlyToggle?: boolean;
657
1443
  /**
658
1444
  * Controls "limited width mode". The observable yields the maximum content
659
1445
  * width in pixels, or `undefined` to let the content fill the available
@@ -664,6 +1450,14 @@ export declare interface EditorViewOptions extends BlockViewOptions {
664
1450
  * root ({@link element}) always spans the full available width.
665
1451
  */
666
1452
  readonly limitedWidth?: IObservable<number | undefined>;
1453
+ /**
1454
+ * Diff mode only: render every read-only original decoration in active
1455
+ * (source) form, so even whole-block removals expose their markdown markers
1456
+ * as real text. Used by the diff-coverage fixture to verify that every
1457
+ * changed original character is rendered somewhere; off in normal use, where
1458
+ * whole removals show a clean solid band.
1459
+ */
1460
+ readonly diffDecorationsActive?: boolean;
667
1461
  }
668
1462
 
669
1463
  export declare class EmphasisAstNode extends AstNode {
@@ -683,6 +1477,111 @@ declare class EmphasisViewData {
683
1477
  constructor(ast: EmphasisAstNode, content: readonly AnyViewData[]);
684
1478
  }
685
1479
 
1480
+ export declare function escapeFindRegex(value: string): string;
1481
+
1482
+ export declare const FIND_MATCH_LIMIT = 19999;
1483
+
1484
+ export declare function findBlockAtOffset(doc: DocumentAstNode, offset: SourceOffset): BlockAstNode | undefined;
1485
+
1486
+ export declare class FindController extends Disposable {
1487
+ private readonly _editorModel;
1488
+ private readonly _view;
1489
+ private readonly _options;
1490
+ readonly model: FindModel;
1491
+ readonly widget: FindWidget;
1492
+ private readonly _selectionForScope;
1493
+ private _forcedMatch;
1494
+ private _selectedMatch;
1495
+ private _observedSourceTextId;
1496
+ private readonly _sourceEditTracker;
1497
+ private _revealRequest;
1498
+ constructor(_editorModel: EditorModel, _view: EditorView, _options: FindControllerOptions);
1499
+ private _setForcedMarkerVisibleBlocks;
1500
+ openAndFocus(): void;
1501
+ close(): void;
1502
+ private _handleKeyDown;
1503
+ private _cancelRevealRequest;
1504
+ private _findByKeyboard;
1505
+ private _querySeed;
1506
+ private _prepareSeed;
1507
+ private _selectedSingleLineText;
1508
+ private _toggleFindInSelection;
1509
+ }
1510
+
1511
+ export declare interface FindControllerOptions {
1512
+ readonly keyboardPlatform: KeyboardPlatform;
1513
+ }
1514
+
1515
+ export declare type FindDirection = 'next' | 'previous';
1516
+
1517
+ export declare class FindHighlightsView extends Disposable {
1518
+ private readonly _view;
1519
+ private readonly _matchesLayer;
1520
+ private readonly _currentLayer;
1521
+ private readonly _highlightRegistration;
1522
+ private readonly _resizeObserver;
1523
+ private readonly _resizeObservedElements;
1524
+ private _snapshot;
1525
+ private _paintRaf;
1526
+ constructor(_view: EditorView, findModel: FindModel);
1527
+ private _schedulePaint;
1528
+ private _paint;
1529
+ private _observeResizeAncestors;
1530
+ private _paintRanges;
1531
+ }
1532
+
1533
+ export declare interface FindMatchesResult {
1534
+ readonly matches: readonly OffsetRange[];
1535
+ readonly isCapped: boolean;
1536
+ }
1537
+
1538
+ export declare class FindModel extends Disposable {
1539
+ private readonly _editorModel;
1540
+ readonly isRevealed: ISettableObservable<boolean, void>;
1541
+ readonly searchString: ISettableObservable<string, void>;
1542
+ readonly isRegex: ISettableObservable<boolean, void>;
1543
+ readonly matchCase: ISettableObservable<boolean, void>;
1544
+ readonly wholeWord: ISettableObservable<boolean, void>;
1545
+ readonly searchScope: ISettableObservable<OffsetRange | undefined, void>;
1546
+ readonly currentMatch: ISettableObservable<OffsetRange | undefined, void>;
1547
+ readonly loop: ISettableObservable<boolean, void>;
1548
+ readonly searchResult: IObservableWithChange< {
1549
+ kind: "invalid";
1550
+ error: Error;
1551
+ pattern?: undefined;
1552
+ matches?: undefined;
1553
+ isCapped?: undefined;
1554
+ } | {
1555
+ kind: "valid";
1556
+ pattern: FindPattern;
1557
+ matches: readonly OffsetRange[];
1558
+ isCapped: boolean;
1559
+ error?: undefined;
1560
+ }, void>;
1561
+ readonly matchesCount: IObservableWithChange<number, void>;
1562
+ readonly isCapped: IObservableWithChange<boolean, void>;
1563
+ readonly currentMatchPosition: IObservableWithChange<number, void>;
1564
+ private _searchOrigin;
1565
+ private readonly _sourceEditTracker;
1566
+ private _pendingInitialDirection;
1567
+ constructor(_editorModel: EditorModel);
1568
+ reveal(options: {
1569
+ readonly origin: number;
1570
+ readonly searchString?: string;
1571
+ readonly direction?: FindDirection;
1572
+ }): void;
1573
+ hide(): void;
1574
+ setSearchOrigin(offset: number): void;
1575
+ setSearchScope(scope: OffsetRange | undefined): void;
1576
+ moveToNextMatch(): OffsetRange | undefined;
1577
+ moveToPreviousMatch(): OffsetRange | undefined;
1578
+ private _move;
1579
+ private _selectFromOrigin;
1580
+ private _mapStateThroughEdit;
1581
+ private _inputSnapshot;
1582
+ private _readInputSnapshot;
1583
+ }
1584
+
686
1585
  /**
687
1586
  * Source offset (relative to `root`) of the node with `target`'s id, or
688
1587
  * `undefined` when it is not in the tree. Ids are stable across edits, so this
@@ -690,14 +1589,117 @@ declare class EmphasisViewData {
690
1589
  */
691
1590
  export declare function findNodeOffsetById(root: AstNode, target: AstNode): number | undefined;
692
1591
 
693
- export declare function findWordAt(text: string, offset: number): {
1592
+ export declare class FindPattern {
1593
+ private readonly _source;
1594
+ private readonly _flags;
1595
+ private readonly _wholeWord;
1596
+ private readonly _wordSeparators;
1597
+ readonly isEmpty: boolean;
1598
+ private constructor();
1599
+ static create(query: FindQuery): FindQueryResult;
1600
+ findMatches(text: string, scope?: OffsetRange, limit?: number): FindMatchesResult;
1601
+ findNextMatch(text: string, after: number, scope?: OffsetRange, loop?: boolean, skip?: OffsetRange): OffsetRange | undefined;
1602
+ findPreviousMatch(text: string, before: number, scope?: OffsetRange, loop?: boolean, skip?: OffsetRange): OffsetRange | undefined;
1603
+ private _forEachMatch;
1604
+ }
1605
+
1606
+ export declare interface FindQuery {
1607
+ readonly searchString: string;
1608
+ readonly isRegex: boolean;
1609
+ readonly matchCase: boolean;
1610
+ readonly wholeWord: boolean;
1611
+ readonly wordSeparators: string;
1612
+ }
1613
+
1614
+ export declare type FindQueryResult = {
1615
+ readonly kind: 'valid';
1616
+ readonly pattern: FindPattern;
1617
+ } | {
1618
+ readonly kind: 'invalid';
1619
+ readonly error: Error;
1620
+ };
1621
+
1622
+ export declare type FindSearchResult = {
1623
+ readonly kind: 'valid';
1624
+ readonly pattern: FindPattern;
1625
+ readonly matches: readonly OffsetRange[];
1626
+ readonly isCapped: boolean;
1627
+ } | {
1628
+ readonly kind: 'invalid';
1629
+ readonly error: Error;
1630
+ };
1631
+
1632
+ export declare class FindWidget extends Disposable {
1633
+ private readonly _view;
1634
+ private readonly _options;
1635
+ readonly element: HTMLElement;
1636
+ readonly panelElement: HTMLElement;
1637
+ readonly focused: ISettableObservable<boolean, void>;
1638
+ private readonly _inputShell;
1639
+ private readonly _input;
1640
+ private readonly _matchesCount;
1641
+ private readonly _previousButton;
1642
+ private readonly _nextButton;
1643
+ private readonly _selectionButton;
1644
+ private readonly _caseButton;
1645
+ private readonly _wholeWordButton;
1646
+ private readonly _regexButton;
1647
+ private readonly _error;
1648
+ constructor(_view: EditorView, _options: FindWidgetOptions);
1649
+ focusAndSelect(): void;
1650
+ private _registerButton;
1651
+ private _render;
1652
+ }
1653
+
1654
+ export declare interface FindWidgetOptions {
1655
+ readonly findModel: FindModel;
1656
+ readonly canFindInSelection: IObservable<boolean>;
1657
+ readonly onNext: () => void;
1658
+ readonly onPrevious: () => void;
1659
+ readonly onToggleFindInSelection: () => void;
1660
+ readonly onClose: () => void;
1661
+ }
1662
+
1663
+ export declare function findWordAt(text: string, offset: number, config?: WordNavigationConfig): {
694
1664
  start: number;
695
1665
  end: number;
696
1666
  };
697
1667
 
698
- export declare function findWordBoundaryLeft(text: string, offset: number): number;
1668
+ export declare function findWordBoundaryLeft(text: string, offset: number, config?: WordNavigationConfig): number;
1669
+
1670
+ export declare function findWordBoundaryRight(text: string, offset: number, config?: WordNavigationConfig): number;
1671
+
1672
+ export declare function findWordDeleteBoundaryLeft(text: string, offset: number, config?: WordNavigationConfig): number;
1673
+
1674
+ export declare function findWordDeleteBoundaryRight(text: string, offset: number, config?: WordNavigationConfig): number;
1675
+
1676
+ /**
1677
+ * A leading YAML front matter block. The YAML value is intentionally opaque:
1678
+ * only the two fences and the exact source between them are modeled.
1679
+ */
1680
+ export declare class FrontMatterAstNode extends BlockAstNodeBase {
1681
+ readonly content: readonly (MarkerAstNode | GlueAstNode)[];
1682
+ readonly leadingTrivia?: GlueAstNode | undefined;
1683
+ readonly kind = "frontMatter";
1684
+ constructor(content: readonly (MarkerAstNode | GlueAstNode)[], leadingTrivia?: GlueAstNode | undefined);
1685
+ get children(): readonly AstNode[];
1686
+ get openFence(): MarkerAstNode | undefined;
1687
+ get closeFence(): MarkerAstNode | undefined;
1688
+ get value(): MarkerAstNode | undefined;
1689
+ mapChildren(m: ReadonlyMap<AstNode, AstNode>): AstNode;
1690
+ withLeadingTrivia(trivia: GlueAstNode | undefined): FrontMatterAstNode;
1691
+ }
699
1692
 
700
- export declare function findWordBoundaryRight(text: string, offset: number): number;
1693
+ declare class FrontMatterViewData {
1694
+ readonly ast: FrontMatterAstNode;
1695
+ /** Active: render both fences; inactive: render only the opaque YAML value. */
1696
+ readonly showMarkup: boolean;
1697
+ readonly content: readonly AnyViewData[];
1698
+ readonly kind = "frontMatter";
1699
+ constructor(ast: FrontMatterAstNode,
1700
+ /** Active: render both fences; inactive: render only the opaque YAML value. */
1701
+ showMarkup: boolean, content: readonly AnyViewData[]);
1702
+ }
701
1703
 
702
1704
  export declare function getAnnotatedSource(node: AstNode, source: string, offset?: number): string;
703
1705
 
@@ -731,6 +1733,29 @@ declare class GlueViewData {
731
1733
  decorateNewline: boolean);
732
1734
  }
733
1735
 
1736
+ /**
1737
+ * A single gutter marker: a source {@link OffsetRange} tagged with a
1738
+ * {@link GutterMarkerType}. The view resolves the range to the visual lines it
1739
+ * covers and paints a bar (or, for `deleted`, a wedge at the range position) in
1740
+ * the left gutter.
1741
+ *
1742
+ * A `deleted` marker is normally an empty range (`range.isEmpty`) sitting at the
1743
+ * boundary where the removed text used to be — there is nothing left to span,
1744
+ * so it is drawn as a caret between lines rather than a bar.
1745
+ */
1746
+ export declare interface GutterMarker {
1747
+ readonly range: OffsetRange;
1748
+ readonly type: GutterMarkerType;
1749
+ }
1750
+
1751
+ /**
1752
+ * The kind of change a gutter marker represents, mirroring the three states a
1753
+ * source-control diff distinguishes (the git change markers in the editor
1754
+ * gutter): a freshly inserted region, an edited region, and a point where
1755
+ * content was removed.
1756
+ */
1757
+ export declare type GutterMarkerType = 'added' | 'modified' | 'deleted';
1758
+
734
1759
  export declare class HeadingAstNode extends BlockAstNodeBase {
735
1760
  readonly level: 1 | 2 | 3 | 4 | 5 | 6;
736
1761
  readonly marker: MarkerAstNode;
@@ -751,6 +1776,10 @@ declare class HeadingViewData {
751
1776
  constructor(ast: HeadingAstNode, content: readonly AnyViewData[]);
752
1777
  }
753
1778
 
1779
+ export declare function hiddenCursorRanges(doc: DocumentAstNode, markerVisibleBlocks: ReadonlySet<BlockAstNode>, cursor: number): readonly OffsetRange[];
1780
+
1781
+ declare type HistoryKeyboardAction = 'undo' | 'redo';
1782
+
754
1783
  /**
755
1784
  * The editor operations a clipboard strategy drives. The strategy never
756
1785
  * touches the model or the DOM directly — it asks through this seam, so the
@@ -787,6 +1816,9 @@ export declare interface IClipboardStrategy {
787
1816
  connect(context: IClipboardContext): IDisposable;
788
1817
  }
789
1818
 
1819
+ /** A live rendering of a {@link CommentsModel}. Dispose to unmount it. */
1820
+ export declare type ICommentsPresenter = IDisposable;
1821
+
790
1822
  /** The Monarch language definitions the default highlighter wires up. */
791
1823
  export declare interface IDefaultMonarchGrammars {
792
1824
  typescript: unknown;
@@ -796,6 +1828,72 @@ export declare interface IDefaultMonarchGrammars {
796
1828
  python: unknown;
797
1829
  rust: unknown;
798
1830
  shell: unknown;
1831
+ yaml: unknown;
1832
+ }
1833
+
1834
+ /**
1835
+ * A live editor embedded in place of a fenced code block's *rendered* form.
1836
+ *
1837
+ * This is the internal seam between the block view and a concrete embedded
1838
+ * editor (e.g. an `<iframe>` speaking the web-editor protocol). The block view
1839
+ * only speaks string edits: it pushes the block's content down via
1840
+ * {@link setContent} and receives the editor's own changes back through
1841
+ * {@link onEdit} (set by the block view on each (re)construction, so it always
1842
+ * routes to the current AST node). The concrete implementation owns its DOM,
1843
+ * transport, and lifecycle.
1844
+ *
1845
+ * A single instance is adopted across re-renders (like the highlighter session)
1846
+ * so the underlying editor keeps its state across edits — see
1847
+ * {@link CodeBlockViewNode}.
1848
+ */
1849
+ declare interface IEmbeddedCodeEditor {
1850
+ /** The element mounted as the block's rendered form. */
1851
+ readonly element: HTMLElement;
1852
+ /**
1853
+ * Document → editor. The block's content changed (from any source). Must be
1854
+ * idempotent: pushing the content the editor already holds is a no-op, which
1855
+ * is how edits the editor itself originated are prevented from echoing back.
1856
+ */
1857
+ setContent(content: string): void;
1858
+ /**
1859
+ * Optional synchronous height (px) to reserve for `content` *before* the
1860
+ * editor has laid out. Return `undefined` to let the editor size itself
1861
+ * (the implementation may report its real height later). Lets a registration
1862
+ * avoid a layout jump when it can cheaply estimate the size from content.
1863
+ */
1864
+ estimateHeight?(content: string): number | undefined;
1865
+ /**
1866
+ * Editor → document. Set by the block view on every (re)construction to
1867
+ * route the editor's own edits, expressed in the block's *content*
1868
+ * coordinates, to the current AST node.
1869
+ */
1870
+ onEdit?: (edit: StringEdit) => void;
1871
+ dispose(): void;
1872
+ }
1873
+
1874
+ /** Creates an {@link IEmbeddedCodeEditor} for a fenced block, or opts out. */
1875
+ declare interface IEmbeddedCodeEditorFactory {
1876
+ /**
1877
+ * Return an editor for a fenced block of `language`, or `undefined` to fall
1878
+ * back to the default (highlighting / {@link BlockViewOptions.renderCustomCodeBlock}).
1879
+ */
1880
+ create(language: string, initialContent: string): IEmbeddedCodeEditor | undefined;
1881
+ }
1882
+
1883
+ /**
1884
+ * Routes undo and redo to whatever owns the document's history: the editor
1885
+ * itself on a standalone page, or the enclosing document in a host like
1886
+ * VS Code.
1887
+ */
1888
+ export declare interface IHistoryStrategy {
1889
+ undo(): void;
1890
+ redo(): void;
1891
+ /**
1892
+ * Invoked around each source mutation so the strategy can record it.
1893
+ * Implemented only by strategies that build their own history; a host that
1894
+ * forwards edits to a VS Code `TextDocument` lets it record them instead.
1895
+ */
1896
+ record?(operation: () => void, edit?: StringEdit): void;
799
1897
  }
800
1898
 
801
1899
  export declare class ImageAstNode extends AstNode {
@@ -832,6 +1930,14 @@ export declare interface IMonarchApi {
832
1930
  MonarchTokenizer: new (languageService: unknown, standaloneThemeService: unknown, languageId: string, lexer: unknown, configurationService: unknown) => MonarchTokenizer;
833
1931
  }
834
1932
 
1933
+ /** Controls tab-stop insertion and non-list line indentation. */
1934
+ export declare interface IndentationConfig {
1935
+ /** Number of visual columns between tab stops. */
1936
+ readonly tabSize: number;
1937
+ /** Whether indentation uses spaces instead of tab characters. */
1938
+ readonly insertSpaces: boolean;
1939
+ }
1940
+
835
1941
  export declare type InlineAstNode = TextAstNode | StrongAstNode | EmphasisAstNode | StrikethroughAstNode | InlineCodeAstNode | InlineMathAstNode | LinkAstNode | ImageAstNode;
836
1942
 
837
1943
  export declare class InlineCodeAstNode extends AstNode {
@@ -881,6 +1987,27 @@ export declare const insertLineBreak: EditCommand;
881
1987
 
882
1988
  export declare const insertParagraph: EditCommand;
883
1989
 
1990
+ /**
1991
+ * Context-aware Enter. The behaviour is chosen from the active block:
1992
+ * - paragraph / heading / thematic break — the "rich text" thing: at the
1993
+ * block's end arm a transient empty paragraph (see {@link SmartEnterResult});
1994
+ * elsewhere split into two paragraphs (`\n\n`).
1995
+ * - fenced code / front matter — insert a newline that preserves the current
1996
+ * line's indentation, staying inside the fences.
1997
+ * - block quote — continue the quote (`\n> `); an empty quote line exits it.
1998
+ * - list — continue the list with the next marker (incrementing ordered
1999
+ * numbers, re-emitting task checkboxes); an empty item exits the list.
2000
+ * A non-collapsed selection, or any other block, falls back to a plain soft line
2001
+ * break, preserving today's behaviour.
2002
+ */
2003
+ export declare const insertSmartEnter: (ctx: CursorCommandContext) => SmartEnterResult;
2004
+
2005
+ /**
2006
+ * VS Code-style Tab: insert to the next tab stop for a caret or partial
2007
+ * single-line selection, and indent every selected line for a line selection.
2008
+ */
2009
+ export declare function insertTab(config?: IndentationConfig): EditCommand;
2010
+
884
2011
  export declare function insertText(text: string): EditCommand;
885
2012
 
886
2013
  /**
@@ -922,6 +2049,29 @@ export declare interface ISyntaxHighlighterDocument extends IDisposable {
922
2049
  readonly snapshot: IObservableWithChange<ISyntaxHighlightedSnapshot, LengthEdit>;
923
2050
  }
924
2051
 
2052
+ export declare interface KeyboardBinding {
2053
+ readonly key: string;
2054
+ readonly modifiers?: KeyboardModifiers;
2055
+ readonly platforms?: readonly KeyboardPlatform[];
2056
+ readonly action: EditorKeyboardAction;
2057
+ }
2058
+
2059
+ export declare interface KeyboardModifiers {
2060
+ readonly shift?: boolean;
2061
+ readonly alt?: boolean;
2062
+ readonly ctrl?: boolean;
2063
+ readonly meta?: boolean;
2064
+ }
2065
+
2066
+ export declare type KeyboardPlatform = 'macos' | 'windows' | 'linux';
2067
+
2068
+ export declare interface KeyboardProfile {
2069
+ /**
2070
+ * Bindings in priority order. The first exact key/modifier/platform match wins.
2071
+ */
2072
+ readonly bindings: readonly KeyboardBinding[];
2073
+ }
2074
+
925
2075
  declare abstract class LeafAstNode extends AstNode {
926
2076
  abstract readonly content: string;
927
2077
  get children(): readonly AstNode[];
@@ -1034,6 +2184,32 @@ declare class ListViewData {
1034
2184
  constructor(ast: ListAstNode, content: readonly AnyViewData[]);
1035
2185
  }
1036
2186
 
2187
+ /**
2188
+ * Compact in-memory history for editors that hold the only copy of the
2189
+ * document, such as a standalone browser page. Where the surrounding host
2190
+ * already records history — a VS Code `TextDocument` — forward to that
2191
+ * instead.
2192
+ */
2193
+ export declare class LocalHistoryStrategy implements IHistoryStrategy {
2194
+ private readonly _model;
2195
+ private readonly _past;
2196
+ private readonly _future;
2197
+ /**
2198
+ * The source text as of the last change this strategy recorded or applied.
2199
+ * Any other value means the document was replaced behind its back, so the
2200
+ * stored edits no longer line up and must be discarded rather than applied.
2201
+ */
2202
+ private _lastKnownText;
2203
+ constructor(_model: EditorModel);
2204
+ record(operation: () => void, edit?: StringEdit): void;
2205
+ undo(): void;
2206
+ redo(): void;
2207
+ /** The entry on top of `stack`, or `undefined` when it cannot be applied. */
2208
+ private _peekApplicable;
2209
+ private _apply;
2210
+ private _clear;
2211
+ }
2212
+
1037
2213
  /**
1038
2214
  * Parses markdown into a {@link DocumentAstNode}.
1039
2215
  *
@@ -1140,8 +2316,8 @@ export declare class MeasuredLayoutDebugRendering {
1140
2316
  * Exposes two DOM nodes the caller can place independently:
1141
2317
  *
1142
2318
  * - {@link overlayElement} — absolutely positioned; the caller mounts it
1143
- * inside the editor element so dashed line-bands and run-boxes line up
1144
- * with the editor's client coordinates.
2319
+ * inside the editor overlay container so dashed line-bands and run-boxes
2320
+ * share the measured editor-local coordinates.
1145
2321
  * - {@link infoElement} — block-flow; the caller mounts it as a sibling
1146
2322
  * *below* the editor. Contains the per-block summary table that used
1147
2323
  * to live on the overlay.
@@ -1152,7 +2328,6 @@ export declare class MeasuredLayoutDebugRendering {
1152
2328
  * the derived subscribed.
1153
2329
  */
1154
2330
  export declare class MeasuredLayoutDebugView extends Disposable {
1155
- private readonly _overlayParent;
1156
2331
  readonly overlayElement: HTMLElement;
1157
2332
  readonly infoElement: HTMLElement;
1158
2333
  readonly rendering: IObservable<MeasuredLayoutDebugRendering>;
@@ -1165,6 +2340,7 @@ export declare class MeasuredLayoutDebugView extends Disposable {
1165
2340
 
1166
2341
  export declare interface MeasuredLayoutDebugViewOptions {
1167
2342
  readonly model: MeasuredLayoutModel;
2343
+ readonly coordinateSpace: EditorCoordinateSpace;
1168
2344
  /**
1169
2345
  * DEBUG ONLY. Maps an absolute source offset to a fill color for that
1170
2346
  * character's glyph rect. The fixture passes the same function to the
@@ -1199,10 +2375,9 @@ export declare interface MeasuredLayoutDebugViewOptions {
1199
2375
  export declare class MeasuredLayoutModel {
1200
2376
  readonly measurements: ISettableObservable<readonly BlockMeasurement[], void>;
1201
2377
  /**
1202
- * Concatenated visual line map across all mounted blocks. Lines are
1203
- * left in DOM (client-coordinate) y order each per-block map already
1204
- * uses absolute client coordinates from `getClientRects()`, so the
1205
- * concatenation is well-formed without re-sorting.
2378
+ * Concatenated visual line map across all mounted blocks. Every per-block
2379
+ * map uses the same editor-local coordinate space, so concatenation is
2380
+ * well-formed without translation or re-sorting.
1206
2381
  */
1207
2382
  readonly visualLineMap: IObservableWithChange<VisualLineMap, void>;
1208
2383
  }
@@ -1244,14 +2419,25 @@ export declare class NativeClipboardStrategy implements IClipboardStrategy {
1244
2419
  connect(context: IClipboardContext): IDisposable;
1245
2420
  }
1246
2421
 
2422
+ declare interface NestedItem {
2423
+ readonly kind: 'nested';
2424
+ readonly original: AstNode;
2425
+ readonly originalStart: number;
2426
+ readonly modified: AstNode;
2427
+ readonly modifiedStart: number;
2428
+ readonly children: readonly DiffItem[];
2429
+ }
2430
+
1247
2431
  /**
1248
2432
  * Move the cursor one position left or right, skipping over hidden marker
1249
2433
  * ranges in inactive blocks (and inactive items of an active list).
1250
2434
  */
1251
- export declare function nextCursorPosition(doc: DocumentAstNode, activeBlock: BlockAstNode | undefined, cursor: number, direction: 'left' | 'right'): number;
2435
+ export declare function nextCursorPosition(doc: DocumentAstNode, markerVisibleBlocks: ReadonlySet<BlockAstNode>, cursor: number, direction: 'left' | 'right'): number;
1252
2436
 
1253
2437
  declare const NO_ACTIVE_BLOCKS: unique symbol;
1254
2438
 
2439
+ export declare function normalizeCursorPosition(doc: DocumentAstNode, markerVisibleBlocks: ReadonlySet<BlockAstNode>, cursor: number, target: number, direction: 'left' | 'right', includeHiddenRangeBoundary?: boolean): number;
2440
+
1255
2441
  export declare class OffsetRange {
1256
2442
  readonly start: number;
1257
2443
  readonly endExclusive: number;
@@ -1279,6 +2465,9 @@ export declare class OffsetRange {
1279
2465
  toString(): string;
1280
2466
  }
1281
2467
 
2468
+ /** Outdent the current line, or every line touched by the selection. */
2469
+ export declare function outdent(config?: IndentationConfig): EditCommand;
2470
+
1282
2471
  export declare class ParagraphAstNode extends BlockAstNodeBase {
1283
2472
  readonly content: readonly (InlineAstNode | GlueAstNode)[];
1284
2473
  readonly leadingTrivia?: GlueAstNode | undefined;
@@ -1297,7 +2486,48 @@ declare class ParagraphViewData {
1297
2486
  }
1298
2487
 
1299
2488
  /**
1300
- * Immutable point in 2D space, in CSS-pixel client coordinates.
2489
+ * A *transient* editing state: the empty paragraph the user conjured by
2490
+ * pressing Enter at the end of a paragraph. Markdown has no empty-paragraph
2491
+ * node, so this never lives in {@link EditorModel['sourceText']} or the parsed
2492
+ * {@link EditorModel.document} — it is pure edit intent that the view renders
2493
+ * as a synthetic blank line and that the controller either *materializes* (the
2494
+ * user types) or *cancels* (the user navigates away / backspaces).
2495
+ */
2496
+ declare interface PendingParagraph {
2497
+ /** The paragraph the blank line is rendered directly after. */
2498
+ readonly anchorBlock: BlockAstNode;
2499
+ /**
2500
+ * Source region rewritten when the pending paragraph is materialized — the
2501
+ * gap between {@link anchorBlock}'s text and whatever follows it.
2502
+ */
2503
+ readonly replaceRange: OffsetRange;
2504
+ /** Whether {@link replaceRange} ends at the end of the document. */
2505
+ readonly atEof: boolean;
2506
+ /**
2507
+ * A throwaway AST node that exists only to give the synthetic view child a
2508
+ * stable identity across render frames (the view pairs nodes by `ast.id`).
2509
+ * It is never part of {@link document}.
2510
+ */
2511
+ readonly syntheticAst: ParagraphAstNode;
2512
+ }
2513
+
2514
+ /**
2515
+ * View-data for the transient empty paragraph (see `PendingParagraph` in the
2516
+ * model). It carries only the throwaway {@link ParagraphAstNode} that gives the
2517
+ * rendered blank line a stable identity across frames; it has no content and is
2518
+ * never measured or part of the selection geometry — the caret is positioned
2519
+ * over it via a dedicated rect, not via the visual-line map.
2520
+ */
2521
+ declare class PendingParagraphViewData {
2522
+ readonly ast: ParagraphAstNode;
2523
+ readonly kind = "pendingParagraph";
2524
+ constructor(ast: ParagraphAstNode);
2525
+ }
2526
+
2527
+ /**
2528
+ * Immutable point in a caller-defined 2D CSS-pixel coordinate space.
2529
+ * Coordinate-owning APIs must document whether values are viewport-client or
2530
+ * editor-local; values from different spaces must not be mixed.
1301
2531
  */
1302
2532
  export declare class Point2D {
1303
2533
  readonly x: number;
@@ -1308,8 +2538,8 @@ export declare class Point2D {
1308
2538
  }
1309
2539
 
1310
2540
  /**
1311
- * Immutable axis-aligned rectangle in 2D space, in CSS-pixel client
1312
- * coordinates. `x`/`y` is the top-left corner, growing right/down.
2541
+ * Immutable axis-aligned rectangle in a caller-defined 2D CSS-pixel coordinate
2542
+ * space. `x`/`y` is the top-left corner, growing right/down.
1313
2543
  *
1314
2544
  * Half-open in both dimensions: `right` and `bottom` are excluded.
1315
2545
  */
@@ -1335,6 +2565,23 @@ export declare class Rect2D {
1335
2565
  translate(dx: number, dy: number): Rect2D;
1336
2566
  }
1337
2567
 
2568
+ declare interface RemovedItem {
2569
+ readonly kind: 'removed';
2570
+ readonly node: AstNode;
2571
+ readonly originalStart: number;
2572
+ readonly deletedLocal: readonly AnnotatedRange[];
2573
+ }
2574
+
2575
+ declare interface ReplacedItem {
2576
+ readonly kind: 'replaced';
2577
+ readonly original: AstNode;
2578
+ readonly originalStart: number;
2579
+ readonly modified: AstNode;
2580
+ readonly modifiedStart: number;
2581
+ readonly insertedLocal: readonly AnnotatedRange[];
2582
+ readonly deletedLocal: readonly AnnotatedRange[];
2583
+ }
2584
+
1338
2585
  export declare const selectAll: SelectionCommand;
1339
2586
 
1340
2587
  export declare function selectBlock(ctx: CursorCommandContext, blockRange: OffsetRange): Selection_2;
@@ -1360,8 +2607,13 @@ export { Selection_2 as Selection }
1360
2607
  export declare interface SelectionBlock {
1361
2608
  readonly block: BlockAstNode;
1362
2609
  readonly absoluteStart: number;
1363
- readonly viewNode: ViewNode;
1364
- readonly element: HTMLElement;
2610
+ /** Block border box in editor-local coordinates. */
2611
+ readonly rect: Rect2D;
2612
+ /** Visible horizontal padding-box bounds for a scrolling block. */
2613
+ readonly viewportClip: {
2614
+ readonly left: number;
2615
+ readonly right: number;
2616
+ } | undefined;
1365
2617
  }
1366
2618
 
1367
2619
  export declare type SelectionCommand = (ctx: CursorCommandContext, offset: SourceOffset) => Selection_2;
@@ -1373,6 +2625,9 @@ export declare interface SelectionRect {
1373
2625
  readonly height: number;
1374
2626
  }
1375
2627
 
2628
+ /** The interaction that produced the current editor selection. */
2629
+ export declare type SelectionSource = 'user' | 'find';
2630
+
1376
2631
  /**
1377
2632
  * Owns the SVG overlay that paints the selection.
1378
2633
  *
@@ -1393,11 +2648,10 @@ export declare interface SelectionRect {
1393
2648
  * envelope shape used by IDE selection rendering.
1394
2649
  */
1395
2650
  export declare class SelectionView extends Disposable {
1396
- private readonly _parent;
1397
2651
  readonly element: SVGSVGElement;
1398
2652
  readonly rendering: IObservable<SelectionViewRendering>;
1399
2653
  private readonly _path;
1400
- constructor(_parent: HTMLElement, options: SelectionViewOptions);
2654
+ constructor(options: SelectionViewOptions);
1401
2655
  }
1402
2656
 
1403
2657
  export declare interface SelectionViewOptions {
@@ -1413,6 +2667,25 @@ export declare class SelectionViewRendering {
1413
2667
 
1414
2668
  export declare const selectWord: SelectionCommand;
1415
2669
 
2670
+ /**
2671
+ * The outcome of {@link insertSmartEnter}: either a concrete source edit (the
2672
+ * ordinary cases), or a request to arm a transient empty paragraph (Enter at
2673
+ * the very end of a paragraph), which the controller turns into
2674
+ * {@link EditorModel.armPendingParagraph} rather than a source edit. Modelling
2675
+ * the empty paragraph as state instead of source keeps the document valid
2676
+ * Markdown — which has no empty-paragraph node — until the user actually types.
2677
+ */
2678
+ export declare type SmartEnterResult = {
2679
+ readonly kind: 'edit';
2680
+ readonly edit: StringEdit;
2681
+ readonly selection: Selection_2;
2682
+ } | {
2683
+ readonly kind: 'pending';
2684
+ readonly anchorBlock: BlockAstNode;
2685
+ readonly replaceRange: OffsetRange;
2686
+ readonly atEof: boolean;
2687
+ };
2688
+
1416
2689
  /**
1417
2690
  * A run of {@link Token}s together with the exact {@link OffsetRange} they
1418
2691
  * cover.
@@ -1426,8 +2699,39 @@ export declare interface SnapshotTokens {
1426
2699
  readonly tokens: readonly Token[];
1427
2700
  }
1428
2701
 
2702
+ /** Metadata delivered synchronously immediately before a model-owned source edit is applied. */
2703
+ export declare interface SourceEditEvent {
2704
+ readonly baseSourceTextId: number;
2705
+ readonly resultSourceTextId: number;
2706
+ readonly edit: StringEdit;
2707
+ readonly transaction: ITransaction;
2708
+ }
2709
+
1429
2710
  export declare type SourceOffset = number;
1430
2711
 
2712
+ export declare abstract class StackedCommentsPresenter extends Disposable implements ICommentsPresenter {
2713
+ protected readonly model: CommentsModel;
2714
+ protected readonly view: EditorView;
2715
+ protected readonly context?: CommentsPresenterContext | undefined;
2716
+ private readonly _layer;
2717
+ private readonly _entries;
2718
+ private _order;
2719
+ private _pendingRevealCommentId;
2720
+ constructor(model: CommentsModel, view: EditorView, context?: CommentsPresenterContext | undefined);
2721
+ /** Build the card DOM for a comment. Called once per new comment. */
2722
+ protected abstract createWidget(comment: Comment_2): StackWidget;
2723
+ revealComment(id: string): void;
2724
+ private _reconcile;
2725
+ private _relayout;
2726
+ private _revealPendingComment;
2727
+ }
2728
+
2729
+ /** The minimal widget contract a subclass must produce. */
2730
+ export declare interface StackWidget {
2731
+ readonly element: HTMLElement;
2732
+ dispose(): void;
2733
+ }
2734
+
1431
2735
  export declare class StrikethroughAstNode extends AstNode {
1432
2736
  readonly openMarker: MarkerAstNode;
1433
2737
  readonly content: readonly (InlineAstNode | GlueAstNode)[];
@@ -1470,6 +2774,11 @@ export declare class StringReplacement {
1470
2774
  constructor(replaceRange: OffsetRange, newText: string);
1471
2775
  get isEmpty(): boolean;
1472
2776
  equals(other: StringReplacement): boolean;
2777
+ /**
2778
+ * Narrows this replacement to the span that actually changes, by trimming
2779
+ * the prefix and suffix it shares with the text it replaces in `source`.
2780
+ */
2781
+ removeCommonSuffixPrefix(source: string): StringReplacement;
1473
2782
  toString(): string;
1474
2783
  }
1475
2784
 
@@ -1498,6 +2807,8 @@ declare class StrongViewData {
1498
2807
  constructor(ast: StrongAstNode, content: readonly AnyViewData[]);
1499
2808
  }
1500
2809
 
2810
+ declare type TabKeyboardAction = 'insert' | 'outdent';
2811
+
1501
2812
  export declare class TableAstNode extends BlockAstNodeBase {
1502
2813
  readonly content: readonly (TableRowAstNode | GlueAstNode)[];
1503
2814
  readonly leadingTrivia?: GlueAstNode | undefined;
@@ -1632,6 +2943,47 @@ export declare class Token {
1632
2943
  className: string | undefined);
1633
2944
  }
1634
2945
 
2946
+ declare interface UnchangedItem {
2947
+ readonly kind: 'unchanged';
2948
+ /** The modified-side node (identical in content to the original). */
2949
+ readonly node: AstNode;
2950
+ readonly modifiedStart: number;
2951
+ }
2952
+
2953
+ /**
2954
+ * A block whose token type the parser does not understand (a setext heading or
2955
+ * any future/extension construct). Rather than dropping the
2956
+ * span — which would demote its text to invisible glue — the parser captures the
2957
+ * whole source range verbatim as a single {@link MarkerAstNode} of kind
2958
+ * `content` and records the originating micromark {@link tokenType}, so the view
2959
+ * can render it as raw, editable text with an "unhandled" affordance. Offsets
2960
+ * stay sound: `content` tiles the block's full source span exactly.
2961
+ */
2962
+ declare class UnhandledBlockAstNode extends BlockAstNodeBase {
2963
+ readonly tokenType: string;
2964
+ readonly content: readonly (MarkerAstNode | GlueAstNode)[];
2965
+ readonly leadingTrivia?: GlueAstNode | undefined;
2966
+ readonly kind = "unhandledBlock";
2967
+ constructor(tokenType: string, content: readonly (MarkerAstNode | GlueAstNode)[], leadingTrivia?: GlueAstNode | undefined);
2968
+ get children(): readonly AstNode[];
2969
+ get code(): MarkerAstNode | undefined;
2970
+ mapChildren(m: ReadonlyMap<AstNode, AstNode>): AstNode;
2971
+ withLeadingTrivia(trivia: GlueAstNode | undefined): UnhandledBlockAstNode;
2972
+ protected _localEquals(o: this): boolean;
2973
+ }
2974
+
2975
+ /**
2976
+ * View-data for an {@link UnhandledBlockAstNode}. It has no active/inactive
2977
+ * split — the raw source *is* both the source and the rendered form — so it
2978
+ * carries no `showMarkup` flag; the renderer always shows the verbatim text.
2979
+ */
2980
+ declare class UnhandledBlockViewData {
2981
+ readonly ast: UnhandledBlockAstNode;
2982
+ readonly content: readonly AnyViewData[];
2983
+ readonly kind = "unhandledBlock";
2984
+ constructor(ast: UnhandledBlockAstNode, content: readonly AnyViewData[]);
2985
+ }
2986
+
1635
2987
  /**
1636
2988
  * Immutable view of an AST node. Pairs `ast` with its rendered `dom` and a
1637
2989
  * mirror of `ast.children` as ViewNode children. Source offsets are NEVER
@@ -1696,10 +3048,12 @@ export declare class ViewNode extends Disposable {
1696
3048
  /**
1697
3049
  * Map a DOM hit that lands on THIS node's own representation into a source
1698
3050
  * range in this node's local space `[0, ast.length)`. Polymorphic: a text
1699
- * leaf maps the caret offset 1:1; an element-only node (KaTeX math, `<hr>`,
1700
- * an image, a hidden marker) has no internal text mapping and snaps to its
1701
- * start by default subclasses may override (e.g. to snap to the nearer
1702
- * edge by x).
3051
+ * leaf maps the caret offset 1:1. For an element hit — an element-only node
3052
+ * (KaTeX math, `<hr>`, an image, a hidden marker) or a wrapper/container
3053
+ * element the platform reports a child-index offset, not a text caret, so
3054
+ * there is no internal mapping to honour: it snaps to the node's nearer
3055
+ * edge, `offset 0` (the "before" side) → start, any `offset >= 1` (the
3056
+ * "after" side) → end. Subclasses may override for finer control.
1703
3057
  */
1704
3058
  getLocalSourceRange(pos: DomPosition): OffsetRange;
1705
3059
  /**
@@ -1743,7 +3097,8 @@ export declare class VisualLine {
1743
3097
  /**
1744
3098
  * How `offset` relates to this line's runs:
1745
3099
  * - `'covers'`: a run starts at or strictly contains the offset
1746
- * (`start <= offset < endExclusive`) the caret belongs on this line.
3100
+ * (`start <= offset < endExclusive`), or a zero-length visual-line
3101
+ * anchor sits at the offset — the caret belongs on this line.
1747
3102
  * - `'end'`: the offset is only some run's trailing boundary
1748
3103
  * (`offset === endExclusive`) with no run covering it — a line-break
1749
3104
  * boundary the caret should leave for the next line.
@@ -1756,23 +3111,37 @@ export declare class VisualLine {
1756
3111
  */
1757
3112
  sourceDistanceTo(offset: SourceOffset): number;
1758
3113
  /**
1759
- * x of the caret position before `offset` on this line. When `offset`
1760
- * is past all runs (trailing whitespace / blank line), returns the
1761
- * right edge of the last run; when before all runs, returns the left
1762
- * edge of the first run.
3114
+ * x of the caret position before `offset` on this line.
3115
+ *
3116
+ * The runs tile the source but are stored in paint order, not sorted by
3117
+ * source offset (hidden-marker runs are appended last). So this scans all
3118
+ * runs rather than assuming any ordering:
3119
+ *
3120
+ * - If some run *covers* `offset`, its own geometry places the caret
3121
+ * (exact glyph boundary for text runs). In the active, markers-visible
3122
+ * form every offset is covered, so this branch keeps distinct offsets
3123
+ * distinct.
3124
+ * - Otherwise `offset` sits in a gap — a hidden inline marker such as the
3125
+ * `**` of `**bold**`, or before/after the painted text. It snaps to the
3126
+ * seam between the source-nearest runs on either side: the right edge of
3127
+ * the closest run ending at/before `offset`, else the left edge of the
3128
+ * closest run starting at/after it. A hidden marker collapses to zero
3129
+ * width, so both edges coincide at the seam.
1763
3130
  */
1764
3131
  xAtOffset(offset: SourceOffset): number;
1765
3132
  /**
1766
3133
  * Snap `x` to the nearest offset on this line. If `x` falls inside a
1767
- * run, the offset is interpolated by character fraction; otherwise it
1768
- * snaps to the closer edge of the nearest run.
3134
+ * run, the run resolves the offset (exact glyph boundary for text runs,
3135
+ * nearer edge for source-less runs); otherwise it snaps to the closer
3136
+ * edge of the nearest run.
1769
3137
  */
1770
3138
  offsetAtX(x: number): SourceOffset;
1771
3139
  }
1772
3140
 
1773
3141
  /**
1774
3142
  * Geometry of the rendered document, as a map from source offsets to 2D
1775
- * positions and back.
3143
+ * positions and back. All geometry is expressed in the editor overlay's local
3144
+ * CSS-pixel coordinate space.
1776
3145
  *
1777
3146
  * Structure (top to bottom):
1778
3147
  *
@@ -1807,7 +3176,7 @@ export declare class VisualLineMap {
1807
3176
  static measure(blockViews: readonly {
1808
3177
  readonly absoluteStart: number;
1809
3178
  readonly viewNode: ViewNode;
1810
- }[]): VisualLineMap;
3179
+ }[], coordinateSpace: EditorCoordinateSpace, transform?: EditorCoordinateTransform): VisualLineMap;
1811
3180
  constructor(lines: readonly VisualLine[]);
1812
3181
  get lineCount(): number;
1813
3182
  get isEmpty(): boolean;
@@ -1845,6 +3214,8 @@ export declare class VisualLineMap {
1845
3214
  offsetAtPoint(point: Point2D): SourceOffset;
1846
3215
  /** Snap `x` to the nearest offset on a specific line. */
1847
3216
  offsetInLineAtX(lineIndex: number, x: number): SourceOffset;
3217
+ lineStartOffset(lineIndex: number): SourceOffset | undefined;
3218
+ lineEndOffset(lineIndex: number): SourceOffset | undefined;
1848
3219
  }
1849
3220
 
1850
3221
  /**
@@ -1853,14 +3224,22 @@ export declare class VisualLineMap {
1853
3224
  * When constructed with a {@link VisualRunSource}, `xAtOffset` returns the
1854
3225
  * pixel-exact x of the caret before character `offset` by measuring the
1855
3226
  * prefix `[textNodeStart, textNodeStart + (offset - sourceStart))` with a
1856
- * DOM `Range`. Without a source it falls back to linear interpolation
1857
- * across `rect.width`.
3227
+ * DOM `Range`.
3228
+ *
3229
+ * A source-less run has no per-offset geometry: it either represents an
3230
+ * element-only block (KaTeX math, a mermaid/custom diagram, an image, an
3231
+ * inactive `<hr>`) whose box does not correspond to source offsets, or a
3232
+ * hand-built run in a test. Either way it maps between offsets and x by
3233
+ * snapping to the nearer edge of {@link rect} rather than fabricating
3234
+ * interior positions.
1858
3235
  */
1859
3236
  export declare class VisualRun {
1860
3237
  readonly sourceRange: OffsetRange;
1861
3238
  readonly rect: Rect2D;
1862
3239
  readonly source?: VisualRunSource | undefined;
1863
- constructor(sourceRange: OffsetRange, rect: Rect2D, source?: VisualRunSource | undefined);
3240
+ readonly isVisualLineAnchor: boolean;
3241
+ static visualLineAnchor(sourceOffset: SourceOffset, rect: Rect2D): VisualRun;
3242
+ constructor(sourceRange: OffsetRange, rect: Rect2D, source?: VisualRunSource | undefined, isVisualLineAnchor?: boolean);
1864
3243
  get sourceStart(): SourceOffset;
1865
3244
  get sourceEndExclusive(): SourceOffset;
1866
3245
  get sourceLength(): number;
@@ -1872,19 +3251,40 @@ export declare class VisualRun {
1872
3251
 
1873
3252
  /**
1874
3253
  * The DOM source of a {@link VisualRun}. When set, `xAtOffset` and
1875
- * `offsetAtX` measure exact glyph positions via `Range.getBoundingClientRect`
1876
- * instead of linear interpolation across the run's rect. This matters for
1877
- * proportional fonts where character widths differ a lot (e.g. `m` vs `i`)
1878
- * and a caret placed by interpolation lands several pixels inside the
1879
- * wrong character.
3254
+ * `offsetAtX` measure exact glyph positions via `Range.getBoundingClientRect`.
3255
+ * This matters for proportional fonts where character widths differ a lot
3256
+ * (e.g. `m` vs `i`): a caret placed by anything coarser than real glyph
3257
+ * measurement lands several pixels inside the wrong character.
1880
3258
  *
1881
- * Hand-built runs (tests) omit this; their `xAtOffset` falls back to
1882
- * linear interpolation.
3259
+ * A run without a source has no per-offset geometry, so it maps between
3260
+ * offsets and x by snapping to the nearer run edge. Real text runs always
3261
+ * carry a source; source-less runs are element-only blocks (see
3262
+ * {@link _appendElementBlockRun}) and hand-built runs in tests.
1883
3263
  */
1884
3264
  declare interface VisualRunSource {
1885
3265
  readonly textNode: Text;
1886
3266
  /** Offset within `textNode.data` corresponding to `sourceRange.start`. */
1887
3267
  readonly textNodeStart: number;
3268
+ readonly coordinateSpace: EditorCoordinateSpace;
3269
+ }
3270
+
3271
+ export declare const vscodeHostKeyboardProfile: KeyboardProfile;
3272
+
3273
+ export declare const vscodeKeyboardProfile: KeyboardProfile;
3274
+
3275
+ export declare const vscodeLocalKeyboardProfile: KeyboardProfile;
3276
+
3277
+ export declare class VscodeStackedCommentsView extends StackedCommentsPresenter {
3278
+ protected createWidget(comment: Comment_2): StackWidget;
3279
+ }
3280
+
3281
+ export declare class VsCodeV2CommentsView extends StackedCommentsPresenter {
3282
+ protected createWidget(comment: Comment_2): StackWidget;
3283
+ }
3284
+
3285
+ export declare interface WordNavigationConfig {
3286
+ readonly wordSeparators: string;
3287
+ readonly wordSegmenterLocales: readonly string[];
1888
3288
  }
1889
3289
 
1890
3290
  export { }