@ggui-ai/dev-stack 0.18.0 → 0.19.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.
@@ -20526,6 +20526,7 @@ function useAction(actionName) {
20526
20526
  if (onDispatchSuppressed) {
20527
20527
  try {
20528
20528
  onDispatchSuppressed({
20529
+ reason: "duplicate-dispatch",
20529
20530
  actionName,
20530
20531
  payloadSignature: decision.signature,
20531
20532
  payload: data,
@@ -20798,11 +20799,26 @@ function buildWireConfig(opts) {
20798
20799
  return internalSeq;
20799
20800
  });
20800
20801
  const validateEnvelope = opts.validateEnvelope ?? validateOutboundActionEnvelope;
20802
+ const spentOneShots = /* @__PURE__ */ new Set();
20801
20803
  return {
20802
20804
  app: opts.app,
20803
20805
  render: opts.render,
20804
20806
  auth: opts.auth,
20807
+ ...opts.onDispatchSuppressed !== void 0 ? { onDispatchSuppressed: opts.onDispatchSuppressed } : {},
20805
20808
  dispatch: (actionName, data) => {
20809
+ const actionSpec = opts.getActiveActionSpec();
20810
+ const isOneShot = actionSpec?.[actionName]?.oneShot === true;
20811
+ if (isOneShot && spentOneShots.has(actionName)) {
20812
+ console.warn(`[ggui] dispatch('${actionName}') suppressed: this action is declared oneShot and already fired for this render. A second gesture is NOT sent to the agent \u2014 the submitted control is spent for the render's lifetime. If the action is meant to repeat, remove \`oneShot\` from its contract entry.`);
20813
+ opts.onDispatchSuppressed?.({
20814
+ reason: "one-shot-spent",
20815
+ actionName,
20816
+ payloadSignature: payloadSignature(actionName, data),
20817
+ payload: data,
20818
+ suppressedAt: Date.now()
20819
+ });
20820
+ return;
20821
+ }
20806
20822
  const envelope = buildActionEnvelope({
20807
20823
  sessionId: opts.render.sessionId,
20808
20824
  type: "data:submit",
@@ -20812,11 +20828,13 @@ function buildWireConfig(opts) {
20812
20828
  },
20813
20829
  clientSeq: nextClientSeq()
20814
20830
  });
20815
- const result = validateEnvelope(opts.getActiveActionSpec(), envelope);
20831
+ const result = validateEnvelope(actionSpec, envelope);
20816
20832
  if (!result.valid) {
20817
20833
  opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
20818
20834
  return;
20819
20835
  }
20836
+ if (isOneShot)
20837
+ spentOneShots.add(actionName);
20820
20838
  opts.emitEnvelope(envelope);
20821
20839
  },
20822
20840
  subscribe: (channelName, handler) => {
@@ -21645,7 +21663,7 @@ function Button({ children, variant = "primary", size = "md", fullWidth, loading
21645
21663
  alignItems: "center",
21646
21664
  justifyContent: "center",
21647
21665
  gap: "var(--ggui-spacing-2, 8px)",
21648
- borderRadius: "var(--ggui-shape-radius-md, 8px)",
21666
+ borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21649
21667
  fontWeight: "var(--ggui-font-weight-medium, 500)",
21650
21668
  cursor: isDisabled ? "not-allowed" : "pointer",
21651
21669
  opacity: isDisabled ? 0.5 : 1,
@@ -21687,7 +21705,7 @@ function Input({ label, placeholder, value, onChange, type = "text", error, help
21687
21705
  }, 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.jsx)("input", { id: inputId, type, placeholder, value, onChange: (e) => onChange?.(e.target.value), disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
21688
21706
  ...sizeStyles2[size],
21689
21707
  width: "100%",
21690
- borderRadius: "var(--ggui-shape-radius-md, 8px)",
21708
+ borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21691
21709
  border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21692
21710
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21693
21711
  color: "var(--ggui-color-onContainer, #18181b)",
@@ -21717,7 +21735,7 @@ function TextArea({ label, placeholder, value, onChange, rows = 4, error, helper
21717
21735
  }, children: [label, required && (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime12.jsx)("textarea", { id: textareaId, placeholder, value, onChange: (e) => onChange?.(e.target.value), rows, disabled, required, maxLength, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
21718
21736
  padding: "10px 12px",
21719
21737
  width: "100%",
21720
- borderRadius: "var(--ggui-shape-radius-md, 8px)",
21738
+ borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21721
21739
  border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21722
21740
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21723
21741
  color: "var(--ggui-color-onContainer, #18181b)",
@@ -21753,7 +21771,7 @@ function Select({ label, value, onChange, options, placeholder, error, helperTex
21753
21771
  }, 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: {
21754
21772
  ...sizeStyles3[size],
21755
21773
  width: "100%",
21756
- borderRadius: "var(--ggui-shape-radius-md, 8px)",
21774
+ borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
21757
21775
  border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
21758
21776
  backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
21759
21777
  color: value ? "var(--ggui-color-onContainer, #18181b)" : "var(--ggui-color-onSunken, #52525b)",
@@ -29431,6 +29449,7 @@ var consumed_tokens_manifest_default = {
29431
29449
  "--ggui-font-weight-normal",
29432
29450
  "--ggui-font-weight-semibold",
29433
29451
  "--ggui-letter-spacing-heading",
29452
+ "--ggui-shape-radius-control",
29434
29453
  "--ggui-shape-radius-full",
29435
29454
  "--ggui-shape-radius-lg",
29436
29455
  "--ggui-shape-radius-md",
@@ -29591,6 +29610,24 @@ function tokenValue(t) {
29591
29610
  return void 0;
29592
29611
  return /^#[0-9a-fA-F]{3,6}$/.test(v) ? `#${parseHex(v).map((n) => n.toString(16).padStart(2, "0")).join("")}` : v;
29593
29612
  }
29613
+ function describeFound(t) {
29614
+ if (t === null)
29615
+ return "null";
29616
+ if (typeof t !== "object")
29617
+ return `${typeof t} ${JSON.stringify(t)}`;
29618
+ return "$value" in t ? "object with a non-string, non-number $value" : "object without $value";
29619
+ }
29620
+ var defaultDiagnostic = (d) => {
29621
+ console.warn(`[ggui-design] theme member unreadable at ${d.path} \u2014 found ${d.found}; expected a DTCG token ({ $value }). The member was ignored and the fallback used.`);
29622
+ };
29623
+ function readToken(t, path, report) {
29624
+ if (t === void 0)
29625
+ return void 0;
29626
+ const v = tokenValue(t);
29627
+ if (v === void 0)
29628
+ report({ kind: "member-unreadable", path, found: describeFound(t) });
29629
+ return v;
29630
+ }
29594
29631
  function stated(group, key) {
29595
29632
  return tokenValue(group?.[key]);
29596
29633
  }
@@ -29651,7 +29688,15 @@ function parseSize(value) {
29651
29688
  const m = /^([0-9.]+)\s*(rem|px|em)$/.exec(value.trim());
29652
29689
  return m ? { n: Number(m[1]), unit: m[2] } : void 0;
29653
29690
  }
29654
- function deriveThemeVariables(doc, mode) {
29691
+ function deriveThemeVariables(doc, mode, options = {}) {
29692
+ const reported = /* @__PURE__ */ new Set();
29693
+ const sink = options.onDiagnostic ?? defaultDiagnostic;
29694
+ const report = (d) => {
29695
+ if (reported.has(d.path))
29696
+ return;
29697
+ reported.add(d.path);
29698
+ sink(d);
29699
+ };
29655
29700
  const c = Object.fromEntries(Object.entries(doc.color));
29656
29701
  const single = (key) => tokenValue(c[key]);
29657
29702
  const group = (key) => c[key];
@@ -29731,14 +29776,14 @@ function deriveThemeVariables(doc, mode) {
29731
29776
  for (const lh of Object.keys(lineHeight))
29732
29777
  V[`--ggui-font-lineHeight-${lh}`] = String(lineHeight[lh]);
29733
29778
  const ramp = doc.font.ramp;
29734
- const base = ramp ? parseSize(tokenValue(ramp.base) ?? "") : void 0;
29735
- const ratio = ramp ? Number(tokenValue(ramp.ratio)) : NaN;
29779
+ const base = ramp ? parseSize(readToken(ramp.base, "font.ramp.base", report) ?? "") : void 0;
29780
+ const ratio = ramp ? Number(readToken(ramp.ratio, "font.ramp.ratio", report)) : NaN;
29736
29781
  for (const [stop, exp] of Object.entries(SIZE_EXP)) {
29737
29782
  V[`--ggui-font-size-${stop}`] = base && Number.isFinite(ratio) && ratio > 0 ? `${Math.round(base.n * ratio ** exp * 1e3) / 1e3}${base.unit}` : fontSize[stop];
29738
29783
  }
29739
29784
  const typeScale = doc.typeScale;
29740
29785
  if (typeScale !== void 0) {
29741
- const bodySize = parseSize(tokenValue(typeScale.body?.size) ?? "");
29786
+ const bodySize = parseSize(readToken(typeScale.body?.size, "typeScale.body.size", report) ?? "");
29742
29787
  if (bodySize !== void 0) {
29743
29788
  const r = Number.isFinite(ratio) && ratio > 0 ? ratio : 1.25;
29744
29789
  for (const [stop, exp] of Object.entries(SIZE_EXP)) {
@@ -29746,28 +29791,28 @@ function deriveThemeVariables(doc, mode) {
29746
29791
  }
29747
29792
  }
29748
29793
  for (const [role, stop] of Object.entries(ROLE_STOP)) {
29749
- const size = tokenValue(typeScale[role]?.size);
29794
+ const size = readToken(typeScale[role]?.size, `typeScale.${role}.size`, report);
29750
29795
  if (size !== void 0)
29751
29796
  V[`--ggui-font-size-${stop}`] = size;
29752
29797
  }
29753
- const headingWeight = tokenValue(typeScale.h1?.weight);
29798
+ const headingWeight = readToken(typeScale.h1?.weight, "typeScale.h1.weight", report);
29754
29799
  if (headingWeight !== void 0)
29755
29800
  V["--ggui-font-weight-heading"] = headingWeight;
29756
- const bodyWeight = tokenValue(typeScale.body?.weight);
29801
+ const bodyWeight = readToken(typeScale.body?.weight, "typeScale.body.weight", report);
29757
29802
  if (bodyWeight !== void 0)
29758
29803
  V["--ggui-font-weight-normal"] = bodyWeight;
29759
- const headingTracking = tokenValue(typeScale.h1?.tracking);
29804
+ const headingTracking = readToken(typeScale.h1?.tracking, "typeScale.h1.tracking", report);
29760
29805
  if (headingTracking !== void 0)
29761
29806
  V["--ggui-letter-spacing-heading"] = headingTracking;
29762
- const bodyTracking = tokenValue(typeScale.body?.tracking);
29807
+ const bodyTracking = readToken(typeScale.body?.tracking, "typeScale.body.tracking", report);
29763
29808
  if (bodyTracking !== void 0)
29764
29809
  V["--ggui-letter-spacing-body"] = bodyTracking;
29765
- const bodyLeading = tokenValue(typeScale.body?.leading);
29810
+ const bodyLeading = readToken(typeScale.body?.leading, "typeScale.body.leading", report);
29766
29811
  if (bodyLeading !== void 0)
29767
29812
  V["--ggui-font-lineHeight-normal"] = bodyLeading;
29768
29813
  }
29769
29814
  const sp = doc.spacing;
29770
- const rhythmBase = doc.rhythm ? parseSize(tokenValue(doc.rhythm.base) ?? "") : void 0;
29815
+ const rhythmBase = doc.rhythm ? parseSize(readToken(doc.rhythm.base, "rhythm.base", report) ?? "") : void 0;
29771
29816
  const onRhythm = (layer1) => {
29772
29817
  if (rhythmBase === void 0)
29773
29818
  return void 0;
@@ -29784,6 +29829,7 @@ function deriveThemeVariables(doc, mode) {
29784
29829
  const rad = doc.shape?.radius;
29785
29830
  for (const [key, value] of Object.entries(radius))
29786
29831
  V[`--ggui-shape-radius-${key}`] = stated(rad, key) ?? String(value);
29832
+ V["--ggui-shape-radius-control"] = stated(rad, "control") ?? V["--ggui-shape-radius-md"];
29787
29833
  const sh = doc.shape?.shadow;
29788
29834
  for (const [key, value] of Object.entries(shadow))
29789
29835
  V[`--ggui-shape-shadow-${key}`] = stated(sh, key) ?? String(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/dev-stack",
3
- "version": "0.18.0",
3
+ "version": "0.19.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.18.0",
32
- "@ggui-ai/project-config": "0.18.0",
33
- "@ggui-ai/ui-registry": "0.18.0"
31
+ "@ggui-ai/agent-runtime": "0.19.0",
32
+ "@ggui-ai/project-config": "0.19.0",
33
+ "@ggui-ai/ui-registry": "0.19.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.18.0",
44
- "@ggui-ai/mcp-apps-react": "0.18.0",
45
- "@ggui-ai/wire": "0.18.0"
43
+ "@ggui-ai/design": "0.19.0",
44
+ "@ggui-ai/mcp-apps-react": "0.19.0",
45
+ "@ggui-ai/wire": "0.19.0"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",