@ggui-ai/dev-stack 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/dev-stack
2
2
 
3
- > Shared local dev engine for the [ggui](https://github.com/ggui-ai/ggui) protocol.
3
+ > Shared local dev engine for the [GGUI](https://github.com/ggui-ai/ggui) protocol.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@ggui-ai/dev-stack.svg)](https://www.npmjs.com/package/@ggui-ai/dev-stack)
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE)
@@ -20910,6 +20910,7 @@ function buildWireConfig(opts) {
20910
20910
  const result = validateEnvelope(actionSpec, envelope);
20911
20911
  if (!result.valid) {
20912
20912
  opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
20913
+ opts.onDispatchRefused?.({ actionName, violations: result.violations });
20913
20914
  return;
20914
20915
  }
20915
20916
  if (isOneShot && !spentOneShots.has(actionName)) {
@@ -21245,7 +21246,7 @@ function surfaceScopeCss(cls, name, bg, ink, accent, outlineOpen) {
21245
21246
  const inkAlias = "--ggui-surface-" + name + "-ink";
21246
21247
  const B = "var(" + bgAlias + ")";
21247
21248
  const I = "var(" + inkAlias + ")";
21248
- 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})}}`;
21249
+ 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})}}`;
21249
21250
  }
21250
21251
  var INVERTED_SCOPE_CSS = surfaceScopeCss(
21251
21252
  INVERTED_SCOPE_CLASS,
@@ -21744,7 +21745,8 @@ var variantStyles = {
21744
21745
  },
21745
21746
  secondary: {
21746
21747
  backgroundColor: "var(--ggui-color-sunken, #f4f4f5)",
21747
- color: "var(--ggui-color-onSunken, #52525b)",
21748
+ // The label reads at full strength; the chip marks the rank (ggui#1566).
21749
+ color: "var(--ggui-color-onContainer, #18181b)",
21748
21750
  border: "none"
21749
21751
  },
21750
21752
  outline: {
@@ -21753,13 +21755,15 @@ var variantStyles = {
21753
21755
  // label is the surface's own on-colour, never a brand-ladder stop
21754
21756
  // (ggui#1034): `primary-600` as the label read mid-grey on a monochrome
21755
21757
  // dark theme's ground — 2.41:1 / 3.5:1 on served reply chips. The brand
21756
- // stays on the border, where contrast is decorative.
21758
+ // stays on the border, and the border identifies the control, so it is the
21759
+ // first brand stop that clears 3:1 on the container (ggui#1494).
21757
21760
  color: "var(--ggui-color-onContainer, #18181b)",
21758
- border: "1px solid var(--ggui-color-primary-600, #0284c7)"
21761
+ border: "1px solid var(--ggui-color-controlAccentOutline, var(--ggui-color-primary-600, #0284c7))"
21759
21762
  },
21760
21763
  ghost: {
21761
21764
  backgroundColor: "transparent",
21762
- color: "var(--ggui-color-onSunken, #52525b)",
21765
+ // A dimmed label reads as disabled; ghost's rank is its missing chrome (ggui#1566).
21766
+ color: "var(--ggui-color-onContainer, #18181b)",
21763
21767
  border: "none"
21764
21768
  },
21765
21769
  danger: {
@@ -21821,7 +21825,7 @@ function Input({ label, placeholder, value, onChange, type = "text", error, help
21821
21825
  ...sizeStyles2[size],
21822
21826
  width: "100%",
21823
21827
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21824
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21828
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
21825
21829
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21826
21830
  color: "var(--ggui-color-onContainer, #18181b)",
21827
21831
  transition: `border-color ${motionVar.duration.base} ${motionVar.easing.standard}, box-shadow ${motionVar.duration.base} ${motionVar.easing.standard}`,
@@ -21851,7 +21855,7 @@ function TextArea({ label, placeholder, value, onChange, rows = 4, error, helper
21851
21855
  padding: "10px 12px",
21852
21856
  width: "100%",
21853
21857
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21854
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21858
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
21855
21859
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21856
21860
  color: "var(--ggui-color-onContainer, #18181b)",
21857
21861
  fontSize: "var(--ggui-font-size-sm, 14px)",
@@ -21879,17 +21883,25 @@ function Select({ label, value, onChange, options, placeholder, error, helperTex
21879
21883
  const messageId = `${uniqueId}-message`;
21880
21884
  const hasError = Boolean(error);
21881
21885
  const hasMessage = Boolean(error || helperText);
21886
+ const startsOnPlaceholder = Boolean(placeholder) && value === void 0 && rest.defaultValue === void 0;
21887
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react17.useState)(startsOnPlaceholder ? "" : typeof rest.defaultValue === "string" ? rest.defaultValue : void 0);
21888
+ const showingPlaceholder = Boolean(placeholder) && (value ?? uncontrolledValue) === "";
21882
21889
  return (0, import_jsx_runtime13.jsxs)("div", { className, style: { display: "flex", flexDirection: "column", gap: "var(--ggui-spacing-1, 4px)", ...style }, children: [label && (0, import_jsx_runtime13.jsxs)("label", { htmlFor: selectId, style: {
21883
21890
  fontSize: "var(--ggui-font-size-sm, 14px)",
21884
21891
  fontWeight: "var(--ggui-font-weight-medium, 500)",
21885
21892
  color: "var(--ggui-color-onSunken, #52525b)"
21886
- }, children: [label, required && (0, import_jsx_runtime13.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime13.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: {
21893
+ }, children: [label, required && (0, import_jsx_runtime13.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime13.jsxs)("select", { id: selectId, value, ...startsOnPlaceholder ? { defaultValue: "" } : {}, onChange: (e) => {
21894
+ if (value === void 0)
21895
+ setUncontrolledValue(e.target.value);
21896
+ onChange?.(e.target.value);
21897
+ }, disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
21887
21898
  ...sizeStyles3[size],
21888
21899
  width: "100%",
21889
21900
  borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21890
- border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21901
+ border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
21891
21902
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21892
- color: value ? "var(--ggui-color-onContainer, #18181b)" : "var(--ggui-color-onSunken, #52525b)",
21903
+ // Muted only while the placeholder is what shows (ggui#1566, #1569).
21904
+ color: showingPlaceholder ? "var(--ggui-color-onSunken, #52525b)" : "var(--ggui-color-onContainer, #18181b)",
21893
21905
  cursor: disabled ? "not-allowed" : "pointer",
21894
21906
  appearance: "none",
21895
21907
  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")`,
@@ -21931,7 +21943,7 @@ function Checkbox({ label, checked, onChange, disabled, description, indetermina
21931
21943
  width: "18px",
21932
21944
  height: "18px",
21933
21945
  borderRadius: "var(--ggui-shape-radius-sm, 4px)",
21934
- border: `2px solid ${checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21946
+ border: `2px solid ${checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
21935
21947
  backgroundColor: checked || indeterminate ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-sunken, #ffffff)",
21936
21948
  display: "flex",
21937
21949
  alignItems: "center",
@@ -21940,7 +21952,7 @@ function Checkbox({ label, checked, onChange, disabled, description, indetermina
21940
21952
  }, children: (checked || indeterminate) && (0, import_jsx_runtime14.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_runtime14.jsx)("path", { d: "M2.5 6H9.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) : (0, import_jsx_runtime14.jsx)("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), (label || description) && (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [label && (0, import_jsx_runtime14.jsx)("span", { style: {
21941
21953
  fontSize: "var(--ggui-font-size-sm, 14px)",
21942
21954
  fontWeight: "var(--ggui-font-weight-medium, 500)",
21943
- color: "var(--ggui-color-onSunken, #52525b)"
21955
+ color: "var(--ggui-color-onContainer, #18181b)"
21944
21956
  }, children: label }), description && (0, import_jsx_runtime14.jsx)("span", { style: {
21945
21957
  fontSize: "var(--ggui-font-size-xs, 12px)",
21946
21958
  color: "var(--ggui-color-onSunken, #52525b)"
@@ -21972,7 +21984,7 @@ function Toggle({ label, checked, onChange, disabled, size = "md", style, classN
21972
21984
  width: config.width,
21973
21985
  height: config.height,
21974
21986
  borderRadius: "9999px",
21975
- backgroundColor: checked ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)",
21987
+ backgroundColor: checked ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))",
21976
21988
  position: "relative",
21977
21989
  transition: `background-color ${motionVar.duration.base} ${motionVar.easing.standard}, box-shadow ${motionVar.duration.base} ${motionVar.easing.standard}`,
21978
21990
  flexShrink: 0,
@@ -21991,7 +22003,7 @@ function Toggle({ label, checked, onChange, disabled, size = "md", style, classN
21991
22003
  } }) }), label && (0, import_jsx_runtime15.jsx)("span", { style: {
21992
22004
  fontSize: "var(--ggui-font-size-sm, 14px)",
21993
22005
  fontWeight: "var(--ggui-font-weight-medium, 500)",
21994
- color: "var(--ggui-color-onSunken, #52525b)"
22006
+ color: "var(--ggui-color-onContainer, #18181b)"
21995
22007
  }, children: label })] });
21996
22008
  }
21997
22009
  function RadioGroup({ label, value, onChange, options, direction = "vertical", disabled, error, style, className }) {
@@ -22035,7 +22047,7 @@ function RadioGroup({ label, value, onChange, options, direction = "vertical", d
22035
22047
  width: "18px",
22036
22048
  height: "18px",
22037
22049
  borderRadius: "var(--ggui-shape-radius-full, 9999px)",
22038
- border: `2px solid ${isSelected ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-outline, #d4d4d8)"}`,
22050
+ border: `2px solid ${isSelected ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d4d4d8))"}`,
22039
22051
  backgroundColor: "var(--ggui-color-sunken, #ffffff)",
22040
22052
  display: "flex",
22041
22053
  alignItems: "center",
@@ -22049,7 +22061,7 @@ function RadioGroup({ label, value, onChange, options, direction = "vertical", d
22049
22061
  } }) })] }), (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [(0, import_jsx_runtime16.jsx)("span", { style: {
22050
22062
  fontSize: "var(--ggui-font-size-sm, 14px)",
22051
22063
  fontWeight: "var(--ggui-font-weight-medium, 500)",
22052
- color: "var(--ggui-color-onSunken, #52525b)"
22064
+ color: "var(--ggui-color-onContainer, #18181b)"
22053
22065
  }, children: option.label }), option.description && (0, import_jsx_runtime16.jsx)("span", { style: {
22054
22066
  fontSize: "var(--ggui-font-size-xs, 12px)",
22055
22067
  color: "var(--ggui-color-onSunken, #52525b)"
@@ -27064,7 +27076,9 @@ function Table({ columns, data, sortKey, sortDirection = "asc", onSort, striped
27064
27076
  width: "100%",
27065
27077
  borderCollapse: "collapse",
27066
27078
  fontSize: "var(--ggui-font-size-sm, 14px)",
27067
- color: "var(--ggui-color-onSunken, #52525b)"
27079
+ // Body cells are the data and inherit this (ggui#1566); the header and
27080
+ // caption set the muted tier themselves.
27081
+ color: "var(--ggui-color-onContainer, #18181b)"
27068
27082
  }, children: [caption && (0, import_jsx_runtime27.jsx)("caption", { style: {
27069
27083
  padding: cellPadding,
27070
27084
  fontSize: "var(--ggui-font-size-sm, 14px)",
@@ -27514,7 +27528,7 @@ function Accordion({ items, expandedKeys, onChange, multiple = false, variant =
27514
27528
  }, children: isExpanded && (0, import_jsx_runtime30.jsx)("div", { style: {
27515
27529
  padding: "0 var(--ggui-spacing-4, 16px) var(--ggui-spacing-4, 16px)",
27516
27530
  fontSize: "var(--ggui-font-size-sm, 14px)",
27517
- color: "var(--ggui-color-onSunken, #52525b)",
27531
+ color: "var(--ggui-color-onContainer, #18181b)",
27518
27532
  lineHeight: "var(--ggui-font-lineHeight-normal, 1.5)"
27519
27533
  }, children: item.content }) })] }, item.key);
27520
27534
  }) });
@@ -27622,7 +27636,7 @@ function SearchField({ value: controlledValue, onChange, onSearch, placeholder =
27622
27636
  paddingLeft: "36px",
27623
27637
  fontSize: size === "lg" ? "16px" : "14px",
27624
27638
  borderRadius: "6px",
27625
- border: `1px solid var(--ggui-color-outline, #d1d5db)`,
27639
+ border: `1px solid var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))`,
27626
27640
  backgroundColor: disabled ? "var(--ggui-color-sunken, #f9fafb)" : "var(--ggui-color-container, #ffffff)",
27627
27641
  color: "var(--ggui-color-onContainer, #111827)",
27628
27642
  outline: "none",
@@ -28757,14 +28771,17 @@ function ChatWindow({ messages, currentUserId, onSendMessage, loading, typing, p
28757
28771
  padding: "10px 14px",
28758
28772
  borderRadius: radius.lg,
28759
28773
  backgroundColor: isOwn ? "var(--ggui-color-primary-600, #0284c7)" : "var(--ggui-color-sunken, #f3f4f6)",
28760
- color: isOwn ? "var(--ggui-color-onPrimary, #ffffff)" : "var(--ggui-color-onSunken, #111827)"
28774
+ color: isOwn ? "var(--ggui-color-onPrimary, #ffffff)" : "var(--ggui-color-onContainer, #111827)"
28761
28775
  }, children: [(0, import_jsx_runtime48.jsx)("p", { style: { margin: 0, fontSize: fontSize.sm }, children: message.content }), (0, import_jsx_runtime48.jsxs)("div", { style: {
28762
28776
  display: "flex",
28763
28777
  alignItems: "center",
28764
28778
  gap: "4px",
28765
28779
  marginTop: "4px",
28766
28780
  fontSize: fontSize.xs,
28767
- opacity: 0.7
28781
+ // Own messages derive the muted line from onPrimary; the other
28782
+ // party's message now reads at onContainer, so its timestamp
28783
+ // takes the muted tier itself (ggui#1566).
28784
+ ...isOwn ? { opacity: 0.7 } : { color: "var(--ggui-color-onSunken, #52525b)" }
28768
28785
  }, children: [(0, import_jsx_runtime48.jsx)("span", { children: timestamp }), isOwn && message.status && (0, import_jsx_runtime48.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);
28769
28786
  }), typing && (0, import_jsx_runtime48.jsx)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: (0, import_jsx_runtime48.jsxs)("span", { style: { fontSize: fontSize.xs, color: "var(--ggui-color-neutral-500, #6b7280)" }, children: [typing.name, " is typing..."] }) }), (0, import_jsx_runtime48.jsx)("div", { ref: messagesEndRef })] }), (0, import_jsx_runtime48.jsxs)("div", { style: {
28770
28787
  padding: "12px 16px",
@@ -28774,7 +28791,7 @@ function ChatWindow({ messages, currentUserId, onSendMessage, loading, typing, p
28774
28791
  }, children: [(0, import_jsx_runtime48.jsx)("input", { type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder, style: {
28775
28792
  flex: 1,
28776
28793
  padding: "10px 14px",
28777
- border: `1px solid var(--ggui-color-outline, #d1d5db)`,
28794
+ border: `1px solid var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))`,
28778
28795
  borderRadius: radius.full,
28779
28796
  fontSize: fontSize.sm,
28780
28797
  outline: "none"
@@ -28879,7 +28896,7 @@ function FileUploader({ files = [], onFilesSelected, onFileRemove, accept, multi
28879
28896
  setIsDragOver(false);
28880
28897
  };
28881
28898
  return (0, import_jsx_runtime50.jsxs)("div", { className, style: { ...style }, children: [(0, import_jsx_runtime50.jsxs)("div", { onClick: () => !disabled && inputRef.current?.click(), onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, style: {
28882
- border: `2px dashed ${isDragOver ? "var(--ggui-color-primary-400, #38bdf8)" : "var(--ggui-color-outline, #d1d5db)"}`,
28899
+ border: `2px dashed ${isDragOver ? "var(--ggui-color-primary-400, #38bdf8)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))"}`,
28883
28900
  borderRadius: radius.lg,
28884
28901
  padding: "32px",
28885
28902
  textAlign: "center",
@@ -29345,7 +29362,7 @@ function Hero({ heading, description, primaryAction, secondaryAction, media, ali
29345
29362
  fontWeight: fontWeight.semibold,
29346
29363
  color: showOverlay ? "#ffffff" : "var(--ggui-color-onContainer, #374151)",
29347
29364
  backgroundColor: "transparent",
29348
- border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-outline, #d1d5db)"}`,
29365
+ border: `1px solid ${showOverlay ? "rgba(255,255,255,0.3)" : "var(--ggui-color-controlOutline, var(--ggui-color-outline, #d1d5db))"}`,
29349
29366
  borderRadius: "8px",
29350
29367
  cursor: "pointer",
29351
29368
  transition: "all 0.15s",
@@ -29467,6 +29484,8 @@ var consumed_tokens_manifest_default = {
29467
29484
  version: 2,
29468
29485
  tokens: [
29469
29486
  "--ggui-color-container",
29487
+ "--ggui-color-controlAccentOutline",
29488
+ "--ggui-color-controlOutline",
29470
29489
  "--ggui-color-elevated",
29471
29490
  "--ggui-color-error",
29472
29491
  "--ggui-color-error-100",
@@ -29820,11 +29839,11 @@ function readableOutline(bg, ink) {
29820
29839
  }
29821
29840
  return ink;
29822
29841
  }
29823
- function readableAccent(V, surface, ink) {
29842
+ function readableAccent(V, surface, ink, floor = 4.5) {
29824
29843
  const light = contrastRatio("#000000", surface) >= contrastRatio("#ffffff", surface);
29825
29844
  for (const stop of light ? ACCENT_WALK.light : ACCENT_WALK.dark) {
29826
29845
  const hex = V[`--ggui-color-primary-${stop}`];
29827
- if (hex !== void 0 && contrastRatio(hex, surface) >= 4.5)
29846
+ if (hex !== void 0 && contrastRatio(hex, surface) >= floor)
29828
29847
  return hex;
29829
29848
  }
29830
29849
  return ink;
@@ -29906,6 +29925,8 @@ function deriveThemeVariables(doc, mode, options = {}) {
29906
29925
  V["--ggui-color-onInverted"] = container;
29907
29926
  V["--ggui-color-inverseOutline"] = readableOutline(onContainer, container);
29908
29927
  V["--ggui-color-heroOutline"] = readableOutline(V["--ggui-color-heroGround"], V["--ggui-color-onHeroGround"]);
29928
+ V["--ggui-color-controlOutline"] = readableOutline(container, onContainer);
29929
+ V["--ggui-color-controlAccentOutline"] = readableAccent(V, container, onContainer, 3);
29909
29930
  const family = doc.font.family;
29910
29931
  const sans = tokenValue(family.sans) ?? fontFamily.sans;
29911
29932
  V["--ggui-font-family-sans"] = sans;
@@ -30543,8 +30564,10 @@ var gguiDark = {
30543
30564
  // paper
30544
30565
  sunken: { $value: "#292929", $type: "color" },
30545
30566
  // ink
30546
- onSunken: { $value: "#d9d9d9", $type: "color" },
30547
- // chrome
30567
+ // Muted text, and text on sunken grounds (ggui#1566): the kit's dark muted
30568
+ // value, 6.57:1 on container and 5.80:1 on sunken. Light's onSunken is ink-3,
30569
+ // the same tier, so dark now reads as light already does.
30570
+ onSunken: { $value: "#a3a3a9", $type: "color" },
30548
30571
  container: { $value: "#1f1f1f", $type: "color" },
30549
30572
  // ink
30550
30573
  onContainer: { $value: "#f4f3ed", $type: "color" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/dev-stack",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Shared local dev engine for the ggui protocol: local UI registry discovery, compile-on-demand, filesystem watcher with SSE, and a read-only HTTP surface for dev tooling. Framework-neutral — agent runtime supervision plugs in via an adapter seam, so no agent SDK is hardcoded.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -28,9 +28,9 @@
28
28
  "dependencies": {
29
29
  "chokidar": "^4.0.3",
30
30
  "esbuild": "^0.28.2",
31
- "@ggui-ai/agent-runtime": "0.25.0",
32
- "@ggui-ai/project-config": "0.25.0",
33
- "@ggui-ai/ui-registry": "0.25.0"
31
+ "@ggui-ai/agent-runtime": "0.26.0",
32
+ "@ggui-ai/ui-registry": "0.26.0",
33
+ "@ggui-ai/project-config": "0.26.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^24.0.0",
@@ -40,9 +40,9 @@
40
40
  "react-dom": "^19.0.0",
41
41
  "typescript": "^5.0.0",
42
42
  "vitest": "^3.2.6",
43
- "@ggui-ai/design": "0.25.0",
44
- "@ggui-ai/mcp-apps-react": "0.25.0",
45
- "@ggui-ai/wire": "0.25.0"
43
+ "@ggui-ai/design": "0.26.0",
44
+ "@ggui-ai/mcp-apps-react": "0.26.0",
45
+ "@ggui-ai/wire": "0.26.0"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",