rainbowindex 0.5.1 → 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 +876 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +225 -69
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -6
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-PDORZSQX.mjs → chunk-FJOZJIKB.mjs} +7814 -5174
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-4CTJLMYM.mjs → chunk-RC6DDE4L.mjs} +37 -23
  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-4UKFK2GE.mjs → chunk-XQGSG2HK.mjs} +213 -756
  18. package/dist/cli.mjs +1101 -125
  19. package/dist/{context-B9yhJxd5.d.ts → context-DcBtnnan.d.ts} +55 -108
  20. package/dist/editor.d.ts +82 -421
  21. package/dist/editor.mjs +68 -363
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-Dx-NpFFx.d.ts → imports-C9esHd5Q.d.ts} +98 -81
  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 +49 -5
  29. package/dist/index.mjs +40 -13
  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 +273 -113
  40. package/package.json +27 -5
  41. package/dist/chunk-F4VCBISU.mjs +0 -1866
  42. package/dist/chunk-RU4756NG.mjs +0 -243
  43. package/dist/safelist-DAkKuxCk.d.ts +0 -96
package/dist/editor.d.ts CHANGED
@@ -1,40 +1,10 @@
1
- import { P as ParsedDirective, R as ResolvedTheme } from './index-Dx-NpFFx.js';
2
- export { b as createThemeSnapshot } from './index-Dx-NpFFx.js';
3
- import { b as CompilationSnapshot, C as ColorDefinition } from './context-B9yhJxd5.js';
4
- export { e as defaultTheme } from './context-B9yhJxd5.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
@@ -44,7 +14,7 @@ declare const CLASS_HELPER_NAMES: readonly string[];
44
14
  declare const VARIANT_HELPER_NAMES: readonly string[];
45
15
  declare function extractClasses(source: string, warnings?: string[]): Set<string>;
46
16
 
47
- declare function expandVariantGroups(input: string, warnings?: string[]): string;
17
+ declare function expandVariantGroups(input: string, warnings?: string[], path?: string): string;
48
18
 
49
19
  interface SourceExtractionInput {
50
20
  path?: string;
@@ -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
  *
@@ -144,140 +123,22 @@ interface ProjectAnalysis {
144
123
  * at-rule, resolver problems at the directive whose body produced them.
145
124
  */
146
125
  diagnostics: Diagnostic[];
147
- }
148
- declare function analyzeProjectCSS(css: string): ProjectAnalysis;
149
-
150
- /**
151
- * Parses utility class strings into structured tokens.
152
- *
153
- * "hover:bg-red-500" → { variants: ["hover"], utility: "bg", value: "red-500" }
154
- * "sm:p-4" → { variants: ["sm"], utility: "p", value: "4" }
155
- * "text-center!" → { variants: [], utility: "text-center", value: null, important: true }
156
- * "p-[13px]" → { variants: [], utility: "p", value: "[13px]", arbitrary: true }
157
- * "@md:flex" → { variants: ["@md"], utility: "flex", value: null }
158
- * "pl-physical-4" → { variants: [], utility: "pl", value: "4", physical: true }
159
- */
160
- interface ParsedUtility {
161
- /** Original class string before parsing */
162
- raw: string;
163
- /** Variant prefixes in order, e.g. ["hover"], ["sm", "hover"] */
164
- variants: string[];
165
- /** The utility name (everything before the value separator), e.g. "bg", "p", "text" */
166
- utility: string;
167
- /** The value part after the last `-`, or null for valueless utilities like "flex" */
168
- value: string | null;
169
- /** Whether the value is an arbitrary bracket expression like [13px] */
170
- arbitrary: boolean;
171
- /** Whether !important is applied */
172
- important: boolean;
173
- /** Whether the -physical- infix is present */
174
- physical: boolean;
175
- /** Whether the utility is negative (prefixed with -) */
176
- negative: boolean;
177
- /** When the class is an arbitrary property like [color:red] */
178
- arbitraryProperty: {
179
- property: string;
180
- value: string;
181
- } | null;
182
126
  /**
183
- * Explicit type hint extracted from an arbitrary value, e.g. the `length`
184
- * in `border-[length:1rem]` or `border-(color:--my-color)`. When set, it
185
- * forces a specific dispatch path in generators — see `borderGenerator`,
186
- * `colorGenerator`, etc. Null when no hint was provided.
127
+ * Codes silenced for the whole entry by `/* ri-disable … *\/`. Later stages
128
+ * push through this so a code the author hid never reaches the caller —
129
+ * scanner and compile warnings included, which no stylesheet comment could
130
+ * reach by position.
187
131
  */
188
- dataType: string | null;
189
- }
190
- /**
191
- * Parse a single utility class string into its structural components.
192
- */
193
- declare function parseUtility(raw: string): ParsedUtility;
194
-
195
- /**
196
- * Shared leaf helpers for the utility generators — result types, tiny
197
- * constructors, and value-grammar helpers. Lives below both the generators
198
- * and the dispatch index (which imports every generator) so that generators
199
- * never import their own aggregator: generator ↔ index cycles would let
200
- * modules observe partially initialized exports.
201
- */
202
- interface CSSDeclaration {
203
- property: string;
204
- value: string;
205
- }
206
-
207
- /**
208
- * Variant resolution — maps variant names to CSS wrappers.
209
- *
210
- * Handles pseudo-classes, pseudo-elements, media queries, responsive breakpoints,
211
- * container queries, data/aria attributes, :has()/:not(), arbitrary variants,
212
- * and custom variants.
213
- */
214
-
215
- type VariantKind = "pseudo-class" | "pseudo-element" | "media" | "breakpoint" | "container" | "special" | "custom" | "pattern";
216
- interface VariantInfo {
217
- /** The variant as typed before the `:` — for patterns, the family prefix. */
218
- name: string;
219
- kind: VariantKind;
220
- /** What the variant emits — a selector suffix, an at-rule, or (for
221
- * patterns) a description of the accepted form. */
222
- wraps: string;
132
+ suppressed: ReadonlySet<string>;
223
133
  }
224
- /**
225
- * Enumerate every concrete variant the given theme resolves, plus the
226
- * open-ended pattern families. Concrete entries (kind ≠ "pattern") are
227
- * guaranteed to resolve via resolveVariant — the list mirrors its checks,
228
- * including the CSS-length guard on breakpoint values.
229
- */
230
- declare function listVariants(theme: ResolvedTheme): VariantInfo[];
134
+ declare function analyzeProjectCSS(css: string): ProjectAnalysis;
231
135
 
232
136
  /**
233
- * Class inspector — single-class validation and explanation for editor
234
- * tooling, running the exact resolution the compile loop performs.
235
- *
236
- * The compiler intentionally drops unknown utilities silently (reserved
237
- * RI-1001): the build scanner over-collects, so at build time an unresolved
238
- * candidate is usually noise. Inside an editor the certainty is inverted — a
239
- * token in a class attribute is meant to be a class — so the inspector
240
- * finally gives RI-1001 a voice: `validate()` reports WHY a class produces no
241
- * CSS, with a typo suggestion when one is close enough.
242
- *
243
- * An inspector instance owns the per-theme caches the compile loop rebuilds
244
- * per pass (custom-variant map, variant memo, breakpoint weights) plus a
245
- * resolution cache, so per-keystroke validation of the same classes is cheap.
246
- * Create one per theme and drop it when the theme changes.
137
+ * Fatal bootstrap (RI-00xx) and runtime (RI-20xx) codes are never silenceable.
138
+ * They report a broken build or a broken call, not a style choice, and a
139
+ * stylesheet that could hide them would hide the reason the build failed.
247
140
  */
248
-
249
- type ClassValidation = {
250
- ok: true;
251
- } | {
252
- ok: false;
253
- reason: "unknown-utility" | "unknown-variant" | "invalid-arbitrary";
254
- /** The failing fragment — the variant for "unknown-variant", the
255
- * base class (variants stripped) otherwise. */
256
- offender: string;
257
- /** Closest known name within typo distance, when one exists. */
258
- suggestion?: string;
259
- };
260
- interface ClassExplanation {
261
- parsed: ParsedUtility;
262
- /** Root declarations before variant wrapping. */
263
- declarations: CSSDeclaration[];
264
- /** Escaped selector, including variant suffixes (`.hover\:px-2:hover`). */
265
- selector: string;
266
- /** The complete rule text, including wrapping at-rules. */
267
- css: string;
268
- /** Deterministic ordering key — lower emits earlier in generated CSS. */
269
- sortKey: number;
270
- }
271
- interface ClassInspector {
272
- readonly theme: ResolvedTheme;
273
- /** Would this class produce CSS under the theme? Reports why not. */
274
- validate(className: string): ClassValidation;
275
- /** Structured breakdown + generated CSS, or null when invalid. */
276
- explain(className: string): ClassExplanation | null;
277
- /** Every variant the theme resolves (cached). */
278
- variants(): readonly VariantInfo[];
279
- }
280
- declare function createClassInspector(theme: ResolvedTheme): ClassInspector;
141
+ declare function isSuppressible(code: string): boolean;
281
142
 
282
143
  /**
283
144
  * Typo suggestion helpers using optimal string alignment (OSA) distance —
@@ -290,236 +151,36 @@ declare function createClassInspector(theme: ResolvedTheme): ClassInspector;
290
151
  */
291
152
  declare function findClosest(input: string, candidates: string[], maxDistance?: number): string | null;
292
153
 
293
- /**
294
- * The single registration table for built-in utility roots: each row binds a
295
- * set of roots to the generators that resolve them (in probe order) AND to the
296
- * value space editor enumeration tries for them. index.ts derives
297
- * PREFIX_DISPATCH from the resolver columns; enumerate.ts derives
298
- * UTILITY_VALUE_SPACES from the spec column — adding a root forces deciding
299
- * both in one row, so the two can never drift.
300
- *
301
- * Value spaces are deliberately GENEROUS (which theme namespaces and keyword
302
- * families to TRY per functional root): every enumeration candidate is probed
303
- * through the real utility resolver, which stays the single authority — a
304
- * spec can over-approximate freely and never emit something `validate()`
305
- * would reject. `{ kinds: [] }` marks a statics-only root.
306
- *
307
- * Ordering is load-bearing twice over: row order fixes PREFIX_DISPATCH key
308
- * insertion order (which drives cross-root enumeration dedup labels), and
309
- * per-root resolver order fixes which generator wins a contested root.
310
- */
311
-
312
- type ValueSpaceKind = "color" | "special-color" | "spacing" | "fraction" | "text-size" | "fluid-text-size" | "font-slot" | "weight" | "rounded" | "rounded-side" | "shadow" | "z" | "ease" | "blur" | "animation" | "leading" | "tracking" | "opacity" | "duration" | "breakpoint" | "int" | "percent" | "keywords";
313
- interface ValueSpaceSpec {
314
- kinds: readonly ValueSpaceKind[];
315
- /** Extra value parts to try verbatim (for "keywords" and beyond). */
316
- keywords?: readonly string[];
317
- }
318
-
319
- /**
320
- * Class enumeration — the completion universe for editor tooling.
321
- *
322
- * Design: candidates are generated from a deliberately GENEROUS value-space
323
- * table (which theme namespaces and keyword families to TRY per functional
324
- * root), then every candidate is probed through the real utility resolver.
325
- * The resolver is the single authority — an enumerated class is one that
326
- * actually compiles, so the table can over-approximate freely and can never
327
- * emit something `validate()` would reject. Coverage is structural: the table
328
- * derives from ROOT_GROUPS (roots.ts), where `spec` is a required field of
329
- * every row — adding a root forces deciding its value space in the same row
330
- * (an empty spec marks a statics-only root); the CI check in enumerate.test.ts
331
- * stays on as a regression tripwire.
332
- *
333
- * Statics come from the merge conflict tables (STATIC_UTILITIES) plus each
334
- * generator's own static-map keys — probed too, for the same guarantee.
335
- */
336
-
337
- /** Root → value spaces to try, derived from ROOT_GROUPS. Every
338
- * PREFIX_DISPATCH root is present by construction — both maps are built
339
- * from the same rows. */
340
- declare const UTILITY_VALUE_SPACES: ReadonlyMap<string, ValueSpaceSpec>;
341
- interface EnumeratedClass {
342
- name: string;
343
- /** How the class was produced: a static table, a custom @utility, or the
344
- * value-space kind that filled the functional root. */
345
- kind: ValueSpaceKind | "static" | "custom";
346
- /** The functional root, or null for statics/custom statics. */
347
- root: string | null;
348
- }
349
- interface ClassTemplate {
350
- root: string;
351
- /** The open-ended space: numeric spacing scale, plain numbers, or a
352
- * functional custom utility that accepts any suffix. */
353
- kind: "spacing" | "number" | "custom";
354
- example: string;
355
- }
356
- interface ClassEnumeration {
357
- /** Every finite concrete class, probed valid, sorted by name. */
358
- classes: EnumeratedClass[];
359
- /** Families whose value space is infinite — offer as snippets. */
360
- templates: ClassTemplate[];
361
- }
362
- /**
363
- * Enumerate the finite completion universe for a theme. Every returned class
364
- * has been resolved by the real utility resolver — `validate()` accepts each
365
- * one by construction. Infinite families (the spacing scale, numeric values,
366
- * functional custom utilities) come back as templates; a template is emitted
367
- * only when at least one of its probes resolved.
368
- */
369
- declare function enumerateClassNames(theme: ResolvedTheme): ClassEnumeration;
370
-
371
- /**
372
- * analyzeMerge() — editor-only merge diagnostics.
373
- *
374
- * Explains ri()'s right-most-wins conflict resolution by threading a
375
- * MergeTrace through the shared merge loop. Split from merge/index.ts so the
376
- * browser-facing runtime file stays free of editor-only analysis; consumed by
377
- * editor/session.ts and the editor entry.
378
- */
154
+ declare function stripRIDirectives(css: string): string;
379
155
 
380
- interface MergeDrop {
381
- index: number;
382
- className: string;
383
- /** Ascending indices of the surviving classes that together claimed every
384
- * CSS property this class sets (px-4 + py-4 jointly dominate p-2). */
385
- overriddenBy: number[];
386
- }
387
- interface MergeAnalysis {
388
- /** The merged output — identical to ri()'s result for this token list. */
389
- output: string;
390
- /** Indices of surviving classes, ascending. */
391
- kept: number[];
392
- /** Dropped classes with attribution, ascending by index. */
393
- dropped: MergeDrop[];
394
- }
395
156
  /**
396
- * Explain ri()'s conflict resolution for a list of class tokens — which
397
- * classes the right-most-wins scan drops, and which survivors claimed their
398
- * properties. Powers "this class is overridden" editor diagnostics.
157
+ * One sort, for everything that orders class names.
399
158
  *
400
- * Unlike ri(), the input is pre-tokenized: one class per element, no falsy
401
- * filtering, no whitespace splitting — exactly the token list an editor
402
- * extracts from one class attribute. `snapshot` binds custom utilities, text
403
- * sizes, and color names the same way createRi(snapshot) does (editors build
404
- * one with createThemeSnapshot()); without it, the module-level state of the
405
- * most recent compile applies. Uncached — call sites own their memoization.
406
- */
407
- declare function analyzeMerge(classes: readonly string[], snapshot?: CompilationSnapshot): MergeAnalysis;
408
-
409
- /**
410
- * 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.
411
165
  *
412
- * Resolves a theme color (and stop) to concrete light/dark colors — the same
413
- * OKLCH math the CSS variable emitter uses (generateStop / computeDarkStop),
414
- * plus an sRGB hex conversion for completion-item swatches and sidebar chips.
415
- * Pure computation, no IO.
416
- */
417
-
418
- /** The canonical palette stops every generative color defines. */
419
- declare const CANONICAL_COLOR_STOPS: readonly number[];
420
- /** Convert an OKLCH color to a #rrggbb hex string (sRGB, channel-clamped). */
421
- declare function oklchToHex(l: number, c: number, h: number): string;
422
- /** Best-effort hex for the CSS color texts theme definitions hold: `oklch()`
423
- * and hex literals. Anything else (named colors, rgb(), vars) returns null —
424
- * the raw CSS text still travels alongside. */
425
- declare function cssColorToHex(css: string): string | null;
426
- interface SwatchColor {
427
- /** CSS color text (`oklch(…)`, or the definition's own value verbatim). */
428
- css: string;
429
- /** sRGB hex, or null when the CSS text isn't convertible. */
430
- hex: string | null;
431
- }
432
- interface ColorSwatch {
433
- light: SwatchColor;
434
- /** Null when the theme's dark mode is off or the color has no dark form. */
435
- dark: SwatchColor | null;
436
- }
437
- /**
438
- * Resolve a theme color name (+ stop, for generative palettes) to concrete
439
- * light/dark swatch colors — exactly the values the emitted CSS variables
440
- * carry. Theme-defined colors always win; the fixed semantic swatches
441
- * (paper/ink/white/black) apply only when the theme does not define the name,
442
- * mirroring the emitter (a user `@color { white: … }` palette emits
443
- * `--color-white-*` variables that suffixed utilities resolve against).
444
- * Returns null for unknown names, unresolvable aliases, and keyword colors
445
- * 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.
446
170
  */
447
- declare function resolveColorSwatch(theme: ResolvedTheme, name: string, stop?: number): ColorSwatch | null;
448
- interface ThemeTokens {
449
- /** Color names with their definition kind — pair with resolveColorSwatch. */
450
- colors: Array<{
451
- name: string;
452
- kind: ColorDefinition["type"];
453
- }>;
454
- colorStops: readonly number[];
455
- spacingBase: string;
456
- textSizes: Array<{
457
- name: string;
458
- fontSize: string;
459
- lineHeight: string;
460
- }>;
461
- breakpoints: Record<string, string>;
462
- shadows: Record<string, string>;
463
- weights: Record<string, number>;
464
- easing: Record<string, string>;
465
- blur: Record<string, string>;
466
- z: Record<string, string>;
467
- leading: Record<string, string>;
468
- tracking: Record<string, string>;
469
- opacity: Record<string, string>;
470
- duration: Record<string, string>;
471
- fonts: Array<{
472
- slot: string;
473
- family: string;
474
- }>;
475
- animations: string[];
476
- }
477
- /** One render-ready view of a theme's token namespaces for sidebar chips and
478
- * completion detail — plain data, no theme internals to traverse. */
479
- declare function listThemeTokens(theme: ResolvedTheme): ThemeTokens;
480
171
 
481
172
  /**
482
- * Editor session — the façade that ties the editor toolkit together.
173
+ * Order `classes` the way the generated stylesheet orders their rules.
483
174
  *
484
- * An editor host holds one session per workspace, calls `setCss()` whenever
485
- * the project's CSS entry changes, and everything else per keystroke. The
486
- * session owns the caching story: theme analysis, the class inspector, the
487
- * enumeration, token introspection, and the merge snapshot are computed
488
- * lazily and invalidated together when the CSS changes — callers never
489
- * juggle per-theme cache keys themselves. Every capability is also exported
490
- * à 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.
491
178
  *
492
- * Pure computation, no IO: the host reads the CSS file (see
493
- * 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.
494
182
  */
495
-
496
- interface EditorSession {
497
- /** The CSS input the session is currently analyzing. */
498
- readonly css: string;
499
- /** Swap the CSS input; all theme-derived caches invalidate together.
500
- * A no-op when the text is unchanged. */
501
- setCss(css: string): void;
502
- readonly theme: ResolvedTheme;
503
- /** Positioned diagnostics for the CSS input (see ProjectAnalysis). */
504
- readonly diagnostics: readonly Diagnostic[];
505
- /** Single-class validation/explanation, cached per theme. */
506
- readonly inspector: ClassInspector;
507
- /** The finite completion universe + templates, cached per theme. */
508
- enumerate(): ClassEnumeration;
509
- /** Render-ready token namespaces, cached per theme. */
510
- tokens(): ThemeTokens;
511
- /** The merge snapshot for this theme, cached. */
512
- snapshot(): CompilationSnapshot;
513
- /** analyzeMerge bound to this theme's snapshot. */
514
- analyzeMerge(classes: readonly string[]): MergeAnalysis;
515
- /** Position-aware class extraction for a source document. */
516
- extractCandidates(content: string, path?: string): ClassCandidate[];
517
- /** Light/dark swatch for a theme color (+ stop), or null. */
518
- swatch(name: string, stop?: number): ColorSwatch | null;
519
- }
520
- declare function createEditorSession(options?: {
521
- css?: string;
522
- }): EditorSession;
183
+ declare function sortClasses(theme: ResolvedTheme, classes: readonly string[]): string[];
523
184
 
524
185
  /**
525
186
  * `rainbowindex/editor` — the IO-free toolkit editor integrations build on.
@@ -542,4 +203,4 @@ declare const EDITOR_API_VERSION = 1;
542
203
  /** Feature-detection roster for this entry. */
543
204
  declare const editorCapabilities: readonly string[];
544
205
 
545
- 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, 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 };