@vscode/markdown-editor 0.0.2-2 → 0.0.2-21

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 (29) hide show
  1. package/dist/_observables/observableInternal/index.d.ts +1 -1
  2. package/dist/_observables/observableInternal/logging/consoleObservableLogger.d.ts +1 -1
  3. package/dist/_observables/observableInternal/logging/debugger/devToolsLogger.d.ts +1 -1
  4. package/dist/_observables/observableInternal/logging/logging.d.ts +1 -1
  5. package/dist/_observables/observableInternal/reactions/{autorun.d.ts → createEffect.d.ts} +11 -3
  6. package/dist/index.d.ts +1000 -52
  7. package/dist/index.js +4005 -2074
  8. package/dist/index.js.map +1 -1
  9. package/dist/markdown-editor.css +1 -0
  10. package/dist/observables.js +79 -101
  11. package/dist/observables.js.map +1 -1
  12. package/dist/{runOnChange-owE1SMC0.js → runOnChange-CkxK2gSn.js} +191 -163
  13. package/dist/runOnChange-CkxK2gSn.js.map +1 -0
  14. package/dist/stringEdit-DzLs4E1d.js +177 -0
  15. package/dist/stringEdit-DzLs4E1d.js.map +1 -0
  16. package/dist/web-editors.d.ts +125 -0
  17. package/dist/web-editors.js +5185 -0
  18. package/dist/web-editors.js.map +1 -0
  19. package/package.json +36 -9
  20. package/src/contrib/comments/commentInput.css +150 -0
  21. package/src/contrib/comments/comments.css +129 -0
  22. package/src/contrib/commentsVscode/vscodeCommentWidgetV2.css +166 -0
  23. package/src/view/editor.css +526 -32
  24. package/src/view/themes/default.css +11 -3
  25. package/src/view/themes/github.css +12 -9
  26. package/src/view/themes/vscode-default.css +347 -0
  27. package/src/view/themes/vscode-github.css +349 -0
  28. package/dist/runOnChange-owE1SMC0.js.map +0 -1
  29. /package/dist/_observables/observableInternal/reactions/{autorunImpl.d.ts → createEffectImpl.d.ts} +0 -0
@@ -1,3 +1,5 @@
1
+ @import '@vscode/codicons/dist/codicon.css';
2
+
1
3
  /*
2
4
  * Base editor styles — ABSOLUTE MINIMUM.
3
5
  *
@@ -12,32 +14,101 @@
12
14
  * theme files (`themes/default.css`, `themes/github.css`) applied via the
13
15
  * `classNames` editor option; those selectors only ever match inside their
14
16
  * own theme class and never style the global scope.
17
+ *
18
+ * NO `--vscode-*` variables may be referenced here. This base file must stay
19
+ * theme-agnostic and know nothing about VS Code. Where a value needs to vary
20
+ * per theme, expose a local `--md-*` custom property WITH a sensible literal
21
+ * fallback (e.g. `var(--md-cursor-background, #000)`) and let a theme file set
22
+ * it. Only the `themes/vscode-*.css` themes are permitted to read `--vscode-*`
23
+ * variables and map them onto these `--md-*` properties.
15
24
  */
16
25
 
17
26
  .md-editor {
18
- position: relative;
27
+ display: flow-root;
19
28
  outline: none;
29
+ container: md-editor / inline-size;
20
30
  /* The editor paints its own cursor (`.md-cursor`); hide the native caret. */
21
31
  caret-color: transparent;
22
- padding: 48px;
32
+ }
33
+
34
+ /*
35
+ * The transient empty paragraph — the blank line conjured by pressing Enter at
36
+ * the end of a paragraph (see `PendingParagraph` in the model). It is not part
37
+ * of the document (Markdown has no empty-paragraph node); it holds a single
38
+ * `<br>` so it occupies one line's height, and the caret is painted over it.
39
+ */
40
+ .md-pending-paragraph {
41
+ min-height: 1em;
42
+ }
43
+
44
+ /*
45
+ * Inner content container. Holds the rendered document and the cursor/selection
46
+ * overlays, which position themselves relative to this box, so it is the
47
+ * positioning context. In limited-width mode the editor sets an inline
48
+ * `max-width` here and the auto inline margins center it within the full-width
49
+ * `.md-editor`.
50
+ */
51
+ .md-editor-content {
52
+ --md-editor-content-inline-start-padding: 48px;
53
+ --md-readonly-toggle-width: 58px;
54
+ --md-readonly-toggle-height: 32px;
55
+ /* Leaves 16px of optical separation beyond the active block's 8px outset glow. */
56
+ --md-readonly-toggle-gap: 24px;
57
+ --md-readonly-toggle-inset: 4px;
58
+ --md-editor-content-inline-end-padding: var(--md-editor-content-inline-start-padding);
59
+ position: relative;
60
+ margin-inline: auto;
61
+ padding-block: 0;
62
+ padding-inline: var(--md-editor-content-inline-start-padding) var(--md-editor-content-inline-end-padding);
63
+ }
64
+
65
+ .md-editor-content-with-readonly-toggle {
66
+ --md-editor-content-inline-end-padding: calc(
67
+ var(--md-readonly-toggle-width) + var(--md-readonly-toggle-gap) + var(--md-readonly-toggle-inset)
68
+ );
69
+ }
70
+
71
+ .md-editor-content-with-readonly-toggle>.md-document :is(.md-heading, .md-paragraph) {
72
+ overflow-wrap: anywhere;
23
73
  }
24
74
 
25
75
  .md-block-active {
26
- background: #f8f8f8;
76
+ background: var(--md-block-active-background, #f8f8f8);
27
77
  border-radius: 4px;
28
- box-shadow: 0 0 0 8px #f8f8f8;
78
+ box-shadow: 0 0 0 8px var(--md-block-active-background, #f8f8f8);
29
79
  }
30
80
 
31
81
  /*
32
- * A rendered (inactive) link opens on a plain click, so it keeps the anchor's
33
- * pointer cursor. Once its block is active the link is editable source and a
34
- * plain click places the caret (Ctrl/Cmd+click still opens), so it shows the
35
- * normal editing cursor like the surrounding text.
82
+ * Cursor mirrors the same open affordance as the underline below: the anchor's
83
+ * pointer cursor appears exactly when a click would open the link. An inactive
84
+ * (rendered) link opens on a plain click, so it keeps the browser's default
85
+ * anchor pointer. Once its block is active a plain click places the caret, so
86
+ * the link shows the normal editing cursor — except while Ctrl/Cmd is held
87
+ * (`.md-mod-down`), when a click opens it and the pointer returns.
36
88
  */
37
89
  .md-block-active a {
38
90
  cursor: inherit;
39
91
  }
40
92
 
93
+ .md-editor.md-mod-down .md-block-active a[href] {
94
+ cursor: pointer;
95
+ }
96
+
97
+ /*
98
+ * Link underline is an "open affordance": show it only while a click on the
99
+ * link right now would actually open it. An inactive (rendered) link opens on
100
+ * a plain click, so hovering it underlines. An active link's plain click edits
101
+ * its source — it only opens with Ctrl/Cmd — so it underlines on hover only
102
+ * while that modifier is held (`.md-mod-down`, set live by the view).
103
+ */
104
+ .md-editor a[href]:hover {
105
+ text-decoration: underline;
106
+ }
107
+
108
+ .md-editor:not(.md-mod-down) .md-block-active a[href]:hover {
109
+ text-decoration: none;
110
+ }
111
+
41
112
  .md-fence-spacer {
42
113
  visibility: hidden;
43
114
  font-family: 'Cascadia Code', 'Fira Code', monospace;
@@ -73,6 +144,55 @@
73
144
  white-space: pre-wrap;
74
145
  }
75
146
 
147
+ /*
148
+ * An unhandled block: a construct the parser does not model (setext heading,
149
+ * frontmatter fence, an extension token). Its raw source is shown verbatim,
150
+ * styled like a code block but visibly flagged as "not understood" — a dashed
151
+ * warning border and a small badge naming the originating token type (from the
152
+ * `data-unhandled-token` attribute) — so the content is preserved and editable
153
+ * rather than silently dropped.
154
+ *
155
+ * The wrapper is the non-scrolling box (border + block spacing + the badge); the
156
+ * inner `.md-unhandled-scroll` <pre> is the horizontal scroller. Keeping the
157
+ * badge on the wrapper (not the scroller) pins it to the visible top-right
158
+ * corner instead of letting it drift with the scrolled content.
159
+ */
160
+ .md-unhandled-block {
161
+ position: relative;
162
+ margin: 0.5em 0;
163
+ border: 1px dashed #d0a000;
164
+ border-radius: 4px;
165
+ }
166
+
167
+ /*
168
+ * Inner scroller. Beats the per-theme `.md-theme-* .md-code-block` box rules
169
+ * (same class count) via the extra `pre` type selector: no margin/border of its
170
+ * own (those live on the wrapper), and top padding that reserves room for the
171
+ * pinned badge so it never overlaps the first source line.
172
+ */
173
+ pre.md-unhandled-scroll.md-code-block {
174
+ margin: 0;
175
+ border: 0;
176
+ border-radius: 4px;
177
+ padding-top: 1.7em;
178
+ }
179
+
180
+ .md-unhandled-block::after {
181
+ content: 'unhandled: ' attr(data-unhandled-token);
182
+ position: absolute;
183
+ top: 0;
184
+ right: 0;
185
+ padding: 1px 6px;
186
+ font-family: system-ui, sans-serif;
187
+ font-size: 10px;
188
+ line-height: 1.4;
189
+ color: #6b5200;
190
+ background: #f5e5a8;
191
+ border-bottom-left-radius: 4px;
192
+ pointer-events: none;
193
+ user-select: none;
194
+ }
195
+
76
196
  /*
77
197
  * Visible whitespace indicators, shown only in the active/source view. The real
78
198
  * whitespace character stays in the DOM (so source ↔ DOM mapping and selection
@@ -172,15 +292,61 @@
172
292
  }
173
293
 
174
294
  /*
175
- * Glue (table cell pipes, the task `[x] ` source, source padding) keeps its
176
- * inline footprint when hidden so column widths and the checkbox gutter stay
177
- * identical between active and inactive states.
295
+ * Glue normally keeps its inline footprint when hidden so source padding and
296
+ * widget gutters stay identical between active and inactive states. Hidden
297
+ * table-cell glue is the exception below: leaving it in inline flow lets its
298
+ * invisible spaces wrap around long cell content.
178
299
  */
179
300
  .md-glue-hidden {
180
301
  display: inline;
181
302
  visibility: hidden;
182
303
  }
183
304
 
305
+ /*
306
+ * Inactive table pipes stay in the DOM/source tree, but leave layout and
307
+ * hit-testing entirely. Keeping an invisible rect either in the content flow or
308
+ * over the cell padding can create phantom lines or steal clicks from visible
309
+ * text. Hidden source offsets therefore collapse to the neighboring semantic
310
+ * seam, matching other display-none markers. Visible pipes retain the per-theme
311
+ * active/source layout.
312
+ */
313
+ .md-table td>.md-glue-tableCellGlue.md-glue-hidden {
314
+ display: none;
315
+ }
316
+
317
+ /*
318
+ * Active table: take each cell's TRAILING structural glue (the source space
319
+ * after the cell text, and — via the per-theme `right: 0` rule — the last
320
+ * column's closing `|`) out of the cell's inline flow. Left in flow, that glue
321
+ * wraps below long cell content (the same wrapping the hidden-glue note above
322
+ * describes), which makes the cell two lines tall: the browser's default
323
+ * `vertical-align: middle` then re-centres the other cells' single-line content
324
+ * in Y, their runs merge into one oversized VisualLine, and the caret — sized
325
+ * directly from that line box — grows far past the line height while the
326
+ * visible pipes drift apart row-to-row. Positioning the trailing glue
327
+ * absolutely removes it from wrapping flow while keeping its real client rects
328
+ * at the static (content-line) position, so caret placement, hit-testing,
329
+ * source mapping and injectivity are unchanged. `top`/`bottom: auto` keep it on
330
+ * that content line rather than the cell's top edge. This mirrors the inactive
331
+ * `display: none` above; the leading `| ` glue deliberately stays in flow (a
332
+ * relative glyph shift, per theme) so first-text X is preserved.
333
+ *
334
+ * `:not(:first-child)` restricts this to a genuine trailing glue: a non-empty
335
+ * cell emits leading + trailing glue, but an EMPTY cell parses to a single glue
336
+ * node that is simultaneously first and last child. That lone node is the
337
+ * cell's leading pipe, so it must stay in flow like every other leading pipe;
338
+ * excluding it keeps empty cells identical to their previous (base) behaviour.
339
+ */
340
+ .md-table.md-block-active td {
341
+ position: relative;
342
+ }
343
+
344
+ .md-table.md-block-active td>.md-glue-tableCellGlue:last-child:not(:first-child) {
345
+ position: absolute;
346
+ top: auto;
347
+ bottom: auto;
348
+ }
349
+
184
350
  /*
185
351
  * Inter-block glue: the run of blank lines between two top-level blocks. It is
186
352
  * mounted as the last inline child of the block that precedes it, so it sits at
@@ -217,15 +383,6 @@
217
383
  visibility: hidden;
218
384
  }
219
385
 
220
- /*
221
- * Table cell-glue pipes (`| `) keep their inline footprint when hidden so the
222
- * column widths stay identical active and inactive.
223
- */
224
- .md-marker-tableCellGlue.md-marker-hidden {
225
- display: inline;
226
- visibility: hidden;
227
- }
228
-
229
386
  /*
230
387
  * The code-block fences (```lang / ```) each sit on their own source line. When
231
388
  * hidden they keep their vertical footprint (visibility hidden, not display
@@ -260,6 +417,48 @@
260
417
  height: 16px;
261
418
  }
262
419
 
420
+ /*
421
+ * A wide table scrolls horizontally inside its wrapper (the block box) instead
422
+ * of overflowing the page and scrolling the editor's fixed left gutter away.
423
+ * The wrapper is the scroll viewport the selection/caret clipping measures (see
424
+ * `BlockViewNode.scrollElement` / `blockViewportClip`); the inner `<table>`
425
+ * keeps `width: auto` (theme-defined) so it sizes to its content and overflows.
426
+ *
427
+ * `width: fit-content` keeps the wrapper hugging the table's intrinsic width, so
428
+ * a narrow table — and the active-block glow redirected onto the wrapper below —
429
+ * stays as wide as the table (unchanged from before this wrapper existed).
430
+ * `max-width: 100%` caps it at the content column, past which the table
431
+ * overflows and scrolls locally. This is the standard responsive-table pattern
432
+ * (cf. GitHub's own `width: max-content; max-width: 100%`).
433
+ */
434
+ .md-table-wrapper {
435
+ overflow-x: auto;
436
+ width: fit-content;
437
+ max-width: 100%;
438
+ }
439
+
440
+ /*
441
+ * The active-block highlight is an *outset* glow (`box-shadow: 0 0 0 8px`).
442
+ * Making the wrapper a scroll box (`overflow-x: auto`, which also forces
443
+ * `overflow-y: auto`) clips descendant paint to its padding box, so the glow on
444
+ * the inner active `<table>` would be cut off (the table has no horizontal
445
+ * margin to give the shadow room). Render the generic active chrome on the
446
+ * wrapper instead — a scroll box's own shadow is never clipped by its own
447
+ * overflow, exactly like a code block carries the glow on its own scroller —
448
+ * and drop it from the table, which keeps `md-block-active` purely for the
449
+ * per-theme source-pipe styling.
450
+ */
451
+ .md-table-wrapper:has(> .md-table.md-block-active) {
452
+ background: var(--md-block-active-background, #f8f8f8);
453
+ border-radius: 4px;
454
+ box-shadow: 0 0 0 8px var(--md-block-active-background, #f8f8f8);
455
+ }
456
+
457
+ .md-table.md-block-active {
458
+ background: none;
459
+ box-shadow: none;
460
+ }
461
+
263
462
  .md-selection-layer {
264
463
  position: absolute;
265
464
  top: 0;
@@ -274,6 +473,54 @@
274
473
  fill: rgba(59, 130, 246, 0.25);
275
474
  }
276
475
 
476
+ /*
477
+ * Gutter markers (source-control style change indicators). The layer fills the
478
+ * content box; each marker is an absolutely-positioned bar pinned to the left
479
+ * gutter (inside `.md-editor-content`'s 48px padding). The view sets only
480
+ * `top`/`height`; horizontal placement, width and color live here so themes can
481
+ * tune them without touching layout. Like the other overlays it never eats
482
+ * pointer events.
483
+ */
484
+ .md-gutter-layer {
485
+ position: absolute;
486
+ top: 0;
487
+ left: 0;
488
+ width: 100%;
489
+ height: 100%;
490
+ pointer-events: none;
491
+ overflow: visible;
492
+ }
493
+
494
+ .md-gutter-marker {
495
+ position: absolute;
496
+ left: var(--md-gutter-marker-left, 18px);
497
+ width: var(--md-gutter-marker-width, 3px);
498
+ border-radius: 2px;
499
+ }
500
+
501
+ .md-gutter-marker-added {
502
+ background: var(--md-gutter-added, #2ea043);
503
+ }
504
+
505
+ .md-gutter-marker-modified {
506
+ background: var(--md-gutter-modified, #0969da);
507
+ }
508
+
509
+ /*
510
+ * A deletion has no lines to span, so it is drawn as a downward-pointing
511
+ * triangle centered on the seam where the removed text used to be (the top of
512
+ * the line that now follows it), matching the git deleted-lines affordance.
513
+ */
514
+ .md-gutter-marker-deleted {
515
+ width: 0;
516
+ height: 0;
517
+ border-radius: 0;
518
+ border-left: var(--md-gutter-marker-width, 3px) solid var(--md-gutter-deleted, #cf222e);
519
+ border-top: 4px solid transparent;
520
+ border-bottom: 4px solid transparent;
521
+ transform: translateY(-4px);
522
+ }
523
+
277
524
  /* Source markers occupy the gutter so toggling them never shifts content. */
278
525
  .md-heading {
279
526
  position: relative;
@@ -346,14 +593,7 @@
346
593
  overflow-x: auto;
347
594
  }
348
595
 
349
- /*
350
- * Paragraphs inside list items render inline so the item's content sits on
351
- * the same line as the list counter / source marker instead of wrapping
352
- * below it. Block-level <p> here would push the text down even with zero
353
- * margin, because the counter sits at the start of the principal box.
354
- */
355
596
  .md-list li>.md-paragraph {
356
- display: inline;
357
597
  margin: 0;
358
598
  }
359
599
 
@@ -433,20 +673,67 @@
433
673
  white-space: pre;
434
674
  }
435
675
 
676
+ /*
677
+ * A continuation-line indent inside a paragraph is source-only padding. When
678
+ * inactive, the preceding soft-break newline already collapses to the single
679
+ * rendered space Markdown requires; retaining the hidden indent would add the
680
+ * tab/space run's width on top of it.
681
+ */
682
+ .md-paragraph>.md-glue-indent.md-glue-hidden {
683
+ display: none;
684
+ }
685
+
686
+ /*
687
+ * A loose list item's later block (its second paragraph etc.) owns the source
688
+ * continuation indent that precedes it as its `leadingTrivia`. Like a nested
689
+ * list's gutter, that indent glue is pulled out of flow to the left so it never
690
+ * widens the line: the block's text stays aligned with the first paragraph
691
+ * above it, and the indent dots only appear in the left margin when the block is
692
+ * active (the footprint is identical in both states, so the text never shifts).
693
+ */
694
+ .md-list li>.md-block,
695
+ .md-task-list-item>.md-block {
696
+ position: relative;
697
+ }
698
+
699
+ .md-list li>.md-block>.md-glue-indent:first-child,
700
+ .md-task-list-item>.md-block>.md-glue-indent:first-child {
701
+ position: absolute;
702
+ right: 100%;
703
+ top: 0;
704
+ }
705
+
436
706
  .md-task-list-item {
437
707
  list-style: none;
438
708
  position: relative;
709
+ /*
710
+ * Width of the checkbox / `[x]` source column. Single-sourced so the marker
711
+ * reservation on the first paragraph and the left indent of any following
712
+ * block (loose task items) stay in lockstep — keeping every block's text in
713
+ * one vertical column under the marker.
714
+ */
715
+ --md-task-marker-width: 1.75em;
439
716
  }
440
717
 
441
718
  /*
442
- * Reserve extra width after the `[x]`/`[ ]` source marker (the first glue in a
443
- * task item's paragraph) so the rendered checkbox widget overlaid on it has a
444
- * comfortable gap before the text. The padding applies in both active and
719
+ * Reserve the marker column after the `[x]`/`[ ]` source marker (the first glue
720
+ * in a task item's paragraph) so the rendered checkbox widget overlaid on it has
721
+ * a comfortable gap before the text. The fixed width applies in both active and
445
722
  * inactive states, so the first text character does not drift horizontally when
446
723
  * markers toggle (see the compare fixture's `checkMarkedTextX` assertion).
447
724
  */
448
725
  .md-task-list-item .md-paragraph>.md-glue:first-child {
449
- padding-right: 0.7em;
726
+ display: inline-block;
727
+ width: var(--md-task-marker-width);
728
+ }
729
+
730
+ /*
731
+ * Following blocks in a loose task item (the second paragraph etc.) carry no
732
+ * marker glue, so indent them by the marker column to line their text up under
733
+ * the first paragraph's text rather than under the checkbox.
734
+ */
735
+ .md-task-list-item>.md-block+.md-block {
736
+ padding-left: var(--md-task-marker-width);
450
737
  }
451
738
 
452
739
  .md-checkbox {
@@ -499,11 +786,19 @@
499
786
  .md-cursor {
500
787
  position: absolute;
501
788
  width: 2px;
502
- background: #000;
789
+ background: var(--md-cursor-background, #000);
503
790
  pointer-events: none;
504
791
  animation: md-cursor-blink 1s step-end infinite;
505
792
  }
506
793
 
794
+ /*
795
+ * Read-only mode retains the logical caret for selection and comment anchoring.
796
+ * Hide only its painted element so unlocking can restore it in place.
797
+ */
798
+ .md-editor.md-readonly .md-cursor {
799
+ visibility: hidden;
800
+ }
801
+
507
802
  @keyframes md-cursor-blink {
508
803
 
509
804
  0%,
@@ -514,4 +809,203 @@
514
809
  50% {
515
810
  opacity: 0;
516
811
  }
812
+ }
813
+
814
+ /*
815
+ * Edit / read-only lock toggle.
816
+ *
817
+ * The asymmetric content padding reserves a real rail for the control:
818
+ * button width + gap + edge inset. The zero-height sticky host spans the
819
+ * document column and that rail, keeping the button beside (never over) the
820
+ * rendered document as it scrolls. Colors use local `--md-readonly-*` custom
821
+ * properties with literal fallbacks so this base file stays theme-agnostic
822
+ * (themes may override them).
823
+ */
824
+ .md-readonly-toggle-host {
825
+ position: sticky;
826
+ top: 0;
827
+ width: calc(100% + var(--md-editor-content-inline-end-padding));
828
+ height: 0;
829
+ z-index: 10;
830
+ pointer-events: none;
831
+ }
832
+
833
+ .md-readonly-toggle {
834
+ position: absolute;
835
+ top: 4px;
836
+ right: var(--md-readonly-toggle-inset);
837
+ pointer-events: auto;
838
+ display: inline-grid;
839
+ grid-template-columns: repeat(2, 24px);
840
+ align-items: center;
841
+ justify-items: center;
842
+ gap: 2px;
843
+ box-sizing: border-box;
844
+ width: var(--md-readonly-toggle-width);
845
+ height: var(--md-readonly-toggle-height);
846
+ padding: 3px;
847
+ overflow: hidden;
848
+ direction: ltr;
849
+ appearance: none;
850
+ border-radius: 999px;
851
+ border: 1px solid var(--md-readonly-border, #b8b8b8);
852
+ background: var(--md-readonly-background, #f3f3f3);
853
+ color: var(--md-readonly-foreground, #444444);
854
+ font-size: 16px;
855
+ line-height: 1;
856
+ cursor: pointer;
857
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.08);
858
+ transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
859
+ }
860
+
861
+ /*
862
+ * Below this width there is not enough document column for the side rail to be
863
+ * useful. Give the host its own row and stop it from sticking, so neither long
864
+ * unbreakable content nor later content scrolled through the viewport can ever
865
+ * pass underneath the button.
866
+ */
867
+ @container md-editor (max-width: 320px) {
868
+ .md-readonly-toggle-host {
869
+ position: relative;
870
+ height: calc(
871
+ var(--md-readonly-toggle-height) + var(--md-readonly-toggle-inset) + var(--md-readonly-toggle-inset)
872
+ );
873
+ }
874
+ }
875
+
876
+ .md-readonly-toggle::after {
877
+ content: '';
878
+ position: absolute;
879
+ z-index: 2;
880
+ top: -8px;
881
+ bottom: -8px;
882
+ left: -30px;
883
+ width: 24px;
884
+ background: linear-gradient(90deg,
885
+ transparent,
886
+ var(--md-readonly-shine-edge, rgba(255, 255, 255, 0.16)) 24%,
887
+ var(--md-readonly-shine, #ffffff) 50%,
888
+ var(--md-readonly-shine-edge, rgba(255, 255, 255, 0.16)) 76%,
889
+ transparent);
890
+ box-shadow: 0 0 8px 2px var(--md-readonly-shine-glow, rgba(255, 255, 255, 0.58));
891
+ opacity: 0;
892
+ pointer-events: none;
893
+ transform: skewX(-20deg);
894
+ }
895
+
896
+ .md-readonly-toggle.md-readonly-toggle-shine::after {
897
+ animation: md-readonly-toggle-shine 2s linear;
898
+ will-change: transform, opacity;
899
+ }
900
+
901
+ @keyframes md-readonly-toggle-shine {
902
+ 0% {
903
+ opacity: 0;
904
+ transform: translateX(0) skewX(-20deg);
905
+ }
906
+
907
+ 12%,
908
+ 88% {
909
+ opacity: 1;
910
+ }
911
+
912
+ 100% {
913
+ opacity: 0;
914
+ transform: translateX(122px) skewX(-20deg);
915
+ }
916
+ }
917
+
918
+ .md-readonly-toggle:hover {
919
+ border-color: var(--md-readonly-border-hover, #8d8d8d);
920
+ background: var(--md-readonly-background-hover, #e9e9e9);
921
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 6px 16px rgba(0, 0, 0, 0.1);
922
+ }
923
+
924
+ .md-readonly-toggle:focus-visible {
925
+ outline: 2px solid var(--md-readonly-accent, #4c8bf5);
926
+ outline-offset: 1px;
927
+ }
928
+
929
+ .md-readonly-toggle .md-readonly-toggle-indicator {
930
+ position: absolute;
931
+ top: 50%;
932
+ left: 3px;
933
+ width: 24px;
934
+ height: 24px;
935
+ border-radius: 50%;
936
+ background: var(--md-readonly-accent, #4c8bf5);
937
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
938
+ transform: translate(26px, -50%);
939
+ transition: background-color 0.12s ease, transform 0.16s ease;
940
+ }
941
+
942
+ .md-readonly-toggle .md-readonly-toggle-icon {
943
+ position: relative;
944
+ z-index: 1;
945
+ display: block;
946
+ width: 16px;
947
+ height: 16px;
948
+ color: var(--md-readonly-foreground, #444444);
949
+ opacity: 0.62;
950
+ pointer-events: none;
951
+ transition: color 0.12s ease, opacity 0.12s ease;
952
+ }
953
+
954
+ .md-readonly-toggle:not(.md-readonly-toggle-locked) .md-readonly-toggle-icon-editing,
955
+ .md-readonly-toggle.md-readonly-toggle-locked .md-readonly-toggle-icon-locked {
956
+ color: var(--md-readonly-accent-foreground, #ffffff);
957
+ opacity: 1;
958
+ }
959
+
960
+ .md-readonly-toggle.md-readonly-toggle-locked .md-readonly-toggle-indicator {
961
+ transform: translate(0, -50%);
962
+ }
963
+
964
+ @media (prefers-reduced-motion: reduce) {
965
+ .md-readonly-toggle,
966
+ .md-readonly-toggle .md-readonly-toggle-indicator,
967
+ .md-readonly-toggle .md-readonly-toggle-icon,
968
+ .md-readonly-toggle.md-readonly-toggle-shine::after {
969
+ animation: none;
970
+ transition: none;
971
+ }
972
+ }
973
+
974
+ @media (forced-colors: active) {
975
+ .md-readonly-toggle {
976
+ forced-color-adjust: none;
977
+ border-color: ButtonText;
978
+ background: ButtonFace;
979
+ color: ButtonText;
980
+ box-shadow: none;
981
+ }
982
+
983
+ .md-readonly-toggle::after {
984
+ display: none;
985
+ }
986
+
987
+ .md-readonly-toggle:hover {
988
+ border-color: Highlight;
989
+ background: ButtonFace;
990
+ box-shadow: none;
991
+ }
992
+
993
+ .md-readonly-toggle:focus-visible {
994
+ outline-color: Highlight;
995
+ }
996
+
997
+ .md-readonly-toggle .md-readonly-toggle-indicator {
998
+ background: Highlight;
999
+ box-shadow: none;
1000
+ }
1001
+
1002
+ .md-readonly-toggle .md-readonly-toggle-icon {
1003
+ color: ButtonText;
1004
+ opacity: 1;
1005
+ }
1006
+
1007
+ .md-readonly-toggle:not(.md-readonly-toggle-locked) .md-readonly-toggle-icon-editing,
1008
+ .md-readonly-toggle.md-readonly-toggle-locked .md-readonly-toggle-icon-locked {
1009
+ color: HighlightText;
1010
+ }
517
1011
  }
@@ -121,9 +121,14 @@
121
121
  --md-list-indent-step: 24px;
122
122
  }
123
123
 
124
+ .md-theme-default .md-table-wrapper {
125
+ /* Block spacing lives on the wrapper (the scroll/BFC box) so it collapses
126
+ with siblings as before; the inner table has no margin of its own. */
127
+ margin: 0.5em 0;
128
+ }
129
+
124
130
  .md-theme-default .md-table {
125
131
  border-collapse: collapse;
126
- margin: 0.5em 0;
127
132
  width: auto;
128
133
  }
129
134
 
@@ -201,6 +206,9 @@
201
206
  * box) so the right outline is a straight line. Absolute removes it from flow,
202
207
  * but it is the trailing node so no text shifts; the leading pipe still fixes
203
208
  * first-text X and the row stays one line tall, keeping the compare invariants.
209
+ * `top: auto` keeps the pipe on the content line (the shared editor.css rule
210
+ * also takes trailing glue out of flow); the previous `top: 0` floated it to
211
+ * the cell's top edge, misaligning it with the leading pipes on wrapping rows.
204
212
  */
205
213
  .md-theme-default .md-table.md-block-active td:last-child {
206
214
  position: relative;
@@ -209,7 +217,7 @@
209
217
  .md-theme-default .md-table.md-block-active td:last-child>.md-glue-tableCellGlue:last-child {
210
218
  position: absolute;
211
219
  right: 0;
212
- top: 0;
220
+ top: auto;
213
221
  }
214
222
 
215
223
  /*
@@ -220,7 +228,7 @@
220
228
  .md-theme-default .md-table.md-block-active .md-table-delimiter-row td:last-child>.md-marker-tableDelimiterClose {
221
229
  position: absolute;
222
230
  right: 0;
223
- top: 0;
231
+ top: auto;
224
232
  }
225
233
 
226
234
  .md-theme-default code {