@agent-native/core 0.98.14 → 0.98.16
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +41 -7
- package/corpus/core/src/server/agent-chat-plugin.ts +11 -2
- package/corpus/templates/analytics/changelog/2026-07-13-ask-app-now-uses-the-current-core-agent-loop-and-response-gu.md +6 -0
- package/corpus/templates/analytics/netlify.toml +5 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +23 -11
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +34 -3
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +152 -62
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +7 -7
- package/corpus/templates/design/app/global.css +2 -2
- package/corpus/templates/design/changelog/2026-07-10-canvas-size-and-option-hover-measurements.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-short-hex-colors-now-expand-like-figma.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +34 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +9 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.98.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cc35446: Resolve the authenticated owner's active provider key for A2A and MCP ask-agent runs, matching the interactive chat engine path.
|
|
8
|
+
|
|
9
|
+
## 0.98.15
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 2161d10: Preserve streamed tool calls when an engine omits its normalized assistant-content event so delegated and MCP agent runs can still execute requested actions.
|
|
14
|
+
|
|
3
15
|
## 0.98.14
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.98.
|
|
3
|
+
"version": "0.98.16",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -3081,6 +3081,8 @@ export async function runAgentLoop(opts: {
|
|
|
3081
3081
|
|
|
3082
3082
|
let assistantContent: EngineContentPart[] | undefined;
|
|
3083
3083
|
let streamedAssistantText = "";
|
|
3084
|
+
const streamedAssistantToolCalls: import("./engine/types.js").EngineToolCallPart[] =
|
|
3085
|
+
[];
|
|
3084
3086
|
let terminalStopReason:
|
|
3085
3087
|
| Extract<EngineEvent, { type: "stop" }>["reason"]
|
|
3086
3088
|
| undefined;
|
|
@@ -3122,6 +3124,7 @@ export async function runAgentLoop(opts: {
|
|
|
3122
3124
|
for (let retry = 0; ; retry++) {
|
|
3123
3125
|
assistantContent = undefined;
|
|
3124
3126
|
streamedAssistantText = "";
|
|
3127
|
+
streamedAssistantToolCalls.length = 0;
|
|
3125
3128
|
terminalStopReason = undefined;
|
|
3126
3129
|
toolCallErrors.clear();
|
|
3127
3130
|
try {
|
|
@@ -3465,7 +3468,16 @@ export async function runAgentLoop(opts: {
|
|
|
3465
3468
|
} else if (event.type === "gateway-heartbeat") {
|
|
3466
3469
|
send({ type: "stream_keepalive" });
|
|
3467
3470
|
} else if (event.type === "tool-call") {
|
|
3468
|
-
//
|
|
3471
|
+
// Most engines repeat the authoritative call in assistant-content,
|
|
3472
|
+
// but delegated/custom engines can expose only the stream event.
|
|
3473
|
+
// Keep it so the loop can still execute the requested action when
|
|
3474
|
+
// the normalized terminal event is missing.
|
|
3475
|
+
streamedAssistantToolCalls.push({
|
|
3476
|
+
type: "tool-call",
|
|
3477
|
+
id: event.id,
|
|
3478
|
+
name: event.name,
|
|
3479
|
+
input: event.input,
|
|
3480
|
+
});
|
|
3469
3481
|
} else if (event.type === "tool-call-error") {
|
|
3470
3482
|
toolCallErrors.set(event.id, {
|
|
3471
3483
|
name: event.name,
|
|
@@ -3553,12 +3565,34 @@ export async function runAgentLoop(opts: {
|
|
|
3553
3565
|
assistantContent = [];
|
|
3554
3566
|
}
|
|
3555
3567
|
|
|
3556
|
-
if (
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
//
|
|
3561
|
-
|
|
3568
|
+
if (
|
|
3569
|
+
(!assistantContent || assistantContent.length === 0) &&
|
|
3570
|
+
(streamedAssistantText.trim() || streamedAssistantToolCalls.length > 0)
|
|
3571
|
+
) {
|
|
3572
|
+
// Some delegated/custom engine streams emit text deltas and/or tool-call
|
|
3573
|
+
// events followed by a terminal stop without the normalized
|
|
3574
|
+
// assistant-content event. Preserve both so final-response guards still
|
|
3575
|
+
// run and requested actions are not silently dropped.
|
|
3576
|
+
assistantContent = [
|
|
3577
|
+
...(streamedAssistantText.trim()
|
|
3578
|
+
? [{ type: "text" as const, text: streamedAssistantText }]
|
|
3579
|
+
: []),
|
|
3580
|
+
...streamedAssistantToolCalls,
|
|
3581
|
+
];
|
|
3582
|
+
} else if (assistantContent && streamedAssistantToolCalls.length > 0) {
|
|
3583
|
+
const existingToolCallIds = new Set(
|
|
3584
|
+
assistantContent
|
|
3585
|
+
.filter(
|
|
3586
|
+
(part): part is import("./engine/types.js").EngineToolCallPart =>
|
|
3587
|
+
part.type === "tool-call",
|
|
3588
|
+
)
|
|
3589
|
+
.map((part) => part.id),
|
|
3590
|
+
);
|
|
3591
|
+
for (const toolCall of streamedAssistantToolCalls) {
|
|
3592
|
+
if (!existingToolCallIds.has(toolCall.id)) {
|
|
3593
|
+
assistantContent.push(toolCall);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3562
3596
|
}
|
|
3563
3597
|
if (!assistantContent) {
|
|
3564
3598
|
// No content — done
|
|
@@ -1252,9 +1252,12 @@ export function createAgentChatPlugin(
|
|
|
1252
1252
|
|
|
1253
1253
|
// Use the SAME agent setup as the interactive chat — identical tools,
|
|
1254
1254
|
// prompt, and capabilities. The A2A agent IS the app's agent.
|
|
1255
|
+
const { getOwnerActiveApiKey } =
|
|
1256
|
+
await import("../agent/production-agent.js");
|
|
1257
|
+
const ownerApiKey = await getOwnerActiveApiKey(userEmail);
|
|
1255
1258
|
const a2aEngine = await resolveEngine({
|
|
1256
1259
|
engineOption: options?.engine,
|
|
1257
|
-
apiKey: options?.apiKey,
|
|
1260
|
+
apiKey: ownerApiKey ?? options?.apiKey,
|
|
1258
1261
|
appId: options?.appId,
|
|
1259
1262
|
});
|
|
1260
1263
|
|
|
@@ -1524,9 +1527,15 @@ export function createAgentChatPlugin(
|
|
|
1524
1527
|
? { externalAgents: options.externalAgents }
|
|
1525
1528
|
: {}),
|
|
1526
1529
|
askAgent: async (message: string) => {
|
|
1530
|
+
const ownerEmail = getRequestUserEmail();
|
|
1531
|
+
const { getOwnerActiveApiKey } =
|
|
1532
|
+
await import("../agent/production-agent.js");
|
|
1533
|
+
const ownerApiKey = ownerEmail
|
|
1534
|
+
? await getOwnerActiveApiKey(ownerEmail)
|
|
1535
|
+
: undefined;
|
|
1527
1536
|
const mcpEngine = await resolveEngine({
|
|
1528
1537
|
engineOption: options?.engine,
|
|
1529
|
-
apiKey: options?.apiKey,
|
|
1538
|
+
apiKey: ownerApiKey ?? options?.apiKey,
|
|
1530
1539
|
appId: options?.appId,
|
|
1531
1540
|
});
|
|
1532
1541
|
const mcpModelCandidate =
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
[build]
|
|
2
2
|
ignore = "node ./scripts/netlify-ignore-build.mjs analytics"
|
|
3
|
-
|
|
3
|
+
# The workspace package dist folders are ignored/generated artifacts. Rebuild
|
|
4
|
+
# core explicitly here so a cached Netlify install cannot bundle an older agent
|
|
5
|
+
# loop into Analytics' server functions (including A2A/MCP final-response
|
|
6
|
+
# guards).
|
|
7
|
+
command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && pnpm --filter @agent-native/core build && NITRO_PRESET=netlify pnpm --filter analytics build"
|
|
4
8
|
publish = "templates/analytics/dist"
|
|
5
9
|
functions = "templates/analytics/.netlify/functions-internal"
|
|
6
10
|
|
|
@@ -5046,17 +5046,29 @@ function SingleScreenCreationOverlay({
|
|
|
5046
5046
|
/>
|
|
5047
5047
|
) : null}
|
|
5048
5048
|
{previewRect ? (
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5049
|
+
<>
|
|
5050
|
+
<div
|
|
5051
|
+
data-creation-preview-rect
|
|
5052
|
+
className="pointer-events-none absolute rounded-[2px] border-[1.5px] border-[var(--design-editor-accent-color)] bg-[var(--design-editor-accent-color)]/10"
|
|
5053
|
+
style={{
|
|
5054
|
+
left: previewRect.x,
|
|
5055
|
+
top: previewRect.y,
|
|
5056
|
+
width: Math.max(1, previewRect.width),
|
|
5057
|
+
height: Math.max(1, previewRect.height),
|
|
5058
|
+
borderRadius: tool === "ellipse" ? "9999px" : undefined,
|
|
5059
|
+
}}
|
|
5060
|
+
/>
|
|
5061
|
+
<span
|
|
5062
|
+
data-creation-preview-size
|
|
5063
|
+
className="pointer-events-none absolute z-10 -translate-x-1/2 translate-y-1 rounded bg-[var(--design-editor-accent-color)] px-1.5 py-0.5 text-[10px] font-medium leading-none text-[var(--design-editor-accent-contrast-color)] shadow-sm"
|
|
5064
|
+
style={{
|
|
5065
|
+
left: previewRect.x + previewRect.width / 2,
|
|
5066
|
+
top: previewRect.y + previewRect.height,
|
|
5067
|
+
}}
|
|
5068
|
+
>
|
|
5069
|
+
{Math.round(previewRect.width)} × {Math.round(previewRect.height)}
|
|
5070
|
+
</span>
|
|
5071
|
+
</>
|
|
5060
5072
|
) : null}
|
|
5061
5073
|
{previewLine ? (
|
|
5062
5074
|
<svg
|
|
@@ -7812,7 +7812,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7812
7812
|
)),
|
|
7813
7813
|
)}
|
|
7814
7814
|
|
|
7815
|
-
{/* Figma-parity alt-hover measurement:
|
|
7815
|
+
{/* Figma-parity alt-hover measurement: orange edge-to-edge distance
|
|
7816
7816
|
lines between the current selection and whatever frame/draft is
|
|
7817
7817
|
under the cursor while Alt is held (pure hover, no drag). */}
|
|
7818
7818
|
{[altHoverMeasurement?.horizontal, altHoverMeasurement?.vertical]
|
|
@@ -7822,7 +7822,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7822
7822
|
.map((line) => (
|
|
7823
7823
|
<span
|
|
7824
7824
|
key={`alt-hover-${line.orientation}`}
|
|
7825
|
-
className="pointer-events-none absolute z-40 bg-
|
|
7825
|
+
className="pointer-events-none absolute z-40 bg-[var(--design-editor-measure-color)]"
|
|
7826
7826
|
style={
|
|
7827
7827
|
line.orientation === "vertical"
|
|
7828
7828
|
? {
|
|
@@ -7857,7 +7857,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7857
7857
|
return (
|
|
7858
7858
|
<span
|
|
7859
7859
|
key={`alt-hover-label-${line.orientation}`}
|
|
7860
|
-
className="pointer-events-none absolute z-40 -translate-x-1/2 -translate-y-1/2 rounded bg-
|
|
7860
|
+
className="pointer-events-none absolute z-40 -translate-x-1/2 -translate-y-1/2 rounded bg-[var(--design-editor-measure-color)] px-1 py-0.5 text-[10px] font-medium leading-none text-white shadow-sm"
|
|
7861
7861
|
style={{
|
|
7862
7862
|
left: pan.x + (SURFACE_PADDING + labelCanvasPoint.x) * scale,
|
|
7863
7863
|
top: pan.y + (SURFACE_PADDING + labelCanvasPoint.y) * scale,
|
|
@@ -7868,6 +7868,37 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7868
7868
|
);
|
|
7869
7869
|
})}
|
|
7870
7870
|
|
|
7871
|
+
{/* Live width × height readout while drawing, pinned below the draft box.
|
|
7872
|
+
Rendered outside the transformed world so it stays a fixed size.
|
|
7873
|
+
Skipped for line/arrow/pen and the pre-drag zero-size state. */}
|
|
7874
|
+
{creationPreview &&
|
|
7875
|
+
creationPreview.tool !== "line" &&
|
|
7876
|
+
creationPreview.tool !== "arrow" &&
|
|
7877
|
+
creationPreview.tool !== "pen" &&
|
|
7878
|
+
creationPreview.geometry.width > 0 &&
|
|
7879
|
+
creationPreview.geometry.height > 0 ? (
|
|
7880
|
+
<span
|
|
7881
|
+
className="pointer-events-none absolute z-40 -translate-x-1/2 translate-y-1 rounded bg-[var(--design-editor-accent-color)] px-1.5 py-0.5 text-[10px] font-medium leading-none text-[var(--design-editor-accent-contrast-color)] shadow-sm"
|
|
7882
|
+
style={{
|
|
7883
|
+
left:
|
|
7884
|
+
pan.x +
|
|
7885
|
+
(SURFACE_PADDING +
|
|
7886
|
+
creationPreview.geometry.x +
|
|
7887
|
+
creationPreview.geometry.width / 2) *
|
|
7888
|
+
scale,
|
|
7889
|
+
top:
|
|
7890
|
+
pan.y +
|
|
7891
|
+
(SURFACE_PADDING +
|
|
7892
|
+
creationPreview.geometry.y +
|
|
7893
|
+
creationPreview.geometry.height) *
|
|
7894
|
+
scale,
|
|
7895
|
+
}}
|
|
7896
|
+
>
|
|
7897
|
+
{Math.round(creationPreview.geometry.width)} ×{" "}
|
|
7898
|
+
{Math.round(creationPreview.geometry.height)}
|
|
7899
|
+
</span>
|
|
7900
|
+
) : null}
|
|
7901
|
+
|
|
7871
7902
|
{/* Equal-gap distance labels render outside the pan/scale-transformed
|
|
7872
7903
|
world container (same reasoning as the marquee/duplicate-preview
|
|
7873
7904
|
overlays above it) so they need the explicit
|
|
@@ -100,6 +100,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
100
100
|
"",
|
|
101
101
|
);
|
|
102
102
|
chromeTransitionStyle.textContent =
|
|
103
|
+
"html{overflow:clip}" /* prevent negative-offset handles from expanding the iframe */ +
|
|
103
104
|
'[data-agent-native-edit-overlay="selection"]{transition:border-width 150ms ease-out}' +
|
|
104
105
|
'[data-agent-native-empty-text-editing="true"] [data-agent-native-edit-overlay="selection"]{display:none!important}' +
|
|
105
106
|
"[data-agent-native-text-editing]{outline:none!important;outline-offset:0!important}" +
|
|
@@ -1844,17 +1845,46 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
1844
1845
|
}
|
|
1845
1846
|
selectionOverlay.appendChild(handle);
|
|
1846
1847
|
});
|
|
1847
|
-
|
|
1848
|
-
var
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1848
|
+
(function () {
|
|
1849
|
+
var baseAngles = { nw: 270, ne: 0, se: 90, sw: 180 };
|
|
1850
|
+
function rotateCursorUri(angleDeg) {
|
|
1851
|
+
var svg =
|
|
1852
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">' +
|
|
1853
|
+
'<g transform="rotate(' +
|
|
1854
|
+
angleDeg +
|
|
1855
|
+
' 10 10)" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">' +
|
|
1856
|
+
'<path d="M 4 8 A 7 7 0 0 1 16 8" stroke="white" stroke-width="3.5"/>' +
|
|
1857
|
+
'<path d="M 4 8 A 7 7 0 0 1 16 8"/>' +
|
|
1858
|
+
'<path d="M 12.5 3.5 L 16 8 L 11 8.5" fill="white" stroke="white" stroke-width="3.5" stroke-linejoin="round"/>' +
|
|
1859
|
+
'<path d="M 12.5 3.5 L 16 8 L 11 8.5" fill="black"/>' +
|
|
1860
|
+
"</g></svg>";
|
|
1861
|
+
return (
|
|
1862
|
+
'url("data:image/svg+xml,' + encodeURIComponent(svg) + '") 10 10, grab'
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
["nw", "ne", "se", "sw"].forEach(function (pos) {
|
|
1866
|
+
var handle = document.createElement("span");
|
|
1867
|
+
handle.setAttribute("data-agent-native-rotate-handle", pos);
|
|
1868
|
+
handle.style.cssText =
|
|
1869
|
+
"position:absolute;width:28px;height:28px;border-radius:999px;pointer-events:auto;";
|
|
1870
|
+
handle.style.cursor = rotateCursorUri(baseAngles[pos]);
|
|
1871
|
+
if (pos.indexOf("n") !== -1) handle.style.top = "-34px";
|
|
1872
|
+
if (pos.indexOf("s") !== -1) handle.style.bottom = "-34px";
|
|
1873
|
+
if (pos.indexOf("w") !== -1) handle.style.left = "-34px";
|
|
1874
|
+
if (pos.indexOf("e") !== -1) handle.style.right = "-34px";
|
|
1875
|
+
selectionOverlay.appendChild(handle);
|
|
1876
|
+
});
|
|
1877
|
+
var button = document.createElement("span");
|
|
1878
|
+
button.setAttribute("data-agent-native-rotate-handle", "top-center");
|
|
1879
|
+
button.style.cssText =
|
|
1880
|
+
"position:absolute;left:50%;transform:translateX(-50%);top:-22px;" +
|
|
1881
|
+
"width:16px;height:16px;pointer-events:auto;" +
|
|
1882
|
+
"display:flex;align-items:center;justify-content:center;" +
|
|
1883
|
+
"border-radius:999px;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.3);" +
|
|
1884
|
+
"cursor:grab;user-select:none;font-size:10px;line-height:1;color:#333;";
|
|
1885
|
+
button.textContent = "↻";
|
|
1886
|
+
selectionOverlay.appendChild(button);
|
|
1887
|
+
})();
|
|
1858
1888
|
var spacingOverlay = document.createElement("div");
|
|
1859
1889
|
spacingOverlay.setAttribute("data-agent-native-spacing-overlay", "");
|
|
1860
1890
|
spacingOverlay.style.cssText =
|
|
@@ -1967,6 +1997,12 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
1967
1997
|
|
|
1968
1998
|
var measurementOverlay = document.createElement("div");
|
|
1969
1999
|
measurementOverlay.setAttribute("data-agent-native-measurement-overlay", "");
|
|
2000
|
+
// Tag as an edit overlay so the content-replacement path preserves it (only
|
|
2001
|
+
// [data-agent-native-edit-overlay] nodes survive a body rebuild).
|
|
2002
|
+
measurementOverlay.setAttribute(
|
|
2003
|
+
"data-agent-native-edit-overlay",
|
|
2004
|
+
"measurement",
|
|
2005
|
+
);
|
|
1970
2006
|
measurementOverlay.style.cssText =
|
|
1971
2007
|
"position:fixed;inset:0;z-index:100001;display:none;pointer-events:none;color:var(--design-editor-measure-color);font:11px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;";
|
|
1972
2008
|
document.body.appendChild(measurementOverlay);
|
|
@@ -3845,50 +3881,44 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
3845
3881
|
.querySelectorAll("[data-agent-native-rotate-handle]")
|
|
3846
3882
|
.forEach(function (handle) {
|
|
3847
3883
|
var pos = handle.getAttribute("data-agent-native-rotate-handle") || "";
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3884
|
+
if (pos === "top-center") {
|
|
3885
|
+
var buttonScale = Math.min(sx, sy);
|
|
3886
|
+
handle.style.width = 16 * buttonScale + "px";
|
|
3887
|
+
handle.style.height = 16 * buttonScale + "px";
|
|
3888
|
+
handle.style.fontSize = 10 * buttonScale + "px";
|
|
3889
|
+
handle.style.top = -22 * sy + "px";
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
var size = Math.min(sx, sy);
|
|
3893
|
+
handle.style.width = 28 * size + "px";
|
|
3894
|
+
handle.style.height = 28 * size + "px";
|
|
3895
|
+
if (pos.indexOf("n") !== -1) handle.style.top = -34 * sy + "px";
|
|
3896
|
+
if (pos.indexOf("s") !== -1) handle.style.bottom = -34 * sy + "px";
|
|
3897
|
+
if (pos.indexOf("w") !== -1) handle.style.left = -34 * sx + "px";
|
|
3898
|
+
if (pos.indexOf("e") !== -1) handle.style.right = -34 * sx + "px";
|
|
3854
3899
|
});
|
|
3855
3900
|
}
|
|
3856
3901
|
|
|
3857
|
-
//
|
|
3858
|
-
// highlightOverlay, and the passive multi-selection overlays: prefer the CSS
|
|
3859
|
-
// box + rotation transform so the outline hugs the rotated element rather
|
|
3860
|
-
// than its inflated axis-aligned bounding box. Returns true when it placed
|
|
3861
|
-
// the overlay (caller should skip the AABB fallback), false when the element
|
|
3862
|
-
// has no usable local box (falls back to getBoundingClientRect).
|
|
3902
|
+
// Returns true when the overlay was placed with the element's rotated CSS box.
|
|
3863
3903
|
function positionOverlayForRotatedLocalBox(
|
|
3864
3904
|
overlay: HTMLElement,
|
|
3865
3905
|
el: Element,
|
|
3866
3906
|
): boolean {
|
|
3867
3907
|
var elCs = window.getComputedStyle(el);
|
|
3868
|
-
var
|
|
3869
|
-
var
|
|
3870
|
-
var elW = readFinitePx(el.style.width || elCs.width);
|
|
3871
|
-
var elH = readFinitePx(el.style.height || elCs.height);
|
|
3908
|
+
var elW = readFinitePx((el as HTMLElement).style.width || elCs.width);
|
|
3909
|
+
var elH = readFinitePx((el as HTMLElement).style.height || elCs.height);
|
|
3872
3910
|
var elRot = currentRotation(el);
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
elW !== null &&
|
|
3878
|
-
elH !== null;
|
|
3879
|
-
if (!canUseLocalBox) return false;
|
|
3880
|
-
// Convert element-local left/top to viewport coords by walking to the
|
|
3881
|
-
// nearest positioned ancestor (same reference frame as getBoundingClientRect).
|
|
3882
|
-
var parentRect = (
|
|
3883
|
-
(el as HTMLElement).offsetParent || document.documentElement
|
|
3884
|
-
).getBoundingClientRect();
|
|
3911
|
+
if (Math.abs(elRot) < 0.01 || elW === null || elH === null) return false;
|
|
3912
|
+
var rect = (el as HTMLElement).getBoundingClientRect();
|
|
3913
|
+
var cx = rect.left + rect.width / 2;
|
|
3914
|
+
var cy = rect.top + rect.height / 2;
|
|
3885
3915
|
overlay.style.display = "block";
|
|
3886
|
-
overlay.style.left =
|
|
3887
|
-
overlay.style.top =
|
|
3916
|
+
overlay.style.left = cx - elW / 2 + "px";
|
|
3917
|
+
overlay.style.top = cy - elH / 2 + "px";
|
|
3888
3918
|
overlay.style.width = elW + "px";
|
|
3889
3919
|
overlay.style.height = elH + "px";
|
|
3890
3920
|
overlay.style.transform = "rotate(" + elRot + "deg)";
|
|
3891
|
-
overlay.style.transformOrigin = "
|
|
3921
|
+
overlay.style.transformOrigin = "50% 50%";
|
|
3892
3922
|
return true;
|
|
3893
3923
|
}
|
|
3894
3924
|
|
|
@@ -4212,6 +4242,11 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
4212
4242
|
}
|
|
4213
4243
|
var selectedRect = a.getBoundingClientRect();
|
|
4214
4244
|
var hoverRect = b.getBoundingClientRect();
|
|
4245
|
+
// A content re-render can rebuild document.body and drop this overlay;
|
|
4246
|
+
// re-attach it before drawing so the lines always render.
|
|
4247
|
+
if (!measurementOverlay.isConnected) {
|
|
4248
|
+
document.body.appendChild(measurementOverlay);
|
|
4249
|
+
}
|
|
4215
4250
|
measurementOverlay.innerHTML = "";
|
|
4216
4251
|
measurementOverlay.style.display = "block";
|
|
4217
4252
|
|
|
@@ -5631,32 +5666,66 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
5631
5666
|
},
|
|
5632
5667
|
);
|
|
5633
5668
|
|
|
5634
|
-
function
|
|
5635
|
-
var
|
|
5636
|
-
el.style.transform || window.getComputedStyle(el).transform || "";
|
|
5637
|
-
var match = transform.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/);
|
|
5669
|
+
function rotationFromTransform(transform) {
|
|
5670
|
+
var match = transform.match(/rotate(?:Z)?\((-?\d+(?:\.\d+)?)deg\)/i);
|
|
5638
5671
|
if (match) return parseFloat(match[1]) || 0;
|
|
5639
5672
|
if (transform && transform !== "none" && window.DOMMatrixReadOnly) {
|
|
5640
5673
|
try {
|
|
5641
5674
|
var matrix = new DOMMatrixReadOnly(transform);
|
|
5642
|
-
return
|
|
5675
|
+
return (Math.atan2(matrix.b, matrix.a) * 180) / Math.PI;
|
|
5643
5676
|
} catch (err) {}
|
|
5644
5677
|
}
|
|
5645
5678
|
return 0;
|
|
5646
5679
|
}
|
|
5647
5680
|
|
|
5648
|
-
function
|
|
5649
|
-
var
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5681
|
+
function independentRotation(rotate) {
|
|
5682
|
+
var match = rotate.match(
|
|
5683
|
+
/^(?:z\s+)?(-?\d+(?:\.\d+)?)(deg|grad|rad|turn)$/i,
|
|
5684
|
+
);
|
|
5685
|
+
if (!match) return 0;
|
|
5686
|
+
var value = parseFloat(match[1]) || 0;
|
|
5687
|
+
var unit = match[2].toLowerCase();
|
|
5688
|
+
if (unit === "grad") return value * 0.9;
|
|
5689
|
+
if (unit === "rad") return (value * 180) / Math.PI;
|
|
5690
|
+
if (unit === "turn") return value * 360;
|
|
5691
|
+
return value;
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
function currentRotation(el) {
|
|
5695
|
+
var computed = window.getComputedStyle(el);
|
|
5696
|
+
return (
|
|
5697
|
+
rotationFromTransform(computed.transform || "") +
|
|
5698
|
+
independentRotation(computed.rotate || "")
|
|
5699
|
+
);
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
// Merge an ABSOLUTE rotation value (degrees) into a transform string.
|
|
5703
|
+
// When the string already has a rotate/rotateZ(), replace it in place.
|
|
5704
|
+
// When the transform is a matrix() (e.g. computed from a class rule), strip
|
|
5705
|
+
// the rotation component and append the new absolute rotate() so the inline
|
|
5706
|
+
// value only adds what the class doesn't already own as non-rotation parts.
|
|
5707
|
+
// When the string has non-rotation functions (translate, scale, etc.) without
|
|
5708
|
+
// an explicit rotate(), append rotate() so other transforms are preserved.
|
|
5709
|
+
function mergeAbsoluteRotation(transform, degrees) {
|
|
5710
|
+
var rotatePattern = /rotate(?:Z)?\((-?\d+(?:\.\d+)?)deg\)/i;
|
|
5711
|
+
if (rotatePattern.test(transform)) {
|
|
5712
|
+
return transform
|
|
5713
|
+
.replace(rotatePattern, "rotate(" + degrees + "deg)")
|
|
5714
|
+
.trim();
|
|
5715
|
+
}
|
|
5716
|
+
// matrix() is the computed form of a class-rule transform; writing it
|
|
5717
|
+
// inline would hard-pin every property from that rule. Instead start fresh
|
|
5718
|
+
// with just the target rotation so the class still owns everything else.
|
|
5719
|
+
if (/^matrix(?:3d)?\(/i.test(transform.trim())) {
|
|
5720
|
+
return "rotate(" + degrees + "deg)";
|
|
5721
|
+
}
|
|
5722
|
+
// transform string with other functions but no existing rotate: append.
|
|
5723
|
+
return (
|
|
5724
|
+
(transform && transform !== "none" ? transform + " " : "") +
|
|
5725
|
+
"rotate(" +
|
|
5726
|
+
degrees +
|
|
5727
|
+
"deg)"
|
|
5728
|
+
).trim();
|
|
5660
5729
|
}
|
|
5661
5730
|
|
|
5662
5731
|
function ensurePositionable(el) {
|
|
@@ -9115,6 +9184,11 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
9115
9184
|
// null-deref in onMove/onUp.
|
|
9116
9185
|
var rotateEl = selectedEl;
|
|
9117
9186
|
var originalInlineTransform = rotateEl.style.transform;
|
|
9187
|
+
var originalComputedTransform = window.getComputedStyle(rotateEl).transform;
|
|
9188
|
+
var baseTransform =
|
|
9189
|
+
originalInlineTransform && originalInlineTransform !== "none"
|
|
9190
|
+
? originalInlineTransform
|
|
9191
|
+
: originalComputedTransform;
|
|
9118
9192
|
function onMove(ev) {
|
|
9119
9193
|
if (!rotateEl) return;
|
|
9120
9194
|
var pointerAngle =
|
|
@@ -9123,7 +9197,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
9123
9197
|
var next = originRotation + pointerAngle - originAngle;
|
|
9124
9198
|
if (ev.shiftKey) next = Math.round(next / 15) * 15;
|
|
9125
9199
|
next = Math.round(next);
|
|
9126
|
-
rotateEl.style.transform =
|
|
9200
|
+
rotateEl.style.transform = mergeAbsoluteRotation(baseTransform, next);
|
|
9127
9201
|
showTransformBadge(next + "deg", ev.clientX, ev.clientY);
|
|
9128
9202
|
refreshOverlays();
|
|
9129
9203
|
}
|
|
@@ -10303,7 +10377,11 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
10303
10377
|
} else {
|
|
10304
10378
|
hideMeasurements();
|
|
10305
10379
|
}
|
|
10306
|
-
|
|
10380
|
+
// While Alt is held (measurement mode) keep hover local: posting it would
|
|
10381
|
+
// update the host's hoveredSelector, re-run replayIframeEditorState, and
|
|
10382
|
+
// echo selection/hover back every move — a loop that jitters selection
|
|
10383
|
+
// and flickers the measurement lines.
|
|
10384
|
+
if (!e.altKey && hoveredEl !== lastHoverInfoPostedEl) {
|
|
10307
10385
|
lastHoverInfoPostedEl = hoveredEl;
|
|
10308
10386
|
var info = getLightElementInfo(hoveredEl);
|
|
10309
10387
|
(window.parent as Window).postMessage(
|
|
@@ -10365,7 +10443,19 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
|
10365
10443
|
window.addEventListener(
|
|
10366
10444
|
"keyup",
|
|
10367
10445
|
function (e) {
|
|
10368
|
-
if (e.key === "Alt")
|
|
10446
|
+
if (e.key === "Alt") {
|
|
10447
|
+
hideMeasurements();
|
|
10448
|
+
// Re-sync the hover suppressed during measurement so the host isn't
|
|
10449
|
+
// left on a stale element until the next pointermove.
|
|
10450
|
+
lastHoverInfoPostedEl = hoveredEl;
|
|
10451
|
+
(window.parent as Window).postMessage(
|
|
10452
|
+
{
|
|
10453
|
+
type: "element-hover",
|
|
10454
|
+
payload: hoveredEl ? getLightElementInfo(hoveredEl) : null,
|
|
10455
|
+
},
|
|
10456
|
+
"*",
|
|
10457
|
+
);
|
|
10458
|
+
}
|
|
10369
10459
|
},
|
|
10370
10460
|
true,
|
|
10371
10461
|
);
|
|
@@ -2678,16 +2678,16 @@ export function parseNumericDraft(draft: string): number | null {
|
|
|
2678
2678
|
}
|
|
2679
2679
|
|
|
2680
2680
|
/**
|
|
2681
|
-
* Expands
|
|
2682
|
-
*
|
|
2683
|
-
*
|
|
2684
|
-
* unparseable. Standard 3/4/6/8-digit hex (already handled by
|
|
2685
|
-
* `parseCssColor`) passes through unchanged.
|
|
2681
|
+
* Expands 1-3 digit hex inputs to their 6-digit Figma-style equivalents.
|
|
2682
|
+
* One digit fills every channel, two digits repeat as a grayscale byte, and
|
|
2683
|
+
* three digits use standard CSS shorthand expansion.
|
|
2686
2684
|
*/
|
|
2687
2685
|
export function expandHexShorthand(value: string): string {
|
|
2688
2686
|
const trimmed = value.trim().replace(/^#/, "");
|
|
2689
|
-
if (/^[0-9a-f]$/i.test(trimmed))
|
|
2690
|
-
|
|
2687
|
+
if (/^[0-9a-f]$/i.test(trimmed)) return trimmed.repeat(6);
|
|
2688
|
+
if (/^[0-9a-f]{2}$/i.test(trimmed)) return trimmed.repeat(3);
|
|
2689
|
+
if (/^[0-9a-f]{3}$/i.test(trimmed)) {
|
|
2690
|
+
return [...trimmed].map((digit) => digit.repeat(2)).join("");
|
|
2691
2691
|
}
|
|
2692
2692
|
return trimmed;
|
|
2693
2693
|
}
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
);
|
|
80
80
|
--design-editor-accent-strong-color: var(--design-editor-accent-color);
|
|
81
81
|
--design-editor-accent-contrast-color: #ffffff;
|
|
82
|
-
--design-editor-measure-color: hsl(
|
|
82
|
+
--design-editor-measure-color: hsl(24 95% 53%);
|
|
83
83
|
--design-editor-panel-bg: hsl(var(--background));
|
|
84
84
|
--design-editor-panel-raised-bg: hsl(var(--secondary));
|
|
85
85
|
--design-editor-control-bg: hsl(var(--muted));
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
--design-editor-panel-divider-color: hsl(0 0% 22%);
|
|
163
163
|
--design-editor-accent-strong-color: var(--design-editor-accent-color);
|
|
164
164
|
--design-editor-accent-contrast-color: #ffffff;
|
|
165
|
-
--design-editor-measure-color: hsl(
|
|
165
|
+
--design-editor-measure-color: hsl(24 95% 53%);
|
|
166
166
|
--design-editor-panel-bg: hsl(0 0% 13%);
|
|
167
167
|
--design-editor-panel-raised-bg: hsl(0 0% 18%);
|
|
168
168
|
--design-editor-control-bg: hsl(0 0% 18%);
|