@liminis/editor 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -72
- package/dist/app/editor/DocumentOutline.d.ts +11 -2
- package/dist/app/editor/DocumentOutline.js +5 -2
- package/dist/app/editor/Editor.js +17 -5
- package/dist/app/editor/documentOutlineHandle.d.ts +28 -0
- package/dist/app/editor/documentOutlineHandle.js +16 -1
- package/dist/app/editor/documentOutlineMarkdown.d.ts +35 -0
- package/dist/app/editor/documentOutlineMarkdown.js +82 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -1
- package/dist/styles.css +132 -0
- package/docs/decisions/adr-091.md +159 -0
- package/docs/decisions/adr-092.md +202 -0
- package/docs/decisions/adr-92-lexical-peer-range-policy.md +152 -0
- package/docs/decisions/adr-93-liminis-editor-defined-aliases.md +288 -0
- package/docs/editor-api.md +71 -0
- package/package.json +28 -26
package/README.md
CHANGED
|
@@ -43,12 +43,20 @@ produce a broken editor context — peering is what prevents both.
|
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
pnpm add react@^19 react-dom@^19 \
|
|
46
|
-
lexical@^0.
|
|
47
|
-
@lexical/code-prism@^0.
|
|
48
|
-
@lexical/mark@^0.
|
|
49
|
-
@lexical/selection@^0.
|
|
46
|
+
lexical@^0.49 @lexical/react@^0.49 @lexical/code@^0.49 \
|
|
47
|
+
@lexical/code-prism@^0.49 @lexical/link@^0.49 @lexical/list@^0.49 \
|
|
48
|
+
@lexical/mark@^0.49 @lexical/markdown@^0.49 @lexical/rich-text@^0.49 \
|
|
49
|
+
@lexical/selection@^0.49 @lexical/table@^0.49 @lexical/utils@^0.49
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
> **The Lexical peer ranges are single-caret, not a wide band.** Lexical is
|
|
53
|
+
> also `0.x`, where every minor is permitted to break — unlike React's range
|
|
54
|
+
> above, which spans an untested-but-stable range, the Lexical range names
|
|
55
|
+
> exactly the one minor this package is built and tested against. Expect it
|
|
56
|
+
> to move forward (e.g. to `^0.50`) only after this package has adopted and
|
|
57
|
+
> tested that release, not automatically at every upstream release. See
|
|
58
|
+
> `docs/decisions/adr-92-lexical-peer-range-policy.md`.
|
|
59
|
+
|
|
52
60
|
pnpm v8+ and npm v7+ install peers automatically, so in practice the first
|
|
53
61
|
command is usually enough.
|
|
54
62
|
|
|
@@ -199,7 +207,25 @@ Neither is a package defect, but only one of them tells you what is wrong.
|
|
|
199
207
|
The editor's colors, borders and a few layout metrics are all CSS custom
|
|
200
208
|
properties under a single package-owned vocabulary, `--liminis-editor-*`.
|
|
201
209
|
Every one of them resolves with no host configuration at all — the table
|
|
202
|
-
below exists so you can *override* a palette,
|
|
210
|
+
below exists so you can *override* a palette, and also so you can *consume*
|
|
211
|
+
one: every property this package declares with a value anywhere in
|
|
212
|
+
`styles.css` is part of this package's public API, readable directly by a
|
|
213
|
+
host, not only overridable. That now includes every `--liminis-editor-*` name
|
|
214
|
+
itself (see "Has a default" below) as well as the pre-`0.2.0` names that still
|
|
215
|
+
carry the real defaults (`--vscode-*`, `--slashmd-*`, `--checkbox-*`, and a
|
|
216
|
+
couple of standalone legacy names) — each `--liminis-editor-*` declaration is
|
|
217
|
+
an alias of its previous name (`--liminis-editor-x: var(--previous-name-x);`),
|
|
218
|
+
so both names resolve to the same value and either can be read directly
|
|
219
|
+
(ADR-93). (Legacy names like `--color-*` that appear only as an inner link in
|
|
220
|
+
a fallback chain, with no `:root`/`.dark` declaration of their own, are not
|
|
221
|
+
part of the defined set this paragraph describes — only the property that
|
|
222
|
+
*carries* the value is.) Some hosts read these definitions directly — mapping
|
|
223
|
+
their own design tokens onto them with `var(--x)` — rather than overriding
|
|
224
|
+
them. Renaming or removing a definition is therefore a breaking change, the
|
|
225
|
+
same as any other change to a supported API surface described under
|
|
226
|
+
"Versioning policy" above, and is distinct from renaming a *consumption* site
|
|
227
|
+
(covered separately in "Migrating from the old names" below) — this package's
|
|
228
|
+
CI guards against a definition disappearing unintentionally (ADR-092).
|
|
203
229
|
|
|
204
230
|
**Previous name** is the pre-`0.2.0` name this property replaced — the exact
|
|
205
231
|
name to look for if you're migrating a host that still overrides the old
|
|
@@ -207,82 +233,92 @@ vocabulary (`—` for a property that was never renamed). **Controls** is a
|
|
|
207
233
|
short description of what the token actually affects. **Kind** distinguishes
|
|
208
234
|
tokens that only affect appearance (`Cosmetic`) from the handful that also
|
|
209
235
|
affect layout (`Structural` — heading indents and the base font). **Has a
|
|
210
|
-
default** marks tokens with a
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
236
|
+
default** marks tokens declared with a value in `styles.css` directly; every
|
|
237
|
+
row below now reads "Yes" — each `--liminis-editor-*` name carries its own
|
|
238
|
+
`:root`/`.dark` declaration, an alias that reads its previous name via `var()`
|
|
239
|
+
(ADR-93), so a host reading `--liminis-editor-x` gets a real value with no
|
|
240
|
+
configuration, and a host overriding only the previous name at
|
|
241
|
+
`:root`/`document.documentElement` still wins, because the alias re-resolves
|
|
242
|
+
through that name's own `.dark`/`@media print` overrides at the point of use
|
|
243
|
+
(ADR-93 documents a scope limitation for an override applied to a descendant
|
|
244
|
+
element instead). See "Migrating from the old names" below.
|
|
216
245
|
|
|
217
246
|
This table is generated from `src/` by `node scripts/generate-theming-docs.mjs`
|
|
218
247
|
and checked for staleness in CI (`tests/theming-contract.test.ts`) — a `var(--x)`
|
|
219
248
|
added to the source without regenerating this block fails the build. Do not
|
|
220
249
|
hand-edit the rows between the markers below. See ADR-085 for how the
|
|
221
|
-
generation and drift guard work,
|
|
222
|
-
|
|
250
|
+
generation and drift guard work, ADR-087 for the rename and its compatibility
|
|
251
|
+
design, ADR-092 for the separate guard that protects the full *defined* set —
|
|
252
|
+
every token declared with a value in `styles.css`, per the checked-in baseline
|
|
253
|
+
(`scripts/lib/theming-defined-tokens-baseline.json`) — against an
|
|
254
|
+
unintentional rename or removal (run `pnpm docs:theming-baseline` after a
|
|
255
|
+
deliberate one), and ADR-93 for the `--liminis-editor-*` alias declarations
|
|
256
|
+
that make the defined set (baseline) a superset of this table's rows —
|
|
257
|
+
every consumed `--liminis-editor-*` name is now also a defined one — rather
|
|
258
|
+
than the disjoint sets they were before.
|
|
223
259
|
|
|
224
260
|
<!-- theming-tokens:start -->
|
|
225
261
|
| Custom property | Previous name | Controls | Kind | Has a default |
|
|
226
262
|
| --- | --- | --- | --- | --- |
|
|
227
|
-
| `--liminis-editor-background` | `--vscode-background` | Base background color of the editor surface and its popovers/menus. | Cosmetic |
|
|
228
|
-
| `--liminis-editor-bold-color` | `--slashmd-bold-color` | Text color of bold (`**text**`) markdown spans. | Cosmetic |
|
|
229
|
-
| `--liminis-editor-border` | `--vscode-border` | Default border color used throughout the editor chrome. | Cosmetic |
|
|
230
|
-
| `--liminis-editor-button-background` | `--vscode-button-background` | Background of primary action buttons (e.g. the correction panel's "Apply" button). | Cosmetic |
|
|
231
|
-
| `--liminis-editor-button-foreground` | `--vscode-button-foreground` | Text color of primary action buttons. | Cosmetic |
|
|
232
|
-
| `--liminis-editor-callout-caution-bg` | `--slashmd-callout-caution-bg` | Background of "caution" callout blocks. | Cosmetic |
|
|
233
|
-
| `--liminis-editor-callout-caution-border` | `--slashmd-callout-caution-border` | Left border accent of "caution" callout blocks. | Cosmetic |
|
|
234
|
-
| `--liminis-editor-callout-important-bg` | `--slashmd-callout-important-bg` | Background of "important" callout blocks. | Cosmetic |
|
|
235
|
-
| `--liminis-editor-callout-important-border` | `--slashmd-callout-important-border` | Left border accent of "important" callout blocks. | Cosmetic |
|
|
236
|
-
| `--liminis-editor-callout-note-bg` | `--slashmd-callout-note-bg` | Background of "note" callout blocks. | Cosmetic |
|
|
237
|
-
| `--liminis-editor-callout-note-border` | `--slashmd-callout-note-border` | Left border accent of "note" callout blocks. | Cosmetic |
|
|
238
|
-
| `--liminis-editor-callout-tip-bg` | `--slashmd-callout-tip-bg` | Background of "tip" callout blocks. | Cosmetic |
|
|
239
|
-
| `--liminis-editor-callout-tip-border` | `--slashmd-callout-tip-border` | Left border accent of "tip" callout blocks. | Cosmetic |
|
|
240
|
-
| `--liminis-editor-callout-warning-bg` | `--slashmd-callout-warning-bg` | Background of "warning" callout blocks. | Cosmetic |
|
|
241
|
-
| `--liminis-editor-callout-warning-border` | `--slashmd-callout-warning-border` | Left border accent of "warning" callout blocks. | Cosmetic |
|
|
242
|
-
| `--liminis-editor-checkbox-border` | `--checkbox-border` | Border color of unchecked task-list checkboxes. | Cosmetic |
|
|
243
|
-
| `--liminis-editor-code-bg` | `--vscode-code-bg` | Background of inline code spans and fenced code blocks. | Cosmetic |
|
|
244
|
-
| `--liminis-editor-errorForeground` | `--vscode-errorForeground` | Text color for error and validation messages. | Cosmetic |
|
|
245
|
-
| `--liminis-editor-external-link` | `--vscode-external-link` | Text color of links that point outside the document. | Cosmetic |
|
|
246
|
-
| `--liminis-editor-focus-border` | `--vscode-focus-border` | Border color of the toolbar link-input field when focused. | Cosmetic |
|
|
247
|
-
| `--liminis-editor-focusBorder` | `--vscode-focusBorder` | Color of the drag-and-drop position indicator while reordering blocks. | Cosmetic |
|
|
248
|
-
| `--liminis-editor-font-family` | `--vscode-font-family` | Base font family for the editor content. | Structural |
|
|
249
|
-
| `--liminis-editor-font-size` | `--vscode-font-size` | Base font size for the editor content. | Structural |
|
|
250
|
-
| `--liminis-editor-foreground` | `--vscode-foreground` | Default text color throughout the editor. | Cosmetic |
|
|
251
|
-
| `--liminis-editor-foreground-muted` | `--vscode-foreground-muted` | Hover border color for the frontmatter tray's raw-view toggle. | Cosmetic |
|
|
252
|
-
| `--liminis-editor-h1-color` | `--slashmd-h1-color` | Text color of level-1 (`#`) headings. | Cosmetic |
|
|
253
|
-
| `--liminis-editor-h1-indent` | `--slashmd-h1-indent` | Left margin of level-1 (`#`) headings. | Structural |
|
|
254
|
-
| `--liminis-editor-h2-color` | `--slashmd-h2-color` | Text color of level-2 (`##`) headings. | Cosmetic |
|
|
255
|
-
| `--liminis-editor-h2-indent` | `--slashmd-h2-indent` | Left margin of level-2 (`##`) headings. | Structural |
|
|
256
|
-
| `--liminis-editor-h3-color` | `--slashmd-h3-color` | Text color of level-3 (`###`) headings. | Cosmetic |
|
|
257
|
-
| `--liminis-editor-h3-indent` | `--slashmd-h3-indent` | Left margin of level-3 (`###`) headings. | Structural |
|
|
258
|
-
| `--liminis-editor-h4-color` | `--slashmd-h4-color` | Text color of level-4 (`####`) headings. | Cosmetic |
|
|
259
|
-
| `--liminis-editor-h4-indent` | `--slashmd-h4-indent` | Left margin of level-4 (`####`) headings. | Structural |
|
|
260
|
-
| `--liminis-editor-h5-color` | `--slashmd-h5-color` | Text color of level-5 (`#####`) headings. | Cosmetic |
|
|
261
|
-
| `--liminis-editor-h5-indent` | `--slashmd-h5-indent` | Left margin of level-5 (`#####`) headings. | Structural |
|
|
262
|
-
| `--liminis-editor-input-bg` | `--vscode-input-bg` | Background of the toolbar link-input field. | Cosmetic |
|
|
263
|
-
| `--liminis-editor-inputValidation-errorBackground` | `--vscode-inputValidation-errorBackground` | Background of inline validation-error messages. | Cosmetic |
|
|
264
|
-
| `--liminis-editor-italic-color` | `--slashmd-italic-color` | Text color of italic (`*text*`) markdown spans. | Cosmetic |
|
|
265
|
-
| `--liminis-editor-link` | `--vscode-link` | Text color of in-document links. | Cosmetic |
|
|
266
|
-
| `--liminis-editor-menu-background` | `--vscode-menu-background` | Background of context menus (block, selection and correction menus). | Cosmetic |
|
|
267
|
-
| `--liminis-editor-menu-border` | `--vscode-menu-border` | Border color of context menus. | Cosmetic |
|
|
268
|
-
| `--liminis-editor-menu-foreground` | `--vscode-menu-foreground` | Text color of context menu items. | Cosmetic |
|
|
269
|
-
| `--liminis-editor-menu-selectionBackground` | `--vscode-menu-selectionBackground` | Background of a hovered/selected context menu item. | Cosmetic |
|
|
270
|
-
| `--liminis-editor-menu-separatorBackground` | `--vscode-menu-separatorBackground` | Color of separator lines inside context menus. | Cosmetic |
|
|
271
|
-
| `--liminis-editor-muted-100` | `--color-muted-100` | Background of the C4 diagram layout-toggle buttons when inactive. | Cosmetic |
|
|
272
|
-
| `--liminis-editor-muted-foreground` | `--color-muted-foreground` | Icon/text color of the C4 diagram layout-toggle buttons when inactive. | Cosmetic |
|
|
273
|
-
| `--liminis-editor-notificationsInfoIcon-foreground` | `--vscode-notificationsInfoIcon-foreground` | Color of informational icons in inline notifications. | Cosmetic |
|
|
274
|
-
| `--liminis-editor-primary` | `--color-primary` | Icon/text color of the C4 diagram layout-toggle buttons when active. | Cosmetic |
|
|
275
|
-
| `--liminis-editor-primary-100` | `--color-primary-100` | Background of the C4 diagram layout-toggle buttons when active. | Cosmetic |
|
|
276
|
-
| `--liminis-editor-selection` | `--vscode-selection` | Background color of selected/highlighted text. | Cosmetic |
|
|
277
|
-
| `--liminis-editor-token-comment` | `--slashmd-token-comment` | Syntax-highlight color for comments in fenced code blocks. | Cosmetic |
|
|
278
|
-
| `--liminis-editor-token-function` | `--slashmd-token-function` | Syntax-highlight color for function names in fenced code blocks. | Cosmetic |
|
|
279
|
-
| `--liminis-editor-token-keyword` | `--slashmd-token-keyword` | Syntax-highlight color for keywords in fenced code blocks. | Cosmetic |
|
|
280
|
-
| `--liminis-editor-token-operator` | `--slashmd-token-operator` | Syntax-highlight color for operators in fenced code blocks. | Cosmetic |
|
|
281
|
-
| `--liminis-editor-token-property` | `--slashmd-token-property` | Syntax-highlight color for object properties in fenced code blocks. | Cosmetic |
|
|
282
|
-
| `--liminis-editor-token-punctuation` | `--slashmd-token-punctuation` | Syntax-highlight color for punctuation in fenced code blocks. | Cosmetic |
|
|
283
|
-
| `--liminis-editor-token-selector` | `--slashmd-token-selector` | Syntax-highlight color for CSS selectors in fenced code blocks. | Cosmetic |
|
|
284
|
-
| `--liminis-editor-token-variable` | `--slashmd-token-variable` | Syntax-highlight color for variables in fenced code blocks. | Cosmetic |
|
|
285
|
-
| `--liminis-editor-toolbar-hoverBackground` | `--vscode-toolbar-hoverBackground` | Background of a toolbar button on hover. | Cosmetic |
|
|
263
|
+
| `--liminis-editor-background` | `--vscode-background` | Base background color of the editor surface and its popovers/menus. | Cosmetic | Yes |
|
|
264
|
+
| `--liminis-editor-bold-color` | `--slashmd-bold-color` | Text color of bold (`**text**`) markdown spans. | Cosmetic | Yes |
|
|
265
|
+
| `--liminis-editor-border` | `--vscode-border` | Default border color used throughout the editor chrome. | Cosmetic | Yes |
|
|
266
|
+
| `--liminis-editor-button-background` | `--vscode-button-background` | Background of primary action buttons (e.g. the correction panel's "Apply" button). | Cosmetic | Yes |
|
|
267
|
+
| `--liminis-editor-button-foreground` | `--vscode-button-foreground` | Text color of primary action buttons. | Cosmetic | Yes |
|
|
268
|
+
| `--liminis-editor-callout-caution-bg` | `--slashmd-callout-caution-bg` | Background of "caution" callout blocks. | Cosmetic | Yes |
|
|
269
|
+
| `--liminis-editor-callout-caution-border` | `--slashmd-callout-caution-border` | Left border accent of "caution" callout blocks. | Cosmetic | Yes |
|
|
270
|
+
| `--liminis-editor-callout-important-bg` | `--slashmd-callout-important-bg` | Background of "important" callout blocks. | Cosmetic | Yes |
|
|
271
|
+
| `--liminis-editor-callout-important-border` | `--slashmd-callout-important-border` | Left border accent of "important" callout blocks. | Cosmetic | Yes |
|
|
272
|
+
| `--liminis-editor-callout-note-bg` | `--slashmd-callout-note-bg` | Background of "note" callout blocks. | Cosmetic | Yes |
|
|
273
|
+
| `--liminis-editor-callout-note-border` | `--slashmd-callout-note-border` | Left border accent of "note" callout blocks. | Cosmetic | Yes |
|
|
274
|
+
| `--liminis-editor-callout-tip-bg` | `--slashmd-callout-tip-bg` | Background of "tip" callout blocks. | Cosmetic | Yes |
|
|
275
|
+
| `--liminis-editor-callout-tip-border` | `--slashmd-callout-tip-border` | Left border accent of "tip" callout blocks. | Cosmetic | Yes |
|
|
276
|
+
| `--liminis-editor-callout-warning-bg` | `--slashmd-callout-warning-bg` | Background of "warning" callout blocks. | Cosmetic | Yes |
|
|
277
|
+
| `--liminis-editor-callout-warning-border` | `--slashmd-callout-warning-border` | Left border accent of "warning" callout blocks. | Cosmetic | Yes |
|
|
278
|
+
| `--liminis-editor-checkbox-border` | `--checkbox-border` | Border color of unchecked task-list checkboxes. | Cosmetic | Yes |
|
|
279
|
+
| `--liminis-editor-code-bg` | `--vscode-code-bg` | Background of inline code spans and fenced code blocks. | Cosmetic | Yes |
|
|
280
|
+
| `--liminis-editor-errorForeground` | `--vscode-errorForeground` | Text color for error and validation messages. | Cosmetic | Yes |
|
|
281
|
+
| `--liminis-editor-external-link` | `--vscode-external-link` | Text color of links that point outside the document. | Cosmetic | Yes |
|
|
282
|
+
| `--liminis-editor-focus-border` | `--vscode-focus-border` | Border color of the toolbar link-input field when focused. | Cosmetic | Yes |
|
|
283
|
+
| `--liminis-editor-focusBorder` | `--vscode-focusBorder` | Color of the drag-and-drop position indicator while reordering blocks. | Cosmetic | Yes |
|
|
284
|
+
| `--liminis-editor-font-family` | `--vscode-font-family` | Base font family for the editor content. | Structural | Yes |
|
|
285
|
+
| `--liminis-editor-font-size` | `--vscode-font-size` | Base font size for the editor content. | Structural | Yes |
|
|
286
|
+
| `--liminis-editor-foreground` | `--vscode-foreground` | Default text color throughout the editor. | Cosmetic | Yes |
|
|
287
|
+
| `--liminis-editor-foreground-muted` | `--vscode-foreground-muted` | Hover border color for the frontmatter tray's raw-view toggle. | Cosmetic | Yes |
|
|
288
|
+
| `--liminis-editor-h1-color` | `--slashmd-h1-color` | Text color of level-1 (`#`) headings. | Cosmetic | Yes |
|
|
289
|
+
| `--liminis-editor-h1-indent` | `--slashmd-h1-indent` | Left margin of level-1 (`#`) headings. | Structural | Yes |
|
|
290
|
+
| `--liminis-editor-h2-color` | `--slashmd-h2-color` | Text color of level-2 (`##`) headings. | Cosmetic | Yes |
|
|
291
|
+
| `--liminis-editor-h2-indent` | `--slashmd-h2-indent` | Left margin of level-2 (`##`) headings. | Structural | Yes |
|
|
292
|
+
| `--liminis-editor-h3-color` | `--slashmd-h3-color` | Text color of level-3 (`###`) headings. | Cosmetic | Yes |
|
|
293
|
+
| `--liminis-editor-h3-indent` | `--slashmd-h3-indent` | Left margin of level-3 (`###`) headings. | Structural | Yes |
|
|
294
|
+
| `--liminis-editor-h4-color` | `--slashmd-h4-color` | Text color of level-4 (`####`) headings. | Cosmetic | Yes |
|
|
295
|
+
| `--liminis-editor-h4-indent` | `--slashmd-h4-indent` | Left margin of level-4 (`####`) headings. | Structural | Yes |
|
|
296
|
+
| `--liminis-editor-h5-color` | `--slashmd-h5-color` | Text color of level-5 (`#####`) headings. | Cosmetic | Yes |
|
|
297
|
+
| `--liminis-editor-h5-indent` | `--slashmd-h5-indent` | Left margin of level-5 (`#####`) headings. | Structural | Yes |
|
|
298
|
+
| `--liminis-editor-input-bg` | `--vscode-input-bg` | Background of the toolbar link-input field. | Cosmetic | Yes |
|
|
299
|
+
| `--liminis-editor-inputValidation-errorBackground` | `--vscode-inputValidation-errorBackground` | Background of inline validation-error messages. | Cosmetic | Yes |
|
|
300
|
+
| `--liminis-editor-italic-color` | `--slashmd-italic-color` | Text color of italic (`*text*`) markdown spans. | Cosmetic | Yes |
|
|
301
|
+
| `--liminis-editor-link` | `--vscode-link` | Text color of in-document links. | Cosmetic | Yes |
|
|
302
|
+
| `--liminis-editor-menu-background` | `--vscode-menu-background` | Background of context menus (block, selection and correction menus). | Cosmetic | Yes |
|
|
303
|
+
| `--liminis-editor-menu-border` | `--vscode-menu-border` | Border color of context menus. | Cosmetic | Yes |
|
|
304
|
+
| `--liminis-editor-menu-foreground` | `--vscode-menu-foreground` | Text color of context menu items. | Cosmetic | Yes |
|
|
305
|
+
| `--liminis-editor-menu-selectionBackground` | `--vscode-menu-selectionBackground` | Background of a hovered/selected context menu item. | Cosmetic | Yes |
|
|
306
|
+
| `--liminis-editor-menu-separatorBackground` | `--vscode-menu-separatorBackground` | Color of separator lines inside context menus. | Cosmetic | Yes |
|
|
307
|
+
| `--liminis-editor-muted-100` | `--color-muted-100` | Background of the C4 diagram layout-toggle buttons when inactive. | Cosmetic | Yes |
|
|
308
|
+
| `--liminis-editor-muted-foreground` | `--color-muted-foreground` | Icon/text color of the C4 diagram layout-toggle buttons when inactive. | Cosmetic | Yes |
|
|
309
|
+
| `--liminis-editor-notificationsInfoIcon-foreground` | `--vscode-notificationsInfoIcon-foreground` | Color of informational icons in inline notifications. | Cosmetic | Yes |
|
|
310
|
+
| `--liminis-editor-primary` | `--color-primary` | Icon/text color of the C4 diagram layout-toggle buttons when active. | Cosmetic | Yes |
|
|
311
|
+
| `--liminis-editor-primary-100` | `--color-primary-100` | Background of the C4 diagram layout-toggle buttons when active. | Cosmetic | Yes |
|
|
312
|
+
| `--liminis-editor-selection` | `--vscode-selection` | Background color of selected/highlighted text. | Cosmetic | Yes |
|
|
313
|
+
| `--liminis-editor-token-comment` | `--slashmd-token-comment` | Syntax-highlight color for comments in fenced code blocks. | Cosmetic | Yes |
|
|
314
|
+
| `--liminis-editor-token-function` | `--slashmd-token-function` | Syntax-highlight color for function names in fenced code blocks. | Cosmetic | Yes |
|
|
315
|
+
| `--liminis-editor-token-keyword` | `--slashmd-token-keyword` | Syntax-highlight color for keywords in fenced code blocks. | Cosmetic | Yes |
|
|
316
|
+
| `--liminis-editor-token-operator` | `--slashmd-token-operator` | Syntax-highlight color for operators in fenced code blocks. | Cosmetic | Yes |
|
|
317
|
+
| `--liminis-editor-token-property` | `--slashmd-token-property` | Syntax-highlight color for object properties in fenced code blocks. | Cosmetic | Yes |
|
|
318
|
+
| `--liminis-editor-token-punctuation` | `--slashmd-token-punctuation` | Syntax-highlight color for punctuation in fenced code blocks. | Cosmetic | Yes |
|
|
319
|
+
| `--liminis-editor-token-selector` | `--slashmd-token-selector` | Syntax-highlight color for CSS selectors in fenced code blocks. | Cosmetic | Yes |
|
|
320
|
+
| `--liminis-editor-token-variable` | `--slashmd-token-variable` | Syntax-highlight color for variables in fenced code blocks. | Cosmetic | Yes |
|
|
321
|
+
| `--liminis-editor-toolbar-hoverBackground` | `--vscode-toolbar-hoverBackground` | Background of a toolbar button on hover. | Cosmetic | Yes |
|
|
286
322
|
<!-- theming-tokens:end -->
|
|
287
323
|
|
|
288
324
|
**A worked example.** To match a host's own palette, override a subset of
|
|
@@ -326,6 +362,25 @@ prefix swap (`--checkbox-border` became `--liminis-editor-checkbox-border`,
|
|
|
326
362
|
not `--liminis-editor-border`, to avoid colliding with `--vscode-border`'s
|
|
327
363
|
new name), so look it up rather than deriving it.
|
|
328
364
|
|
|
365
|
+
Reading, not just overriding, is also migratable: a host that currently reads
|
|
366
|
+
a legacy name directly (e.g. mapping its own design tokens onto
|
|
367
|
+
`--vscode-foreground`) can switch that read to the corresponding
|
|
368
|
+
`--liminis-editor-*` name and get an identical value, **as long as it isn't
|
|
369
|
+
also setting the `--liminis-editor-*` name itself** — a directly-set
|
|
370
|
+
`--liminis-editor-*` value always wins over the previous name's own value,
|
|
371
|
+
by ordinary cascade precedence (see "Has a default" below), so the two reads
|
|
372
|
+
are identical only while the host relies solely on the previous name. Until
|
|
373
|
+
ADR-93, `--liminis-editor-*` names were only ever *consumed*, never
|
|
374
|
+
themselves declared with a value — there was nothing for such a host to
|
|
375
|
+
migrate its reads to. Every `--liminis-editor-*` name now carries its own
|
|
376
|
+
`:root`/`.dark` alias declaration that resolves to the previous name's own
|
|
377
|
+
value in that legacy-only case, so the read migration is a like-for-like
|
|
378
|
+
swap with no observable difference (see verveguy/zusammen#129, the migration
|
|
379
|
+
this addresses). This guarantee holds for a previous-name override applied
|
|
380
|
+
at `:root`/`document.documentElement`, the standard integration point; see
|
|
381
|
+
ADR-93 for a documented limitation when a host scopes its override to a
|
|
382
|
+
descendant element instead.
|
|
383
|
+
|
|
329
384
|
## Annotations
|
|
330
385
|
|
|
331
386
|
An annotation is a range-anchored marker over document text whose anchor
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import type { DocumentOutlineHandle } from './documentOutlineHandle.js';
|
|
1
|
+
import type { DocumentOutlineHandle, OutlineEntry } from './documentOutlineHandle.js';
|
|
2
2
|
export interface DocumentOutlineProps {
|
|
3
3
|
/** The controller created by `createDocumentOutlineHandle()` and also passed to `<Editor documentOutlineHandle={…}>`. */
|
|
4
4
|
handle: DocumentOutlineHandle;
|
|
5
5
|
/** Additional class name on the outer `<nav>`, for host placement/layout (FR-008). */
|
|
6
6
|
className?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Fires with the clicked entry in addition to `handle.scrollToHeading`.
|
|
9
|
+
* `handle.scrollToHeading` no-ops when no Lexical editor is connected
|
|
10
|
+
* (the markdown-derived/raw-mode path, issue #84) — a raw-mode host has no
|
|
11
|
+
* other way to react to a click, since it doesn't render the entry list
|
|
12
|
+
* itself, so this is how it navigates its own (non-Lexical) editor via
|
|
13
|
+
* `entry.line`.
|
|
14
|
+
*/
|
|
15
|
+
onEntrySelect?: (entry: OutlineEntry) => void;
|
|
7
16
|
}
|
|
8
17
|
/**
|
|
9
18
|
* Renders a navigable "on this page" heading outline for one editor
|
|
@@ -14,4 +23,4 @@ export interface DocumentOutlineProps {
|
|
|
14
23
|
* placement, width-gating, and collapse/persistence are the host's concern
|
|
15
24
|
* (FR-008) — this component only renders the list and reacts to the handle.
|
|
16
25
|
*/
|
|
17
|
-
export declare function DocumentOutline({ handle, className }: DocumentOutlineProps): import("react").JSX.Element | null;
|
|
26
|
+
export declare function DocumentOutline({ handle, className, onEntrySelect }: DocumentOutlineProps): import("react").JSX.Element | null;
|
|
@@ -9,7 +9,7 @@ import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';
|
|
|
9
9
|
* placement, width-gating, and collapse/persistence are the host's concern
|
|
10
10
|
* (FR-008) — this component only renders the list and reacts to the handle.
|
|
11
11
|
*/
|
|
12
|
-
export function DocumentOutline({ handle, className }) {
|
|
12
|
+
export function DocumentOutline({ handle, className, onEntrySelect }) {
|
|
13
13
|
// Wrapped rather than passed directly: `handle.subscribe`/`handle.getSnapshot`
|
|
14
14
|
// are unbound method references off a plain interface value.
|
|
15
15
|
const subscribe = useCallback((onStoreChange) => handle.subscribe(onStoreChange), [handle]);
|
|
@@ -27,6 +27,9 @@ export function DocumentOutline({ handle, className }) {
|
|
|
27
27
|
return null;
|
|
28
28
|
return (_jsx("nav", { className: className ? `editor-outline ${className}` : 'editor-outline', "aria-label": "Document outline", children: _jsx("ul", { className: "editor-outline-list", ref: listRef, children: snapshot.entries.map((entry) => {
|
|
29
29
|
const isActive = entry.index === snapshot.activeIndex;
|
|
30
|
-
return (_jsx("li", { "data-outline-index": entry.index, "data-outline-level": entry.level, className: isActive ? 'editor-outline-item editor-outline-item-active' : 'editor-outline-item', children: _jsxs("button", { type: "button", className: "editor-outline-item-button", "aria-current": isActive ? 'location' : undefined, onClick: () =>
|
|
30
|
+
return (_jsx("li", { "data-outline-index": entry.index, "data-outline-level": entry.level, className: isActive ? 'editor-outline-item editor-outline-item-active' : 'editor-outline-item', children: _jsxs("button", { type: "button", className: "editor-outline-item-button", "aria-current": isActive ? 'location' : undefined, onClick: () => {
|
|
31
|
+
handle.scrollToHeading(entry.index);
|
|
32
|
+
onEntrySelect?.(entry);
|
|
33
|
+
}, children: [_jsx("span", { className: "editor-outline-indicator", "aria-hidden": "true" }), _jsx("span", { className: "editor-outline-item-text", children: entry.text })] }) }, entry.index));
|
|
31
34
|
}) }) }));
|
|
32
35
|
}
|
|
@@ -240,6 +240,20 @@ function CursorRestorePlugin({ contentVersion, cursorToRestoreRef, }) {
|
|
|
240
240
|
const cursorToRestore = cursorToRestoreRef.current;
|
|
241
241
|
if (!cursorToRestore)
|
|
242
242
|
return;
|
|
243
|
+
// Only restore the caret if the user was actually in the editor when the
|
|
244
|
+
// reload arrived. A content reload is not always the user's doing: an
|
|
245
|
+
// agent writing the open file reaches here too, and re-focusing then pulls
|
|
246
|
+
// the caret out of whatever the user is typing in — a chat prompt beside
|
|
247
|
+
// the editor, say — mid-sentence. Restoring a caret nobody is looking at
|
|
248
|
+
// buys nothing, so an unfocused editor is left entirely alone (no
|
|
249
|
+
// selection write either, so reconciliation cannot move focus as a side
|
|
250
|
+
// effect). Same rule as `AutoFocusPlugin`: never take focus unasked.
|
|
251
|
+
// Read synchronously, before the update below can perturb focus.
|
|
252
|
+
const rootElement = editor.getRootElement();
|
|
253
|
+
const hadFocus = rootElement !== null &&
|
|
254
|
+
(rootElement === document.activeElement || rootElement.contains(document.activeElement));
|
|
255
|
+
if (!hadFocus)
|
|
256
|
+
return;
|
|
243
257
|
console.log('[CursorRestorePlugin] Restoring cursor', {
|
|
244
258
|
contentVersion,
|
|
245
259
|
offset: cursorToRestore.offset,
|
|
@@ -262,11 +276,9 @@ function CursorRestorePlugin({ contentVersion, cursorToRestoreRef, }) {
|
|
|
262
276
|
console.log('[CursorRestorePlugin] Selection restored at offset', newOffset);
|
|
263
277
|
}
|
|
264
278
|
});
|
|
265
|
-
// Re-focus the editor
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
rootElement.focus({ preventScroll: true });
|
|
269
|
-
}
|
|
279
|
+
// Re-focus the editor. Reached only when it already had focus above, so
|
|
280
|
+
// this restores focus the reload disturbed rather than taking it.
|
|
281
|
+
rootElement.focus({ preventScroll: true });
|
|
270
282
|
});
|
|
271
283
|
}, [editor, contentVersion, cursorToRestoreRef]);
|
|
272
284
|
return null;
|
|
@@ -23,6 +23,13 @@ export interface OutlineEntry {
|
|
|
23
23
|
level: 1 | 2 | 3 | 4 | 5;
|
|
24
24
|
/** Heading text with inline content (including inline code) reduced to plain text. */
|
|
25
25
|
text: string;
|
|
26
|
+
/**
|
|
27
|
+
* 1-based source line of the heading, matching mdast's
|
|
28
|
+
* `position.start.line`. Only populated on markdown-derived entries (see
|
|
29
|
+
* `documentOutlineMarkdown.ts`) — the live-Lexical path has no source-line
|
|
30
|
+
* mapping to supply, so it leaves this unset.
|
|
31
|
+
*/
|
|
32
|
+
line?: number;
|
|
26
33
|
}
|
|
27
34
|
export interface DocumentOutlineSnapshot {
|
|
28
35
|
entries: OutlineEntry[];
|
|
@@ -53,6 +60,21 @@ export interface DocumentOutlineHandle {
|
|
|
53
60
|
getSnapshot(): DocumentOutlineSnapshot;
|
|
54
61
|
/** Scroll the editor to the heading at `index`. No-ops if no editor is currently connected, or `index` is out of range. */
|
|
55
62
|
scrollToHeading(index: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* Derive entries from `markdown` (see `deriveOutlineFromMarkdown` in
|
|
65
|
+
* `documentOutlineMarkdown.ts`) and publish them — the raw-mode
|
|
66
|
+
* counterpart to `OutlinePlugin` feeding entries from live Lexical state.
|
|
67
|
+
* Call this on every content change in a host with no mounted Lexical
|
|
68
|
+
* editor (issue #84).
|
|
69
|
+
*/
|
|
70
|
+
publishFromMarkdown(markdown: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve `line` against the entries from the most recent
|
|
73
|
+
* `publishFromMarkdown` call and publish the corresponding active index —
|
|
74
|
+
* the raw-mode counterpart to `OutlinePlugin`'s scroll-spy. Pass `null`
|
|
75
|
+
* for "no active heading yet" (e.g. before first scroll).
|
|
76
|
+
*/
|
|
77
|
+
setActiveLine(line: number | null): void;
|
|
56
78
|
}
|
|
57
79
|
/**
|
|
58
80
|
* The concrete object `createDocumentOutlineHandle()` returns. Consumers only
|
|
@@ -66,9 +88,15 @@ export declare class OutlineHandleImpl implements DocumentOutlineHandle {
|
|
|
66
88
|
private snapshot;
|
|
67
89
|
private readonly listeners;
|
|
68
90
|
private scrollImpl;
|
|
91
|
+
/** Entries from the most recent `publishFromMarkdown` call, so `setActiveLine` can resolve against them independently. */
|
|
92
|
+
private markdownEntries;
|
|
93
|
+
/** Last line passed to `setActiveLine`, kept so a subsequent `publishFromMarkdown` re-resolves against it. */
|
|
94
|
+
private lastActiveLine;
|
|
69
95
|
subscribe: (onStoreChange: () => void) => (() => void);
|
|
70
96
|
getSnapshot: () => DocumentOutlineSnapshot;
|
|
71
97
|
scrollToHeading: (index: number) => void;
|
|
98
|
+
publishFromMarkdown: (markdown: string) => void;
|
|
99
|
+
setActiveLine: (line: number | null) => void;
|
|
72
100
|
/** Called by `OutlinePlugin` while its editor is mounted. */
|
|
73
101
|
connect(scrollImpl: (index: number) => void): void;
|
|
74
102
|
/** Called by `OutlinePlugin` on unmount, so a stale handle no-ops rather than scrolling a torn-down editor. */
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* instance — it is naturally scoped to that instance, so mounting more than
|
|
12
12
|
* one editor at once never aggregates headings across them.
|
|
13
13
|
*/
|
|
14
|
+
import { deriveOutlineFromMarkdown, resolveActiveOutlineIndex } from './documentOutlineMarkdown.js';
|
|
14
15
|
/**
|
|
15
16
|
* Not exported, so nothing outside this module can reference it. Its only
|
|
16
17
|
* purpose is to make `DocumentOutlineHandle` nominal instead of structural:
|
|
@@ -30,7 +31,7 @@ function snapshotsEqual(a, b) {
|
|
|
30
31
|
for (let i = 0; i < a.entries.length; i++) {
|
|
31
32
|
const x = a.entries[i];
|
|
32
33
|
const y = b.entries[i];
|
|
33
|
-
if (x.index !== y.index || x.level !== y.level || x.text !== y.text)
|
|
34
|
+
if (x.index !== y.index || x.level !== y.level || x.text !== y.text || x.line !== y.line)
|
|
34
35
|
return false;
|
|
35
36
|
}
|
|
36
37
|
return true;
|
|
@@ -47,6 +48,10 @@ export class OutlineHandleImpl {
|
|
|
47
48
|
snapshot = EMPTY_SNAPSHOT;
|
|
48
49
|
listeners = new Set();
|
|
49
50
|
scrollImpl = null;
|
|
51
|
+
/** Entries from the most recent `publishFromMarkdown` call, so `setActiveLine` can resolve against them independently. */
|
|
52
|
+
markdownEntries = [];
|
|
53
|
+
/** Last line passed to `setActiveLine`, kept so a subsequent `publishFromMarkdown` re-resolves against it. */
|
|
54
|
+
lastActiveLine = null;
|
|
50
55
|
subscribe = (onStoreChange) => {
|
|
51
56
|
this.listeners.add(onStoreChange);
|
|
52
57
|
return () => {
|
|
@@ -57,6 +62,16 @@ export class OutlineHandleImpl {
|
|
|
57
62
|
scrollToHeading = (index) => {
|
|
58
63
|
this.scrollImpl?.(index);
|
|
59
64
|
};
|
|
65
|
+
publishFromMarkdown = (markdown) => {
|
|
66
|
+
this.markdownEntries = deriveOutlineFromMarkdown(markdown);
|
|
67
|
+
const activeIndex = resolveActiveOutlineIndex(this.markdownEntries, this.lastActiveLine);
|
|
68
|
+
this.publish({ entries: this.markdownEntries, activeIndex });
|
|
69
|
+
};
|
|
70
|
+
setActiveLine = (line) => {
|
|
71
|
+
this.lastActiveLine = line;
|
|
72
|
+
const activeIndex = resolveActiveOutlineIndex(this.markdownEntries, line);
|
|
73
|
+
this.publish({ entries: this.markdownEntries, activeIndex });
|
|
74
|
+
};
|
|
60
75
|
/** Called by `OutlinePlugin` while its editor is mounted. */
|
|
61
76
|
connect(scrollImpl) {
|
|
62
77
|
this.scrollImpl = scrollImpl;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derives `OutlineEntry` entries directly from markdown text, independent of
|
|
3
|
+
* any Lexical editor state — the raw-mode counterpart to `OutlinePlugin`'s
|
|
4
|
+
* live-tree derivation (issue #84). A host with no mounted Lexical editor
|
|
5
|
+
* (liminis#1022's raw markdown mode) has no heading DOM/tree to walk, but it
|
|
6
|
+
* does have the markdown text and, per this issue's spec, is expected to know
|
|
7
|
+
* which source line is currently in view — this module turns both into the
|
|
8
|
+
* same `OutlineEntry` shape `OutlinePlugin` produces, plus a source line
|
|
9
|
+
* neither Lexical nor the DOM can supply.
|
|
10
|
+
*/
|
|
11
|
+
import type { OutlineEntry } from './documentOutlineHandle.js';
|
|
12
|
+
/**
|
|
13
|
+
* Derive `OutlineEntry[]` from a markdown string alone — no Lexical editor,
|
|
14
|
+
* no DOM. Mirrors `OutlinePlugin.readHeadingEntries()`: only top-level nodes
|
|
15
|
+
* are inspected (headings nested inside blockquotes/lists are not walked
|
|
16
|
+
* into, matching `$getRoot().getChildren()`), levels are restricted to
|
|
17
|
+
* H1–H5 (mdast allows depth 6; excluded exactly like the Lexical path
|
|
18
|
+
* excludes H6), and identity is positional (`index`), not by `text`.
|
|
19
|
+
*
|
|
20
|
+
* Unlike the Lexical path, an empty-title heading is still included — see
|
|
21
|
+
* ADR for why this issue does not replicate the pre-extraction app's
|
|
22
|
+
* `if (!title) continue`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function deriveOutlineFromMarkdown(markdown: string): OutlineEntry[];
|
|
25
|
+
/**
|
|
26
|
+
* Resolve which entry is "active" for a supplied source `line` — the
|
|
27
|
+
* raw-mode counterpart to `OutlinePlugin`'s viewport-based scroll-spy. An
|
|
28
|
+
* entry is active from its own line up to (but not including) the next
|
|
29
|
+
* entry's line, matching the spec's literal FR-006 semantics (no tolerance/
|
|
30
|
+
* fudge factor — that is host-side scroll-spy tuning, per the plan).
|
|
31
|
+
*
|
|
32
|
+
* Returns `null` when `line` is `null` (no active heading yet, e.g. before
|
|
33
|
+
* first scroll) or falls before the first heading.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveActiveOutlineIndex(entries: OutlineEntry[], line: number | null): number | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derives `OutlineEntry` entries directly from markdown text, independent of
|
|
3
|
+
* any Lexical editor state — the raw-mode counterpart to `OutlinePlugin`'s
|
|
4
|
+
* live-tree derivation (issue #84). A host with no mounted Lexical editor
|
|
5
|
+
* (liminis#1022's raw markdown mode) has no heading DOM/tree to walk, but it
|
|
6
|
+
* does have the markdown text and, per this issue's spec, is expected to know
|
|
7
|
+
* which source line is currently in view — this module turns both into the
|
|
8
|
+
* same `OutlineEntry` shape `OutlinePlugin` produces, plus a source line
|
|
9
|
+
* neither Lexical nor the DOM can supply.
|
|
10
|
+
*/
|
|
11
|
+
import { parseMarkdown, isHeading, isText, isInlineCode } from '../../markdown/parse.js';
|
|
12
|
+
/**
|
|
13
|
+
* Recursively reduces inline content to plain text, matching Lexical's
|
|
14
|
+
* `getTextContent()` semantics: every inline node's text is concatenated in
|
|
15
|
+
* order with no separator, bottoming out at `text`/`inlineCode` leaves. This
|
|
16
|
+
* mirrors `OutlinePlugin.test.tsx`'s inline-code fixture (`'See ' + 'inline
|
|
17
|
+
* code'` → `'See inline code'`) rather than enumerating every mdast inline
|
|
18
|
+
* type by name.
|
|
19
|
+
*/
|
|
20
|
+
function extractText(node) {
|
|
21
|
+
if (isText(node) || isInlineCode(node))
|
|
22
|
+
return node.value;
|
|
23
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
24
|
+
return node.children.map((child) => extractText(child)).join('');
|
|
25
|
+
}
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Derive `OutlineEntry[]` from a markdown string alone — no Lexical editor,
|
|
30
|
+
* no DOM. Mirrors `OutlinePlugin.readHeadingEntries()`: only top-level nodes
|
|
31
|
+
* are inspected (headings nested inside blockquotes/lists are not walked
|
|
32
|
+
* into, matching `$getRoot().getChildren()`), levels are restricted to
|
|
33
|
+
* H1–H5 (mdast allows depth 6; excluded exactly like the Lexical path
|
|
34
|
+
* excludes H6), and identity is positional (`index`), not by `text`.
|
|
35
|
+
*
|
|
36
|
+
* Unlike the Lexical path, an empty-title heading is still included — see
|
|
37
|
+
* ADR for why this issue does not replicate the pre-extraction app's
|
|
38
|
+
* `if (!title) continue`.
|
|
39
|
+
*/
|
|
40
|
+
export function deriveOutlineFromMarkdown(markdown) {
|
|
41
|
+
const { root } = parseMarkdown(markdown);
|
|
42
|
+
const entries = [];
|
|
43
|
+
let index = 0;
|
|
44
|
+
for (const node of root.children) {
|
|
45
|
+
if (!isHeading(node))
|
|
46
|
+
continue;
|
|
47
|
+
if (node.depth < 1 || node.depth > 5)
|
|
48
|
+
continue;
|
|
49
|
+
const text = node.children.map((child) => extractText(child)).join('');
|
|
50
|
+
entries.push({
|
|
51
|
+
index: index++,
|
|
52
|
+
level: node.depth,
|
|
53
|
+
text,
|
|
54
|
+
line: node.position?.start?.line,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return entries;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve which entry is "active" for a supplied source `line` — the
|
|
61
|
+
* raw-mode counterpart to `OutlinePlugin`'s viewport-based scroll-spy. An
|
|
62
|
+
* entry is active from its own line up to (but not including) the next
|
|
63
|
+
* entry's line, matching the spec's literal FR-006 semantics (no tolerance/
|
|
64
|
+
* fudge factor — that is host-side scroll-spy tuning, per the plan).
|
|
65
|
+
*
|
|
66
|
+
* Returns `null` when `line` is `null` (no active heading yet, e.g. before
|
|
67
|
+
* first scroll) or falls before the first heading.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveActiveOutlineIndex(entries, line) {
|
|
70
|
+
if (line === null)
|
|
71
|
+
return null;
|
|
72
|
+
let active = null;
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (entry.line === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
if (entry.line <= line)
|
|
77
|
+
active = entry.index;
|
|
78
|
+
else
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
return active;
|
|
82
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { DocumentOutline } from './app/editor/DocumentOutline.js';
|
|
|
22
22
|
export type { DocumentOutlineProps } from './app/editor/DocumentOutline.js';
|
|
23
23
|
export { createDocumentOutlineHandle } from './app/editor/documentOutlineHandle.js';
|
|
24
24
|
export type { DocumentOutlineHandle, DocumentOutlineSnapshot, OutlineEntry, } from './app/editor/documentOutlineHandle.js';
|
|
25
|
+
export { deriveOutlineFromMarkdown, resolveActiveOutlineIndex } from './app/editor/documentOutlineMarkdown.js';
|
|
25
26
|
export { OPEN_ANNOTATION_COMPOSER_COMMAND } from './app/editor/annotationCommands.js';
|
|
26
27
|
export type { AnnotationCreateEvent } from './app/editor/AnnotationPlugin.js';
|
|
27
28
|
export type { Annotation, AnnotationKind, AnnotationKindConfig, AnnotationKindConfigs, AnnotationCreateAffordance, AnnotationMarkerStyle, AnnotationPresentation, AnnotationEditorHandle, MarkerTarget, } from './annotations/types.js';
|
package/dist/index.js
CHANGED
|
@@ -15,12 +15,18 @@ export { createHostMessageApi, useHostMessages } from './host/messages.js';
|
|
|
15
15
|
// --- Components ------------------------------------------------------------
|
|
16
16
|
export { App } from './app/App.js';
|
|
17
17
|
export { Editor } from './app/editor/index.js';
|
|
18
|
-
// --- Document outline (issue #69)
|
|
18
|
+
// --- Document outline (issue #69, markdown-derived path issue #84) --------
|
|
19
19
|
// `createDocumentOutlineHandle()` makes the controller shared between one
|
|
20
20
|
// `<Editor documentOutlineHandle={…}>` and one `<DocumentOutline handle={…}>`
|
|
21
21
|
// — create it once per editor instance and pass the same object to both.
|
|
22
|
+
// A host with no mounted Lexical editor (e.g. a raw markdown mode) can feed
|
|
23
|
+
// the same handle from markdown text alone via `handle.publishFromMarkdown`/
|
|
24
|
+
// `handle.setActiveLine`; `deriveOutlineFromMarkdown`/
|
|
25
|
+
// `resolveActiveOutlineIndex` are exported standalone for a host that wants
|
|
26
|
+
// the pure derivation without going through the handle.
|
|
22
27
|
export { DocumentOutline } from './app/editor/DocumentOutline.js';
|
|
23
28
|
export { createDocumentOutlineHandle } from './app/editor/documentOutlineHandle.js';
|
|
29
|
+
export { deriveOutlineFromMarkdown, resolveActiveOutlineIndex } from './app/editor/documentOutlineMarkdown.js';
|
|
24
30
|
// --- Annotations, React surface (ADR-077) ---------------------------------
|
|
25
31
|
// The kind-configuration types a host needs to turn the mechanism on, plus the
|
|
26
32
|
// create-event shape. The DOM-free anchor model, resolver and marker-target
|