@agent-native/core 0.80.0 → 0.80.2

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 (228) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +14 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/application-state/handlers.ts +1 -1
  27. package/corpus/core/src/brand-kit/index.ts +1 -1
  28. package/corpus/core/src/cli/skills.ts +18 -0
  29. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  30. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  31. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  32. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  33. package/corpus/core/src/extensions/path.ts +1 -4
  34. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  35. package/corpus/core/src/mcp/build-server.ts +2 -2
  36. package/corpus/core/src/mcp/embed-app.ts +35 -1
  37. package/corpus/core/src/org/handlers.ts +5 -5
  38. package/corpus/core/src/org/plugin.ts +2 -2
  39. package/corpus/core/src/scripts/call-agent.ts +1 -1
  40. package/corpus/core/src/scripts/db/safety.ts +1 -1
  41. package/corpus/core/src/server/auth.ts +3 -0
  42. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  43. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  44. package/corpus/core/src/server/index.ts +8 -0
  45. package/corpus/core/src/styles/agent-native.css +14 -7
  46. package/corpus/core/src/usage/store.ts +2 -2
  47. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  48. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  49. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  50. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  51. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  52. package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
  53. package/corpus/templates/analytics/app/i18n-data.ts +404 -0
  54. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  55. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  56. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  57. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
  58. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
  59. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  62. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  63. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  66. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  67. package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
  68. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  69. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  70. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  71. package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  73. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  74. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  75. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  76. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  77. package/corpus/templates/content/shared/document-export.ts +1 -1
  78. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  79. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  80. package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
  81. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  82. package/corpus/templates/design/actions/update-file.ts +9 -2
  83. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
  84. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
  85. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
  86. package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
  87. package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
  88. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
  89. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
  90. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
  91. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
  92. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
  93. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
  94. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
  95. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  96. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  97. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
  98. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
  99. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  100. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
  101. package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
  102. package/corpus/templates/design/app/global.css +29 -3
  103. package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
  104. package/corpus/templates/design/app/i18n-data.ts +239 -1
  105. package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
  106. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  107. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  108. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  109. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  110. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  111. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  112. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  113. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  114. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  115. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  116. package/corpus/templates/design/shared/code-layer.ts +209 -9
  117. package/corpus/templates/design/shared/html-content.ts +21 -0
  118. package/corpus/templates/design/shared/pen-path.ts +207 -0
  119. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  120. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  121. package/corpus/templates/slides/AGENTS.md +3 -2
  122. package/corpus/templates/slides/actions/import-file.ts +26 -17
  123. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  124. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  125. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  126. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  127. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  128. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  129. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  130. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  131. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  132. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  133. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  134. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  135. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  136. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  137. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  138. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  139. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  140. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  141. package/dist/application-state/handlers.js +1 -1
  142. package/dist/application-state/handlers.js.map +1 -1
  143. package/dist/brand-kit/index.d.ts +1 -1
  144. package/dist/brand-kit/index.js +1 -1
  145. package/dist/brand-kit/index.js.map +1 -1
  146. package/dist/cli/skills.d.ts.map +1 -1
  147. package/dist/cli/skills.js +18 -0
  148. package/dist/cli/skills.js.map +1 -1
  149. package/dist/client/AgentPanel.js +1 -1
  150. package/dist/client/AgentPanel.js.map +1 -1
  151. package/dist/client/MultiTabAssistantChat.js +1 -1
  152. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  153. package/dist/client/extensions/portable-extension.d.ts +1 -1
  154. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  155. package/dist/client/extensions/portable-extension.js.map +1 -1
  156. package/dist/code-agents/transcript-normalizer.js +1 -1
  157. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  158. package/dist/collab/awareness.d.ts +2 -2
  159. package/dist/collab/awareness.d.ts.map +1 -1
  160. package/dist/collab/routes.d.ts +1 -1
  161. package/dist/extensions/path.d.ts +1 -1
  162. package/dist/extensions/path.d.ts.map +1 -1
  163. package/dist/extensions/path.js.map +1 -1
  164. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  165. package/dist/integrations/a2a-continuation-processor.js +1 -1
  166. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  167. package/dist/mcp/build-server.d.ts +1 -1
  168. package/dist/mcp/build-server.d.ts.map +1 -1
  169. package/dist/mcp/build-server.js +1 -1
  170. package/dist/mcp/build-server.js.map +1 -1
  171. package/dist/mcp/embed-app.d.ts.map +1 -1
  172. package/dist/mcp/embed-app.js +35 -1
  173. package/dist/mcp/embed-app.js.map +1 -1
  174. package/dist/notifications/routes.d.ts +3 -3
  175. package/dist/observability/routes.d.ts +8 -8
  176. package/dist/org/handlers.js +5 -5
  177. package/dist/org/handlers.js.map +1 -1
  178. package/dist/org/plugin.js +2 -2
  179. package/dist/org/plugin.js.map +1 -1
  180. package/dist/resources/handlers.d.ts +1 -1
  181. package/dist/scripts/call-agent.js +1 -1
  182. package/dist/scripts/call-agent.js.map +1 -1
  183. package/dist/scripts/db/safety.js +1 -1
  184. package/dist/scripts/db/safety.js.map +1 -1
  185. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  186. package/dist/server/auth.d.ts.map +1 -1
  187. package/dist/server/auth.js +3 -0
  188. package/dist/server/auth.js.map +1 -1
  189. package/dist/server/builder-design-systems.d.ts +28 -0
  190. package/dist/server/builder-design-systems.d.ts.map +1 -0
  191. package/dist/server/builder-design-systems.js +217 -0
  192. package/dist/server/builder-design-systems.js.map +1 -0
  193. package/dist/server/core-routes-plugin.d.ts +1 -0
  194. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  195. package/dist/server/core-routes-plugin.js +20 -3
  196. package/dist/server/core-routes-plugin.js.map +1 -1
  197. package/dist/server/index.d.ts +1 -0
  198. package/dist/server/index.d.ts.map +1 -1
  199. package/dist/server/index.js +1 -0
  200. package/dist/server/index.js.map +1 -1
  201. package/dist/styles/agent-native.css +14 -7
  202. package/dist/usage/store.js +2 -2
  203. package/dist/usage/store.js.map +1 -1
  204. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  205. package/docs/content/locales/ar-SA/template-design.mdx +14 -1
  206. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  207. package/docs/content/locales/de-DE/template-design.mdx +15 -1
  208. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  209. package/docs/content/locales/es-ES/template-design.mdx +15 -1
  210. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  211. package/docs/content/locales/fr-FR/template-design.mdx +15 -1
  212. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  213. package/docs/content/locales/hi-IN/template-design.mdx +15 -1
  214. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  215. package/docs/content/locales/ja-JP/template-design.mdx +16 -1
  216. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  217. package/docs/content/locales/ko-KR/template-design.mdx +14 -1
  218. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  219. package/docs/content/locales/pt-BR/template-design.mdx +14 -1
  220. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  221. package/docs/content/locales/zh-CN/template-design.mdx +14 -1
  222. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  223. package/docs/content/locales/zh-TW/template-design.mdx +14 -1
  224. package/docs/content/skills-guide.mdx +5 -0
  225. package/docs/content/template-design.mdx +14 -1
  226. package/package.json +1 -1
  227. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  228. package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
@@ -1,3 +1,5 @@
1
+ import { useState } from "react";
2
+
1
3
  import {
2
4
  Select,
3
5
  SelectContent,
@@ -62,28 +64,40 @@ const DEFAULT_LABELS: ConstraintsWidgetLabels = {
62
64
  };
63
65
 
64
66
  // ── pin-box geometry ────────────────────────────────────────────────────────
65
- // The preview box is 40×40px (size-10). Inside it sits a 16×16px inner rect
66
- // (representing the element) centered at (20,20). The four edge pins are thin
67
- // bars that can be toggled on/off; a center-cross appears for "center" mode.
67
+ // The preview box is 40×40px (size-10). Inside it sits an 18×14px inner rect
68
+ // (representing a child element) centered at (20,20). The proportions match
69
+ // Figma's "little box" widget inner rect is ~45–50% of the outer box width
70
+ // and slightly shorter in height to resemble a real element.
71
+ //
72
+ // Edge pins: 5px long, 2px wide, placed 2px from the box edge (flush to border).
73
+ // left : x=2..7, y center=20
74
+ // right : x=33..38, y center=20
75
+ // top : x center=20, y=2..7
76
+ // bottom: x center=20, y=33..38
68
77
  //
69
- // Edge pins: 6px long, 1.5px wide, placed 2px from the box edge.
70
- // left : x=2..8, y center=20
71
- // right : x=32..38, y center=20
72
- // top : x center=20, y=2..8
73
- // bottom: x center=20, y=32..38
78
+ // Gap between pin tip and inner rect edge:
79
+ // left : inner left edge at x=11 → gap from x=7 to x=11 (4px)
80
+ // right : inner right edge at x=29 gap from x=29 to x=33 (4px)
81
+ // top : inner top edge at y=13 → gap from y=7 to y=13 (6px)
82
+ // bottom: inner bottom edge at y=27 → gap from y=27 to y=33 (6px)
74
83
  //
75
- // Center marker: a 4px dot at (20,20) for h=center or v=center.
84
+ // Center marker: for h=center or v=center, a single accent line runs through
85
+ // the full width/height of the box (including through the inner rect), drawn
86
+ // on top at the midpoint — matching Figma's solid crosshair treatment.
87
+ //
88
+ // Scale mode: all four pins on that axis render dashed, accent color.
76
89
 
77
90
  const BOX = 40; // viewBox width/height (matches size-10 = 40px)
78
- const INNER = 16; // inner rect size
79
- const INNER_X = (BOX - INNER) / 2; // 12
80
- const INNER_Y = (BOX - INNER) / 2; // 12
81
- const PIN_LEN = 6;
82
- const PIN_W = 1.5;
83
- const MARGIN = 2; // gap between box edge and pin start
91
+ const INNER_W = 18; // inner rect width
92
+ const INNER_H = 14; // inner rect height (slightly shorter than wide = realistic element)
93
+ const INNER_X = (BOX - INNER_W) / 2; // 11
94
+ const INNER_Y = (BOX - INNER_H) / 2; // 13
95
+ const PIN_LEN = 5; // visual pin length
96
+ const PIN_W = 2; // pin stroke width (Figma: 2px thick pins)
97
+ const MARGIN = 2; // gap between outer box edge and pin start
84
98
  const CENTER = BOX / 2; // 20
85
99
 
86
- // Returns whether a given horizontal pin should be active.
100
+ // Returns whether a given horizontal pin should be active (solid/accent).
87
101
  function hPinActive(side: "left" | "right", h: HorizontalConstraint): boolean {
88
102
  if (side === "left") return h === "left" || h === "left-right";
89
103
  return h === "right" || h === "left-right";
@@ -94,8 +108,11 @@ function vPinActive(side: "top" | "bottom", v: VerticalConstraint): boolean {
94
108
  return v === "bottom" || v === "top-bottom";
95
109
  }
96
110
 
97
- // Clicking a left/right pin cycles: if that side is the only active one →
98
- // "left-right"; if "left-right" single side; if neithersingle side.
111
+ // Clicking a left/right pin cycles the constraint:
112
+ // - if that side is the only active one "left-right"
113
+ // - if "left-right" or scale/center → single side
114
+ // - if neither active → single side
115
+ // Can't clear both sides; reverts to single side instead.
99
116
  function toggleHPin(
100
117
  side: "left" | "right",
101
118
  current: HorizontalConstraint,
@@ -155,6 +172,10 @@ function PinBox({
155
172
  onToggleH,
156
173
  onToggleV,
157
174
  }: PinBoxProps) {
175
+ const [hoveredPin, setHoveredPin] = useState<
176
+ "left" | "right" | "top" | "bottom" | null
177
+ >(null);
178
+
158
179
  const leftOn = hPinActive("left", value.horizontal);
159
180
  const rightOn = hPinActive("right", value.horizontal);
160
181
  const topOn = vPinActive("top", value.vertical);
@@ -164,9 +185,47 @@ function PinBox({
164
185
  const hScale = value.horizontal === "scale";
165
186
  const vScale = value.vertical === "scale";
166
187
 
167
- // Hit-area size for each pin button (larger than the visual stroke for
168
- // easy clicking 8×8 centered on the pin midpoint).
169
- const HIT = 8;
188
+ // Colors:
189
+ // active / scale accent (primary)
190
+ // hovered inactive slightly brighter muted
191
+ // inactive → muted (30% foreground opacity)
192
+ const ACCENT = "hsl(var(--primary))";
193
+ // Hover: lighten inactive pins on hover — use 55% opacity instead of 30%
194
+ const MUTED = "hsl(var(--foreground) / 0.30)";
195
+ const MUTED_HOVER = "hsl(var(--foreground) / 0.60)";
196
+ const SCALE_DASH = "3 2";
197
+
198
+ // Hit-area size for each pin (larger than the visual stroke for easy clicking
199
+ // — 10px wide / 14px tall centered on the pin midpoint).
200
+ const HIT_CROSS = 10; // perpendicular extent of hit area
201
+ const HIT_LONG = 14; // along-pin extent of hit area (covers pin + gap to inner rect)
202
+
203
+ // Pin color: accent when active or scale; hover-boosted or muted when inactive.
204
+ function pinColor(
205
+ side: "left" | "right" | "top" | "bottom",
206
+ isActive: boolean,
207
+ isScale: boolean,
208
+ ): string {
209
+ if (isActive || isScale) return ACCENT;
210
+ if (hoveredPin === side) return MUTED_HOVER;
211
+ return MUTED;
212
+ }
213
+
214
+ const lColor = pinColor("left", leftOn, hScale);
215
+ const rColor = pinColor("right", rightOn, hScale);
216
+ const tColor = pinColor("top", topOn, vScale);
217
+ const bColor = pinColor("bottom", bottomOn, vScale);
218
+
219
+ const lDash = hScale ? SCALE_DASH : undefined;
220
+ const rDash = hScale ? SCALE_DASH : undefined;
221
+ const tDash = vScale ? SCALE_DASH : undefined;
222
+ const bDash = vScale ? SCALE_DASH : undefined;
223
+
224
+ // Pin end coordinates (tip = closer to inner rect)
225
+ // left pin: x from MARGIN to MARGIN+PIN_LEN, y=CENTER
226
+ // right pin: x from BOX-MARGIN to BOX-MARGIN-PIN_LEN, y=CENTER
227
+ // top pin: x=CENTER, y from MARGIN to MARGIN+PIN_LEN
228
+ // bottom pin: x=CENTER, y from BOX-MARGIN to BOX-MARGIN-PIN_LEN
170
229
 
171
230
  return (
172
231
  <svg
@@ -178,7 +237,7 @@ function PinBox({
178
237
  "shrink-0 rounded-sm",
179
238
  disabled && "pointer-events-none opacity-40",
180
239
  )}
181
- style={{ background: "hsl(var(--muted) / 0.3)" }}
240
+ style={{ background: "hsl(var(--muted) / 0.4)" }}
182
241
  >
183
242
  {/* outer border */}
184
243
  <rect
@@ -186,77 +245,61 @@ function PinBox({
186
245
  y={0.75}
187
246
  width={BOX - 1.5}
188
247
  height={BOX - 1.5}
189
- rx={3}
248
+ rx={2.5}
190
249
  fill="none"
191
- stroke="hsl(var(--border))"
250
+ stroke="hsl(var(--foreground) / 0.20)"
192
251
  strokeWidth={1.5}
193
252
  />
194
253
 
195
- {/* inner element rect */}
254
+ {/* inner element rect — represents the selected element */}
196
255
  <rect
197
256
  x={INNER_X}
198
257
  y={INNER_Y}
199
- width={INNER}
200
- height={INNER}
201
- rx={1.5}
258
+ width={INNER_W}
259
+ height={INNER_H}
260
+ rx={1}
202
261
  fill="hsl(var(--background))"
203
- stroke="hsl(var(--foreground) / 0.5)"
262
+ stroke="hsl(var(--foreground) / 0.35)"
204
263
  strokeWidth={1}
205
264
  />
206
265
 
207
- {/* scale dashed overlay */}
208
- {(hScale || vScale) && (
209
- <rect
210
- x={INNER_X + 1}
211
- y={INNER_Y + 1}
212
- width={INNER - 2}
213
- height={INNER - 2}
214
- rx={1}
215
- fill="none"
216
- stroke="hsl(var(--primary) / 0.8)"
266
+ {/* center crosshair lines — drawn THROUGH the full box width/height
267
+ (including through the inner rect), matching Figma's solid crosshair.
268
+ Only rendered when the constraint is "center" on that axis. */}
269
+ {hCenter && (
270
+ <line
271
+ x1={MARGIN + PIN_LEN}
272
+ y1={CENTER}
273
+ x2={BOX - MARGIN - PIN_LEN}
274
+ y2={CENTER}
275
+ stroke={ACCENT}
217
276
  strokeWidth={1}
218
- strokeDasharray="2 1.5"
277
+ strokeLinecap="round"
219
278
  />
220
279
  )}
221
-
222
- {/* center cross for h-center or v-center */}
223
- {(hCenter || vCenter) && (
224
- <>
225
- {hCenter && (
226
- <line
227
- x1={INNER_X - 2}
228
- y1={CENTER}
229
- x2={INNER_X + INNER + 2}
230
- y2={CENTER}
231
- stroke="hsl(var(--primary))"
232
- strokeWidth={1.5}
233
- />
234
- )}
235
- {vCenter && (
236
- <line
237
- x1={CENTER}
238
- y1={INNER_Y - 2}
239
- x2={CENTER}
240
- y2={INNER_Y + INNER + 2}
241
- stroke="hsl(var(--primary))"
242
- strokeWidth={1.5}
243
- />
244
- )}
245
- </>
280
+ {vCenter && (
281
+ <line
282
+ x1={CENTER}
283
+ y1={MARGIN + PIN_LEN}
284
+ x2={CENTER}
285
+ y2={BOX - MARGIN - PIN_LEN}
286
+ stroke={ACCENT}
287
+ strokeWidth={1}
288
+ strokeLinecap="round"
289
+ />
246
290
  )}
247
291
 
248
292
  {/* edge pins — visual strokes */}
249
- {/* left pin */}
293
+ {/* left pin: from outer edge toward inner rect */}
250
294
  <line
251
295
  x1={MARGIN}
252
296
  y1={CENTER}
253
297
  x2={MARGIN + PIN_LEN}
254
298
  y2={CENTER}
255
- stroke={
256
- leftOn ? "hsl(var(--primary))" : "hsl(var(--foreground) / 0.35)"
257
- }
299
+ stroke={lColor}
258
300
  strokeWidth={PIN_W}
259
301
  strokeLinecap="round"
302
+ strokeDasharray={lDash}
260
303
  />
261
304
  {/* right pin */}
262
305
  <line
@@ -264,11 +307,10 @@ function PinBox({
264
307
  y1={CENTER}
265
308
  x2={BOX - MARGIN - PIN_LEN}
266
309
  y2={CENTER}
267
- stroke={
268
- rightOn ? "hsl(var(--primary))" : "hsl(var(--foreground) / 0.35)"
269
- }
310
+ stroke={rColor}
270
311
  strokeWidth={PIN_W}
271
312
  strokeLinecap="round"
313
+ strokeDasharray={rDash}
272
314
  />
273
315
  {/* top pin */}
274
316
  <line
@@ -276,9 +318,10 @@ function PinBox({
276
318
  y1={MARGIN}
277
319
  x2={CENTER}
278
320
  y2={MARGIN + PIN_LEN}
279
- stroke={topOn ? "hsl(var(--primary))" : "hsl(var(--foreground) / 0.35)"}
321
+ stroke={tColor}
280
322
  strokeWidth={PIN_W}
281
323
  strokeLinecap="round"
324
+ strokeDasharray={tDash}
282
325
  />
283
326
  {/* bottom pin */}
284
327
  <line
@@ -286,61 +329,68 @@ function PinBox({
286
329
  y1={BOX - MARGIN}
287
330
  x2={CENTER}
288
331
  y2={BOX - MARGIN - PIN_LEN}
289
- stroke={
290
- bottomOn ? "hsl(var(--primary))" : "hsl(var(--foreground) / 0.35)"
291
- }
332
+ stroke={bColor}
292
333
  strokeWidth={PIN_W}
293
334
  strokeLinecap="round"
335
+ strokeDasharray={bDash}
294
336
  />
295
337
 
296
- {/* invisible click targets — rendered on top of strokes */}
338
+ {/* invisible click + hover targets — rendered on top of strokes */}
297
339
  {!disabled && (
298
340
  <>
299
341
  {/* left pin hit area */}
300
342
  <rect
301
- x={MARGIN - HIT / 2}
302
- y={CENTER - HIT / 2}
303
- width={PIN_LEN + HIT}
304
- height={HIT}
343
+ x={0}
344
+ y={CENTER - HIT_CROSS / 2}
345
+ width={MARGIN + PIN_LEN + HIT_LONG / 2}
346
+ height={HIT_CROSS}
305
347
  fill="transparent"
306
348
  className="cursor-pointer"
307
349
  onClick={() => onToggleH("left")}
350
+ onMouseEnter={() => setHoveredPin("left")}
351
+ onMouseLeave={() => setHoveredPin(null)}
308
352
  role="button"
309
353
  aria-label={labels.left}
310
354
  />
311
355
  {/* right pin hit area */}
312
356
  <rect
313
- x={BOX - MARGIN - PIN_LEN - HIT / 2}
314
- y={CENTER - HIT / 2}
315
- width={PIN_LEN + HIT}
316
- height={HIT}
357
+ x={BOX - MARGIN - PIN_LEN - HIT_LONG / 2}
358
+ y={CENTER - HIT_CROSS / 2}
359
+ width={MARGIN + PIN_LEN + HIT_LONG / 2}
360
+ height={HIT_CROSS}
317
361
  fill="transparent"
318
362
  className="cursor-pointer"
319
363
  onClick={() => onToggleH("right")}
364
+ onMouseEnter={() => setHoveredPin("right")}
365
+ onMouseLeave={() => setHoveredPin(null)}
320
366
  role="button"
321
367
  aria-label={labels.right}
322
368
  />
323
369
  {/* top pin hit area */}
324
370
  <rect
325
- x={CENTER - HIT / 2}
326
- y={MARGIN - HIT / 2}
327
- width={HIT}
328
- height={PIN_LEN + HIT}
371
+ x={CENTER - HIT_CROSS / 2}
372
+ y={0}
373
+ width={HIT_CROSS}
374
+ height={MARGIN + PIN_LEN + HIT_LONG / 2}
329
375
  fill="transparent"
330
376
  className="cursor-pointer"
331
377
  onClick={() => onToggleV("top")}
378
+ onMouseEnter={() => setHoveredPin("top")}
379
+ onMouseLeave={() => setHoveredPin(null)}
332
380
  role="button"
333
381
  aria-label={labels.top}
334
382
  />
335
383
  {/* bottom pin hit area */}
336
384
  <rect
337
- x={CENTER - HIT / 2}
338
- y={BOX - MARGIN - PIN_LEN - HIT / 2}
339
- width={HIT}
340
- height={PIN_LEN + HIT}
385
+ x={CENTER - HIT_CROSS / 2}
386
+ y={BOX - MARGIN - PIN_LEN - HIT_LONG / 2}
387
+ width={HIT_CROSS}
388
+ height={MARGIN + PIN_LEN + HIT_LONG / 2}
341
389
  fill="transparent"
342
390
  className="cursor-pointer"
343
391
  onClick={() => onToggleV("bottom")}
392
+ onMouseEnter={() => setHoveredPin("bottom")}
393
+ onMouseLeave={() => setHoveredPin(null)}
344
394
  role="button"
345
395
  aria-label={labels.bottom}
346
396
  />
@@ -377,7 +427,7 @@ export function ConstraintsWidget({
377
427
  </span>
378
428
 
379
429
  {/* main row: pin-box LEFT + dropdowns RIGHT */}
380
- <div className="flex items-center gap-2">
430
+ <div className="flex items-center gap-1.5">
381
431
  {/* pin box */}
382
432
  <PinBox
383
433
  value={value}