@ggui-ai/ui-visual-tester 0.25.0 → 0.26.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
@@ -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",
@@ -27824,14 +27837,17 @@
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
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: "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: {
27837
27853
  padding: "12px 16px",
@@ -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",
@@ -28436,7 +28452,7 @@
28436
28452
  fontWeight: fontWeight.semibold,
28437
28453
  color: showOverlay ? "#ffffff" : "var(--ggui-color-onContainer, #374151)",
28438
28454
  backgroundColor: "transparent",
28439
- border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-outline, #d1d5db)"}`,
28455
+ border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))"}`,
28440
28456
  borderRadius: "8px",
28441
28457
  cursor: "pointer",
28442
28458
  transition: "all 0.15s",
@@ -29517,6 +29533,7 @@
29517
29533
  const result = validateEnvelope(actionSpec, envelope);
29518
29534
  if (!result.valid) {
29519
29535
  opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
29536
+ opts.onDispatchRefused?.({ actionName, violations: result.violations });
29520
29537
  return;
29521
29538
  }
29522
29539
  if (isOneShot && !spentOneShots.has(actionName)) {
@@ -29579,6 +29596,8 @@
29579
29596
  version: 2,
29580
29597
  tokens: [
29581
29598
  "--ggui-color-container",
29599
+ "--ggui-color-controlAccentOutline",
29600
+ "--ggui-color-controlOutline",
29582
29601
  "--ggui-color-elevated",
29583
29602
  "--ggui-color-error",
29584
29603
  "--ggui-color-error-100",
@@ -30210,8 +30229,10 @@
30210
30229
  // paper
30211
30230
  sunken: { $value: "#292929", $type: "color" },
30212
30231
  // ink
30213
- onSunken: { $value: "#d9d9d9", $type: "color" },
30214
- // chrome
30232
+ // Muted text, and text on sunken grounds (ggui#1566): the kit's dark muted
30233
+ // value, 6.57:1 on container and 5.80:1 on sunken. Light's onSunken is ink-3,
30234
+ // the same tier, so dark now reads as light already does.
30235
+ onSunken: { $value: "#a3a3a9", $type: "color" },
30215
30236
  container: { $value: "#1f1f1f", $type: "color" },
30216
30237
  // ink
30217
30238
  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.26.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.26.0",
57
+ "@ggui-ai/wire": "0.26.0",
58
+ "@ggui-ai/protocol": "0.26.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@playwright/test": "^1.60.0",