@agent-native/core-corpus 0.209.2 → 0.209.3-nightly-20261016183441

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 (62) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  3. package/corpus/templates/analytics/actions/update-dashboard.ts +9 -0
  4. package/corpus/templates/analytics/app/components/auth/SqlQuerySnapshotSessionBridge.tsx +25 -0
  5. package/corpus/templates/analytics/app/components/dashboard/PanelFreshnessClock.tsx +140 -0
  6. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +104 -48
  7. package/corpus/templates/analytics/app/components/dashboard/panel-freshness.ts +171 -0
  8. package/corpus/templates/analytics/app/global.css +59 -0
  9. package/corpus/templates/analytics/app/i18n/en-US.ts +8 -0
  10. package/corpus/templates/analytics/app/lib/cost-tracker.ts +122 -16
  11. package/corpus/templates/analytics/app/lib/query-metrics.ts +50 -13
  12. package/corpus/templates/analytics/app/lib/sql-query-cache.ts +697 -0
  13. package/corpus/templates/analytics/app/lib/sql-query-freshness.ts +85 -0
  14. package/corpus/templates/analytics/app/lib/sql-query-snapshot-session.ts +145 -0
  15. package/corpus/templates/analytics/app/lib/sql-query.ts +366 -36
  16. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +73 -18
  17. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +45 -12
  18. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +5 -0
  19. package/corpus/templates/analytics/app/root.tsx +2 -0
  20. package/corpus/templates/analytics/changelog/2026-10-14-dashboard-charts-now-reappear-instantly-after-a-reload-from-.md +6 -0
  21. package/corpus/templates/analytics/changelog/2026-10-14-dashboard-panels-show-fetch-time-in-a-clock-and-label-only-st.md +6 -0
  22. package/corpus/templates/analytics/changelog/2026-10-15-dashboard-query-costs-and-refresh-confirmations-reflect-actu.md +6 -0
  23. package/corpus/templates/analytics/changelog/2026-10-15-dashboard-results-disappear-when-access-is-withdrawn-and-exp.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-10-16-dashboard-freshness-stays-in-the-title-row-with-compact-upda.md +6 -0
  25. package/corpus/templates/analytics/changelog/2026-10-16-dashboard-update-status-remains-visible-for-sources-without-.md +6 -0
  26. package/corpus/templates/analytics/server/lib/bigquery.ts +35 -4
  27. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +2 -3
  28. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +9 -6
  29. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +2 -2
  30. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  31. package/corpus/templates/analytics/shared/sql-query-contract.ts +49 -0
  32. package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  33. package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  34. package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  35. package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  36. package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  37. package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  38. package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  39. package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  40. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +24 -0
  41. package/corpus/templates/design/app/components/design/types.ts +1 -0
  42. package/corpus/templates/design/app/lib/design-import.ts +35 -0
  43. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +11 -0
  44. package/corpus/templates/design/app/pages/design-editor/command-types.ts +2 -0
  45. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +3 -0
  46. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +19 -0
  47. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +118 -20
  48. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-layer-actions.ts +15 -3
  49. package/corpus/templates/design/app/pages/design-editor/relative-position-offset.ts +109 -0
  50. package/corpus/templates/design/changelog/2026-10-15-dragged-layers-use-the-final-release-position.md +6 -0
  51. package/corpus/templates/design/changelog/2026-10-15-repeated-pastes-preserve-relative-layer-offsets.md +6 -0
  52. package/corpus/templates/design/changelog/2026-10-15-text-wrapping-hug-widths.md +6 -0
  53. package/corpus/templates/design/shared/code-layer.ts +2 -1
  54. package/corpus/templates/design/shared/relative-position-containing-block.ts +40 -0
  55. package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  56. package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  57. package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  58. package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  59. package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  60. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +137 -118
  61. package/corpus/templates/slides/changelog/2026-10-16-search-and-preview-reference-decks-before-choosing-one.md +6 -0
  62. package/package.json +1 -1
@@ -756,6 +756,17 @@ function setRootLayerPosition(
756
756
  return false;
757
757
  }
758
758
  if (position.positionMode === "relative") {
759
+ host.style.removeProperty("inset");
760
+ [
761
+ ["inset-inline", "auto"],
762
+ ["inset-inline-start", "auto"],
763
+ ["inset-inline-end", "auto"],
764
+ ["inset-block", "auto"],
765
+ ["inset-block-start", "auto"],
766
+ ["inset-block-end", "auto"],
767
+ ["right", "auto"],
768
+ ["bottom", "auto"],
769
+ ].forEach(([property, value]) => host.style.setProperty(property!, value!));
759
770
  host.style.position = "relative";
760
771
  } else {
761
772
  host.style.removeProperty("inset");
@@ -7,6 +7,7 @@ import type {
7
7
  } from "@/components/design/types";
8
8
  import type { UploadedFile } from "@/components/editor/PromptDialog";
9
9
  import type { DesignClipboardManagedStyleSnapshot } from "@/lib/design-clipboard-managed-styles";
10
+ import type { RelativePositionOffset } from "@/lib/design-import";
10
11
 
11
12
  import type { PendingLiveStructureEdit } from "./pending-edits";
12
13
 
@@ -74,6 +75,7 @@ export interface CanvasLayerClipboardEntry {
74
75
  rootNodeId?: string;
75
76
  sourceParentNodeId?: string;
76
77
  sourceFileId: string;
78
+ sourceRelativeOffset?: RelativePositionOffset | null;
77
79
  portableStyleSnapshot?: PortableStyleSnapshot;
78
80
  styleSnapshotCaptureFailed?: boolean;
79
81
  managedStyleSnapshot?: DesignClipboardManagedStyleSnapshot;
@@ -113,6 +113,9 @@ export async function runCopySelection({
113
113
  rootNodeId: snapshot.rootNodeId,
114
114
  sourceParentNodeId: snapshot.sourceParentNodeId,
115
115
  sourceFileId: snapshot.sourceFileId,
116
+ ...(snapshot.sourceRelativeOffset !== undefined
117
+ ? { sourceRelativeOffset: snapshot.sourceRelativeOffset }
118
+ : {}),
116
119
  ...portableStylesForClipboard(snapshot, readPortableStyleSnapshot),
117
120
  managedStyleSnapshot: snapshot.managedStyleSnapshot,
118
121
  }));
@@ -21,6 +21,7 @@ import type {
21
21
  } from "@/pages/design-editor/command-types";
22
22
  import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
23
23
  import { shouldUseRuntimeLayerProjection } from "@/pages/design-editor/pending-edits";
24
+ import { effectiveRelativePositionOffset } from "@/pages/design-editor/relative-position-offset";
24
25
  import type { DesignFile } from "@/pages/design-editor/types";
25
26
 
26
27
  function getSourceParentNodeId(
@@ -134,11 +135,21 @@ export function runGetSelectedLayerSnapshots({
134
135
  : undefined;
135
136
  const styleSnapshotCaptureFailed =
136
137
  selectedInfo?.styleSnapshotCaptureFailed ? true : undefined;
138
+ const sourceRelativeOffset =
139
+ selectedInfo?.computedStyles?.position === "relative"
140
+ ? effectiveRelativePositionOffset(
141
+ selectedInfo.computedStyles,
142
+ selectedInfo.relativeContainingBlockDirection,
143
+ )
144
+ : undefined;
137
145
  snapshots.push({
138
146
  html,
139
147
  rootNodeId: node.dataAttributes["data-agent-native-node-id"] ?? node.id,
140
148
  sourceParentNodeId: getSourceParentNodeId(projection, node),
141
149
  sourceFileId: file.id,
150
+ ...(selectedInfo?.computedStyles?.position === "relative"
151
+ ? { sourceRelativeOffset }
152
+ : {}),
142
153
  portableStyleSnapshot,
143
154
  styleSnapshotCaptureFailed,
144
155
  managedStyleSnapshot: extractDesignClipboardManagedStyles(
@@ -190,6 +201,14 @@ export function runGetSelectedLayerSnapshots({
190
201
  selectedElement.id,
191
202
  sourceParentNodeId: getSourceParentNodeId(projection, node),
192
203
  sourceFileId: activeFile.id,
204
+ ...(selectedElement.computedStyles?.position === "relative"
205
+ ? {
206
+ sourceRelativeOffset: effectiveRelativePositionOffset(
207
+ selectedElement.computedStyles,
208
+ selectedElement.relativeContainingBlockDirection,
209
+ ),
210
+ }
211
+ : {}),
193
212
  portableStyleSnapshot: selectedElement.portableStyleSnapshot,
194
213
  styleSnapshotCaptureFailed: selectedElement.styleSnapshotCaptureFailed,
195
214
  managedStyleSnapshot: extractDesignClipboardManagedStyles(
@@ -19,7 +19,10 @@ import type {
19
19
  ClipboardContentMutationPublication,
20
20
  } from "@/lib/clipboard-content-lineage";
21
21
  import type { DesignClipboardManagedStyleSnapshot } from "@/lib/design-clipboard-managed-styles";
22
- import type { DesignClipboardScreenEntry } from "@/lib/design-import";
22
+ import type {
23
+ DesignClipboardScreenEntry,
24
+ RelativePositionOffset,
25
+ } from "@/lib/design-import";
23
26
  import {
24
27
  extractLayerPosition,
25
28
  insertClonedHtmlLayers,
@@ -55,6 +58,7 @@ import {
55
58
  resolvePastePlacementForSelection,
56
59
  resolvePasteSourceAnchor,
57
60
  } from "@/pages/design-editor/paste-placement";
61
+ import { effectiveRelativePositionOffset } from "@/pages/design-editor/relative-position-offset";
58
62
  import type { DesignFile } from "@/pages/design-editor/types";
59
63
 
60
64
  import type { ApplyLinkedComponentEdit } from "./linked-component-structure";
@@ -212,7 +216,11 @@ function cloneLayerPositionForRepeat(
212
216
  function clipboardRepeatLayerLayout(args: {
213
217
  position?: string;
214
218
  left?: string;
219
+ right?: string;
215
220
  top?: string;
221
+ bottom?: string;
222
+ parentDirection?: string;
223
+ relativeOffset?: RelativePositionOffset | null;
216
224
  unknownPosition?: boolean;
217
225
  }): ClipboardRepeatLayerLayout {
218
226
  const position = args.position?.trim().toLowerCase();
@@ -223,11 +231,26 @@ function clipboardRepeatLayerLayout(args: {
223
231
  : { kind: "unavailable" };
224
232
  }
225
233
  if (position === "relative") {
234
+ const hasSourceOffset = Object.prototype.hasOwnProperty.call(
235
+ args,
236
+ "relativeOffset",
237
+ );
238
+ const relativeOffset = hasSourceOffset
239
+ ? args.relativeOffset
240
+ : effectiveRelativePositionOffset(
241
+ {
242
+ left: args.left,
243
+ right: args.right,
244
+ top: args.top,
245
+ bottom: args.bottom,
246
+ },
247
+ args.parentDirection,
248
+ );
226
249
  return {
227
250
  kind: "flow",
228
- position: isPixelPosition(args.left, args.top),
251
+ position: relativeOffset ?? null,
229
252
  positionMode: "relative",
230
- unknownPosition: false,
253
+ unknownPosition: relativeOffset === null,
231
254
  };
232
255
  }
233
256
  return {
@@ -254,15 +277,41 @@ function clipboardRepeatSourceLayout(
254
277
  const snapshotStyles = entry.portableStyleSnapshot?.nodes.find(
255
278
  (node) => node.path.length === 0,
256
279
  )?.styles;
257
- const position =
258
- root.style.getPropertyValue("position") || snapshotStyles?.position;
280
+ const hasRelativeOffsetMetadata = Object.prototype.hasOwnProperty.call(
281
+ entry,
282
+ "sourceRelativeOffset",
283
+ );
284
+ const position = hasRelativeOffsetMetadata
285
+ ? "relative"
286
+ : root.style.getPropertyValue("position") || snapshotStyles?.position;
259
287
  const left = root.style.left || snapshotStyles?.left;
288
+ const right = root.style.right || snapshotStyles?.right;
260
289
  const top = root.style.top || snapshotStyles?.top;
290
+ const bottom = root.style.bottom || snapshotStyles?.bottom;
261
291
  const unknownPosition = !position;
292
+ const hasRelativeShorthand = [
293
+ "inset",
294
+ "inset-block",
295
+ "inset-block-end",
296
+ "inset-block-start",
297
+ "inset-inline",
298
+ "inset-inline-end",
299
+ "inset-inline-start",
300
+ ].some(
301
+ (property) =>
302
+ root.style.getPropertyValue(property) || snapshotStyles?.[property],
303
+ );
262
304
  return clipboardRepeatLayerLayout({
263
305
  position,
264
306
  left,
307
+ right,
265
308
  top,
309
+ bottom,
310
+ ...(hasRelativeOffsetMetadata
311
+ ? { relativeOffset: entry.sourceRelativeOffset }
312
+ : hasRelativeShorthand
313
+ ? { relativeOffset: null }
314
+ : {}),
266
315
  unknownPosition,
267
316
  });
268
317
  } catch {
@@ -273,7 +322,20 @@ function clipboardRepeatSourceLayout(
273
322
  function clipboardRepeatSelectedCloneLayout(args: {
274
323
  html?: string;
275
324
  inlineStyles?: Record<string, string>;
325
+ computedStyles?: Record<string, string>;
326
+ parentDirection?: string;
276
327
  }): ClipboardRepeatLayerLayout {
328
+ if (args.computedStyles?.position) {
329
+ return clipboardRepeatLayerLayout({
330
+ position: args.computedStyles.position,
331
+ left: args.computedStyles.left,
332
+ right: args.computedStyles.right,
333
+ top: args.computedStyles.top,
334
+ bottom: args.computedStyles.bottom,
335
+ parentDirection: args.parentDirection,
336
+ unknownPosition: false,
337
+ });
338
+ }
277
339
  if (args.html !== undefined) {
278
340
  if (typeof window === "undefined") return { kind: "unavailable" };
279
341
  try {
@@ -287,7 +349,9 @@ function clipboardRepeatSelectedCloneLayout(args: {
287
349
  return clipboardRepeatLayerLayout({
288
350
  position: root.style.position,
289
351
  left: root.style.left,
352
+ right: root.style.right,
290
353
  top: root.style.top,
354
+ bottom: root.style.bottom,
291
355
  unknownPosition: !root.style.position,
292
356
  });
293
357
  } catch {
@@ -297,7 +361,10 @@ function clipboardRepeatSelectedCloneLayout(args: {
297
361
  return clipboardRepeatLayerLayout({
298
362
  position: args.inlineStyles?.position,
299
363
  left: args.inlineStyles?.left,
364
+ right: args.inlineStyles?.right,
300
365
  top: args.inlineStyles?.top,
366
+ bottom: args.inlineStyles?.bottom,
367
+ parentDirection: args.parentDirection,
301
368
  });
302
369
  }
303
370
 
@@ -312,7 +379,10 @@ function positionsFromSelectedClipboardClone(
312
379
  }
313
380
  if (
314
381
  sourceLayouts.some((layout) => layout.kind === "unavailable") ||
315
- selectedCloneLayout.kind === "unavailable"
382
+ selectedCloneLayout.kind === "unavailable" ||
383
+ (selectedCloneLayout.kind === "flow" &&
384
+ selectedCloneLayout.positionMode === "relative" &&
385
+ selectedCloneLayout.unknownPosition)
316
386
  ) {
317
387
  return null;
318
388
  }
@@ -320,16 +390,36 @@ function positionsFromSelectedClipboardClone(
320
390
  layout.kind === "unavailable" ? null : layout.position,
321
391
  );
322
392
  const hasSourcePosition = sourcePositions.some(Boolean);
393
+ if (
394
+ sourceLayouts.some(
395
+ (layout) =>
396
+ layout.kind === "flow" &&
397
+ layout.positionMode === "relative" &&
398
+ layout.unknownPosition,
399
+ )
400
+ ) {
401
+ return null;
402
+ }
323
403
  const unknownPositionInMixedRoots =
324
404
  hasSourcePosition &&
325
405
  sourceLayouts.some(
326
- (layout) => layout.kind === "flow" && layout.unknownPosition,
406
+ (layout) =>
407
+ layout.kind === "flow" &&
408
+ layout.positionMode !== "relative" &&
409
+ layout.unknownPosition,
327
410
  );
328
411
  if (unknownPositionInMixedRoots) return null;
329
412
  const naturalFlowRoots = sourceLayouts.map(
330
413
  (layout) => layout.kind === "flow",
331
414
  );
332
- if (naturalFlowRoots.every(Boolean) && selectedCloneLayout.kind === "flow") {
415
+ const allUnpositionedFlowRoots = sourceLayouts.every(
416
+ (layout) => layout.kind === "flow" && layout.positionMode !== "relative",
417
+ );
418
+ if (
419
+ allUnpositionedFlowRoots &&
420
+ naturalFlowRoots.every(Boolean) &&
421
+ selectedCloneLayout.kind === "flow"
422
+ ) {
333
423
  return undefined;
334
424
  }
335
425
  const selectedClonePosition = selectedCloneLayout.position;
@@ -466,13 +556,7 @@ function positionsFromSource(
466
556
  cascadeOffset = 0,
467
557
  ) {
468
558
  if (cascadeOffset === 0) return undefined;
469
- if (
470
- sourceLayouts.some(
471
- (layout) =>
472
- layout.kind === "unavailable" ||
473
- (layout.kind === "flow" && layout.unknownPosition),
474
- )
475
- ) {
559
+ if (sourceLayouts.some((layout) => layout.kind === "unavailable")) {
476
560
  return undefined;
477
561
  }
478
562
  return sourceLayouts.map((layout) =>
@@ -821,6 +905,7 @@ export async function runPasteSelection(
821
905
  const sourceParentAnchor = sourceParentNodeId
822
906
  ? { selector: "", sourceId: sourceParentNodeId }
823
907
  : null;
908
+ const sourceLayouts = entries.map(clipboardRepeatSourceLayout);
824
909
  const sourcePositions = entries.map((entry) =>
825
910
  extractLayerPosition(entry.html),
826
911
  );
@@ -865,20 +950,19 @@ export async function runPasteSelection(
865
950
  const selectedCloneLayout = selectedPastedLayer
866
951
  ? clipboardRepeatSelectedCloneLayout({
867
952
  inlineStyles: selectedElement?.inlineStyles,
953
+ computedStyles: selectedElement?.computedStyles,
954
+ parentDirection: selectedElement?.relativeContainingBlockDirection,
868
955
  })
869
956
  : null;
870
957
  const selectedSourcePositions = selectedPastedLayer
871
958
  ? positionsFromSelectedClipboardClone(
872
- entries.map(clipboardRepeatSourceLayout),
959
+ sourceLayouts,
873
960
  selectedPastedLayer.entryIndex,
874
961
  selectedCloneLayout ?? { kind: "unavailable" },
875
962
  cascadeOffset,
876
963
  )
877
964
  : directlyCopiedAnchor
878
- ? positionsFromSource(
879
- entries.map(clipboardRepeatSourceLayout),
880
- cascadeOffset,
881
- )
965
+ ? positionsFromSource(sourceLayouts, cascadeOffset)
882
966
  : undefined;
883
967
  if (selectedSourcePositions === null) {
884
968
  toast.error(t("designEditor.toasts.layerMoveFailed"), {
@@ -903,6 +987,10 @@ export async function runPasteSelection(
903
987
  space: "visual" as const,
904
988
  };
905
989
  }
990
+ const layout = sourceLayouts[index];
991
+ if (layout?.kind === "flow" && layout.positionMode === "relative") {
992
+ return undefined;
993
+ }
906
994
  return source
907
995
  ? {
908
996
  x: source.x + cascadeOffset,
@@ -1162,10 +1250,15 @@ export async function runPasteSelection(
1162
1250
  selectedNode.source.start,
1163
1251
  selectedNode.source.end,
1164
1252
  ),
1253
+ computedStyles: selectedElement?.computedStyles,
1254
+ parentDirection: selectedElement?.relativeContainingBlockDirection,
1165
1255
  })
1166
1256
  : selectedPastedLayer
1167
1257
  ? clipboardRepeatSelectedCloneLayout({
1168
1258
  inlineStyles: selectedElement?.inlineStyles,
1259
+ computedStyles: selectedElement?.computedStyles,
1260
+ parentDirection:
1261
+ selectedElement?.relativeContainingBlockDirection,
1169
1262
  })
1170
1263
  : null;
1171
1264
  const selectedSourcePositions = selectedClipboardRoot
@@ -1234,6 +1327,7 @@ export async function runPasteSelection(
1234
1327
  const sourcePositions = entries.map((entry) =>
1235
1328
  extractLayerPosition(entry.html),
1236
1329
  );
1330
+ const sourceLayouts = entries.map(clipboardRepeatSourceLayout);
1237
1331
  const positionedSources = sourcePositions.filter(
1238
1332
  (source): source is { x: number; y: number } => Boolean(source),
1239
1333
  );
@@ -1316,6 +1410,10 @@ export async function runPasteSelection(
1316
1410
  space: "visual" as const,
1317
1411
  };
1318
1412
  }
1413
+ const layout = sourceLayouts[index];
1414
+ if (layout?.kind === "flow" && layout.positionMode === "relative") {
1415
+ return undefined;
1416
+ }
1319
1417
  if (rootFallbackPlacement) {
1320
1418
  return {
1321
1419
  x: ORPHANED_PASTE_INSET + cascadeOffset + index * 16,
@@ -54,6 +54,7 @@ import {
54
54
  measurePositionCoordinateContext,
55
55
  positionCoordinateRenderOffsetForWindow,
56
56
  } from "../position-coordinate-context";
57
+ import { readRelativePositionStyles } from "../relative-position-offset";
57
58
  import {
58
59
  isScreenRootElementInfo,
59
60
  resolveMarqueeAdditive,
@@ -93,7 +94,9 @@ function readRenderedLayerInfo(
93
94
  preferredCodeLayerSelector(owner.node),
94
95
  );
95
96
  if (!element) continue;
96
- const computed = preview.getComputedStyle(element);
97
+ const relativePosition = readRelativePositionStyles(element, (target) =>
98
+ preview.getComputedStyle(target),
99
+ );
97
100
  const parent = element.parentElement;
98
101
  const renderOffset = positionCoordinateRenderOffsetForWindow(preview);
99
102
  if (!renderOffset) continue;
@@ -115,9 +118,10 @@ function readRenderedLayerInfo(
115
118
  sourceLayerIdentity: { screenId: owner.fileId, nodeId: owner.node.id },
116
119
  computedStyles: {
117
120
  ...base.computedStyles,
118
- position: computed.position,
119
- zIndex: computed.zIndex,
121
+ ...relativePosition.computedStyles,
120
122
  },
123
+ relativeContainingBlockDirection:
124
+ relativePosition.relativeContainingBlockDirection,
121
125
  boundingRect: {
122
126
  x: rect.x + scrollX,
123
127
  y: rect.y + scrollY,
@@ -971,9 +975,17 @@ export function useEditorLayerActions({
971
975
  measured.positionContainingBlockOrigin,
972
976
  positionContainingBlockTransform:
973
977
  measured.positionContainingBlockTransform,
978
+ relativeContainingBlockDirection:
979
+ measured.relativeContainingBlockDirection,
974
980
  computedStyles: {
975
981
  ...measured.computedStyles,
976
982
  ...current.computedStyles,
983
+ position: measured.computedStyles.position,
984
+ left: measured.computedStyles.left,
985
+ right: measured.computedStyles.right,
986
+ top: measured.computedStyles.top,
987
+ bottom: measured.computedStyles.bottom,
988
+ zIndex: measured.computedStyles.zIndex,
977
989
  },
978
990
  };
979
991
  return samePlainData(merged, current) ? current : merged;
@@ -0,0 +1,109 @@
1
+ import { relativePositionContainingBlockDirection } from "@shared/relative-position-containing-block";
2
+
3
+ import type { RelativePositionOffset } from "@/lib/design-import";
4
+
5
+ type PositionStyle = Pick<
6
+ CSSStyleDeclaration,
7
+ | "position"
8
+ | "left"
9
+ | "right"
10
+ | "top"
11
+ | "bottom"
12
+ | "zIndex"
13
+ | "display"
14
+ | "direction"
15
+ >;
16
+
17
+ export function readRelativePositionStyles<
18
+ T extends { parentElement: T | null },
19
+ >(
20
+ element: T,
21
+ getComputedStyle: (element: T) => PositionStyle,
22
+ ): {
23
+ computedStyles: Pick<
24
+ Record<string, string>,
25
+ "position" | "left" | "right" | "top" | "bottom" | "zIndex"
26
+ >;
27
+ relativeContainingBlockDirection?: "ltr" | "rtl";
28
+ } {
29
+ const computed = getComputedStyle(element);
30
+ const direction =
31
+ computed.position === "relative"
32
+ ? relativePositionContainingBlockDirection(element, getComputedStyle)
33
+ : undefined;
34
+ return {
35
+ computedStyles: {
36
+ position: computed.position,
37
+ left: computed.left,
38
+ right: computed.right,
39
+ top: computed.top,
40
+ bottom: computed.bottom,
41
+ zIndex: computed.zIndex,
42
+ },
43
+ ...(computed.position === "relative"
44
+ ? { relativeContainingBlockDirection: direction }
45
+ : {}),
46
+ };
47
+ }
48
+
49
+ type RelativeInsetValues = {
50
+ left?: string;
51
+ right?: string;
52
+ top?: string;
53
+ bottom?: string;
54
+ };
55
+
56
+ type ParsedInset =
57
+ | { kind: "auto" }
58
+ | { kind: "missing" }
59
+ | { kind: "value"; value: number }
60
+ | null;
61
+
62
+ function parsePixelInset(value: string | undefined): ParsedInset {
63
+ if (value === undefined) return { kind: "missing" };
64
+ const normalized = value.trim().toLowerCase();
65
+ if (normalized === "auto" || normalized === "") {
66
+ return { kind: "auto" };
67
+ }
68
+ const match = normalized.match(/^([+-]?(?:\d+\.?\d*|\.\d+))(px)?$/);
69
+ if (!match) return null;
70
+ const parsed = Number(match[1]);
71
+ if (!match[2] && parsed !== 0) return null;
72
+ return Number.isFinite(parsed) ? { kind: "value", value: parsed } : null;
73
+ }
74
+
75
+ function resolveAxis(
76
+ startInset: string | undefined,
77
+ endInset: string | undefined,
78
+ startSign: 1 | -1,
79
+ endSign: 1 | -1,
80
+ direction?: string,
81
+ ): number | null {
82
+ const start = parsePixelInset(startInset);
83
+ const end = parsePixelInset(endInset);
84
+ if (!start || !end) return null;
85
+ if (
86
+ (start.kind === "missing" && end.kind !== "value") ||
87
+ (end.kind === "missing" && start.kind !== "value")
88
+ ) {
89
+ return null;
90
+ }
91
+ if (start.kind === "value" && end.kind === "value") {
92
+ if (direction !== "ltr" && direction !== "rtl") return null;
93
+ const useEnd = startSign === 1 && direction === "rtl";
94
+ return useEnd ? end.value * endSign : start.value * startSign;
95
+ }
96
+ if (start.kind === "value") return start.value * startSign;
97
+ if (end.kind === "value") return end.value * endSign;
98
+ return 0;
99
+ }
100
+
101
+ export function effectiveRelativePositionOffset(
102
+ insets: RelativeInsetValues,
103
+ parentDirection?: string,
104
+ ): RelativePositionOffset | null {
105
+ const x = resolveAxis(insets.left, insets.right, 1, -1, parentDirection);
106
+ const y = resolveAxis(insets.top, insets.bottom, 1, -1, "ltr");
107
+ if (x === null || y === null) return null;
108
+ return { x, y };
109
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ Dragged layers use the final pointer release position even when it differs from the last move event, and keep that position after saving and reloading.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ Repeated pastes preserve relative layer offsets in right-to-left layouts and across Screens.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ Text layers retain their individual widths when wrapped in auto layout.
@@ -8616,7 +8616,8 @@ function applyWrapNodes(
8616
8616
  return frag;
8617
8617
  });
8618
8618
 
8619
- const autoLayoutStyle = "display: flex; flex-direction: column; gap: 8px";
8619
+ const autoLayoutStyle =
8620
+ "display: flex; flex-direction: column; align-items: flex-start; gap: 8px";
8620
8621
  const autoLayoutOrigin = autoLayout
8621
8622
  ? (targetGeometry ?? computeAbsoluteUnionOrigin(targetElements))
8622
8623
  : null;
@@ -0,0 +1,40 @@
1
+ type ElementWithParent<T> = {
2
+ parentElement: T | null;
3
+ };
4
+
5
+ type DisplayDirectionStyle = Pick<CSSStyleDeclaration, "display" | "direction">;
6
+
7
+ export function relativePositionContainingBlockDirection<
8
+ T extends ElementWithParent<T>,
9
+ >(
10
+ element: T,
11
+ getComputedStyle: (element: T) => DisplayDirectionStyle,
12
+ ): "ltr" | "rtl" | undefined {
13
+ let ancestor = element.parentElement;
14
+ while (ancestor) {
15
+ const styles = getComputedStyle(ancestor);
16
+ const display = styles.display;
17
+ if (display === "inline" || display === "contents") {
18
+ ancestor = ancestor.parentElement;
19
+ continue;
20
+ }
21
+ if (
22
+ display === "block" ||
23
+ display === "flow-root" ||
24
+ display === "list-item" ||
25
+ display === "inline-block" ||
26
+ display === "flex" ||
27
+ display === "inline-flex" ||
28
+ display === "grid" ||
29
+ display === "inline-grid" ||
30
+ display === "table-cell" ||
31
+ display === "table-caption"
32
+ ) {
33
+ return styles.direction === "ltr" || styles.direction === "rtl"
34
+ ? styles.direction
35
+ : undefined;
36
+ }
37
+ return undefined;
38
+ }
39
+ return undefined;
40
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","core-routes","db","feature-flags","identity-sso","integrations","org-apps-directory","otel","setup-dispatch"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","core-routes","db","otel","transactional-emails"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","core-routes","db","integrations","mail-jobs","oauth-refresh","otel"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["00-mcp","agent-chat","auth","collab","core-routes","db","labs","otel","plan-assets","transactional-emails"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["_dev-upload-stub","agent-chat","auth","collab","config","core-routes","creative-context","db","feature-flags","labs","otel","transactional-emails"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }