@citisen/litearea 0.1.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +514 -0
  3. package/README.zh.md +362 -0
  4. package/dist/grammars.cjs +1228 -0
  5. package/dist/grammars.cjs.map +1 -0
  6. package/dist/grammars.js +1213 -0
  7. package/dist/grammars.js.map +1 -0
  8. package/dist/index.cjs +3103 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.js +3040 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/react.cjs +3032 -0
  13. package/dist/react.cjs.map +1 -0
  14. package/dist/react.js +3010 -0
  15. package/dist/react.js.map +1 -0
  16. package/dist/styles.cjs +453 -0
  17. package/dist/styles.cjs.map +1 -0
  18. package/dist/styles.css +432 -0
  19. package/dist/styles.js +447 -0
  20. package/dist/styles.js.map +1 -0
  21. package/dist/types/core/complete.d.ts +70 -0
  22. package/dist/types/core/complete.d.ts.map +1 -0
  23. package/dist/types/core/format.d.ts +34 -0
  24. package/dist/types/core/format.d.ts.map +1 -0
  25. package/dist/types/core/grammar.d.ts +14 -0
  26. package/dist/types/core/grammar.d.ts.map +1 -0
  27. package/dist/types/core/hover.d.ts +23 -0
  28. package/dist/types/core/hover.d.ts.map +1 -0
  29. package/dist/types/core/index.d.ts +12 -0
  30. package/dist/types/core/index.d.ts.map +1 -0
  31. package/dist/types/core/inspect.d.ts +34 -0
  32. package/dist/types/core/inspect.d.ts.map +1 -0
  33. package/dist/types/core/rank.d.ts +82 -0
  34. package/dist/types/core/rank.d.ts.map +1 -0
  35. package/dist/types/core/scan.d.ts +51 -0
  36. package/dist/types/core/scan.d.ts.map +1 -0
  37. package/dist/types/core/segments.d.ts +44 -0
  38. package/dist/types/core/segments.d.ts.map +1 -0
  39. package/dist/types/core/text.d.ts +113 -0
  40. package/dist/types/core/text.d.ts.map +1 -0
  41. package/dist/types/core/types.d.ts +604 -0
  42. package/dist/types/core/types.d.ts.map +1 -0
  43. package/dist/types/core/vocabulary.d.ts +82 -0
  44. package/dist/types/core/vocabulary.d.ts.map +1 -0
  45. package/dist/types/dom/create.d.ts +17 -0
  46. package/dist/types/dom/create.d.ts.map +1 -0
  47. package/dist/types/dom/editing.d.ts +89 -0
  48. package/dist/types/dom/editing.d.ts.map +1 -0
  49. package/dist/types/dom/editor.d.ts +366 -0
  50. package/dist/types/dom/editor.d.ts.map +1 -0
  51. package/dist/types/dom/index.d.ts +9 -0
  52. package/dist/types/dom/index.d.ts.map +1 -0
  53. package/dist/types/dom/mirror.d.ts +107 -0
  54. package/dist/types/dom/mirror.d.ts.map +1 -0
  55. package/dist/types/dom/overlay.d.ts +52 -0
  56. package/dist/types/dom/overlay.d.ts.map +1 -0
  57. package/dist/types/dom/popup.d.ts +95 -0
  58. package/dist/types/dom/popup.d.ts.map +1 -0
  59. package/dist/types/dom/support.d.ts +41 -0
  60. package/dist/types/dom/support.d.ts.map +1 -0
  61. package/dist/types/dom/tooltip.d.ts +39 -0
  62. package/dist/types/dom/tooltip.d.ts.map +1 -0
  63. package/dist/types/grammars/dshFont.d.ts +127 -0
  64. package/dist/types/grammars/dshFont.d.ts.map +1 -0
  65. package/dist/types/grammars/dshSentry.d.ts +84 -0
  66. package/dist/types/grammars/dshSentry.d.ts.map +1 -0
  67. package/dist/types/grammars/index.d.ts +3 -0
  68. package/dist/types/grammars/index.d.ts.map +1 -0
  69. package/dist/types/index.d.ts +15 -0
  70. package/dist/types/index.d.ts.map +1 -0
  71. package/dist/types/react/index.d.ts +91 -0
  72. package/dist/types/react/index.d.ts.map +1 -0
  73. package/dist/types/styles.d.ts +29 -0
  74. package/dist/types/styles.d.ts.map +1 -0
  75. package/docs/architecture.md +316 -0
  76. package/docs/completion.md +320 -0
  77. package/docs/grammar.md +823 -0
  78. package/package.json +105 -0
  79. package/scripts/browser-check.mjs +838 -0
  80. package/scripts/build-css.mjs +35 -0
  81. package/scripts/release.mjs +91 -0
  82. package/scripts/verify-package.mjs +253 -0
  83. package/src/core/complete.ts +286 -0
  84. package/src/core/format.ts +71 -0
  85. package/src/core/grammar.ts +40 -0
  86. package/src/core/hover.ts +129 -0
  87. package/src/core/index.ts +98 -0
  88. package/src/core/inspect.ts +198 -0
  89. package/src/core/rank.ts +317 -0
  90. package/src/core/scan.ts +720 -0
  91. package/src/core/segments.ts +185 -0
  92. package/src/core/text.ts +238 -0
  93. package/src/core/types.ts +681 -0
  94. package/src/core/vocabulary.ts +196 -0
  95. package/src/dom/create.ts +31 -0
  96. package/src/dom/editing.ts +213 -0
  97. package/src/dom/editor.ts +1143 -0
  98. package/src/dom/index.ts +46 -0
  99. package/src/dom/mirror.ts +305 -0
  100. package/src/dom/overlay.ts +106 -0
  101. package/src/dom/popup.ts +323 -0
  102. package/src/dom/support.ts +88 -0
  103. package/src/dom/tooltip.ts +112 -0
  104. package/src/grammars/dshFont.ts +1004 -0
  105. package/src/grammars/dshSentry.ts +742 -0
  106. package/src/grammars/index.ts +57 -0
  107. package/src/index.ts +122 -0
  108. package/src/react/index.tsx +248 -0
  109. package/src/styles.ts +529 -0
package/dist/styles.js ADDED
@@ -0,0 +1,447 @@
1
+ // src/styles.ts
2
+ var SCOPE_PALETTE = [
3
+ { scope: "text", light: "var(--litearea-fg)" },
4
+ { scope: "word", light: "var(--litearea-fg)" },
5
+ { scope: "family", light: "var(--litearea-fg)" },
6
+ { scope: "family-generic", light: "#7c3aed", dark: "#c4a2ff" },
7
+ { scope: "family-unknown", light: "var(--litearea-warning)" },
8
+ { scope: "family-unclosed", light: "var(--litearea-error)" },
9
+ { scope: "weight", light: "var(--litearea-accent)" },
10
+ { scope: "weight-missing", light: "var(--litearea-warning)" },
11
+ { scope: "state", light: "var(--litearea-accent)" },
12
+ { scope: "property", light: "#7c3aed", dark: "#c4a2ff" },
13
+ { scope: "operator", light: "var(--litearea-fg-dim)" },
14
+ { scope: "separator", light: "var(--litearea-fg-dim)" },
15
+ { scope: "value-shape", light: "#0f766e", dark: "#5eead4" },
16
+ { scope: "value-color", light: "#0f766e", dark: "#5eead4" },
17
+ { scope: "value-pattern", light: "#0f766e", dark: "#5eead4" },
18
+ { scope: "value-motion", light: "#0f766e", dark: "#5eead4" },
19
+ { scope: "value-number", light: "#b45309", dark: "#fbbf24" },
20
+ { scope: "comment", light: "var(--litearea-fg-dim)" },
21
+ { scope: "invalid", light: "var(--litearea-error)" },
22
+ { scope: "keyword", light: "var(--litearea-accent)" },
23
+ { scope: "string", light: "#0f766e", dark: "#5eead4" },
24
+ { scope: "number", light: "#b45309", dark: "#fbbf24" }
25
+ ];
26
+ function scopeVariables(scheme) {
27
+ return SCOPE_PALETTE.filter((entry) => scheme === "light" || entry.dark !== void 0).map((entry) => {
28
+ const value = scheme === "light" ? entry.light : entry.dark;
29
+ return ` --litearea-scope-${entry.scope}: ${String(value)};`;
30
+ }).join("\n");
31
+ }
32
+ function scopeRules() {
33
+ return SCOPE_PALETTE.map(
34
+ (entry) => `.litearea-scope-${entry.scope} { color: var(--litearea-scope-${entry.scope}); }`
35
+ ).join("\n");
36
+ }
37
+ var LITEAREA_STYLES = `
38
+ .litearea {
39
+ --litearea-font: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
40
+ --litearea-font-size: 13px;
41
+ --litearea-line-height: 20px;
42
+ --litearea-padding-block: 6px;
43
+ --litearea-padding-inline: 10px;
44
+ --litearea-radius: 8px;
45
+ --litearea-fg: #1f2328;
46
+ --litearea-fg-dim: #6b7280;
47
+ --litearea-fg-strong: #111827;
48
+ --litearea-bg: #ffffff;
49
+ --litearea-bg-raised: #ffffff;
50
+ --litearea-border: #d8dbe0;
51
+ --litearea-border-focus: #4d6bfe;
52
+ --litearea-accent: #4d6bfe;
53
+ --litearea-accent-soft: rgba(77, 107, 254, 0.12);
54
+ --litearea-selection: rgba(77, 107, 254, 0.22);
55
+ --litearea-error: #e5484d;
56
+ --litearea-warning: #d97706;
57
+ --litearea-info: #4d6bfe;
58
+ --litearea-hint: #8b8f97;
59
+ --litearea-shadow: 0 6px 24px rgba(15, 23, 42, 0.14);
60
+
61
+ ${scopeVariables("light")}
62
+
63
+ position: relative;
64
+ display: block;
65
+ color: var(--litearea-fg);
66
+ }
67
+
68
+ @media (prefers-color-scheme: dark) {
69
+ .litearea {
70
+ --litearea-fg: #e6e8eb;
71
+ --litearea-fg-dim: #8b919b;
72
+ --litearea-fg-strong: #ffffff;
73
+ --litearea-bg: #1b1e24;
74
+ --litearea-bg-raised: #23262c;
75
+ --litearea-border: #363b44;
76
+ ${scopeVariables("dark")}
77
+ --litearea-error: #ff6b6b;
78
+ --litearea-warning: #f59e0b;
79
+ --litearea-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
80
+ }
81
+ }
82
+
83
+ /* \u2500\u2500 the box \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
84
+
85
+ .litearea-box {
86
+ position: relative;
87
+ display: block;
88
+ border: 1px solid var(--litearea-border);
89
+ border-radius: var(--litearea-radius);
90
+ background: var(--litearea-bg);
91
+ transition: border-color 120ms ease;
92
+ }
93
+
94
+ .litearea-box:focus-within {
95
+ border-color: var(--litearea-border-focus);
96
+ }
97
+
98
+ .litearea-invalid .litearea-box {
99
+ border-color: var(--litearea-error);
100
+ }
101
+
102
+ /*
103
+ * The layer and the field share this rule and nothing may be added to one alone.
104
+ * Every property here is a property the mirror copies too \u2014 three elements, one
105
+ * typography, or the paint drifts.
106
+ */
107
+ .litearea-layer,
108
+ .litearea-input {
109
+ box-sizing: border-box;
110
+ width: 100%;
111
+ margin: 0;
112
+ padding: var(--litearea-padding-block) var(--litearea-padding-inline);
113
+ border: none;
114
+ font-family: var(--litearea-font);
115
+ font-size: var(--litearea-font-size);
116
+ font-weight: 400;
117
+ font-style: normal;
118
+ font-stretch: normal;
119
+ font-variant-ligatures: none;
120
+ font-kerning: none;
121
+ font-feature-settings: "liga" 0, "calt" 0, "dlig" 0;
122
+ line-height: var(--litearea-line-height);
123
+ letter-spacing: normal;
124
+ word-spacing: normal;
125
+ text-transform: none;
126
+ text-indent: 0;
127
+ text-align: left;
128
+ direction: ltr;
129
+ tab-size: 2;
130
+ white-space: pre-wrap;
131
+ overflow-wrap: break-word;
132
+ word-break: break-word;
133
+ }
134
+
135
+ .litearea-layer {
136
+ position: absolute;
137
+ inset: 0;
138
+ overflow: hidden;
139
+ pointer-events: none;
140
+ user-select: none;
141
+ }
142
+
143
+ /*
144
+ * When the box is clamped to its maximum height the field grows a scrollbar, and a
145
+ * scrollbar narrows the text. If the layer kept the full width it would wrap
146
+ * differently from the field and every colour would slide off its character, so the
147
+ * field's measured scrollbar width is published as --litearea-scrollbar and added
148
+ * here. The value is 0 whenever no scrollbar is shown.
149
+ */
150
+ .litearea-layer {
151
+ padding-right: calc(var(--litearea-padding-inline) + var(--litearea-scrollbar, 0px));
152
+ }
153
+
154
+ .litearea-paint {
155
+ position: relative;
156
+ min-height: 100%;
157
+ /* A zero-width space keeps the layer's last line box as tall as the field's:
158
+ a trailing newline would otherwise collapse in the paint alone, and the box
159
+ would jump the moment one was typed. */
160
+ will-change: transform;
161
+ }
162
+
163
+ .litearea-input {
164
+ position: relative;
165
+ display: block;
166
+ resize: none;
167
+ background: transparent;
168
+ color: transparent;
169
+ caret-color: var(--litearea-fg);
170
+ outline: none;
171
+ overflow-y: hidden;
172
+ }
173
+
174
+ .litearea-growable .litearea-input {
175
+ resize: none;
176
+ }
177
+
178
+ .litearea-resizable .litearea-input {
179
+ resize: vertical;
180
+ }
181
+
182
+ .litearea-input::placeholder {
183
+ color: var(--litearea-fg-dim);
184
+ }
185
+
186
+ .litearea-input::selection {
187
+ background: var(--litearea-selection);
188
+ }
189
+
190
+ .litearea-readonly .litearea-input {
191
+ caret-color: transparent;
192
+ }
193
+
194
+ /*
195
+ * The rules that spend the variables, generated from the same list that declares them. There is
196
+ * deliberately NO catch-all rule setting a property on every painted span: there was one, and a
197
+ * class plus an element outranks a bare class on specificity, so it silently switched off every
198
+ * squiggle in the library.
199
+ *
200
+ * A scope that is not in the list gets no colour from here, and a host writing a grammar of its
201
+ * own styles it with a plain rule \u2014 .litearea-scope-my-thing { color: \u2026 } \u2014 since nothing in
202
+ * this file competes for that selector.
203
+ */
204
+ ${scopeRules()}
205
+
206
+ .litearea-dec-effective {
207
+ border-radius: 3px;
208
+ background: var(--litearea-accent-soft);
209
+ box-shadow: 0 0 0 1px var(--litearea-accent-soft);
210
+ }
211
+
212
+ /* The four shapes a diagnostic can take. Wavy for the two that mean "fix this", dotted for
213
+ the two that mean "worth knowing" \u2014 the same distinction VSCode draws, and the reason
214
+ severity is a class rather than an inline colour. */
215
+ .litearea-diag-error {
216
+ text-decoration-line: underline;
217
+ text-decoration-style: wavy;
218
+ text-decoration-color: var(--litearea-error);
219
+ text-decoration-skip-ink: none;
220
+ text-underline-offset: 3px;
221
+ }
222
+
223
+ .litearea-diag-warning {
224
+ text-decoration-line: underline;
225
+ text-decoration-style: wavy;
226
+ text-decoration-color: var(--litearea-warning);
227
+ text-decoration-skip-ink: none;
228
+ text-underline-offset: 3px;
229
+ }
230
+
231
+ .litearea-diag-info {
232
+ text-decoration-line: underline;
233
+ text-decoration-style: dotted;
234
+ text-decoration-color: var(--litearea-info);
235
+ text-decoration-skip-ink: none;
236
+ text-underline-offset: 3px;
237
+ }
238
+
239
+ .litearea-diag-hint {
240
+ text-decoration-line: underline;
241
+ text-decoration-style: dotted;
242
+ text-decoration-color: var(--litearea-hint);
243
+ text-decoration-skip-ink: none;
244
+ text-underline-offset: 3px;
245
+ }
246
+
247
+ /* \u2500\u2500 the completion list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
248
+
249
+ /*
250
+ * The container holds two things and scrolls NEITHER of them: the list scrolls itself, and the
251
+ * documentation is pinned below it. Putting the documentation inside the scroll range \u2014 which is
252
+ * what this used to do \u2014 makes it unreachable with a long list and a long explanation: the
253
+ * arrows move the active row rather than the scrollbar, so a keyboard user never sees it, and a
254
+ * mouse user has to scroll down to read it and back up to reach the next row.
255
+ */
256
+ .litearea-popup {
257
+ position: absolute;
258
+ z-index: 30;
259
+ display: none;
260
+ flex-direction: column;
261
+ max-width: 460px;
262
+ padding: 4px;
263
+ border: 1px solid var(--litearea-border);
264
+ border-radius: 10px;
265
+ background: var(--litearea-bg-raised);
266
+ box-shadow: var(--litearea-shadow);
267
+ font-family: var(--litearea-font);
268
+ font-size: 12px;
269
+ line-height: 18px;
270
+ color: var(--litearea-fg);
271
+ overflow: hidden;
272
+ }
273
+
274
+ .litearea-popup[data-open="true"] {
275
+ display: flex;
276
+ }
277
+
278
+ /* The rows, and the only part that scrolls. Its height is bounded so the documentation below
279
+ always has somewhere to live. */
280
+ .litearea-list {
281
+ min-height: 0;
282
+ max-height: 208px;
283
+ overflow-y: auto;
284
+ }
285
+
286
+ /*
287
+ * The explanation, pinned. Its own height is bounded and it scrolls ITSELF, so a long
288
+ * explanation stays readable while the rows stay put \u2014 and arrowing to the next row swaps the
289
+ * text in place instead of requiring a scroll back up.
290
+ */
291
+ .litearea-docs {
292
+ flex: none;
293
+ max-height: 132px;
294
+ overflow-y: auto;
295
+ margin: 4px -4px -4px;
296
+ padding: 6px 10px;
297
+ border-top: 1px solid var(--litearea-border);
298
+ background: var(--litearea-bg);
299
+ border-radius: 0 0 10px 10px;
300
+ color: var(--litearea-fg);
301
+ white-space: pre-wrap;
302
+ }
303
+
304
+ .litearea-popup[data-docs="false"] .litearea-docs {
305
+ display: none;
306
+ }
307
+
308
+ .litearea-row {
309
+ display: flex;
310
+ align-items: baseline;
311
+ gap: 8px;
312
+ padding: 3px 6px;
313
+ border-radius: 6px;
314
+ cursor: pointer;
315
+ white-space: nowrap;
316
+ overflow: hidden;
317
+ }
318
+
319
+ .litearea-row[aria-selected="true"] {
320
+ background: var(--litearea-accent-soft);
321
+ }
322
+
323
+ .litearea-rowKind {
324
+ flex: none;
325
+ width: 7px;
326
+ height: 7px;
327
+ border-radius: 2px;
328
+ background: var(--litearea-fg-dim);
329
+ transform: translateY(-1px);
330
+ }
331
+
332
+ .litearea-kind-family { background: var(--litearea-scope-family-generic); }
333
+ .litearea-kind-generic { background: var(--litearea-scope-family-generic); }
334
+ .litearea-kind-weight { background: var(--litearea-scope-weight); }
335
+ .litearea-kind-state { background: var(--litearea-scope-state); }
336
+ .litearea-kind-property { background: var(--litearea-scope-property); }
337
+ .litearea-kind-value { background: var(--litearea-scope-value-shape); }
338
+ .litearea-kind-number { background: var(--litearea-scope-value-number); }
339
+ .litearea-kind-custom { background: var(--litearea-fg-dim); }
340
+
341
+ .litearea-rowLabel {
342
+ flex: 1 1 auto;
343
+ min-width: 0;
344
+ overflow: hidden;
345
+ text-overflow: ellipsis;
346
+ }
347
+
348
+ .litearea-rowMatch {
349
+ color: var(--litearea-accent);
350
+ font-weight: 600;
351
+ }
352
+
353
+ .litearea-rowDetail {
354
+ flex: none;
355
+ color: var(--litearea-fg-dim);
356
+ font-size: 11px;
357
+ }
358
+
359
+ .litearea-docsTitle {
360
+ font-weight: 600;
361
+ }
362
+
363
+ .litearea-docsDetail {
364
+ color: var(--litearea-fg-dim);
365
+ }
366
+
367
+ .litearea-docsBody {
368
+ margin-top: 2px;
369
+ color: var(--litearea-fg-dim);
370
+ }
371
+
372
+ /* \u2500\u2500 the hover tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
373
+
374
+ .litearea-tooltip {
375
+ position: absolute;
376
+ z-index: 40;
377
+ display: none;
378
+ max-width: 340px;
379
+ padding: 6px 10px;
380
+ border: 1px solid var(--litearea-border);
381
+ border-radius: 8px;
382
+ background: var(--litearea-bg-raised);
383
+ box-shadow: var(--litearea-shadow);
384
+ font-family: var(--litearea-font);
385
+ font-size: 11px;
386
+ line-height: 16px;
387
+ color: var(--litearea-fg);
388
+ pointer-events: none;
389
+ white-space: pre-wrap;
390
+ }
391
+
392
+ .litearea-tooltip[data-open="true"] {
393
+ display: block;
394
+ }
395
+
396
+ .litearea-tooltipTitle {
397
+ font-weight: 600;
398
+ }
399
+
400
+ .litearea-tooltipDetail {
401
+ color: var(--litearea-fg-dim);
402
+ }
403
+
404
+ .litearea-tooltipBody {
405
+ margin-top: 3px;
406
+ color: var(--litearea-fg-dim);
407
+ }
408
+
409
+ .litearea-srOnly {
410
+ position: absolute;
411
+ width: 1px;
412
+ height: 1px;
413
+ padding: 0;
414
+ margin: -1px;
415
+ overflow: hidden;
416
+ clip: rect(0, 0, 0, 0);
417
+ white-space: nowrap;
418
+ border: 0;
419
+ }
420
+ `.trim();
421
+ function injectStyles(ownerDocument, nonce) {
422
+ if (ownerDocument === null || ownerDocument === void 0) return void 0;
423
+ const existing = ownerDocument.querySelector("style[data-litearea-styles]");
424
+ if (existing !== null) return existing;
425
+ const style = ownerDocument.createElement("style");
426
+ style.dataset.liteareaStyles = "";
427
+ if (nonce !== void 0) style.nonce = nonce;
428
+ style.textContent = LITEAREA_STYLES;
429
+ const head = ownerDocument.head ?? ownerDocument.documentElement;
430
+ if (head === null || head === void 0) return void 0;
431
+ head.appendChild(style);
432
+ return style;
433
+ }
434
+ function scopeClass(scope) {
435
+ const folded = scope.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
436
+ return folded === "" ? "litearea-scope-text" : `litearea-scope-${folded}`;
437
+ }
438
+ function decorationClass(kind) {
439
+ return `litearea-dec-${kind.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
440
+ }
441
+ function severityClass(severity) {
442
+ return `litearea-diag-${severity.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
443
+ }
444
+
445
+ export { LITEAREA_STYLES, decorationClass, injectStyles, scopeClass, severityClass };
446
+ //# sourceMappingURL=styles.js.map
447
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/styles.ts"],"names":[],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgItB,YAAY;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAwNZ,IAAA;AAYK,SAAS,YAAA,CACd,eACA,KAAA,EAC8B;AAC9B,EAAA,IAAI,aAAA,KAAkB,IAAA,IAAQ,aAAA,KAAkB,MAAA,EAAW,OAAO,MAAA;AAClE,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,aAAA,CAAgC,6BAA6B,CAAA;AAC5F,EAAA,IAAI,QAAA,KAAa,MAAM,OAAO,QAAA;AAC9B,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,aAAA,CAAc,OAAO,CAAA;AACjD,EAAA,KAAA,CAAM,QAAQ,cAAA,GAAiB,EAAA;AAC/B,EAAA,IAAI,KAAA,KAAU,MAAA,EAAW,KAAA,CAAM,KAAA,GAAQ,KAAA;AACvC,EAAA,KAAA,CAAM,WAAA,GAAc,eAAA;AACpB,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,IAAA,IAAQ,aAAA,CAAc,eAAA;AACjD,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,MAAA,EAAW,OAAO,MAAA;AAChD,EAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,EAAA,OAAO,KAAA;AACT;AAYO,SAAS,WAAW,KAAA,EAAuB;AAChD,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAA,CAAE,OAAA,CAAQ,YAAY,EAAE,CAAA;AAC5E,EAAA,OAAO,MAAA,KAAW,EAAA,GAAK,qBAAA,GAAwB,CAAA,eAAA,EAAkB,MAAM,CAAA,CAAA;AACzE;AAGO,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,OAAO,CAAA,aAAA,EAAgB,IAAA,CAAK,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAC,CAAA,CAAA;AAC9D;AAGO,SAAS,cAAc,QAAA,EAA0B;AACtD,EAAA,OAAO,CAAA,cAAA,EAAiB,QAAA,CAAS,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAC,CAAA,CAAA;AACnE","file":"styles.js","sourcesContent":["// ─── styles: the one stylesheet ─────────────────────────────────────────────\n//\n// The CSS lives in a TypeScript string so it can be injected with no build step\n// and no CSS loader, which is what keeps the library usable from a plain script\n// tag. `scripts/build-css.mjs` also writes it out as `dist/styles.css` for the\n// hosts that would rather link it, from this same string, so the two can never\n// disagree.\n//\n// The alignment contract\n// ----------------------\n// The painted layer sits behind the real textarea and draws the same characters, so it may\n// change only what does not move a glyph: colour, background, and text-decoration. Anything\n// that changes an advance — a different font, a weight, letter-spacing, a font-feature —\n// slides the paint off the character it belongs to, and by a different amount on every\n// character, so it reads as a rendering glitch rather than as a styling mistake.\n//\n// The two elements therefore share ONE rule for their typography, and that rule is the only\n// thing that has to hold. THE FONT ITSELF IS THE HOST'S CHOICE: a proportional face aligns just\n// as well as a monospace one, because the layer, the field, and the measuring mirror all read\n// the same computed font. `--litearea-font` is a variable like any other.\n//\n// What is NOT the host's choice is ligatures and kerning, which are forced off, and the reason\n// is subtler than \"a ligature would look wrong\". A ligature draws one glyph where the field\n// holds two characters — and the layer splits its spans wherever a diagnostic or a decoration\n// begins, which cuts a ligature in half. The field would then render one glyph and the layer\n// two, at different widths, and the rest of the line would slide. Turning them off removes the\n// possibility rather than relying on no span boundary ever landing inside a pair.\n\n/**\n * One colour per scope, in one list, because the variables and the rules that use them MUST\n * agree and once did not.\n *\n * Twenty-nine scope variables were declared here with no rule anywhere that consumed them. The\n * tokenizer painted the right classes, the variables were documented, the stylesheet looked\n * complete — and every scope rendered in the inherited text colour, so the editor had no syntax\n * colouring at all. Nothing in the DOM revealed it: the spans were correct. Generating both\n * halves from this list is the fix, and it is the kind of drift a browser check cannot catch\n * either, since a colourless token is a perfectly ordinary thing to find.\n *\n * A key is the FOLDED scope name, which is what a scope becomes as a class: `value.shape` is\n * written `value-shape` here because `scopeClass` turns its dots into hyphens.\n */\nconst SCOPE_PALETTE: ReadonlyArray<{ scope: string; light: string; dark?: string }> = [\n { scope: 'text', light: 'var(--litearea-fg)' },\n { scope: 'word', light: 'var(--litearea-fg)' },\n { scope: 'family', light: 'var(--litearea-fg)' },\n { scope: 'family-generic', light: '#7c3aed', dark: '#c4a2ff' },\n { scope: 'family-unknown', light: 'var(--litearea-warning)' },\n { scope: 'family-unclosed', light: 'var(--litearea-error)' },\n { scope: 'weight', light: 'var(--litearea-accent)' },\n { scope: 'weight-missing', light: 'var(--litearea-warning)' },\n { scope: 'state', light: 'var(--litearea-accent)' },\n { scope: 'property', light: '#7c3aed', dark: '#c4a2ff' },\n { scope: 'operator', light: 'var(--litearea-fg-dim)' },\n { scope: 'separator', light: 'var(--litearea-fg-dim)' },\n { scope: 'value-shape', light: '#0f766e', dark: '#5eead4' },\n { scope: 'value-color', light: '#0f766e', dark: '#5eead4' },\n { scope: 'value-pattern', light: '#0f766e', dark: '#5eead4' },\n { scope: 'value-motion', light: '#0f766e', dark: '#5eead4' },\n { scope: 'value-number', light: '#b45309', dark: '#fbbf24' },\n { scope: 'comment', light: 'var(--litearea-fg-dim)' },\n { scope: 'invalid', light: 'var(--litearea-error)' },\n { scope: 'keyword', light: 'var(--litearea-accent)' },\n { scope: 'string', light: '#0f766e', dark: '#5eead4' },\n { scope: 'number', light: '#b45309', dark: '#fbbf24' },\n]\n\n/**\n * The variable declarations for one colour scheme.\n * @param scheme - `light` declares every scope; `dark` declares only those that differ.\n * @returns one CSS declaration per line.\n */\nfunction scopeVariables(scheme: 'light' | 'dark'): string {\n return SCOPE_PALETTE.filter((entry) => scheme === 'light' || entry.dark !== undefined)\n .map((entry) => {\n const value = scheme === 'light' ? entry.light : entry.dark\n return ` --litearea-scope-${entry.scope}: ${String(value)};`\n })\n .join('\\n')\n}\n\n/**\n * The rules that spend those variables.\n * @returns one CSS rule per line.\n */\nfunction scopeRules(): string {\n return SCOPE_PALETTE.map(\n (entry) =>\n `.litearea-scope-${entry.scope} { color: var(--litearea-scope-${entry.scope}); }`,\n ).join('\\n')\n}\n\n/** Everything a scope or a mark is painted with comes from one of these. */\nexport const LITEAREA_STYLES = `\n.litearea {\n --litearea-font: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n --litearea-font-size: 13px;\n --litearea-line-height: 20px;\n --litearea-padding-block: 6px;\n --litearea-padding-inline: 10px;\n --litearea-radius: 8px;\n --litearea-fg: #1f2328;\n --litearea-fg-dim: #6b7280;\n --litearea-fg-strong: #111827;\n --litearea-bg: #ffffff;\n --litearea-bg-raised: #ffffff;\n --litearea-border: #d8dbe0;\n --litearea-border-focus: #4d6bfe;\n --litearea-accent: #4d6bfe;\n --litearea-accent-soft: rgba(77, 107, 254, 0.12);\n --litearea-selection: rgba(77, 107, 254, 0.22);\n --litearea-error: #e5484d;\n --litearea-warning: #d97706;\n --litearea-info: #4d6bfe;\n --litearea-hint: #8b8f97;\n --litearea-shadow: 0 6px 24px rgba(15, 23, 42, 0.14);\n\n${scopeVariables('light')}\n\n position: relative;\n display: block;\n color: var(--litearea-fg);\n}\n\n@media (prefers-color-scheme: dark) {\n .litearea {\n --litearea-fg: #e6e8eb;\n --litearea-fg-dim: #8b919b;\n --litearea-fg-strong: #ffffff;\n --litearea-bg: #1b1e24;\n --litearea-bg-raised: #23262c;\n --litearea-border: #363b44;\n${scopeVariables('dark')}\n --litearea-error: #ff6b6b;\n --litearea-warning: #f59e0b;\n --litearea-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);\n }\n}\n\n/* ── the box ─────────────────────────────────────────────────────────────── */\n\n.litearea-box {\n position: relative;\n display: block;\n border: 1px solid var(--litearea-border);\n border-radius: var(--litearea-radius);\n background: var(--litearea-bg);\n transition: border-color 120ms ease;\n}\n\n.litearea-box:focus-within {\n border-color: var(--litearea-border-focus);\n}\n\n.litearea-invalid .litearea-box {\n border-color: var(--litearea-error);\n}\n\n/*\n * The layer and the field share this rule and nothing may be added to one alone.\n * Every property here is a property the mirror copies too — three elements, one\n * typography, or the paint drifts.\n */\n.litearea-layer,\n.litearea-input {\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: var(--litearea-padding-block) var(--litearea-padding-inline);\n border: none;\n font-family: var(--litearea-font);\n font-size: var(--litearea-font-size);\n font-weight: 400;\n font-style: normal;\n font-stretch: normal;\n font-variant-ligatures: none;\n font-kerning: none;\n font-feature-settings: \"liga\" 0, \"calt\" 0, \"dlig\" 0;\n line-height: var(--litearea-line-height);\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-indent: 0;\n text-align: left;\n direction: ltr;\n tab-size: 2;\n white-space: pre-wrap;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n\n.litearea-layer {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n user-select: none;\n}\n\n/*\n * When the box is clamped to its maximum height the field grows a scrollbar, and a\n * scrollbar narrows the text. If the layer kept the full width it would wrap\n * differently from the field and every colour would slide off its character, so the\n * field's measured scrollbar width is published as --litearea-scrollbar and added\n * here. The value is 0 whenever no scrollbar is shown.\n */\n.litearea-layer {\n padding-right: calc(var(--litearea-padding-inline) + var(--litearea-scrollbar, 0px));\n}\n\n.litearea-paint {\n position: relative;\n min-height: 100%;\n /* A zero-width space keeps the layer's last line box as tall as the field's:\n a trailing newline would otherwise collapse in the paint alone, and the box\n would jump the moment one was typed. */\n will-change: transform;\n}\n\n.litearea-input {\n position: relative;\n display: block;\n resize: none;\n background: transparent;\n color: transparent;\n caret-color: var(--litearea-fg);\n outline: none;\n overflow-y: hidden;\n}\n\n.litearea-growable .litearea-input {\n resize: none;\n}\n\n.litearea-resizable .litearea-input {\n resize: vertical;\n}\n\n.litearea-input::placeholder {\n color: var(--litearea-fg-dim);\n}\n\n.litearea-input::selection {\n background: var(--litearea-selection);\n}\n\n.litearea-readonly .litearea-input {\n caret-color: transparent;\n}\n\n/*\n * The rules that spend the variables, generated from the same list that declares them. There is\n * deliberately NO catch-all rule setting a property on every painted span: there was one, and a\n * class plus an element outranks a bare class on specificity, so it silently switched off every\n * squiggle in the library.\n *\n * A scope that is not in the list gets no colour from here, and a host writing a grammar of its\n * own styles it with a plain rule — .litearea-scope-my-thing { color: … } — since nothing in\n * this file competes for that selector.\n */\n${scopeRules()}\n\n.litearea-dec-effective {\n border-radius: 3px;\n background: var(--litearea-accent-soft);\n box-shadow: 0 0 0 1px var(--litearea-accent-soft);\n}\n\n/* The four shapes a diagnostic can take. Wavy for the two that mean \"fix this\", dotted for\n the two that mean \"worth knowing\" — the same distinction VSCode draws, and the reason\n severity is a class rather than an inline colour. */\n.litearea-diag-error {\n text-decoration-line: underline;\n text-decoration-style: wavy;\n text-decoration-color: var(--litearea-error);\n text-decoration-skip-ink: none;\n text-underline-offset: 3px;\n}\n\n.litearea-diag-warning {\n text-decoration-line: underline;\n text-decoration-style: wavy;\n text-decoration-color: var(--litearea-warning);\n text-decoration-skip-ink: none;\n text-underline-offset: 3px;\n}\n\n.litearea-diag-info {\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-color: var(--litearea-info);\n text-decoration-skip-ink: none;\n text-underline-offset: 3px;\n}\n\n.litearea-diag-hint {\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-color: var(--litearea-hint);\n text-decoration-skip-ink: none;\n text-underline-offset: 3px;\n}\n\n/* ── the completion list ─────────────────────────────────────────────────── */\n\n/*\n * The container holds two things and scrolls NEITHER of them: the list scrolls itself, and the\n * documentation is pinned below it. Putting the documentation inside the scroll range — which is\n * what this used to do — makes it unreachable with a long list and a long explanation: the\n * arrows move the active row rather than the scrollbar, so a keyboard user never sees it, and a\n * mouse user has to scroll down to read it and back up to reach the next row.\n */\n.litearea-popup {\n position: absolute;\n z-index: 30;\n display: none;\n flex-direction: column;\n max-width: 460px;\n padding: 4px;\n border: 1px solid var(--litearea-border);\n border-radius: 10px;\n background: var(--litearea-bg-raised);\n box-shadow: var(--litearea-shadow);\n font-family: var(--litearea-font);\n font-size: 12px;\n line-height: 18px;\n color: var(--litearea-fg);\n overflow: hidden;\n}\n\n.litearea-popup[data-open=\"true\"] {\n display: flex;\n}\n\n/* The rows, and the only part that scrolls. Its height is bounded so the documentation below\n always has somewhere to live. */\n.litearea-list {\n min-height: 0;\n max-height: 208px;\n overflow-y: auto;\n}\n\n/*\n * The explanation, pinned. Its own height is bounded and it scrolls ITSELF, so a long\n * explanation stays readable while the rows stay put — and arrowing to the next row swaps the\n * text in place instead of requiring a scroll back up.\n */\n.litearea-docs {\n flex: none;\n max-height: 132px;\n overflow-y: auto;\n margin: 4px -4px -4px;\n padding: 6px 10px;\n border-top: 1px solid var(--litearea-border);\n background: var(--litearea-bg);\n border-radius: 0 0 10px 10px;\n color: var(--litearea-fg);\n white-space: pre-wrap;\n}\n\n.litearea-popup[data-docs=\"false\"] .litearea-docs {\n display: none;\n}\n\n.litearea-row {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 3px 6px;\n border-radius: 6px;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.litearea-row[aria-selected=\"true\"] {\n background: var(--litearea-accent-soft);\n}\n\n.litearea-rowKind {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 2px;\n background: var(--litearea-fg-dim);\n transform: translateY(-1px);\n}\n\n.litearea-kind-family { background: var(--litearea-scope-family-generic); }\n.litearea-kind-generic { background: var(--litearea-scope-family-generic); }\n.litearea-kind-weight { background: var(--litearea-scope-weight); }\n.litearea-kind-state { background: var(--litearea-scope-state); }\n.litearea-kind-property { background: var(--litearea-scope-property); }\n.litearea-kind-value { background: var(--litearea-scope-value-shape); }\n.litearea-kind-number { background: var(--litearea-scope-value-number); }\n.litearea-kind-custom { background: var(--litearea-fg-dim); }\n\n.litearea-rowLabel {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.litearea-rowMatch {\n color: var(--litearea-accent);\n font-weight: 600;\n}\n\n.litearea-rowDetail {\n flex: none;\n color: var(--litearea-fg-dim);\n font-size: 11px;\n}\n\n.litearea-docsTitle {\n font-weight: 600;\n}\n\n.litearea-docsDetail {\n color: var(--litearea-fg-dim);\n}\n\n.litearea-docsBody {\n margin-top: 2px;\n color: var(--litearea-fg-dim);\n}\n\n/* ── the hover tooltip ───────────────────────────────────────────────────── */\n\n.litearea-tooltip {\n position: absolute;\n z-index: 40;\n display: none;\n max-width: 340px;\n padding: 6px 10px;\n border: 1px solid var(--litearea-border);\n border-radius: 8px;\n background: var(--litearea-bg-raised);\n box-shadow: var(--litearea-shadow);\n font-family: var(--litearea-font);\n font-size: 11px;\n line-height: 16px;\n color: var(--litearea-fg);\n pointer-events: none;\n white-space: pre-wrap;\n}\n\n.litearea-tooltip[data-open=\"true\"] {\n display: block;\n}\n\n.litearea-tooltipTitle {\n font-weight: 600;\n}\n\n.litearea-tooltipDetail {\n color: var(--litearea-fg-dim);\n}\n\n.litearea-tooltipBody {\n margin-top: 3px;\n color: var(--litearea-fg-dim);\n}\n\n.litearea-srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n`.trim()\n\n/**\n * Put the stylesheet in a document, once.\n *\n * Marked with a data attribute so two editors on one page, or two copies of the\n * library, do not stack two identical sheets — and so a host can find and replace\n * it.\n * @param ownerDocument - the document to install into.\n * @param nonce - an optional CSP nonce for the style element.\n * @returns the style element, or undefined when there is no document.\n */\nexport function injectStyles(\n ownerDocument: Document,\n nonce?: string,\n): HTMLStyleElement | undefined {\n if (ownerDocument === null || ownerDocument === undefined) return undefined\n const existing = ownerDocument.querySelector<HTMLStyleElement>('style[data-litearea-styles]')\n if (existing !== null) return existing\n const style = ownerDocument.createElement('style')\n style.dataset.liteareaStyles = ''\n if (nonce !== undefined) style.nonce = nonce\n style.textContent = LITEAREA_STYLES\n const head = ownerDocument.head ?? ownerDocument.documentElement\n if (head === null || head === undefined) return undefined\n head.appendChild(style)\n return style\n}\n\n/**\n * The class a scope is painted with.\n *\n * Dots and other punctuation are folded to hyphens, because a scope like\n * `value.shape` is a nice name to write and a terrible one to select: the CSS\n * would need a backslash before the dot at every use, and one forgotten escape\n * silently paints nothing.\n * @param scope - the scope name.\n * @returns the class name.\n */\nexport function scopeClass(scope: string): string {\n const folded = scope.replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '')\n return folded === '' ? 'litearea-scope-text' : `litearea-scope-${folded}`\n}\n\n/** The class a decoration kind is painted with. */\nexport function decorationClass(kind: string): string {\n return `litearea-dec-${kind.replace(/[^A-Za-z0-9_-]+/g, '-')}`\n}\n\n/** The class a diagnostic severity is underlined with. */\nexport function severityClass(severity: string): string {\n return `litearea-diag-${severity.replace(/[^A-Za-z0-9_-]+/g, '-')}`\n}\n"]}
@@ -0,0 +1,70 @@
1
+ import type { Completion, CompletionTrigger, Grammar, Range, SuggestionItem } from './types.js';
2
+ import type { Inspection } from './inspect.js';
3
+ import { type ResolvedGrammar } from './scan.js';
4
+ /** What the editor knows when it asks for a list. */
5
+ export interface CompletionRequest {
6
+ text: string;
7
+ caret: number;
8
+ trigger: CompletionTrigger;
9
+ /**
10
+ * The source that answered last time, when a list is already open.
11
+ *
12
+ * Passing it is what keeps a list stable while the user types: the same source answers
13
+ * again for as long as it stays eligible, so typing a character that makes a different
14
+ * source eligible does not swap the rows out from under the reader. Only the SOURCE is
15
+ * held — the range is recomputed from the caret every time, so it grows with the word.
16
+ */
17
+ previousSourceId?: string | undefined;
18
+ /** The most rows to return. */
19
+ limit?: number;
20
+ }
21
+ /**
22
+ * Resolve a completion for a caret.
23
+ *
24
+ * @param inspection - the current inspection of the document.
25
+ * @param grammar - the language.
26
+ * @param request - the caret, the trigger, and any list already open.
27
+ * @returns the range and the rows, or undefined when nothing applies.
28
+ */
29
+ export declare function complete<State>(inspection: Inspection<State>, grammar: Grammar<State> | ResolvedGrammar<State>, request: CompletionRequest): Completion | undefined;
30
+ /** An edit produced by accepting a row. */
31
+ export interface AppliedCompletion {
32
+ /** The document after the edit. */
33
+ text: string;
34
+ /** Where the caret belongs in it. */
35
+ caret: number;
36
+ /**
37
+ * What changed, in the NEW document. The editor uses it to scroll the result
38
+ * into view and to describe the edit to a host.
39
+ */
40
+ range: Range;
41
+ /**
42
+ * The offset the edit starts at in the ORIGINAL document.
43
+ *
44
+ * Exposed alongside `insert` because an editor must write only the range that
45
+ * changed. Assigning the whole recomputed `text` would be simpler and would throw
46
+ * away the browser's undo history, which is the one thing this library exists to
47
+ * protect.
48
+ */
49
+ from: number;
50
+ /** The offset the edit ends at in the original document. */
51
+ to: number;
52
+ /** The text written between those two offsets. */
53
+ insert: string;
54
+ }
55
+ /**
56
+ * Apply a chosen row to a document.
57
+ *
58
+ * The row decides three things: what is written, whether it is written over the
59
+ * range or in front of it, and how much text follows it. `append` is the
60
+ * grammar's business and not the engine's — inviting another entry with a `, `
61
+ * is a fact about font stacks, and an engine that appended one by default would
62
+ * be guessing about every other language.
63
+ *
64
+ * @param text - the document.
65
+ * @param range - the range the list was opened over.
66
+ * @param item - the chosen row.
67
+ * @returns the resulting text, caret, and the edit that produced them.
68
+ */
69
+ export declare function applyCompletion(text: string, range: Range, item: SuggestionItem): AppliedCompletion;
70
+ //# sourceMappingURL=complete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/core/complete.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EACV,UAAU,EAGV,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,cAAc,EACf,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9C,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,WAAW,CAAA;AAKnF,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,iBAAiB,CAAA;IAC1B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAC5B,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,EAC7B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,EAChD,OAAO,EAAE,iBAAiB,GACzB,UAAU,GAAG,SAAS,CAkExB;AAyDD,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,KAAK,CAAA;IACZ;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,GAAG,iBAAiB,CAmCnG"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Substitute `{name}` placeholders in a template.
3
+ *
4
+ * A placeholder with no matching key is left exactly as it was rather than
5
+ * blanked: a visible `{allowed}` in a message is a bug report, and an empty gap
6
+ * is a mystery.
7
+ * @param template - the template, with `{name}` placeholders.
8
+ * @param values - the substitutions.
9
+ * @returns the filled text.
10
+ */
11
+ export declare function fillTemplate(template: string, values: Readonly<Record<string, string>>): string;
12
+ /**
13
+ * A readable list: `a`, `a or b`, `a, b, or c`.
14
+ *
15
+ * The conjunction is a parameter because the same list reads differently as
16
+ * "expected one of X, Y, or Z" and as "X, Y, and Z are installed".
17
+ * @param items - the members.
18
+ * @param options - `conjunction` (`'or'` by default) and `limit`, the most
19
+ * members to name before summarising the rest as `… and N more`.
20
+ * @returns the phrase.
21
+ */
22
+ export declare function listPhrase(items: readonly string[], options?: {
23
+ conjunction?: string;
24
+ limit?: number;
25
+ }): string;
26
+ /**
27
+ * Collapse the whitespace in a snippet of user text so it can be quoted inside a
28
+ * one-line message.
29
+ * @param text - the text.
30
+ * @param limit - the longest result, ellipsised beyond it.
31
+ * @returns the excerpt.
32
+ */
33
+ export declare function excerpt(text: string, limit?: number): string;
34
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/core/format.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAI/F;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,MAAM,CAoBR;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAGxD"}
@@ -0,0 +1,14 @@
1
+ import type { CompletionSource, Grammar } from './types.js';
2
+ /**
3
+ * Declare a grammar, inferring its state type from `analyze`.
4
+ * @param grammar - the grammar.
5
+ * @returns the same grammar, typed.
6
+ */
7
+ export declare function defineGrammar<State = unknown>(grammar: Grammar<State>): Grammar<State>;
8
+ /**
9
+ * Declare a completion source, inferring the state type from its context.
10
+ * @param source - the source.
11
+ * @returns the same source, typed.
12
+ */
13
+ export declare function defineCompletion<State = unknown>(source: CompletionSource<State>): CompletionSource<State>;
14
+ //# sourceMappingURL=grammar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../../src/core/grammar.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3D;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAEtF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAC9C,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,gBAAgB,CAAC,KAAK,CAAC,CAEzB"}
@@ -0,0 +1,23 @@
1
+ import type { Diagnostic, Grammar, HoverInfo } from './types.js';
2
+ import type { Inspection } from './inspect.js';
3
+ import { type ResolvedGrammar } from './scan.js';
4
+ /**
5
+ * Turn one diagnostic into a tooltip.
6
+ *
7
+ * Exported because a host that shows its own marker list wants the same wording,
8
+ * and because a test should be able to assert the tooltip without going through
9
+ * the ordering rules below.
10
+ * @param diagnostic - the diagnostic.
11
+ * @returns the tooltip.
12
+ */
13
+ export declare function diagnosticHover(diagnostic: Diagnostic): HoverInfo;
14
+ /**
15
+ * Resolve what a hover should show at an offset.
16
+ *
17
+ * @param inspection - the current inspection.
18
+ * @param grammar - the language.
19
+ * @param offset - the character offset the pointer resolved to.
20
+ * @returns the tooltip, or undefined when there is nothing to say.
21
+ */
22
+ export declare function resolveHover<State>(inspection: Inspection<State>, grammar: Grammar<State> | ResolvedGrammar<State>, offset: number): HoverInfo | undefined;
23
+ //# sourceMappingURL=hover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../../src/core/hover.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAc,UAAU,EAAE,OAAO,EAAgB,SAAS,EAAY,MAAM,YAAY,CAAA;AACpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,WAAW,CAAA;AAUnF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAQjE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAChC,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,EAC7B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,EAChD,MAAM,EAAE,MAAM,GACb,SAAS,GAAG,SAAS,CAkCvB"}
@@ -0,0 +1,12 @@
1
+ export type { CheckRule, Completion, CompletionContext, CompletionRow, CompletionSource, CompletionTrigger, Decoration, DiagnoseContext, Diagnostic, Grammar, HoverContext, HoverInfo, LineInfo, MatchRule, Range, RegionRule, ResolvedVocabulary, Rule, RuleContext, RuleMatch, Scope, ScopeSpec, Severity, SuggestionItem, Token, VocabularyContext, VocabularyEntry, WordInfo, WordsRule, WordsSource, } from './types.js';
2
+ export { excerpt, fillTemplate, listPhrase } from './format.js';
3
+ export { clamp, containsOffset, isEmptyRange, isOffset, isWordChar, lineAt, lineIndexAt, lineStarts, scopeAt, tokenAfter, tokenAt, tokenBefore, tokensOnLine, wordInfoAt, } from './text.js';
4
+ export { asResolvedVocabulary, defineVocabulary, resolveWordsSource, vocabularyWords, type VocabularySpec, } from './vocabulary.js';
5
+ export { isResolvedGrammar, resolveGrammar, scan, type ResolvedGrammar, type ScanResult, } from './scan.js';
6
+ export { buildSegments, segmentClasses, type PaintSegment, type SegmentInput } from './segments.js';
7
+ export { inspect, normalizeDiagnostics, type Inspection } from './inspect.js';
8
+ export { fuzzyMatch, highlightSegments, isWordStart, rank, type FuzzyMatch, type Ranked, type RankOptions, } from './rank.js';
9
+ export { applyCompletion, complete, type AppliedCompletion, type CompletionRequest, } from './complete.js';
10
+ export { diagnosticHover, resolveHover } from './hover.js';
11
+ export { defineCompletion, defineGrammar } from './grammar.js';
12
+ //# sourceMappingURL=index.d.ts.map