@1agh/maude 0.46.0 → 0.47.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.
Files changed (56) hide show
  1. package/apps/studio/acp/bootstrap-brief.ts +8 -0
  2. package/apps/studio/acp/bridge.ts +113 -5
  3. package/apps/studio/annotations-layer.tsx +42 -0
  4. package/apps/studio/api.ts +417 -2
  5. package/apps/studio/bin/_agent-browser-safe-config.json +1 -0
  6. package/apps/studio/bin/_agent-browser-safe.mjs +228 -0
  7. package/apps/studio/bin/_agent-browser-safe.test.mjs +165 -0
  8. package/apps/studio/bin/_curl-local.mjs +349 -0
  9. package/apps/studio/bin/_curl-local.test.mjs +280 -0
  10. package/apps/studio/bin/agent-browser-safe.sh +29 -0
  11. package/apps/studio/bin/curl-local.sh +28 -0
  12. package/apps/studio/canvas-cursors.ts +6 -0
  13. package/apps/studio/canvas-edit.ts +678 -11
  14. package/apps/studio/canvas-icons.tsx +13 -0
  15. package/apps/studio/canvas-lib.tsx +3 -0
  16. package/apps/studio/canvas-shell.tsx +646 -26
  17. package/apps/studio/client/app.jsx +898 -52
  18. package/apps/studio/client/panels/ChatPanel.jsx +47 -2
  19. package/apps/studio/client/styles/3-shell-maude.css +39 -0
  20. package/apps/studio/contextual-toolbar.tsx +5 -3
  21. package/apps/studio/dist/client.bundle.js +1103 -1103
  22. package/apps/studio/dist/comment-mount.js +2 -2
  23. package/apps/studio/dist/styles.css +1 -1
  24. package/apps/studio/grid-track-handles.ts +179 -0
  25. package/apps/studio/handoff.ts +35 -0
  26. package/apps/studio/http.ts +118 -0
  27. package/apps/studio/input-router.tsx +73 -17
  28. package/apps/studio/test/acp-session-allowed-tools.test.ts +107 -11
  29. package/apps/studio/test/browse-posture.test.tsx +107 -0
  30. package/apps/studio/test/canvas-hide-chrome.test.ts +58 -0
  31. package/apps/studio/test/canvas-meta-api.test.ts +70 -0
  32. package/apps/studio/test/canvas-origin-gate.test.ts +4 -0
  33. package/apps/studio/test/comment-mount.test.ts +2 -1
  34. package/apps/studio/test/component-map.test.ts +48 -0
  35. package/apps/studio/test/convert-to-absolute.test.ts +333 -0
  36. package/apps/studio/test/detach-component.test.ts +94 -0
  37. package/apps/studio/test/edit-scope-api.test.ts +8 -4
  38. package/apps/studio/test/element-structural-api.test.ts +74 -0
  39. package/apps/studio/test/element-structural-edit.test.ts +113 -0
  40. package/apps/studio/test/grid-track-handles.test.ts +160 -0
  41. package/apps/studio/test/handoff.test.ts +48 -0
  42. package/apps/studio/test/input-router.test.ts +82 -8
  43. package/apps/studio/test/layers-synthetic-groups.test.ts +96 -0
  44. package/apps/studio/test/pdf-print-boxes.test.ts +54 -0
  45. package/apps/studio/test/use-tool-mode.test.tsx +10 -2
  46. package/apps/studio/tool-palette.tsx +3 -1
  47. package/apps/studio/use-canvas-media-drop.tsx +126 -0
  48. package/apps/studio/use-element-resize.tsx +3 -1
  49. package/apps/studio/use-grid-track-handles.tsx +364 -0
  50. package/apps/studio/use-keyboard-discipline.tsx +15 -0
  51. package/apps/studio/use-tool-mode.tsx +30 -3
  52. package/apps/studio/web-overlay-content.tsx +52 -0
  53. package/apps/studio/whats-new.json +27 -0
  54. package/cli/commands/design.mjs +16 -0
  55. package/package.json +8 -8
  56. package/plugins/design/templates/_shell.html +4 -0
@@ -152,6 +152,17 @@ const SUGGESTIONS = [
152
152
  '/design:new Pricing "a 3-tier pricing page"',
153
153
  ];
154
154
 
155
+ // DDR-185 — shared "did a genuinely new item arrive" check for the
156
+ // permission/elicitation effects below: both need the identical shape (diff
157
+ // the incoming list's ids against the previous snapshot, fire onNew only for
158
+ // a real arrival, never for a resolution/removal), just against a different
159
+ // ref and a different onNew callback.
160
+ function fireOnNewArrival(prevIdsRef, list, onNew) {
161
+ const hasNew = list.some((item) => !prevIdsRef.current.has(item.id));
162
+ prevIdsRef.current = new Set(list.map((item) => item.id));
163
+ if (hasNew) onNew?.();
164
+ }
165
+
155
166
  function safeStorageGet(key, fallback) {
156
167
  try {
157
168
  return localStorage.getItem(key) ?? fallback;
@@ -1486,6 +1497,8 @@ function ChatThread({
1486
1497
  designRel,
1487
1498
  transcriptView,
1488
1499
  onSetTranscriptView,
1500
+ onPermissionRequest,
1501
+ onElicitationRequest,
1489
1502
  }) {
1490
1503
  // Live session capabilities (feature-acp-panel-dynamic-claude-code-
1491
1504
  // capabilities) — `caps` is THIS chat's own connection's mode roster +
@@ -1619,9 +1632,29 @@ function ChatThread({
1619
1632
  // Post-turn-end continuation (the tail the client used to drop — RCA F2).
1620
1633
  const [bgParts, setBgParts] = useState([]);
1621
1634
  useEffect(() => conn.onBackground(setBgParts), [conn]);
1635
+ // DDR-185 — surface a "Maude needs your input" attention ping to the parent
1636
+ // (app.jsx) whenever a GENUINELY NEW permission/elicitation request arrives,
1637
+ // not on every list churn (a request resolving/timing out also updates the
1638
+ // list — that must NOT re-fire the notification). Read via a ref, not the
1639
+ // effect's own dependency array, so a parent re-render that produces a new
1640
+ // callback identity doesn't force conn.onPermission/onElicitation to
1641
+ // re-subscribe.
1642
+ const attentionCbRef = useRef({ onPermissionRequest, onElicitationRequest });
1643
+ useEffect(() => {
1644
+ attentionCbRef.current = { onPermissionRequest, onElicitationRequest };
1645
+ }, [onPermissionRequest, onElicitationRequest]);
1646
+
1622
1647
  // Pending permission requests (Milestone B) — one card at a time, oldest first.
1623
1648
  const [pendingPermissions, setPendingPermissions] = useState([]);
1624
- useEffect(() => conn.onPermission(setPendingPermissions), [conn]);
1649
+ const prevPermissionIdsRef = useRef(new Set());
1650
+ useEffect(
1651
+ () =>
1652
+ conn.onPermission((list) => {
1653
+ fireOnNewArrival(prevPermissionIdsRef, list, attentionCbRef.current.onPermissionRequest);
1654
+ setPendingPermissions(list);
1655
+ }),
1656
+ [conn]
1657
+ );
1625
1658
  const activePermission = pendingPermissions[0] || null;
1626
1659
  const respondPermission = useCallback(
1627
1660
  (decision) => activePermission && conn.respondPermission(activePermission.id, decision),
@@ -1630,7 +1663,15 @@ function ChatThread({
1630
1663
  // Pending elicitation requests (feature-acp-ask-user-question) — same
1631
1664
  // one-at-a-time, oldest-first treatment as pendingPermissions above.
1632
1665
  const [pendingElicitations, setPendingElicitations] = useState([]);
1633
- useEffect(() => conn.onElicitation(setPendingElicitations), [conn]);
1666
+ const prevElicitationIdsRef = useRef(new Set());
1667
+ useEffect(
1668
+ () =>
1669
+ conn.onElicitation((list) => {
1670
+ fireOnNewArrival(prevElicitationIdsRef, list, attentionCbRef.current.onElicitationRequest);
1671
+ setPendingElicitations(list);
1672
+ }),
1673
+ [conn]
1674
+ );
1634
1675
  const activeElicitation = pendingElicitations[0] || null;
1635
1676
  const respondElicitation = useCallback(
1636
1677
  (response) => activeElicitation && conn.respondElicitation(activeElicitation.id, response),
@@ -1731,6 +1772,8 @@ export default function ChatPanel({
1731
1772
  hidden = false,
1732
1773
  onBusyChange,
1733
1774
  onFinished,
1775
+ onPermissionRequest,
1776
+ onElicitationRequest,
1734
1777
  }) {
1735
1778
  // Model/effort/mode picks — persisted across sessions per config id (never a
1736
1779
  // fixed model/effort pair; see loadPersistedPicks). Read live by each open
@@ -2229,6 +2272,8 @@ export default function ChatPanel({
2229
2272
  designRel={designRel}
2230
2273
  transcriptView={transcriptView}
2231
2274
  onSetTranscriptView={setTranscriptView}
2275
+ onPermissionRequest={onPermissionRequest}
2276
+ onElicitationRequest={onElicitationRequest}
2232
2277
  />
2233
2278
  );
2234
2279
  })
@@ -404,6 +404,10 @@
404
404
  .st-rp-hd { font-family: var(--font-mono); font-size: var(--type-xs); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--fg-2); }
405
405
  /* Stage H (INV-3) edit-scope strip — local (this element) vs shared (edits N places). */
406
406
  .st-scope { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); font-size: var(--type-xs); border-bottom: 1px solid var(--border-subtle); flex: none; animation: st-pop var(--dur-soft) var(--ease-out); }
407
+ /* feature-4 detach-component — inline action on the Shared badge. */
408
+ .st-scope-detach { margin-left: auto; padding: 1px 8px; border-radius: var(--radius-xs); font-size: var(--type-xs); background: var(--bg-2); color: var(--fg-1); border: 1px solid var(--border-subtle); cursor: pointer; }
409
+ .st-scope-detach:hover { background: var(--bg-3); color: var(--fg-0); }
410
+ .st-scope-detach:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
407
411
  .st-scope-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
408
412
  .st-scope--local { color: var(--fg-2); }
409
413
  .st-scope--local .st-scope-dot { background: var(--fg-3); }
@@ -664,6 +668,30 @@
664
668
  .st-layer--row:hover .st-layer-eye, .st-layer--row:focus-within .st-layer-eye { opacity: 1; }
665
669
  .st-layer-eye:hover { color: var(--fg-0); background: var(--bg-3); }
666
670
  .st-layer-eye:focus-visible { outline: none; color: var(--accent); box-shadow: 0 0 0 2px var(--accent-tint); }
671
+ /* feature-4 T7 — synthetic group row (unstamped wrapper): a muted, italic
672
+ structural stand-in. Not selectable, not draggable, no eye — it exists only
673
+ to restore real nesting in the tree. The folder icon reads as "container". */
674
+ .st-layer.is-group { cursor: default; color: var(--fg-3); }
675
+ .st-layer.is-group .st-layer-label { font-style: italic; }
676
+ .st-layer.is-group .st-layer-type { opacity: 0.7; }
677
+ .st-layer.is-group:hover { background: var(--bg-2); }
678
+ /* feature-4 T7a — component-instance rows (Figma purple). ◆ = the component's
679
+ own frame root, ◇ = an inner member. The purple is deliberately Figma-adjacent
680
+ so the vocabulary transfers. */
681
+ .st-layer.is-instance { color: var(--layer-instance, #a78bfa); }
682
+ .st-layer.is-instance .st-layer-label { color: var(--layer-instance, #a78bfa); }
683
+ .st-layer.is-instance .st-layer-type { color: var(--layer-instance, #a78bfa); opacity: 0.75; }
684
+ .st-layer-inst-glyph { flex: none; width: 14px; text-align: center; font-size: 10px; color: var(--layer-instance, #a78bfa); }
685
+ .st-layer.is-sel.is-instance { box-shadow: inset 2px 0 0 var(--layer-instance, #a78bfa); }
686
+ /* feature-4 T7b — locked rows: padlock stays visible when locked; dimmed label. */
687
+ .st-layer-lock { flex: none; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 0; color: var(--fg-3); cursor: pointer; padding: 0; border-radius: var(--radius-xs); opacity: 0; }
688
+ .st-layer--row:hover .st-layer-lock, .st-layer--row:focus-within .st-layer-lock { opacity: 1; }
689
+ .st-layer-lock:hover { color: var(--fg-0); background: var(--bg-3); }
690
+ .st-layer-lock:focus-visible { outline: none; color: var(--accent); box-shadow: 0 0 0 2px var(--accent-tint); }
691
+ .st-layer.is-locked .st-layer-lock { opacity: 1; color: var(--fg-1); }
692
+ .st-layer.is-locked .st-layer-label { opacity: 0.6; }
693
+ /* feature-4 T7c — inline rename input matches the row's mono type. */
694
+ .st-layer-rename { flex: 1 1 auto; min-width: 40px; font: inherit; color: var(--fg-0); background: var(--bg-1); border: 1px solid var(--accent); border-radius: var(--radius-xs); padding: 0 4px; outline: none; }
667
695
  /* a hidden layer stays dim + keeps its eye visible (so it can be re-shown) */
668
696
  .st-layer.is-hidden { opacity: 0.45; }
669
697
  .st-layer.is-hidden .st-layer-eye { opacity: 1; color: var(--fg-2); }
@@ -1141,6 +1169,17 @@ body.st-scrubbing, body.st-scrubbing * { cursor: ew-resize !important; user-sele
1141
1169
  .st-cp-makeflex:hover:not(:disabled) { border-color: var(--accent); color: var(--fg-0); }
1142
1170
  .st-cp-makeflex:disabled { opacity: 0.5; cursor: default; }
1143
1171
 
1172
+ /* feature-3-web-artboards T5 — Grid section's track-list editor. */
1173
+ .st-cp-gridtracks { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-1) 0; }
1174
+ .st-cp-gridtracks + .st-cp-gridtracks { margin-top: var(--space-2); border-top: 1px solid var(--border-subtle); padding-top: var(--space-2); }
1175
+ .st-cp-gridtracks-hd { display: flex; align-items: center; justify-content: space-between; }
1176
+ .st-cp-gridtracks-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--fg-2); }
1177
+ .st-cp-gridtracks-add { min-height: 22px; padding: 0 var(--space-2); font-size: 10px; }
1178
+ .st-cp-gridtrack-row { display: flex; align-items: center; gap: var(--space-1); }
1179
+ .st-cp-gridtrack-row .st-cp-nsel { flex: 1 1 auto; }
1180
+ .st-cp-gridtrack-remove { flex: 0 0 auto; min-height: 22px; min-width: 22px; padding: 0; justify-content: center; color: var(--fg-2); }
1181
+ .st-cp-gridtrack-remove:hover:not(:disabled) { color: var(--status-error); }
1182
+
1144
1183
  /* provenance legend */
1145
1184
  .st-cp-legend { display: flex; flex-wrap: wrap; gap: var(--space-3); padding-top: var(--space-1); border-top: 1px solid var(--border-subtle); }
1146
1185
  .st-cp-legend span { display: inline-flex; align-items: center; gap: var(--space-1); font-family: var(--font-mono); font-size: 10px; color: var(--fg-2); }
@@ -170,9 +170,11 @@ interface AlignTarget {
170
170
  /**
171
171
  * Resolve every selection to its current world-space box, gated on
172
172
  * absolute/fixed positioning — align/distribute/tidy-up write `left`/`top`,
173
- * which is only meaningful for out-of-flow elements (this codebase
174
- * deliberately never does an implicit convert-to-absolute-on-drag; see the
175
- * Stage-D dogfood notes). Returns `null` (all-or-nothing) if any selection
173
+ * which is only meaningful for out-of-flow elements. This lane still never does
174
+ * an IMPLICIT convert-to-absolute-on-drag (see the Stage-D dogfood notes); the
175
+ * EXPLICIT context-menu "Convert children to absolute position" (feature-4 T8,
176
+ * DDR-188) is the opt-in path to flatten a flow layout first. Returns `null`
177
+ * (all-or-nothing) if any selection
176
178
  * doesn't resolve, or isn't out-of-flow, or has a non-numeric `left`/`top`
177
179
  * (e.g. positioned via `right`/`bottom` instead — a known v1 limit).
178
180
  */