@1agh/maude 0.40.0 → 0.41.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 (73) hide show
  1. package/apps/studio/ai-banner.tsx +2 -2
  2. package/apps/studio/annotations-layer.tsx +53 -2
  3. package/apps/studio/api.ts +504 -3
  4. package/apps/studio/artboard-marquee.tsx +1 -1
  5. package/apps/studio/bin/_canvas-rects-playwright.mjs +55 -0
  6. package/apps/studio/bin/_canvas-rects-static.mjs +166 -0
  7. package/apps/studio/bin/_fetch-asset.mjs +556 -0
  8. package/apps/studio/bin/annotate.mjs +576 -34
  9. package/apps/studio/bin/canvas-rects.sh +152 -0
  10. package/apps/studio/bin/fetch-asset.sh +34 -0
  11. package/apps/studio/bin/read-annotations.mjs +138 -7
  12. package/apps/studio/bin/smoke.sh +42 -6
  13. package/apps/studio/build.ts +21 -0
  14. package/apps/studio/canvas-comment-mount.tsx +138 -4
  15. package/apps/studio/canvas-edit.ts +744 -11
  16. package/apps/studio/canvas-lib.tsx +219 -2
  17. package/apps/studio/canvas-shell.tsx +487 -20
  18. package/apps/studio/client/app.jsx +1451 -22
  19. package/apps/studio/client/comments-overlay.css +130 -126
  20. package/apps/studio/client/github.js +8 -0
  21. package/apps/studio/client/styles/3-shell-maude.css +48 -0
  22. package/apps/studio/comments-overlay.tsx +148 -41
  23. package/apps/studio/context-menu.tsx +15 -5
  24. package/apps/studio/contextual-toolbar.tsx +262 -4
  25. package/apps/studio/cursors-overlay.tsx +4 -4
  26. package/apps/studio/dist/client.bundle.js +20 -20
  27. package/apps/studio/dist/comment-mount.js +59 -1
  28. package/apps/studio/dist/styles.css +1 -1
  29. package/apps/studio/dom-selection.ts +127 -1
  30. package/apps/studio/drag-state.ts +24 -0
  31. package/apps/studio/equal-spacing-detector.ts +205 -0
  32. package/apps/studio/export-dialog.tsx +1 -1
  33. package/apps/studio/history.ts +47 -1
  34. package/apps/studio/http.ts +223 -0
  35. package/apps/studio/input-router.tsx +12 -0
  36. package/apps/studio/marquee-overlay.tsx +1 -1
  37. package/apps/studio/measure-overlay.tsx +241 -0
  38. package/apps/studio/participants-chrome.tsx +3 -3
  39. package/apps/studio/sizing-mode.ts +117 -0
  40. package/apps/studio/spacing-handles.ts +166 -0
  41. package/apps/studio/test/annotate-write.test.ts +890 -0
  42. package/apps/studio/test/camera-reveal.test.tsx +173 -0
  43. package/apps/studio/test/canvas-edit.test.ts +50 -0
  44. package/apps/studio/test/canvas-origin-gate.test.ts +18 -0
  45. package/apps/studio/test/canvas-rects.test.ts +198 -0
  46. package/apps/studio/test/comments-overlay.test.ts +117 -0
  47. package/apps/studio/test/dom-selection.test.ts +130 -0
  48. package/apps/studio/test/edit-css-occurrence.test.ts +81 -0
  49. package/apps/studio/test/edit-scope-api.test.ts +115 -0
  50. package/apps/studio/test/element-resize.test.ts +136 -0
  51. package/apps/studio/test/element-structural-api.test.ts +360 -0
  52. package/apps/studio/test/element-structural-edit.test.ts +233 -0
  53. package/apps/studio/test/equal-spacing-detector.test.ts +165 -1
  54. package/apps/studio/test/history-rollback.test.ts +26 -0
  55. package/apps/studio/test/knob-props-authored.test.ts +87 -0
  56. package/apps/studio/test/read-annotations.test.ts +154 -0
  57. package/apps/studio/test/sizing-mode.test.ts +102 -0
  58. package/apps/studio/test/spacing-handles.test.ts +138 -0
  59. package/apps/studio/test/specimen-select.test.ts +88 -0
  60. package/apps/studio/test/tool-palette-insert-anchor.test.ts +84 -0
  61. package/apps/studio/test/undo-sequence-byte-compare.test.ts +211 -0
  62. package/apps/studio/tool-palette.tsx +122 -2
  63. package/apps/studio/undo-hud.tsx +2 -2
  64. package/apps/studio/use-element-resize.tsx +732 -0
  65. package/apps/studio/use-keyboard-discipline.tsx +205 -15
  66. package/apps/studio/use-selection-set.tsx +14 -0
  67. package/apps/studio/use-spacing-handles.tsx +388 -0
  68. package/apps/studio/whats-new.json +28 -0
  69. package/cli/commands/design.mjs +6 -1
  70. package/cli/commands/design.test.mjs +49 -1
  71. package/cli/lib/fetch-asset.test.mjs +213 -0
  72. package/package.json +8 -8
  73. package/plugins/design/dependencies.json +10 -2
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @file spacing-handles.ts — Stage J (feature-element-editing-robustness)
3
+ * @purpose Pure geometry + value math for the on-canvas padding + gap drag
4
+ * overlay. Framework-free (unit-testable without a DOM), mirroring
5
+ * `equal-spacing-detector.ts`'s screen-coordinate midpoint shape so
6
+ * the two spacing affordances (read-only equal-spacing dots vs this
7
+ * draggable padding/gap overlay) share a visual vocabulary.
8
+ *
9
+ * Padding: 4 edge lines inset from the element's screen border-box by
10
+ * its resolved padding × zoom, one per side, each carrying a drag axis
11
+ * (top/bottom = vertical, left/right = horizontal).
12
+ * Gap: midpoints between consecutive DIRECT children along the flex
13
+ * main axis (row → x, column → y) — flex-only in v1; CSS-Grid gap
14
+ * editing is deferred to `feature-grid-track-editor.md` (Stage M3).
15
+ */
16
+
17
+ export interface ScreenRect {
18
+ x: number;
19
+ y: number;
20
+ w: number;
21
+ h: number;
22
+ }
23
+
24
+ export type PaddingSide = 'top' | 'right' | 'bottom' | 'left';
25
+
26
+ export interface PaddingLine {
27
+ side: PaddingSide;
28
+ /** Drag axis: top/bottom edges scrub vertically, left/right horizontally. */
29
+ axis: 'x' | 'y';
30
+ /** Line midpoint in screen coords (the handle's paint position). */
31
+ x: number;
32
+ y: number;
33
+ /** Line length in screen px (spans the box's inner width/height on that side). */
34
+ length: number;
35
+ }
36
+
37
+ /**
38
+ * Compute the 4 padding-edge handle positions from the element's screen
39
+ * border-box rect + its resolved padding (WORLD px, pre-zoom) + the render
40
+ * zoom. A handle sits at the midpoint of its edge, INSET from the border by
41
+ * `padding × zoom` — i.e. right on the padding/content boundary, so dragging it
42
+ * outward (toward the border) shrinks padding, inward (toward center) grows it.
43
+ */
44
+ export function computePaddingLines(
45
+ rect: ScreenRect,
46
+ padding: { top: number; right: number; bottom: number; left: number },
47
+ zoom: number
48
+ ): PaddingLine[] {
49
+ const z = zoom > 0 ? zoom : 1;
50
+ const pt = padding.top * z;
51
+ const pr = padding.right * z;
52
+ const pb = padding.bottom * z;
53
+ const pl = padding.left * z;
54
+ return [
55
+ { side: 'top', axis: 'y', x: rect.x + rect.w / 2, y: rect.y + pt, length: rect.w - pl - pr },
56
+ {
57
+ side: 'right',
58
+ axis: 'x',
59
+ x: rect.x + rect.w - pr,
60
+ y: rect.y + rect.h / 2,
61
+ length: rect.h - pt - pb,
62
+ },
63
+ {
64
+ side: 'bottom',
65
+ axis: 'y',
66
+ x: rect.x + rect.w / 2,
67
+ y: rect.y + rect.h - pb,
68
+ length: rect.w - pl - pr,
69
+ },
70
+ { side: 'left', axis: 'x', x: rect.x + pl, y: rect.y + rect.h / 2, length: rect.h - pt - pb },
71
+ ];
72
+ }
73
+
74
+ /**
75
+ * New padding value (WORLD px, clamped ≥ 0) for a drag on `side`, given the
76
+ * screen-space cursor delta and the render zoom. Top/bottom respond to
77
+ * vertical delta, left/right to horizontal — and the SIGN flips for
78
+ * bottom/right (dragging toward the element's center — up for bottom, left for
79
+ * right — GROWS that side's padding, mirroring how top/left already grow when
80
+ * dragged down/right).
81
+ */
82
+ export function computePaddingDrag(
83
+ side: PaddingSide,
84
+ startValue: number,
85
+ dxScreen: number,
86
+ dyScreen: number,
87
+ zoom: number
88
+ ): number {
89
+ const z = zoom > 0 ? zoom : 1;
90
+ const d = side === 'top' || side === 'bottom' ? dyScreen / z : dxScreen / z;
91
+ const signed = side === 'bottom' || side === 'right' ? -d : d;
92
+ return Math.max(0, round2(startValue + signed));
93
+ }
94
+
95
+ export type FlexAxis = 'x' | 'y';
96
+
97
+ /** Row/row-reverse → x (horizontal main axis); column/column-reverse → y. */
98
+ export function flexMainAxis(flexDirection: string | null | undefined): FlexAxis {
99
+ const dir = (flexDirection || 'row').trim();
100
+ return dir.startsWith('column') ? 'y' : 'x';
101
+ }
102
+
103
+ /**
104
+ * Midpoints between consecutive DIRECT children's screen rects, along `axis`.
105
+ * Mirrors `detectEqualSpacing`'s midpoint shape but needs no equal-spacing
106
+ * DETECTION (there's exactly one `gap` value to visualize, not several to
107
+ * confirm) — just the geometric midpoint of each adjacent pair's facing edges.
108
+ * Fewer than 2 children → no gaps to show.
109
+ */
110
+ export function computeGapMidpoints(
111
+ childRects: ScreenRect[],
112
+ axis: FlexAxis
113
+ ): Array<{ x: number; y: number }> {
114
+ if (childRects.length < 2) return [];
115
+ const sorted = [...childRects].sort((a, b) => (axis === 'x' ? a.x - b.x : a.y - b.y));
116
+ const out: Array<{ x: number; y: number }> = [];
117
+ for (let i = 0; i < sorted.length - 1; i++) {
118
+ const a = sorted[i] as ScreenRect;
119
+ const b = sorted[i + 1] as ScreenRect;
120
+ if (axis === 'x') {
121
+ out.push({ x: (a.x + a.w + b.x) / 2, y: a.y + a.h / 2 });
122
+ } else {
123
+ out.push({ x: a.x + a.w / 2, y: (a.y + a.h + b.y) / 2 });
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+
129
+ /** New `gap` value (WORLD px, clamped ≥ 0) for a drag along `axis`. Gap is a
130
+ * single shared CSS value, so every handle drags the SAME number 1:1 with the
131
+ * cursor (no doubling/halving — dragging a handle N screen px outward should
132
+ * feel like N world px of extra breathing room). */
133
+ export function computeGapDrag(
134
+ axis: FlexAxis,
135
+ startValue: number,
136
+ dxScreen: number,
137
+ dyScreen: number,
138
+ zoom: number
139
+ ): number {
140
+ const z = zoom > 0 ? zoom : 1;
141
+ const d = axis === 'x' ? dxScreen / z : dyScreen / z;
142
+ return Math.max(0, round2(startValue + d));
143
+ }
144
+
145
+ function round2(n: number): number {
146
+ return Math.round(n * 100) / 100;
147
+ }
148
+
149
+ /**
150
+ * Which padding sides a drag on `side` touches, given the held modifiers —
151
+ * matches the CSS-panel box-model widget's OWN scrub grammar exactly (`side()`
152
+ * in `client/app.jsx`, `title="drag to scrub · alt = symmetric · alt+shift =
153
+ * all sides"`): plain drag = just this side; Alt = the symmetric axis PAIR
154
+ * (top+bottom, or left+right); Alt+Shift = all four. Every touched side is set
155
+ * to the SAME value (not each grown by its own delta) — same semantics as the
156
+ * panel's `sidesFor`/`last` write.
157
+ */
158
+ export function paddingSideSet(
159
+ side: PaddingSide,
160
+ altKey: boolean,
161
+ shiftKey: boolean
162
+ ): PaddingSide[] {
163
+ if (altKey && shiftKey) return ['top', 'right', 'bottom', 'left'];
164
+ if (altKey) return side === 'top' || side === 'bottom' ? ['top', 'bottom'] : ['left', 'right'];
165
+ return [side];
166
+ }