@estiva-app/ui 0.13.1 → 0.15.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 (91) hide show
  1. package/README.md +28 -0
  2. package/dist/Avatar.d.ts +1 -1
  3. package/dist/Avatar.d.ts.map +1 -1
  4. package/dist/AvatarGroup.d.ts.map +1 -1
  5. package/dist/ChipInput.d.ts +14 -1
  6. package/dist/ChipInput.d.ts.map +1 -1
  7. package/dist/Divider.d.ts +5 -2
  8. package/dist/Divider.d.ts.map +1 -1
  9. package/dist/EmptyState.d.ts +3 -0
  10. package/dist/EmptyState.d.ts.map +1 -1
  11. package/dist/Property.d.ts +7 -0
  12. package/dist/Property.d.ts.map +1 -1
  13. package/dist/Reaction.d.ts +12 -6
  14. package/dist/Reaction.d.ts.map +1 -1
  15. package/dist/TextInput.d.ts +10 -1
  16. package/dist/TextInput.d.ts.map +1 -1
  17. package/dist/Toast.d.ts +19 -6
  18. package/dist/Toast.d.ts.map +1 -1
  19. package/dist/Toolbar.d.ts +1 -1
  20. package/dist/Toolbar.d.ts.map +1 -1
  21. package/dist/eslint/escape.d.ts +61 -0
  22. package/dist/eslint/escape.d.ts.map +1 -0
  23. package/dist/eslint/index.d.ts +45 -0
  24. package/dist/eslint/index.d.ts.map +1 -0
  25. package/dist/eslint/index.js +118 -0
  26. package/dist/eslint/index.js.map +7 -0
  27. package/dist/eslint/no-raw-button.d.ts +11 -0
  28. package/dist/eslint/no-raw-button.d.ts.map +1 -0
  29. package/dist/index.js +276 -249
  30. package/dist/index.js.map +3 -3
  31. package/package.json +5 -1
  32. package/src/AttachmentCard.tsx +14 -14
  33. package/src/Avatar.mdx +7 -2
  34. package/src/Avatar.picture.test.tsx +60 -0
  35. package/src/Avatar.stories.tsx +1 -1
  36. package/src/Avatar.tsx +50 -14
  37. package/src/AvatarGroup.tsx +1 -0
  38. package/src/Breadcrumb.tsx +2 -2
  39. package/src/Checkbox.tsx +1 -1
  40. package/src/Chip.tsx +1 -1
  41. package/src/ChipInput.mdx +9 -0
  42. package/src/ChipInput.stories.tsx +18 -0
  43. package/src/ChipInput.test.tsx +18 -0
  44. package/src/ChipInput.tsx +18 -4
  45. package/src/DialogShell.stories.tsx +1 -1
  46. package/src/Divider.mdx +3 -2
  47. package/src/Divider.test.tsx +15 -0
  48. package/src/Divider.tsx +11 -9
  49. package/src/EditableText.stories.tsx +2 -2
  50. package/src/EmptyState.mdx +3 -2
  51. package/src/EmptyState.stories.tsx +7 -3
  52. package/src/EmptyState.test.tsx +15 -0
  53. package/src/EmptyState.tsx +4 -1
  54. package/src/IconButton.stories.tsx +1 -1
  55. package/src/Kbd.tsx +3 -3
  56. package/src/Menu.stories.tsx +2 -2
  57. package/src/Menu.tsx +3 -3
  58. package/src/Popover.stories.tsx +1 -1
  59. package/src/PreviewCard.stories.tsx +5 -5
  60. package/src/Property.mdx +8 -0
  61. package/src/Property.stories.tsx +4 -4
  62. package/src/Property.test.tsx +44 -0
  63. package/src/Property.tsx +23 -8
  64. package/src/RailItem.tsx +1 -1
  65. package/src/Reaction.mdx +14 -3
  66. package/src/Reaction.test.tsx +62 -0
  67. package/src/Reaction.tsx +21 -12
  68. package/src/ReactionPicker.tsx +1 -1
  69. package/src/SectionLabel.stories.tsx +1 -1
  70. package/src/SectionLabel.tsx +1 -1
  71. package/src/Select.test.tsx +20 -3
  72. package/src/Select.tsx +3 -3
  73. package/src/TextInput.mdx +6 -0
  74. package/src/TextInput.stories.tsx +45 -0
  75. package/src/TextInput.test.tsx +38 -0
  76. package/src/TextInput.tsx +16 -4
  77. package/src/Textarea.tsx +1 -1
  78. package/src/Toast.mdx +28 -4
  79. package/src/Toast.stories.tsx +12 -1
  80. package/src/Toast.test.tsx +137 -0
  81. package/src/Toast.tsx +127 -83
  82. package/src/Toolbar.tsx +4 -2
  83. package/src/cn.ts +1 -1
  84. package/src/eslint/escape.ts +112 -0
  85. package/src/eslint/index.test.ts +82 -0
  86. package/src/eslint/index.ts +100 -0
  87. package/src/eslint/no-raw-button.test.ts +118 -0
  88. package/src/eslint/no-raw-button.ts +39 -0
  89. package/stories/Choosing.mdx +1 -0
  90. package/stories/TokensPage.tsx +6 -1
  91. package/tailwind-preset.js +7 -0
package/README.md CHANGED
@@ -63,6 +63,34 @@ export default {
63
63
  }
64
64
  ```
65
65
 
66
+ ### The lint rules — `@estiva-app/ui/eslint`
67
+
68
+ The UI Guardrails' rules ship with the package, as an ESLint plugin, so an app
69
+ gets a new rule with a version bump. Each rule names the component to use
70
+ instead. Today there is one: `estiva/no-raw-button`.
71
+
72
+ ```js
73
+ // eslint.config.js — alongside your own rules
74
+ import estiva from '@estiva-app/ui/eslint'
75
+ export default [
76
+ // …your config, with a parser that reads TSX
77
+ { files: ['src/**/*.tsx'], ignores: ['**/*.test.tsx'], ...estiva.configs.recommended },
78
+ ]
79
+ ```
80
+
81
+ A place that keeps a raw element on purpose says why, on the line above:
82
+
83
+ ```tsx
84
+ // @estiva-escape: the reason, at least ten characters
85
+ <button …>
86
+ ```
87
+
88
+ or, as a JSX child, `{/* @estiva-escape: the reason */}`. Not `eslint-disable`:
89
+ it switches the rule off without saying why, and the gate counts it as a
90
+ failure. `countGates(results)` counts errors and escapes for
91
+ `.gates-count.json`; lint with `settings: { estiva: { reportEscapes: true } }`
92
+ for the escapes to be counted.
93
+
66
94
  ## The rules
67
95
 
68
96
  ### Forking is allowed
package/dist/Avatar.d.ts CHANGED
@@ -7,7 +7,7 @@ export declare const hueFor: (key: string) => string;
7
7
  */
8
8
  export declare const initialsFor: (name: string) => string;
9
9
  export interface AvatarProps {
10
- /** The picture URL, resolved by the caller. A picture that fails to load falls back to the initials. */
10
+ /** The picture URL, resolved by the caller. Until it arrives, and if it fails, the initials show. */
11
11
  src?: string;
12
12
  /** The person's name — where the initials and the colour come from. */
13
13
  name?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../src/Avatar.tsx"],"names":[],"mappings":"AA2BA,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,WAIjC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,WAIvC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,wGAAwG;IACxG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uGAAuG;IACvG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAQ,EAAE,IAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,WAAW,+BAiD/F"}
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../src/Avatar.tsx"],"names":[],"mappings":"AA+BA,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,WAIjC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,WAIvC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,qGAAqG;IACrG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uGAAuG;IACvG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAQ,EAAE,IAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,WAAW,+BAiF/F"}
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../src/AvatarGroup.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,IAAS,EAAE,EAAE,gBAAgB,+BAiCnE"}
1
+ {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../src/AvatarGroup.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,IAAS,EAAE,EAAE,gBAAgB,+BAkCnE"}
@@ -16,9 +16,22 @@ export interface InputChipProps {
16
16
  leading?: ReactNode;
17
17
  /** Draws the ✕; absent, the chip is display-only. */
18
18
  onRemove?: () => void;
19
+ /**
20
+ * The ✕'s name for a screen reader. `Remove <label>` when not given. A chip
21
+ * whose ✕ does something other than remove it names what it does — a
22
+ * launcher's scope chip leaves the scope ("Leave Ship").
23
+ */
24
+ removeLabel?: string;
25
+ /**
26
+ * Cut a long label with an ellipsis once the chip is capped, by a
27
+ * `max-w-*` on `className`. Off unless asked: cutting clips up to 4px of a
28
+ * letter's soft edge at 1x even when the label fits (measured 2026-09-15),
29
+ * so a chip that is never capped keeps every pixel.
30
+ */
31
+ truncate?: boolean;
19
32
  className?: string;
20
33
  }
21
- export declare function InputChip({ label, leading, onRemove, className }: InputChipProps): import("react").JSX.Element;
34
+ export declare function InputChip({ label, leading, onRemove, removeLabel, truncate, className }: InputChipProps): import("react").JSX.Element;
22
35
  /**
23
36
  * A multi-select input: chips for the chosen, a typeahead for the rest —
24
37
  * Peek's PersonChipInput (2026-09-01), generalised on the way in, and on Base
@@ -1 +1 @@
1
- {"version":3,"file":"ChipInput.d.ts","sourceRoot":"","sources":["../src/ChipInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAuB7E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,+BAwBhF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACzE,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IAC7B,4CAA4C;IAC5C,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,qDAAqD;IACrD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACtC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACrC,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,EACrE,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,SAAS,EACT,UAAe,EACf,WAAW,EACX,UAAU,EACV,GAAG,IAAI,EACR,EAAE,cAAc,CAAC,CAAC,CAAC,+BAkJnB"}
1
+ {"version":3,"file":"ChipInput.d.ts","sourceRoot":"","sources":["../src/ChipInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAuB7E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,+BAyBvG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACzE,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IAC7B,4CAA4C;IAC5C,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,qDAAqD;IACrD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACtC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACrC,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,EACrE,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,SAAS,EACT,UAAe,EACf,WAAW,EACX,UAAU,EACV,GAAG,IAAI,EACR,EAAE,cAAc,CAAC,CAAC,CAAC,+BAkJnB"}
package/dist/Divider.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Peek's Divider (2026-08-28): a hairline in `border-subtle`, inset 12px each
3
3
  * side. Plus what Ship added: `orientation="vertical"` — the same hairline
4
- * standing up, stretching to its row's height, no inset — and the separator
5
- * role for assistive tech.
4
+ * standing up, stretching to its row's height, no inset.
5
+ *
6
+ * On Base UI's `Separator` since stage 6 of the migration (2026-09-14). It
7
+ * writes the `separator` role and `aria-orientation` this component used to
8
+ * write by hand, and adds `data-orientation`. Nothing it draws changed.
6
9
  */
7
10
  export interface DividerProps {
8
11
  orientation?: 'horizontal' | 'vertical';
@@ -1 +1 @@
1
- {"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../src/Divider.tsx"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,WAA0B,EAAE,KAAK,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,YAAY,+BA2BvG"}
1
+ {"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../src/Divider.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,WAA0B,EAAE,KAAK,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,YAAY,+BAyBvG"}
@@ -12,6 +12,9 @@ import type { ReactNode } from 'react';
12
12
  * - `section` — one section of a page is empty, and the page has other
13
13
  * things on it: the line alone, left-aligned, no icon. A section's
14
14
  * emptiness is a line among the page's content, not a stage of its own.
15
+ * It is the quiet line — caption size in the muted colour, as a Field's
16
+ * helper line is — so beside rows it reads as "nothing here", not as one
17
+ * more row (Katerina, 2026-09-15: "not that intense", "smaller font").
15
18
  *
16
19
  * The message is the caller's — a shared component has no words of its own
17
20
  * for what is missing.
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../src/EmptyState.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAc,EAAE,SAAS,EAAE,EAAE,eAAe,+BAUvF"}
1
+ {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../src/EmptyState.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAc,EAAE,SAAS,EAAE,EAAE,eAAe,+BAUvF"}
@@ -13,6 +13,13 @@ import type { ReactNode } from 'react';
13
13
  * is the `menu` token (9px / 115% / 500), uppercase and letter-spaced —
14
14
  * its own voice, deliberately not SectionLabel's (unmerged, Katerina
15
15
  * 2026-09-01). A literal string, not merged, so the token size survives.
16
+ *
17
+ * **A name and its value, in the markup made for it** (stage 6 of the
18
+ * migration, 2026-09-14). Base UI has no part for this, so it is HTML's own:
19
+ * a `<dl>` holding one `<dt>` (the label) and one `<dd>` (the value), so a
20
+ * screen reader hears the label as the name of what follows rather than as a
21
+ * loose word beside it. Each property is a list of one, which needs no
22
+ * wrapper from the caller.
16
23
  */
17
24
  export interface PropertyProps {
18
25
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Property.d.ts","sourceRoot":"","sources":["../src/Property.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAkBrF"}
1
+ {"version":3,"file":"Property.d.ts","sourceRoot":"","sources":["../src/Property.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAUD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAkBrF"}
@@ -20,8 +20,10 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
20
20
  * 1px hairline against no border at all — which is legible and is not the
21
21
  * signal Peek's accent fill gives.
22
22
  *
23
- * It is a real `<button>` with `aria-pressed`, so the state reaches assistive
24
- * tech as a toggle rather than as a colour.
23
+ * On Base UI's `Toggle` since stage 6 of the migration (2026-09-14): a native
24
+ * `<button>` with `aria-pressed` and `data-pressed`, so the state reaches
25
+ * assistive tech as a toggle rather than as a colour. Base UI writes both;
26
+ * this component used to write the first by hand.
25
27
  *
26
28
  * ## `aria-label` is required, and the reason is specific
27
29
  *
@@ -37,12 +39,16 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
37
39
  * `small`, the same 8px horizontal padding, the `chip` type token for the
38
40
  * count so it sits at 11px/500 like every other count in the system.
39
41
  */
40
- export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
42
+ export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type' | 'value'> {
41
43
  /** The emoji, drawn decoratively — name the control with `aria-label`. */
42
44
  emoji: ReactNode;
43
- /** How many people reacted. Drawn as-is; `0` is not a reaction and is not drawn. */
45
+ /** How many people reacted. Drawn as it is. A count of `0` is not a reaction: do not render one. */
44
46
  count: number;
45
- /** You are one of them: the accent tint and edge, and `aria-pressed`. */
47
+ /**
48
+ * You are one of them: the accent tint and edge, and `aria-pressed`. The
49
+ * caller owns it — a press calls `onClick` and the pill keeps showing this
50
+ * value until the caller changes it.
51
+ */
46
52
  pressed?: boolean;
47
53
  /**
48
54
  * Names the control. **Required** — the emoji is decorative and the count is
@@ -50,5 +56,5 @@ export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonEleme
50
56
  */
51
57
  'aria-label': string;
52
58
  }
53
- export declare function Reaction({ emoji, count, pressed, className, type, ...props }: ReactionProps): import("react").JSX.Element;
59
+ export declare function Reaction({ emoji, count, pressed, className, ...props }: ReactionProps): import("react").JSX.Element;
54
60
  //# sourceMappingURL=Reaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Reaction.d.ts","sourceRoot":"","sources":["../src/Reaction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC9F,0EAA0E;IAC1E,KAAK,EAAE,SAAS,CAAA;IAChB,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAe,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BAqCnG"}
1
+ {"version":3,"file":"Reaction.d.ts","sourceRoot":"","sources":["../src/Reaction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACjH,0EAA0E;IAC1E,KAAK,EAAE,SAAS,CAAA;IAChB,oGAAoG;IACpG,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAe,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BAuC7F"}
@@ -12,6 +12,15 @@ import { type InputHTMLAttributes } from 'react';
12
12
  * context behind it — is gone. Outside a `Field` it behaves as before: the
13
13
  * field context has a default, so nothing has to be wrapped.
14
14
  */
15
- export type TextInputProps = InputHTMLAttributes<HTMLInputElement>;
15
+ export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
16
+ /**
17
+ * `small` is 24px tall with 12px text — the small `Select`'s size, so a
18
+ * field can share a dense row with small selects (D67, 2026-09-13: Peek's
19
+ * reference widget hand-built one because this was 38px). Replaces the
20
+ * native `size` attribute, which counts characters and which no caller
21
+ * used; set a width with the layout instead.
22
+ */
23
+ size?: 'default' | 'small';
24
+ }
16
25
  export declare const TextInput: import("react").ForwardRefExoticComponent<TextInputProps & import("react").RefAttributes<HTMLInputElement>>;
17
26
  //# sourceMappingURL=TextInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,CAAA;AAElE,eAAO,MAAM,SAAS,6GAgBpB,CAAA"}
1
+ {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACzF;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAC3B;AAED,eAAO,MAAM,SAAS,6GAmBpB,CAAA"}
package/dist/Toast.d.ts CHANGED
@@ -8,9 +8,12 @@ import { type ReactNode } from 'react';
8
8
  * the same dark overlay pill; the type lives in the icon's colour and glow,
9
9
  * not the surface.
10
10
  *
11
- * The provider owns positioning (bottom-left), the portal, and auto-dismiss
12
- * (5 s; pass `durationMs: 0` to keep one up). One toast at a time — a new
13
- * one replaces the standing one, it does not queue.
11
+ * The provider sits on Base UI's `Toast` since stage 6 of the migration
12
+ * (2026-09-14). Base UI owns the portal, the timers, the stack and what a
13
+ * screen reader hears; this file owns how a toast looks and where the stack
14
+ * stands (bottom-left). **Up to three show at once** (D7): the newest nearest
15
+ * the corner, a fourth hides the oldest until one of the three closes. Before
16
+ * stage 6 a new toast replaced the standing one, and nothing announced either.
14
17
  */
15
18
  export type ToastType = 'success' | 'brand' | 'neutral' | 'warning' | 'error';
16
19
  export interface ToastProps {
@@ -33,12 +36,20 @@ export interface ToastProps {
33
36
  onAction?: () => void;
34
37
  className?: string;
35
38
  }
39
+ /**
40
+ * One toast, drawn in place. What the provider shows is this pill; draw it
41
+ * yourself only where a page needs a toast's look without its timing — a
42
+ * story, a picture of the set.
43
+ */
36
44
  export declare function Toast({ label, type, leadingIcon, actionLabel, onAction, className }: ToastProps): import("react").JSX.Element;
37
45
  export interface ToastOptions {
38
46
  label: string;
39
47
  /** Which of the five this is — it decides the surface and the leading icon. Defaults to 'neutral'. */
40
48
  type?: ToastType;
41
- /** When set, renders a clickable action on the right side. */
49
+ /**
50
+ * Renders an action on the right side. Pressing it runs `onAction`, if
51
+ * given, and closes this toast — so a Dismiss needs only its label.
52
+ */
42
53
  actionLabel?: string;
43
54
  onAction?: () => void;
44
55
  /** Show the leading icon — a check, a `!` or an `×`, per `type`. Defaults to true. */
@@ -47,8 +58,10 @@ export interface ToastOptions {
47
58
  durationMs?: number;
48
59
  }
49
60
  interface ToastValue {
50
- showToast: (opts: ToastOptions) => void;
51
- dismissToast: () => void;
61
+ /** Shows a toast and returns its id, for `dismissToast`. */
62
+ showToast: (opts: ToastOptions) => string;
63
+ /** Closes the toast with this id; with no id, closes every toast on screen. */
64
+ dismissToast: (id?: string) => void;
52
65
  }
53
66
  export declare function ToastProvider({ children }: {
54
67
  children: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAK5G;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA8CD,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAgB,EAAE,WAAkB,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,UAAU,+BAiClH;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,sGAAsG;IACtG,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAMD,UAAU,UAAU;IAClB,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;IACvC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAMD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAiDlE;AAED,wBAAgB,QAAQ,IAAI,UAAU,CAIrC"}
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAK1E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAgB,EAAE,WAAkB,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,UAAU,+BAelH;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,sGAAsG;IACtG,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,UAAU,UAAU;IAClB,4DAA4D;IAC5D,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,CAAA;IACzC,+EAA+E;IAC/E,YAAY,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAcD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAgClE;AAsCD,wBAAgB,QAAQ,IAAI,UAAU,CAIrC"}
package/dist/Toolbar.d.ts CHANGED
@@ -83,7 +83,7 @@ export declare function ToolbarButton({ ref, disabled, disabledReason, ...props
83
83
  * a `TextInput` dropped in the row.
84
84
  */
85
85
  export type ToolbarInputProps = TextInputProps;
86
- export declare function ToolbarInput(props: ToolbarInputProps): import("react").JSX.Element;
86
+ export declare function ToolbarInput({ size, ...props }: ToolbarInputProps): import("react").JSX.Element;
87
87
  /**
88
88
  * The hairline between two groups of controls. It is `Divider`'s rule and
89
89
  * `Toolbar.Separator`'s role, so a screen reader hears the grouping a sighted
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../src/Toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAE/D,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,SAAS,CAAA;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,WAA0B,EAAE,SAAgB,EAAE,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,+BAwBnJ;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BAqB5F;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAEpD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BASrE"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../src/Toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAE/D,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,SAAS,CAAA;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,WAA0B,EAAE,SAAgB,EAAE,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,+BAwBnJ;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BAqB5F;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,+BAIjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BASrE"}
@@ -0,0 +1,61 @@
1
+ import type { AST, Rule } from 'eslint';
2
+ /**
3
+ * The escape marker (UIG-3, seam S4 in docs/GATES.md §16): one written reason
4
+ * why one element stays as it is.
5
+ *
6
+ * A line comment directly above the element,
7
+ *
8
+ * // @estiva-escape: <reason>
9
+ *
10
+ * or, where the element is a JSX child, the JSX comment on the line above,
11
+ *
12
+ * {/* @estiva-escape: <reason> *\/}
13
+ *
14
+ * with at least ten characters of reason, spaces not counted.
15
+ *
16
+ * Not `eslint-disable`. A directive switches a rule off without saying it was
17
+ * meant, and the count of escapes (`.gates-count.json`) could not see it. A
18
+ * marker written inside a directive that switches one of these rules off is
19
+ * reported for that reason.
20
+ *
21
+ * The token lint's older notes (`eslint-disable-next-line <rule> -- @estiva-escape:
22
+ * <reason>`, Katerina's ruling A2 of 15 September) are not read here: they
23
+ * escape the token lint's rules, which are not this plugin's, and they stay as
24
+ * they are (Katerina, 15 September).
25
+ */
26
+ export declare const ESCAPE_MARKER = "@estiva-escape";
27
+ /** Characters of reason, spaces not counted. */
28
+ export declare const MIN_REASON = 10;
29
+ /** The key under ESLint's `settings` this plugin reads. */
30
+ export declare const SETTINGS_KEY = "estiva";
31
+ /**
32
+ * Every rule spreads these into its `meta.messages`, because `isEscaped`
33
+ * reports through the rule that called it.
34
+ */
35
+ export declare const ESCAPE_MESSAGES: {
36
+ readonly escapeWithoutReason: "An escape needs its reason, at least 10 characters: `// @estiva-escape: <why this stays>`. An escape with no reason hides nothing.";
37
+ readonly escapeInDirective: "Write the escape as its own comment on the line above: `// @estiva-escape: <reason>`. Inside an eslint-disable comment it switches the rule off instead of recording why.";
38
+ readonly escaped: "Escaped: {{reason}}";
39
+ };
40
+ export interface EstivaSettings {
41
+ /**
42
+ * Report every sanctioned escape as a message with the id `escaped`, so a
43
+ * count can read them. Off in the lint anyone runs; on only in the count.
44
+ */
45
+ reportEscapes?: boolean;
46
+ }
47
+ interface Located {
48
+ loc?: AST.SourceLocation | null;
49
+ range?: [number, number];
50
+ }
51
+ /**
52
+ * Whether the element at `node` carries a valid escape on the line above.
53
+ *
54
+ * Every rule of this plugin calls it before it reports. It reports, through
55
+ * that rule, a marker with too short a reason and a marker inside an
56
+ * eslint-disable directive for this rule; either way the element is not
57
+ * escaped, so the rule reports it too.
58
+ */
59
+ export declare function isEscaped(context: Rule.RuleContext, node: Located): boolean;
60
+ export {};
61
+ //# sourceMappingURL=escape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/eslint/escape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,mBAAmB,CAAA;AAE7C,gDAAgD;AAChD,eAAO,MAAM,UAAU,KAAK,CAAA;AAE5B,2DAA2D;AAC3D,eAAO,MAAM,YAAY,WAAW,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;CAIlB,CAAA;AAEV,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,UAAU,OAAO;IACf,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,GAAG,IAAI,CAAA;IAC/B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzB;AAaD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAmC3E"}
@@ -0,0 +1,45 @@
1
+ import type { ESLint, Linter } from 'eslint';
2
+ export { ESCAPE_MARKER, MIN_REASON, SETTINGS_KEY, isEscaped, type EstivaSettings } from './escape';
3
+ /** The name the configs register the plugin under, so every rule id is `estiva/<rule>`. */
4
+ export declare const PLUGIN_KEY = "estiva";
5
+ declare const plugin: {
6
+ meta: {
7
+ name: string;
8
+ version: string;
9
+ };
10
+ rules: {
11
+ 'no-raw-button': import("eslint").Rule.RuleModule;
12
+ };
13
+ configs: {
14
+ recommended: Linter.Config;
15
+ strict: Linter.Config;
16
+ };
17
+ };
18
+ export default plugin;
19
+ export interface GateRuleCount {
20
+ errors: number;
21
+ warnings: number;
22
+ escapes: number;
23
+ }
24
+ export interface GateCount {
25
+ /** Per rule id, including rules with nothing to report. */
26
+ rules: Record<string, GateRuleCount>;
27
+ /**
28
+ * Reports of these rules that an `eslint-disable` directive silenced. Not
29
+ * an escape: a gate fails on any of these (docs/GATES.md §16, S4).
30
+ */
31
+ disabled: {
32
+ filePath: string;
33
+ line: number;
34
+ ruleId: string;
35
+ }[];
36
+ }
37
+ /**
38
+ * Count what a lint of these rules found, for `.gates-count.json` (seam S3).
39
+ *
40
+ * Run the lint with `settings: { estiva: { reportEscapes: true } }` for the
41
+ * escapes to be counted; without it they are silent and count 0. A marker
42
+ * with no reason, or inside a directive, counts as an error of its rule.
43
+ */
44
+ export declare function countGates(results: ESLint.LintResult[]): GateCount;
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAG5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAIlG,2FAA2F;AAC3F,eAAO,MAAM,UAAU,WAAW,CAAA;AAMlC,QAAA,MAAM,MAAM;;;;;;;;aAGK;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAA;KAAE;CAC7C,CAAA;AAqBzB,eAAe,MAAM,CAAA;AAErB,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACpC;;;OAGG;IACH,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC/D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,SAAS,CAgBlE"}
@@ -0,0 +1,118 @@
1
+ // src/eslint/index.ts
2
+ import { createRequire } from "node:module";
3
+
4
+ // src/eslint/escape.ts
5
+ var ESCAPE_MARKER = "@estiva-escape";
6
+ var MIN_REASON = 10;
7
+ var SETTINGS_KEY = "estiva";
8
+ var ESCAPE_MESSAGES = {
9
+ escapeWithoutReason: `An escape needs its reason, at least ${MIN_REASON} characters: \`// ${ESCAPE_MARKER}: <why this stays>\`. An escape with no reason hides nothing.`,
10
+ escapeInDirective: `Write the escape as its own comment on the line above: \`// ${ESCAPE_MARKER}: <reason>\`. Inside an eslint-disable comment it switches the rule off instead of recording why.`,
11
+ escaped: "Escaped: {{reason}}"
12
+ };
13
+ var DIRECTIVE = /^\s*eslint-disable(?:-next-line|-line)?(?=\s|$)([^]*)$/;
14
+ function directiveRules(rest) {
15
+ const dashes = rest.search(/(?:^|\s)--(?:\s|$)/);
16
+ return (dashes === -1 ? rest : rest.slice(0, dashes)).split(/[\s,]+/).filter(Boolean);
17
+ }
18
+ var BETWEEN = /^[\s}]*$/;
19
+ function isEscaped(context, node) {
20
+ const { sourceCode } = context;
21
+ if (!node.loc || !node.range) return false;
22
+ const line = node.loc.start.line;
23
+ const nodeStart = node.range[0];
24
+ const comment = sourceCode.getAllComments().find((c) => c.loc && c.range && c.loc.end.line === line - 1 && BETWEEN.test(sourceCode.text.slice(c.range[1], nodeStart)));
25
+ if (!comment?.loc) return false;
26
+ const at = comment.value.indexOf(ESCAPE_MARKER);
27
+ if (at === -1) return false;
28
+ const directive = DIRECTIVE.exec(comment.value);
29
+ if (directive) {
30
+ const rules2 = directiveRules(directive[1]);
31
+ if (rules2.length > 0 && !rules2.includes(context.id)) return false;
32
+ context.report({ loc: comment.loc, messageId: "escapeInDirective" });
33
+ return false;
34
+ }
35
+ const reason = comment.value.slice(at + ESCAPE_MARKER.length).replace(/^:/, "").trim();
36
+ if (reason.replace(/\s/g, "").length < MIN_REASON) {
37
+ context.report({ loc: comment.loc, messageId: "escapeWithoutReason" });
38
+ return false;
39
+ }
40
+ const settings = context.settings[SETTINGS_KEY];
41
+ if (settings?.reportEscapes) context.report({ loc: comment.loc, messageId: "escaped", data: { reason } });
42
+ return true;
43
+ }
44
+
45
+ // src/eslint/no-raw-button.ts
46
+ var noRawButton = {
47
+ meta: {
48
+ type: "problem",
49
+ docs: { description: "A raw <button> where @estiva-app/ui has one" },
50
+ schema: [],
51
+ messages: {
52
+ raw: "Use `Button` from @estiva-app/ui instead of a raw <button>.",
53
+ ...ESCAPE_MESSAGES
54
+ }
55
+ },
56
+ create(context) {
57
+ return {
58
+ // ESLint's types know ESTree's nodes, not JSX's; the parser hands this one over.
59
+ JSXOpeningElement(node) {
60
+ const element = node;
61
+ if (element.name.type !== "JSXIdentifier" || element.name.name !== "button") return;
62
+ if (isEscaped(context, element)) return;
63
+ context.report({ loc: element.loc, messageId: "raw" });
64
+ }
65
+ };
66
+ }
67
+ };
68
+
69
+ // src/eslint/index.ts
70
+ var { version } = createRequire(import.meta.url)("../../package.json");
71
+ var PLUGIN_KEY = "estiva";
72
+ var rules = {
73
+ "no-raw-button": noRawButton
74
+ };
75
+ var plugin = {
76
+ meta: { name: "@estiva-app/ui/eslint", version },
77
+ rules,
78
+ configs: {}
79
+ };
80
+ var ruleIds = Object.keys(rules).map((name) => `${PLUGIN_KEY}/${name}`);
81
+ plugin.configs.recommended = {
82
+ name: "@estiva-app/ui/recommended",
83
+ plugins: { [PLUGIN_KEY]: plugin },
84
+ rules: { [`${PLUGIN_KEY}/no-raw-button`]: "error" }
85
+ };
86
+ plugin.configs.strict = {
87
+ name: "@estiva-app/ui/strict",
88
+ plugins: { [PLUGIN_KEY]: plugin },
89
+ rules: Object.fromEntries(ruleIds.map((id) => [id, "error"]))
90
+ };
91
+ var index_default = plugin;
92
+ function countGates(results) {
93
+ const counts = Object.fromEntries(ruleIds.map((id) => [id, { errors: 0, warnings: 0, escapes: 0 }]));
94
+ const disabled = [];
95
+ for (const result of results) {
96
+ for (const message of result.messages) {
97
+ const count = message.ruleId ? counts[message.ruleId] : void 0;
98
+ if (!count) continue;
99
+ if (message.messageId === "escaped") count.escapes += 1;
100
+ else if (message.severity === 2) count.errors += 1;
101
+ else count.warnings += 1;
102
+ }
103
+ for (const message of result.suppressedMessages ?? []) {
104
+ if (message.ruleId && counts[message.ruleId]) disabled.push({ filePath: result.filePath, line: message.line, ruleId: message.ruleId });
105
+ }
106
+ }
107
+ return { rules: counts, disabled };
108
+ }
109
+ export {
110
+ ESCAPE_MARKER,
111
+ MIN_REASON,
112
+ PLUGIN_KEY,
113
+ SETTINGS_KEY,
114
+ countGates,
115
+ index_default as default,
116
+ isEscaped
117
+ };
118
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/eslint/index.ts", "../../src/eslint/escape.ts", "../../src/eslint/no-raw-button.ts"],
4
+ "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * `@estiva-app/ui/eslint` \u2014 the UI Guardrails' lint rules, as a plugin (UIG-3,\n * seam S1 in docs/GATES.md \u00A716).\n *\n * A plugin rather than config, so every app \u2014 Peek, Ship, Leaf \u2014 gets a new\n * rule through an ordinary version bump, and an app's editor hook runs the\n * very rule code its CI runs.\n *\n * import estiva from '@estiva-app/ui/eslint'\n * export default [{ files: ['src/**\\/*.tsx'], ...estiva.configs.recommended }]\n *\n * Register it under `estiva` (the configs do): the rule ids are\n * `estiva/<rule>`, and `countGates` counts those ids.\n *\n * Built on its own by build.mjs, for Node, into `dist/eslint/`; nothing here\n * reaches the components' browser bundle.\n */\nimport { createRequire } from 'node:module'\nimport type { ESLint, Linter } from 'eslint'\nimport { noRawButton } from './no-raw-button'\n\nexport { ESCAPE_MARKER, MIN_REASON, SETTINGS_KEY, isEscaped, type EstivaSettings } from './escape'\n\nconst { version } = createRequire(import.meta.url)('../../package.json') as { version: string }\n\n/** The name the configs register the plugin under, so every rule id is `estiva/<rule>`. */\nexport const PLUGIN_KEY = 'estiva'\n\nconst rules = {\n 'no-raw-button': noRawButton,\n}\n\nconst plugin = {\n meta: { name: '@estiva-app/ui/eslint', version },\n rules,\n configs: {} as { recommended: Linter.Config; strict: Linter.Config },\n} satisfies ESLint.Plugin\n\nconst ruleIds = Object.keys(rules).map((name) => `${PLUGIN_KEY}/${name}`)\n\n/**\n * `recommended` switches every rule on at the level it was ruled at: an error\n * blocks, a warning is reported and never blocks. `strict` makes every rule an\n * error. With one rule, an error, the two are the same today; they part when\n * the first warning-level rule arrives (UIG-25).\n */\nplugin.configs.recommended = {\n name: '@estiva-app/ui/recommended',\n plugins: { [PLUGIN_KEY]: plugin },\n rules: { [`${PLUGIN_KEY}/no-raw-button`]: 'error' },\n}\nplugin.configs.strict = {\n name: '@estiva-app/ui/strict',\n plugins: { [PLUGIN_KEY]: plugin },\n rules: Object.fromEntries(ruleIds.map((id) => [id, 'error'])),\n}\n\nexport default plugin\n\nexport interface GateRuleCount {\n errors: number\n warnings: number\n escapes: number\n}\n\nexport interface GateCount {\n /** Per rule id, including rules with nothing to report. */\n rules: Record<string, GateRuleCount>\n /**\n * Reports of these rules that an `eslint-disable` directive silenced. Not\n * an escape: a gate fails on any of these (docs/GATES.md \u00A716, S4).\n */\n disabled: { filePath: string; line: number; ruleId: string }[]\n}\n\n/**\n * Count what a lint of these rules found, for `.gates-count.json` (seam S3).\n *\n * Run the lint with `settings: { estiva: { reportEscapes: true } }` for the\n * escapes to be counted; without it they are silent and count 0. A marker\n * with no reason, or inside a directive, counts as an error of its rule.\n */\nexport function countGates(results: ESLint.LintResult[]): GateCount {\n const counts: Record<string, GateRuleCount> = Object.fromEntries(ruleIds.map((id) => [id, { errors: 0, warnings: 0, escapes: 0 }]))\n const disabled: GateCount['disabled'] = []\n for (const result of results) {\n for (const message of result.messages) {\n const count = message.ruleId ? counts[message.ruleId] : undefined\n if (!count) continue\n if (message.messageId === 'escaped') count.escapes += 1\n else if (message.severity === 2) count.errors += 1\n else count.warnings += 1\n }\n for (const message of result.suppressedMessages ?? []) {\n if (message.ruleId && counts[message.ruleId]) disabled.push({ filePath: result.filePath, line: message.line, ruleId: message.ruleId })\n }\n }\n return { rules: counts, disabled }\n}\n", "import type { AST, Rule } from 'eslint'\n\n/**\n * The escape marker (UIG-3, seam S4 in docs/GATES.md \u00A716): one written reason\n * why one element stays as it is.\n *\n * A line comment directly above the element,\n *\n * // @estiva-escape: <reason>\n *\n * or, where the element is a JSX child, the JSX comment on the line above,\n *\n * {/* @estiva-escape: <reason> *\\/}\n *\n * with at least ten characters of reason, spaces not counted.\n *\n * Not `eslint-disable`. A directive switches a rule off without saying it was\n * meant, and the count of escapes (`.gates-count.json`) could not see it. A\n * marker written inside a directive that switches one of these rules off is\n * reported for that reason.\n *\n * The token lint's older notes (`eslint-disable-next-line <rule> -- @estiva-escape:\n * <reason>`, Katerina's ruling A2 of 15 September) are not read here: they\n * escape the token lint's rules, which are not this plugin's, and they stay as\n * they are (Katerina, 15 September).\n */\nexport const ESCAPE_MARKER = '@estiva-escape'\n\n/** Characters of reason, spaces not counted. */\nexport const MIN_REASON = 10\n\n/** The key under ESLint's `settings` this plugin reads. */\nexport const SETTINGS_KEY = 'estiva'\n\n/**\n * Every rule spreads these into its `meta.messages`, because `isEscaped`\n * reports through the rule that called it.\n */\nexport const ESCAPE_MESSAGES = {\n escapeWithoutReason: `An escape needs its reason, at least ${MIN_REASON} characters: \\`// ${ESCAPE_MARKER}: <why this stays>\\`. An escape with no reason hides nothing.`,\n escapeInDirective: `Write the escape as its own comment on the line above: \\`// ${ESCAPE_MARKER}: <reason>\\`. Inside an eslint-disable comment it switches the rule off instead of recording why.`,\n escaped: 'Escaped: {{reason}}',\n} as const\n\nexport interface EstivaSettings {\n /**\n * Report every sanctioned escape as a message with the id `escaped`, so a\n * count can read them. Off in the lint anyone runs; on only in the count.\n */\n reportEscapes?: boolean\n}\n\ninterface Located {\n loc?: AST.SourceLocation | null\n range?: [number, number]\n}\n\nconst DIRECTIVE = /^\\s*eslint-disable(?:-next-line|-line)?(?=\\s|$)([^]*)$/\n\n/** The rules a directive names: what comes before its ` -- ` description. None means every rule. */\nfunction directiveRules(rest: string): string[] {\n const dashes = rest.search(/(?:^|\\s)--(?:\\s|$)/)\n return (dashes === -1 ? rest : rest.slice(0, dashes)).split(/[\\s,]+/).filter(Boolean)\n}\n\n/** Only spaces and a JSX expression's closing brace between the marker and the element. */\nconst BETWEEN = /^[\\s}]*$/\n\n/**\n * Whether the element at `node` carries a valid escape on the line above.\n *\n * Every rule of this plugin calls it before it reports. It reports, through\n * that rule, a marker with too short a reason and a marker inside an\n * eslint-disable directive for this rule; either way the element is not\n * escaped, so the rule reports it too.\n */\nexport function isEscaped(context: Rule.RuleContext, node: Located): boolean {\n const { sourceCode } = context\n if (!node.loc || !node.range) return false\n const line = node.loc.start.line\n const nodeStart = node.range[0]\n\n const comment = sourceCode\n .getAllComments()\n .find((c) => c.loc && c.range && c.loc.end.line === line - 1 && BETWEEN.test(sourceCode.text.slice(c.range[1], nodeStart)))\n if (!comment?.loc) return false\n\n const at = comment.value.indexOf(ESCAPE_MARKER)\n if (at === -1) return false\n\n const directive = DIRECTIVE.exec(comment.value)\n if (directive) {\n const rules = directiveRules(directive[1])\n // A directive for other rules (the token lint's notes) is not an escape of this one.\n if (rules.length > 0 && !rules.includes(context.id)) return false\n context.report({ loc: comment.loc, messageId: 'escapeInDirective' })\n return false\n }\n\n const reason = comment.value\n .slice(at + ESCAPE_MARKER.length)\n .replace(/^:/, '')\n .trim()\n if (reason.replace(/\\s/g, '').length < MIN_REASON) {\n context.report({ loc: comment.loc, messageId: 'escapeWithoutReason' })\n return false\n }\n\n const settings = context.settings[SETTINGS_KEY] as EstivaSettings | undefined\n if (settings?.reportEscapes) context.report({ loc: comment.loc, messageId: 'escaped', data: { reason } })\n return true\n}\n", "import type { Rule } from 'eslint'\nimport { ESCAPE_MESSAGES, isEscaped } from './escape'\n\ninterface JSXOpeningElement {\n name: { type: string; name?: string }\n loc: NonNullable<Rule.Node['loc']>\n range: [number, number]\n}\n\n/**\n * A raw `<button>` in an app (UIG-3, the tracer). The package's `Button`,\n * `IconButton`, `MenuItem` and chips are buttons already; an app that writes\n * its own has a look and a behaviour nobody else keeps in step with.\n *\n * Only the JSX element named `button`. `<Button>`, `<Foo.button>` and\n * `createElement('button')` are not this rule's business.\n */\nexport const noRawButton: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: { description: 'A raw <button> where @estiva-app/ui has one' },\n schema: [],\n messages: {\n raw: 'Use `Button` from @estiva-app/ui instead of a raw <button>.',\n ...ESCAPE_MESSAGES,\n },\n },\n create(context) {\n return {\n // ESLint's types know ESTree's nodes, not JSX's; the parser hands this one over.\n JSXOpeningElement(node: Rule.Node) {\n const element = node as unknown as JSXOpeningElement\n if (element.name.type !== 'JSXIdentifier' || element.name.name !== 'button') return\n if (isEscaped(context, element)) return\n context.report({ loc: element.loc, messageId: 'raw' })\n },\n }\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["rules"]
7
+ }
@@ -0,0 +1,11 @@
1
+ import type { Rule } from 'eslint';
2
+ /**
3
+ * A raw `<button>` in an app (UIG-3, the tracer). The package's `Button`,
4
+ * `IconButton`, `MenuItem` and chips are buttons already; an app that writes
5
+ * its own has a look and a behaviour nobody else keeps in step with.
6
+ *
7
+ * Only the JSX element named `button`. `<Button>`, `<Foo.button>` and
8
+ * `createElement('button')` are not this rule's business.
9
+ */
10
+ export declare const noRawButton: Rule.RuleModule;
11
+ //# sourceMappingURL=no-raw-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-raw-button.d.ts","sourceRoot":"","sources":["../../src/eslint/no-raw-button.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AASlC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,IAAI,CAAC,UAqB9B,CAAA"}