rainbowindex 0.6.0 → 0.7.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 (43) hide show
  1. package/CHANGELOG.md +543 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +219 -65
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -4
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-KSNYSR3C.mjs → chunk-FJOZJIKB.mjs} +2499 -329
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-3LWJTLOJ.mjs → chunk-RC6DDE4L.mjs} +23 -15
  13. package/dist/chunk-TQJYVQPE.mjs +217 -0
  14. package/dist/chunk-W756NVYI.mjs +33 -0
  15. package/dist/chunk-WBESS2ZD.mjs +598 -0
  16. package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
  17. package/dist/{chunk-6U4IOFOS.mjs → chunk-XQGSG2HK.mjs} +199 -555
  18. package/dist/cli.mjs +1077 -123
  19. package/dist/{index-DSgpB6bS.d.ts → context-DcBtnnan.d.ts} +47 -103
  20. package/dist/editor.d.ts +71 -433
  21. package/dist/editor.mjs +51 -368
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-4Kyaq3IZ.d.ts → imports-C9esHd5Q.d.ts} +78 -84
  25. package/dist/index-CNqdL5U0.d.ts +56 -0
  26. package/dist/index-Czx-EUwh.d.ts +138 -0
  27. package/dist/index-DW8YSxTz.d.ts +104 -0
  28. package/dist/index.d.ts +46 -5
  29. package/dist/index.mjs +33 -9
  30. package/dist/oxlint.d.ts +21 -3
  31. package/dist/oxlint.mjs +19 -1
  32. package/dist/recipe.d.ts +111 -0
  33. package/dist/recipe.mjs +71 -0
  34. package/dist/safelist-CH3_PywB.d.ts +43 -0
  35. package/dist/session-CMaskdB7.d.ts +543 -0
  36. package/dist/tailwind.css +644 -0
  37. package/dist/theme-CIZiGlce.d.ts +115 -0
  38. package/dist/vite.d.ts +10 -1
  39. package/dist/vite.mjs +266 -118
  40. package/package.json +27 -5
  41. package/dist/chunk-WK6S4HTC.mjs +0 -1921
  42. package/dist/chunk-ZR7XJMUN.mjs +0 -251
  43. package/dist/safelist-CGCtF-Fr.d.ts +0 -96
package/dist/editor.d.ts CHANGED
@@ -1,40 +1,10 @@
1
- import { P as ParsedDirective, R as ResolvedTheme } from './index-4Kyaq3IZ.js';
2
- export { b as createThemeSnapshot, d as describeLoadedWeights, w as weightIsLoaded } from './index-4Kyaq3IZ.js';
3
- import { b as CompilationSnapshot, C as ColorDefinition } from './index-DSgpB6bS.js';
4
- export { e as defaultTheme } from './index-DSgpB6bS.js';
5
-
6
- /** Where a candidate was found. "expression" marks a string literal sitting
7
- * in a JS expression position that cannot be a class list — today, an
8
- * operand of `==`/`!=` (`mode === "default"`). It is NOT a certain origin:
9
- * editors should treat it like "plain" and never report it as a bad class. */
10
- type CandidateOrigin = "attribute" | "helper" | "safelist" | "plain" | "expression";
11
- interface ClassCandidate {
12
- /** The class string in expanded form — for variant-group members this
13
- * includes the group prefix (`hover:bg-red-500`) even though the span
14
- * covers only the member token inside the braces. */
15
- value: string;
16
- /** Absolute [start, end) span of the visible token in the original source.
17
- * `source.slice(start, end)` is the member token for group members and
18
- * `value` itself everywhere else. */
19
- start: number;
20
- end: number;
21
- origin: CandidateOrigin;
22
- /** The call the class was found in, when origin is "helper"/"safelist". */
23
- helperName?: string;
24
- /** Identity of the innermost scanned helper/safelist call context:
25
- * candidates from the same call share one id, distinct calls get distinct
26
- * ids. A class helper nested inside another class helper's arguments is
27
- * not scanned as its own call — its literals belong to the outer call —
28
- * while a class helper inside a cva/tv config does get its own id. Ids
29
- * are only comparable within one extraction's result. Absent for
30
- * attribute/plain candidates. */
31
- callId?: number;
32
- /** For variant-group members: span of the group's variant prefix (`hover:`). */
33
- groupPrefix?: {
34
- start: number;
35
- end: number;
36
- };
37
- }
1
+ import { C as ClassCandidate, D as Diagnostic } from './session-CMaskdB7.js';
2
+ export { a as CANONICAL_COLOR_STOPS, b as CandidateOrigin, c as ClassEnumeration, d as ClassExplanation, e as ClassInspector, f as ClassTemplate, g as ClassValidation, h as ColorSwatch, i as DiagnosticSeverity, E as EditorSession, j as EnumeratedClass, M as MergeAnalysis, k as MergeDrop, P as ParsedUtility, R as RenderStylesheetOptions, l as RenderedStylesheet, S as SwatchColor, T as ThemeTokens, U as UTILITY_VALUE_SPACES, V as ValueSpaceKind, m as ValueSpaceSpec, n as VariantInfo, o as VariantKind, p as analyzeMerge, q as createClassInspector, r as createEditorSession, s as cssColorToHex, t as diagnosticFromWarning, u as enumerateClassNames, v as listThemeTokens, w as listVariants, x as oklchToHex, y as parseUtility, z as renderStylesheet, A as resolveColorSwatch, B as severityForCode, F as warningCode } from './session-CMaskdB7.js';
3
+ import { P as ParsedDirective, R as ResolvedTheme } from './imports-C9esHd5Q.js';
4
+ export { a as ImportResolution, I as ImportResolver, b as InlineImportsOptions, c as InlineImportsResult, d as describeLoadedWeights, i as inlineDirectiveImports, w as weightIsLoaded } from './imports-C9esHd5Q.js';
5
+ export { C as ColorDefinition, d as defaultTheme } from './index-Czx-EUwh.js';
6
+ export { a as CompilationSnapshot, S as SerializedSnapshot, h as hydrateSnapshot, p as publishSnapshot, s as serializeSnapshot } from './context-DcBtnnan.js';
7
+ export { b as createThemeSnapshot } from './index-DW8YSxTz.js';
38
8
 
39
9
  /** Helper-call names whose string arguments are walked for class literals.
40
10
  * Exported for editor tooling so completion-context detection can match the
@@ -69,6 +39,42 @@ declare function extractClassCandidates(input: SourceExtractionInput, warnings?:
69
39
  */
70
40
  declare function isSourceFile(file: string): boolean;
71
41
 
42
+ /**
43
+ * `outermostCandidates` — the candidates that correspond to what was typed.
44
+ *
45
+ * The scanner over-collects on purpose. It is a lexer, not a parser, and a
46
+ * class it invents costs nothing: an unknown utility compiles to no rule. So
47
+ * in JavaScript, where `:` is also a ternary and an object key, it emits both
48
+ * the joined token and the fragments around each colon —
49
+ * `sm:hover:focus:flex` arrives as four candidates, three of whose spans sit
50
+ * inside the fourth. The HTML extractor, which has no such ambiguity, emits
51
+ * one.
52
+ *
53
+ * For a build that asymmetry is invisible. For anything that maps a candidate
54
+ * back to a *place in the source* — an underline, a quick fix, a hover — it is
55
+ * a defect: an editor would report `sm` in `sm:flex` as an unknown class,
56
+ * which is both wrong and impossible to act on. This is the filter that turns
57
+ * the scanner's candidate stream into one entry per class the author wrote.
58
+ *
59
+ * Kept out of the scanner deliberately. The extra candidates are the
60
+ * over-collection working as designed, and the compile path is entitled to
61
+ * them; it is the editor path that wants one candidate per class.
62
+ */
63
+
64
+ /**
65
+ * Drop every candidate whose source span lies inside another candidate's span.
66
+ *
67
+ * A candidate is dropped when some other span strictly contains it — another
68
+ * candidate's own span, or the `groupPrefix` span a variant-group member
69
+ * points back at, which is what covers the stray `hover` in
70
+ * `hover:{px-2 py-1}`. Two candidates over the identical span keep the first
71
+ * in input order. Everything kept is returned in source order.
72
+ *
73
+ * Origins are untouched: filter to `attribute` / `helper` / `safelist` first
74
+ * if prose and bare identifiers are not wanted either.
75
+ */
76
+ declare function outermostCandidates(candidates: readonly ClassCandidate[]): ClassCandidate[];
77
+
72
78
  /**
73
79
  * Default CSS entry candidates — the paths the CLI, the Vite plugin, and
74
80
  * editor tooling probe (in order) when locating the project's Rainbow Index
@@ -79,7 +85,13 @@ declare function isSourceFile(file: string): boolean;
79
85
  declare const CSS_ENTRY_CANDIDATES: readonly string[];
80
86
 
81
87
  /** Import specifiers that activate RainbowIndex. Single source for the
82
- * activation scan here, the PostCSS import matcher, and the strip regex. */
88
+ * activation scan here, the PostCSS import matcher, and the strip regex.
89
+ *
90
+ * The preset is on the list because a stylesheet whose only Rainbow Index
91
+ * content is `@import "rainbowindex/tailwind.css"` is unmistakably asking to
92
+ * be compiled. It is deliberately *not* in the marker list above: unlike the
93
+ * two markers it is a real stylesheet full of directives, and the inliner has
94
+ * to resolve and read it like any other package preset. */
83
95
  declare const RI_IMPORT_SPECIFIERS: readonly string[];
84
96
  /**
85
97
  * Detect whether source activates RainbowIndex: any RI directive token
@@ -88,39 +100,6 @@ declare const RI_IMPORT_SPECIFIERS: readonly string[];
88
100
  */
89
101
  declare function hasRIActivation(src: string): boolean;
90
102
 
91
- /**
92
- * Structured diagnostics — the typed view of the `[RI-NNNN] message` warning
93
- * strings, for editor tooling that anchors problems to source spans.
94
- *
95
- * The legacy string arrays stay the wire format everywhere (and the warning
96
- * budget/dedup in warnings.ts keeps operating on them); a Diagnostic carries
97
- * the same full message text verbatim, plus the parsed code, a severity
98
- * derived from the documented code-range convention, and — when the emission
99
- * site knew one — a [start, end) span into the CSS input. Consumers can rely
100
- * on `diagnostics[i].message === warnings[i]` wherever both are returned.
101
- */
102
- type DiagnosticSeverity = "error" | "warning";
103
- interface Diagnostic {
104
- /** The RI-NNNN code, or null when the message carries no parseable code. */
105
- code: string | null;
106
- severity: DiagnosticSeverity;
107
- /** The full legacy warning text, including the `[RI-NNNN]` prefix. */
108
- message: string;
109
- /** [start, end) span in the analyzed CSS input, or null when unknown. */
110
- start: number | null;
111
- end: number | null;
112
- }
113
- /** Extract the RI-NNNN code from a legacy warning string, or null. */
114
- declare function warningCode(message: string): string | null;
115
- /**
116
- * Severity by code range — the same convention warnings.ts budgets by:
117
- * RI-0xxx (fatal/bootstrap) and RI-2xxx (compile/runtime errors) are errors,
118
- * everything else (informational 1xxx ranges) is a warning.
119
- */
120
- declare function severityForCode(code: string | null): DiagnosticSeverity;
121
- /** Wrap a legacy warning string as a Diagnostic, with an optional span. */
122
- declare function diagnosticFromWarning(message: string, span?: readonly [number, number] | null): Diagnostic;
123
-
124
103
  /**
125
104
  * Theme-only project analysis — the cheap front half of compileProject.
126
105
  *
@@ -161,138 +140,6 @@ declare function analyzeProjectCSS(css: string): ProjectAnalysis;
161
140
  */
162
141
  declare function isSuppressible(code: string): boolean;
163
142
 
164
- /**
165
- * Parses utility class strings into structured tokens.
166
- *
167
- * "hover:bg-red-500" → { variants: ["hover"], utility: "bg", value: "red-500" }
168
- * "sm:p-4" → { variants: ["sm"], utility: "p", value: "4" }
169
- * "text-center!" → { variants: [], utility: "text-center", value: null, important: true }
170
- * "p-[13px]" → { variants: [], utility: "p", value: "[13px]", arbitrary: true }
171
- * "@md:flex" → { variants: ["@md"], utility: "flex", value: null }
172
- * "pl-physical-4" → { variants: [], utility: "pl", value: "4", physical: true }
173
- */
174
- interface ParsedUtility {
175
- /** Original class string before parsing */
176
- raw: string;
177
- /** Variant prefixes in order, e.g. ["hover"], ["sm", "hover"] */
178
- variants: string[];
179
- /** The utility name (everything before the value separator), e.g. "bg", "p", "text" */
180
- utility: string;
181
- /** The value part after the last `-`, or null for valueless utilities like "flex" */
182
- value: string | null;
183
- /** Whether the value is an arbitrary bracket expression like [13px] */
184
- arbitrary: boolean;
185
- /** Whether !important is applied */
186
- important: boolean;
187
- /** Whether the -physical- infix is present */
188
- physical: boolean;
189
- /** Whether the utility is negative (prefixed with -) */
190
- negative: boolean;
191
- /** When the class is an arbitrary property like [color:red] */
192
- arbitraryProperty: {
193
- property: string;
194
- value: string;
195
- } | null;
196
- /**
197
- * Explicit type hint extracted from an arbitrary value, e.g. the `length`
198
- * in `border-[length:1rem]` or `border-(color:--my-color)`. When set, it
199
- * forces a specific dispatch path in generators — see `borderGenerator`,
200
- * `colorGenerator`, etc. Null when no hint was provided.
201
- */
202
- dataType: string | null;
203
- }
204
- /**
205
- * Parse a single utility class string into its structural components.
206
- */
207
- declare function parseUtility(raw: string): ParsedUtility;
208
-
209
- /**
210
- * Shared leaf helpers for the utility generators — result types, tiny
211
- * constructors, and value-grammar helpers. Lives below both the generators
212
- * and the dispatch index (which imports every generator) so that generators
213
- * never import their own aggregator: generator ↔ index cycles would let
214
- * modules observe partially initialized exports.
215
- */
216
- interface CSSDeclaration {
217
- property: string;
218
- value: string;
219
- }
220
-
221
- /**
222
- * Variant resolution — maps variant names to CSS wrappers.
223
- *
224
- * Handles pseudo-classes, pseudo-elements, media queries, responsive breakpoints,
225
- * container queries, data/aria attributes, :has()/:not(), arbitrary variants,
226
- * and custom variants.
227
- */
228
-
229
- type VariantKind = "pseudo-class" | "pseudo-element" | "media" | "breakpoint" | "container" | "special" | "custom" | "pattern";
230
- interface VariantInfo {
231
- /** The variant as typed before the `:` — for patterns, the family prefix. */
232
- name: string;
233
- kind: VariantKind;
234
- /** What the variant emits — a selector suffix, an at-rule, or (for
235
- * patterns) a description of the accepted form. */
236
- wraps: string;
237
- }
238
- /**
239
- * Enumerate every concrete variant the given theme resolves, plus the
240
- * open-ended pattern families. Concrete entries (kind ≠ "pattern") are
241
- * guaranteed to resolve via resolveVariant — the list mirrors its checks,
242
- * including the CSS-length guard on breakpoint values.
243
- */
244
- declare function listVariants(theme: ResolvedTheme): VariantInfo[];
245
-
246
- /**
247
- * Class inspector — single-class validation and explanation for editor
248
- * tooling, running the exact resolution the compile loop performs.
249
- *
250
- * The compiler intentionally drops unknown utilities silently (reserved
251
- * RI-1001): the build scanner over-collects, so at build time an unresolved
252
- * candidate is usually noise. Inside an editor the certainty is inverted — a
253
- * token in a class attribute is meant to be a class — so the inspector
254
- * finally gives RI-1001 a voice: `validate()` reports WHY a class produces no
255
- * CSS, with a typo suggestion when one is close enough.
256
- *
257
- * An inspector instance owns the per-theme caches the compile loop rebuilds
258
- * per pass (custom-variant map, variant memo, breakpoint weights) plus a
259
- * resolution cache, so per-keystroke validation of the same classes is cheap.
260
- * Create one per theme and drop it when the theme changes.
261
- */
262
-
263
- type ClassValidation = {
264
- ok: true;
265
- } | {
266
- ok: false;
267
- reason: "unknown-utility" | "unknown-variant" | "invalid-arbitrary";
268
- /** The failing fragment — the variant for "unknown-variant", the
269
- * base class (variants stripped) otherwise. */
270
- offender: string;
271
- /** Closest known name within typo distance, when one exists. */
272
- suggestion?: string;
273
- };
274
- interface ClassExplanation {
275
- parsed: ParsedUtility;
276
- /** Root declarations before variant wrapping. */
277
- declarations: CSSDeclaration[];
278
- /** Escaped selector, including variant suffixes (`.hover\:px-2:hover`). */
279
- selector: string;
280
- /** The complete rule text, including wrapping at-rules. */
281
- css: string;
282
- /** Deterministic ordering key — lower emits earlier in generated CSS. */
283
- sortKey: number;
284
- }
285
- interface ClassInspector {
286
- readonly theme: ResolvedTheme;
287
- /** Would this class produce CSS under the theme? Reports why not. */
288
- validate(className: string): ClassValidation;
289
- /** Structured breakdown + generated CSS, or null when invalid. */
290
- explain(className: string): ClassExplanation | null;
291
- /** Every variant the theme resolves (cached). */
292
- variants(): readonly VariantInfo[];
293
- }
294
- declare function createClassInspector(theme: ResolvedTheme): ClassInspector;
295
-
296
143
  /**
297
144
  * Typo suggestion helpers using optimal string alignment (OSA) distance —
298
145
  * Levenshtein plus adjacent transpositions, so `felx` → `flex` is distance 1.
@@ -304,245 +151,36 @@ declare function createClassInspector(theme: ResolvedTheme): ClassInspector;
304
151
  */
305
152
  declare function findClosest(input: string, candidates: string[], maxDistance?: number): string | null;
306
153
 
307
- /**
308
- * The single registration table for built-in utility roots: each row binds a
309
- * set of roots to the generators that resolve them (in probe order) AND to the
310
- * value space editor enumeration tries for them. index.ts derives
311
- * PREFIX_DISPATCH from the resolver columns; enumerate.ts derives
312
- * UTILITY_VALUE_SPACES from the spec column — adding a root forces deciding
313
- * both in one row, so the two can never drift.
314
- *
315
- * Value spaces are deliberately GENEROUS (which theme namespaces and keyword
316
- * families to TRY per functional root): every enumeration candidate is probed
317
- * through the real utility resolver, which stays the single authority — a
318
- * spec can over-approximate freely and never emit something `validate()`
319
- * would reject. `{ kinds: [] }` marks a statics-only root.
320
- *
321
- * Ordering is load-bearing twice over: row order fixes PREFIX_DISPATCH key
322
- * insertion order (which drives cross-root enumeration dedup labels), and
323
- * per-root resolver order fixes which generator wins a contested root.
324
- */
325
-
326
- type ValueSpaceKind = "color" | "special-color" | "spacing" | "fraction" | "text-size" | "fluid-text-size" | "font-slot" | "weight" | "rounded" | "rounded-side" | "shadow" | "z" | "ease" | "blur" | "animation" | "leading" | "fluid-range" | "tracking" | "opacity" | "duration" | "breakpoint" | "int" | "percent" | "keywords";
327
- interface ValueSpaceSpec {
328
- kinds: readonly ValueSpaceKind[];
329
- /** Extra value parts to try verbatim (for "keywords" and beyond). */
330
- keywords?: readonly string[];
331
- }
332
-
333
- /**
334
- * Class enumeration — the completion universe for editor tooling.
335
- *
336
- * Design: candidates are generated from a deliberately GENEROUS value-space
337
- * table (which theme namespaces and keyword families to TRY per functional
338
- * root), then every candidate is probed through the real utility resolver.
339
- * The resolver is the single authority — an enumerated class is one that
340
- * actually compiles, so the table can over-approximate freely and can never
341
- * emit something `validate()` would reject. Coverage is structural: the table
342
- * derives from ROOT_GROUPS (roots.ts), where `spec` is a required field of
343
- * every row — adding a root forces deciding its value space in the same row
344
- * (an empty spec marks a statics-only root); the CI check in enumerate.test.ts
345
- * stays on as a regression tripwire.
346
- *
347
- * Statics come from the merge conflict tables (STATIC_UTILITIES) plus each
348
- * generator's own static-map keys — probed too, for the same guarantee.
349
- */
350
-
351
- /** Root → value spaces to try, derived from ROOT_GROUPS. Every
352
- * PREFIX_DISPATCH root is present by construction — both maps are built
353
- * from the same rows. */
354
- declare const UTILITY_VALUE_SPACES: ReadonlyMap<string, ValueSpaceSpec>;
355
- interface EnumeratedClass {
356
- name: string;
357
- /** How the class was produced: a static table, a custom @utility, or the
358
- * value-space kind that filled the functional root. */
359
- kind: ValueSpaceKind | "static" | "custom";
360
- /** The functional root, or null for statics/custom statics. */
361
- root: string | null;
362
- }
363
- interface ClassTemplate {
364
- root: string;
365
- /** The open-ended space: numeric spacing scale, plain numbers, or a
366
- * functional custom utility that accepts any suffix. */
367
- kind: "spacing" | "number" | "custom";
368
- example: string;
369
- }
370
- interface ClassEnumeration {
371
- /** Every finite concrete class, probed valid, sorted by name. */
372
- classes: EnumeratedClass[];
373
- /** Families whose value space is infinite — offer as snippets. */
374
- templates: ClassTemplate[];
375
- }
376
- /**
377
- * Enumerate the finite completion universe for a theme. Every returned class
378
- * has been resolved by the real utility resolver — `validate()` accepts each
379
- * one by construction. Infinite families (the spacing scale, numeric values,
380
- * functional custom utilities) come back as templates; a template is emitted
381
- * only when at least one of its probes resolved.
382
- */
383
- declare function enumerateClassNames(theme: ResolvedTheme): ClassEnumeration;
384
-
385
- /**
386
- * analyzeMerge() — editor-only merge diagnostics.
387
- *
388
- * Explains ri()'s right-most-wins conflict resolution by threading a
389
- * MergeTrace through the shared merge loop. Split from merge/index.ts so the
390
- * browser-facing runtime file stays free of editor-only analysis; consumed by
391
- * editor/session.ts and the editor entry.
392
- */
154
+ declare function stripRIDirectives(css: string): string;
393
155
 
394
- interface MergeDrop {
395
- index: number;
396
- className: string;
397
- /** Ascending indices of the surviving classes that together claimed every
398
- * CSS property this class sets (px-4 + py-4 jointly dominate p-2). */
399
- overriddenBy: number[];
400
- }
401
- interface MergeAnalysis {
402
- /** The merged output — identical to ri()'s result for this token list. */
403
- output: string;
404
- /** Indices of surviving classes, ascending. */
405
- kept: number[];
406
- /** Dropped classes with attribution, ascending by index. */
407
- dropped: MergeDrop[];
408
- }
409
156
  /**
410
- * Explain ri()'s conflict resolution for a list of class tokens — which
411
- * classes the right-most-wins scan drops, and which survivors claimed their
412
- * properties. Powers "this class is overridden" editor diagnostics.
157
+ * One sort, for everything that orders class names.
413
158
  *
414
- * Unlike ri(), the input is pre-tokenized: one class per element, no falsy
415
- * filtering, no whitespace splitting — exactly the token list an editor
416
- * extracts from one class attribute. `snapshot` binds custom utilities, text
417
- * sizes, and color names the same way createRi(snapshot) does (editors build
418
- * one with createThemeSnapshot()); without it, the module-level state of the
419
- * most recent compile applies. Uncached — call sites own their memoization.
420
- */
421
- declare function analyzeMerge(classes: readonly string[], snapshot?: CompilationSnapshot): MergeAnalysis;
422
-
423
- /**
424
- * Color swatches and theme-token introspection for editor tooling.
159
+ * A "sort classes" command in an editor, a formatter plugin, and a CLI
160
+ * codemod all have to agree, and the only way to guarantee that is for all
161
+ * three to call the same function. The order is the one the emitted CSS uses:
162
+ * `sortKey` first, then the same codepoint tie-break `compile()` applies to
163
+ * rules, so reading a sorted class attribute tells you what the cascade will
164
+ * do — including which of two conflicting classes actually wins.
425
165
  *
426
- * Resolves a theme color (and stop) to concrete light/dark colors — the same
427
- * OKLCH math the CSS variable emitter uses (generateStop / computeDarkStop),
428
- * plus an sRGB hex conversion for completion-item swatches and sidebar chips.
429
- * Pure computation, no IO.
430
- */
431
-
432
- /** The canonical palette stops every generative color defines. */
433
- declare const CANONICAL_COLOR_STOPS: readonly number[];
434
- /** Convert an OKLCH color to a #rrggbb hex string (sRGB, channel-clamped). */
435
- declare function oklchToHex(l: number, c: number, h: number): string;
436
- /** Best-effort hex for the CSS color texts theme definitions hold: `oklch()`
437
- * and hex literals. Anything else (named colors, rgb(), vars) returns null —
438
- * the raw CSS text still travels alongside. */
439
- declare function cssColorToHex(css: string): string | null;
440
- interface SwatchColor {
441
- /** CSS color text (`oklch(…)`, or the definition's own value verbatim). */
442
- css: string;
443
- /** sRGB hex, or null when the CSS text isn't convertible. */
444
- hex: string | null;
445
- }
446
- interface ColorSwatch {
447
- light: SwatchColor;
448
- /** Null when the theme's dark mode is off or the color has no dark form. */
449
- dark: SwatchColor | null;
450
- }
451
- /**
452
- * Resolve a theme color name (+ stop, for generative palettes) to concrete
453
- * light/dark swatch colors — exactly the values the emitted CSS variables
454
- * carry. Theme-defined colors always win; the fixed semantic swatches
455
- * (paper/ink/white/black) apply only when the theme does not define the name,
456
- * mirroring the emitter (a user `@color { white: … }` palette emits
457
- * `--color-white-*` variables that suffixed utilities resolve against).
458
- * Returns null for unknown names, unresolvable aliases, and keyword colors
459
- * with no concrete value (transparent, currentColor).
166
+ * **A class list that reaches `ri()` must not be sorted.** `ri()` is
167
+ * right-most-wins over its arguments, so reordering changes the result. This
168
+ * is for a static class attribute, where the CSS decides and the attribute's
169
+ * order carries no meaning at all.
460
170
  */
461
- declare function resolveColorSwatch(theme: ResolvedTheme, name: string, stop?: number): ColorSwatch | null;
462
- interface ThemeTokens {
463
- /** Color names with their definition kind — pair with resolveColorSwatch. */
464
- colors: Array<{
465
- name: string;
466
- kind: ColorDefinition["type"];
467
- }>;
468
- colorStops: readonly number[];
469
- spacingBase: string;
470
- textSizes: Array<{
471
- name: string;
472
- fontSize: string;
473
- lineHeight: string;
474
- }>;
475
- breakpoints: Record<string, string>;
476
- shadows: Record<string, string>;
477
- weights: Record<string, number>;
478
- easing: Record<string, string>;
479
- blur: Record<string, string>;
480
- z: Record<string, string>;
481
- leading: Record<string, string>;
482
- tracking: Record<string, string>;
483
- opacity: Record<string, string>;
484
- duration: Record<string, string>;
485
- /** Named radii from `@rounded { roof: 24px; }` — the class is `rounded-roof`.
486
- * Unnamed radii are spacing multiples and carry no token. */
487
- radii: Record<string, string>;
488
- /** Named ranges from `@fluid <name> { min; max; }` — each one makes the scope
489
- * class `fluid-<name>`. A bound absent from the block is absent here. */
490
- fluidRanges: Record<string, {
491
- min?: string;
492
- max?: string;
493
- }>;
494
- fonts: Array<{
495
- slot: string;
496
- family: string;
497
- }>;
498
- animations: string[];
499
- }
500
- /** One render-ready view of a theme's token namespaces for sidebar chips and
501
- * completion detail — plain data, no theme internals to traverse. */
502
- declare function listThemeTokens(theme: ResolvedTheme): ThemeTokens;
503
171
 
504
172
  /**
505
- * Editor session — the façade that ties the editor toolkit together.
173
+ * Order `classes` the way the generated stylesheet orders their rules.
506
174
  *
507
- * An editor host holds one session per workspace, calls `setCss()` whenever
508
- * the project's CSS entry changes, and everything else per keystroke. The
509
- * session owns the caching story: theme analysis, the class inspector, the
510
- * enumeration, token introspection, and the merge snapshot are computed
511
- * lazily and invalidated together when the CSS changes — callers never
512
- * juggle per-theme cache keys themselves. Every capability is also exported
513
- * à la carte from `rainbowindex/editor` for hosts that want finer control.
175
+ * Unresolvable classes come first, in their original order; everything else
176
+ * follows in emission order. Duplicates are kept — dropping a class silently
177
+ * is never the right answer for a formatter — and the result is a new array.
514
178
  *
515
- * Pure computation, no IO: the host reads the CSS file (see
516
- * CSS_ENTRY_CANDIDATES / hasRIActivation for locating it) and passes text.
179
+ * Variant groups (`hover:{underline font-bold}`) are one token here, and one
180
+ * token is all a sort can move. Expand them with `expandVariantGroups` first
181
+ * if the members should be ordered individually.
517
182
  */
518
-
519
- interface EditorSession {
520
- /** The CSS input the session is currently analyzing. */
521
- readonly css: string;
522
- /** Swap the CSS input; all theme-derived caches invalidate together.
523
- * A no-op when the text is unchanged. */
524
- setCss(css: string): void;
525
- readonly theme: ResolvedTheme;
526
- /** Positioned diagnostics for the CSS input (see ProjectAnalysis). */
527
- readonly diagnostics: readonly Diagnostic[];
528
- /** Single-class validation/explanation, cached per theme. */
529
- readonly inspector: ClassInspector;
530
- /** The finite completion universe + templates, cached per theme. */
531
- enumerate(): ClassEnumeration;
532
- /** Render-ready token namespaces, cached per theme. */
533
- tokens(): ThemeTokens;
534
- /** The merge snapshot for this theme, cached. */
535
- snapshot(): CompilationSnapshot;
536
- /** analyzeMerge bound to this theme's snapshot. */
537
- analyzeMerge(classes: readonly string[]): MergeAnalysis;
538
- /** Position-aware class extraction for a source document. */
539
- extractCandidates(content: string, path?: string): ClassCandidate[];
540
- /** Light/dark swatch for a theme color (+ stop), or null. */
541
- swatch(name: string, stop?: number): ColorSwatch | null;
542
- }
543
- declare function createEditorSession(options?: {
544
- css?: string;
545
- }): EditorSession;
183
+ declare function sortClasses(theme: ResolvedTheme, classes: readonly string[]): string[];
546
184
 
547
185
  /**
548
186
  * `rainbowindex/editor` — the IO-free toolkit editor integrations build on.
@@ -565,4 +203,4 @@ declare const EDITOR_API_VERSION = 1;
565
203
  /** Feature-detection roster for this entry. */
566
204
  declare const editorCapabilities: readonly string[];
567
205
 
568
- export { CANONICAL_COLOR_STOPS, CLASS_HELPER_NAMES, CSS_ENTRY_CANDIDATES, type CandidateOrigin, type ClassCandidate, type ClassEnumeration, type ClassExplanation, type ClassInspector, type ClassTemplate, type ClassValidation, ColorDefinition, type ColorSwatch, CompilationSnapshot, type Diagnostic, type DiagnosticSeverity, EDITOR_API_VERSION, type EditorSession, type EnumeratedClass, type MergeAnalysis, type MergeDrop, ParsedDirective, type ParsedUtility, type ProjectAnalysis, RI_IMPORT_SPECIFIERS, ResolvedTheme, type SourceExtractionInput, type SwatchColor, type ThemeTokens, UTILITY_VALUE_SPACES, VARIANT_HELPER_NAMES, type ValueSpaceKind, type ValueSpaceSpec, type VariantInfo, type VariantKind, analyzeMerge, analyzeProjectCSS, createClassInspector, createEditorSession, cssColorToHex, diagnosticFromWarning, editorCapabilities, enumerateClassNames, expandVariantGroups, extractClassCandidates, extractClasses, extractClassesFromSource, findClosest, hasRIActivation, isSourceFile, isSuppressible, listThemeTokens, listVariants, oklchToHex, parseUtility, resolveColorSwatch, severityForCode, version, warningCode };
206
+ export { CLASS_HELPER_NAMES, CSS_ENTRY_CANDIDATES, ClassCandidate, Diagnostic, EDITOR_API_VERSION, ParsedDirective, type ProjectAnalysis, RI_IMPORT_SPECIFIERS, ResolvedTheme, type SourceExtractionInput, VARIANT_HELPER_NAMES, analyzeProjectCSS, editorCapabilities, expandVariantGroups, extractClassCandidates, extractClasses, extractClassesFromSource, findClosest, hasRIActivation, isSourceFile, isSuppressible, outermostCandidates, sortClasses, stripRIDirectives, version };