@nibapp/ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +124 -2
  4. package/TRADEMARKS.md +22 -0
  5. package/dist/_bundled/money/index.d.ts +98 -0
  6. package/dist/_bundled/money/index.js +152 -0
  7. package/dist/_bundled/tokens/index.d.ts +2 -0
  8. package/dist/_bundled/tokens/index.js +2 -0
  9. package/dist/_bundled/tokens/palette.gen.d.ts +51 -0
  10. package/dist/_bundled/tokens/palette.gen.js +206 -0
  11. package/dist/_bundled/tokens/spec.d.ts +291 -0
  12. package/dist/_bundled/tokens/spec.js +221 -0
  13. package/dist/components/accordion.d.ts +27 -0
  14. package/dist/components/accordion.js +66 -0
  15. package/dist/components/alert-dialog.d.ts +59 -0
  16. package/dist/components/alert-dialog.js +82 -0
  17. package/dist/components/alert.d.ts +141 -0
  18. package/dist/components/alert.js +55 -0
  19. package/dist/components/appear.d.ts +21 -0
  20. package/dist/components/appear.js +42 -0
  21. package/dist/components/avatar.d.ts +196 -0
  22. package/dist/components/avatar.js +135 -0
  23. package/dist/components/badge.d.ts +183 -0
  24. package/dist/components/badge.js +68 -0
  25. package/dist/components/block-stack.d.ts +33 -0
  26. package/dist/components/block-stack.js +197 -0
  27. package/dist/components/button.d.ts +327 -0
  28. package/dist/components/button.js +101 -0
  29. package/dist/components/card.d.ts +131 -0
  30. package/dist/components/card.js +100 -0
  31. package/dist/components/checkbox.d.ts +34 -0
  32. package/dist/components/checkbox.js +37 -0
  33. package/dist/components/choice-row.d.ts +104 -0
  34. package/dist/components/choice-row.js +56 -0
  35. package/dist/components/clamped-text.d.ts +17 -0
  36. package/dist/components/clamped-text.js +47 -0
  37. package/dist/components/code-block.d.ts +48 -0
  38. package/dist/components/code-block.js +124 -0
  39. package/dist/components/code-input.d.ts +49 -0
  40. package/dist/components/code-input.js +59 -0
  41. package/dist/components/combobox.d.ts +194 -0
  42. package/dist/components/combobox.js +176 -0
  43. package/dist/components/command-palette.d.ts +57 -0
  44. package/dist/components/command-palette.js +83 -0
  45. package/dist/components/context-menu.d.ts +24 -0
  46. package/dist/components/context-menu.js +30 -0
  47. package/dist/components/copy-button.d.ts +9 -0
  48. package/dist/components/copy-button.js +20 -0
  49. package/dist/components/copy-command.d.ts +18 -0
  50. package/dist/components/copy-command.js +19 -0
  51. package/dist/components/data-table.d.ts +240 -0
  52. package/dist/components/data-table.js +63 -0
  53. package/dist/components/dialog.d.ts +80 -0
  54. package/dist/components/dialog.js +85 -0
  55. package/dist/components/diff-lines.d.ts +20 -0
  56. package/dist/components/diff-lines.js +10 -0
  57. package/dist/components/diff-view.d.ts +128 -0
  58. package/dist/components/diff-view.js +675 -0
  59. package/dist/components/disclosure.d.ts +33 -0
  60. package/dist/components/disclosure.js +51 -0
  61. package/dist/components/drawn-check.d.ts +8 -0
  62. package/dist/components/drawn-check.js +16 -0
  63. package/dist/components/edge-resizer.d.ts +26 -0
  64. package/dist/components/edge-resizer.js +53 -0
  65. package/dist/components/empty-state.d.ts +112 -0
  66. package/dist/components/empty-state.js +56 -0
  67. package/dist/components/expand-toggle.d.ts +19 -0
  68. package/dist/components/expand-toggle.js +10 -0
  69. package/dist/components/field.d.ts +48 -0
  70. package/dist/components/field.js +60 -0
  71. package/dist/components/filter-chip.d.ts +47 -0
  72. package/dist/components/filter-chip.js +33 -0
  73. package/dist/components/group.d.ts +10 -0
  74. package/dist/components/group.js +9 -0
  75. package/dist/components/icon-text.d.ts +11 -0
  76. package/dist/components/icon-text.js +10 -0
  77. package/dist/components/image-sources.d.ts +57 -0
  78. package/dist/components/image-sources.js +58 -0
  79. package/dist/components/image-thumbnail.d.ts +34 -0
  80. package/dist/components/image-thumbnail.js +58 -0
  81. package/dist/components/image-viewer.d.ts +60 -0
  82. package/dist/components/image-viewer.js +411 -0
  83. package/dist/components/inline-edit.d.ts +22 -0
  84. package/dist/components/inline-edit.js +51 -0
  85. package/dist/components/input.d.ts +155 -0
  86. package/dist/components/input.js +109 -0
  87. package/dist/components/item-label.d.ts +11 -0
  88. package/dist/components/item-label.js +5 -0
  89. package/dist/components/item-row.d.ts +23 -0
  90. package/dist/components/item-row.js +19 -0
  91. package/dist/components/kbd.d.ts +55 -0
  92. package/dist/components/kbd.js +86 -0
  93. package/dist/components/link-policy.d.ts +24 -0
  94. package/dist/components/link-policy.js +32 -0
  95. package/dist/components/linked-text.d.ts +15 -0
  96. package/dist/components/linked-text.js +48 -0
  97. package/dist/components/loading-region.d.ts +18 -0
  98. package/dist/components/loading-region.js +17 -0
  99. package/dist/components/markdown.d.ts +34 -0
  100. package/dist/components/markdown.js +307 -0
  101. package/dist/components/menu.d.ts +132 -0
  102. package/dist/components/menu.js +102 -0
  103. package/dist/components/message-bubble.d.ts +57 -0
  104. package/dist/components/message-bubble.js +28 -0
  105. package/dist/components/meta-line.d.ts +28 -0
  106. package/dist/components/meta-line.js +36 -0
  107. package/dist/components/money-input.d.ts +23 -0
  108. package/dist/components/money-input.js +34 -0
  109. package/dist/components/native-select.d.ts +120 -0
  110. package/dist/components/native-select.js +85 -0
  111. package/dist/components/page-trail.d.ts +30 -0
  112. package/dist/components/page-trail.js +43 -0
  113. package/dist/components/page.d.ts +80 -0
  114. package/dist/components/page.js +60 -0
  115. package/dist/components/path-text.d.ts +8 -0
  116. package/dist/components/path-text.js +11 -0
  117. package/dist/components/percent-input.d.ts +20 -0
  118. package/dist/components/percent-input.js +32 -0
  119. package/dist/components/popover.d.ts +52 -0
  120. package/dist/components/popover.js +36 -0
  121. package/dist/components/progress.d.ts +389 -0
  122. package/dist/components/progress.js +142 -0
  123. package/dist/components/radio.d.ts +65 -0
  124. package/dist/components/radio.js +54 -0
  125. package/dist/components/remote-image-gate.d.ts +34 -0
  126. package/dist/components/remote-image-gate.js +60 -0
  127. package/dist/components/resizable.d.ts +23 -0
  128. package/dist/components/resizable.js +30 -0
  129. package/dist/components/rolling-number.d.ts +13 -0
  130. package/dist/components/rolling-number.js +69 -0
  131. package/dist/components/scroll-area.d.ts +17 -0
  132. package/dist/components/scroll-area.js +25 -0
  133. package/dist/components/section-label.d.ts +10 -0
  134. package/dist/components/section-label.js +21 -0
  135. package/dist/components/section-nav.d.ts +22 -0
  136. package/dist/components/section-nav.js +80 -0
  137. package/dist/components/segmented-control.d.ts +125 -0
  138. package/dist/components/segmented-control.js +101 -0
  139. package/dist/components/select.d.ts +120 -0
  140. package/dist/components/select.js +89 -0
  141. package/dist/components/separator.d.ts +10 -0
  142. package/dist/components/separator.js +12 -0
  143. package/dist/components/sheet.d.ts +101 -0
  144. package/dist/components/sheet.js +129 -0
  145. package/dist/components/shortcut-recorder.d.ts +31 -0
  146. package/dist/components/shortcut-recorder.js +67 -0
  147. package/dist/components/skeleton.d.ts +31 -0
  148. package/dist/components/skeleton.js +33 -0
  149. package/dist/components/slider.d.ts +138 -0
  150. package/dist/components/slider.js +98 -0
  151. package/dist/components/spinner.d.ts +37 -0
  152. package/dist/components/spinner.js +16 -0
  153. package/dist/components/switch.d.ts +59 -0
  154. package/dist/components/switch.js +56 -0
  155. package/dist/components/tab-strip.d.ts +29 -0
  156. package/dist/components/tab-strip.js +42 -0
  157. package/dist/components/tabs.d.ts +13 -0
  158. package/dist/components/tabs.js +68 -0
  159. package/dist/components/text-link.d.ts +54 -0
  160. package/dist/components/text-link.js +38 -0
  161. package/dist/components/textarea.d.ts +75 -0
  162. package/dist/components/textarea.js +90 -0
  163. package/dist/components/toast.d.ts +74 -0
  164. package/dist/components/toast.js +128 -0
  165. package/dist/components/tooltip.d.ts +50 -0
  166. package/dist/components/tooltip.js +46 -0
  167. package/dist/components/truncated-text.d.ts +15 -0
  168. package/dist/components/truncated-text.js +19 -0
  169. package/dist/i18n/defaults.d.ts +7 -0
  170. package/dist/i18n/defaults.js +7 -0
  171. package/dist/i18n/format.d.ts +84 -0
  172. package/dist/i18n/format.js +169 -0
  173. package/dist/i18n/index.d.ts +6 -0
  174. package/dist/i18n/index.js +8 -0
  175. package/dist/i18n/locales/ru-formatters.d.ts +3 -0
  176. package/dist/i18n/locales/ru-formatters.js +22 -0
  177. package/dist/i18n/locales/ru.d.ts +5 -0
  178. package/dist/i18n/locales/ru.js +185 -0
  179. package/dist/i18n/plural.d.ts +20 -0
  180. package/dist/i18n/plural.js +26 -0
  181. package/dist/i18n/provider.d.ts +36 -0
  182. package/dist/i18n/provider.js +39 -0
  183. package/dist/i18n/rich.d.ts +14 -0
  184. package/dist/i18n/rich.js +44 -0
  185. package/dist/i18n/ru-plural.d.ts +3 -0
  186. package/dist/i18n/ru-plural.js +4 -0
  187. package/dist/i18n/strings/alert-dialog.d.ts +5 -0
  188. package/dist/i18n/strings/alert-dialog.js +5 -0
  189. package/dist/i18n/strings/block-stack.d.ts +17 -0
  190. package/dist/i18n/strings/block-stack.js +11 -0
  191. package/dist/i18n/strings/code-block.d.ts +18 -0
  192. package/dist/i18n/strings/code-block.js +13 -0
  193. package/dist/i18n/strings/combobox.d.ts +12 -0
  194. package/dist/i18n/strings/combobox.js +10 -0
  195. package/dist/i18n/strings/command-palette.d.ts +17 -0
  196. package/dist/i18n/strings/command-palette.js +11 -0
  197. package/dist/i18n/strings/diff-view.d.ts +92 -0
  198. package/dist/i18n/strings/diff-view.js +68 -0
  199. package/dist/i18n/strings/image-viewer.d.ts +43 -0
  200. package/dist/i18n/strings/image-viewer.js +32 -0
  201. package/dist/i18n/strings/index.d.ts +314 -0
  202. package/dist/i18n/strings/index.js +43 -0
  203. package/dist/i18n/strings/kbd.d.ts +42 -0
  204. package/dist/i18n/strings/kbd.js +39 -0
  205. package/dist/i18n/strings/markdown.d.ts +23 -0
  206. package/dist/i18n/strings/markdown.js +18 -0
  207. package/dist/i18n/strings/money.d.ts +7 -0
  208. package/dist/i18n/strings/money.js +5 -0
  209. package/dist/i18n/strings/primitives.d.ts +25 -0
  210. package/dist/i18n/strings/primitives.js +18 -0
  211. package/dist/i18n/strings/progress.d.ts +4 -0
  212. package/dist/i18n/strings/progress.js +4 -0
  213. package/dist/i18n/strings/shortcut-recorder.d.ts +15 -0
  214. package/dist/i18n/strings/shortcut-recorder.js +11 -0
  215. package/dist/i18n/strings/time.d.ts +41 -0
  216. package/dist/i18n/strings/time.js +20 -0
  217. package/dist/i18n/strings/toast.d.ts +6 -0
  218. package/dist/i18n/strings/toast.js +6 -0
  219. package/dist/i18n/time-zones.d.ts +20 -0
  220. package/dist/i18n/time-zones.js +64 -0
  221. package/dist/index.d.ts +84 -0
  222. package/dist/index.js +88 -0
  223. package/dist/lib/ansi.d.ts +55 -0
  224. package/dist/lib/ansi.js +438 -0
  225. package/dist/lib/clock.d.ts +32 -0
  226. package/dist/lib/clock.js +106 -0
  227. package/dist/lib/cn.d.ts +12 -0
  228. package/dist/lib/cn.js +28 -0
  229. package/dist/lib/control-props.d.ts +21 -0
  230. package/dist/lib/control-props.js +14 -0
  231. package/dist/lib/direction.d.ts +13 -0
  232. package/dist/lib/direction.js +25 -0
  233. package/dist/lib/image-size.d.ts +12 -0
  234. package/dist/lib/image-size.js +66 -0
  235. package/dist/lib/keycaps.d.ts +21 -0
  236. package/dist/lib/keycaps.js +62 -0
  237. package/dist/lib/keyed.d.ts +9 -0
  238. package/dist/lib/keyed.js +14 -0
  239. package/dist/lib/keys.d.ts +49 -0
  240. package/dist/lib/keys.js +159 -0
  241. package/dist/lib/kit-motion.d.ts +9 -0
  242. package/dist/lib/kit-motion.js +12 -0
  243. package/dist/lib/link-check-dialog.d.ts +11 -0
  244. package/dist/lib/link-check-dialog.js +18 -0
  245. package/dist/lib/motion-features.d.ts +1 -0
  246. package/dist/lib/motion-features.js +4 -0
  247. package/dist/lib/motion-provider.d.ts +5 -0
  248. package/dist/lib/motion-provider.js +8 -0
  249. package/dist/lib/motion.d.ts +39 -0
  250. package/dist/lib/motion.js +49 -0
  251. package/dist/lib/number-format.d.ts +114 -0
  252. package/dist/lib/number-format.js +313 -0
  253. package/dist/lib/patch.d.ts +72 -0
  254. package/dist/lib/patch.js +344 -0
  255. package/dist/lib/path.d.ts +34 -0
  256. package/dist/lib/path.js +85 -0
  257. package/dist/lib/search.d.ts +10 -0
  258. package/dist/lib/search.js +20 -0
  259. package/dist/lib/shiki-themes.d.ts +7 -0
  260. package/dist/lib/shiki-themes.js +115 -0
  261. package/dist/lib/shiki.d.ts +101 -0
  262. package/dist/lib/shiki.js +353 -0
  263. package/dist/lib/shortcut-context.d.ts +23 -0
  264. package/dist/lib/shortcut-context.js +29 -0
  265. package/dist/lib/text.d.ts +56 -0
  266. package/dist/lib/text.js +149 -0
  267. package/dist/lib/theme-scope.d.ts +18 -0
  268. package/dist/lib/theme-scope.js +24 -0
  269. package/dist/lib/theme.d.ts +40 -0
  270. package/dist/lib/theme.js +96 -0
  271. package/dist/lib/tree-keys.d.ts +72 -0
  272. package/dist/lib/tree-keys.js +126 -0
  273. package/dist/lib/url.d.ts +17 -0
  274. package/dist/lib/url.js +38 -0
  275. package/dist/lib/use-copy-to-clipboard.d.ts +13 -0
  276. package/dist/lib/use-copy-to-clipboard.js +40 -0
  277. package/dist/lib/use-delayed-loading.d.ts +11 -0
  278. package/dist/lib/use-delayed-loading.js +31 -0
  279. package/dist/lib/use-file-picker.d.ts +26 -0
  280. package/dist/lib/use-file-picker.js +43 -0
  281. package/dist/lib/use-hydrated.d.ts +6 -0
  282. package/dist/lib/use-hydrated.js +11 -0
  283. package/dist/lib/use-latest.d.ts +12 -0
  284. package/dist/lib/use-latest.js +22 -0
  285. package/dist/lib/use-resolved-theme.d.ts +12 -0
  286. package/dist/lib/use-resolved-theme.js +25 -0
  287. package/dist/styles/base.css +393 -0
  288. package/dist/styles/code.css +35 -0
  289. package/dist/styles/fonts.css +12 -0
  290. package/dist/styles/index.css +13 -0
  291. package/dist/styles/tokens.gen.css +494 -0
  292. package/package.json +208 -4
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { useUiLocale, useUiStrings } from "../i18n/provider.js";
5
+ import { cn } from "../lib/cn.js";
6
+ import { keyGlyph } from "../lib/keycaps.js";
7
+ import { isMacPlatform } from "../lib/keys.js";
8
+ import { formatList } from "../lib/text.js";
9
+ import { useLatest } from "../lib/use-latest.js";
10
+ import { Button } from "./button.js";
11
+ import { Shortcut } from "./kbd.js";
12
+ /** A modifier pressed on its own is the start of a combination, not a refused press. */
13
+ const MODIFIER_KEYS = new Set(["Alt", "AltGraph", "Control", "Meta", "Shift"]);
14
+ /**
15
+ * Records a keyboard shortcut. Change starts recording and the next combination becomes the
16
+ * shortcut; Escape, a second click on Change or focus leaving it stops without one. While recording,
17
+ * every key press is the recorder's: the app's own shortcuts don't act on it. A press that can't be a
18
+ * shortcut says what it needs; the state and the recorded keys are announced.
19
+ */
20
+ export function ShortcutRecorder({ keys, parse, needs, onRecord, children, className, }) {
21
+ const t = useUiStrings().shortcutRecorder;
22
+ const kbd = useUiStrings().kbd;
23
+ const locale = useUiLocale();
24
+ const [recording, setRecording] = useState(false);
25
+ const [refused, setRefused] = useState(false);
26
+ const latest = useLatest({ parse, onRecord });
27
+ const record = (on) => {
28
+ setRecording(on);
29
+ setRefused(false);
30
+ };
31
+ useEffect(() => {
32
+ if (!recording)
33
+ return undefined;
34
+ // Capture on the window: the press reaches the recorder before any other key handler, wherever
35
+ // focus is (WebKit doesn't focus a clicked button).
36
+ const onKeyDown = (event) => {
37
+ event.preventDefault();
38
+ event.stopPropagation();
39
+ if (event.repeat || MODIFIER_KEYS.has(event.key))
40
+ return;
41
+ if (event.key === "Escape") {
42
+ setRecording(false);
43
+ setRefused(false);
44
+ return;
45
+ }
46
+ const shortcut = latest.current.parse(event);
47
+ if (shortcut === undefined) {
48
+ setRefused(true);
49
+ return;
50
+ }
51
+ setRecording(false);
52
+ setRefused(false);
53
+ latest.current.onRecord(shortcut);
54
+ };
55
+ window.addEventListener("keydown", onKeyDown, true);
56
+ return () => window.removeEventListener("keydown", onKeyDown, true);
57
+ }, [recording]);
58
+ const mac = isMacPlatform();
59
+ // "⌥, ⌘, ⌃ or ⇧" on a Mac, "Alt, Ctrl or Shift" elsewhere, joined the way the language joins them.
60
+ const modifiers = formatList(needs.modifiers.map((key) => keyGlyph(key, mac, kbd)), locale, "disjunction");
61
+ const hint = needs.functionKey === true ? t.needsModifierOrFunctionKey({ modifiers }) : t.needsModifier({ modifiers });
62
+ return (_jsxs("div", { "data-slot": "shortcut-recorder", className: cn("flex flex-col items-end", className), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-live": "polite", className: "text-sm text-fg-muted", children: recording ? t.recording : keys === null ? t.off : _jsx(Shortcut, { keys: keys, spoken: true }) }), _jsx(Button, { size: "sm", variant: "secondary", "aria-pressed": recording, onClick: (event) => {
63
+ // Focused, so focus leaving it stops the recording (WebKit doesn't focus a clicked button).
64
+ event.currentTarget.focus();
65
+ record(!recording);
66
+ }, onBlur: () => record(false), children: t.change }), !recording && children] }), _jsx("span", { "aria-live": "polite", className: "text-xs text-fg-subtle", children: refused && _jsx("span", { className: "mt-1 block", children: hint }) })] }));
67
+ }
@@ -0,0 +1,31 @@
1
+ import type { ComponentProps } from "react";
2
+ import { type VariantProps } from "../lib/cn.js";
3
+ declare const skeleton: import("tailwind-variants").TVReturnType<{
4
+ shape: {
5
+ rect: "rounded-md";
6
+ circle: "rounded-full";
7
+ text: "h-[0.8em] rounded-[0.25em]";
8
+ };
9
+ }, undefined, string[], {
10
+ shape: {
11
+ rect: "rounded-md";
12
+ circle: "rounded-full";
13
+ text: "h-[0.8em] rounded-[0.25em]";
14
+ };
15
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
16
+ shape: {
17
+ rect: "rounded-md";
18
+ circle: "rounded-full";
19
+ text: "h-[0.8em] rounded-[0.25em]";
20
+ };
21
+ }, undefined>>;
22
+ export interface SkeletonProps extends ComponentProps<"span">, VariantProps<typeof skeleton> {
23
+ /** Lines of text (implies the text shape); the last one is shorter. */
24
+ lines?: number;
25
+ }
26
+ /**
27
+ * Loading placeholder. Size it with classes (`h-8 w-40`, `size-8`). Decorative — mark the loading
28
+ * region itself with `aria-busy` and announce completion there.
29
+ */
30
+ export declare function Skeleton({ shape, lines, className, style, ...props }: SkeletonProps): import("react").JSX.Element;
31
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn, tv } from "../lib/cn.js";
3
+ const skeleton = tv({
4
+ base: [
5
+ // A tint of the ink, not a surface color: it shows on any surface in both themes (in dark mode
6
+ // a raised card is the same gray as `bg-muted`, so placeholders on it vanished).
7
+ "block shrink-0 bg-fg/8",
8
+ // A soft highlight drifts across on the shared shimmer rhythm (2.2s, ease-in-out).
9
+ "bg-[linear-gradient(100deg,transparent_35%,color-mix(in_oklab,var(--fg)_5%,transparent)_50%,transparent_65%)]",
10
+ "bg-size-[250%_100%] bg-no-repeat animate-shimmer",
11
+ "motion-reduce:animate-none motion-reduce:bg-none",
12
+ ],
13
+ variants: {
14
+ shape: {
15
+ rect: "rounded-md",
16
+ circle: "rounded-full",
17
+ text: "h-[0.8em] rounded-[0.25em]",
18
+ },
19
+ },
20
+ defaultVariants: { shape: "rect" },
21
+ });
22
+ /** Width pattern for multi-line text so paragraphs look written, not ruled. */
23
+ const LINE_WIDTHS = ["100%", "94%", "97%", "88%", "92%"];
24
+ /**
25
+ * Loading placeholder. Size it with classes (`h-8 w-40`, `size-8`). Decorative — mark the loading
26
+ * region itself with `aria-busy` and announce completion there.
27
+ */
28
+ export function Skeleton({ shape, lines = 1, className, style, ...props }) {
29
+ if (lines > 1) {
30
+ return (_jsx("span", { "data-slot": "skeleton", "aria-hidden": true, className: cn("flex w-full flex-col gap-[0.6em]", className), style: style, ...props, children: Array.from({ length: lines }, (_, i) => i).map((line) => (_jsx("span", { className: skeleton({ shape: "text" }), style: { width: line === lines - 1 ? "62%" : LINE_WIDTHS[line % LINE_WIDTHS.length] } }, line))) }));
31
+ }
32
+ return (_jsx("span", { "data-slot": "skeleton", "aria-hidden": true, className: cn(skeleton({ shape }), shape === "text" && "w-full", className), style: style, ...props }));
33
+ }
@@ -0,0 +1,138 @@
1
+ import { Slider as BaseSlider } from "@base-ui/react/slider";
2
+ import { type ComponentProps, type ReactNode } from "react";
3
+ import { type VariantProps } from "../lib/cn.js";
4
+ declare const sliderStyles: import("tailwind-variants").TVReturnType<{
5
+ size: {
6
+ sm: {
7
+ control: string;
8
+ track: string;
9
+ thumb: string;
10
+ };
11
+ md: {
12
+ control: string;
13
+ track: string;
14
+ thumb: string;
15
+ };
16
+ };
17
+ tooltip: {
18
+ never: {
19
+ bubble: string;
20
+ };
21
+ active: {
22
+ bubble: string[];
23
+ };
24
+ always: {
25
+ bubble: string;
26
+ };
27
+ };
28
+ }, {
29
+ root: string;
30
+ header: string;
31
+ label: string;
32
+ value: string;
33
+ control: string;
34
+ track: string;
35
+ indicator: string;
36
+ thumb: string[];
37
+ bubble: string[];
38
+ tick: string;
39
+ marks: string;
40
+ }, undefined, {
41
+ size: {
42
+ sm: {
43
+ control: string;
44
+ track: string;
45
+ thumb: string;
46
+ };
47
+ md: {
48
+ control: string;
49
+ track: string;
50
+ thumb: string;
51
+ };
52
+ };
53
+ tooltip: {
54
+ never: {
55
+ bubble: string;
56
+ };
57
+ active: {
58
+ bubble: string[];
59
+ };
60
+ always: {
61
+ bubble: string;
62
+ };
63
+ };
64
+ }, {
65
+ root: string;
66
+ header: string;
67
+ label: string;
68
+ value: string;
69
+ control: string;
70
+ track: string;
71
+ indicator: string;
72
+ thumb: string[];
73
+ bubble: string[];
74
+ tick: string;
75
+ marks: string;
76
+ }, import("tailwind-variants").TVReturnTypeLike<{
77
+ size: {
78
+ sm: {
79
+ control: string;
80
+ track: string;
81
+ thumb: string;
82
+ };
83
+ md: {
84
+ control: string;
85
+ track: string;
86
+ thumb: string;
87
+ };
88
+ };
89
+ tooltip: {
90
+ never: {
91
+ bubble: string;
92
+ };
93
+ active: {
94
+ bubble: string[];
95
+ };
96
+ always: {
97
+ bubble: string;
98
+ };
99
+ };
100
+ }, {
101
+ root: string;
102
+ header: string;
103
+ label: string;
104
+ value: string;
105
+ control: string;
106
+ track: string;
107
+ indicator: string;
108
+ thumb: string[];
109
+ bubble: string[];
110
+ tick: string;
111
+ marks: string;
112
+ }>>;
113
+ export interface SliderMark {
114
+ value: number;
115
+ /** Text under the track; also announced when the thumb sits on this value. */
116
+ label?: string;
117
+ }
118
+ export interface SliderProps extends Omit<ComponentProps<typeof BaseSlider.Root>, "children">, VariantProps<typeof sliderStyles> {
119
+ /** Visible label above the track. */
120
+ label?: ReactNode;
121
+ /** Show the formatted value(s) opposite the label. */
122
+ showValue?: boolean;
123
+ /** Formats values for the header, tooltip and screen readers. Defaults to `Intl.NumberFormat`. */
124
+ formatValue?: (value: number) => string;
125
+ /**
126
+ * Accessible names per thumb, e.g. `["Minimum", "Maximum"]`. A single thumb is named by the
127
+ * slider's `aria-label`; a range keeps it on the group.
128
+ */
129
+ thumbLabels?: readonly string[];
130
+ /** Snap points rendered as ticks (and labels) along the track. */
131
+ marks?: readonly SliderMark[];
132
+ }
133
+ /**
134
+ * Ink-filled slider. Pass an array value for a range. `tooltip="active"` floats the value over
135
+ * the thumb while hovered, focused or dragged.
136
+ */
137
+ export declare function Slider({ size, tooltip, label, showValue, formatValue, thumbLabels, marks, className, "aria-label": ariaLabel, ...props }: SliderProps): import("react").JSX.Element;
138
+ export {};
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Slider as BaseSlider } from "@base-ui/react/slider";
4
+ import { useMemo } from "react";
5
+ import { useUiLocale } from "../i18n/provider.js";
6
+ import { cn, resolveClassName, tv } from "../lib/cn.js";
7
+ const sliderStyles = tv({
8
+ slots: {
9
+ root: "flex w-full min-w-0 flex-col gap-2 data-disabled:opacity-45",
10
+ header: "flex items-baseline justify-between gap-3",
11
+ label: "text-sm font-medium text-fg select-none",
12
+ value: "text-sm text-fg-muted tabular",
13
+ control: "relative flex w-full touch-none select-none items-center data-disabled:pointer-events-none",
14
+ track: "relative w-full rounded-full bg-border select-none",
15
+ indicator: "rounded-full bg-primary select-none",
16
+ thumb: [
17
+ "group/thumb relative rounded-full border border-border-strong bg-white shadow-sm outline-none select-none",
18
+ "ring-0 ring-primary/10 dark:ring-white/12",
19
+ "transition-[box-shadow,scale] duration-(--duration-spring-snappy) ease-(--ease-spring-snappy)",
20
+ "hover:ring-4 data-dragging:scale-110 data-dragging:ring-4",
21
+ "has-[:focus-visible]:ring-4 has-[:focus-visible]:ring-focus/45",
22
+ "motion-reduce:transition-none motion-reduce:data-dragging:scale-100",
23
+ ],
24
+ bubble: [
25
+ "pointer-events-none absolute bottom-full left-1/2 mb-2 -translate-x-1/2 whitespace-nowrap rounded-md px-1.5 py-0.5",
26
+ "bg-gray-12 text-xs font-medium text-gray-1 tabular shadow-md",
27
+ "origin-bottom transition-[opacity,scale] duration-(--duration-spring-snappy) ease-(--ease-spring-snappy) motion-reduce:transition-none",
28
+ ],
29
+ tick: "pointer-events-none absolute top-1/2 size-1 -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2 rounded-full transition-colors",
30
+ marks: "relative h-4 w-full text-xs text-fg-subtle select-none",
31
+ },
32
+ variants: {
33
+ size: {
34
+ sm: { control: "h-5", track: "h-[3px]", thumb: "size-3.5" },
35
+ md: { control: "h-6", track: "h-1", thumb: "size-4" },
36
+ },
37
+ tooltip: {
38
+ never: { bubble: "hidden" },
39
+ active: {
40
+ bubble: [
41
+ "scale-90 opacity-0",
42
+ "group-hover/thumb:scale-100 group-hover/thumb:opacity-100",
43
+ "group-data-dragging/thumb:scale-100 group-data-dragging/thumb:opacity-100",
44
+ "group-has-[:focus-visible]/thumb:scale-100 group-has-[:focus-visible]/thumb:opacity-100",
45
+ ],
46
+ },
47
+ always: { bubble: "scale-100 opacity-100" },
48
+ },
49
+ },
50
+ defaultVariants: { size: "md", tooltip: "never" },
51
+ });
52
+ /**
53
+ * Ink-filled slider. Pass an array value for a range. `tooltip="active"` floats the value over
54
+ * the thumb while hovered, focused or dragged.
55
+ */
56
+ export function Slider({ size, tooltip, label, showValue = false, formatValue, thumbLabels, marks, className, "aria-label": ariaLabel, ...props }) {
57
+ const styles = sliderStyles({ size, tooltip });
58
+ const min = props.min ?? 0;
59
+ const max = props.max ?? 100;
60
+ const initial = props.value ?? props.defaultValue;
61
+ const thumbCount = Array.isArray(initial) ? initial.length : 1;
62
+ // The provider's locale unless the caller passes one (Base UI formats the value with it too).
63
+ const uiLocale = useUiLocale();
64
+ const locale = props.locale ?? uiLocale;
65
+ // One formatter per locale and options: a slider re-renders on every pointer move while dragging.
66
+ const numberFormat = useMemo(() => new Intl.NumberFormat(locale, props.format), [locale, props.format]);
67
+ const format = formatValue ?? ((value) => numberFormat.format(value));
68
+ const markLabel = (value) => marks?.find((mark) => mark.value === value)?.label;
69
+ const percent = (value) => ((value - min) / (max - min || 1)) * 100;
70
+ // The range input is what a screen reader names; the group around it is not.
71
+ const thumbName = (index) => {
72
+ const name = thumbLabels?.[index] ?? (thumbCount === 1 ? ariaLabel : undefined);
73
+ return name === undefined ? {} : { "aria-label": name };
74
+ };
75
+ return (_jsxs(BaseSlider.Root, { "data-slot": "slider", className: (state) => styles.root({ className: resolveClassName(className, state) }), ...props, ...(thumbCount > 1 && ariaLabel !== undefined ? { "aria-label": ariaLabel } : {}), locale: locale, children: [(label !== undefined || showValue) && (_jsxs("div", { className: styles.header(), children: [label !== undefined && _jsx(BaseSlider.Label, { className: styles.label(), children: label }), showValue && (_jsx(BaseSlider.Value, { className: cn(styles.value(), "ms-auto"), children: (_formatted, values) => values.map((value) => markLabel(value) ?? format(value)).join(" – ") }))] })), _jsx(BaseSlider.Control, { className: styles.control(), children: _jsxs(BaseSlider.Track, { className: styles.track(), render: (trackProps, state) => {
76
+ const low = thumbCount > 1 ? Math.min(...state.values) : min;
77
+ const high = Math.max(...state.values);
78
+ return (_jsxs("div", { ...trackProps, children: [trackProps.children, marks?.map((mark) => {
79
+ const active = mark.value >= low && mark.value <= high;
80
+ return (_jsx("span", { "aria-hidden": true, className: cn(styles.tick(), active ? "bg-primary-fg/70" : "bg-fg-faint"), style: { insetInlineStart: `${percent(mark.value)}%` } }, mark.value));
81
+ })] }));
82
+ }, children: [_jsx(BaseSlider.Indicator, { className: styles.indicator() }), Array.from({ length: thumbCount }, (_, index) => (_jsx(BaseSlider.Thumb
83
+ // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional by definition.
84
+ , { index: index, className: styles.thumb(), getAriaValueText: (formatted, value) => markLabel(value) ?? (formatValue ? format(value) : formatted), ...thumbName(index), render: (thumbProps, state) => {
85
+ const value = state.values[index] ?? min;
86
+ return (_jsxs("div", { ...thumbProps, children: [thumbProps.children, _jsx("span", { "aria-hidden": true, className: styles.bubble(), children: markLabel(value) ?? format(value) })] }));
87
+ } }, index)))] }) }), marks?.some((mark) => mark.label !== undefined) && (_jsx("div", { "aria-hidden": true, className: styles.marks(), children: marks.map((mark) => {
88
+ const at = percent(mark.value);
89
+ // Pinned inside the track at its ends, centred under the value between them; mirrored
90
+ // in a right-to-left layout, where Base UI runs the slider from the right.
91
+ const shift = at <= 0
92
+ ? "translate-x-0"
93
+ : at >= 100
94
+ ? "-translate-x-full rtl:translate-x-full"
95
+ : "-translate-x-1/2 rtl:translate-x-1/2";
96
+ return (_jsx("span", { className: cn("absolute top-0 whitespace-nowrap", shift), style: { insetInlineStart: `${at}%` }, children: mark.label }, mark.value));
97
+ }) }))] }));
98
+ }
@@ -0,0 +1,37 @@
1
+ import type { ComponentProps } from "react";
2
+ import { type VariantProps } from "../lib/cn.js";
3
+ declare const spinner: import("tailwind-variants").TVReturnType<{
4
+ size: {
5
+ xs: "size-3";
6
+ sm: "size-3.5";
7
+ md: "size-4";
8
+ lg: "size-5";
9
+ xl: "size-6";
10
+ };
11
+ }, undefined, "inline-block shrink-0 animate-spin motion-reduce:animate-[spin_2.4s_linear_infinite]", {
12
+ size: {
13
+ xs: "size-3";
14
+ sm: "size-3.5";
15
+ md: "size-4";
16
+ lg: "size-5";
17
+ xl: "size-6";
18
+ };
19
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
20
+ size: {
21
+ xs: "size-3";
22
+ sm: "size-3.5";
23
+ md: "size-4";
24
+ lg: "size-5";
25
+ xl: "size-6";
26
+ };
27
+ }, undefined>>;
28
+ export interface SpinnerProps extends Omit<ComponentProps<"svg">, "children">, VariantProps<typeof spinner> {
29
+ /** Accessible label; omit when a surrounding element already announces the busy state. */
30
+ label?: string;
31
+ }
32
+ /**
33
+ * Minimal busy indicator: a quarter arc over a faint track, for buttons, inline loads and small
34
+ * regions. A region that holds placeholders while it loads is a `LoadingRegion`.
35
+ */
36
+ export declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("react").JSX.Element;
37
+ export {};
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn, tv } from "../lib/cn.js";
3
+ const spinner = tv({
4
+ base: "inline-block shrink-0 animate-spin motion-reduce:animate-[spin_2.4s_linear_infinite]",
5
+ variants: {
6
+ size: { xs: "size-3", sm: "size-3.5", md: "size-4", lg: "size-5", xl: "size-6" },
7
+ },
8
+ defaultVariants: { size: "md" },
9
+ });
10
+ /**
11
+ * Minimal busy indicator: a quarter arc over a faint track, for buttons, inline loads and small
12
+ * regions. A region that holds placeholders while it loads is a `LoadingRegion`.
13
+ */
14
+ export function Spinner({ size, label, className, ...props }) {
15
+ return (_jsxs("svg", { "data-slot": "spinner", viewBox: "0 0 16 16", fill: "none", className: cn(spinner({ size }), className), role: label ? "status" : "img", "aria-label": label, "aria-hidden": label ? undefined : true, ...props, children: [_jsx("circle", { cx: "8", cy: "8", r: "6.25", stroke: "currentColor", strokeOpacity: "0.18", strokeWidth: "1.5" }), _jsx("path", { d: "M8 1.75A6.25 6.25 0 0 1 14.25 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }));
16
+ }
@@ -0,0 +1,59 @@
1
+ import { Switch as BaseSwitch } from "@base-ui/react/switch";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ import { type VariantProps } from "../lib/cn.js";
4
+ export declare const switchStyles: import("tailwind-variants").TVReturnType<{
5
+ size: {
6
+ /** 16 × 28 — dense rows, menus. */
7
+ sm: {
8
+ track: string;
9
+ thumb: string[];
10
+ };
11
+ /** 20 × 36 — settings rows. */
12
+ md: {
13
+ track: string;
14
+ thumb: string[];
15
+ };
16
+ };
17
+ }, {
18
+ track: string[];
19
+ thumb: string[];
20
+ }, undefined, {
21
+ size: {
22
+ /** 16 × 28 — dense rows, menus. */
23
+ sm: {
24
+ track: string;
25
+ thumb: string[];
26
+ };
27
+ /** 20 × 36 — settings rows. */
28
+ md: {
29
+ track: string;
30
+ thumb: string[];
31
+ };
32
+ };
33
+ }, {
34
+ track: string[];
35
+ thumb: string[];
36
+ }, import("tailwind-variants").TVReturnTypeLike<{
37
+ size: {
38
+ /** 16 × 28 — dense rows, menus. */
39
+ sm: {
40
+ track: string;
41
+ thumb: string[];
42
+ };
43
+ /** 20 × 36 — settings rows. */
44
+ md: {
45
+ track: string;
46
+ thumb: string[];
47
+ };
48
+ };
49
+ }, {
50
+ track: string[];
51
+ thumb: string[];
52
+ }>>;
53
+ export interface SwitchProps extends Omit<ComponentProps<typeof BaseSwitch.Root>, "children">, VariantProps<typeof switchStyles> {
54
+ /** Visible label to the right of the switch. For settings rows prefer <Field orientation="horizontal">. */
55
+ label?: ReactNode;
56
+ description?: ReactNode;
57
+ }
58
+ /** Ink toggle with a springy thumb that stretches while pressed. */
59
+ export declare function Switch({ size, label, description, className, ...props }: SwitchProps): import("react").JSX.Element;
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Switch as BaseSwitch } from "@base-ui/react/switch";
4
+ import { cn, resolveClassName, tv } from "../lib/cn.js";
5
+ const spring = "duration-(--duration-spring-snappy) ease-(--ease-spring-snappy) motion-reduce:transition-none";
6
+ export const switchStyles = tv({
7
+ slots: {
8
+ track: [
9
+ "group/switch relative inline-flex shrink-0 items-center rounded-full p-0.5 outline-none touch-target",
10
+ "bg-border-strong [box-shadow:inset_0_1px_1px_var(--shadow-1)]",
11
+ `transition-[background-color,box-shadow] ${spring}`,
12
+ "hover:bg-fg-faint/80 focus-visible:ring-focus",
13
+ "data-checked:bg-primary data-checked:hover:bg-primary-hover",
14
+ "data-invalid:data-unchecked:[box-shadow:inset_0_0_0_1px_var(--danger-border)]",
15
+ "data-disabled:pointer-events-none data-disabled:opacity-45 data-readonly:pointer-events-none",
16
+ ],
17
+ // The thumb starts at the track's start edge and slides toward its end: right in a left-to-right
18
+ // language, left in a right-to-left one.
19
+ thumb: [
20
+ "pointer-events-none block rounded-full bg-white shadow-sm dark:bg-gray-11",
21
+ "data-checked:bg-primary-fg",
22
+ `transition-[translate,scale,background-color] ${spring}`,
23
+ ],
24
+ },
25
+ variants: {
26
+ size: {
27
+ /** 16 × 28 — dense rows, menus. */
28
+ sm: {
29
+ track: "h-4 w-7",
30
+ thumb: [
31
+ "h-3 w-3 data-checked:translate-x-3 rtl:data-checked:-translate-x-3",
32
+ "group-active/switch:scale-x-115",
33
+ ],
34
+ },
35
+ /** 20 × 36 — settings rows. */
36
+ md: {
37
+ track: "h-5 w-9",
38
+ thumb: [
39
+ "h-4 w-4 data-checked:translate-x-4 rtl:data-checked:-translate-x-4",
40
+ "group-active/switch:scale-x-115",
41
+ ],
42
+ },
43
+ },
44
+ },
45
+ defaultVariants: { size: "md" },
46
+ });
47
+ /** Ink toggle with a springy thumb that stretches while pressed. */
48
+ export function Switch({ size, label, description, className, ...props }) {
49
+ const styles = switchStyles({ size });
50
+ const control = (_jsx(BaseSwitch.Root, { "data-slot": "switch", className: (state) => styles.track({
51
+ className: cn((label !== undefined || description !== undefined) && (size === "sm" ? "mt-[3px]" : "mt-px"), resolveClassName(className, state)),
52
+ }), ...props, children: _jsx(BaseSwitch.Thumb, { className: styles.thumb() }) }));
53
+ if (label === undefined && description === undefined)
54
+ return control;
55
+ return (_jsxs("label", { "data-slot": "switch-label", className: "inline-flex max-w-full cursor-default select-none items-start gap-2.5 has-data-disabled:opacity-45", children: [control, _jsxs("span", { className: "flex min-w-0 flex-col", children: [label !== undefined && _jsx("span", { className: "text-base text-fg", children: label }), description !== undefined && _jsx("span", { className: "text-sm text-fg-subtle", children: description })] })] }));
56
+ }
@@ -0,0 +1,29 @@
1
+ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ export interface TabStripProps {
4
+ /** The selected tab's value; `null` when none is. */
5
+ readonly value: string | null;
6
+ readonly onValueChange: (value: string) => void;
7
+ /** The strip's name for assistive technology ("Terminals", "Browser tabs"). */
8
+ readonly "aria-label": string;
9
+ readonly className?: string | undefined;
10
+ readonly children: ReactNode;
11
+ }
12
+ /**
13
+ * Document tabs over one view: the browser pane's pages, the terminals, the side pane's kinds. Base
14
+ * UI Tabs give every strip one keyboard model: Tab reaches the selected tab, the arrow keys (and Home
15
+ * and End) move between tabs, Enter or Space selects one. There are no panels: the caller shows the
16
+ * selected tab's view. Tabs may close (`TabStripTab` `onClose`) and carry their own handlers (rename
17
+ * on F2 or a double click).
18
+ */
19
+ export declare function TabStrip({ value, onValueChange, className, children, ...props }: TabStripProps): import("react").JSX.Element;
20
+ export interface TabStripTabProps extends Omit<ComponentProps<typeof BaseTabs.Tab>, "value" | "render" | "nativeButton" | "className"> {
21
+ readonly value: string;
22
+ /** Closes the tab: its ×, Delete or Backspace while the tab has focus, and a middle click. */
23
+ readonly onClose?: (() => void) | undefined;
24
+ /** What the × does, as its tooltip ("Close Terminal 2"). */
25
+ readonly closeLabel?: string | undefined;
26
+ readonly className?: string | undefined;
27
+ }
28
+ /** One tab: what the caller puts in it (an icon and a label, or a rename field), then its × button. */
29
+ export declare function TabStripTab({ onClose, closeLabel, className, children, onKeyDown, onAuxClick, ...props }: TabStripTabProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
4
+ import { X } from "lucide-react";
5
+ import { cn } from "../lib/cn.js";
6
+ /**
7
+ * Document tabs over one view: the browser pane's pages, the terminals, the side pane's kinds. Base
8
+ * UI Tabs give every strip one keyboard model: Tab reaches the selected tab, the arrow keys (and Home
9
+ * and End) move between tabs, Enter or Space selects one. There are no panels: the caller shows the
10
+ * selected tab's view. Tabs may close (`TabStripTab` `onClose`) and carry their own handlers (rename
11
+ * on F2 or a double click).
12
+ */
13
+ export function TabStrip({ value, onValueChange, className, children, ...props }) {
14
+ return (_jsx(BaseTabs.Root, { "data-slot": "tab-strip", value: value, onValueChange: (next) => {
15
+ if (typeof next === "string")
16
+ onValueChange(next);
17
+ }, className: cn("flex min-w-0", className), children: _jsx(BaseTabs.List, { "aria-label": props["aria-label"], className: "flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto scrollbar-none", children: children }) }));
18
+ }
19
+ /** One tab: what the caller puts in it (an icon and a label, or a rename field), then its × button. */
20
+ export function TabStripTab({ onClose, closeLabel, className, children, onKeyDown, onAuxClick, ...props }) {
21
+ return (_jsxs(BaseTabs.Tab, { "data-slot": "tab-strip-tab", nativeButton: false,
22
+ // A div: the tab holds its own × target.
23
+ render: _jsx("div", {}), onKeyDown: (event) => {
24
+ if (onClose !== undefined &&
25
+ event.target === event.currentTarget &&
26
+ (event.key === "Delete" || event.key === "Backspace")) {
27
+ event.preventDefault();
28
+ onClose();
29
+ return;
30
+ }
31
+ onKeyDown?.(event);
32
+ }, ...(onClose === undefined ? {} : { "aria-keyshortcuts": "Delete" }), onAuxClick: (event) => {
33
+ if (onClose !== undefined && event.button === 1) {
34
+ event.preventDefault();
35
+ onClose();
36
+ }
37
+ onAuxClick?.(event);
38
+ }, className: cn("group/tab relative flex h-7 shrink-0 cursor-default select-none items-center gap-1.5 rounded-md ps-2 text-sm outline-none", "text-fg-subtle transition-colors duration-(--duration-fast) hover:bg-hover hover:text-fg focus-visible:ring-focus", "data-active:bg-selected data-active:text-fg [&>svg]:size-3.5 [&>svg]:shrink-0", onClose === undefined ? "pe-2" : "pe-1", className), ...props, children: [children, onClose !== undefined && (_jsx("span", { "aria-hidden": true, "data-slot": "tab-strip-close", title: closeLabel, onClick: (event) => {
39
+ event.stopPropagation();
40
+ onClose();
41
+ }, className: cn("grid size-5 shrink-0 place-items-center rounded-sm text-fg-subtle hover:bg-hover hover:text-fg", "opacity-0 transition-opacity duration-(--duration-fast) group-hover/tab:opacity-100 group-data-active/tab:opacity-100"), children: _jsx(X, { className: "size-3" }) }))] }));
42
+ }
@@ -0,0 +1,13 @@
1
+ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
2
+ import { type ComponentProps } from "react";
3
+ export type TabsVariant = "underline" | "pill";
4
+ export interface TabsProps extends ComponentProps<typeof BaseTabs.Root> {
5
+ variant?: TabsVariant;
6
+ }
7
+ /** Root. `variant` styles every List/Tab inside it. */
8
+ export declare function Tabs({ variant, className, ...props }: TabsProps): import("react").JSX.Element;
9
+ /** The strip of tabs. Renders the sliding indicator automatically. */
10
+ export declare function TabsList({ className, children, ...props }: ComponentProps<typeof BaseTabs.List>): import("react").JSX.Element;
11
+ export declare function TabsTab({ className, ...props }: ComponentProps<typeof BaseTabs.Tab>): import("react").JSX.Element;
12
+ /** Content for one tab; fades and rises in when it becomes active. */
13
+ export declare function TabsPanel({ className, ...props }: ComponentProps<typeof BaseTabs.Panel>): import("react").JSX.Element;