@adecore/editor 0.17.0-beta.1 → 0.17.0-beta.2

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/LICENSE CHANGED
@@ -6,7 +6,7 @@ FSL-1.1-MIT
6
6
 
7
7
  ## Notice
8
8
 
9
- Copyright 2026 Bas Milius
9
+ Copyright 2026 - present Bas Milius
10
10
 
11
11
  ## Terms and Conditions
12
12
 
package/README.md CHANGED
@@ -1,82 +1,57 @@
1
1
  # @adecore/editor
2
2
 
3
- Read the [browser editor handbook](https://adecore.dev/editor/handbook/) for the complete setup, concepts, host integration and testing guides.
3
+ [![npm](https://img.shields.io/npm/v/@adecore/editor)](https://www.npmjs.com/package/@adecore/editor)
4
+ [![Docs](https://img.shields.io/badge/docs-adecore.dev-blue)](https://adecore.dev/editor/)
4
5
 
5
- The DOM view of the smart editor. It draws a `DocumentModel` from `@adecore/editor-core` with its own layout and implements the `EditorEngine` contract. Language features live in `@adecore/editor-react`; the engine itself needs no language service.
6
+ A code editor for the DOM. It draws a [`DocumentModel`](https://adecore.dev/editor-core/) with a layout of its own, renders only the rows in view, and takes the decorations, rows and marks a host hands it: problems, inlay hints, semantic colors, change marks, widgets between lines, code vision, agent attribution and ghost text. It knows no language server; [`@adecore/editor-react`](https://adecore.dev/editor-react/) adds one.
6
7
 
7
- ```ts
8
- const engine = createSmartEditorEngine({
9
- tokenizer: shikiTokenizers(() => highlighter),
10
- handBack: shellShortcuts,
11
- apple
12
- });
13
- const editor = engine.mount(element, { text, language: 'typescript', theme: 'github-dark' });
14
- ```
15
-
16
- Import `@adecore/ui/theme.css`, then `@adecore/editor/editor.css`. The element needs a positioned box sized by its parent. Editor, find, change and attribution colors have defaults on Adecore theme tokens. The code face uses `--font-mono`, `--code-font-size` and `--code-line-height`; the latter two default to `--text-code` and `--text-code--line-height`. Override them after the editor CSS, and call `refreshFont()` when they change.
17
-
18
- ## How it is put together
19
-
20
- - `layout.ts`: the rows of the document, free of the DOM. A row is a line, a widget or a fold's first line. It owns wrapping into visual lines, tab stops, inlays, folded lines, caret and selection boxes, hit testing and moving by visual line. Only the rows on screen are measured; the others stand on an estimate that the first draw corrects.
21
- - `view.ts`: the DOM, the scroll (native: the gutter and the pinned headers stick to the corner of the one scroll container, and the scroll event draws the rows, a viewport of them on each side, in the frame it shows in), the folds and what is drawn behind and above the text. `paint.ts` holds the drawing of rows, the gutter, the selection and the carets. Rows are absolutely placed runs of text, so the layout decides where each character is. A draw reads the viewport's scroll position and size once at its start, since a read after the first write of a frame makes the browser lay the page out again, and a host setter whose value did not change draws nothing. The horizontal scroll extent is the widest line measured since the text last changed, inlays included, plus three columns as on the platform; a host row follows the scroll and clips what it draws past its width, since an overflow there would grow the extent with every scroll and carry the sticky gutter out of view.
22
- - `controller.ts`: what a person does. Keys come from one table (`keymap-table.ts`, which the client prints in its menus as well) and are mapped in `keymap.ts` to the model's commands, typing goes through `DocumentModel.typeText`, a paste through `DocumentModel.paste`, the mouse through `pointer.ts`. A copy or cut of a bare caret remembers its text for every editor of the page, and pasting that same text again, in any of them, lands as whole lines. Adding a caret above or below is the double tap of a modifier and an arrow (`modifier-gesture.ts`). The smart keys (`EditorSmartKeys`: pairing, surrounding, tabbing out, smart Enter, indent on paste, the smart semicolon and the camel humps) are options of the editor, set at mounting and changed with `setSmartKeys`; all are on except the camel humps. The textarea (`native-input.ts`) is only the sink for the platform's typing, composition and paste, and carries the text around the caret for assistive technology.
23
- - `tokens.ts` and `shiki.ts`: coloring. A grammar needs the state the previous line ended in, so lines are colored from a frontier down, a slice at a time, on the screen plus a margin. An edit moves the frontier to the changed line and stops as soon as a line ends in the state it had before. The tokenizer is injected (`TokenizerSource`); `shikiTokenizers` adapts the viewer's Shiki highlighter. It loads the languages a grammar embeds only on demand (a style block's `lang="scss"`, a Markdown fence) when the document names them, since Shiki rebuilds the host grammar when one loads later, and a tokenizer says so with `stale` so the colors start over from the top. A language added to a document after it opened (a new fence) is colored once the file opens again. `php-html.ts` is the grammar for a PHP file as a document (HTML with PHP between its tags), which Shiki does not bundle; PHP inside an HTML attribute is not colored.
24
- - `outline.ts`: the blocks of the document with a header line, read from braces and indentation until the host hands over better ones with `setBlocks` (a language server's symbols). Sticky scroll pins the headers of the blocks scrolled out of sight, and `onScope` reports the named blocks around the caret for a breadcrumb.
25
- - `overview.ts`: the ticks in the scroll track for the host's change marks (`setChangeMarks`, also drawn in the gutter) and the find matches.
26
- - `find.ts`: the matches of the host's find bar. They are marked in the editor and never touch the selection until `endFind`.
27
- - `semantic.ts` and `theme-scopes.ts`: what a language server classified, drawn over the grammar's colors. The host hands scopes (`setSemanticTokens`), and the theme's own rules decide the color (`scopeColors`, `shikiScopeColors`), so a class is the theme's class color. A scope the theme says nothing about keeps what the grammar made.
28
- - `scroll.ts` and `scroll-animation.ts`: where a scroll goes (the platform's scrolling model: margins, and a jump a third from the top) and how it gets there, over up to a tenth of a second and never for a line or less.
29
- - `keymap-table.ts`: the keys of every editor and language command for macOS and the other platforms, which the client prints in its menus too (`@adecore/editor/keymap`).
30
- - `engine.ts`: the `Editor` contract over a model and a view.
31
-
32
- ## What a language feature gets
33
-
34
- The editor knows no language server. It reports every change of the text as LSP content changes (`onTextChange`, with its source), says where the caret is (`getCaret`, `onCaret`) and where a character is on the screen (`rectAt`, in the page's pixels so a popup is right at any canvas zoom), and takes what a host draws or does: `setMarkers` (squiggles, faded and struck text, a lane in the scroll track), `setInlayHints`, `setSemanticTokens`, `setHighlights`, `setBlocks`, `setWidgets` (rows of the host's own DOM next to lines, drawn in the layout's block rows; `placement: 'above'` puts one over its line, and each owner has its own set, see below), `setCodeVision` (a quiet row above a declaration, see below), `setGhostText` (a suggestion after the caret, see below), `setGutterAction` (one button in the gutter, such as a lightbulb, which `onGutterAction` reports), `setGutterMarkers` (small buttons in the gutter per owner, such as the mark of a saved inline edit, which `onGutterMarker` reports by id; a marker stays on its line until its owner sets them again), `setLink` (a name underlined as a link while a modifier is held) `trackRange` (a range that follows the text through edits and reads null once an edit touched it, inside or across an end; text inserted exactly at an end does not count, so a range that was a selection still holds the same text) and `applyEdits`. A marker's `message` is what the tick of the problem says in the scroll track, where a press on the tick goes to the problem. `getSelection`, `getSelections`, `setSelections` (the last range is the primary one), `getVisibleRange` and `getIndentation` say what a feature asks about, and `setSelection` selects a range, such as the stop of a snippet. `onHover` reports the character under the pointer, `onClick` offers a press on a character first (Mod+click follows a name) and `onContextMenu` asks the host for its menu, which the editor does not draw, `onKeyDown` lets a host take a key before the editor does, and `onViewChange` says when the screen position of a character moved.
35
-
36
- `setText` applies the difference as a line diff in one batch (`changedSpans`), one undo step reported as an external change, so a reload from disk or a sync between two editors on a file keeps the scroll, carets, folds, markers, inlays and code vision rows wherever the text around them stayed. A language server hears it as the one stretch from the first change to the last.
37
-
38
- `runCommand` runs an editing command by name, which is how a menu or the palette reaches the editor; the folding commands (collapse and expand a region, all, recursively, a selection, documentation comments and "expand all to level 1 through 5") and column mode are the view's own.
39
-
40
- A collapsed fold is one row for the line commands, as the platform has it: delete, duplicate, comment, move and a copy or cut with no selection take the whole fold (a moved fold stays collapsed on its new lines), a word move steps over it and add caret above and below skips it. The view hands the commands its rows through `lineSpan`.
8
+ **[Documentation with live demos](https://adecore.dev/editor/)**
41
9
 
42
- Folds are kept by line (`getFolds`, and `folds` on mounting), the way scroll and caret are. A fold has a role (the file header, imports, documentation comments, regions, the body of a function, a method or a class, object and array literals, tags, attributes and so on, `FoldRole` of the core), and `foldDefaults` on mounting lists the roles that fold by themselves in a file that has no folds of its own. `setFoldHints` hands over a language server's symbols and folding ranges, which name the bodies and tags the text cannot, and the defaults fold what they name once: not a fold somebody opened or closed, not one that holds the caret, and nothing after the first edit. `foldOutline` (`off`, `hover` or `always`) says when the arrow shows in the gutter. Indent guides, whitespace and a right margin are options and setters (`guides`, `whitespace`, `rightMargin`). The find takes `inSelection`, `selectFindMatches` puts a caret on every match, `setReplacePreview` draws what a regular expression writes under the current match, and `findFromCursor` steps without a find bar.
10
+ ## Install
43
11
 
44
- `handBack` shortcuts are not handled by the editor and not prevented, so the page's own listeners get them. Mod+S is `onSave`.
45
-
46
- ## Agent marks
47
-
48
- `setAttributionMarks` draws a three pixel bar in the gutter, where the change marks are, for each run of lines (`id`, one-based `startLine` and `endLine`, `color`). The color is a custom property of the page (`--agent-1` to `--agent-6`, `AGENT_COLORS`, defined in `editor.css` for both themes, with `--agent-ink` for text on them) or any CSS color. On a line that has both, the bar stands in the place of the git change mark, which the scroll track still shows; a removal triangle between lines stays. A run follows its text through edits until the host sets the marks again, and an edit costs one mapping of two offsets per run, so thousands of runs stay smooth. `onAttributionHover` reports the id and the bar's box on the line under the pointer (a few pixels either side of the bar count), and null when it leaves.
49
-
50
- `setRemoteCursors` draws the caret of an agent (`id`, `position`, `name`, `color`, the same colors) over the text, two pixels wide with its name in a label above it, the label's lower left corner square. Where there is no room above the caret (the first line, or under the pinned headers) the name goes under it. It follows its text through edits until set again, is not interactive, and is left out on a line a fold hides. The editor's own caret and selection never move for it.
51
-
52
- ## Review and conflict rows
53
-
54
- `setLineHighlights` tints lines behind the text (`startLine` and `endLine` one-based, a `color`, an optional `sign` such as `+` in the gutter beside the number, an optional `fill` that paints the rows as it stands instead of the 14% tint of the color), one box per row so a widget row between two tinted lines stays clear. It follows its text through edits like the marks do. `renderCode(container, text, options)` fills a widget row's element with lines that are not in the document, such as the lines an agent removed or the other side of a conflict, in the editor's face and token colors, with a gutter column as wide as the editor's, so the text lines up with the document's (`firstLine` for numbers, `sign`, `color` for the tint and the bar, `emphasis` for character ranges per line drawn in a stronger tint, such as the words a change replaced, and `faded` for text that is gone). Both take the same colors as the attribution marks. A widget has `placement: 'above'` for a row over its line. `setLineActions(actions, owner?)` puts the host's own DOM after the last character of a line (`id`, zero-based `line`, `render(container)` once per action), such as the buttons of a change under review. It is no row: it takes no height, follows its line through edits, is hidden where a fold hides the line, and a press on it never moves the caret. A long line with no wrap pushes it past the edge of the view.
55
-
56
- ## Widgets per owner
12
+ ```sh
13
+ bun add @adecore/editor
14
+ ```
57
15
 
58
- `setWidgets(widgets, owner?)` replaces the rows of one owner, `default` without one, and leaves every other owner's alone, so a peek, the review rows of an agent's change and a conflict stand together. Rows next to the same line are ordered by their owners' names and then in the order each owner gave them, not by who set them last, so they never swap places. An empty list takes the owner's rows away.
16
+ ## Set up
59
17
 
60
- ## Code vision rows
18
+ ```css
19
+ @import '@adecore/ui/theme.css';
20
+ @import '@adecore/editor/editor.css';
21
+ ```
61
22
 
62
- `setCodeVision` takes a row per declaration: the line, and the entries of the row (`id`, `text`, an optional `user` or `users` icon, and `activate`, which gets the box of the pressed entry in the page's pixels). The row is a block row above the line (`lens` on a `BlockWidget`, `lens.ts` for its buttons), one code line high whatever its entries are, placed at the indentation of the declaration and never measured, so it holds its height with no entries and the text does not move when they arrive. It follows its declaration through edits like every block does, is kept apart from `setWidgets` (a peek under a name never takes a row away), and is drawn again only when the words of its entries change. An entry stands a text size from the next one, is muted, and lights up as a link with an underline under the pointer; a press on it never moves the caret or takes the focus.
23
+ ```ts
24
+ import { createSmartEditorEngine, shikiTokenizers } from '@adecore/editor';
63
25
 
64
- ## Ghost text
26
+ const engine = createSmartEditorEngine({ tokenizer: shikiTokenizers(getHighlighter) });
27
+ const editor = engine.mount(element, { text, language: 'typescript', theme: 'github-light' });
28
+ ```
65
29
 
66
- `setGhostText({ position, text, accessory? })` draws a suggestion after the caret that is not in the document, such as a continuation from a model, and `null` takes it away. The first line of `text` sits in the line in the code face and the editor's faint color, italic, and the caret stays in front of it. Further lines are a row under the line, in the same color and with the gutter column of the document, so the lines below move down while it is shown. `accessory` fills an element drawn after the end of the line, such as the keys that take the suggestion. The editor never accepts it: the host binds the keys (Tab, `acceptGhostWord`) and inserts the text with `applyEdits`, which is one undo step. Any change of the text takes the suggestion away, so the host sets it again for the text it was made for; moving the caret does not, and the host decides what that means.
30
+ The element needs a position and a size from its parent.
67
31
 
68
- ## Limits
32
+ ## Entry points
69
33
 
70
- - No rendered documentation blocks.
71
- - Wrapped lines break at spaces and inside a word that does not fit, and continue at the line's indentation plus two characters. A click past the end of a visual line draws the caret at its end until the caret moves; an arrow key onto that offset draws it at the start of the next line.
72
- - A line over 20,000 characters is not colored.
73
- - Every edit rebuilds the row list, which is linear in the number of lines.
74
- - No bidirectional text, and no screen reader testing beyond the textarea's own context.
34
+ | Import | What it holds |
35
+ |---|---|
36
+ | `@adecore/editor` | `createSmartEditorEngine`, the `Editor` contract and its types, the Shiki adapters |
37
+ | `@adecore/editor/editor.css` | The styles |
38
+ | `@adecore/editor/shiki` | The Shiki adapters and grammar loading |
39
+ | `@adecore/editor/keymap` | The key table, `resolveKeymap`, `chordOf` and `parseChord` |
40
+ | `@adecore/editor/fake` | `FakeEditorEngine`, for tests of host code |
41
+ | `@adecore/editor/testing` | `mountEditor` on a LinkeDOM page |
75
42
 
76
- ## Keymaps and package exports
43
+ ## Documentation
77
44
 
78
- `@adecore/editor/keymap` exports `KEYMAP`, `resolveKeymap`, `chordOf` and `parseChord`. Pass the same resolved keymap and `apple` flag to the engine and to `ProjectLanguage` so editor keys, language commands and menu hints agree. Application collisions belong in host overrides. AI commands have no universal bindings. `handBack` remains the host's global shortcut list.
45
+ | Page | What it covers |
46
+ |---|---|
47
+ | [Getting started](https://adecore.dev/editor/getting-started) | Install, styles, mounting and the engine options |
48
+ | [Options and keymaps](https://adecore.dev/editor/options) | Every option and setter, smart keys and the key table |
49
+ | [Text and events](https://adecore.dev/editor/editing) | Positions, edits, selections, events and tracked ranges |
50
+ | [Decorations](https://adecore.dev/editor/decorations), [Rows and widgets](https://adecore.dev/editor/rows), [Agents](https://adecore.dev/editor/agents) | What a host draws into the editor |
51
+ | [Find and folding](https://adecore.dev/editor/find-folding) | The find matcher, folds and sticky scroll |
52
+ | [Theme and syntax](https://adecore.dev/editor/theme) | CSS tokens, Shiki and a tokenizer of your own |
53
+ | [Testing](https://adecore.dev/editor/testing) | `mountEditor` and `FakeEditorEngine` |
79
54
 
80
- The root exports the engine and its types. `/shiki` supplies the Shiki adapter, `/fake` supplies the DOM-free `FakeEditorEngine`, and `/testing` supplies DOM fixtures backed by LinkeDOM. `/editor.css` supplies the required editor chrome.
55
+ ## License
81
56
 
82
- This unpublished package is private at version `0.0.0` and retains FSL-1.1-MIT. Source consumers enable the `source` condition in the bundler, TypeScript and Bun. Default exports use compiled JavaScript and declarations. Run `bun run build`, `bun run typecheck` and `bun run test` from this package after workspace installation. See the editor migration guide for consumer adapters and the transferred revision.
57
+ FSL-1.1-MIT
package/dist/engine.js CHANGED
@@ -5,6 +5,7 @@ import { emit, subscribe } from './listeners.js';
5
5
  import { fillLens, lensSignature } from './lens.js';
6
6
  import { EditorView } from './view.js';
7
7
  const DEFAULT_TAB_SIZE = 4;
8
+ const DEFAULT_LABEL = 'Code editor';
8
9
  const DEFAULT_WIDGET_OWNER = 'default';
9
10
  class SmartEditor {
10
11
  model;
@@ -41,6 +42,7 @@ class SmartEditor {
41
42
  wrap: options.wrap ?? false,
42
43
  smartKeys: resolveSmartKeys(options.smartKeys),
43
44
  messages: options.messages ?? {},
45
+ label: options.label ?? DEFAULT_LABEL,
44
46
  guides: options.guides !== false,
45
47
  whitespace: options.whitespace === true,
46
48
  rightMargin: options.rightMargin ?? null,
@@ -522,6 +524,10 @@ class SmartEditor {
522
524
  this.settings.foldOutline = outline;
523
525
  this.view.applySettings();
524
526
  }
527
+ setLabel(label) {
528
+ this.settings.label = label;
529
+ this.view.applySettings();
530
+ }
525
531
  setFoldHints(hints) {
526
532
  this.view.setFoldHints(hints === null
527
533
  ? null
package/dist/fake.d.ts CHANGED
@@ -111,6 +111,8 @@ export declare class FakeEditor implements Editor {
111
111
  foldOutline: EditorFoldOutline;
112
112
  foldHints: EditorFoldHints | null;
113
113
  setFoldOutline(outline: EditorFoldOutline): void;
114
+ label: string;
115
+ setLabel(label: string): void;
114
116
  setFoldHints(hints: EditorFoldHints | null): void;
115
117
  setSmartKeys(keys: Partial<EditorSmartKeys>): void;
116
118
  setIndentation(indentation: EditorIndentation): void;
package/dist/fake.js CHANGED
@@ -43,6 +43,7 @@ export class FakeEditor {
43
43
  this.theme = options.theme;
44
44
  this.readOnly = options.readOnly ?? false;
45
45
  this.readOnlyReason = options.readOnlyReason;
46
+ this.label = options.label ?? 'Code editor';
46
47
  this.revealedLine = options.line ?? null;
47
48
  this.column = options.column ?? null;
48
49
  this.scrollTop = options.scrollTop ?? null;
@@ -372,6 +373,10 @@ export class FakeEditor {
372
373
  setFoldOutline(outline) {
373
374
  this.foldOutline = outline;
374
375
  }
376
+ label;
377
+ setLabel(label) {
378
+ this.label = label;
379
+ }
375
380
  setFoldHints(hints) {
376
381
  this.foldHints = hints;
377
382
  }
package/dist/types.d.ts CHANGED
@@ -28,6 +28,7 @@ export interface EditorOptions {
28
28
  readonly rightMargin?: number | null;
29
29
  readonly foldOutline?: EditorFoldOutline;
30
30
  readonly messages?: Partial<EditorMessages>;
31
+ readonly label?: string;
31
32
  readonly folds?: EditorFolds;
32
33
  readonly foldDefaults?: readonly FoldRole[];
33
34
  readonly line?: number;
@@ -258,6 +259,7 @@ export interface Editor {
258
259
  setWhitespace(whitespace: boolean): void;
259
260
  setRightMargin(column: number | null): void;
260
261
  setFoldOutline(outline: EditorFoldOutline): void;
262
+ setLabel(label: string): void;
261
263
  setFoldHints(hints: EditorFoldHints | null): void;
262
264
  setChangeMarks(marks: readonly EditorChangeMark[]): void;
263
265
  setBlocks(blocks: readonly EditorBlock[] | null): void;
package/dist/view.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface ViewSettings {
13
13
  wrap: boolean;
14
14
  smartKeys: EditorSmartKeys;
15
15
  messages: Partial<EditorMessages>;
16
+ label: string;
16
17
  guides: boolean;
17
18
  whitespace: boolean;
18
19
  rightMargin: number | null;
package/dist/view.js CHANGED
@@ -208,7 +208,6 @@ export class EditorView {
208
208
  this.input.setAttribute(name, 'off');
209
209
  }
210
210
  this.input.setAttribute('aria-multiline', 'true');
211
- this.input.setAttribute('aria-label', 'Code editor');
212
211
  this.sticky.setAttribute('aria-hidden', 'true');
213
212
  this.sticky.hidden = true;
214
213
  this.overview = make('div', 'se-overview');
@@ -252,6 +251,7 @@ export class EditorView {
252
251
  /* Settings that are read off the page or off `settings` are applied again: after a change to either. */
253
252
  applySettings() {
254
253
  this.input.readOnly = this.settings.readOnly;
254
+ this.input.setAttribute('aria-label', this.settings.label);
255
255
  this.root.dataset.readonly = String(this.settings.readOnly);
256
256
  this.root.dataset.wrap = String(this.settings.wrap);
257
257
  this.root.dataset.foldOutline = this.settings.foldOutline;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/editor",
3
- "version": "0.17.0-beta.1",
3
+ "version": "0.17.0-beta.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -39,7 +39,7 @@
39
39
  "test": "bun test --conditions=source"
40
40
  },
41
41
  "dependencies": {
42
- "@adecore/editor-core": "0.17.0-beta.1",
42
+ "@adecore/editor-core": "0.17.0-beta.2",
43
43
  "shiki": "^4.4.3",
44
44
  "linkedom": "^0.18.13"
45
45
  },
package/src/engine.ts CHANGED
@@ -51,6 +51,7 @@ import { fillLens, lensSignature } from './lens.ts';
51
51
  import { EditorView, type ViewSettings } from './view.ts';
52
52
 
53
53
  const DEFAULT_TAB_SIZE = 4;
54
+ const DEFAULT_LABEL = 'Code editor';
54
55
  const DEFAULT_WIDGET_OWNER = 'default';
55
56
 
56
57
  class SmartEditor implements Editor {
@@ -90,6 +91,7 @@ class SmartEditor implements Editor {
90
91
  wrap: options.wrap ?? false,
91
92
  smartKeys: resolveSmartKeys(options.smartKeys),
92
93
  messages: options.messages ?? {},
94
+ label: options.label ?? DEFAULT_LABEL,
93
95
  guides: options.guides !== false,
94
96
  whitespace: options.whitespace === true,
95
97
  rightMargin: options.rightMargin ?? null,
@@ -659,6 +661,11 @@ class SmartEditor implements Editor {
659
661
  this.view.applySettings();
660
662
  }
661
663
 
664
+ setLabel(label: string): void {
665
+ this.settings.label = label;
666
+ this.view.applySettings();
667
+ }
668
+
662
669
  setFoldHints(hints: EditorFoldHints | null): void {
663
670
  this.view.setFoldHints(
664
671
  hints === null
package/src/fake.ts CHANGED
@@ -91,6 +91,7 @@ export class FakeEditor implements Editor {
91
91
  this.theme = options.theme;
92
92
  this.readOnly = options.readOnly ?? false;
93
93
  this.readOnlyReason = options.readOnlyReason;
94
+ this.label = options.label ?? 'Code editor';
94
95
  this.revealedLine = options.line ?? null;
95
96
  this.column = options.column ?? null;
96
97
  this.scrollTop = options.scrollTop ?? null;
@@ -487,6 +488,12 @@ export class FakeEditor implements Editor {
487
488
  this.foldOutline = outline;
488
489
  }
489
490
 
491
+ label: string;
492
+
493
+ setLabel(label: string): void {
494
+ this.label = label;
495
+ }
496
+
490
497
  setFoldHints(hints: EditorFoldHints | null): void {
491
498
  this.foldHints = hints;
492
499
  }
package/src/types.ts CHANGED
@@ -68,6 +68,8 @@ export interface EditorOptions {
68
68
  readonly foldOutline?: EditorFoldOutline;
69
69
  /* What the editor says to a person itself, in the host's words. */
70
70
  readonly messages?: Partial<EditorMessages>;
71
+ /* The accessible name of the editor, such as "Commit message", in the host's language; `Code editor` without one. */
72
+ readonly label?: string;
71
73
  /* The folds a host kept when the file was last open. */
72
74
  readonly folds?: EditorFolds;
73
75
  /* Without remembered folds, the folds of these roles fold, as they do the first time a file opens, and again as a language server names more of them. */
@@ -491,6 +493,7 @@ export interface Editor {
491
493
  /* The column to draw a line at; null takes it away. */
492
494
  setRightMargin(column: number | null): void;
493
495
  setFoldOutline(outline: EditorFoldOutline): void;
496
+ setLabel(label: string): void;
494
497
  /* What a language server knows about the folds: the bodies of symbols and the ranges it folds. They follow their text through edits until set again; null forgets them. */
495
498
  setFoldHints(hints: EditorFoldHints | null): void;
496
499
  /* Marks that follow their lines through edits until the host sets them again. */
package/src/view.ts CHANGED
@@ -67,6 +67,7 @@ export interface ViewSettings {
67
67
  wrap: boolean;
68
68
  smartKeys: EditorSmartKeys;
69
69
  messages: Partial<EditorMessages>;
70
+ label: string;
70
71
  /* A line at each indentation level, and the one of the scope around the caret stronger. */
71
72
  guides: boolean;
72
73
  /* Spaces drawn as dots and tabs as arrows. */
@@ -299,7 +300,6 @@ export class EditorView {
299
300
  this.input.setAttribute(name, 'off');
300
301
  }
301
302
  this.input.setAttribute('aria-multiline', 'true');
302
- this.input.setAttribute('aria-label', 'Code editor');
303
303
  this.sticky.setAttribute('aria-hidden', 'true');
304
304
  this.sticky.hidden = true;
305
305
  this.overview = make('div', 'se-overview');
@@ -345,6 +345,7 @@ export class EditorView {
345
345
  /* Settings that are read off the page or off `settings` are applied again: after a change to either. */
346
346
  applySettings(): void {
347
347
  this.input.readOnly = this.settings.readOnly;
348
+ this.input.setAttribute('aria-label', this.settings.label);
348
349
  this.root.dataset.readonly = String(this.settings.readOnly);
349
350
  this.root.dataset.wrap = String(this.settings.wrap);
350
351
  this.root.dataset.foldOutline = this.settings.foldOutline;