@citisen/litearea 0.1.0 → 0.2.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 CHANGED
@@ -59,15 +59,17 @@ So this library makes the opposite choices, and each one is load-bearing:
59
59
  npm install @citisen/litearea
60
60
  ```
61
61
 
62
- Three entry points and one stylesheet:
62
+ Three entry points:
63
63
 
64
64
  | Import | What it is |
65
65
  | --- | --- |
66
66
  | `@citisen/litearea` | The pure engine plus the DOM layer (`createEditor`, `LiteArea`) |
67
67
  | `@citisen/litearea/react` | The React binding (`LiteAreaEditor`) |
68
- | `@citisen/litearea/grammars` | The two worked example grammars |
69
68
  | `@citisen/litearea/styles.css` | The same stylesheet the editor injects, for hosts that link CSS |
70
69
 
70
+ There is no grammar entry point. The library ships no syntax at all — a caller
71
+ supplies the rules — and nothing in `src/core/` imports a language.
72
+
71
73
  React is an optional peer dependency (`react >= 18`) and the only peer. The
72
74
  package has no runtime dependencies at all. The stylesheet is injected into the
73
75
  document once, on the first editor, unless `injectStyles: false` is passed.
@@ -299,25 +301,44 @@ diagnostic vocabulary, and an end-to-end walkthrough that adds `analyze`,
299
301
  `checks`, and `validate` to this same language — is in
300
302
  [docs/grammar.md](docs/grammar.md).
301
303
 
302
- ## The two reference grammars
304
+ ## No grammar is shipped
303
305
 
304
- The core ships no syntax: there is no built-in language, no language identifier
305
- to switch on, and nothing in `src/core/` that knows what a font stack is. The two
306
- grammars in `@citisen/litearea/grammars` exist to check that claim rather than
307
- merely assert it. Both are real DSLs from the two plugins this library came out
308
- of, and both are importable by a host that does not want to retype a language
309
- that already exists:
306
+ There is no built-in language, no language identifier to switch on, no bundled
307
+ tokenizer, and no grammar to import: nothing in `src/core/` knows what a font
308
+ stack is, and the package publishes no grammar entry point. A caller supplies the
309
+ rules, and the two examples in this file are the whole of what this repository
310
+ offers as a worked language.
310
311
 
311
- | Grammar | The language | What it exercises |
312
- | --- | --- | --- |
313
- | `dshSentryStyleGrammar()` | dsh-sentry's appearance document: one line per session state, then positional values or `key=value` pairs | A first-word vocabulary with rejection, an analysis that both fills slots and records the problems it found, a declarative `check`, two completion sources using `sortText`, and hover from vocabulary documentation |
314
- | `dshFontQueryGrammar()` | dsh-font's font query: a CSS font-family list with the weight written beside the family it belongs to | Quote handling in the lexical rules, a `scope` function that reads the analysis, dynamic vocabularies resolved from the host's installed catalogue, multi-word phrases, completion that inserts *before* an entry, and a semantic decoration |
312
+ That is a deliberate boundary rather than an omission. The editor came out of two
313
+ plugins with real DSLs dsh-sentry's appearance document and dsh-font's font
314
+ query and each of those grammars now lives beside the plugin that owns it, as a
315
+ grammar object the plugin hands to `createEditor` (or to `LiteAreaEditor`). The
316
+ shape of one is worth showing, because it is what a grammar written against a real
317
+ product looks like, and because it says plainly whose code it is:
318
+
319
+ ```ts
320
+ // In the dsh-font plugin, NOT in litearea. The plugin owns the language; the
321
+ // library owns the engine that reads it, and ships no language of its own.
322
+ export const fontQueryGrammar = defineGrammar({
323
+ id: 'dsh-font-query',
324
+ // `-apple-system` must be ONE word, or a completion would replace half of it.
325
+ wordChars: /[\p{L}\p{N}_-]/u,
326
+ rules: [
327
+ { kind: 'words', words: (context) => context.state.catalogue },
328
+ { kind: 'match', scope: 'family.generic', pattern: /monospace|sans-serif|serif/ },
329
+ { kind: 'match', scope: 'weight', pattern: /thin|light|regular|medium|bold/ },
330
+ { kind: 'match', scope: 'separator', pattern: /,/ },
331
+ ],
332
+ })
333
+ ```
315
334
 
316
- Neither is built in. Nothing in `src/core/` imports them, no option switches them
317
- on, and an editor constructed without one of them has no syntax whatsoever.
335
+ Shipping either grammar would be a scaling trap as much as an inconsistency: every
336
+ consumer that inlines the library would carry every language, so adding a
337
+ JavaScript, CSS, HTML, or Rust grammar later would grow the bundle of hosts that
338
+ use none of them. A grammar belongs where the language is parsed.
318
339
 
319
- Two decisions in `dshSentryStyleGrammar` are worth stating, because both were
320
- found by comparing the grammar against the parser it edits for:
340
+ Two decisions in that plugin's own grammar are worth stating anyway, because both
341
+ were found by comparing the grammar against the parser it edits for:
321
342
 
322
343
  - **It is deliberately stricter than the host parser.** `parseStyle` in the
323
344
  plugin does no value checking for a known option: it writes `shape=bogus` into
@@ -403,7 +424,7 @@ Colours, spacing, and the type scale come from custom properties on the wrapper:
403
424
  | `--litearea-accent` / `--litearea-accent-soft` / `--litearea-selection` | `#4d6bfe` and two alpha variants |
404
425
  | `--litearea-error` / `--litearea-warning` / `--litearea-info` / `--litearea-hint` | The four severity colours |
405
426
  | `--litearea-shadow` | The floating panel's shadow |
406
- | `--litearea-scope-*` | One colour per scope the two reference grammars use |
427
+ | `--litearea-scope-*` | One colour per scope name in the shipped palette, so a scope a grammar invents still has a themed colour to fall back on |
407
428
 
408
429
  A dark scheme is applied automatically from `prefers-color-scheme`.
409
430
 
package/README.zh.md CHANGED
@@ -32,15 +32,16 @@
32
32
  npm install @citisen/litearea
33
33
  ```
34
34
 
35
- 三个入口加一份样式表:
35
+ 三个入口:
36
36
 
37
37
  | 导入 | 是什么 |
38
38
  | --- | --- |
39
39
  | `@citisen/litearea` | 纯引擎加上 DOM 层(`createEditor`、`LiteArea`) |
40
40
  | `@citisen/litearea/react` | React 绑定(`LiteAreaEditor`) |
41
- | `@citisen/litearea/grammars` | 两个作为范例的 grammar |
42
41
  | `@citisen/litearea/styles.css` | 编辑器自己注入的那份样式表,给想用 `<link>` 的宿主 |
43
42
 
43
+ 没有 grammar 入口。这个库一点语法都不带 —— 规则由调用方提供 —— `src/core/` 里也不导入任何语言。
44
+
44
45
  React 是可选的 peer 依赖(`react >= 18`),也是唯一的 peer 依赖。这个包没有任何运行时依赖。样式表在第一个编辑器创建时注入文档一次,除非传 `injectStyles: false`。
45
46
 
46
47
  ## 快速开始
@@ -256,18 +257,31 @@ export const formSchema = defineGrammar({
256
257
 
257
258
  完整参考 —— 每种规则的全部字段、优先级、诊断词汇,以及在同一门语言上补出 `analyze`、`checks`、`validate` 的端到端走查 —— 在 [docs/grammar.md](docs/grammar.md)。
258
259
 
259
- ## 两个参考 grammar
260
+ ## 不带任何 grammar
260
261
 
261
- 核心不含语法:没有内置语言、没有可以 switch 的语言标识,`src/core/` 里没有任何代码知道字体栈是什么。`@citisen/litearea/grammars` 里的两个 grammar 是为了**验证**这个说法,而不是仅仅声明它。两门语言都是真实的 DSL,来自这个库所出的那两个插件;不想重打一遍已有语言的宿主可以直接导入:
262
+ 没有内置语言、没有可以 switch 的语言标识、没有捆绑的分词器,也没有可以导入的 grammar:`src/core/` 里没有任何代码知道字体栈是什么,包里也不发布 grammar 入口。规则由调用方提供,本文里的两个例子就是这个仓库给出的全部"写好的语言"。
262
263
 
263
- | Grammar | 语言 | 用到了什么 |
264
- | --- | --- | --- |
265
- | `dshSentryStyleGrammar()` | dsh-sentry 的外观文档:一个会话状态一行,后面跟位置取值或 `key=value` | 行首词汇表加拒绝、一次同时填槽位并记录问题的分析、一条声明式 `check`、两个用 `sortText` 的补全来源、来自 vocabulary 文档的悬浮说明 |
266
- | `dshFontQueryGrammar()` | dsh-font 的字体查询:一条 CSS font-family 列表,字重写在它所属的字族旁边 | 词法规则里处理引号、读分析结果的 `scope` 函数、从宿主已安装目录解析出来的动态 vocabulary、多词短语、**插到前面**的补全、以及一个语义 decoration |
264
+ 这是一条刻意划下的边界,而不是遗漏。这个编辑器出自两个带真实 DSL 的插件 —— dsh-sentry 的外观文档和 dsh-font 的字体查询 —— 而这两个 grammar 现在都待在拥有它们的插件身边,作为插件交给 `createEditor`(或 `LiteAreaEditor`)的 grammar 对象。其中一个的样子值得看一眼,因为它展示了"针对真实产品写的 grammar"长什么样,也因为它清楚地说明了这段代码归谁:
265
+
266
+ ```ts
267
+ // dsh-font 插件里,不在 litearea 里。语言归插件所有;库只拥有读它的引擎,
268
+ // 自己一点语言都不带。
269
+ export const fontQueryGrammar = defineGrammar({
270
+ id: 'dsh-font-query',
271
+ // `-apple-system` 必须算作一个词,否则补全会只替换它的一半。
272
+ wordChars: /[\p{L}\p{N}_-]/u,
273
+ rules: [
274
+ { kind: 'words', words: (context) => context.state.catalogue },
275
+ { kind: 'match', scope: 'family.generic', pattern: /monospace|sans-serif|serif/ },
276
+ { kind: 'match', scope: 'weight', pattern: /thin|light|regular|medium|bold/ },
277
+ { kind: 'match', scope: 'separator', pattern: /,/ },
278
+ ],
279
+ })
280
+ ```
267
281
 
268
- 两个都不是内置的。`src/core/` 不导入它们,没有任何选项能打开它们,不带 grammar 构造出来的编辑器一点语法都没有。
282
+ 把其中任何一个 grammar 打进包里,既是扩展性陷阱,也是前后不一致:每个内联了这个库的消费者都会背上所有语言,以后再想加 JavaScript、CSS、HTML 或 Rust 的 grammar,就会让完全用不到它们的宿主包体变大。语言应该待在解析它的地方。
269
283
 
270
- `dshSentryStyleGrammar` 里有两个决定值得单独说明,因为它们都是拿 grammar 和被编辑的解析器对照之后发现的:
284
+ 不过,那个插件自己的 grammar 里有两个决定仍然值得单独说明,因为它们都是拿 grammar 和被编辑的解析器对照之后发现的:
271
285
 
272
286
  - **它刻意比宿主解析器更严格。** 插件里的 `parseStyle` 对已知选项不做取值检查:它把 `shape=bogus` 原样写进规则,之后由 `resolveLook` 悄悄换成出厂默认值,于是拼错的表现只是"图标怎么都不变"。这个 grammar 会把它报出来 —— 报成 warning 而不是 error,因为文档仍然能用,只是它说的不是它想说的。
273
287
  - **它不接受 `fallback` 作为一个状态。** 插件自己的模块注释里写着 `fallback none`,但 `STYLE_STATES` 只有那四个状态,`fallback` 是内部从 `STYLE_FALLBACK_LOOK` 推导出来的,从来就没有被解析过。那句注释是过期的,把它照抄进 grammar,只会让编辑器和它服务的解析器互相矛盾。
@@ -327,7 +341,7 @@ export const formSchema = defineGrammar({
327
341
  | `--litearea-accent` / `--litearea-accent-soft` / `--litearea-selection` | `#4d6bfe` 以及两个带透明度的变体 |
328
342
  | `--litearea-error` / `--litearea-warning` / `--litearea-info` / `--litearea-hint` | 四种严重级别的颜色 |
329
343
  | `--litearea-shadow` | 浮层的阴影 |
330
- | `--litearea-scope-*` | 两个参考 grammar 用到的每个 scope 的颜色 |
344
+ | `--litearea-scope-*` | 出厂调色板里每个 scope 名字一个颜色,所以 grammar 自己新造的 scope 也有主题色可回退 |
331
345
 
332
346
  深色方案由 `prefers-color-scheme` 自动应用。
333
347
 
@@ -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. */
@@ -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" |
@@ -163,9 +163,9 @@ eligible while that head is being spelled out. `runn\|` is no longer "at the sta
163
163
  of the line" in the strict sense — the line already has content — but it is
164
164
  unmistakably completing the first word. A source that asked `firstOnLine` would
165
165
  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.
166
+ exists to prevent. A line-oriented grammar's first-word completion asks
167
+ `firstWord` for exactly this reason, and it is the difference between a list that
168
+ helps and a list that keeps disappearing.
169
169
 
170
170
  ## Ranking
171
171
 
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.0",
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
  },