@toolr/ui-design 0.1.7 → 0.1.8

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 (36) hide show
  1. package/components/hooks/use-modal-behavior.ts +32 -3
  2. package/components/sections/golden-snapshots/file-diff-viewer.tsx +1 -1
  3. package/components/sections/golden-snapshots/status-overview.tsx +1 -1
  4. package/components/ui/action-dialog.tsx +14 -6
  5. package/components/ui/ai-action-button.tsx +2 -4
  6. package/components/ui/badge.tsx +12 -4
  7. package/components/ui/breadcrumb.tsx +5 -5
  8. package/components/ui/checkbox.tsx +17 -11
  9. package/components/ui/collapsible-section.tsx +1 -0
  10. package/components/ui/confirm-badge.tsx +12 -4
  11. package/components/ui/cookie-consent.tsx +1 -1
  12. package/components/ui/extension-list-card.tsx +1 -1
  13. package/components/ui/file-tree.tsx +4 -4
  14. package/components/ui/filter-dropdown.tsx +5 -2
  15. package/components/ui/form-actions.tsx +7 -5
  16. package/components/ui/icon-button.tsx +5 -5
  17. package/components/ui/input.tsx +8 -3
  18. package/components/ui/label.tsx +4 -0
  19. package/components/ui/layout-tab-bar.tsx +5 -5
  20. package/components/ui/modal.tsx +9 -5
  21. package/components/ui/nav-card.tsx +1 -1
  22. package/components/ui/navigation-bar.tsx +4 -4
  23. package/components/ui/number-input.tsx +6 -0
  24. package/components/ui/segmented-toggle.tsx +2 -0
  25. package/components/ui/select.tsx +6 -3
  26. package/components/ui/selection-grid.tsx +4 -0
  27. package/components/ui/setting-row.tsx +4 -2
  28. package/components/ui/settings-card.tsx +2 -2
  29. package/components/ui/settings-info-box.tsx +1 -2
  30. package/components/ui/sort-dropdown.tsx +8 -5
  31. package/components/ui/tab-bar.tsx +14 -4
  32. package/components/ui/toggle.tsx +19 -11
  33. package/components/ui/tooltip.tsx +5 -5
  34. package/dist/index.d.ts +13 -7
  35. package/dist/index.js +258 -156
  36. package/package.json +9 -1
package/dist/index.js CHANGED
@@ -461,15 +461,14 @@ function Tooltip({
461
461
  if (!triggerRef.current) return;
462
462
  const triggerRect = triggerRef.current.getBoundingClientRect();
463
463
  const tooltipEl = tooltipRef.current;
464
+ const tooltipRect = tooltipEl?.getBoundingClientRect();
464
465
  let resolvedPosition = position === "auto" ? "top" : position;
465
- if (position === "auto" && tooltipEl) {
466
- const tooltipRect = tooltipEl.getBoundingClientRect();
466
+ if (position === "auto" && tooltipRect) {
467
467
  resolvedPosition = resolveAutoPosition(triggerRect, tooltipRect);
468
468
  setActualPosition(resolvedPosition);
469
469
  }
470
470
  let newCoords = calculateBasePosition(triggerRect, resolvedPosition, align);
471
- if (tooltipEl) {
472
- const tooltipRect = tooltipEl.getBoundingClientRect();
471
+ if (tooltipRect) {
473
472
  newCoords = adjustForTooltipSize(newCoords, tooltipRect, resolvedPosition, align);
474
473
  newCoords = clampToViewport(newCoords, tooltipRect);
475
474
  }
@@ -491,7 +490,8 @@ function Tooltip({
491
490
  "div",
492
491
  {
493
492
  ref: tooltipRef,
494
- className: `fixed px-3 py-1.5 bg-[var(--popover)] border border-neutral-600 rounded-lg shadow-xl z-[9999] ${interactive || trigger === "click" ? "" : "pointer-events-none"} ${multiline ? "whitespace-pre-line" : "whitespace-nowrap"}`,
493
+ role: "tooltip",
494
+ className: `fixed px-3 py-1.5 bg-[var(--popover)] border border-neutral-600 rounded-lg shadow-lg z-[9999] ${interactive || trigger === "click" ? "" : "pointer-events-none"} ${multiline ? "whitespace-pre-line" : "whitespace-nowrap"}`,
495
495
  style: {
496
496
  top: coords.top,
497
497
  left: coords.left,
@@ -708,9 +708,9 @@ var statusIcons = {
708
708
  };
709
709
  var statusConfig = {
710
710
  loading: { color: void 0, active: true, animation: "animate-spin" },
711
- success: { color: "green", active: true, animation: "animate-pulse" },
712
- warning: { color: "amber", active: true, animation: "animate-pulse" },
713
- error: { color: "red", active: true, animation: "animate-pulse" }
711
+ success: { color: "green", active: true, animation: "" },
712
+ warning: { color: "amber", active: true, animation: "" },
713
+ error: { color: "red", active: true, animation: "" }
714
714
  };
715
715
  function resolveIcon(icon, status) {
716
716
  if (status) return statusIcons[status];
@@ -772,7 +772,7 @@ function IconButton({
772
772
  href,
773
773
  target: "_blank",
774
774
  rel: "noopener noreferrer",
775
- "aria-label": tooltip?.title,
775
+ "aria-label": tooltip?.title || (typeof tooltip?.description === "string" ? tooltip.description : void 0),
776
776
  "data-testid": testId,
777
777
  className: `${sharedClassName} cursor-pointer no-underline`,
778
778
  children: inner
@@ -783,7 +783,7 @@ function IconButton({
783
783
  type: "button",
784
784
  onClick,
785
785
  disabled,
786
- "aria-label": tooltip?.title,
786
+ "aria-label": tooltip?.title || (typeof tooltip?.description === "string" ? tooltip.description : void 0),
787
787
  "data-testid": testId,
788
788
  className: `${sharedClassName} cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed`,
789
789
  children: inner
@@ -892,6 +892,10 @@ function Label({
892
892
  hasProgress && /* @__PURE__ */ jsx5(
893
893
  "span",
894
894
  {
895
+ role: "progressbar",
896
+ "aria-valuenow": Math.min(progress, 100),
897
+ "aria-valuemin": 0,
898
+ "aria-valuemax": 100,
895
899
  className: `absolute inset-y-0 left-0 ${progressFillColors[color]} rounded-[inherit]`,
896
900
  style: { width: `${Math.min(progress, 100)}%` }
897
901
  }
@@ -936,6 +940,16 @@ function Label({
936
940
  );
937
941
  }
938
942
 
943
+ // components/ui/badge.tsx
944
+ import { memo } from "react";
945
+
946
+ // components/lib/cn.ts
947
+ import { clsx } from "clsx";
948
+ import { twMerge } from "tailwind-merge";
949
+ function cn(...inputs) {
950
+ return twMerge(clsx(inputs));
951
+ }
952
+
939
953
  // components/ui/badge.tsx
940
954
  import { jsx as jsx6 } from "react/jsx-runtime";
941
955
  var sizeClasses2 = {
@@ -945,11 +959,11 @@ var sizeClasses2 = {
945
959
  md: "min-w-[20px] h-[20px] px-1.5 text-xs",
946
960
  lg: "min-w-[22px] h-[22px] px-1.5 text-sm"
947
961
  };
948
- function Badge({
962
+ var Badge = memo(function Badge2({
949
963
  value,
950
964
  color = "neutral",
951
965
  size = "sm",
952
- className = "",
966
+ className,
953
967
  testId
954
968
  }) {
955
969
  const display = typeof value === "number" && value > 99 ? "99+" : value;
@@ -957,13 +971,20 @@ function Badge({
957
971
  "span",
958
972
  {
959
973
  "data-testid": testId,
960
- className: `inline-flex items-center justify-center border rounded-full font-medium leading-none tabular-nums ${FORM_COLORS[color].border} ${FORM_COLORS[color].accent} ${sizeClasses2[size]} ${className}`,
974
+ className: cn(
975
+ "inline-flex items-center justify-center border rounded-full font-medium leading-none tabular-nums",
976
+ FORM_COLORS[color].border,
977
+ FORM_COLORS[color].accent,
978
+ sizeClasses2[size],
979
+ className
980
+ ),
961
981
  children: display
962
982
  }
963
983
  );
964
- }
984
+ });
965
985
 
966
986
  // components/ui/confirm-badge.tsx
987
+ import { memo as memo2 } from "react";
967
988
  import { Check as Check2 } from "lucide-react";
968
989
  import { jsx as jsx7 } from "react/jsx-runtime";
969
990
  var sizeClasses3 = {
@@ -980,21 +1001,27 @@ var iconSizeClasses2 = {
980
1001
  md: "w-3.5 h-3.5",
981
1002
  lg: "w-4 h-4"
982
1003
  };
983
- function ConfirmBadge({
1004
+ var ConfirmBadge = memo2(function ConfirmBadge2({
984
1005
  color = "neutral",
985
1006
  size = "sm",
986
- className = "",
1007
+ className,
987
1008
  testId
988
1009
  }) {
989
1010
  return /* @__PURE__ */ jsx7(
990
1011
  "span",
991
1012
  {
992
1013
  "data-testid": testId,
993
- className: `inline-flex items-center justify-center border ${FORM_COLORS[color].border} ${FORM_COLORS[color].accent} ${sizeClasses3[size]} ${className}`,
1014
+ className: cn(
1015
+ "inline-flex items-center justify-center border",
1016
+ FORM_COLORS[color].border,
1017
+ FORM_COLORS[color].accent,
1018
+ sizeClasses3[size],
1019
+ className
1020
+ ),
994
1021
  children: /* @__PURE__ */ jsx7(Check2, { className: iconSizeClasses2[size] })
995
1022
  }
996
1023
  );
997
- }
1024
+ });
998
1025
 
999
1026
  // components/ui/checkbox.tsx
1000
1027
  import { Check as Check3 } from "lucide-react";
@@ -1030,26 +1057,29 @@ function Checkbox({
1030
1057
  size = "sm",
1031
1058
  color = "blue",
1032
1059
  variant = "outline",
1033
- className = "",
1060
+ className,
1061
+ "aria-label": ariaLabel,
1034
1062
  testId
1035
1063
  }) {
1036
1064
  const s = CHECKBOX_SIZES[size];
1037
1065
  const c = CHECKBOX_COLORS[color];
1038
- const uncheckedStyle = variant === "outline" ? `${c.border} ${c.hover}` : `bg-neutral-700 ${c.border} ${c.hover}`;
1039
1066
  return /* @__PURE__ */ jsx8(
1040
1067
  "button",
1041
1068
  {
1042
1069
  type: "button",
1070
+ role: "checkbox",
1071
+ "aria-checked": checked,
1072
+ "aria-label": ariaLabel,
1043
1073
  onClick: () => !disabled && onChange(!checked),
1044
1074
  disabled,
1045
1075
  "data-testid": testId,
1046
- className: `
1047
- ${s.box} rounded border flex items-center justify-center transition-colors flex-shrink-0
1048
- cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed
1049
- ${checked ? `${c.bg} ${c.border}` : uncheckedStyle}
1050
- ${className}
1051
- `,
1052
- children: checked && /* @__PURE__ */ jsx8(Check3, { className: `${s.icon} ${c.icon}` })
1076
+ className: cn(
1077
+ "rounded border flex items-center justify-center transition-colors flex-shrink-0 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed",
1078
+ s.box,
1079
+ checked ? cn(c.bg, c.border) : cn(variant === "filled" && "bg-neutral-700", c.border, c.hover),
1080
+ className
1081
+ ),
1082
+ children: checked && /* @__PURE__ */ jsx8(Check3, { className: cn(s.icon, c.icon) })
1053
1083
  }
1054
1084
  );
1055
1085
  }
@@ -1136,8 +1166,9 @@ function Toggle({
1136
1166
  onChange,
1137
1167
  disabled = false,
1138
1168
  size = "sm",
1139
- className = "",
1169
+ className,
1140
1170
  color = "blue",
1171
+ "aria-label": ariaLabel,
1141
1172
  testId
1142
1173
  }) {
1143
1174
  const s = TOGGLE_SIZES[size];
@@ -1147,24 +1178,28 @@ function Toggle({
1147
1178
  "button",
1148
1179
  {
1149
1180
  type: "button",
1181
+ role: "switch",
1182
+ "aria-checked": checked,
1183
+ "aria-label": ariaLabel,
1150
1184
  onClick: () => !disabled && onChange(!checked),
1151
1185
  disabled,
1152
1186
  "data-testid": testId,
1153
1187
  style: { boxShadow: `inset 0 0 0 1px ${checked ? bc.active : bc.idle}` },
1154
- className: `
1155
- relative ${s.track} rounded-full transition-all flex-shrink-0
1156
- cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed
1157
- ${checked ? TOGGLE_CHECKED_TRACK[color] : TOGGLE_UNCHECKED_TRACK[color]}
1158
- ${className}
1159
- `,
1188
+ className: cn(
1189
+ "relative rounded-full transition-all flex-shrink-0 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed",
1190
+ s.track,
1191
+ checked ? TOGGLE_CHECKED_TRACK[color] : TOGGLE_UNCHECKED_TRACK[color],
1192
+ className
1193
+ ),
1160
1194
  children: /* @__PURE__ */ jsx9(
1161
1195
  "span",
1162
1196
  {
1163
1197
  style: { backgroundColor: checked ? kc.on : kc.off },
1164
- className: `
1165
- block absolute top-0.5 left-0.5 ${s.knob} rounded-full transition-transform
1166
- ${checked ? s.translate : "translate-x-0"}
1167
- `
1198
+ className: cn(
1199
+ "block absolute top-0.5 left-0.5 rounded-full transition-transform",
1200
+ s.knob,
1201
+ checked ? s.translate : "translate-x-0"
1202
+ )
1168
1203
  }
1169
1204
  )
1170
1205
  }
@@ -1172,7 +1207,7 @@ function Toggle({
1172
1207
  }
1173
1208
 
1174
1209
  // components/ui/input.tsx
1175
- import { forwardRef, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
1210
+ import { forwardRef, useEffect as useEffect3, useId, useRef as useRef2, useState as useState2 } from "react";
1176
1211
  import { Search as Search2, X as X2, Eye as Eye2, EyeOff as EyeOff2 } from "lucide-react";
1177
1212
 
1178
1213
  // components/ui/debounce-border-overlay.tsx
@@ -1254,6 +1289,7 @@ var Input = forwardRef(function Input2({
1254
1289
  const isSearch = type === "search";
1255
1290
  const isPassword = type === "password";
1256
1291
  const [isPasswordVisible, setIsPasswordVisible] = useState2(false);
1292
+ const errorId = useId();
1257
1293
  const [internalValue, setInternalValue] = useState2(value);
1258
1294
  const [debounceKey, setDebounceKey] = useState2(0);
1259
1295
  const timerRef = useRef2(void 0);
@@ -1311,6 +1347,9 @@ var Input = forwardRef(function Input2({
1311
1347
  },
1312
1348
  disabled,
1313
1349
  "data-testid": testId,
1350
+ "aria-invalid": hasError || void 0,
1351
+ "aria-describedby": typeof error === "string" && error ? errorId : void 0,
1352
+ ...isSearch && !props["aria-label"] ? { "aria-label": props.placeholder || "Search" } : {},
1314
1353
  ...searchAutoProps,
1315
1354
  className: `
1316
1355
  w-full border rounded-lg text-neutral-200 placeholder-neutral-500
@@ -1329,6 +1368,7 @@ var Input = forwardRef(function Input2({
1329
1368
  "button",
1330
1369
  {
1331
1370
  type: "button",
1371
+ "aria-label": "Clear search",
1332
1372
  onClick: handleClear,
1333
1373
  className: "absolute right-2 top-1/2 -translate-y-1/2 w-[18px] h-[18px] flex items-center justify-center rounded-md text-neutral-400 hover:text-neutral-300 hover:bg-neutral-500/20 transition-colors z-10 cursor-pointer",
1334
1374
  children: /* @__PURE__ */ jsx11(X2, { className: "w-2.5 h-2.5" })
@@ -1340,14 +1380,14 @@ var Input = forwardRef(function Input2({
1340
1380
  {
1341
1381
  type: "button",
1342
1382
  onClick: () => setIsPasswordVisible(!isPasswordVisible),
1343
- title: isPasswordVisible ? "Hide" : "Reveal",
1383
+ "aria-label": isPasswordVisible ? "Hide password" : "Show password",
1344
1384
  className: "absolute right-2 top-1/2 -translate-y-1/2 w-[18px] h-[18px] flex items-center justify-center rounded-md text-neutral-400 hover:text-neutral-300 hover:bg-neutral-500/20 transition-colors z-10 cursor-pointer",
1345
1385
  children: isPasswordVisible ? /* @__PURE__ */ jsx11(EyeOff2, { className: "w-2.5 h-2.5" }) : /* @__PURE__ */ jsx11(Eye2, { className: "w-2.5 h-2.5" })
1346
1386
  }
1347
1387
  ),
1348
1388
  debounceMs > 0 && debounceKey > 0 && /* @__PURE__ */ jsx11(DebounceBorderOverlay, { debounceKey, durationMs: debounceMs })
1349
1389
  ] }),
1350
- typeof error === "string" && error && /* @__PURE__ */ jsx11("p", { className: "text-sm text-red-400 mt-1 text-right", children: error })
1390
+ typeof error === "string" && error && /* @__PURE__ */ jsx11("p", { id: errorId, className: "text-sm text-red-400 mt-1 text-right", role: "alert", children: error })
1351
1391
  ]
1352
1392
  }
1353
1393
  );
@@ -1378,7 +1418,8 @@ function NumberInput({
1378
1418
  color = "blue",
1379
1419
  size = "sm",
1380
1420
  disabled = false,
1381
- className = ""
1421
+ className = "",
1422
+ "aria-label": ariaLabel
1382
1423
  }) {
1383
1424
  const [focused, setFocused] = useState3(false);
1384
1425
  const [editText, setEditText] = useState3(null);
@@ -1429,6 +1470,7 @@ function NumberInput({
1429
1470
  ref: inputRef,
1430
1471
  type: "text",
1431
1472
  inputMode: "numeric",
1473
+ "aria-label": ariaLabel,
1432
1474
  value: editText ?? value,
1433
1475
  onChange: (e) => setEditText(e.target.value),
1434
1476
  onFocus: () => {
@@ -1470,6 +1512,7 @@ function NumberInput({
1470
1512
  "button",
1471
1513
  {
1472
1514
  type: "button",
1515
+ "aria-label": "Increase value",
1473
1516
  tabIndex: -1,
1474
1517
  onMouseDown: (e) => e.preventDefault(),
1475
1518
  onClick: () => nudge(1),
@@ -1489,6 +1532,7 @@ function NumberInput({
1489
1532
  "button",
1490
1533
  {
1491
1534
  type: "button",
1535
+ "aria-label": "Decrease value",
1492
1536
  tabIndex: -1,
1493
1537
  onMouseDown: (e) => e.preventDefault(),
1494
1538
  onClick: () => nudge(-1),
@@ -1665,6 +1709,8 @@ function SegmentedToggle({
1665
1709
  return /* @__PURE__ */ jsx14(Tooltip, { content: option.tooltip, position: tooltipPosition, children: /* @__PURE__ */ jsxs8(
1666
1710
  "button",
1667
1711
  {
1712
+ "aria-pressed": isActive,
1713
+ "aria-label": option.label || (typeof option.tooltip.description === "string" ? option.tooltip.description : void 0),
1668
1714
  onClick: () => onChange(option.value),
1669
1715
  disabled,
1670
1716
  className: `flex items-center justify-center ${sizeClass} ${rounding} font-medium transition-all cursor-pointer ${isActive ? ACTIVE_COLORS[accentColor] || ACTIVE_COLORS.blue : `text-neutral-400 ${HOVER_COLORS[accentColor] || HOVER_COLORS.blue}`}`,
@@ -1980,12 +2026,14 @@ function Select({
1980
2026
  {
1981
2027
  ref: buttonRef,
1982
2028
  type: "button",
2029
+ "aria-expanded": isOpen,
2030
+ "aria-haspopup": "listbox",
1983
2031
  onClick: () => !disabled && (isOpen ? close() : open()),
1984
2032
  disabled,
1985
2033
  className: `flex items-center gap-1.5 min-w-0 rounded-lg border ${v.bg} ${FORM_COLORS[color].border} text-neutral-200 focus:outline-none ${FORM_COLORS[color].focus} transition-colors ${disabled ? "opacity-50 cursor-not-allowed" : `cursor-pointer ${FORM_COLORS[color].hover}`} ${s}`,
1986
2034
  children: [
1987
2035
  selectedOption?.icon,
1988
- /* @__PURE__ */ jsx16("span", { className: `whitespace-nowrap ${selectedOption ? "" : "text-neutral-500"}`, children: selectedOption?.label ?? placeholder }),
2036
+ /* @__PURE__ */ jsx16("span", { className: `truncate ${selectedOption ? "" : "text-neutral-500"}`, children: selectedOption?.label ?? placeholder }),
1989
2037
  /* @__PURE__ */ jsx16(ChevronDown3, { className: `w-3 h-3 ml-auto text-neutral-500 transition-transform shrink-0 ${isOpen ? "rotate-180" : ""}` })
1990
2038
  ]
1991
2039
  }
@@ -1995,7 +2043,8 @@ function Select({
1995
2043
  "div",
1996
2044
  {
1997
2045
  ref: menuRef,
1998
- className: `fixed z-[9999] whitespace-nowrap ${v.menuBg} border ${FORM_COLORS[color].border} rounded-lg shadow-xl overflow-hidden`,
2046
+ role: "listbox",
2047
+ className: `fixed z-[9999] whitespace-nowrap ${v.menuBg} border ${FORM_COLORS[color].border} rounded-lg shadow-lg overflow-hidden`,
1999
2048
  style: {
2000
2049
  top: menuPos.top,
2001
2050
  left: align === "right" ? void 0 : menuPos.left,
@@ -2019,7 +2068,7 @@ function Select({
2019
2068
  children: [
2020
2069
  /* @__PURE__ */ jsx16(Check4, { className: `w-3 h-3 shrink-0 ${isSelected ? FORM_COLORS[color].accent : "invisible"}` }),
2021
2070
  opt.icon,
2022
- /* @__PURE__ */ jsx16("span", { children: opt.label })
2071
+ /* @__PURE__ */ jsx16("span", { className: "truncate", children: opt.label })
2023
2072
  ]
2024
2073
  },
2025
2074
  String(opt.value)
@@ -2104,12 +2153,14 @@ function FilterDropdown({
2104
2153
  /* @__PURE__ */ jsxs11(
2105
2154
  "button",
2106
2155
  {
2156
+ "aria-expanded": isOpen,
2157
+ "aria-haspopup": "listbox",
2107
2158
  onClick: () => setIsOpen(!isOpen),
2108
2159
  className: `flex items-center gap-1.5 h-7 px-2 rounded-md border ${v.bg} text-sm transition-colors cursor-pointer ${isActive ? `${clearable ? "rounded-r-none border-r-0" : ""} ${FORM_COLORS[color].border} text-neutral-200 ${FORM_COLORS[color].hover}` : isOpen ? `${FORM_COLORS[color].border} text-neutral-200` : `${FORM_COLORS[color].border} text-neutral-400 ${FORM_COLORS[color].hover} hover:text-neutral-200`}`,
2109
2160
  children: [
2110
2161
  /* @__PURE__ */ jsx17(Filter2, { className: `w-3 h-3 ${isActive ? FORM_COLORS[color].accent : ""}` }),
2111
2162
  labelExtra,
2112
- /* @__PURE__ */ jsx17("span", { className: "whitespace-nowrap", children: selectedLabel }),
2163
+ /* @__PURE__ */ jsx17("span", { className: "truncate", children: selectedLabel }),
2113
2164
  /* @__PURE__ */ jsx17(ChevronDown4, { className: `w-3 h-3 transition-transform ${isOpen ? "rotate-180" : ""}` })
2114
2165
  ]
2115
2166
  }
@@ -2117,12 +2168,13 @@ function FilterDropdown({
2117
2168
  isActive && clearable && /* @__PURE__ */ jsx17(
2118
2169
  "button",
2119
2170
  {
2171
+ "aria-label": "Clear filter",
2120
2172
  onClick: () => onChange("all"),
2121
2173
  className: `flex items-center justify-center h-7 px-1.5 rounded-r-md border border-l-0 ${FORM_COLORS[color].border} ${v.bg} text-neutral-400 ${FORM_COLORS[color].hover} hover:text-neutral-200 transition-colors cursor-pointer`,
2122
2174
  children: /* @__PURE__ */ jsx17(X3, { className: "w-3 h-3" })
2123
2175
  }
2124
2176
  ),
2125
- isOpen && /* @__PURE__ */ jsxs11("div", { ref: menuRef, className: `absolute right-0 top-full z-50 mt-1 min-w-[140px] whitespace-nowrap bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-xl overflow-hidden`, children: [
2177
+ isOpen && /* @__PURE__ */ jsxs11("div", { ref: menuRef, role: "listbox", className: `absolute right-0 top-full z-50 mt-1 min-w-[140px] whitespace-nowrap bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-lg overflow-hidden`, children: [
2126
2178
  showSearch && /* @__PURE__ */ jsx17("div", { className: `sticky top-0 p-1.5 bg-[var(--popover)] border-b ${FORM_COLORS[color].border} z-10`, children: /* @__PURE__ */ jsxs11("div", { className: "relative", children: [
2127
2179
  /* @__PURE__ */ jsx17(Search3, { className: "absolute left-2 top-1/2 -translate-y-1/2 w-3 h-3 text-neutral-500" }),
2128
2180
  /* @__PURE__ */ jsx17(
@@ -2222,18 +2274,21 @@ function SortDropdown({
2222
2274
  /* @__PURE__ */ jsxs12(
2223
2275
  "button",
2224
2276
  {
2277
+ "aria-expanded": isOpen,
2278
+ "aria-haspopup": "listbox",
2225
2279
  onClick: () => setIsOpen(!isOpen),
2226
2280
  className: `flex items-center gap-1.5 h-7 px-2 rounded-md border ${v.bg} text-sm transition-colors cursor-pointer ${FORM_COLORS[color].border} text-neutral-200 ${FORM_COLORS[color].hover}`,
2227
2281
  children: [
2228
2282
  /* @__PURE__ */ jsx18(
2229
- "span",
2283
+ "button",
2230
2284
  {
2231
- className: `${FORM_COLORS[color].accent} hover:brightness-125 transition-colors`,
2285
+ type: "button",
2286
+ "aria-label": ascending ? "Sort descending" : "Sort ascending",
2287
+ className: `${FORM_COLORS[color].accent} hover:brightness-125 transition-colors cursor-pointer`,
2232
2288
  onClick: (e) => {
2233
2289
  e.stopPropagation();
2234
2290
  onToggleDirection();
2235
2291
  },
2236
- role: "button",
2237
2292
  children: /* @__PURE__ */ jsx18(DirIcon, { className: "w-3 h-3" })
2238
2293
  }
2239
2294
  ),
@@ -2242,7 +2297,7 @@ function SortDropdown({
2242
2297
  ]
2243
2298
  }
2244
2299
  ),
2245
- isOpen && /* @__PURE__ */ jsx18("div", { ref: menuRef, className: `absolute right-0 top-full z-50 mt-1 min-w-[140px] bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-xl overflow-hidden`, children: fields.map((f, idx) => /* @__PURE__ */ jsxs12(
2300
+ isOpen && /* @__PURE__ */ jsx18("div", { ref: menuRef, role: "listbox", className: `absolute right-0 top-full z-50 mt-1 min-w-[140px] bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-lg overflow-hidden`, children: fields.map((f, idx) => /* @__PURE__ */ jsxs12(
2246
2301
  "button",
2247
2302
  {
2248
2303
  onClick: () => {
@@ -2294,9 +2349,13 @@ function FormActions({
2294
2349
  padding = "normal"
2295
2350
  }) {
2296
2351
  const showBorder = border ?? DEFAULT_BORDER[padding];
2297
- const paddingClass = showBorder ? `${PADDING_CLASSES[padding]} ${BORDER_CLASS}` : PADDING_CLASSES[padding];
2298
2352
  const hasLeft = onBack || statusText;
2299
- return /* @__PURE__ */ jsxs13("div", { className: `flex items-center ${hasLeft ? "justify-between" : "justify-end"} gap-2 ${paddingClass}`, children: [
2353
+ return /* @__PURE__ */ jsxs13("div", { className: cn(
2354
+ "flex items-center gap-2",
2355
+ hasLeft ? "justify-between" : "justify-end",
2356
+ PADDING_CLASSES[padding],
2357
+ showBorder && BORDER_CLASS
2358
+ ), children: [
2300
2359
  hasLeft && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
2301
2360
  onBack && /* @__PURE__ */ jsx19(
2302
2361
  IconButton,
@@ -2353,13 +2412,13 @@ function FormActions({
2353
2412
  }
2354
2413
 
2355
2414
  // components/ui/modal.tsx
2356
- import { useRef as useRef9, useState as useState8 } from "react";
2415
+ import { useId as useId2, useRef as useRef9, useState as useState8 } from "react";
2357
2416
  import { createPortal as createPortal3 } from "react-dom";
2358
2417
  import { Info as Info2, AlertTriangle as AlertTriangle2, AlertCircle as AlertCircle2, Check as Check7 } from "lucide-react";
2359
2418
 
2360
2419
  // components/hooks/use-modal-behavior.ts
2361
2420
  import { useEffect as useEffect7 } from "react";
2362
- function useModalBehavior(isOpen, onClose) {
2421
+ function useModalBehavior(isOpen, onClose, containerRef) {
2363
2422
  useEffect7(() => {
2364
2423
  if (!isOpen) return;
2365
2424
  const handler = (e) => {
@@ -2376,6 +2435,36 @@ function useModalBehavior(isOpen, onClose) {
2376
2435
  document.body.style.overflow = prev;
2377
2436
  };
2378
2437
  }, [isOpen]);
2438
+ useEffect7(() => {
2439
+ if (!isOpen || !containerRef?.current) return;
2440
+ const container = containerRef.current;
2441
+ const focusable = container.querySelectorAll(
2442
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2443
+ );
2444
+ if (focusable.length > 0) focusable[0].focus();
2445
+ const handler = (e) => {
2446
+ if (e.key !== "Tab") return;
2447
+ const nodes = container.querySelectorAll(
2448
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2449
+ );
2450
+ if (nodes.length === 0) return;
2451
+ const first = nodes[0];
2452
+ const last = nodes[nodes.length - 1];
2453
+ if (e.shiftKey) {
2454
+ if (document.activeElement === first) {
2455
+ e.preventDefault();
2456
+ last.focus();
2457
+ }
2458
+ } else {
2459
+ if (document.activeElement === last) {
2460
+ e.preventDefault();
2461
+ first.focus();
2462
+ }
2463
+ }
2464
+ };
2465
+ document.addEventListener("keydown", handler);
2466
+ return () => document.removeEventListener("keydown", handler);
2467
+ }, [isOpen, containerRef]);
2379
2468
  }
2380
2469
 
2381
2470
  // components/ui/modal.tsx
@@ -2395,21 +2484,25 @@ var KIND_ICON = {
2395
2484
  };
2396
2485
  function Modal({ isOpen, onClose, title, children, kind = "info", size = "md", hideCloseButton = false, headerActions, testId }) {
2397
2486
  const modalRef = useRef9(null);
2398
- useModalBehavior(isOpen, onClose);
2487
+ const titleId = useId2();
2488
+ useModalBehavior(isOpen, onClose, modalRef);
2399
2489
  if (!isOpen) return null;
2400
2490
  return createPortal3(
2401
2491
  /* @__PURE__ */ jsxs14("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2402
- /* @__PURE__ */ jsx20("div", { className: "absolute inset-0 bg-[var(--dialog-backdrop)] backdrop-blur-sm", onClick: onClose }),
2492
+ /* @__PURE__ */ jsx20("div", { className: "absolute inset-0 bg-[var(--dialog-backdrop)]", onClick: onClose, "aria-hidden": "true" }),
2403
2493
  /* @__PURE__ */ jsxs14(
2404
2494
  "div",
2405
2495
  {
2406
2496
  ref: modalRef,
2497
+ role: "dialog",
2498
+ "aria-modal": "true",
2499
+ "aria-labelledby": titleId,
2407
2500
  "data-testid": testId,
2408
- className: `relative bg-neutral-900 border border-neutral-700 rounded-xl shadow-2xl ${SIZE_CLASSES2[size]} w-full mx-4 overflow-hidden`,
2501
+ className: `relative bg-neutral-900 border border-neutral-700 rounded-xl shadow-lg ${SIZE_CLASSES2[size]} w-full mx-4 overflow-hidden`,
2409
2502
  children: [
2410
2503
  /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3 px-5 py-4 border-b border-neutral-800", children: [
2411
2504
  KIND_ICON[kind],
2412
- /* @__PURE__ */ jsx20("h3", { className: "text-lg font-semibold text-white flex-1", children: title }),
2505
+ /* @__PURE__ */ jsx20("h3", { id: titleId, className: "text-lg font-semibold text-white flex-1 min-w-0 truncate", children: title }),
2413
2506
  headerActions?.map((a, i) => /* @__PURE__ */ jsx20(IconButton, { ...a }, i)),
2414
2507
  !hideCloseButton && /* @__PURE__ */ jsx20(
2415
2508
  IconButton,
@@ -2504,15 +2597,9 @@ function AlertModal({
2504
2597
  }
2505
2598
 
2506
2599
  // components/ui/action-dialog.tsx
2600
+ import { useId as useId3, useRef as useRef10 } from "react";
2507
2601
  import { createPortal as createPortal4 } from "react-dom";
2508
2602
 
2509
- // components/lib/cn.ts
2510
- import { clsx } from "clsx";
2511
- import { twMerge } from "tailwind-merge";
2512
- function cn(...inputs) {
2513
- return twMerge(clsx(inputs));
2514
- }
2515
-
2516
2603
  // components/ui/selection-grid.tsx
2517
2604
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2518
2605
  function makeToolLogo(toolKey) {
@@ -2623,6 +2710,8 @@ function GridCard({ item, selected, onClick }) {
2623
2710
  "button",
2624
2711
  {
2625
2712
  type: "button",
2713
+ "aria-pressed": selected,
2714
+ "aria-label": item.name,
2626
2715
  onClick,
2627
2716
  disabled: item.disabled,
2628
2717
  className: cn(
@@ -2651,6 +2740,8 @@ function ListCard({ item, selected, onClick }) {
2651
2740
  "button",
2652
2741
  {
2653
2742
  type: "button",
2743
+ "aria-pressed": selected,
2744
+ "aria-label": item.name,
2654
2745
  onClick,
2655
2746
  disabled: item.disabled,
2656
2747
  className: cn(
@@ -2762,7 +2853,9 @@ function ActionDialog({
2762
2853
  children,
2763
2854
  className
2764
2855
  }) {
2765
- useModalBehavior(true, () => onCancel?.());
2856
+ const dialogRef = useRef10(null);
2857
+ const titleId = useId3();
2858
+ useModalBehavior(true, () => onCancel?.(), dialogRef);
2766
2859
  const Icon = icon ? iconMap[icon] : null;
2767
2860
  const hasSelection = (items && items.length > 0 || presets && presets.length > 0) && selectedIds && onSelect;
2768
2861
  const hasScenarios = scenarios && scenarios.length > 0 && selectedScenarioIds && onSelectScenarios;
@@ -2778,12 +2871,16 @@ function ActionDialog({
2778
2871
  }
2779
2872
  return createPortal4(
2780
2873
  /* @__PURE__ */ jsxs17("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2781
- /* @__PURE__ */ jsx23("div", { className: "absolute inset-0 bg-[var(--dialog-backdrop)] backdrop-blur-sm", onClick: onCancel }),
2874
+ /* @__PURE__ */ jsx23("div", { className: "absolute inset-0 bg-[var(--dialog-backdrop)]", onClick: onCancel, "aria-hidden": "true" }),
2782
2875
  /* @__PURE__ */ jsxs17(
2783
2876
  "div",
2784
2877
  {
2878
+ ref: dialogRef,
2879
+ role: "dialog",
2880
+ "aria-modal": "true",
2881
+ "aria-labelledby": titleId,
2785
2882
  className: cn(
2786
- "relative bg-neutral-950 border border-neutral-700 rounded-xl shadow-2xl w-full max-w-[800px] mx-4 flex flex-col",
2883
+ "relative bg-neutral-950 border border-neutral-700 rounded-xl shadow-lg w-full max-w-[800px] mx-4 flex flex-col",
2787
2884
  "max-h-[80vh]",
2788
2885
  className
2789
2886
  ),
@@ -2796,9 +2893,9 @@ function ActionDialog({
2796
2893
  style: iconColor ? { color: iconColor } : void 0
2797
2894
  }
2798
2895
  ),
2799
- /* @__PURE__ */ jsxs17("div", { className: "flex flex-col", children: [
2800
- /* @__PURE__ */ jsx23("span", { className: "text-md font-semibold text-neutral-200", children: title }),
2801
- subtitle && /* @__PURE__ */ jsx23("span", { className: "text-sm text-neutral-500", children: subtitle })
2896
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-col min-w-0", children: [
2897
+ /* @__PURE__ */ jsx23("span", { id: titleId, className: "text-md font-semibold text-neutral-200 truncate", children: title }),
2898
+ subtitle && /* @__PURE__ */ jsx23("span", { className: "text-sm text-neutral-500 truncate", children: subtitle })
2802
2899
  ] }),
2803
2900
  /* @__PURE__ */ jsx23("div", { className: "flex-1" }),
2804
2901
  onSettings && /* @__PURE__ */ jsx23(
@@ -2927,7 +3024,7 @@ function collectDirPaths(nodes, rootName, prefix = "") {
2927
3024
  function FileTree({ nodes, rootName, selectedPath, onSelectFile, prefix = "", expandedPaths, onTogglePath, accentColor = "blue" }) {
2928
3025
  if (rootName) {
2929
3026
  const rootNode = { name: rootName, type: "directory", children: nodes };
2930
- return /* @__PURE__ */ jsx24("ul", { className: "space-y-0.5", children: /* @__PURE__ */ jsx24(
3027
+ return /* @__PURE__ */ jsx24("ul", { role: "tree", className: "space-y-0.5", children: /* @__PURE__ */ jsx24(
2931
3028
  FileTreeNodeItem,
2932
3029
  {
2933
3030
  node: rootNode,
@@ -2940,7 +3037,7 @@ function FileTree({ nodes, rootName, selectedPath, onSelectFile, prefix = "", ex
2940
3037
  }
2941
3038
  ) });
2942
3039
  }
2943
- return /* @__PURE__ */ jsx24("ul", { className: "space-y-0.5", children: nodes.filter(nodeHasFiles).map((node) => {
3040
+ return /* @__PURE__ */ jsx24("ul", { role: "tree", className: "space-y-0.5", children: nodes.filter(nodeHasFiles).map((node) => {
2944
3041
  const fullPath = prefix ? `${prefix}/${node.name}` : node.name;
2945
3042
  return /* @__PURE__ */ jsx24(
2946
3043
  FileTreeNodeItem,
@@ -2965,7 +3062,7 @@ function FileTreeNodeItem({ node, path, selectedPath, onSelectFile, expandedPath
2965
3062
  const selectedClass = ACCENT_SELECTED[accentColor] ?? ACCENT_SELECTED.blue;
2966
3063
  const iconColorClass = ACCENT_ICON[accentColor] ?? ACCENT_ICON.blue;
2967
3064
  const rowClass = isSelected ? `${base} ${selectedClass}` : isDir ? `${base} cursor-pointer text-white hover:text-neutral-200` : `${base} cursor-pointer text-white hover:bg-neutral-700/50 hover:text-neutral-200`;
2968
- return /* @__PURE__ */ jsxs18("li", { children: [
3065
+ return /* @__PURE__ */ jsxs18("li", { role: "treeitem", "aria-expanded": isDir ? expanded : void 0, "aria-selected": isSelected, children: [
2969
3066
  /* @__PURE__ */ jsxs18(
2970
3067
  "button",
2971
3068
  {
@@ -2978,7 +3075,7 @@ function FileTreeNodeItem({ node, path, selectedPath, onSelectFile, expandedPath
2978
3075
  ]
2979
3076
  }
2980
3077
  ),
2981
- isDir && expanded && node.children && /* @__PURE__ */ jsx24("ul", { className: "ml-4 space-y-0.5", children: node.children.filter(nodeHasFiles).map((child) => {
3078
+ isDir && expanded && node.children && /* @__PURE__ */ jsx24("ul", { role: "group", className: "ml-4 space-y-0.5", children: node.children.filter(nodeHasFiles).map((child) => {
2982
3079
  const childPath = `${path}/${child.name}`;
2983
3080
  return /* @__PURE__ */ jsx24(
2984
3081
  FileTreeNodeItem,
@@ -3090,7 +3187,7 @@ function EditorToolbar({
3090
3187
  }
3091
3188
 
3092
3189
  // components/ui/bottom-panel-header.tsx
3093
- import { useState as useState10, useRef as useRef10, useEffect as useEffect8, useCallback as useCallback5 } from "react";
3190
+ import { useState as useState10, useRef as useRef11, useEffect as useEffect8, useCallback as useCallback5 } from "react";
3094
3191
  import { RefreshCw as RefreshCw2 } from "lucide-react";
3095
3192
  import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
3096
3193
  var DEFAULT_ACTIVE_TEXT = "text-neutral-300";
@@ -3118,8 +3215,8 @@ function BottomPanelHeader({
3118
3215
  statusBanner,
3119
3216
  onCollapse
3120
3217
  }) {
3121
- const containerRef = useRef10(null);
3122
- const actionsRef = useRef10(null);
3218
+ const containerRef = useRef11(null);
3219
+ const actionsRef = useRef11(null);
3123
3220
  const [layoutMode, setLayoutMode] = useState10("full");
3124
3221
  const computeLayout = useCallback5(() => {
3125
3222
  const container = containerRef.current;
@@ -3264,7 +3361,7 @@ function FrontmatterFormHeader({
3264
3361
  }
3265
3362
 
3266
3363
  // components/ui/editor-placeholder-card.tsx
3267
- import { useState as useState11, useRef as useRef11, useLayoutEffect as useLayoutEffect3 } from "react";
3364
+ import { useState as useState11, useRef as useRef12, useLayoutEffect as useLayoutEffect3 } from "react";
3268
3365
  import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
3269
3366
  var COLORS = {
3270
3367
  purple: {
@@ -3301,7 +3398,7 @@ function EditorPlaceholderCard({
3301
3398
  const [isPlaceholderCopied, setIsPlaceholderCopied] = useState11(false);
3302
3399
  const [isValueCopied, setIsValueCopied] = useState11(false);
3303
3400
  const [isOverflowing, setIsOverflowing] = useState11(false);
3304
- const valueRef = useRef11(null);
3401
+ const valueRef = useRef12(null);
3305
3402
  const colors = COLORS[accentColor];
3306
3403
  const hasValue = !!value;
3307
3404
  useLayoutEffect3(() => {
@@ -3927,11 +4024,11 @@ function RegistryCard(props) {
3927
4024
  }
3928
4025
 
3929
4026
  // components/ui/registry-detail.tsx
3930
- import { useState as useState14, useRef as useRef13, useEffect as useEffect10, useCallback as useCallback7 } from "react";
4027
+ import { useState as useState14, useRef as useRef14, useEffect as useEffect10, useCallback as useCallback7 } from "react";
3931
4028
  import { ChevronsUpDown as ChevronsUpDown2, ChevronsDownUp as ChevronsDownUp2 } from "lucide-react";
3932
4029
 
3933
4030
  // components/ui/file-structure-section.tsx
3934
- import { useState as useState13, useEffect as useEffect9, useCallback as useCallback6, useRef as useRef12, useMemo } from "react";
4031
+ import { useState as useState13, useEffect as useEffect9, useCallback as useCallback6, useRef as useRef13, useMemo } from "react";
3935
4032
  import { FileCode as FileCode3, FolderTree, Loader2 as Loader23, AlertCircle as AlertCircle3, AlignLeft, Code2, Type } from "lucide-react";
3936
4033
  import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
3937
4034
  var ACCENT_BORDER = {
@@ -4102,10 +4199,10 @@ function FileStructureSection({
4102
4199
  }, []);
4103
4200
  const allCollapsed = expandedPaths.size === 0;
4104
4201
  const [treeHeight, setTreeHeight] = useState13(null);
4105
- const sectionRef = useRef12(null);
4106
- const resizing = useRef12(false);
4107
- const startY = useRef12(0);
4108
- const startHeight = useRef12(0);
4202
+ const sectionRef = useRef13(null);
4203
+ const resizing = useRef13(false);
4204
+ const startY = useRef13(0);
4205
+ const startHeight = useRef13(0);
4109
4206
  useEffect9(() => {
4110
4207
  if (variant === "list" || treeHeight !== null || !sectionRef.current) return;
4111
4208
  const el = sectionRef.current;
@@ -4299,7 +4396,7 @@ import { jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
4299
4396
  var MARKDOWN_CLASSES = "text-md text-neutral-400 leading-relaxed [&_strong]:text-neutral-200 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:bg-neutral-700/40 [&_code]:border [&_code]:border-neutral-500/40 [&_code]:text-neutral-200 [&_code]:font-mono [&_code]:text-sm [&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-neutral-200 [&_h1]:mb-2 [&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-neutral-200 [&_h2]:mb-2 [&_h3]:text-md [&_h3]:font-medium [&_h3]:text-neutral-200 [&_h3]:mb-1 [&_ul]:list-disc [&_ul]:pl-4 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-1 [&_p]:mb-2 [&_pre]:bg-neutral-900 [&_pre]:rounded [&_pre]:p-3 [&_pre]:overflow-x-auto [&_pre]:text-sm";
4300
4397
  var COLLAPSED_MAX_HEIGHT = 240;
4301
4398
  function CollapsibleTextSection({ children, header }) {
4302
- const contentRef = useRef13(null);
4399
+ const contentRef = useRef14(null);
4303
4400
  const [overflows, setOverflows] = useState14(false);
4304
4401
  const [expanded, setExpanded] = useState14(false);
4305
4402
  const measure = useCallback7(() => {
@@ -4416,7 +4513,7 @@ function RegistryDetail({
4416
4513
  }
4417
4514
 
4418
4515
  // components/ui/registry-browser.tsx
4419
- import { useState as useState15, useEffect as useEffect11, useRef as useRef14, useCallback as useCallback8 } from "react";
4516
+ import { useState as useState15, useEffect as useEffect11, useRef as useRef15, useCallback as useCallback8 } from "react";
4420
4517
  import { Search as Search4, ArrowRight as ArrowRight2, RefreshCw as RefreshCw3, Loader2 as Loader24, X as X4, AlertTriangle as AlertTriangle4 } from "lucide-react";
4421
4518
  import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
4422
4519
  var PAGE_SIZE = 60;
@@ -4448,10 +4545,10 @@ function RegistryBrowser({
4448
4545
  const totalCount = items.length;
4449
4546
  const needsPaging = totalCount > PAGE_SIZE;
4450
4547
  const [visibleCount, setVisibleCount] = useState15(PAGE_SIZE);
4451
- const scrollRef = useRef14(null);
4452
- const sentinelRef = useRef14(null);
4453
- const isFirstRender = useRef14(true);
4454
- const prevTotalCount = useRef14(totalCount);
4548
+ const scrollRef = useRef15(null);
4549
+ const sentinelRef = useRef15(null);
4550
+ const isFirstRender = useRef15(true);
4551
+ const prevTotalCount = useRef15(totalCount);
4455
4552
  useEffect11(() => {
4456
4553
  setVisibleCount(PAGE_SIZE);
4457
4554
  }, [totalCount]);
@@ -4471,7 +4568,7 @@ function RegistryBrowser({
4471
4568
  prevTotalCount.current = totalCount;
4472
4569
  }
4473
4570
  }, [totalCount, initialScrollTop]);
4474
- const scrollTimerRef = useRef14(void 0);
4571
+ const scrollTimerRef = useRef15(void 0);
4475
4572
  const handleScroll = useCallback8(() => {
4476
4573
  if (!onScrollChange) return;
4477
4574
  clearTimeout(scrollTimerRef.current);
@@ -4639,7 +4736,6 @@ function AiActionButton({
4639
4736
  return tooltip;
4640
4737
  }, [tooltip, forceDisabled, disabledReason, isRunning, isCompleted, runningTooltipTitle, completedTooltipTitle]);
4641
4738
  const isDisabled = forceDisabled;
4642
- const blinkClass = isCompleted ? "animate-pulse" : "";
4643
4739
  return /* @__PURE__ */ jsx34(
4644
4740
  IconButton,
4645
4741
  {
@@ -4650,8 +4746,8 @@ function AiActionButton({
4650
4746
  onClick: isDisabled ? () => {
4651
4747
  } : onClick,
4652
4748
  tooltip: resolvedTooltip,
4653
- active: isRunning,
4654
- className: `${className ?? ""} ${blinkClass}`.trim() || void 0,
4749
+ active: isRunning || isCompleted,
4750
+ className,
4655
4751
  testId
4656
4752
  }
4657
4753
  );
@@ -4745,7 +4841,7 @@ function AiExecutionActionButtons({
4745
4841
  import { Loader2 as Loader25, Send as Send2 } from "lucide-react";
4746
4842
 
4747
4843
  // components/sections/report-bug/screenshot-uploader.tsx
4748
- import { useCallback as useCallback9, useRef as useRef15, useState as useState16 } from "react";
4844
+ import { useCallback as useCallback9, useRef as useRef16, useState as useState16 } from "react";
4749
4845
  import { ImagePlus, X as X5, AlertCircle as AlertCircle4 } from "lucide-react";
4750
4846
  import { jsx as jsx36, jsxs as jsxs29 } from "react/jsx-runtime";
4751
4847
  var DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
@@ -4761,7 +4857,7 @@ function ScreenshotUploader({
4761
4857
  disabled = false,
4762
4858
  className
4763
4859
  }) {
4764
- const fileInputRef = useRef15(null);
4860
+ const fileInputRef = useRef16(null);
4765
4861
  const [isDragging, setIsDragging] = useState16(false);
4766
4862
  const [error, setError] = useState16(null);
4767
4863
  const totalSize = screenshots.reduce((sum, s) => sum + s.size, 0);
@@ -5901,7 +5997,7 @@ import { useState as useState22, useEffect as useEffect13 } from "react";
5901
5997
  import { RefreshCw as RefreshCw4, AlertCircle as AlertCircle5, Check as Check9, Activity, GitCompareArrows, Archive as Archive3, Tag as Tag3 } from "lucide-react";
5902
5998
 
5903
5999
  // components/sections/golden-snapshots/status-overview.tsx
5904
- import { useState as useState19, useRef as useRef16 } from "react";
6000
+ import { useState as useState19, useRef as useRef17 } from "react";
5905
6001
  import { Archive, Check as Check8, AlertTriangle as AlertTriangle5, RotateCcw as RotateCcw2 } from "lucide-react";
5906
6002
  import { jsx as jsx38, jsxs as jsxs31 } from "react/jsx-runtime";
5907
6003
  function getComponentVersion(meta, component) {
@@ -5952,7 +6048,7 @@ function StatusOverview({
5952
6048
  }) {
5953
6049
  const anyResetting = resettingComponent !== null || resettingAll;
5954
6050
  const [showResetMenu, setShowResetMenu] = useState19(false);
5955
- const resetMenuRef = useRef16(null);
6051
+ const resetMenuRef = useRef17(null);
5956
6052
  const resetMenuDropdownRef = useDropdownMaxHeight(showResetMenu);
5957
6053
  useClickOutside(resetMenuRef, showResetMenu, () => setShowResetMenu(false));
5958
6054
  const getLabel = (comp) => componentLabels?.[comp] ?? comp.charAt(0).toUpperCase() + comp.slice(1);
@@ -6023,7 +6119,7 @@ function StatusOverview({
6023
6119
  tooltip: { title: "Reset options", description: "Reset live files to golden" }
6024
6120
  }
6025
6121
  ),
6026
- showResetMenu && /* @__PURE__ */ jsxs31("div", { ref: resetMenuDropdownRef, className: "absolute right-0 top-full mt-1 w-56 bg-neutral-850 border border-neutral-700 rounded-lg shadow-xl z-50 py-1 overflow-hidden", children: [
6122
+ showResetMenu && /* @__PURE__ */ jsxs31("div", { ref: resetMenuDropdownRef, className: "absolute right-0 top-full mt-1 w-56 bg-neutral-850 border border-neutral-700 rounded-lg shadow-lg z-50 py-1 overflow-hidden", children: [
6027
6123
  /* @__PURE__ */ jsx38(
6028
6124
  "button",
6029
6125
  {
@@ -6409,7 +6505,7 @@ function FileDiffViewer({ sync, componentLabels, monacoTheme, renderFileIcon })
6409
6505
  devtools && hasUnsavedChanges && /* @__PURE__ */ jsx39(
6410
6506
  IconButton,
6411
6507
  {
6412
- icon: /* @__PURE__ */ jsx39(Save2, { className: saving ? "animate-pulse" : "" }),
6508
+ icon: /* @__PURE__ */ jsx39(Save2, { className: saving ? "animate-spin" : "" }),
6413
6509
  onClick: handleSaveLiveFile,
6414
6510
  disabled: saving,
6415
6511
  color: "amber",
@@ -6943,13 +7039,13 @@ function GoldenSyncPanel({
6943
7039
  import { Check as Check10, X as X6, RefreshCw as RefreshCw5, Terminal as Terminal3 } from "lucide-react";
6944
7040
 
6945
7041
  // components/sections/ai-tools-paths/use-tools-paths.ts
6946
- import { useState as useState23, useCallback as useCallback12, useRef as useRef17, useEffect as useEffect14 } from "react";
7042
+ import { useState as useState23, useCallback as useCallback12, useRef as useRef18, useEffect as useEffect14 } from "react";
6947
7043
  function useToolsPaths({ api, tools, onToolConfigChange }) {
6948
7044
  const [isDetecting, setIsDetecting] = useState23(false);
6949
7045
  const [hasScanned, setHasScanned] = useState23(false);
6950
7046
  const [refreshingTools, setRefreshingTools] = useState23(/* @__PURE__ */ new Set());
6951
7047
  const [scannedTools, setScannedTools] = useState23(/* @__PURE__ */ new Set());
6952
- const wasDetectingRef = useRef17(false);
7048
+ const wasDetectingRef = useRef18(false);
6953
7049
  useEffect14(() => {
6954
7050
  if (wasDetectingRef.current && !isDetecting) {
6955
7051
  setHasScanned(true);
@@ -7734,7 +7830,7 @@ function SnapshotTree({
7734
7830
  }
7735
7831
 
7736
7832
  // components/sections/snapshot-browser/use-snapshot-browser.ts
7737
- import { useState as useState25, useMemo as useMemo6, useEffect as useEffect16, useRef as useRef18, useCallback as useCallback14 } from "react";
7833
+ import { useState as useState25, useMemo as useMemo6, useEffect as useEffect16, useRef as useRef19, useCallback as useCallback14 } from "react";
7738
7834
  function collectExpandablePaths(scopes) {
7739
7835
  const paths = [];
7740
7836
  for (const scope of scopes) {
@@ -7765,7 +7861,7 @@ function useSnapshotBrowser({ scopes, api }) {
7765
7861
  const [searchQuery, setSearchQuery] = useState25("");
7766
7862
  const [expandedPaths, setExpandedPaths] = useState25(() => /* @__PURE__ */ new Set());
7767
7863
  const [deletingSnapshotId, setDeletingSnapshotId] = useState25(null);
7768
- const prevSearchRef = useRef18("");
7864
+ const prevSearchRef = useRef19("");
7769
7865
  const allExpandablePaths = useMemo6(() => collectExpandablePaths(scopes), [scopes]);
7770
7866
  const totalSnapshotCount = useMemo6(() => countSnapshots(scopes), [scopes]);
7771
7867
  const allExpanded = allExpandablePaths.length > 0 && allExpandablePaths.every((p) => expandedPaths.has(p));
@@ -7911,10 +8007,10 @@ function SnapshotBrowserPanel({
7911
8007
  }
7912
8008
 
7913
8009
  // components/hooks/use-resizable-sidebar.ts
7914
- import { useState as useState26, useCallback as useCallback15, useRef as useRef19 } from "react";
8010
+ import { useState as useState26, useCallback as useCallback15, useRef as useRef20 } from "react";
7915
8011
  function useResizableSidebar({ min, max, defaultWidth, direction = "right" }) {
7916
8012
  const [width, setWidth] = useState26(defaultWidth);
7917
- const widthRef = useRef19(defaultWidth);
8013
+ const widthRef = useRef20(defaultWidth);
7918
8014
  const onPointerDown = useCallback15((e) => {
7919
8015
  e.preventDefault();
7920
8016
  const el = e.currentTarget;
@@ -8402,7 +8498,7 @@ function SnippetForm({
8402
8498
  }
8403
8499
 
8404
8500
  // components/sections/prompt-editor/tabbed-prompt-editor.tsx
8405
- import { useState as useState28, useRef as useRef20, useCallback as useCallback17, useEffect as useEffect17, useMemo as useMemo8 } from "react";
8501
+ import { useState as useState28, useRef as useRef21, useCallback as useCallback17, useEffect as useEffect17, useMemo as useMemo8 } from "react";
8406
8502
  import Editor2 from "@monaco-editor/react";
8407
8503
  import { Variable, Info as Info4, Search as Search6, X as X10, AlertTriangle as AlertTriangle7 } from "lucide-react";
8408
8504
  import { jsx as jsx48, jsxs as jsxs41 } from "react/jsx-runtime";
@@ -8426,10 +8522,10 @@ function TabbedPromptEditor({
8426
8522
  const [variableSearch, setVariableSearch] = useState28("");
8427
8523
  const [localContent, setLocalContent] = useState28(prompts);
8428
8524
  const [isDirty, setIsDirty] = useState28(false);
8429
- const editorRef = useRef20(null);
8430
- const monacoRef = useRef20(null);
8431
- const decorationsRef = useRef20([]);
8432
- const completionProviderRef = useRef20(null);
8525
+ const editorRef = useRef21(null);
8526
+ const monacoRef = useRef21(null);
8527
+ const decorationsRef = useRef21([]);
8528
+ const completionProviderRef = useRef21(null);
8433
8529
  useEffect17(() => {
8434
8530
  setLocalContent(prompts);
8435
8531
  setIsDirty(false);
@@ -9004,6 +9100,7 @@ function CollapsibleSection({
9004
9100
  "button",
9005
9101
  {
9006
9102
  type: "button",
9103
+ "aria-expanded": open,
9007
9104
  onClick: () => setOpen(!open),
9008
9105
  className: "flex w-full items-center gap-2 py-2.5 px-1 text-left hover:bg-neutral-700/30 transition-colors cursor-pointer",
9009
9106
  children: [
@@ -9398,7 +9495,7 @@ function Breadcrumb({
9398
9495
  }) {
9399
9496
  const s = sizeConfig2[size];
9400
9497
  const isBox = variant === "box";
9401
- return /* @__PURE__ */ jsx54("nav", { className: cn("flex items-center", className), children: /* @__PURE__ */ jsx54("div", { className: cn(
9498
+ return /* @__PURE__ */ jsx54("nav", { className: cn("flex items-center min-w-0", className), children: /* @__PURE__ */ jsx54("div", { className: cn(
9402
9499
  "flex items-center gap-1",
9403
9500
  isBox && [s.px, s.py, "bg-neutral-800/50 border border-neutral-700/50 rounded-lg"]
9404
9501
  ), children: segments.map((segment, index) => {
@@ -9406,7 +9503,7 @@ function Breadcrumb({
9406
9503
  const isClickable = !isLast && !!segment.onClick;
9407
9504
  const colors = segment.color && ACCENT_NAV[segment.color] ? ACCENT_NAV[segment.color] : null;
9408
9505
  const isFirstPlain = !isBox && index === 0;
9409
- return /* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-1", children: [
9506
+ return /* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-1 min-w-0", children: [
9410
9507
  index > 0 && /* @__PURE__ */ jsx54(Separator, { type: separator, size }),
9411
9508
  isClickable ? /* @__PURE__ */ jsxs47(
9412
9509
  "button",
@@ -9414,7 +9511,7 @@ function Breadcrumb({
9414
9511
  type: "button",
9415
9512
  onClick: segment.onClick,
9416
9513
  className: cn(
9417
- "flex items-center gap-1.5 pr-2 py-0.5 rounded-md transition-colors cursor-pointer",
9514
+ "flex items-center gap-1.5 pr-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0",
9418
9515
  isFirstPlain ? "pl-0" : "pl-2",
9419
9516
  s.text,
9420
9517
  "font-medium hover:text-white",
@@ -9422,14 +9519,14 @@ function Breadcrumb({
9422
9519
  ),
9423
9520
  children: [
9424
9521
  segment.icon && /* @__PURE__ */ jsx54(SegmentIcon, { icon: segment.icon, color: segment.color, size }),
9425
- /* @__PURE__ */ jsx54("span", { children: segment.label })
9522
+ /* @__PURE__ */ jsx54("span", { className: "truncate max-w-[200px]", children: segment.label })
9426
9523
  ]
9427
9524
  }
9428
9525
  ) : /* @__PURE__ */ jsxs47(
9429
9526
  "div",
9430
9527
  {
9431
9528
  className: cn(
9432
- "flex items-center gap-1.5 pr-2 py-0.5 rounded-md",
9529
+ "flex items-center gap-1.5 pr-2 py-0.5 rounded-md min-w-0",
9433
9530
  isFirstPlain ? "pl-0" : "pl-2",
9434
9531
  s.text,
9435
9532
  isLast ? ["font-medium bg-neutral-700/50", colors ? colors.text : "text-white"] : ["font-medium", colors ? colors.text : "text-neutral-300"]
@@ -9445,7 +9542,7 @@ function Breadcrumb({
9445
9542
  }
9446
9543
 
9447
9544
  // components/ui/navigation-bar.tsx
9448
- import { useState as useState34, useRef as useRef21, useCallback as useCallback20 } from "react";
9545
+ import { useState as useState34, useRef as useRef22, useCallback as useCallback20 } from "react";
9449
9546
  import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight10, History as History2 } from "lucide-react";
9450
9547
  import { Fragment as Fragment12, jsx as jsx55, jsxs as jsxs48 } from "react/jsx-runtime";
9451
9548
  var sizeConfig3 = {
@@ -9505,7 +9602,7 @@ function NavigationBar({
9505
9602
  const hasNav = !!(onBack || onForward);
9506
9603
  const LeadIcon = leadingAction ? iconMap[leadingAction.icon] : null;
9507
9604
  const [historyOpen, setHistoryOpen] = useState34(false);
9508
- const historyRef = useRef21(null);
9605
+ const historyRef = useRef22(null);
9509
9606
  const closeHistory = useCallback20(() => setHistoryOpen(false), []);
9510
9607
  useClickOutside(historyRef, historyOpen, closeHistory);
9511
9608
  const hasHistoryEntries = historyEntries && historyEntries.length > 0;
@@ -9533,7 +9630,7 @@ function NavigationBar({
9533
9630
  active: historyOpen
9534
9631
  }
9535
9632
  ),
9536
- historyOpen && hasHistoryEntries && /* @__PURE__ */ jsxs48("div", { className: "absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-800 border border-neutral-700 rounded-lg shadow-xl z-50", children: [
9633
+ historyOpen && hasHistoryEntries && /* @__PURE__ */ jsxs48("div", { className: "absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-800 border border-neutral-700 rounded-lg shadow-lg z-50", children: [
9537
9634
  /* @__PURE__ */ jsx55("div", { className: "px-3 py-1.5 border-b border-neutral-700/50", children: /* @__PURE__ */ jsx55("p", { className: "text-sm font-medium text-neutral-500", children: "History" }) }),
9538
9635
  /* @__PURE__ */ jsx55("div", { className: "max-h-[300px] overflow-y-auto py-1", children: historyEntries.map((entry, i) => /* @__PURE__ */ jsx55(
9539
9636
  "button",
@@ -9564,7 +9661,7 @@ function NavigationBar({
9564
9661
  const isLast = index === segments.length - 1;
9565
9662
  const isClickable = !isLast && !!segment.onClick;
9566
9663
  const colors = segment.color && ACCENT_NAV[segment.color] ? ACCENT_NAV[segment.color] : null;
9567
- return /* @__PURE__ */ jsxs48("div", { className: "flex items-center gap-1", children: [
9664
+ return /* @__PURE__ */ jsxs48("div", { className: "flex items-center gap-1 min-w-0", children: [
9568
9665
  index > 0 && /* @__PURE__ */ jsx55(SegmentSeparator, { type: separator, size }),
9569
9666
  isClickable ? /* @__PURE__ */ jsxs48(
9570
9667
  "button",
@@ -9572,7 +9669,7 @@ function NavigationBar({
9572
9669
  type: "button",
9573
9670
  onClick: segment.onClick,
9574
9671
  className: cn(
9575
- "flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer",
9672
+ "flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0",
9576
9673
  s.text,
9577
9674
  "font-medium hover:text-white",
9578
9675
  colors ? [colors.text, `hover:${colors.bg}`] : ["text-neutral-300", "hover:bg-neutral-700/50"]
@@ -9586,7 +9683,7 @@ function NavigationBar({
9586
9683
  "div",
9587
9684
  {
9588
9685
  className: cn(
9589
- "flex items-center gap-1.5 px-2 py-0.5 rounded-md",
9686
+ "flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0",
9590
9687
  s.text,
9591
9688
  isLast ? ["font-medium bg-neutral-700/50", colors ? colors.text : "text-white"] : ["font-medium", colors ? colors.text : "text-neutral-300"]
9592
9689
  ),
@@ -9602,7 +9699,7 @@ function NavigationBar({
9602
9699
  }
9603
9700
 
9604
9701
  // components/ui/tab-bar.tsx
9605
- import { useRef as useRef22, useState as useState35, useEffect as useEffect18, useCallback as useCallback21 } from "react";
9702
+ import { useRef as useRef23, useState as useState35, useEffect as useEffect18, useCallback as useCallback21 } from "react";
9606
9703
  import { X as X11 } from "lucide-react";
9607
9704
  import { jsx as jsx56, jsxs as jsxs49 } from "react/jsx-runtime";
9608
9705
  var sizeConfig4 = {
@@ -9658,12 +9755,13 @@ function TabBadge({ badge, size, badgeColor }) {
9658
9755
  const s = sizeConfig4[size];
9659
9756
  return /* @__PURE__ */ jsx56(Badge, { value: badge, color: badgeColor, size: s.badgeSize, className: "flex-shrink-0" });
9660
9757
  }
9661
- function CloseButton({ size, onClick }) {
9758
+ function CloseButton({ size, onClick, tabLabel }) {
9662
9759
  const s = sizeConfig4[size];
9663
9760
  return /* @__PURE__ */ jsx56(
9664
9761
  "button",
9665
9762
  {
9666
9763
  type: "button",
9764
+ "aria-label": `Close ${tabLabel}`,
9667
9765
  onClick: (e) => {
9668
9766
  e.stopPropagation();
9669
9767
  onClick();
@@ -9691,6 +9789,8 @@ function CompactTab({
9691
9789
  "button",
9692
9790
  {
9693
9791
  type: "button",
9792
+ role: "tab",
9793
+ "aria-selected": isActive,
9694
9794
  onClick: onSelect,
9695
9795
  className: cn(
9696
9796
  "relative flex items-center justify-center transition-colors cursor-pointer",
@@ -9725,6 +9825,8 @@ function UnderlineTab({
9725
9825
  "button",
9726
9826
  {
9727
9827
  type: "button",
9828
+ role: "tab",
9829
+ "aria-selected": isActive,
9728
9830
  onClick: onSelect,
9729
9831
  className: cn(
9730
9832
  "group relative flex items-center whitespace-nowrap transition-colors cursor-pointer",
@@ -9739,7 +9841,7 @@ function UnderlineTab({
9739
9841
  tab.icon && /* @__PURE__ */ jsx56(TabIcon, { icon: tab.icon, size, color: isActive ? tab.color : void 0 }),
9740
9842
  /* @__PURE__ */ jsx56("span", { children: tab.label }),
9741
9843
  tab.badge !== void 0 && /* @__PURE__ */ jsx56(TabBadge, { badge: tab.badge, size, badgeColor: tab.badgeColor }),
9742
- showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose }),
9844
+ showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose, tabLabel: tab.label }),
9743
9845
  isActive && /* @__PURE__ */ jsx56("span", { className: cn("absolute bottom-0 left-0 right-0 h-0.5 rounded-full", c.indicator) })
9744
9846
  ]
9745
9847
  }
@@ -9759,6 +9861,8 @@ function PillTab({
9759
9861
  "button",
9760
9862
  {
9761
9863
  type: "button",
9864
+ role: "tab",
9865
+ "aria-selected": isActive,
9762
9866
  onClick: onSelect,
9763
9867
  className: cn(
9764
9868
  "group flex items-center whitespace-nowrap rounded-md transition-colors cursor-pointer",
@@ -9773,7 +9877,7 @@ function PillTab({
9773
9877
  tab.icon && /* @__PURE__ */ jsx56(TabIcon, { icon: tab.icon, size, color: isActive ? tab.color : void 0 }),
9774
9878
  /* @__PURE__ */ jsx56("span", { children: tab.label }),
9775
9879
  tab.badge !== void 0 && /* @__PURE__ */ jsx56(TabBadge, { badge: tab.badge, size, badgeColor: tab.badgeColor }),
9776
- showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose })
9880
+ showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose, tabLabel: tab.label })
9777
9881
  ]
9778
9882
  }
9779
9883
  );
@@ -9792,6 +9896,8 @@ function CardTab({
9792
9896
  "button",
9793
9897
  {
9794
9898
  type: "button",
9899
+ role: "tab",
9900
+ "aria-selected": isActive,
9795
9901
  onClick: onSelect,
9796
9902
  className: cn(
9797
9903
  "group relative flex items-center whitespace-nowrap transition-colors cursor-pointer rounded-t-lg border border-b-0",
@@ -9806,7 +9912,7 @@ function CardTab({
9806
9912
  tab.icon && /* @__PURE__ */ jsx56(TabIcon, { icon: tab.icon, size, color: isActive ? tab.color : void 0 }),
9807
9913
  /* @__PURE__ */ jsx56("span", { children: tab.label }),
9808
9914
  tab.badge !== void 0 && /* @__PURE__ */ jsx56(TabBadge, { badge: tab.badge, size, badgeColor: tab.badgeColor }),
9809
- showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose }),
9915
+ showClose && /* @__PURE__ */ jsx56(CloseButton, { size, onClick: onClose, tabLabel: tab.label }),
9810
9916
  isActive && /* @__PURE__ */ jsx56("span", { className: "absolute -bottom-px left-0 right-0 h-px bg-neutral-800" })
9811
9917
  ]
9812
9918
  }
@@ -9826,7 +9932,7 @@ function TabBar({
9826
9932
  size = "sm",
9827
9933
  className
9828
9934
  }) {
9829
- const containerRef = useRef22(null);
9935
+ const containerRef = useRef23(null);
9830
9936
  const [compact, setCompact] = useState35(false);
9831
9937
  const TabComponent = tabComponents[variant];
9832
9938
  const computeLayout = useCallback21(() => {
@@ -9848,6 +9954,7 @@ function TabBar({
9848
9954
  "div",
9849
9955
  {
9850
9956
  ref: containerRef,
9957
+ role: "tablist",
9851
9958
  className: cn(
9852
9959
  "flex items-end",
9853
9960
  variant === "underline" && "border-b border-neutral-700",
@@ -9884,7 +9991,7 @@ function TabBar({
9884
9991
  }
9885
9992
 
9886
9993
  // components/ui/layout-tab-bar.tsx
9887
- import { useState as useState36, useRef as useRef23, useCallback as useCallback22, useEffect as useEffect19 } from "react";
9994
+ import { useState as useState36, useRef as useRef24, useCallback as useCallback22, useEffect as useEffect19 } from "react";
9888
9995
  import { ChevronLeft as ChevronLeft3, ChevronRight as ChevronRight11, X as X12 } from "lucide-react";
9889
9996
  import { jsx as jsx57, jsxs as jsxs50 } from "react/jsx-runtime";
9890
9997
  var COLORS2 = {
@@ -9929,11 +10036,11 @@ function getGradient(color, isActive) {
9929
10036
  return isActive ? `linear-gradient(135deg, rgba(${rgb}, 0.54) 0%, rgba(${rgb}, 0.10) 100%)` : `linear-gradient(135deg, rgba(${rgb}, 0.36) 0%, rgba(${rgb}, 0.06) 100%)`;
9930
10037
  }
9931
10038
  function LayoutTabBar({ tabs, activeId, onSelect, onClose, onReorder, className }) {
9932
- const scrollRef = useRef23(null);
10039
+ const scrollRef = useRef24(null);
9933
10040
  const [showLeft, setShowLeft] = useState36(false);
9934
10041
  const [showRight, setShowRight] = useState36(false);
9935
10042
  const [drag, setDrag] = useState36(null);
9936
- const skipClickRef = useRef23(false);
10043
+ const skipClickRef = useRef24(false);
9937
10044
  const updateArrows = useCallback22(() => {
9938
10045
  const el = scrollRef.current;
9939
10046
  if (!el) return;
@@ -10056,20 +10163,15 @@ function LayoutTabBar({ tabs, activeId, onSelect, onClose, onReorder, className
10056
10163
  /* @__PURE__ */ jsx57("span", { className: cn("truncate text-sm", isActive ? subC : "text-neutral-500"), children: tab.subtitle })
10057
10164
  ] }),
10058
10165
  tab.closable && onClose && /* @__PURE__ */ jsx57(
10059
- "span",
10166
+ "button",
10060
10167
  {
10061
- role: "button",
10168
+ type: "button",
10169
+ "aria-label": `Close ${tab.title}`,
10062
10170
  tabIndex: -1,
10063
10171
  onClick: (e) => {
10064
10172
  e.stopPropagation();
10065
10173
  onClose(tab.id);
10066
10174
  },
10067
- onKeyDown: (e) => {
10068
- if (e.key === "Enter") {
10069
- e.stopPropagation();
10070
- onClose(tab.id);
10071
- }
10072
- },
10073
10175
  className: "absolute right-1.5 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 hover:bg-neutral-700 rounded p-0.5 transition-opacity cursor-pointer",
10074
10176
  children: /* @__PURE__ */ jsx57(X12, { className: "w-3 h-3" })
10075
10177
  }
@@ -10088,7 +10190,7 @@ function LayoutTabBar({ tabs, activeId, onSelect, onClose, onReorder, className
10088
10190
  const ghostIconC = getTextClass(t.iconColor || t.titleColor || "white");
10089
10191
  const ghostSubC = getTextClass(t.subtitleColor || t.selectedColor || t.color);
10090
10192
  const ghostSubIconC = getTextClass(t.subtitleIconColor || t.subtitleColor || t.selectedColor || t.color);
10091
- return /* @__PURE__ */ jsx57("div", { className: "fixed z-50 pointer-events-none opacity-90", style: { left: drag.x + 12, top: drag.y - 20 }, children: /* @__PURE__ */ jsxs50("div", { className: cn("flex flex-col gap-0.5 px-2.5 py-1.5 rounded-t-lg border-t-2 shadow-xl bg-neutral-800", ghostBorder), children: [
10193
+ return /* @__PURE__ */ jsx57("div", { className: "fixed z-50 pointer-events-none opacity-90", style: { left: drag.x + 12, top: drag.y - 20 }, children: /* @__PURE__ */ jsxs50("div", { className: cn("flex flex-col gap-0.5 px-2.5 py-1.5 rounded-t-lg border-t-2 shadow-lg bg-neutral-800", ghostBorder), children: [
10092
10194
  /* @__PURE__ */ jsxs50("div", { className: "flex items-center gap-1.5", children: [
10093
10195
  t.icon && /* @__PURE__ */ jsx57("span", { className: cn("shrink-0 inline-flex", ghostIconC), style: { width: 14, height: 14 }, children: t.icon }),
10094
10196
  /* @__PURE__ */ jsx57("span", { className: cn("text-md font-medium", ghostTitleC), children: t.title })
@@ -10139,7 +10241,7 @@ function NavCard({
10139
10241
  children: /* @__PURE__ */ jsx58(Icon, { className: "w-4.5 h-4.5", style: { color: iconColor } })
10140
10242
  }
10141
10243
  ),
10142
- /* @__PURE__ */ jsx58("h3", { className: "text-md font-medium text-neutral-200", children: title }),
10244
+ /* @__PURE__ */ jsx58("h3", { className: "text-md font-medium text-neutral-200 truncate", children: title }),
10143
10245
  description && /* @__PURE__ */ jsx58("p", { className: "mt-1 text-sm text-neutral-500 leading-relaxed line-clamp-2", children: description }),
10144
10246
  stats && /* @__PURE__ */ jsx58("p", { className: "mt-2 text-sm text-neutral-600", children: stats })
10145
10247
  ]
@@ -10148,9 +10250,9 @@ function NavCard({
10148
10250
  }
10149
10251
 
10150
10252
  // components/ui/extension-list-card.tsx
10151
- import { memo, useState as useState37, useRef as useRef24, useCallback as useCallback23, useEffect as useEffect20 } from "react";
10253
+ import { memo as memo3, useState as useState37, useRef as useRef25, useCallback as useCallback23, useEffect as useEffect20 } from "react";
10152
10254
  import { jsx as jsx59, jsxs as jsxs52 } from "react/jsx-runtime";
10153
- var ExtensionListCard = memo(function ExtensionListCard2({
10255
+ var ExtensionListCard = memo3(function ExtensionListCard2({
10154
10256
  icon: Icon,
10155
10257
  iconColor,
10156
10258
  borderColor,
@@ -10164,7 +10266,7 @@ var ExtensionListCard = memo(function ExtensionListCard2({
10164
10266
  testId
10165
10267
  }) {
10166
10268
  const [isHovered, setIsHovered] = useState37(false);
10167
- const hoverTimerRef = useRef24(void 0);
10269
+ const hoverTimerRef = useRef25(void 0);
10168
10270
  useEffect20(() => () => clearTimeout(hoverTimerRef.current), []);
10169
10271
  const handleMouseEnter = useCallback23(() => {
10170
10272
  hoverTimerRef.current = setTimeout(() => setIsHovered(true), 300);
@@ -10191,7 +10293,7 @@ var ExtensionListCard = memo(function ExtensionListCard2({
10191
10293
  /* @__PURE__ */ jsx59(Icon, { className: cn("w-5 h-5 shrink-0", iconColor) }),
10192
10294
  /* @__PURE__ */ jsxs52("div", { className: "min-w-0 flex-1", children: [
10193
10295
  /* @__PURE__ */ jsxs52("div", { className: "flex items-center gap-2 flex-wrap", children: [
10194
- /* @__PURE__ */ jsx59("span", { className: cn("text-md font-medium", titleClassName), children: title }),
10296
+ /* @__PURE__ */ jsx59("span", { className: cn("text-md font-medium truncate", titleClassName), children: title }),
10195
10297
  badges
10196
10298
  ] }),
10197
10299
  description && /* @__PURE__ */ jsx59("div", { className: cn("text-sm text-neutral-500 mt-1", !isHovered && "line-clamp-2"), children: description })
@@ -10386,7 +10488,7 @@ function CookieConsent({
10386
10488
  onConsent?.(choice);
10387
10489
  };
10388
10490
  if (!isVisible) return null;
10389
- return /* @__PURE__ */ jsx63("div", { className: "fixed bottom-0 left-0 right-0 z-50 p-4 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-700/50", children: /* @__PURE__ */ jsxs56("div", { className: "max-w-6xl mx-auto flex flex-col sm:flex-row items-start sm:items-center gap-4", children: [
10491
+ return /* @__PURE__ */ jsx63("div", { className: "fixed bottom-0 left-0 right-0 z-50 p-4 bg-neutral-900 border-t border-neutral-700/50", children: /* @__PURE__ */ jsxs56("div", { className: "max-w-6xl mx-auto flex flex-col sm:flex-row items-start sm:items-center gap-4", children: [
10390
10492
  /* @__PURE__ */ jsxs56("div", { className: "flex-grow", children: [
10391
10493
  /* @__PURE__ */ jsx63("p", { className: "text-md text-neutral-200 mb-1", children: heading }),
10392
10494
  /* @__PURE__ */ jsx63("p", { className: "text-sm text-neutral-400", children: description })
@@ -10500,8 +10602,8 @@ function useNavigationHistory(initial, maxEntries = 50) {
10500
10602
  // components/ui/setting-row.tsx
10501
10603
  import { jsx as jsx64, jsxs as jsxs57 } from "react/jsx-runtime";
10502
10604
  function SettingRow(props) {
10503
- const { label, description, disabled, className = "" } = props;
10504
- return /* @__PURE__ */ jsxs57("div", { className: `flex items-start justify-between gap-4 ${className}`, children: [
10605
+ const { label, description, disabled, className } = props;
10606
+ return /* @__PURE__ */ jsxs57("div", { className: cn("flex items-start justify-between gap-4", className), children: [
10505
10607
  /* @__PURE__ */ jsxs57("div", { children: [
10506
10608
  /* @__PURE__ */ jsx64("label", { className: "text-neutral-200 leading-7", children: label }),
10507
10609
  description && /* @__PURE__ */ jsx64("p", { className: "text-md text-neutral-500", children: description })
@@ -10513,7 +10615,8 @@ function SettingRow(props) {
10513
10615
  onChange: props.onChange,
10514
10616
  disabled,
10515
10617
  color: props.color,
10516
- size: props.size
10618
+ size: props.size,
10619
+ "aria-label": label
10517
10620
  }
10518
10621
  ),
10519
10622
  props.type === "select" && /* @__PURE__ */ jsx64(
@@ -10551,8 +10654,8 @@ function SettingsCard({ children, className, title, description, testId }) {
10551
10654
  "data-testid": testId,
10552
10655
  children: [
10553
10656
  title && /* @__PURE__ */ jsxs58("div", { children: [
10554
- /* @__PURE__ */ jsx65("h3", { className: "text-md font-medium text-neutral-200", children: title }),
10555
- description && /* @__PURE__ */ jsx65("p", { className: "text-md text-neutral-500 mt-1", children: description })
10657
+ /* @__PURE__ */ jsx65("h3", { className: "text-md font-medium text-neutral-200 truncate", children: title }),
10658
+ description && /* @__PURE__ */ jsx65("p", { className: "text-md text-neutral-500 mt-1 line-clamp-2", children: description })
10556
10659
  ] }),
10557
10660
  !title && description && /* @__PURE__ */ jsx65("p", { className: "text-md text-neutral-500", children: description }),
10558
10661
  children
@@ -10603,8 +10706,7 @@ function SettingsInfoBox({ children, color = "neutral", className, testId }) {
10603
10706
  return /* @__PURE__ */ jsxs59(
10604
10707
  "div",
10605
10708
  {
10606
- className: cn("flex items-start gap-3 border-l-2", borderColorMap[color], className),
10607
- style: { paddingLeft: 10 },
10709
+ className: cn("flex items-start gap-3 border-l-2 pl-2.5", borderColorMap[color], className),
10608
10710
  "data-testid": testId,
10609
10711
  children: [
10610
10712
  /* @__PURE__ */ jsx66(Icon, { className: cn("w-4 h-4 mt-0.5 shrink-0", ACCENT_TEXT[color]) }),