@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.
- package/apps/studio/acp/bootstrap-brief.ts +8 -0
- package/apps/studio/acp/bridge.ts +113 -5
- package/apps/studio/annotations-layer.tsx +42 -0
- package/apps/studio/api.ts +417 -2
- package/apps/studio/bin/_agent-browser-safe-config.json +1 -0
- package/apps/studio/bin/_agent-browser-safe.mjs +228 -0
- package/apps/studio/bin/_agent-browser-safe.test.mjs +165 -0
- package/apps/studio/bin/_curl-local.mjs +349 -0
- package/apps/studio/bin/_curl-local.test.mjs +280 -0
- package/apps/studio/bin/agent-browser-safe.sh +29 -0
- package/apps/studio/bin/curl-local.sh +28 -0
- package/apps/studio/canvas-cursors.ts +6 -0
- package/apps/studio/canvas-edit.ts +678 -11
- package/apps/studio/canvas-icons.tsx +13 -0
- package/apps/studio/canvas-lib.tsx +3 -0
- package/apps/studio/canvas-shell.tsx +646 -26
- package/apps/studio/client/app.jsx +898 -52
- package/apps/studio/client/panels/ChatPanel.jsx +47 -2
- package/apps/studio/client/styles/3-shell-maude.css +39 -0
- package/apps/studio/contextual-toolbar.tsx +5 -3
- package/apps/studio/dist/client.bundle.js +1103 -1103
- package/apps/studio/dist/comment-mount.js +2 -2
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/grid-track-handles.ts +179 -0
- package/apps/studio/handoff.ts +35 -0
- package/apps/studio/http.ts +118 -0
- package/apps/studio/input-router.tsx +73 -17
- package/apps/studio/test/acp-session-allowed-tools.test.ts +107 -11
- package/apps/studio/test/browse-posture.test.tsx +107 -0
- package/apps/studio/test/canvas-hide-chrome.test.ts +58 -0
- package/apps/studio/test/canvas-meta-api.test.ts +70 -0
- package/apps/studio/test/canvas-origin-gate.test.ts +4 -0
- package/apps/studio/test/comment-mount.test.ts +2 -1
- package/apps/studio/test/component-map.test.ts +48 -0
- package/apps/studio/test/convert-to-absolute.test.ts +333 -0
- package/apps/studio/test/detach-component.test.ts +94 -0
- package/apps/studio/test/edit-scope-api.test.ts +8 -4
- package/apps/studio/test/element-structural-api.test.ts +74 -0
- package/apps/studio/test/element-structural-edit.test.ts +113 -0
- package/apps/studio/test/grid-track-handles.test.ts +160 -0
- package/apps/studio/test/handoff.test.ts +48 -0
- package/apps/studio/test/input-router.test.ts +82 -8
- package/apps/studio/test/layers-synthetic-groups.test.ts +96 -0
- package/apps/studio/test/pdf-print-boxes.test.ts +54 -0
- package/apps/studio/test/use-tool-mode.test.tsx +10 -2
- package/apps/studio/tool-palette.tsx +3 -1
- package/apps/studio/use-canvas-media-drop.tsx +126 -0
- package/apps/studio/use-element-resize.tsx +3 -1
- package/apps/studio/use-grid-track-handles.tsx +364 -0
- package/apps/studio/use-keyboard-discipline.tsx +15 -0
- package/apps/studio/use-tool-mode.tsx +30 -3
- package/apps/studio/web-overlay-content.tsx +52 -0
- package/apps/studio/whats-new.json +27 -0
- package/cli/commands/design.mjs +16 -0
- package/package.json +8 -8
- 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
|
-
|
|
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
|
-
|
|
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
|
|
174
|
-
*
|
|
175
|
-
*
|
|
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
|
*/
|