@ggui-ai/ui-visual-tester 0.25.0 → 0.27.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ggui-ai/ui-visual-tester
2
2
 
3
- Behavioral validator for [ggui](https://ggui.ai)-generated component code.
3
+ Behavioral validator for [GGUI](https://ggui.ai)-generated component code.
4
4
 
5
5
  Structural validators (schema checks, wire-preservation, compile/lint)
6
6
  confirm that a component is _shaped_ correctly. They cannot tell you
@@ -20085,7 +20085,7 @@
20085
20085
  case "muted":
20086
20086
  return "var(--ggui-color-onSunken, #52525b)";
20087
20087
  case "subtle":
20088
- return "var(--ggui-color-neutral-500, #71717a)";
20088
+ return "var(--ggui-color-onSunken, #52525b)";
20089
20089
  case "emphasized":
20090
20090
  return "var(--ggui-color-link, var(--ggui-color-primary-700, #0369a1))";
20091
20091
  case "loud":
@@ -20121,7 +20121,7 @@
20121
20121
  const inkAlias = "--ggui-surface-" + name + "-ink";
20122
20122
  const B = "var(" + bgAlias + ")";
20123
20123
  const I = "var(" + inkAlias + ")";
20124
- return `.${cls}{${bgAlias}:${bg};${inkAlias}:${ink};--ggui-color-onInverted:${ink}}.${cls}>*{--ggui-color-container:${B};--ggui-color-onContainer:${I};--ggui-color-elevated:${B};--ggui-color-onElevated:${I};--ggui-color-sunken:${B};--ggui-color-onSunken:${I};--ggui-color-neutral-500:${I};--ggui-color-ground:${B};--ggui-color-onGround:${I};--ggui-color-link:${accent}}@supports (color: color-mix(in srgb, red, blue)){.${cls}>*{--ggui-color-container:color-mix(in srgb, ${B} 94%, ${I});--ggui-color-elevated:color-mix(in srgb, ${B} 92%, ${I});--ggui-color-sunken:color-mix(in srgb, ${B} 88%, ${I});--ggui-color-onSunken:color-mix(in srgb, ${I} 76%, ${B});--ggui-color-neutral-500:color-mix(in srgb, ${I} 62%, ${B});--ggui-color-outline:${outlineOpen}color-mix(in srgb, ${I} 32%, ${B}));--ggui-color-outlineVariant:color-mix(in srgb, ${I} 18%, ${B})}}`;
20124
+ return `.${cls}{${bgAlias}:${bg};${inkAlias}:${ink};--ggui-color-onInverted:${ink}}.${cls}>*{--ggui-color-container:${B};--ggui-color-onContainer:${I};--ggui-color-elevated:${B};--ggui-color-onElevated:${I};--ggui-color-sunken:${B};--ggui-color-onSunken:${I};--ggui-color-neutral-500:${I};--ggui-color-ground:${B};--ggui-color-onGround:${I};--ggui-color-link:${accent};--ggui-color-controlAccentOutline:${accent}}@supports (color: color-mix(in srgb, red, blue)){.${cls}>*{--ggui-color-container:color-mix(in srgb, ${B} 94%, ${I});--ggui-color-elevated:color-mix(in srgb, ${B} 92%, ${I});--ggui-color-sunken:color-mix(in srgb, ${B} 88%, ${I});--ggui-color-onSunken:color-mix(in srgb, ${I} 76%, ${B});--ggui-color-neutral-500:color-mix(in srgb, ${I} 62%, ${B});--ggui-color-outline:${outlineOpen}color-mix(in srgb, ${I} 32%, ${B}));--ggui-color-controlOutline:${outlineOpen}color-mix(in srgb, ${I} 32%, ${B}));--ggui-color-outlineVariant:color-mix(in srgb, ${I} 18%, ${B})}}`;
20125
20125
  }
20126
20126
  var INVERTED_SCOPE_CSS = surfaceScopeCss(
20127
20127
  INVERTED_SCOPE_CLASS,
@@ -20657,7 +20657,8 @@
20657
20657
  },
20658
20658
  secondary: {
20659
20659
  backgroundColor: "var(--ggui-color-sunken, #f4f4f5)",
20660
- color: "var(--ggui-color-onSunken, #52525b)",
20660
+ // The label reads at full strength; the chip marks the rank (ggui#1566).
20661
+ color: "var(--ggui-color-onContainer, #18181b)",
20661
20662
  border: "none"
20662
20663
  },
20663
20664
  outline: {
@@ -20666,13 +20667,15 @@
20666
20667
  // label is the surface's own on-colour, never a brand-ladder stop
20667
20668
  // (ggui#1034): `primary-600` as the label read mid-grey on a monochrome
20668
20669
  // dark theme's ground — 2.41:1 / 3.5:1 on served reply chips. The brand
20669
- // stays on the border, where contrast is decorative.
20670
+ // stays on the border, and the border identifies the control, so it is the
20671
+ // first brand stop that clears 3:1 on the container (ggui#1494).
20670
20672
  color: "var(--ggui-color-onContainer, #18181b)",
20671
- border: "1px solid var(--ggui-color-primary-600, #0284c7)"
20673
+ border: "1px solid var(--ggui-color-controlAccentOutline, var(--ggui-color-primary-600, #0284c7))"
20672
20674
  },
20673
20675
  ghost: {
20674
20676
  backgroundColor: "transparent",
20675
- color: "var(--ggui-color-onSunken, #52525b)",
20677
+ // A dimmed label reads as disabled; ghost's rank is its missing chrome (ggui#1566).
20678
+ color: "var(--ggui-color-onContainer, #18181b)",
20676
20679
  border: "none"
20677
20680
  },
20678
20681
  danger: {
@@ -20738,7 +20741,7 @@
20738
20741
  ...sizeStyles2[size],
20739
20742
  width: "100%",
20740
20743
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
20741
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
20744
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
20742
20745
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
20743
20746
  color: "var(--ggui-color-onContainer, #18181b)",
20744
20747
  transition: `border-color ${motionVar.duration.base} ${motionVar.easing.standard}, box-shadow ${motionVar.duration.base} ${motionVar.easing.standard}`,
@@ -20772,7 +20775,7 @@
20772
20775
  padding: "10px 12px",
20773
20776
  width: "100%",
20774
20777
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
20775
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
20778
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
20776
20779
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
20777
20780
  color: "var(--ggui-color-onContainer, #18181b)",
20778
20781
  fontSize: "var(--ggui-font-size-sm, 14px)",
@@ -20804,17 +20807,25 @@
20804
20807
  const messageId = `${uniqueId}-message`;
20805
20808
  const hasError = Boolean(error);
20806
20809
  const hasMessage = Boolean(error || helperText);
20810
+ const startsOnPlaceholder = Boolean(placeholder) && value === void 0 && rest.defaultValue === void 0;
20811
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react7.useState)(startsOnPlaceholder ? "" : typeof rest.defaultValue === "string" ? rest.defaultValue : void 0);
20812
+ const showingPlaceholder = Boolean(placeholder) && (value ?? uncontrolledValue) === "";
20807
20813
  return (0, import_jsx_runtime11.jsxs)("div", { className, style: { display: "flex", flexDirection: "column", gap: "var(--ggui-spacing-1, 4px)", ...style }, children: [label && (0, import_jsx_runtime11.jsxs)("label", { htmlFor: selectId, style: {
20808
20814
  fontSize: "var(--ggui-font-size-sm, 14px)",
20809
20815
  fontWeight: "var(--ggui-font-weight-medium, 500)",
20810
20816
  color: "var(--ggui-color-onSunken, #52525b)"
20811
- }, children: [label, required && (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime11.jsxs)("select", { id: selectId, value, onChange: (e) => onChange?.(e.target.value), disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
20817
+ }, children: [label, required && (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime11.jsxs)("select", { id: selectId, value, ...startsOnPlaceholder ? { defaultValue: "" } : {}, onChange: (e) => {
20818
+ if (value === void 0)
20819
+ setUncontrolledValue(e.target.value);
20820
+ onChange?.(e.target.value);
20821
+ }, disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
20812
20822
  ...sizeStyles3[size],
20813
20823
  width: "100%",
20814
20824
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
20815
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
20825
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
20816
20826
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
20817
- color: value ? "var(--ggui-color-onContainer, #18181b)" : "var(--ggui-color-onSunken, #52525b)",
20827
+ // Muted only while the placeholder is what shows (ggui#1566, #1569).
20828
+ color: showingPlaceholder ? "var(--ggui-color-onSunken, #52525b)" : "var(--ggui-color-onContainer, #18181b)",
20818
20829
  cursor: disabled ? "not-allowed" : "pointer",
20819
20830
  appearance: "none",
20820
20831
  backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M3 4.5L6 7.5L9 4.5'/%3E%3C/svg%3E")`,
@@ -20859,7 +20870,7 @@
20859
20870
  width: "18px",
20860
20871
  height: "18px",
20861
20872
  borderRadius: "var(--ggui-shape-radius-sm, 4px)",
20862
- border: `2px solid ${checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)"}`,
20873
+ border: `2px solid ${checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
20863
20874
  backgroundColor: checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-sunken, #ffffff)",
20864
20875
  display: "flex",
20865
20876
  alignItems: "center",
@@ -20868,7 +20879,7 @@
20868
20879
  }, children: (checked || indeterminate) && (0, import_jsx_runtime12.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { color: "var(--ggui-color-onPrimary, #ffffff)" }, children: indeterminate ? (0, import_jsx_runtime12.jsx)("path", { d: "M2.5 6H9.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) : (0, import_jsx_runtime12.jsx)("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), (label || description) && (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [label && (0, import_jsx_runtime12.jsx)("span", { style: {
20869
20880
  fontSize: "var(--ggui-font-size-sm, 14px)",
20870
20881
  fontWeight: "var(--ggui-font-weight-medium, 500)",
20871
- color: "var(--ggui-color-onSunken, #52525b)"
20882
+ color: "var(--ggui-color-onContainer, #18181b)"
20872
20883
  }, children: label }), description && (0, import_jsx_runtime12.jsx)("span", { style: {
20873
20884
  fontSize: "var(--ggui-font-size-xs, 12px)",
20874
20885
  color: "var(--ggui-color-onSunken, #52525b)"
@@ -20904,7 +20915,7 @@
20904
20915
  width: config.width,
20905
20916
  height: config.height,
20906
20917
  borderRadius: "9999px",
20907
- backgroundColor: checked ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)",
20918
+ backgroundColor: checked ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))",
20908
20919
  position: "relative",
20909
20920
  transition: `background-color ${motionVar.duration.base} ${motionVar.easing.standard}, box-shadow ${motionVar.duration.base} ${motionVar.easing.standard}`,
20910
20921
  flexShrink: 0,
@@ -20923,7 +20934,7 @@
20923
20934
  } }) }), label && (0, import_jsx_runtime13.jsx)("span", { style: {
20924
20935
  fontSize: "var(--ggui-font-size-sm, 14px)",
20925
20936
  fontWeight: "var(--ggui-font-weight-medium, 500)",
20926
- color: "var(--ggui-color-onSunken, #52525b)"
20937
+ color: "var(--ggui-color-onContainer, #18181b)"
20927
20938
  }, children: label })] });
20928
20939
  }
20929
20940
 
@@ -20971,7 +20982,7 @@
20971
20982
  width: "18px",
20972
20983
  height: "18px",
20973
20984
  borderRadius: "var(--ggui-shape-radius-full, 9999px)",
20974
- border: `2px solid ${isSelected ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)"}`,
20985
+ border: `2px solid ${isSelected ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
20975
20986
  backgroundColor: "var(--ggui-color-sunken, #ffffff)",
20976
20987
  display: "flex",
20977
20988
  alignItems: "center",
@@ -20985,7 +20996,7 @@
20985
20996
  } }) })] }), (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [(0, import_jsx_runtime14.jsx)("span", { style: {
20986
20997
  fontSize: "var(--ggui-font-size-sm, 14px)",
20987
20998
  fontWeight: "var(--ggui-font-weight-medium, 500)",
20988
- color: "var(--ggui-color-onSunken, #52525b)"
20999
+ color: "var(--ggui-color-onContainer, #18181b)"
20989
21000
  }, children: option.label }), option.description && (0, import_jsx_runtime14.jsx)("span", { style: {
20990
21001
  fontSize: "var(--ggui-font-size-xs, 12px)",
20991
21002
  color: "var(--ggui-color-onSunken, #52525b)"
@@ -26043,7 +26054,9 @@
26043
26054
  width: "100%",
26044
26055
  borderCollapse: "collapse",
26045
26056
  fontSize: "var(--ggui-font-size-sm, 14px)",
26046
- color: "var(--ggui-color-onSunken, #52525b)"
26057
+ // Body cells are the data and inherit this (ggui#1566); the header and
26058
+ // caption set the muted tier themselves.
26059
+ color: "var(--ggui-color-onContainer, #18181b)"
26047
26060
  }, children: [caption && (0, import_jsx_runtime25.jsx)("caption", { style: {
26048
26061
  padding: cellPadding,
26049
26062
  fontSize: "var(--ggui-font-size-sm, 14px)",
@@ -26509,7 +26522,7 @@
26509
26522
  }, children: isExpanded && (0, import_jsx_runtime28.jsx)("div", { style: {
26510
26523
  padding: "0 var(--ggui-spacing-4, 16px) var(--ggui-spacing-4, 16px)",
26511
26524
  fontSize: "var(--ggui-font-size-sm, 14px)",
26512
- color: "var(--ggui-color-onSunken, #52525b)",
26525
+ color: "var(--ggui-color-onContainer, #18181b)",
26513
26526
  lineHeight: "var(--ggui-font-lineHeight-normal, 1.5)"
26514
26527
  }, children: item.content }) })] }, item.key);
26515
26528
  }) });
@@ -26626,7 +26639,7 @@
26626
26639
  paddingLeft: "36px",
26627
26640
  fontSize: size === "lg" ? "16px" : "14px",
26628
26641
  borderRadius: "6px",
26629
- border: `1px solid var(--ggui-color-outline, #d1d5db)`,
26642
+ border: `1px solid var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))`,
26630
26643
  backgroundColor: disabled ? "var(--ggui-color-sunken, #f9fafb)" : "var(--ggui-color-container, #ffffff)",
26631
26644
  color: "var(--ggui-color-onContainer, #111827)",
26632
26645
  outline: "none",
@@ -26649,11 +26662,11 @@
26649
26662
  color: "var(--ggui-color-onContainer, #374151)"
26650
26663
  }, children: [label, required && (0, import_jsx_runtime30.jsx)("span", { style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), description && (0, import_jsx_runtime30.jsx)("span", { style: {
26651
26664
  fontSize: fontSize.xs,
26652
- color: "var(--ggui-color-neutral-500, #6b7280)",
26665
+ color: resolveToneCss("subtle"),
26653
26666
  marginBottom: "4px"
26654
26667
  }, children: description }), children, (error || helperText) && (0, import_jsx_runtime30.jsx)("span", { style: {
26655
26668
  fontSize: fontSize.xs,
26656
- color: hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-neutral-500, #6b7280)"
26669
+ color: hasError ? "var(--ggui-color-error-500, #ef4444)" : resolveToneCss("subtle")
26657
26670
  }, children: error || helperText })] });
26658
26671
  }
26659
26672
 
@@ -26899,7 +26912,7 @@
26899
26912
  }, children: loading ? (0, import_jsx_runtime34.jsx)("div", { style: { padding: "12px", display: "flex", justifyContent: "center" }, children: (0, import_jsx_runtime34.jsx)(Spinner, { size: 20 }) }) : filteredOptions.length === 0 ? (0, import_jsx_runtime34.jsx)("div", { style: {
26900
26913
  padding: "12px",
26901
26914
  textAlign: "center",
26902
- color: "var(--ggui-color-neutral-500, #6b7280)",
26915
+ color: resolveToneCss("subtle"),
26903
26916
  fontSize: fontSize.sm
26904
26917
  }, children: noResultsText }) : filteredOptions.map((option, index) => (0, import_jsx_runtime34.jsxs)("div", { onClick: () => !option.disabled && handleSelect(option), style: {
26905
26918
  padding: "8px 12px",
@@ -26910,7 +26923,7 @@
26910
26923
  display: "flex",
26911
26924
  alignItems: "center",
26912
26925
  gap: "8px"
26913
- }, onMouseEnter: () => setHighlightedIndex(index), children: [option.icon && (0, import_jsx_runtime34.jsx)("span", { style: { flexShrink: 0 }, children: option.icon }), (0, import_jsx_runtime34.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime34.jsx)("div", { children: option.label }), option.description && (0, import_jsx_runtime34.jsx)("div", { style: { fontSize: fontSize.xs, color: "var(--ggui-color-neutral-500, #6b7280)" }, children: option.description })] })] }, option.value)) })] });
26926
+ }, onMouseEnter: () => setHighlightedIndex(index), children: [option.icon && (0, import_jsx_runtime34.jsx)("span", { style: { flexShrink: 0 }, children: option.icon }), (0, import_jsx_runtime34.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime34.jsx)("div", { children: option.label }), option.description && (0, import_jsx_runtime34.jsx)("div", { style: { fontSize: fontSize.xs, color: resolveToneCss("subtle") }, children: option.description })] })] }, option.value)) })] });
26914
26927
  }
26915
26928
 
26916
26929
  // ../design/dist/components/Breadcrumb.js
@@ -26928,7 +26941,7 @@
26928
26941
  return (0, import_jsx_runtime35.jsxs)(import_react23.Fragment, { children: [index > 0 && (0, import_jsx_runtime35.jsx)("span", { style: { color: "var(--ggui-color-neutral-400, #9ca3af)" }, children: separator }), item.icon && (0, import_jsx_runtime35.jsx)("span", { style: {
26929
26942
  display: "flex",
26930
26943
  alignItems: "center",
26931
- color: isLast ? "var(--ggui-color-onContainer, #111827)" : "var(--ggui-color-neutral-500, #6b7280)"
26944
+ color: isLast ? "var(--ggui-color-onContainer, #111827)" : resolveToneCss("subtle")
26932
26945
  }, children: item.icon }), isLast ? (0, import_jsx_runtime35.jsx)("span", { style: {
26933
26946
  color: "var(--ggui-color-onContainer, #111827)",
26934
26947
  fontWeight: 500
@@ -26941,7 +26954,7 @@
26941
26954
  background: "none",
26942
26955
  border: "none",
26943
26956
  padding: 0,
26944
- color: "var(--ggui-color-neutral-500, #6b7280)",
26957
+ color: resolveToneCss("subtle"),
26945
26958
  cursor: "pointer",
26946
26959
  fontSize: "inherit"
26947
26960
  }, children: item.label })] }, index);
@@ -27687,7 +27700,7 @@
27687
27700
  }
27688
27701
  };
27689
27702
  const timestamp = comment.timestamp instanceof Date ? comment.timestamp.toLocaleString() : comment.timestamp;
27690
- return (0, import_jsx_runtime44.jsxs)("div", { style: { marginLeft: depth > 0 ? "40px" : 0 }, children: [(0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: "12px" }, children: [(0, import_jsx_runtime44.jsx)(Avatar, { name: comment.author.name, src: comment.author.avatar, size: "sm" }), (0, import_jsx_runtime44.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [(0, import_jsx_runtime44.jsx)("span", { style: { fontWeight: 500, fontSize: fontSize.sm }, children: comment.author.name }), (0, import_jsx_runtime44.jsx)("span", { style: { color: "var(--ggui-color-neutral-500, #6b7280)", fontSize: fontSize.xs }, children: timestamp })] }), (0, import_jsx_runtime44.jsx)("p", { style: { margin: "4px 0 8px", fontSize: fontSize.sm, color: "var(--ggui-color-onContainer, #374151)" }, children: comment.content }), (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "12px" }, children: [comment.reactions?.map((reaction) => (0, import_jsx_runtime44.jsxs)("button", { onClick: () => onReaction?.(comment.id, reaction.emoji), style: {
27703
+ return (0, import_jsx_runtime44.jsxs)("div", { style: { marginLeft: depth > 0 ? "40px" : 0 }, children: [(0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: "12px" }, children: [(0, import_jsx_runtime44.jsx)(Avatar, { name: comment.author.name, src: comment.author.avatar, size: "sm" }), (0, import_jsx_runtime44.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [(0, import_jsx_runtime44.jsx)("span", { style: { fontWeight: 500, fontSize: fontSize.sm }, children: comment.author.name }), (0, import_jsx_runtime44.jsx)("span", { style: { color: resolveToneCss("subtle"), fontSize: fontSize.xs }, children: timestamp })] }), (0, import_jsx_runtime44.jsx)("p", { style: { margin: "4px 0 8px", fontSize: fontSize.sm, color: "var(--ggui-color-onContainer, #374151)" }, children: comment.content }), (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "12px" }, children: [comment.reactions?.map((reaction) => (0, import_jsx_runtime44.jsxs)("button", { onClick: () => onReaction?.(comment.id, reaction.emoji), style: {
27691
27704
  padding: "2px 8px",
27692
27705
  border: `1px solid var(--ggui-color-outlineVariant, #e5e7eb)`,
27693
27706
  borderRadius: "12px",
@@ -27697,7 +27710,7 @@
27697
27710
  }, children: [reaction.emoji, " ", reaction.count] }, reaction.emoji)), (0, import_jsx_runtime44.jsx)("button", { onClick: () => setShowReplyInput(!showReplyInput), style: {
27698
27711
  background: "none",
27699
27712
  border: "none",
27700
- color: "var(--ggui-color-neutral-500, #6b7280)",
27713
+ color: resolveToneCss("subtle"),
27701
27714
  fontSize: fontSize.xs,
27702
27715
  cursor: "pointer"
27703
27716
  }, children: "Reply" })] }), showReplyInput && (0, import_jsx_runtime44.jsxs)("div", { style: { marginTop: "12px" }, children: [(0, import_jsx_runtime44.jsx)(TextArea, { value: replyContent, onChange: setReplyContent, placeholder: "Write a reply...", rows: 2 }), (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: "8px", marginTop: "8px" }, children: [(0, import_jsx_runtime44.jsx)(Button, { size: "sm", onClick: handleSubmitReply, children: "Reply" }), (0, import_jsx_runtime44.jsx)(Button, { size: "sm", variant: "ghost", onClick: () => setShowReplyInput(false), children: "Cancel" })] })] })] })] }), comment.replies?.map((reply) => (0, import_jsx_runtime44.jsx)("div", { style: { marginTop: "16px" }, children: (0, import_jsx_runtime44.jsx)(CommentItem, { comment: reply, onReply, onReaction, depth: depth + 1 }) }, reply.id))] });
@@ -27755,7 +27768,7 @@
27755
27768
  borderBottom: "1px solid var(--ggui-color-outlineVariant, #e5e7eb)",
27756
27769
  cursor: col.sortable ? "pointer" : void 0,
27757
27770
  width: col.width
27758
- }, onClick: () => col.sortable && onSort?.(col.key, sortKey === col.key && sortDirection === "asc" ? "desc" : "asc"), children: (0, import_jsx_runtime45.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [col.header, col.sortable && sortKey === col.key && (0, import_jsx_runtime45.jsx)(Icon, { name: sortDirection === "asc" ? "chevron-up" : "chevron-down", size: 14 })] }) }, col.key))] }) }), (0, import_jsx_runtime45.jsx)("tbody", { children: loading ? (0, import_jsx_runtime45.jsx)("tr", { children: (0, import_jsx_runtime45.jsx)("td", { colSpan: columns.length + (selectable ? 1 : 0), style: { padding: "48px", textAlign: "center" }, children: (0, import_jsx_runtime45.jsx)(Spinner, { size: 24 }) }) }) : data.length === 0 ? (0, import_jsx_runtime45.jsx)("tr", { children: (0, import_jsx_runtime45.jsx)("td", { colSpan: columns.length + (selectable ? 1 : 0), style: { padding: "48px", textAlign: "center", color: "var(--ggui-color-neutral-500, #6b7280)" }, children: emptyText }) }) : data.map((row, index) => {
27771
+ }, onClick: () => col.sortable && onSort?.(col.key, sortKey === col.key && sortDirection === "asc" ? "desc" : "asc"), children: (0, import_jsx_runtime45.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [col.header, col.sortable && sortKey === col.key && (0, import_jsx_runtime45.jsx)(Icon, { name: sortDirection === "asc" ? "chevron-up" : "chevron-down", size: 14 })] }) }, col.key))] }) }), (0, import_jsx_runtime45.jsx)("tbody", { children: loading ? (0, import_jsx_runtime45.jsx)("tr", { children: (0, import_jsx_runtime45.jsx)("td", { colSpan: columns.length + (selectable ? 1 : 0), style: { padding: "48px", textAlign: "center" }, children: (0, import_jsx_runtime45.jsx)(Spinner, { size: 24 }) }) }) : data.length === 0 ? (0, import_jsx_runtime45.jsx)("tr", { children: (0, import_jsx_runtime45.jsx)("td", { colSpan: columns.length + (selectable ? 1 : 0), style: { padding: "48px", textAlign: "center", color: resolveToneCss("subtle") }, children: emptyText }) }) : data.map((row, index) => {
27759
27772
  const key = getRowKey(row, index);
27760
27773
  const isSelected = selectedKeys.includes(key);
27761
27774
  return (0, import_jsx_runtime45.jsxs)("tr", { onClick: () => onRowClick?.(row, index), style: {
@@ -27824,16 +27837,19 @@
27824
27837
  padding: "10px 14px",
27825
27838
  borderRadius: radius.lg,
27826
27839
  backgroundColor: isOwn ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-sunken, #f3f4f6)",
27827
- color: isOwn ? "var(--ggui-color-onPrimary, #ffffff)" : "var(--ggui-color-onSunken, #111827)"
27840
+ color: isOwn ? "var(--ggui-color-onPrimary, #ffffff)" : "var(--ggui-color-onContainer, #111827)"
27828
27841
  }, children: [(0, import_jsx_runtime46.jsx)("p", { style: { margin: 0, fontSize: fontSize.sm }, children: message.content }), (0, import_jsx_runtime46.jsxs)("div", { style: {
27829
27842
  display: "flex",
27830
27843
  alignItems: "center",
27831
27844
  gap: "4px",
27832
27845
  marginTop: "4px",
27833
27846
  fontSize: fontSize.xs,
27834
- opacity: 0.7
27847
+ // Own messages derive the muted line from onPrimary; the other
27848
+ // party's message now reads at onContainer, so its timestamp
27849
+ // takes the muted tier itself (ggui#1566).
27850
+ ...isOwn ? { opacity: 0.7 } : { color: "var(--ggui-color-onSunken, #52525b)" }
27835
27851
  }, children: [(0, import_jsx_runtime46.jsx)("span", { children: timestamp }), isOwn && message.status && (0, import_jsx_runtime46.jsxs)("span", { children: [message.status === "sending" && "\u2022", message.status === "sent" && "\u2713", message.status === "delivered" && "\u2713\u2713", message.status === "read" && "\u2713\u2713", message.status === "error" && "!"] })] })] })] }, message.id);
27836
- }), typing && (0, import_jsx_runtime46.jsx)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: (0, import_jsx_runtime46.jsxs)("span", { style: { fontSize: fontSize.xs, color: "var(--ggui-color-neutral-500, #6b7280)" }, children: [typing.name, " is typing..."] }) }), (0, import_jsx_runtime46.jsx)("div", { ref: messagesEndRef })] }), (0, import_jsx_runtime46.jsxs)("div", { style: {
27852
+ }), typing && (0, import_jsx_runtime46.jsx)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: (0, import_jsx_runtime46.jsxs)("span", { style: { fontSize: fontSize.xs, color: resolveToneCss("subtle") }, children: [typing.name, " is typing..."] }) }), (0, import_jsx_runtime46.jsx)("div", { ref: messagesEndRef })] }), (0, import_jsx_runtime46.jsxs)("div", { style: {
27837
27853
  padding: "12px 16px",
27838
27854
  borderTop: `1px solid var(--ggui-color-outlineVariant, #e5e7eb)`,
27839
27855
  display: "flex",
@@ -27841,7 +27857,7 @@
27841
27857
  }, children: [(0, import_jsx_runtime46.jsx)("input", { type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder, style: {
27842
27858
  flex: 1,
27843
27859
  padding: "10px 14px",
27844
- border: `1px solid var(--ggui-color-outline, #d1d5db)`,
27860
+ border: `1px solid var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))`,
27845
27861
  borderRadius: radius.full,
27846
27862
  fontSize: fontSize.sm,
27847
27863
  outline: "none"
@@ -27953,7 +27969,7 @@
27953
27969
  setIsDragOver(false);
27954
27970
  };
27955
27971
  return (0, import_jsx_runtime48.jsxs)("div", { className, style: { ...style }, children: [(0, import_jsx_runtime48.jsxs)("div", { onClick: () => !disabled && inputRef.current?.click(), onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, style: {
27956
- border: `2px dashed ${isDragOver ? "var(--ggui-color-primary-400, #38bdf8)" : "var(--ggui-color-outline, #d1d5db)"}`,
27972
+ border: `2px dashed ${isDragOver ? "var(--ggui-color-primary-400, #38bdf8)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))"}`,
27957
27973
  borderRadius: radius.lg,
27958
27974
  padding: "32px",
27959
27975
  textAlign: "center",
@@ -27961,7 +27977,7 @@
27961
27977
  cursor: disabled ? "not-allowed" : "pointer",
27962
27978
  transition: "all 0.2s",
27963
27979
  opacity: disabled ? 0.5 : 1
27964
- }, children: [(0, import_jsx_runtime48.jsx)("input", { ref: inputRef, type: "file", accept, multiple, onChange: (e) => handleFiles(e.target.files), disabled, style: { display: "none" } }), (0, import_jsx_runtime48.jsx)(Icon, { name: "plus", size: 32, tone: "subtle" }), (0, import_jsx_runtime48.jsx)("p", { style: { margin: "12px 0 4px", color: "var(--ggui-color-onContainer, #374151)", fontSize: fontSize.sm }, children: dragDrop ? "Drag and drop files here, or click to browse" : "Click to browse files" }), (0, import_jsx_runtime48.jsxs)("p", { style: { margin: 0, color: "var(--ggui-color-neutral-500, #6b7280)", fontSize: fontSize.xs }, children: [accept && `Accepted: ${accept}`, maxSize && ` \u2022 Max size: ${formatFileSize(maxSize)}`, maxFiles && ` \u2022 Max files: ${maxFiles}`] })] }), files.length > 0 && (0, import_jsx_runtime48.jsx)("div", { style: { marginTop: "16px", display: "flex", flexDirection: "column", gap: "8px" }, children: files.map((file) => (0, import_jsx_runtime48.jsxs)("div", { style: {
27980
+ }, children: [(0, import_jsx_runtime48.jsx)("input", { ref: inputRef, type: "file", accept, multiple, onChange: (e) => handleFiles(e.target.files), disabled, style: { display: "none" } }), (0, import_jsx_runtime48.jsx)(Icon, { name: "plus", size: 32, tone: "subtle" }), (0, import_jsx_runtime48.jsx)("p", { style: { margin: "12px 0 4px", color: "var(--ggui-color-onContainer, #374151)", fontSize: fontSize.sm }, children: dragDrop ? "Drag and drop files here, or click to browse" : "Click to browse files" }), (0, import_jsx_runtime48.jsxs)("p", { style: { margin: 0, color: resolveToneCss("subtle"), fontSize: fontSize.xs }, children: [accept && `Accepted: ${accept}`, maxSize && ` \u2022 Max size: ${formatFileSize(maxSize)}`, maxFiles && ` \u2022 Max files: ${maxFiles}`] })] }), files.length > 0 && (0, import_jsx_runtime48.jsx)("div", { style: { marginTop: "16px", display: "flex", flexDirection: "column", gap: "8px" }, children: files.map((file) => (0, import_jsx_runtime48.jsxs)("div", { style: {
27965
27981
  display: "flex",
27966
27982
  alignItems: "center",
27967
27983
  gap: "12px",
@@ -27976,7 +27992,7 @@
27976
27992
  whiteSpace: "nowrap",
27977
27993
  overflow: "hidden",
27978
27994
  textOverflow: "ellipsis"
27979
- }, children: file.name }), (0, import_jsx_runtime48.jsxs)("p", { style: { margin: "2px 0 0", fontSize: fontSize.xs, color: "var(--ggui-color-neutral-500, #6b7280)" }, children: [formatFileSize(file.size), file.error && (0, import_jsx_runtime48.jsx)("span", { style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "8px" }, children: file.error })] }), file.status === "uploading" && file.progress !== void 0 && (0, import_jsx_runtime48.jsx)(Progress, { value: file.progress, size: "sm", style: { marginTop: "8px" } })] }), (0, import_jsx_runtime48.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => onFileRemove?.(file.id), children: (0, import_jsx_runtime48.jsx)(Icon, { name: "x", size: 16 }) })] }, file.id)) })] });
27995
+ }, children: file.name }), (0, import_jsx_runtime48.jsxs)("p", { style: { margin: "2px 0 0", fontSize: fontSize.xs, color: resolveToneCss("subtle") }, children: [formatFileSize(file.size), file.error && (0, import_jsx_runtime48.jsx)("span", { style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "8px" }, children: file.error })] }), file.status === "uploading" && file.progress !== void 0 && (0, import_jsx_runtime48.jsx)(Progress, { value: file.progress, size: "sm", style: { marginTop: "8px" } })] }), (0, import_jsx_runtime48.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => onFileRemove?.(file.id), children: (0, import_jsx_runtime48.jsx)(Icon, { name: "x", size: 16 }) })] }, file.id)) })] });
27980
27996
  }
27981
27997
 
27982
27998
  // ../design/dist/compositions/NotificationCenter.js
@@ -27994,7 +28010,10 @@
27994
28010
  display: "flex",
27995
28011
  gap: "12px",
27996
28012
  padding: "12px",
27997
- backgroundColor: notification.read ? "transparent" : "var(--ggui-color-primaryContainer, #f0f9ff)",
28013
+ // Unread is carried by the dot and the title's weight; the row takes the
28014
+ // ground its muted text is measured on (ggui#1567), not a raised accent
28015
+ // ground where that text falls under AA in dark themes.
28016
+ backgroundColor: notification.read ? "transparent" : "var(--ggui-color-sunken, #f4f4f5)",
27998
28017
  borderRadius: radius.md,
27999
28018
  transition: "background-color 0.15s"
28000
28019
  }, children: [(0, import_jsx_runtime49.jsx)("div", { style: {
@@ -28020,7 +28039,7 @@
28020
28039
  margin: "4px 0 0",
28021
28040
  color: "var(--ggui-color-onSunken, #4b5563)",
28022
28041
  fontSize: fontSize.xs
28023
- }, children: notification.message }), (0, import_jsx_runtime49.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "12px", marginTop: "8px" }, children: [(0, import_jsx_runtime49.jsx)("span", { style: { color: "var(--ggui-color-neutral-400, #9ca3af)", fontSize: fontSize.xs }, children: timestamp }), !notification.read && (0, import_jsx_runtime49.jsx)("button", { onClick: () => onMarkAsRead?.(notification.id), style: {
28042
+ }, children: notification.message }), (0, import_jsx_runtime49.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "12px", marginTop: "8px" }, children: [(0, import_jsx_runtime49.jsx)("span", { style: { color: resolveToneCss("subtle"), fontSize: fontSize.xs }, children: timestamp }), !notification.read && (0, import_jsx_runtime49.jsx)("button", { onClick: () => onMarkAsRead?.(notification.id), style: {
28024
28043
  background: "none",
28025
28044
  border: "none",
28026
28045
  padding: 0,
@@ -28055,7 +28074,7 @@
28055
28074
  color: "var(--ggui-color-onPrimaryContainer, #0369a1)",
28056
28075
  fontSize: fontSize.xs,
28057
28076
  fontWeight: fontWeight.medium
28058
- }, children: unreadCount })] }), (0, import_jsx_runtime49.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [unreadCount > 0 && (0, import_jsx_runtime49.jsx)(Button, { variant: "ghost", size: "sm", onClick: onMarkAllAsRead, children: "Mark all read" }), notifications.length > 0 && (0, import_jsx_runtime49.jsx)(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: "Clear all" })] })] }), (0, import_jsx_runtime49.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "8px" }, children: loading ? (0, import_jsx_runtime49.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "32px" }, children: (0, import_jsx_runtime49.jsx)(Spinner, { size: 24 }) }) : notifications.length === 0 ? (0, import_jsx_runtime49.jsx)("div", { style: { textAlign: "center", padding: "32px", color: "var(--ggui-color-neutral-500, #6b7280)" }, children: emptyText }) : (0, import_jsx_runtime49.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: notifications.map((notification) => (0, import_jsx_runtime49.jsx)(NotificationItem, { notification, onMarkAsRead, onDismiss }, notification.id)) }) })] });
28077
+ }, children: unreadCount })] }), (0, import_jsx_runtime49.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [unreadCount > 0 && (0, import_jsx_runtime49.jsx)(Button, { variant: "ghost", size: "sm", onClick: onMarkAllAsRead, children: "Mark all read" }), notifications.length > 0 && (0, import_jsx_runtime49.jsx)(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: "Clear all" })] })] }), (0, import_jsx_runtime49.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "8px" }, children: loading ? (0, import_jsx_runtime49.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "32px" }, children: (0, import_jsx_runtime49.jsx)(Spinner, { size: 24 }) }) : notifications.length === 0 ? (0, import_jsx_runtime49.jsx)("div", { style: { textAlign: "center", padding: "32px", color: resolveToneCss("subtle") }, children: emptyText }) : (0, import_jsx_runtime49.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: notifications.map((notification) => (0, import_jsx_runtime49.jsx)(NotificationItem, { notification, onMarkAsRead, onDismiss }, notification.id)) }) })] });
28059
28078
  }
28060
28079
 
28061
28080
  // ../design/dist/compositions/Modal.js
@@ -28245,11 +28264,11 @@
28245
28264
  fontSize: fontSize.base,
28246
28265
  outline: "none",
28247
28266
  backgroundColor: "transparent"
28248
- } })] }) }), (0, import_jsx_runtime51.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "8px" }, children: loading ? (0, import_jsx_runtime51.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "32px" }, children: (0, import_jsx_runtime51.jsx)(Spinner, { size: 24 }) }) : flatCommands.length === 0 ? (0, import_jsx_runtime51.jsx)("div", { style: { textAlign: "center", padding: "32px", color: "var(--ggui-color-neutral-500, #6b7280)" }, children: "No commands found" }) : Array.from(filteredCommands.entries()).map(([group, cmds]) => (0, import_jsx_runtime51.jsxs)("div", { style: { marginBottom: "8px" }, children: [(0, import_jsx_runtime51.jsx)("div", { style: {
28267
+ } })] }) }), (0, import_jsx_runtime51.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "8px" }, children: loading ? (0, import_jsx_runtime51.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "32px" }, children: (0, import_jsx_runtime51.jsx)(Spinner, { size: 24 }) }) : flatCommands.length === 0 ? (0, import_jsx_runtime51.jsx)("div", { style: { textAlign: "center", padding: "32px", color: resolveToneCss("subtle") }, children: "No commands found" }) : Array.from(filteredCommands.entries()).map(([group, cmds]) => (0, import_jsx_runtime51.jsxs)("div", { style: { marginBottom: "8px" }, children: [(0, import_jsx_runtime51.jsx)("div", { style: {
28249
28268
  padding: "8px 12px",
28250
28269
  fontSize: fontSize.xs,
28251
28270
  fontWeight: fontWeight.semibold,
28252
- color: "var(--ggui-color-neutral-500, #6b7280)",
28271
+ color: resolveToneCss("subtle"),
28253
28272
  textTransform: "uppercase",
28254
28273
  letterSpacing: "0.05em"
28255
28274
  }, children: group }), cmds.map((cmd) => {
@@ -28272,12 +28291,12 @@
28272
28291
  color: cmd.disabled ? "var(--ggui-color-neutral-400, #9ca3af)" : "var(--ggui-color-onElevated, #111827)",
28273
28292
  textAlign: "left",
28274
28293
  cursor: cmd.disabled ? "not-allowed" : "pointer"
28275
- }, children: [cmd.icon && (0, import_jsx_runtime51.jsx)("span", { style: { display: "flex", color: "var(--ggui-color-neutral-500, #6b7280)" }, children: cmd.icon }), (0, import_jsx_runtime51.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime51.jsx)("div", { style: { fontSize: fontSize.sm }, children: cmd.label }), cmd.description && (0, import_jsx_runtime51.jsx)("div", { style: { fontSize: fontSize.xs, color: "var(--ggui-color-neutral-500, #6b7280)" }, children: cmd.description })] }), cmd.shortcut && (0, import_jsx_runtime51.jsx)("kbd", { style: {
28294
+ }, children: [cmd.icon && (0, import_jsx_runtime51.jsx)("span", { style: { display: "flex", color: resolveToneCss("subtle") }, children: cmd.icon }), (0, import_jsx_runtime51.jsxs)("div", { style: { flex: 1 }, children: [(0, import_jsx_runtime51.jsx)("div", { style: { fontSize: fontSize.sm }, children: cmd.label }), cmd.description && (0, import_jsx_runtime51.jsx)("div", { style: { fontSize: fontSize.xs, color: resolveToneCss("subtle") }, children: cmd.description })] }), cmd.shortcut && (0, import_jsx_runtime51.jsx)("kbd", { style: {
28276
28295
  padding: "2px 6px",
28277
28296
  backgroundColor: "var(--ggui-color-sunken, #f3f4f6)",
28278
28297
  borderRadius: radius.sm,
28279
28298
  fontSize: fontSize.xs,
28280
- color: "var(--ggui-color-neutral-500, #6b7280)",
28299
+ color: resolveToneCss("subtle"),
28281
28300
  fontFamily: "inherit"
28282
28301
  }, children: cmd.shortcut })] }, cmd.id);
28283
28302
  })] }, group)) }), (0, import_jsx_runtime51.jsxs)("div", { style: {
@@ -28287,7 +28306,7 @@
28287
28306
  alignItems: "center",
28288
28307
  gap: "16px",
28289
28308
  fontSize: fontSize.xs,
28290
- color: "var(--ggui-color-neutral-500, #6b7280)"
28309
+ color: resolveToneCss("subtle")
28291
28310
  }, children: [(0, import_jsx_runtime51.jsx)("span", { children: "\u2191\u2193 Navigate" }), (0, import_jsx_runtime51.jsx)("span", { children: "\u21B5 Select" }), (0, import_jsx_runtime51.jsx)("span", { children: "Esc Close" })] })] })] });
28292
28311
  }
28293
28312
 
@@ -28340,20 +28359,20 @@
28340
28359
  borderTop: "1px solid var(--ggui-color-outlineVariant, #e5e7eb)"
28341
28360
  }, children: [bottomText && (0, import_jsx_runtime52.jsx)("span", { style: {
28342
28361
  fontSize: fontSize.sm,
28343
- color: "var(--ggui-color-neutral-500, #6b7280)",
28362
+ color: resolveToneCss("subtle"),
28344
28363
  lineHeight: lineHeight.normal
28345
28364
  }, children: bottomText }), (0, import_jsx_runtime52.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "20px" }, children: [socialLinks?.map((social, idx) => (0, import_jsx_runtime52.jsx)("a", { href: social.href, "aria-label": social.label, style: {
28346
28365
  display: "flex",
28347
28366
  alignItems: "center",
28348
28367
  justifyContent: "center",
28349
- color: "var(--ggui-color-neutral-500, #6b7280)",
28368
+ color: resolveToneCss("subtle"),
28350
28369
  transition: "color 0.15s"
28351
28370
  }, children: social.icon }, idx)), bottomLinks?.map((link, idx) => (0, import_jsx_runtime52.jsx)("a", { href: link.href, onClick: link.onClick ? (e) => {
28352
28371
  e.preventDefault();
28353
28372
  link.onClick?.();
28354
28373
  } : void 0, style: {
28355
28374
  fontSize: fontSize.sm,
28356
- color: "var(--ggui-color-neutral-500, #6b7280)",
28375
+ color: resolveToneCss("subtle"),
28357
28376
  textDecoration: "none",
28358
28377
  transition: "color 0.15s"
28359
28378
  }, children: link.label }, idx))] })] })] });
@@ -28436,7 +28455,7 @@
28436
28455
  fontWeight: fontWeight.semibold,
28437
28456
  color: showOverlay ? "#ffffff" : "var(--ggui-color-onContainer, #374151)",
28438
28457
  backgroundColor: "transparent",
28439
- border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-outline, #d1d5db)"}`,
28458
+ border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))"}`,
28440
28459
  borderRadius: "8px",
28441
28460
  cursor: "pointer",
28442
28461
  transition: "all 0.15s",
@@ -29517,6 +29536,7 @@
29517
29536
  const result = validateEnvelope(actionSpec, envelope);
29518
29537
  if (!result.valid) {
29519
29538
  opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
29539
+ opts.onDispatchRefused?.({ actionName, violations: result.violations });
29520
29540
  return;
29521
29541
  }
29522
29542
  if (isOneShot && !spentOneShots.has(actionName)) {
@@ -29579,6 +29599,8 @@
29579
29599
  version: 2,
29580
29600
  tokens: [
29581
29601
  "--ggui-color-container",
29602
+ "--ggui-color-controlAccentOutline",
29603
+ "--ggui-color-controlOutline",
29582
29604
  "--ggui-color-elevated",
29583
29605
  "--ggui-color-error",
29584
29606
  "--ggui-color-error-100",
@@ -30210,8 +30232,10 @@
30210
30232
  // paper
30211
30233
  sunken: { $value: "#292929", $type: "color" },
30212
30234
  // ink
30213
- onSunken: { $value: "#d9d9d9", $type: "color" },
30214
- // chrome
30235
+ // Muted text, and text on sunken grounds (ggui#1566): the kit's dark muted
30236
+ // value, 6.57:1 on container and 5.80:1 on sunken. Light's onSunken is ink-3,
30237
+ // the same tier, so dark now reads as light already does.
30238
+ onSunken: { $value: "#a3a3a9", $type: "color" },
30215
30239
  container: { $value: "#1f1f1f", $type: "color" },
30216
30240
  // ink
30217
30241
  onContainer: { $value: "#f4f3ed", $type: "color" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/ui-visual-tester",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Behavioral validator for ggui-generated component code. Renders a component in real Chromium via Playwright, clicks each action declared in its contract, and verifies the action actually does something — mutating bound state or dispatching to the agent. Catches buttons wired to no-op handlers that structural validators miss. Playwright is an optional peer dependency, injected by the caller.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -53,9 +53,9 @@
53
53
  "dependencies": {
54
54
  "react": "^19.0.0",
55
55
  "react-dom": "^19.0.0",
56
- "@ggui-ai/protocol": "0.25.0",
57
- "@ggui-ai/wire": "0.25.0",
58
- "@ggui-ai/design": "0.25.0"
56
+ "@ggui-ai/design": "0.27.0",
57
+ "@ggui-ai/protocol": "0.27.0",
58
+ "@ggui-ai/wire": "0.27.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@playwright/test": "^1.60.0",