@citisen/litearea 0.1.0 → 0.2.1

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.
@@ -36,9 +36,9 @@ export interface Token extends Range {
36
36
  * tokens and they are deliberately not tokens here.
37
37
  *
38
38
  * The distinction earns its keep. A token is what the *characters* are; a
39
- * decoration is what they *mean*, and the two change on different schedules. The
40
- * font-query grammar paints `Geist Mono` as a family from the characters alone,
41
- * but which family is *in effect* depends on the installed catalogue — the same
39
+ * decoration is what they *mean*, and the two change on different schedules. A
40
+ * grammar can paint `Geist Mono` as a family from the characters alone, but which
41
+ * family is *in effect* depends on a catalogue the HOST supplied — the same
42
42
  * characters mean something else on another machine. Painting that as a token
43
43
  * would mean re-lexing the document whenever the catalogue changed; painting it
44
44
  * as a decoration means recomputing one range list, which is what it is.
@@ -431,8 +431,8 @@ export interface CompletionContext<State = unknown> {
431
431
  * `runn|` is no longer "at the start of the line" in the strict sense, but it is
432
432
  * unmistakably completing the first word. A source that asked `firstOnLine` would
433
433
  * switch itself off after the very first keystroke — which is exactly the bug this
434
- * field was added to fix, and exactly the kind of thing a reference grammar is
435
- * for finding.
434
+ * field was added to fix, and exactly the kind of thing a grammar written against
435
+ * a real document is for finding.
436
436
  */
437
437
  firstWord: boolean;
438
438
  /** The first non-whitespace token on the caret's line, when there is one. */
@@ -31,9 +31,14 @@ export interface LiteAreaCompletion {
31
31
  /**
32
32
  * Characters that open the list in addition to word characters.
33
33
  *
34
- * Default `' '`, and that default is doing real work: both reference languages
35
- * put a value after a name, so a space is exactly where the next word becomes
36
- * guessable.
34
+ * Empty by default, because a separator is the wrong moment to interrupt: it is
35
+ * where the next token starts, which is the argument FOR opening there, and it
36
+ * loses to the habit every reader already has — the editor this is modelled on
37
+ * offers nothing on a space and waits for a letter or for `Ctrl+Space`. Nothing
38
+ * is stranded by that. The list stays open once it is open, so a value typed
39
+ * after a name is still filtered in place, and `Ctrl+Space` opens it on demand.
40
+ * Set it to `' '` or `','` for a language where a separator really is where the
41
+ * next token becomes guessable.
37
42
  */
38
43
  triggerCharacters?: string;
39
44
  /** The most rows to offer. Default 100. */
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../src/dom/editor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../src/dom/editor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAeA,YAAY,EACV,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,KAAK,EACL,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAA;AAExB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEpE,OAAO,EACL,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,IAAI,EACJ,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAElF,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,eAAe,EACf,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEnE,OAAO,EACL,eAAe,EACf,eAAe,EACf,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAA;AAEpB,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAgBA,YAAY,EACV,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,KAAK,EACL,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAA;AAExB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEpE,OAAO,EACL,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,IAAI,EACJ,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAElF,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,eAAe,EACf,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEnE,OAAO,EACL,eAAe,EACf,eAAe,EACf,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAA;AAEpB,cAAc,gBAAgB,CAAA"}
@@ -265,11 +265,15 @@ of them are exactly why `scripts/browser-check.mjs` exists: a textarea's undo
265
265
  stack has no API to read and no way to fake, so the only honest test is to make an
266
266
  edit and undo it in a browser that has a real one.
267
267
 
268
- The harness bundles the **shipped** `dist/` files rather than `src/`so what it
269
- drives is the artifact a host installs inlines them as an IIFE (Chromium refuses
270
- to fetch an ES module from a `file://` URL), and drives the public API and the
271
- public DOM only: it mounts editors, types through `execCommand('insertText')`,
272
- presses keys, dispatches a `mousedown` on a row, and hovers a squiggle. The page
268
+ The harness bundles the **shipped** `dist/index.js` — the package's one JavaScript
269
+ entry point rather than `src/`, so what it drives is the artifact a host
270
+ installs, and it inlines it as an IIFE (Chromium refuses to fetch an ES module
271
+ from a `file://` URL). The language the checks run against is defined inside the
272
+ checklist itself, because the package ships none: proving that a particular
273
+ product DSL works in a browser belongs beside the plugin that owns it, while the
274
+ questions here are all about the engine. It drives the public API and the public
275
+ DOM only: it mounts editors, types through `execCommand('insertText')`, presses
276
+ keys, dispatches a `mousedown` on a row, and hovers a squiggle. The page
273
277
  reports through `document.title`, with `error` and `unhandledrejection` listeners
274
278
  that write to the same channel, because a page whose only output channel is its
275
279
  title has to report its own failures through that channel — otherwise a syntax
@@ -311,6 +315,6 @@ again", which is all most hosts need.
311
315
  | `contenteditable` | It makes the document a DOM tree that the browser edits in ways you do not control — nested elements, `<br>` for a blank line, pasted HTML — so the value would have to be re-serialised on every keystroke, which is the same re-render problem with more failure modes and no native undo semantics worth having |
312
316
  | A second highlighting layer per range list (one for scopes, one for decorations, one for squiggles) | Each layer would have to position its own spans, so each would need its own idea of where a range starts, and a span positioned by measuring drifts as soon as the font, the wrapping, or the padding is even slightly different from what was measured. One merged stream cannot drift because nothing is positioned |
313
317
  | Measuring the live field (`scrollHeight` after collapsing it) | It reflows the page and flickers on every keystroke — the measurement that is supposed to decide the height is what makes the height unstable. The offscreen mirror pays one extra layout and keeps the field's geometry out of the question |
314
- | A bundled grammar (a default language, or a language registry) | The claim of the library is that the core knows no syntax. A default grammar would make that claim false, and it would be the thing every host inherited. The two reference grammars are importable and deliberately outside `src/core/` |
318
+ | A bundled grammar (a default language, or a language registry) | The claim of the library is that the core knows no syntax. A default grammar would make that claim false, and it would be the thing every host inherited. The package therefore ships no grammar at all: a DSL lives beside the plugin that owns it and is handed to the editor as a value |
315
319
  | Synthetic `beforeinput`/`InputEvent` edits | Untrusted, so the browser will not run them through the editing pipeline: the text changes and the history does not, which is worse than a documented `'direct'` fallback because it fails silently |
316
320
  | Tracking the caret as a character index in one module and a screen position in another | Every position that crosses a module boundary is a character offset; pixels enter only in `src/dom/`, only to place a floating element, and never travel back inward. Two coordinate systems for one caret is what produced the range that "had already moved" |
@@ -34,7 +34,7 @@ two bugs that make a completer feel broken have somewhere to live:
34
34
  | Event | What happens |
35
35
  | --- | --- |
36
36
  | A word character is typed | The list opens, in `'auto'` mode. The predicate is the grammar's `wordChars` |
37
- | A character typed through `triggerCharacters` | The list opens, in `'auto'` mode. Default `' '`, and that default is doing real work: a value usually follows a name, so a space is exactly where the next word becomes guessable |
37
+ | A character typed through `triggerCharacters` | Also opens it, in `'auto'` mode. **Empty by default**, so a separator does not open the list see below |
38
38
  | More characters are typed while the list is open | Re-resolved: the same source answers again if it is still eligible, the range is recomputed from the caret, and the rows are re-filtered |
39
39
  | A character is deleted while the list is open | Re-filtered. A backspace with no list open opens nothing, because a deletion is not a request for suggestions |
40
40
  | `Ctrl+Space` / `Cmd+Space` | Opens with no needle (`'explicit'`), or closes the list when it is already open |
@@ -45,6 +45,14 @@ two bugs that make a completer feel broken have somewhere to live:
45
45
  | The editor itself is writing | Nothing re-opens: the editor knows its own edit from the user's |
46
46
  | Any `input` event while `readOnly` | Nothing |
47
47
 
48
+ `triggerCharacters` is empty by default, so a separator does not open the list. A
49
+ space is where the next token starts, which is the argument for opening there,
50
+ and it loses to the habit a reader already has: the editor this is modelled on
51
+ offers nothing on a space and waits for a letter or for `Ctrl+Space`. Nothing is
52
+ stranded by it — the list stays open while the next token is typed, so a value
53
+ after a name is still suggested in place — and a language whose separator really
54
+ is the moment the next token becomes guessable names it explicitly.
55
+
48
56
  `auto: false` (or `completion: false` on the editor) switches off every row above
49
57
  except `Ctrl+Space`, `Escape`, and the arrows; `showCompletions()` and
50
58
  `hideCompletions()` drive the same open and close by hand, and `currentCompletion`
@@ -163,9 +171,9 @@ eligible while that head is being spelled out. `runn\|` is no longer "at the sta
163
171
  of the line" in the strict sense — the line already has content — but it is
164
172
  unmistakably completing the first word. A source that asked `firstOnLine` would
165
173
  switch itself off after the very first keystroke, which is the bug the second field
166
- exists to prevent. `dshSentryStyleGrammar` asks `firstWord` for exactly this
167
- reason, and it is the difference between a list that helps and a list that keeps
168
- disappearing.
174
+ exists to prevent. A line-oriented grammar's first-word completion asks
175
+ `firstWord` for exactly this reason, and it is the difference between a list that
176
+ helps and a list that keeps disappearing.
169
177
 
170
178
  ## Ranking
171
179
 
@@ -276,7 +284,7 @@ default would be guessing about every other language.
276
284
 
277
285
  | VSCode | litearea |
278
286
  | --- | --- |
279
- | Typing triggers suggestions | A word character or one of `triggerCharacters` (default `' '`) opens the list; `auto: false` turns it off |
287
+ | Typing triggers suggestions | A word character or one of `triggerCharacters` (empty by default) opens the list; `auto: false` turns it off |
280
288
  | `Ctrl+Space` | `Ctrl+Space`/`Cmd+Space` opens on demand, and closes when the list is already open |
281
289
  | Typing filters the list | The needle is the text between the source's current range start and the caret, matched fuzzily |
282
290
  | `Enter` accepts | `Enter` accepts the active row |
package/docs/grammar.md CHANGED
@@ -76,9 +76,9 @@ A function is handed everything a lexical decision could need:
76
76
  | `state` | Whatever `analyze` returned |
77
77
 
78
78
  The `state` field is the one that earns the function form: it is how a scope can
79
- depend on something the characters do not say. `dshFontQueryGrammar` uses it to
80
- paint a weight word as `weight` or `weight.missing`, which depends on the
81
- machine's installed faces, not on the word.
79
+ depend on something the characters do not say. A grammar for a host's installed
80
+ fonts uses it to paint a weight word as `weight` or `weight.missing`, which
81
+ depends on the faces the host reported, not on the word.
82
82
 
83
83
  ### `match`: a regular expression
84
84
 
@@ -116,7 +116,7 @@ states the host actually has — costs one call, not one per token.
116
116
 
117
117
  | Field | Default | Notes |
118
118
  | --- | --- | --- |
119
- | `max` | 4 | The most words one entry may span. A span longer than `max` is never attempted, and four covers every family in the shipped catalogue (`Source Han Serif SC` is four) |
119
+ | `max` | 4 | The most words one entry may span. A span longer than `max` is never attempted, and four covers a name like `Source Han Serif SC`, which is four words |
120
120
 
121
121
  The engine tries the longest span first and keeps the longest member it finds, so
122
122
  a catalogue holding both `IBM Plex` and `IBM Plex Mono` resolves the longer name.
@@ -346,10 +346,10 @@ decides three things at once:
346
346
  3. **Where a double click puts the selection**, because that is the platform's
347
347
  own word test and the browser uses the same notion of a word the field does.
348
348
 
349
- A language whose names contain a dot or a hyphen must say so. `dshFontQueryGrammar`
350
- uses `/[\p{L}\p{N}_-]/u` so `-apple-system` is one word; `dshSentryStyleGrammar`
351
- deliberately does *not* include `.`, so a stray `circle.` is not read as one
352
- unknown word and reported as a name the user never typed.
349
+ A language whose names contain a dot or a hyphen must say so. A font query uses
350
+ `/[\p{L}\p{N}_-]/u` so `-apple-system` is one word; a grammar whose names never
351
+ contain a dot leaves it out, so a stray `circle.` is not read as one unknown word
352
+ and reported as a name the user never typed.
353
353
 
354
354
  The flags are normalised: `g` and `y` are stripped from the predicate, because
355
355
  `RegExp.prototype.test` advances `lastIndex` on a global pattern and a predicate
@@ -442,7 +442,8 @@ diagnostic's `source` is always the grammar's `id`.
442
442
 
443
443
  `report` is also the place a structural walk that ran in `analyze` publishes what
444
444
  it found — record the ranges while the structure is known, and report them here.
445
- Both reference grammars do exactly that.
445
+ That is the usual arrangement: `analyze` decides what is wrong and `validate`
446
+ hands the ranges over.
446
447
 
447
448
  ### Codes and severities
448
449
 
@@ -476,12 +477,12 @@ detail and the body. Decorations are clamped into the document, dropped when the
476
477
  come out empty, and sorted — a grammar computing them from a stale parse cannot
477
478
  paint a span nobody can see.
478
479
 
479
- The font grammar's "in effect" pill is the worked case. `Geist Mono` is painted as
480
- a family from the characters alone — that is a token. Which family is *in effect*
481
- depends on the machine's installed catalogue, so the same characters mean
482
- something else on another computer. Painting that as a token would mean re-lexing
483
- the document whenever the catalogue changed; as a decoration it is one recomputed
484
- range list, which is what it actually is.
480
+ A host's "in effect" mark is the worked case. `Geist Mono` is painted as a family
481
+ from the characters alone — that is a token. Which family is *in effect* depends
482
+ on the host's installed catalogue, so the same characters mean something else on
483
+ another computer. Painting that as a token would mean re-lexing the document
484
+ whenever the catalogue changed; as a decoration it is one recomputed range list,
485
+ which is what it actually is.
485
486
 
486
487
  Reach for `decorate` when the mark depends on something outside the document. If
487
488
  nothing in your language does, do not add one — a decoration that could have been
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citisen/litearea",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "A lightweight textarea-based code editor: native undo/redo, fully custom syntax highlighting, VSCode-style completion, diagnostics, and hover tooltips. No CodeMirror, no Monaco.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,11 +27,6 @@
27
27
  "import": "./dist/react.js",
28
28
  "require": "./dist/react.cjs"
29
29
  },
30
- "./grammars": {
31
- "types": "./dist/types/grammars/index.d.ts",
32
- "import": "./dist/grammars.js",
33
- "require": "./dist/grammars.cjs"
34
- },
35
30
  "./styles.css": "./dist/styles.css",
36
31
  "./package.json": "./package.json"
37
32
  },
@@ -20,6 +20,12 @@
20
20
  * 4. **Auto-sizing.** Whether a box has a scrollbar is `scrollHeight` against
21
21
  * `clientHeight`, measured after a real layout.
22
22
  *
23
+ * The checks are about the ENGINE, so the language they run against is a FIXTURE
24
+ * defined inside the checklist itself. The package ships no syntax and no grammar
25
+ * to import: a harness that borrowed a real DSL would be checking that DSL rather
26
+ * than the artifact this exists to check, and proving a particular product DSL
27
+ * belongs beside the plugin that owns it.
28
+ *
23
29
  * Usage:
24
30
  * node scripts/browser-check.mjs [path/to/chrome]
25
31
  *
@@ -88,20 +94,20 @@ if (browser === undefined) {
88
94
  * The page is opened over `file://`, and Chromium refuses to fetch an ES module from
89
95
  * a file URL — so the library is bundled to an IIFE and inlined. The entry is built
90
96
  * from the SHIPPED files rather than from `src/`, so what this check drives is the
91
- * artifact a host would actually install.
97
+ * artifact a host would actually install — and it is the root entry ALONE, because
98
+ * that is all the package publishes.
92
99
  * @returns the script source.
93
100
  */
94
101
  async function bundle() {
95
102
  const index = join(root, 'dist', 'index.js')
96
- const grammars = join(root, 'dist', 'grammars.js')
97
- if (!existsSync(index) || !existsSync(grammars)) {
103
+ if (!existsSync(index)) {
98
104
  console.error('browser-check: dist/ is missing; run `npm run build` first')
99
105
  process.exit(1)
100
106
  }
101
107
  const { build } = await import('esbuild')
102
108
  const result = await build({
103
109
  stdin: {
104
- contents: `export * from ${JSON.stringify(index)}\nexport * from ${JSON.stringify(grammars)}\n`,
110
+ contents: `export * from ${JSON.stringify(index)}\n`,
105
111
  resolveDir: root,
106
112
  sourcefile: 'litearea-browser-entry.js',
107
113
  loader: 'js',
@@ -156,12 +162,57 @@ const CHECKLIST = String.raw`
156
162
  field.dispatchEvent(new KeyboardEvent('keydown', { key: key, bubbles: true, cancelable: true }))
157
163
  }
158
164
 
159
- const sentry = api.dshSentryStyleGrammar()
160
- const font = api.dshFontQueryGrammar({
161
- catalogue: ['Inter', 'IBM Plex Mono', 'Geist Mono', 'Fira Code'],
162
- enumerated: true,
163
- styles: { 'Geist Mono': ['Regular', 'Medium', 'Bold'] },
164
- shippedWeight: 400,
165
+ // ── the fixture grammar ─────────────────────────────────────────────
166
+ // The package ships no syntax, so the checklist defines the language it
167
+ // drives. This is a FIXTURE and not a product DSL: one closed vocabulary,
168
+ // three rules, one completion source, and it is published nowhere. The two
169
+ // checks that need a differently-shaped grammar — the naive one in section
170
+ // 10 and the bulky one in section 8 — define their own, for the same reason.
171
+ const SHAPES = api.defineVocabulary({
172
+ id: 'shape',
173
+ words: ['circle', 'square', 'rounded'],
174
+ scope: 'shape',
175
+ unknownMessage: '"{word}" is not a shape — expected {allowed}.',
176
+ docs: {
177
+ circle: { detail: 'a disc', body: 'Fixture documentation for circle.' },
178
+ square: { detail: 'four corners', body: 'Fixture documentation for square.' },
179
+ rounded: { detail: 'a rounded box', body: 'Fixture documentation for rounded.' },
180
+ },
181
+ })
182
+
183
+ const fixture = api.defineGrammar({
184
+ id: 'browser-fixture',
185
+ name: 'browser fixture',
186
+ rules: [
187
+ // The number rule sits ABOVE the vocabulary: digits are word characters,
188
+ // so a words rule placed first would swallow and reject them.
189
+ { kind: 'match', scope: 'keyword', pattern: /draw|fill/ },
190
+ { kind: 'match', scope: 'number', pattern: /\d+(?:\.\d+)?/ },
191
+ { kind: 'words', words: SHAPES, unknown: {} },
192
+ ],
193
+ compose: [
194
+ {
195
+ id: 'shape',
196
+ range: (context) => context.word,
197
+ items: (context) => {
198
+ const written = context.text.slice(0, context.caret)
199
+ return ['circle', 'square', 'rounded'].map((shape) => {
200
+ // A shape the document already uses leads the list, which is what
201
+ // sortText is for: the ranking must put that group first without
202
+ // pretending its label starts with a zero.
203
+ const used = written.indexOf(shape) >= 0
204
+ return {
205
+ label: shape,
206
+ append: ' ',
207
+ kind: 'shape',
208
+ detail: SHAPES.entryFor(shape)?.detail,
209
+ documentation: SHAPES.entryFor(shape)?.body,
210
+ sortText: used ? '0' : '1',
211
+ }
212
+ })
213
+ },
214
+ },
215
+ ],
165
216
  })
166
217
 
167
218
  ;(async () => {
@@ -169,7 +220,7 @@ const CHECKLIST = String.raw`
169
220
  check(typeof api.createEditor === 'function', 'the bundle did not expose createEditor')
170
221
 
171
222
  // ── 1. the layer reproduces the document, and lines up with it ──────
172
- const one = mount(sentry, 'running circle blue turn 3')
223
+ const one = mount(fixture, 'draw circle fill rounded 10')
173
224
  const paint = paintOf(one)
174
225
  check(
175
226
  paint.textContent === one.input.value,
@@ -190,7 +241,7 @@ const CHECKLIST = String.raw`
190
241
  // screen: a stylesheet rule of higher specificity once set text-decoration on every
191
242
  // painted span, which quietly switched off every squiggle in the library. Nothing in
192
243
  // the DOM reveals that, so the only honest check is the browser's own computed style.
193
- const marked = mount(sentry, 'running bogus blue')
244
+ const marked = mount(fixture, 'draw bogus 10')
194
245
  const markedSpans = [...marked.element.querySelectorAll('.litearea-paint > span')]
195
246
  const flaggedSpan = markedSpans.find((span) => span.className.indexOf('litearea-diag-error') >= 0)
196
247
  check(flaggedSpan !== undefined, 'a rejected word must carry a severity class')
@@ -220,14 +271,14 @@ const CHECKLIST = String.raw`
220
271
  // is the first thing a host asks about, so it is measured rather than asserted in prose.
221
272
  // Georgia is chosen precisely because it is unlike the default in every metric.
222
273
  const themed = mount(
223
- sentry,
224
- 'running circle blue turn 3\nwaiting rounded amber blink 1.1',
274
+ fixture,
275
+ 'draw circle 10\nfill square 20',
225
276
  {
226
277
  variables: {
227
278
  font: 'Georgia, Times New Roman, serif',
228
279
  'font-size': '15px',
229
280
  'line-height': '26px',
230
- 'scope-state': '#b91c1c',
281
+ 'scope-keyword': '#b91c1c',
231
282
  accent: '#c2410c',
232
283
  },
233
284
  },
@@ -253,8 +304,8 @@ const CHECKLIST = String.raw`
253
304
  'a themed layer must still reproduce the document',
254
305
  )
255
306
  const themedState = [...themed.element.querySelectorAll('.litearea-paint > span')]
256
- .find((span) => span.className.indexOf('litearea-scope-state') >= 0)
257
- check(themedState !== undefined, 'the themed document must have a state token')
307
+ .find((span) => span.className.indexOf('litearea-scope-keyword') >= 0)
308
+ check(themedState !== undefined, 'the themed document must have a keyword token')
258
309
  if (themedState !== undefined) {
259
310
  check(
260
311
  getComputedStyle(themedState).color === 'rgb(185, 28, 28)',
@@ -280,10 +331,10 @@ const CHECKLIST = String.raw`
280
331
  ' > ' + one.input.clientHeight + ')',
281
332
  )
282
333
  const oneLine = one.input.offsetHeight
283
- one.setValue('running circle blue turn 3\nwaiting rounded amber blink 1.1\ndone circle green flush 1.6', true)
334
+ one.setValue('draw circle 10\nfill square 20\ndraw rounded 30', true)
284
335
  const threeLines = one.input.offsetHeight
285
336
  check(threeLines > oneLine, 'more lines must grow the box (was ' + oneLine + ', now ' + threeLines + ')')
286
- one.setValue('running circle blue turn 3', true)
337
+ one.setValue('draw circle 10', true)
287
338
  const backToOne = one.input.offsetHeight
288
339
  check(
289
340
  Math.abs(backToOne - oneLine) <= 1,
@@ -292,8 +343,8 @@ const CHECKLIST = String.raw`
292
343
 
293
344
  // ── 3. a maximum height clamps the box and introduces a scrollbar ───
294
345
  const clamped = mount(
295
- sentry,
296
- 'running circle blue turn 3\nwaiting rounded amber blink 1.1\ndone circle green flush 1.6\napproval rounded amber blink 1.9',
346
+ fixture,
347
+ 'draw circle 1\nfill square 2\ndraw rounded 3\nfill circle 4',
297
348
  { sizing: { maxRows: 2 } },
298
349
  )
299
350
  check(
@@ -333,22 +384,22 @@ const CHECKLIST = String.raw`
333
384
  notes.push('control: a direct value assignment is not undoable, as expected')
334
385
 
335
386
  // ── 5. undo and redo survive a completion ──────────────────────────
336
- const undoable = mount(sentry, '')
387
+ const undoable = mount(fixture, '')
337
388
  undoable.focus()
338
- document.execCommand('insertText', false, 'runn')
389
+ document.execCommand('insertText', false, 'rou')
339
390
  check(
340
- undoable.value === 'runn',
391
+ undoable.value === 'rou',
341
392
  'typing through the editing pipeline must land (value is ' + JSON.stringify(undoable.value) + ')',
342
393
  )
343
394
  check(
344
395
  undoable.currentCompletion !== undefined,
345
- 'typing a state prefix must open the completion list',
396
+ 'typing a shape prefix must open the completion list',
346
397
  )
347
398
  check(rowsOf(undoable).length > 0, 'the completion list must render rows')
348
399
  const beforeCompletion = undoable.value
349
400
  press(undoable.input, 'Enter')
350
401
  check(
351
- undoable.value.indexOf('running') === 0,
402
+ undoable.value.indexOf('rounded') === 0,
352
403
  'Enter must accept the highlighted completion (value is ' + JSON.stringify(undoable.value) + ')',
353
404
  )
354
405
  check(
@@ -381,7 +432,7 @@ const CHECKLIST = String.raw`
381
432
  // letter — so accepting the suggestion produced the completion followed by the
382
433
  // leftover character. Every character must be typed SEPARATELY here, so that the list
383
434
  // is filtered once per keystroke exactly as a person would drive it.
384
- const grown = mount(sentry, '')
435
+ const grown = mount(fixture, '')
385
436
  grown.focus()
386
437
  document.execCommand('insertText', false, 'r')
387
438
  check(grown.currentCompletion !== undefined, 'the first letter must open the list')
@@ -394,26 +445,28 @@ const CHECKLIST = String.raw`
394
445
  )
395
446
  press(grown.input, 'Enter')
396
447
  check(
397
- grown.value === 'running ',
448
+ grown.value === 'rounded ',
398
449
  'accepting after two letters must replace BOTH of them (got ' + JSON.stringify(grown.value) + ')',
399
450
  )
400
451
 
401
452
  // The same thing with a longer word and a middle-of-document caret, because the
402
- // off-by-one is easiest to miss when the range is not anchored at zero.
403
- const midword = mount(sentry, 'running circle blue turn 3\nwaiting rounded amber blink 1.1')
453
+ // off-by-one is easiest to miss when the range is not anchored at zero. The
454
+ // inserted letters are a subsequence of the word rather than its next letters, so
455
+ // the row is still offered and the replacement has to cover everything typed.
456
+ const midword = mount(fixture, 'draw circle 1\nrounded square 2')
404
457
  midword.focus()
405
- const lineStart = midword.input.value.indexOf('waiting')
458
+ const lineStart = midword.input.value.indexOf('rounded')
406
459
  midword.setSelection(lineStart + 1)
407
- document.execCommand('insertText', false, 'ai')
460
+ document.execCommand('insertText', false, 'ou')
408
461
  press(midword.input, 'Enter')
409
462
  check(
410
- midword.input.value.split('\n')[1] === 'waiting rounded amber blink 1.1',
463
+ midword.input.value.split('\n')[1] === 'rounded square 2',
411
464
  'completing a word in the middle of a document must leave no debris (line is ' +
412
465
  JSON.stringify(midword.input.value.split('\n')[1]) + ')',
413
466
  )
414
467
 
415
468
  // ── 7. a repaint does not move the caret ───────────────────────────
416
- const careful = mount(sentry, 'running circle blue turn 3\nwaiting rounded amber blink 1.1')
469
+ const careful = mount(fixture, 'draw circle fill rounded 10\nfill square 20')
417
470
  careful.focus()
418
471
  const at = careful.input.value.indexOf('rounded')
419
472
  careful.setSelection(at)
@@ -435,7 +488,7 @@ const CHECKLIST = String.raw`
435
488
  )
436
489
 
437
490
  // ── 7. the list opens, navigates, and dismisses ─────────────────────
438
- const listed = mount(sentry, 'running ')
491
+ const listed = mount(fixture, 'draw ')
439
492
  listed.focus()
440
493
  listed.setSelection(listed.input.value.length)
441
494
  listed.showCompletions()
@@ -470,7 +523,7 @@ const CHECKLIST = String.raw`
470
523
  // row rather than the scrollbar, so a keyboard user never saw it, and a mouse user had
471
524
  // to scroll down to read it and back up to reach the next row. The fix is positional, so
472
525
  // this asks the layout rather than the DOM.
473
- const bulky = litearea.defineGrammar({
526
+ const bulky = api.defineGrammar({
474
527
  id: 'bulky',
475
528
  rules: [{ kind: 'match', scope: 'word', pattern: /[a-z]+/ }],
476
529
  wordChars: /[\p{L}]/u,
@@ -540,10 +593,10 @@ const CHECKLIST = String.raw`
540
593
  }
541
594
 
542
595
  // ── 9. the tooltip explains a problem ───────────────────────────────
543
- const hovered = mount(sentry, 'running bogus blue', { hover: { enabled: true, delay: 0 } })
596
+ const hovered = mount(fixture, 'draw bogus 10', { hover: { enabled: true, delay: 0 } })
544
597
  const flagged = [...hovered.element.querySelectorAll('.litearea-paint > span')]
545
598
  .find((span) => span.className.indexOf('litearea-diag-') >= 0)
546
- check(flagged !== undefined, 'an unknown state must be underlined')
599
+ check(flagged !== undefined, 'an unknown shape must be underlined')
547
600
  if (flagged !== undefined) {
548
601
  const box = flagged.getBoundingClientRect()
549
602
  hovered.input.dispatchEvent(new MouseEvent('mousemove', {
@@ -567,19 +620,42 @@ const CHECKLIST = String.raw`
567
620
  check(tip.dataset.open === 'false', 'leaving the field must close the tooltip')
568
621
  }
569
622
 
570
- // ── 9. the font grammar works in a browser too ─────────────────────
571
- const fonts = mount(font, 'Geist Mono m')
572
- fonts.focus()
573
- fonts.setSelection(fonts.input.value.length)
574
- fonts.showCompletions()
575
- const fontRows = [...rowsOf(fonts)].map((row) => row.textContent)
576
- check(
577
- fontRows.some((label) => label.indexOf('medium') >= 0),
578
- 'the font grammar must offer Geist Mono weights (got ' + JSON.stringify(fontRows.slice(0, 6)) + ')',
579
- )
623
+ // ── 9b. the fixture's own completion offers its own vocabulary ─────
624
+ // This replaced a check that drove the dsh-font DSL in a browser. Proving
625
+ // that a particular product DSL works is the plugin's job and not the
626
+ // library's: a DSL belongs beside the plugin that owns it. What is left is
627
+ // the engine claim the old check was reaching for — rows carry an append
628
+ // and a sortText, and the list honours both.
629
+ const offered = mount(fixture, 'draw circle 1\nfill ')
630
+ offered.focus()
631
+ offered.setSelection(offered.input.value.length)
632
+ offered.showCompletions()
633
+ check(
634
+ rowsOf(offered).length === 3,
635
+ 'the fixture completion must offer its whole vocabulary (got ' + rowsOf(offered).length + ')',
636
+ )
637
+ const offeredState = offered.currentCompletion
638
+ check(offeredState !== undefined, 'showCompletions must open the list over an empty word')
639
+ if (offeredState !== undefined) {
640
+ const firstRow = offeredState.rows[0]
641
+ check(
642
+ firstRow !== undefined && firstRow.item.sortText === '0',
643
+ 'A ROW THAT LEADS BY sortText MUST LEAD THE LIST (got ' +
644
+ JSON.stringify(firstRow === undefined ? null : firstRow.item.label) + ')',
645
+ )
646
+ check(
647
+ firstRow !== undefined && firstRow.item.label === 'circle',
648
+ 'the shape the document already uses must be offered first (got ' +
649
+ JSON.stringify(firstRow === undefined ? null : firstRow.item.label) + ')',
650
+ )
651
+ check(
652
+ offeredState.rows.some((row) => row.item.append === ' '),
653
+ 'a row must carry the append a host would type next',
654
+ )
655
+ }
580
656
  check(
581
- paintOf(fonts).textContent === 'Geist Mono m',
582
- 'the font layer must reproduce the query',
657
+ paintOf(offered).textContent === offered.input.value,
658
+ 'the fixture layer must reproduce the document it is showing a list over',
583
659
  )
584
660
 
585
661
  // ── 10. resting on blank space must explain nothing ────────────────
@@ -589,10 +665,10 @@ const CHECKLIST = String.raw`
589
665
  // an implementation detail and not an explanation of anything.
590
666
  //
591
667
  // The grammar here is naive ON PURPOSE: it describes every token it is handed, including
592
- // whitespace. Testing this against one of the reference grammars would prove nothing,
593
- // because both of them happen to stay quiet about a scope they have no documentation
594
- // for — the check has to exercise the ENGINE's refusal to ask, not a grammar's manners.
595
- const naive = litearea.defineGrammar({
668
+ // whitespace. Testing this against a grammar that happens to stay quiet about a scope it
669
+ // has no documentation for would prove nothing the check has to exercise the ENGINE's
670
+ // refusal to ask, not a grammar's manners.
671
+ const naive = api.defineGrammar({
596
672
  id: 'naive',
597
673
  rules: [{ kind: 'match', scope: 'word', pattern: /[a-z]+/ }],
598
674
  describe: (context) =>
@@ -665,6 +741,9 @@ const CHECKLIST = String.raw`
665
741
  document.querySelectorAll('style[data-litearea-styles]').length + ')',
666
742
  )
667
743
 
744
+ // A host's own write at the very end: it must be accepted without throwing,
745
+ // and it is deliberately not a check, because a direct assignment is exactly
746
+ // what the control above proved the pipeline does not record.
668
747
  const edits = one.input.value
669
748
  one.input.value = edits
670
749