@mastra/playground-ui 64.0.1-alpha.0 → 65.0.0-alpha.1

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 (127) hide show
  1. package/README.md +10 -2
  2. package/components.css +340 -0
  3. package/dist/code-DJReJPSX.cjs.map +1 -1
  4. package/dist/code-DLuATvYu.js.map +1 -1
  5. package/dist/components/Breadcrumb.cjs.js +4 -2
  6. package/dist/components/Breadcrumb.cjs.js.map +1 -1
  7. package/dist/components/Breadcrumb.es.js +5 -3
  8. package/dist/components/Breadcrumb.es.js.map +1 -1
  9. package/dist/components/DataList.cjs.js +15 -17
  10. package/dist/components/DataList.cjs.js.map +1 -1
  11. package/dist/components/DataList.es.js +4 -5
  12. package/dist/components/DataList.es.js.map +1 -1
  13. package/dist/components/EnvironmentVariablesEditor.cjs.js +1 -1
  14. package/dist/components/EnvironmentVariablesEditor.es.js +1 -1
  15. package/dist/components/LogsDataList.cjs.js +5 -2
  16. package/dist/components/LogsDataList.cjs.js.map +1 -1
  17. package/dist/components/LogsDataList.es.js +5 -2
  18. package/dist/components/LogsDataList.es.js.map +1 -1
  19. package/dist/{data-list-top-DSr_ocKA.js → data-list-DdCZZctL.js} +332 -5
  20. package/dist/data-list-DdCZZctL.js.map +1 -0
  21. package/dist/{data-list-top-ByT2Mo77.cjs → data-list-ecHsw-zr.cjs} +339 -13
  22. package/dist/data-list-ecHsw-zr.cjs.map +1 -0
  23. package/dist/{data-list-skeleton-B_SXAeE0.js → data-list-skeleton-CGKlcoiI.js} +2 -2
  24. package/dist/{data-list-skeleton-B_SXAeE0.js.map → data-list-skeleton-CGKlcoiI.js.map} +1 -1
  25. package/dist/{data-list-skeleton-BqC9Gggi.cjs → data-list-skeleton-DGMTqeNP.cjs} +6 -6
  26. package/dist/{data-list-skeleton-BqC9Gggi.cjs.map → data-list-skeleton-DGMTqeNP.cjs.map} +1 -1
  27. package/dist/domains/chat/tools/badges/workflow-badge.cjs.js +4 -0
  28. package/dist/domains/chat/tools/badges/workflow-badge.cjs.js.map +1 -1
  29. package/dist/domains/chat/tools/badges/workflow-badge.es.js +4 -0
  30. package/dist/domains/chat/tools/badges/workflow-badge.es.js.map +1 -1
  31. package/dist/domains/datasets/components/save-as-dataset-item-dialog.cjs.js +104 -92
  32. package/dist/domains/datasets/components/save-as-dataset-item-dialog.cjs.js.map +1 -1
  33. package/dist/domains/datasets/components/save-as-dataset-item-dialog.es.js +105 -93
  34. package/dist/domains/datasets/components/save-as-dataset-item-dialog.es.js.map +1 -1
  35. package/dist/domains/logs/components/logs-list-view.cjs.js +3 -2
  36. package/dist/domains/logs/components/logs-list-view.cjs.js.map +1 -1
  37. package/dist/domains/logs/components/logs-list-view.es.js +3 -2
  38. package/dist/domains/logs/components/logs-list-view.es.js.map +1 -1
  39. package/dist/domains/observability/components/add-trace-mocks-to-item-dialog.cjs.js +3 -3
  40. package/dist/domains/observability/components/add-trace-mocks-to-item-dialog.es.js +3 -3
  41. package/dist/domains/observability/components/trace-as-item-dialog.cjs.js +6 -10
  42. package/dist/domains/observability/components/trace-as-item-dialog.cjs.js.map +1 -1
  43. package/dist/domains/observability/components/trace-as-item-dialog.es.js +7 -11
  44. package/dist/domains/observability/components/trace-as-item-dialog.es.js.map +1 -1
  45. package/dist/domains/scores/components/score-as-item-dialog.cjs.js +3 -9
  46. package/dist/domains/scores/components/score-as-item-dialog.cjs.js.map +1 -1
  47. package/dist/domains/scores/components/score-as-item-dialog.es.js +4 -10
  48. package/dist/domains/scores/components/score-as-item-dialog.es.js.map +1 -1
  49. package/dist/domains/scores/components/score-data-panel.cjs.js +9 -1
  50. package/dist/domains/scores/components/score-data-panel.cjs.js.map +1 -1
  51. package/dist/domains/scores/components/score-data-panel.es.js +9 -1
  52. package/dist/domains/scores/components/score-data-panel.es.js.map +1 -1
  53. package/dist/domains/scores/components/trace-scores-tab.cjs.js +2 -1
  54. package/dist/domains/scores/components/trace-scores-tab.cjs.js.map +1 -1
  55. package/dist/domains/scores/components/trace-scores-tab.es.js +2 -1
  56. package/dist/domains/scores/components/trace-scores-tab.es.js.map +1 -1
  57. package/dist/domains/traces/components/thread-view-by-trace.cjs.js +1 -0
  58. package/dist/domains/traces/components/thread-view-by-trace.cjs.js.map +1 -1
  59. package/dist/domains/traces/components/thread-view-by-trace.es.js +1 -0
  60. package/dist/domains/traces/components/thread-view-by-trace.es.js.map +1 -1
  61. package/dist/domains/traces/components/trace-data-panel.cjs.js +3 -0
  62. package/dist/domains/traces/components/trace-data-panel.cjs.js.map +1 -1
  63. package/dist/domains/traces/components/trace-data-panel.es.js +3 -0
  64. package/dist/domains/traces/components/trace-data-panel.es.js.map +1 -1
  65. package/dist/domains/traces/components/traces-list-view.cjs.js +4 -5
  66. package/dist/domains/traces/components/traces-list-view.cjs.js.map +1 -1
  67. package/dist/domains/traces/components/traces-list-view.es.js +3 -4
  68. package/dist/domains/traces/components/traces-list-view.es.js.map +1 -1
  69. package/dist/domains/traces/components/traces-page-skeleton.cjs.js +4 -1
  70. package/dist/domains/traces/components/traces-page-skeleton.cjs.js.map +1 -1
  71. package/dist/domains/traces/components/traces-page-skeleton.es.js +4 -1
  72. package/dist/domains/traces/components/traces-page-skeleton.es.js.map +1 -1
  73. package/dist/domains/workflows/components/workflows-list/workflows-list.cjs.js +2 -2
  74. package/dist/domains/workflows/components/workflows-list/workflows-list.es.js +2 -2
  75. package/dist/domains/workflows/workflow/workflow-input-data.cjs.js +1 -0
  76. package/dist/domains/workflows/workflow/workflow-input-data.cjs.js.map +1 -1
  77. package/dist/domains/workflows/workflow/workflow-input-data.es.js +1 -0
  78. package/dist/domains/workflows/workflow/workflow-input-data.es.js.map +1 -1
  79. package/dist/ee/signals/components/entity-index-list.cjs.js +3 -1
  80. package/dist/ee/signals/components/entity-index-list.cjs.js.map +1 -1
  81. package/dist/ee/signals/components/entity-index-list.es.js +3 -1
  82. package/dist/ee/signals/components/entity-index-list.es.js.map +1 -1
  83. package/dist/ee/signals/components/trace-intelligence-entity-index.cjs.js +3 -1
  84. package/dist/ee/signals/components/trace-intelligence-entity-index.cjs.js.map +1 -1
  85. package/dist/ee/signals/components/trace-intelligence-entity-index.es.js +3 -1
  86. package/dist/ee/signals/components/trace-intelligence-entity-index.es.js.map +1 -1
  87. package/dist/ee/topics/components/topic-trace-summary-list.cjs.js +1 -1
  88. package/dist/ee/topics/components/topic-trace-summary-list.es.js +1 -1
  89. package/dist/layout/tabbed-container.cjs.js +1 -1
  90. package/dist/layout/tabbed-container.es.js +1 -1
  91. package/dist/{logs-data-list-CoER2Hst.js → logs-data-list-DBJINzIL.js} +2 -3
  92. package/dist/{logs-data-list-CoER2Hst.js.map → logs-data-list-DBJINzIL.js.map} +1 -1
  93. package/dist/{logs-data-list-DRR6cMgl.cjs → logs-data-list-IWZgMCKG.cjs} +21 -22
  94. package/dist/{logs-data-list-DRR6cMgl.cjs.map → logs-data-list-IWZgMCKG.cjs.map} +1 -1
  95. package/dist/primitives/raised-surface.cjs.js.map +1 -1
  96. package/dist/primitives/raised-surface.es.js.map +1 -1
  97. package/dist/src/domains/datasets/components/index.d.ts +1 -1
  98. package/dist/src/domains/datasets/components/save-as-dataset-item-dialog.d.ts +14 -3
  99. package/dist/src/domains/observability/components/trace-as-item-dialog.d.ts +5 -3
  100. package/dist/src/domains/scores/components/score-as-item-dialog.d.ts +3 -3
  101. package/dist/src/ds/components/Breadcrumb/Breadcrumb.d.ts +2 -2
  102. package/dist/src/ds/components/Code/code.d.ts +1 -1
  103. package/dist/src/ds/primitives/raised-surface.d.ts +1 -1
  104. package/dist/style.css +22 -19
  105. package/dist/toast.css +1 -1
  106. package/dist/tokens.cjs.js.map +1 -1
  107. package/dist/tokens.es.js.map +1 -1
  108. package/dist/{traces-data-list-BgjeB-BF.cjs → traces-data-list-BH24OVKh.cjs} +18 -20
  109. package/dist/{traces-data-list-BgjeB-BF.cjs.map → traces-data-list-BH24OVKh.cjs.map} +1 -1
  110. package/dist/{traces-data-list-Zoai0OW_.js → traces-data-list-BOJB37dV.js} +2 -4
  111. package/dist/{traces-data-list-Zoai0OW_.js.map → traces-data-list-BOJB37dV.js.map} +1 -1
  112. package/dist/use-text-highlight.css +4 -4
  113. package/package.json +14 -10
  114. package/sources.css +1 -0
  115. package/theme/code.css +17 -0
  116. package/theme/surfaces.css +2 -2
  117. package/theme.css +2 -0
  118. package/dist/data-list-DUfXIhyC.cjs +0 -142
  119. package/dist/data-list-DUfXIhyC.cjs.map +0 -1
  120. package/dist/data-list-DXQY_iQW.js +0 -138
  121. package/dist/data-list-DXQY_iQW.js.map +0 -1
  122. package/dist/data-list-spacer-ByA4ce2a.js +0 -209
  123. package/dist/data-list-spacer-ByA4ce2a.js.map +0 -1
  124. package/dist/data-list-spacer-tQaBaum4.cjs +0 -218
  125. package/dist/data-list-spacer-tQaBaum4.cjs.map +0 -1
  126. package/dist/data-list-top-ByT2Mo77.cjs.map +0 -1
  127. package/dist/data-list-top-DSr_ocKA.js.map +0 -1
package/README.md CHANGED
@@ -21,6 +21,14 @@ export function SaveButton() {
21
21
  }
22
22
  ```
23
23
 
24
+ With a Tailwind build of your own, compile one sheet instead of `style.css`: import the components' stylesheet uncompiled, and `sources.css` so Tailwind emits the classes the compiled components use.
25
+
26
+ ```css
27
+ @import 'tailwindcss';
28
+ @import '@mastra/playground-ui/components.css';
29
+ @import '@mastra/playground-ui/sources.css';
30
+ ```
31
+
24
32
  ### Tool approvals
25
33
 
26
34
  `ToolApproval` renders a standalone approval request in the shared activity layout: the tool name
@@ -227,7 +235,7 @@ Text fields, textareas, input groups, and the default Select, Combobox, and Date
227
235
 
228
236
  A field in an error state sets `--field-rim` and `--field-rim-focus` to `--destructive-indicator`, so the red outline shows on every surface and stays red while focused.
229
237
 
230
- If your app generates additional semantic utilities, import `@mastra/playground-ui/theme.css` into its Tailwind stylesheet so Tailwind can read the `@theme inline` mappings.
238
+ If your app keeps `style.css` and generates additional semantic utilities, import `@mastra/playground-ui/theme.css` into its Tailwind stylesheet so Tailwind can read the `@theme inline` mappings. `components.css` already includes it.
231
239
 
232
240
  ### Brand colors
233
241
 
@@ -263,7 +271,7 @@ import { ProductBadge } from '@mastra/playground-ui/components/ProductBadge';
263
271
 
264
272
  Charts use hue-named roles for categories (`--chart-blue`, `--chart-blue-deep`, `--chart-amber`, `--chart-green`, `--chart-purple`, `--chart-orange`, `--chart-pink`, `--chart-red`) and `--chart-sequential-1` through `--chart-sequential-5` for ordered values. Span colors use `--span-agent`, `--span-workflow`, and the other span names. Pastel chart colors are fills, not text colors; keep labels on `--foreground` or `--muted-foreground`.
265
273
 
266
- Numbered `accent*` tokens, `positive1`, `negative1`, `warning1`, `notice-success/destructive/warning/info`, and `--brand-green-*` are removed. The `yellow` ramp is now `amber`: `--yellow-*`, `--yellow-soft-*`, `--badge-yellow-*`, `--chart-yellow`, and the Badge `yellow` tone are removed in favor of their `amber` names. `--color-brand-yellow` is unchanged. Status consumers use the existing `success`, `destructive`, `warning`, or `info` roles. Focus styling uses `border-focus`; categorical charts and span icons use their own roles. CodeMirror uses five local `--syntax-*` properties scoped to `.cm-editor`, not a global palette.
274
+ Numbered `accent*` tokens, `positive1`, `negative1`, `warning1`, `notice-success/destructive/warning/info`, and `--brand-green-*` are removed. The `yellow` ramp is now `amber`: `--yellow-*`, `--yellow-soft-*`, `--badge-yellow-*`, `--chart-yellow`, and the Badge `yellow` tone are removed in favor of their `amber` names. `--color-brand-yellow` is unchanged. Status consumers use the existing `success`, `destructive`, `warning`, or `info` roles. Focus styling uses `border-focus`; categorical charts and span icons use their own roles. CodeMirror reads five `--syntax-*` roles from `theme/code.css`, not a global palette.
267
275
 
268
276
  Every badge hue (`green`, `red`, `amber`, `blue`, `purple`, `orange`, `cyan`, `pink`) is a categorical tone built from the ramps, with `--badge-{hue}-strong`, `-subtle`, `-edge`, `-foreground`, and `-indicator`. `-strong` and `-subtle` are the fills behind `emphasis="strong"` and `emphasis="subtle"`. Badge backgrounds are translucent tints, so a badge follows the surface under it when a card or row changes on hover: `--{hue}-600` at 32% (strong) and 13% (subtle) in dark mode, `--{hue}-400` at 20% and 10% in light mode. On a resting card they match the solid fills they replaced. Product avatars stay solid (`--{hue}-soft-900` / `--{hue}-soft-950` in dark mode, `--{hue}-100` / `--{hue}-50` in light mode) because they overlap each other. The soft steps keep the lightness and hue of `900`/`950` at 75% chroma, and are available as utilities such as `bg-green-soft-900`. `--badge-{hue}-indicator` colors dots, icons, and labels for a category: `--{hue}-soft-300` in dark mode and `--{hue}-soft-600` in light mode, which hold every hue at the same 0.1 chroma so no hue shouts. Use the status role with `-subtle`, `-edge`, `-subtle-foreground`, or `-indicator` for its intended job. Notice variants are unchanged. Badge status variants are `success`, `destructive`, `warning`, and `info`, for states. `green`, `red`, `amber`, and `blue` are categorical tones like `purple` and `orange`, for labels that are not states. `Badge` takes `emphasis="strong"` (the default) or `emphasis="subtle"` (a quieter tinted fill). Use `--green-*` for the shared chromatic ramp.
269
277
 
package/components.css ADDED
@@ -0,0 +1,340 @@
1
+ /* The components' stylesheet, uncompiled: tokens, plugins, custom utilities, base styles,
2
+ * keyframes. An app compiles it into its own sheet after `@import 'tailwindcss'`, with
3
+ * `sources.css` beside it. `sources.css` is not imported here: `src/index.css` builds
4
+ * `dist/style.css` from this file and would scan its own output. */
5
+ @import 'tw-animate-css';
6
+
7
+ @import './theme.css';
8
+
9
+ @plugin '@tailwindcss/typography';
10
+
11
+ @custom-variant dark (&:is(.dark *));
12
+
13
+ @utility container {
14
+ margin-inline: auto;
15
+ padding-inline: 2rem;
16
+ @media (width >= theme(--breakpoint-3xl)) {
17
+ max-width: none;
18
+ }
19
+ @media (width >= 1400px) {
20
+ max-width: 1400px;
21
+ }
22
+ }
23
+
24
+ /* Assembled here, not in `theme.css`, so `--surface-rim` resolves against the
25
+ * element wearing the utility: a custom property that referenced it would be
26
+ * substituted once on the root and inherited as a finished string.
27
+ *
28
+ * `--surface-tint` is the state layer of a surface that cannot carry a
29
+ * pseudo-element — an `<input>`. An inset shadow paints over the element's own
30
+ * fill and under its content, so a wash there behaves like `state-layer`'s
31
+ * `::before` without needing a child, and it sits below the rim layer so the
32
+ * boundary keeps its resting weight while the fill moves. Each utility resets
33
+ * it, because a custom property would otherwise inherit into every raised
34
+ * surface nested inside a hovered one. */
35
+ @utility shadow-raised {
36
+ --surface-tint: transparent;
37
+
38
+ box-shadow:
39
+ var(--elevation-lip),
40
+ inset 0 0 0 1px var(--surface-rim),
41
+ inset 0 0 0 9999px var(--surface-tint),
42
+ var(--elevation-raised);
43
+ }
44
+
45
+ @utility shadow-input {
46
+ --surface-tint: transparent;
47
+ --surface-rim: var(--field-rim);
48
+
49
+ box-shadow:
50
+ var(--elevation-lip),
51
+ inset 0 0 0 1px var(--surface-rim),
52
+ inset 0 0 0 9999px var(--surface-tint),
53
+ var(--elevation-raised);
54
+ }
55
+
56
+ @utility shadow-inset {
57
+ box-shadow:
58
+ inset 0 1px 0 var(--inset-highlight),
59
+ inset 0 0 0 1px var(--inset-rim);
60
+ }
61
+
62
+ @utility shadow-rim {
63
+ box-shadow: inset 0 0 0 1px var(--surface-rim);
64
+ }
65
+
66
+ @utility shadow-overlay {
67
+ --surface-tint: transparent;
68
+
69
+ box-shadow:
70
+ var(--elevation-lip),
71
+ inset 0 0 0 1px var(--surface-rim),
72
+ inset 0 0 0 9999px var(--surface-tint),
73
+ var(--elevation-overlay);
74
+ }
75
+
76
+ /* Interaction states for a surface that already has an opaque fill.
77
+ *
78
+ * A `hover:bg-fill-subtle` on such a surface *replaces* its `background-color`,
79
+ * so the rung composites over the parent instead of over the surface. Measured
80
+ * on `bg-muted`, that lands 3 levels darker in dark and 5 lighter in light — the
81
+ * hover inverts across themes. Layered, the same rung is +9/-9 from whatever it
82
+ * sits on, in both themes, whatever the resting rung.
83
+ *
84
+ * The layer is a pseudo-element rather than a `background-image` so it can still
85
+ * transition, and it stops a pixel short of the edge to leave the inset rim of
86
+ * `shadow-raised` visible. `isolation` is what keeps a negative z-index from
87
+ * escaping to an ancestor's stacking context.
88
+ *
89
+ * It does not touch the rim: `--surface-rim` inherits, so brightening it here
90
+ * would repaint the rim of every raised surface nested inside the hovered one.
91
+ *
92
+ * This and `--surface-tint` are the same rung by two mechanisms, and both stay:
93
+ * the pseudo-element transitions its colour and stops a pixel short of the rim,
94
+ * which is what a surface with content inside it wants; the tint paints edge to
95
+ * edge, cannot transition (a box-shadow list transitions as a whole, so the rim
96
+ * and the drops would animate with it), and is the only option for an element
97
+ * that cannot own a child — an `<input>`. Reach for this one unless the element
98
+ * is a bare control. */
99
+ @utility state-layer {
100
+ position: relative;
101
+ isolation: isolate;
102
+
103
+ &::before {
104
+ content: '';
105
+ position: absolute;
106
+ inset: 1px;
107
+ z-index: -1;
108
+ border-radius: inherit;
109
+ pointer-events: none;
110
+ transition: background-color var(--duration-fast) var(--ease-out-custom);
111
+ }
112
+
113
+ &:not(:disabled, [aria-disabled='true']) {
114
+ &:hover::before,
115
+ &:focus-visible::before {
116
+ background-color: var(--fill-subtle);
117
+ }
118
+
119
+ &:active::before {
120
+ background-color: var(--fill);
121
+ }
122
+ }
123
+
124
+ @media (prefers-reduced-motion: reduce) {
125
+ &::before {
126
+ transition: none;
127
+ }
128
+ }
129
+ }
130
+
131
+ /* Pulls a ScrollArea's scrollbar ends in from a rounded container's corners, keyed to the same
132
+ radius steps as `rounded-*`: pair `rounded-xl` with `scrollbar-inset-xl`. At 0.7 of the radius
133
+ the corner's curve no longer cuts into the 6px bar. An arbitrary radius takes an arbitrary
134
+ inset, `scrollbar-inset-[22px]`. ScrollBar reads the variable and defaults to 0. */
135
+ @utility scrollbar-inset-* {
136
+ --scroll-area-scrollbar-inset: calc(--value(--radius-*, [length]) * 0.7);
137
+ }
138
+
139
+ @utility rounded-studio-frame {
140
+ border-radius: var(--studio-frame-radius, 1.5rem);
141
+ }
142
+
143
+ @utility rounded-studio-panel {
144
+ border-radius: max(0px, calc(var(--studio-frame-radius, 1.5rem) - var(--studio-frame-inset, 0.5rem)));
145
+ }
146
+
147
+ @utility rounded-tr-studio-panel {
148
+ border-top-right-radius: max(0px, calc(var(--studio-frame-radius, 1.5rem) - var(--studio-frame-inset, 0.5rem)));
149
+ }
150
+
151
+ @layer utilities {
152
+ .shiki-token {
153
+ color: var(--shiki-light, var(--foreground));
154
+ background-color: var(--shiki-light-bg, transparent);
155
+ }
156
+
157
+ .dark .shiki-token {
158
+ color: var(--shiki-dark, var(--foreground));
159
+ background-color: var(--shiki-dark-bg, transparent);
160
+ }
161
+ }
162
+
163
+ @layer base {
164
+ * {
165
+ border-color: var(--border);
166
+ }
167
+
168
+ :where(code, samp, pre, .font-mono) {
169
+ font-size-adjust: var(--font-mono-size-adjust, none);
170
+ }
171
+
172
+ kbd {
173
+ font-size-adjust: none;
174
+ }
175
+
176
+ :focus-visible {
177
+ outline: 1px solid var(--border-focus);
178
+ outline-offset: 0;
179
+ }
180
+
181
+ body {
182
+ background-color: var(--sidebar);
183
+ color: var(--foreground);
184
+ -webkit-font-smoothing: antialiased;
185
+ -moz-osx-font-smoothing: grayscale;
186
+ }
187
+
188
+ * {
189
+ scrollbar-width: thin;
190
+ scrollbar-color: var(--border-strong) transparent;
191
+ }
192
+ ::-webkit-scrollbar {
193
+ width: 10px;
194
+ height: 10px;
195
+ }
196
+ ::-webkit-scrollbar-track {
197
+ background: transparent;
198
+ }
199
+ ::-webkit-scrollbar-thumb {
200
+ background-color: var(--border-strong);
201
+ border-radius: 9999px;
202
+ border: 3px solid transparent;
203
+ background-clip: padding-box;
204
+ }
205
+ ::-webkit-scrollbar-thumb:hover {
206
+ background-color: var(--border-focus);
207
+ }
208
+ ::-webkit-scrollbar-corner {
209
+ background: transparent;
210
+ }
211
+ }
212
+
213
+ @utility wrap-break-word {
214
+ overflow-wrap: break-word;
215
+ }
216
+
217
+ @utility time-input {
218
+ &::-webkit-calendar-picker-indicator {
219
+ @apply cursor-pointer;
220
+ background: url('/icons/clock.svg') no-repeat center;
221
+ background-size: 1em 1em;
222
+ filter: invert(50%);
223
+ }
224
+ }
225
+
226
+ .thread-viewport > [data-radix-scroll-area-content] {
227
+ @apply flex flex-col items-center self-stretch bg-inherit;
228
+ }
229
+
230
+ [hidden]:where(:not([hidden='until-found'])) {
231
+ display: none !important;
232
+ }
233
+
234
+ .unset-position {
235
+ position: unset;
236
+ }
237
+
238
+ .react-flow__controls-button {
239
+ --xy-controls-button-color: var(--foreground);
240
+ --xy-controls-button-color-hover: var(--foreground);
241
+ --xy-controls-button-background-color: var(--surface-panel);
242
+ --xy-controls-button-background-color-hover: var(--gray-2);
243
+ --xy-controls-button-border-color: transparent;
244
+ }
245
+
246
+ /* Skeleton highlight: enters off the left edge and leaves off the right before it loops. It
247
+ moves `translate` (what Tailwind v4's translate utilities set), so it doesn't stack on the
248
+ skeleton's own `-translate-x-full` start position. */
249
+ @keyframes shimmer {
250
+ from {
251
+ translate: -100% 0;
252
+ }
253
+
254
+ to {
255
+ translate: 100% 0;
256
+ }
257
+ }
258
+
259
+ /* Chart ghost sweep, in SVG user units (a 0-100 viewBox): a 40-unit band of light from off the
260
+ left edge to off the right, on the same 2s loop as the skeleton shimmer. */
261
+ @keyframes chart-ghost-sweep {
262
+ from {
263
+ transform: translateX(-40px);
264
+ }
265
+
266
+ to {
267
+ transform: translateX(140px);
268
+ }
269
+ }
270
+
271
+ @keyframes click-ripple {
272
+ 0% {
273
+ transform: scale(0);
274
+ opacity: 0.5;
275
+ }
276
+
277
+ 100% {
278
+ transform: scale(1);
279
+ opacity: 0;
280
+ }
281
+ }
282
+
283
+ .animate-click-ripple {
284
+ animation: click-ripple 300ms ease-out forwards;
285
+ }
286
+
287
+ @keyframes fade-row-highlight {
288
+ 0% {
289
+ background-color: var(--fill-hover);
290
+ }
291
+
292
+ 100% {
293
+ background-color: transparent;
294
+ }
295
+ }
296
+
297
+ .animate-row-highlight {
298
+ animation: fade-row-highlight 2000ms ease-out forwards;
299
+ }
300
+
301
+ /* Sort arrows are "drawn" in their pointing direction: asc reveals from the
302
+ bottom up, desc from the top down. */
303
+ @keyframes sort-arrow-reveal-up {
304
+ 0% {
305
+ clip-path: inset(100% 0 0 0);
306
+ transform: translateY(20%);
307
+ }
308
+
309
+ 100% {
310
+ clip-path: inset(0);
311
+ transform: translateY(0);
312
+ }
313
+ }
314
+
315
+ @keyframes sort-arrow-reveal-down {
316
+ 0% {
317
+ clip-path: inset(0 0 100% 0);
318
+ transform: translateY(-20%);
319
+ }
320
+
321
+ 100% {
322
+ clip-path: inset(0);
323
+ transform: translateY(0);
324
+ }
325
+ }
326
+
327
+ .animate-sort-arrow-up {
328
+ animation: sort-arrow-reveal-up 200ms var(--ease-out-custom) both;
329
+ }
330
+
331
+ .animate-sort-arrow-down {
332
+ animation: sort-arrow-reveal-down 200ms var(--ease-out-custom) both;
333
+ }
334
+
335
+ @media (prefers-reduced-motion: reduce) {
336
+ .animate-sort-arrow-up,
337
+ .animate-sort-arrow-down {
338
+ animation: none;
339
+ }
340
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"code-DJReJPSX.cjs","sources":["../src/ds/components/Code/use-highlight.ts","../src/ds/components/Code/code.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ThemedToken } from 'shiki/core';\nimport { useDebounce } from 'use-debounce';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\nconst HIGHLIGHT_SAMPLE_INTERVAL_MS = 75;\n\n/** Tokens land a pass behind the code, so the value may still describe the previous code. */\nexport function useHighlight(code: string, lang: string | undefined): Highlighted | null {\n const [highlighted, setHighlighted] = useState<Highlighted | null>(null);\n // maxWait equal to the delay turns the debounce into a throttle.\n const [sampledCode, sampler] = useDebounce(code, HIGHLIGHT_SAMPLE_INTERVAL_MS, {\n leading: true,\n maxWait: HIGHLIGHT_SAMPLE_INTERVAL_MS,\n });\n\n // With `leading`, use-debounce's pending timer still calls setState after unmount.\n useEffect(() => () => sampler.cancel(), [sampler]);\n\n useEffect(() => {\n if (!lang) return;\n\n let cancelled = false;\n\n void highlight(sampledCode, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code: sampledCode, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [sampledCode, lang]);\n\n return lang ? highlighted : null;\n}\n\nexport function tokenStyle(token: ThemedToken): CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n","import * as React from 'react';\n\nimport { tokenStyle, useHighlight } from './use-highlight';\nimport type { Highlighted } from './use-highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n /** Per-line class, e.g. for diff or search highlighting. When set, every line is wrapped in a `[data-line]` span. */\n lineClassName?: (lineIndex: number, lineText: string) => string | undefined;\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (index.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a sample\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, lineClassName, ...props }: CodeProps) {\n const highlighted = useHighlight(code, lang);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n if (!lineClassName) return <pre {...props}>{code}</pre>;\n const lines = code.split('\\n');\n return (\n <pre {...props}>\n {lines.map((text, i) => (\n <React.Fragment key={i}>\n <span data-line={i} className={lineClassName(i, text)}>\n {text}\n </span>\n {i !== lines.length - 1 && '\\n'}\n </React.Fragment>\n ))}\n </pre>\n );\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code className=\"font-[inherit]\">\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n {lineClassName ? (\n <span\n data-line={lineIndex}\n className={lineClassName(lineIndex, line.map(token => token.content).join(''))}\n >\n {tokenSpans}\n </span>\n ) : (\n <span>{tokenSpans}</span>\n )}\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["useState","useDebounce","useEffect","highlight","React","Code","jsx","jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"code-DJReJPSX.cjs","sources":["../src/ds/components/Code/use-highlight.ts","../src/ds/components/Code/code.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ThemedToken } from 'shiki/core';\nimport { useDebounce } from 'use-debounce';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\nconst HIGHLIGHT_SAMPLE_INTERVAL_MS = 75;\n\n/** Tokens land a pass behind the code, so the value may still describe the previous code. */\nexport function useHighlight(code: string, lang: string | undefined): Highlighted | null {\n const [highlighted, setHighlighted] = useState<Highlighted | null>(null);\n // maxWait equal to the delay turns the debounce into a throttle.\n const [sampledCode, sampler] = useDebounce(code, HIGHLIGHT_SAMPLE_INTERVAL_MS, {\n leading: true,\n maxWait: HIGHLIGHT_SAMPLE_INTERVAL_MS,\n });\n\n // With `leading`, use-debounce's pending timer still calls setState after unmount.\n useEffect(() => () => sampler.cancel(), [sampler]);\n\n useEffect(() => {\n if (!lang) return;\n\n let cancelled = false;\n\n void highlight(sampledCode, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code: sampledCode, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [sampledCode, lang]);\n\n return lang ? highlighted : null;\n}\n\nexport function tokenStyle(token: ThemedToken): CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n","import * as React from 'react';\n\nimport { tokenStyle, useHighlight } from './use-highlight';\nimport type { Highlighted } from './use-highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n /** Per-line class, e.g. for diff or search highlighting. When set, every line is wrapped in a `[data-line]` span. */\n lineClassName?: (lineIndex: number, lineText: string) => string | undefined;\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (components.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a sample\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, lineClassName, ...props }: CodeProps) {\n const highlighted = useHighlight(code, lang);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n if (!lineClassName) return <pre {...props}>{code}</pre>;\n const lines = code.split('\\n');\n return (\n <pre {...props}>\n {lines.map((text, i) => (\n <React.Fragment key={i}>\n <span data-line={i} className={lineClassName(i, text)}>\n {text}\n </span>\n {i !== lines.length - 1 && '\\n'}\n </React.Fragment>\n ))}\n </pre>\n );\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code className=\"font-[inherit]\">\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n {lineClassName ? (\n <span\n data-line={lineIndex}\n className={lineClassName(lineIndex, line.map(token => token.content).join(''))}\n >\n {tokenSpans}\n </span>\n ) : (\n <span>{tokenSpans}</span>\n )}\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["useState","useDebounce","useEffect","highlight","React","Code","jsx","jsxs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"code-DLuATvYu.js","sources":["../src/ds/components/Code/use-highlight.ts","../src/ds/components/Code/code.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ThemedToken } from 'shiki/core';\nimport { useDebounce } from 'use-debounce';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\nconst HIGHLIGHT_SAMPLE_INTERVAL_MS = 75;\n\n/** Tokens land a pass behind the code, so the value may still describe the previous code. */\nexport function useHighlight(code: string, lang: string | undefined): Highlighted | null {\n const [highlighted, setHighlighted] = useState<Highlighted | null>(null);\n // maxWait equal to the delay turns the debounce into a throttle.\n const [sampledCode, sampler] = useDebounce(code, HIGHLIGHT_SAMPLE_INTERVAL_MS, {\n leading: true,\n maxWait: HIGHLIGHT_SAMPLE_INTERVAL_MS,\n });\n\n // With `leading`, use-debounce's pending timer still calls setState after unmount.\n useEffect(() => () => sampler.cancel(), [sampler]);\n\n useEffect(() => {\n if (!lang) return;\n\n let cancelled = false;\n\n void highlight(sampledCode, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code: sampledCode, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [sampledCode, lang]);\n\n return lang ? highlighted : null;\n}\n\nexport function tokenStyle(token: ThemedToken): CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n","import * as React from 'react';\n\nimport { tokenStyle, useHighlight } from './use-highlight';\nimport type { Highlighted } from './use-highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n /** Per-line class, e.g. for diff or search highlighting. When set, every line is wrapped in a `[data-line]` span. */\n lineClassName?: (lineIndex: number, lineText: string) => string | undefined;\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (index.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a sample\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, lineClassName, ...props }: CodeProps) {\n const highlighted = useHighlight(code, lang);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n if (!lineClassName) return <pre {...props}>{code}</pre>;\n const lines = code.split('\\n');\n return (\n <pre {...props}>\n {lines.map((text, i) => (\n <React.Fragment key={i}>\n <span data-line={i} className={lineClassName(i, text)}>\n {text}\n </span>\n {i !== lines.length - 1 && '\\n'}\n </React.Fragment>\n ))}\n </pre>\n );\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code className=\"font-[inherit]\">\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n {lineClassName ? (\n <span\n data-line={lineIndex}\n className={lineClassName(lineIndex, line.map(token => token.content).join(''))}\n >\n {tokenSpans}\n </span>\n ) : (\n <span>{tokenSpans}</span>\n )}\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["Code"],"mappings":"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"}
1
+ {"version":3,"file":"code-DLuATvYu.js","sources":["../src/ds/components/Code/use-highlight.ts","../src/ds/components/Code/code.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ThemedToken } from 'shiki/core';\nimport { useDebounce } from 'use-debounce';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\nconst HIGHLIGHT_SAMPLE_INTERVAL_MS = 75;\n\n/** Tokens land a pass behind the code, so the value may still describe the previous code. */\nexport function useHighlight(code: string, lang: string | undefined): Highlighted | null {\n const [highlighted, setHighlighted] = useState<Highlighted | null>(null);\n // maxWait equal to the delay turns the debounce into a throttle.\n const [sampledCode, sampler] = useDebounce(code, HIGHLIGHT_SAMPLE_INTERVAL_MS, {\n leading: true,\n maxWait: HIGHLIGHT_SAMPLE_INTERVAL_MS,\n });\n\n // With `leading`, use-debounce's pending timer still calls setState after unmount.\n useEffect(() => () => sampler.cancel(), [sampler]);\n\n useEffect(() => {\n if (!lang) return;\n\n let cancelled = false;\n\n void highlight(sampledCode, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code: sampledCode, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [sampledCode, lang]);\n\n return lang ? highlighted : null;\n}\n\nexport function tokenStyle(token: ThemedToken): CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n","import * as React from 'react';\n\nimport { tokenStyle, useHighlight } from './use-highlight';\nimport type { Highlighted } from './use-highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n /** Per-line class, e.g. for diff or search highlighting. When set, every line is wrapped in a `[data-line]` span. */\n lineClassName?: (lineIndex: number, lineText: string) => string | undefined;\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (components.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a sample\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, lineClassName, ...props }: CodeProps) {\n const highlighted = useHighlight(code, lang);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n if (!lineClassName) return <pre {...props}>{code}</pre>;\n const lines = code.split('\\n');\n return (\n <pre {...props}>\n {lines.map((text, i) => (\n <React.Fragment key={i}>\n <span data-line={i} className={lineClassName(i, text)}>\n {text}\n </span>\n {i !== lines.length - 1 && '\\n'}\n </React.Fragment>\n ))}\n </pre>\n );\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code className=\"font-[inherit]\">\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n {lineClassName ? (\n <span\n data-line={lineIndex}\n className={lineClassName(lineIndex, line.map(token => token.content).join(''))}\n >\n {tokenSpans}\n </span>\n ) : (\n <span>{tokenSpans}</span>\n )}\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["Code"],"mappings":"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"}
@@ -18,6 +18,7 @@ const CrumbSkeleton = (props) => /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleto
18
18
  const Crumb = ({
19
19
  className,
20
20
  as,
21
+ to,
21
22
  isCurrent,
22
23
  action,
23
24
  switcher,
@@ -27,7 +28,7 @@ const Crumb = ({
27
28
  ...props
28
29
  }) => {
29
30
  const Root = as || "span";
30
- const switcherCoversCrumb = Boolean(isCurrent && switcher);
31
+ const switcherCoversCrumb = Boolean(switcher && (isCurrent || !to));
31
32
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
32
33
  /* @__PURE__ */ jsxRuntime.jsxs(
33
34
  "li",
@@ -49,9 +50,10 @@ const Crumb = ({
49
50
  primitives_controlSize.controlSizeClasses.sm,
50
51
  primitives_transitions.controlStateColorTransition,
51
52
  // Long labels truncate: the current crumb gets more room than nav crumbs.
52
- isCurrent ? "max-w-xs cursor-default text-foreground" : utils_cn.cn(primitives_typography.quietTextHover, "max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill"),
53
+ isCurrent ? "max-w-xs cursor-default text-foreground" : switcherCoversCrumb ? utils_cn.cn(primitives_typography.quietTextHoverInGroup, "max-w-48") : utils_cn.cn(primitives_typography.quietTextHover, "max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill"),
53
54
  className
54
55
  ),
56
+ to,
55
57
  ...props,
56
58
  children: [
57
59
  icon && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"Breadcrumb.cjs.js","sources":["../../src/ds/components/Breadcrumb/Breadcrumb.tsx","../../src/ds/components/Breadcrumb/crumb-switcher.ts"],"sourcesContent":["import React from 'react';\n\nimport { Icon } from '../../icons/Icon';\nimport { SlashIcon } from '../../icons/SlashIcon';\nimport { Skeleton } from '@/ds/components/Skeleton';\nimport { controlSizeClasses } from '@/ds/primitives/control-size';\nimport { controlStateColorTransition } from '@/ds/primitives/transitions';\nimport { quietTextHover } from '@/ds/primitives/typography';\nimport { cn } from '@/lib/utils';\n\nexport interface BreadcrumbProps {\n children?: React.ReactNode;\n label?: string;\n className?: string;\n listClassName?: string;\n}\n\nexport const Breadcrumb = ({ children, label, className, listClassName }: BreadcrumbProps) => {\n return (\n <nav aria-label={label} className={className}>\n <ol className={cn('flex items-center gap-0.5', listClassName)}>{children}</ol>\n </nav>\n );\n};\n\nexport interface CrumbProps {\n isCurrent?: boolean;\n as: React.ElementType;\n className?: string;\n to?: string;\n prefetch?: boolean | null;\n children?: React.ReactNode;\n /** Prefix icon (bare SVG). The crumb wraps it in `<Icon>` and aligns it like a Button adornment. */\n icon?: React.ReactNode;\n /** Renders `CrumbSkeleton` in place of the label. */\n isLoading?: boolean;\n /**\n * Sibling control rendered next to the label (never inside it). Expected to be a\n * `size=\"icon-sm\"` ghost control, e.g. a CopyButton. Entity switchers go in `switcher`.\n */\n action?: React.ReactNode;\n /**\n * Entity switcher rendered next to the label; spread `crumbSwitcherTriggerProps` on it.\n * On the current crumb there is nowhere to navigate, so its hit area covers the whole crumb.\n */\n switcher?: React.ReactNode;\n 'data-testid'?: string;\n}\n\nexport const CrumbSkeleton = (props: { 'data-testid'?: string }) => <Skeleton className=\"h-3 w-24\" {...props} />;\n\nexport const Crumb = ({\n className,\n as,\n isCurrent,\n action,\n switcher,\n icon,\n isLoading,\n children,\n ...props\n}: CrumbProps) => {\n const Root = as || 'span';\n const switcherCoversCrumb = Boolean(isCurrent && switcher);\n\n return (\n <>\n <li\n className={cn(\n 'group flex h-control-sm min-w-0 items-center',\n isCurrent ? 'shrink' : 'shrink-0',\n switcherCoversCrumb &&\n 'relative rounded-full has-[[data-slot=crumb-switcher]_[data-popup-open]]:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:hover:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:active:bg-fill',\n )}\n >\n <Root\n aria-current={isCurrent ? 'page' : undefined}\n className={cn(\n // Same box as `buttonVariants({ variant: 'ghost', size: 'sm' })` so a label and an\n // icon-sm control sitting next to it share height, radius, padding and colors.\n 'inline-flex min-w-0 items-center gap-2 overflow-hidden rounded-full px-[.9em]',\n controlSizeClasses.sm,\n controlStateColorTransition,\n // Long labels truncate: the current crumb gets more room than nav crumbs.\n isCurrent\n ? 'max-w-xs cursor-default text-foreground'\n : cn(quietTextHover, 'max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill'),\n className,\n )}\n {...props}\n >\n {icon && (\n <Icon\n className={cn(\n '-ml-[.3em] shrink-0 opacity-50 group-hover:opacity-100',\n 'transition-opacity duration-normal ease-out-custom',\n )}\n >\n {icon}\n </Icon>\n )}\n {isLoading ? (\n <CrumbSkeleton />\n ) : (\n // `text-overflow` needs a block container, so the label truncates in its\n // own box rather than on the flex Root. Works for plain strings and for\n // components that resolve to a string (route-header `Component` crumbs).\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n )}\n </Root>\n {switcher && (\n <span\n data-slot=\"crumb-switcher\"\n className={cn(\n '-ml-1 flex h-control-sm shrink-0 items-center',\n switcherCoversCrumb &&\n '[&_button]:bg-transparent! [&_button:enabled]:after:absolute [&_button:enabled]:after:inset-0 [&_button:enabled]:after:rounded-full',\n )}\n >\n {switcher}\n </span>\n )}\n {action && <span className=\"-ml-1 flex h-control-sm shrink-0 items-center\">{action}</span>}\n </li>\n {!isCurrent && (\n <li role=\"separator\" className=\"flex h-full items-center\">\n <Icon className=\"text-placeholder\">\n <SlashIcon />\n </Icon>\n </li>\n )}\n </>\n );\n};\n","import type { ComboboxProps } from '../Combobox';\n\nexport const crumbSwitcherTriggerProps = {\n variant: 'ghost',\n size: 'icon-sm',\n align: 'end',\n} as const satisfies Pick<ComboboxProps, 'variant' | 'size' | 'align'>;\n"],"names":["jsx","cn","Skeleton","jsxs","Fragment","controlSizeClasses","controlStateColorTransition","quietTextHover","Icon","SlashIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Breadcrumb.cjs.js","sources":["../../src/ds/components/Breadcrumb/Breadcrumb.tsx","../../src/ds/components/Breadcrumb/crumb-switcher.ts"],"sourcesContent":["import React from 'react';\n\nimport { Icon } from '../../icons/Icon';\nimport { SlashIcon } from '../../icons/SlashIcon';\nimport { Skeleton } from '@/ds/components/Skeleton';\nimport { controlSizeClasses } from '@/ds/primitives/control-size';\nimport { controlStateColorTransition } from '@/ds/primitives/transitions';\nimport { quietTextHover, quietTextHoverInGroup } from '@/ds/primitives/typography';\nimport { cn } from '@/lib/utils';\n\nexport interface BreadcrumbProps {\n children?: React.ReactNode;\n label?: string;\n className?: string;\n listClassName?: string;\n}\n\nexport const Breadcrumb = ({ children, label, className, listClassName }: BreadcrumbProps) => {\n return (\n <nav aria-label={label} className={className}>\n <ol className={cn('flex items-center gap-0.5', listClassName)}>{children}</ol>\n </nav>\n );\n};\n\nexport interface CrumbProps {\n isCurrent?: boolean;\n as: React.ElementType;\n className?: string;\n to?: string;\n prefetch?: boolean | null;\n children?: React.ReactNode;\n /** Prefix icon (bare SVG). The crumb wraps it in `<Icon>` and aligns it like a Button adornment. */\n icon?: React.ReactNode;\n /** Renders `CrumbSkeleton` in place of the label. */\n isLoading?: boolean;\n /**\n * Sibling control rendered next to the label (never inside it). Expected to be a\n * `size=\"icon-sm\"` ghost control, e.g. a CopyButton. Entity switchers go in `switcher`.\n */\n action?: React.ReactNode;\n /**\n * Entity switcher rendered next to the label; spread `crumbSwitcherTriggerProps` on it.\n * When the crumb doesn't navigate (current crumb, or no `to`), its hit area covers the whole crumb.\n */\n switcher?: React.ReactNode;\n 'data-testid'?: string;\n}\n\nexport const CrumbSkeleton = (props: { 'data-testid'?: string }) => <Skeleton className=\"h-3 w-24\" {...props} />;\n\nexport const Crumb = ({\n className,\n as,\n to,\n isCurrent,\n action,\n switcher,\n icon,\n isLoading,\n children,\n ...props\n}: CrumbProps) => {\n const Root = as || 'span';\n const switcherCoversCrumb = Boolean(switcher && (isCurrent || !to));\n\n return (\n <>\n <li\n className={cn(\n 'group flex h-control-sm min-w-0 items-center',\n isCurrent ? 'shrink' : 'shrink-0',\n switcherCoversCrumb &&\n 'relative rounded-full has-[[data-slot=crumb-switcher]_[data-popup-open]]:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:hover:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:active:bg-fill',\n )}\n >\n <Root\n aria-current={isCurrent ? 'page' : undefined}\n className={cn(\n // Same box as `buttonVariants({ variant: 'ghost', size: 'sm' })` so a label and an\n // icon-sm control sitting next to it share height, radius, padding and colors.\n 'inline-flex min-w-0 items-center gap-2 overflow-hidden rounded-full px-[.9em]',\n controlSizeClasses.sm,\n controlStateColorTransition,\n // Long labels truncate: the current crumb gets more room than nav crumbs.\n isCurrent\n ? 'max-w-xs cursor-default text-foreground'\n : switcherCoversCrumb\n ? cn(quietTextHoverInGroup, 'max-w-48')\n : cn(quietTextHover, 'max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill'),\n className,\n )}\n to={to}\n {...props}\n >\n {icon && (\n <Icon\n className={cn(\n '-ml-[.3em] shrink-0 opacity-50 group-hover:opacity-100',\n 'transition-opacity duration-normal ease-out-custom',\n )}\n >\n {icon}\n </Icon>\n )}\n {isLoading ? (\n <CrumbSkeleton />\n ) : (\n // `text-overflow` needs a block container, so the label truncates in its\n // own box rather than on the flex Root. Works for plain strings and for\n // components that resolve to a string (route-header `Component` crumbs).\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n )}\n </Root>\n {switcher && (\n <span\n data-slot=\"crumb-switcher\"\n className={cn(\n '-ml-1 flex h-control-sm shrink-0 items-center',\n switcherCoversCrumb &&\n '[&_button]:bg-transparent! [&_button:enabled]:after:absolute [&_button:enabled]:after:inset-0 [&_button:enabled]:after:rounded-full',\n )}\n >\n {switcher}\n </span>\n )}\n {action && <span className=\"-ml-1 flex h-control-sm shrink-0 items-center\">{action}</span>}\n </li>\n {!isCurrent && (\n <li role=\"separator\" className=\"flex h-full items-center\">\n <Icon className=\"text-placeholder\">\n <SlashIcon />\n </Icon>\n </li>\n )}\n </>\n );\n};\n","import type { ComboboxProps } from '../Combobox';\n\nexport const crumbSwitcherTriggerProps = {\n variant: 'ghost',\n size: 'icon-sm',\n align: 'end',\n} as const satisfies Pick<ComboboxProps, 'variant' | 'size' | 'align'>;\n"],"names":["jsx","cn","Skeleton","jsxs","Fragment","controlSizeClasses","controlStateColorTransition","quietTextHoverInGroup","quietTextHover","Icon","SlashIcon"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { SlashIcon } from '../icons/SlashIcon.es.js';
4
4
  import { S as Skeleton } from '../skeleton-Q4fDAXcz.js';
5
5
  import { controlSizeClasses } from '../primitives/control-size.es.js';
6
6
  import { controlStateColorTransition } from '../primitives/transitions.es.js';
7
- import { quietTextHover } from '../primitives/typography.es.js';
7
+ import { quietTextHoverInGroup, quietTextHover } from '../primitives/typography.es.js';
8
8
  import { c as cn } from '../cn-CQUhN5Jx.js';
9
9
 
10
10
  const Breadcrumb = ({ children, label, className, listClassName }) => {
@@ -14,6 +14,7 @@ const CrumbSkeleton = (props) => /* @__PURE__ */ jsx(Skeleton, { className: "h-3
14
14
  const Crumb = ({
15
15
  className,
16
16
  as,
17
+ to,
17
18
  isCurrent,
18
19
  action,
19
20
  switcher,
@@ -23,7 +24,7 @@ const Crumb = ({
23
24
  ...props
24
25
  }) => {
25
26
  const Root = as || "span";
26
- const switcherCoversCrumb = Boolean(isCurrent && switcher);
27
+ const switcherCoversCrumb = Boolean(switcher && (isCurrent || !to));
27
28
  return /* @__PURE__ */ jsxs(Fragment, { children: [
28
29
  /* @__PURE__ */ jsxs(
29
30
  "li",
@@ -45,9 +46,10 @@ const Crumb = ({
45
46
  controlSizeClasses.sm,
46
47
  controlStateColorTransition,
47
48
  // Long labels truncate: the current crumb gets more room than nav crumbs.
48
- isCurrent ? "max-w-xs cursor-default text-foreground" : cn(quietTextHover, "max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill"),
49
+ isCurrent ? "max-w-xs cursor-default text-foreground" : switcherCoversCrumb ? cn(quietTextHoverInGroup, "max-w-48") : cn(quietTextHover, "max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill"),
49
50
  className
50
51
  ),
52
+ to,
51
53
  ...props,
52
54
  children: [
53
55
  icon && /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"Breadcrumb.es.js","sources":["../../src/ds/components/Breadcrumb/Breadcrumb.tsx","../../src/ds/components/Breadcrumb/crumb-switcher.ts"],"sourcesContent":["import React from 'react';\n\nimport { Icon } from '../../icons/Icon';\nimport { SlashIcon } from '../../icons/SlashIcon';\nimport { Skeleton } from '@/ds/components/Skeleton';\nimport { controlSizeClasses } from '@/ds/primitives/control-size';\nimport { controlStateColorTransition } from '@/ds/primitives/transitions';\nimport { quietTextHover } from '@/ds/primitives/typography';\nimport { cn } from '@/lib/utils';\n\nexport interface BreadcrumbProps {\n children?: React.ReactNode;\n label?: string;\n className?: string;\n listClassName?: string;\n}\n\nexport const Breadcrumb = ({ children, label, className, listClassName }: BreadcrumbProps) => {\n return (\n <nav aria-label={label} className={className}>\n <ol className={cn('flex items-center gap-0.5', listClassName)}>{children}</ol>\n </nav>\n );\n};\n\nexport interface CrumbProps {\n isCurrent?: boolean;\n as: React.ElementType;\n className?: string;\n to?: string;\n prefetch?: boolean | null;\n children?: React.ReactNode;\n /** Prefix icon (bare SVG). The crumb wraps it in `<Icon>` and aligns it like a Button adornment. */\n icon?: React.ReactNode;\n /** Renders `CrumbSkeleton` in place of the label. */\n isLoading?: boolean;\n /**\n * Sibling control rendered next to the label (never inside it). Expected to be a\n * `size=\"icon-sm\"` ghost control, e.g. a CopyButton. Entity switchers go in `switcher`.\n */\n action?: React.ReactNode;\n /**\n * Entity switcher rendered next to the label; spread `crumbSwitcherTriggerProps` on it.\n * On the current crumb there is nowhere to navigate, so its hit area covers the whole crumb.\n */\n switcher?: React.ReactNode;\n 'data-testid'?: string;\n}\n\nexport const CrumbSkeleton = (props: { 'data-testid'?: string }) => <Skeleton className=\"h-3 w-24\" {...props} />;\n\nexport const Crumb = ({\n className,\n as,\n isCurrent,\n action,\n switcher,\n icon,\n isLoading,\n children,\n ...props\n}: CrumbProps) => {\n const Root = as || 'span';\n const switcherCoversCrumb = Boolean(isCurrent && switcher);\n\n return (\n <>\n <li\n className={cn(\n 'group flex h-control-sm min-w-0 items-center',\n isCurrent ? 'shrink' : 'shrink-0',\n switcherCoversCrumb &&\n 'relative rounded-full has-[[data-slot=crumb-switcher]_[data-popup-open]]:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:hover:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:active:bg-fill',\n )}\n >\n <Root\n aria-current={isCurrent ? 'page' : undefined}\n className={cn(\n // Same box as `buttonVariants({ variant: 'ghost', size: 'sm' })` so a label and an\n // icon-sm control sitting next to it share height, radius, padding and colors.\n 'inline-flex min-w-0 items-center gap-2 overflow-hidden rounded-full px-[.9em]',\n controlSizeClasses.sm,\n controlStateColorTransition,\n // Long labels truncate: the current crumb gets more room than nav crumbs.\n isCurrent\n ? 'max-w-xs cursor-default text-foreground'\n : cn(quietTextHover, 'max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill'),\n className,\n )}\n {...props}\n >\n {icon && (\n <Icon\n className={cn(\n '-ml-[.3em] shrink-0 opacity-50 group-hover:opacity-100',\n 'transition-opacity duration-normal ease-out-custom',\n )}\n >\n {icon}\n </Icon>\n )}\n {isLoading ? (\n <CrumbSkeleton />\n ) : (\n // `text-overflow` needs a block container, so the label truncates in its\n // own box rather than on the flex Root. Works for plain strings and for\n // components that resolve to a string (route-header `Component` crumbs).\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n )}\n </Root>\n {switcher && (\n <span\n data-slot=\"crumb-switcher\"\n className={cn(\n '-ml-1 flex h-control-sm shrink-0 items-center',\n switcherCoversCrumb &&\n '[&_button]:bg-transparent! [&_button:enabled]:after:absolute [&_button:enabled]:after:inset-0 [&_button:enabled]:after:rounded-full',\n )}\n >\n {switcher}\n </span>\n )}\n {action && <span className=\"-ml-1 flex h-control-sm shrink-0 items-center\">{action}</span>}\n </li>\n {!isCurrent && (\n <li role=\"separator\" className=\"flex h-full items-center\">\n <Icon className=\"text-placeholder\">\n <SlashIcon />\n </Icon>\n </li>\n )}\n </>\n );\n};\n","import type { ComboboxProps } from '../Combobox';\n\nexport const crumbSwitcherTriggerProps = {\n variant: 'ghost',\n size: 'icon-sm',\n align: 'end',\n} as const satisfies Pick<ComboboxProps, 'variant' | 'size' | 'align'>;\n"],"names":[],"mappings":";;;;;;;;;AAiBO,MAAM,aAAa,CAAC,EAAE,UAAU,KAAA,EAAO,SAAA,EAAW,eAAc,KAAuB;AAC5F,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,YAAA,EAAY,KAAA,EAAO,SAAA,EACtB,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAW,EAAA,CAAG,2BAAA,EAA6B,aAAa,CAAA,EAAI,UAAS,CAAA,EAC3E,CAAA;AAEJ;AA0BO,MAAM,aAAA,GAAgB,CAAC,KAAA,qBAAsC,GAAA,CAAC,YAAS,SAAA,EAAU,UAAA,EAAY,GAAG,KAAA,EAAO;AAEvG,MAAM,QAAQ,CAAC;AAAA,EACpB,SAAA;AAAA,EACA,EAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAkB;AAChB,EAAA,MAAM,OAAO,EAAA,IAAM,MAAA;AACnB,EAAA,MAAM,mBAAA,GAAsB,OAAA,CAAQ,SAAA,IAAa,QAAQ,CAAA;AAEzD,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA;AAAA,UACT,8CAAA;AAAA,UACA,YAAY,QAAA,GAAW,UAAA;AAAA,UACvB,mBAAA,IACE;AAAA,SACJ;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,IAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cACC,cAAA,EAAc,YAAY,MAAA,GAAS,MAAA;AAAA,cACnC,SAAA,EAAW,EAAA;AAAA;AAAA;AAAA,gBAGT,+EAAA;AAAA,gBACA,kBAAA,CAAmB,EAAA;AAAA,gBACnB,2BAAA;AAAA;AAAA,gBAEA,SAAA,GACI,yCAAA,GACA,EAAA,CAAG,cAAA,EAAgB,6DAA6D,CAAA;AAAA,gBACpF;AAAA,eACF;AAAA,cACC,GAAG,KAAA;AAAA,cAEH,QAAA,EAAA;AAAA,gBAAA,IAAA,oBACC,GAAA;AAAA,kBAAC,IAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA;AAAA,sBACT,wDAAA;AAAA,sBACA;AAAA,qBACF;AAAA,oBAEC,QAAA,EAAA;AAAA;AAAA,iBACH;AAAA,gBAED,SAAA,uBACE,aAAA,EAAA,EAAc,CAAA;AAAA;AAAA;AAAA;AAAA,kCAKf,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yBAAA,EAA2B,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA,WAExD;AAAA,UACC,QAAA,oBACC,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,WAAA,EAAU,gBAAA;AAAA,cACV,SAAA,EAAW,EAAA;AAAA,gBACT,+CAAA;AAAA,gBACA,mBAAA,IACE;AAAA,eACJ;AAAA,cAEC,QAAA,EAAA;AAAA;AAAA,WACH;AAAA,UAED,MAAA,oBAAU,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iDAAiD,QAAA,EAAA,MAAA,EAAO;AAAA;AAAA;AAAA,KACrF;AAAA,IACC,CAAC,SAAA,oBACA,GAAA,CAAC,IAAA,EAAA,EAAG,MAAK,WAAA,EAAY,SAAA,EAAU,0BAAA,EAC7B,QAAA,kBAAA,GAAA,CAAC,QAAK,SAAA,EAAU,kBAAA,EACd,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,GACb,CAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ;;ACnIO,MAAM,yBAAA,GAA4B;AAAA,EACvC,OAAA,EAAS,OAAA;AAAA,EACT,IAAA,EAAM,SAAA;AAAA,EACN,KAAA,EAAO;AACT;;;;"}
1
+ {"version":3,"file":"Breadcrumb.es.js","sources":["../../src/ds/components/Breadcrumb/Breadcrumb.tsx","../../src/ds/components/Breadcrumb/crumb-switcher.ts"],"sourcesContent":["import React from 'react';\n\nimport { Icon } from '../../icons/Icon';\nimport { SlashIcon } from '../../icons/SlashIcon';\nimport { Skeleton } from '@/ds/components/Skeleton';\nimport { controlSizeClasses } from '@/ds/primitives/control-size';\nimport { controlStateColorTransition } from '@/ds/primitives/transitions';\nimport { quietTextHover, quietTextHoverInGroup } from '@/ds/primitives/typography';\nimport { cn } from '@/lib/utils';\n\nexport interface BreadcrumbProps {\n children?: React.ReactNode;\n label?: string;\n className?: string;\n listClassName?: string;\n}\n\nexport const Breadcrumb = ({ children, label, className, listClassName }: BreadcrumbProps) => {\n return (\n <nav aria-label={label} className={className}>\n <ol className={cn('flex items-center gap-0.5', listClassName)}>{children}</ol>\n </nav>\n );\n};\n\nexport interface CrumbProps {\n isCurrent?: boolean;\n as: React.ElementType;\n className?: string;\n to?: string;\n prefetch?: boolean | null;\n children?: React.ReactNode;\n /** Prefix icon (bare SVG). The crumb wraps it in `<Icon>` and aligns it like a Button adornment. */\n icon?: React.ReactNode;\n /** Renders `CrumbSkeleton` in place of the label. */\n isLoading?: boolean;\n /**\n * Sibling control rendered next to the label (never inside it). Expected to be a\n * `size=\"icon-sm\"` ghost control, e.g. a CopyButton. Entity switchers go in `switcher`.\n */\n action?: React.ReactNode;\n /**\n * Entity switcher rendered next to the label; spread `crumbSwitcherTriggerProps` on it.\n * When the crumb doesn't navigate (current crumb, or no `to`), its hit area covers the whole crumb.\n */\n switcher?: React.ReactNode;\n 'data-testid'?: string;\n}\n\nexport const CrumbSkeleton = (props: { 'data-testid'?: string }) => <Skeleton className=\"h-3 w-24\" {...props} />;\n\nexport const Crumb = ({\n className,\n as,\n to,\n isCurrent,\n action,\n switcher,\n icon,\n isLoading,\n children,\n ...props\n}: CrumbProps) => {\n const Root = as || 'span';\n const switcherCoversCrumb = Boolean(switcher && (isCurrent || !to));\n\n return (\n <>\n <li\n className={cn(\n 'group flex h-control-sm min-w-0 items-center',\n isCurrent ? 'shrink' : 'shrink-0',\n switcherCoversCrumb &&\n 'relative rounded-full has-[[data-slot=crumb-switcher]_[data-popup-open]]:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:hover:bg-fill-subtle has-[[data-slot=crumb-switcher]_button:enabled]:active:bg-fill',\n )}\n >\n <Root\n aria-current={isCurrent ? 'page' : undefined}\n className={cn(\n // Same box as `buttonVariants({ variant: 'ghost', size: 'sm' })` so a label and an\n // icon-sm control sitting next to it share height, radius, padding and colors.\n 'inline-flex min-w-0 items-center gap-2 overflow-hidden rounded-full px-[.9em]',\n controlSizeClasses.sm,\n controlStateColorTransition,\n // Long labels truncate: the current crumb gets more room than nav crumbs.\n isCurrent\n ? 'max-w-xs cursor-default text-foreground'\n : switcherCoversCrumb\n ? cn(quietTextHoverInGroup, 'max-w-48')\n : cn(quietTextHover, 'max-w-48 cursor-pointer hover:bg-fill-subtle active:bg-fill'),\n className,\n )}\n to={to}\n {...props}\n >\n {icon && (\n <Icon\n className={cn(\n '-ml-[.3em] shrink-0 opacity-50 group-hover:opacity-100',\n 'transition-opacity duration-normal ease-out-custom',\n )}\n >\n {icon}\n </Icon>\n )}\n {isLoading ? (\n <CrumbSkeleton />\n ) : (\n // `text-overflow` needs a block container, so the label truncates in its\n // own box rather than on the flex Root. Works for plain strings and for\n // components that resolve to a string (route-header `Component` crumbs).\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n )}\n </Root>\n {switcher && (\n <span\n data-slot=\"crumb-switcher\"\n className={cn(\n '-ml-1 flex h-control-sm shrink-0 items-center',\n switcherCoversCrumb &&\n '[&_button]:bg-transparent! [&_button:enabled]:after:absolute [&_button:enabled]:after:inset-0 [&_button:enabled]:after:rounded-full',\n )}\n >\n {switcher}\n </span>\n )}\n {action && <span className=\"-ml-1 flex h-control-sm shrink-0 items-center\">{action}</span>}\n </li>\n {!isCurrent && (\n <li role=\"separator\" className=\"flex h-full items-center\">\n <Icon className=\"text-placeholder\">\n <SlashIcon />\n </Icon>\n </li>\n )}\n </>\n );\n};\n","import type { ComboboxProps } from '../Combobox';\n\nexport const crumbSwitcherTriggerProps = {\n variant: 'ghost',\n size: 'icon-sm',\n align: 'end',\n} as const satisfies Pick<ComboboxProps, 'variant' | 'size' | 'align'>;\n"],"names":[],"mappings":"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"}