@agent-native/core-corpus 0.203.1-nightly-20261007154213 → 0.203.1

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 (40) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +4 -0
  3. package/corpus/templates/clips/app/components/library/library-grid.tsx +23 -13
  4. package/corpus/templates/clips/app/components/library/recording-card.tsx +3 -3
  5. package/corpus/templates/clips/app/hooks/use-library.ts +2 -1
  6. package/corpus/templates/clips/app/routes/_app.trash.tsx +23 -13
  7. package/corpus/templates/content/CHANGELOG.md +2 -0
  8. package/corpus/templates/design/.agents/skills/design-figma-parity/SKILL.md +116 -0
  9. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +183 -4
  10. package/corpus/templates/design/AGENTS.md +1 -0
  11. package/corpus/templates/design/CHANGELOG.md +7 -0
  12. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +252 -3
  13. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +150 -11
  14. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +12 -0
  15. package/corpus/templates/design/app/components/design/types.ts +13 -0
  16. package/corpus/templates/design/app/lib/design-editor-route.ts +11 -0
  17. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +33 -29
  18. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +18 -5
  19. package/corpus/templates/design/app/pages/design-editor/position-coordinate-context.ts +228 -0
  20. package/corpus/templates/design/changelog/2026-10-07-design-keeps-frame-relative-positions-and-selections-stable-.md +6 -0
  21. package/corpus/templates/design/changelog/2026-10-07-position-values-stay-aligned-with-selected-layers-in-layouts.md +6 -0
  22. package/corpus/templates/design/e2e/parity-oracle.ts +144 -0
  23. package/corpus/templates/design/parity/oracle/fig.canvas.evenly-spaced-row-drag.json +45 -0
  24. package/corpus/templates/design/parity/oracle/fig.canvas.overlap-drag.json +46 -0
  25. package/corpus/templates/design/parity/oracle/fig.canvas.position-relative-to-frame.json +57 -0
  26. package/corpus/templates/design/parity/oracle/fig.inspector.align-center-within-group.json +36 -0
  27. package/corpus/templates/design/parity/oracle/fig.inspector.align-left-within-group.json +43 -0
  28. package/corpus/templates/design/parity/oracle/fig.inspector.empty-effects-title.json +42 -0
  29. package/corpus/templates/design/parity/oracle/fig.inspector.empty-fill-title.json +41 -0
  30. package/corpus/templates/design/parity/oracle/fig.inspector.empty-stroke-title.json +39 -0
  31. package/corpus/templates/design/parity/oracle/fig.inspector.populated-section-title.json +41 -0
  32. package/corpus/templates/design/scripts/runtime-budget-zoom.ts +56 -0
  33. package/corpus/templates/design/scripts/runtime-budget.ts +49 -14
  34. package/corpus/templates/dispatch/.agents/skills/recurring-jobs/SKILL.md +16 -1
  35. package/corpus/templates/dispatch/DEVELOPING.md +7 -0
  36. package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
  37. package/corpus/templates/factory/actions/save-factory-automation.ts +17 -5
  38. package/corpus/templates/factory/server/lib/factory-automation-config.ts +55 -0
  39. package/corpus/templates/factory/server/lib/factory-automation-history.ts +116 -14
  40. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -32,4 +32,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core-corpus/corpu
32
32
 
33
33
  ## Generated Counts
34
34
 
35
- - template files: 11326
35
+ - template files: 11341
@@ -11,6 +11,10 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
11
11
  - Save exact session replay screens as PNG screenshots.
12
12
  - Session replays can be saved as screenshots without the replay controls.
13
13
 
14
+ ### Improved
15
+
16
+ - Agent dashboard edits are now checked against the live chart before they save, so the agent no longer reports a chart change that did not happen.
17
+
14
18
  ### Fixed
15
19
 
16
20
  - Collaborators see a consistent dashboard after simultaneous edits
@@ -104,7 +104,7 @@ function Skeleton() {
104
104
  );
105
105
  }
106
106
 
107
- const PAGE_SIZE = 100;
107
+ const PAGE_SIZE = 20;
108
108
 
109
109
  interface FolderTargetRow {
110
110
  id: string;
@@ -278,12 +278,6 @@ export function LibraryGrid({
278
278
  [view, kind, folderId, spaceId, tagFilter],
279
279
  );
280
280
  const { data: totalCount } = useRecordingsCount(countArgs);
281
- const total = totalCount ?? 0;
282
- const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
283
-
284
- useEffect(() => {
285
- if (page > totalPages) setPage(totalPages);
286
- }, [page, totalPages]);
287
281
 
288
282
  const args: ListRecordingsArgs = useMemo(
289
283
  () => ({
@@ -302,6 +296,19 @@ export function LibraryGrid({
302
296
  const { data, isLoading, isError, refetch, isRefetching } =
303
297
  useRecordings(args);
304
298
 
299
+ // A failed count is not zero clips: without it, offer "next" while the page
300
+ // comes back full so later pages stay reachable.
301
+ const totalKnown = typeof totalCount === "number";
302
+ const total = totalKnown ? totalCount : 0;
303
+ const pageIsFull = (data?.recordings.length ?? 0) >= PAGE_SIZE;
304
+ const totalPages = totalKnown
305
+ ? Math.max(1, Math.ceil(total / PAGE_SIZE))
306
+ : page + (pageIsFull ? 1 : 0);
307
+
308
+ useEffect(() => {
309
+ if (totalKnown && page > totalPages) setPage(totalPages);
310
+ }, [totalKnown, page, totalPages]);
311
+
305
312
  const trashRecording = useTrashRecording();
306
313
  const archiveRecording = useArchiveRecording();
307
314
  const restoreRecording = useRestoreRecording();
@@ -889,11 +896,12 @@ export function LibraryGrid({
889
896
  {!isLoading && recordings.length > 0 && totalPages > 1 && (
890
897
  <div className="flex shrink-0 items-center justify-between gap-3 border-t border-border px-5 py-2.5">
891
898
  <span className="text-xs text-muted-foreground">
892
- {t("libraryGrid.paginationRange", {
893
- start: (page - 1) * PAGE_SIZE + 1,
894
- end: (page - 1) * PAGE_SIZE + recordings.length,
895
- total,
896
- })}
899
+ {totalKnown &&
900
+ t("libraryGrid.paginationRange", {
901
+ start: (page - 1) * PAGE_SIZE + 1,
902
+ end: (page - 1) * PAGE_SIZE + recordings.length,
903
+ total,
904
+ })}
897
905
  </span>
898
906
  <div className="flex items-center gap-2">
899
907
  <Button
@@ -907,7 +915,9 @@ export function LibraryGrid({
907
915
  {t("libraryGrid.paginationPrevious")}
908
916
  </Button>
909
917
  <span className="text-xs text-muted-foreground">
910
- {t("libraryGrid.paginationPage", { page, totalPages })}
918
+ {totalKnown
919
+ ? t("libraryGrid.paginationPage", { page, totalPages })
920
+ : page}
911
921
  </span>
912
922
  <Button
913
923
  variant="outline"
@@ -392,7 +392,7 @@ export function RecordingCard({
392
392
  checked={selected}
393
393
  onClick={handleCheckbox}
394
394
  className={cn(
395
- "pointer-events-auto absolute start-2 top-2 z-20 size-5 rounded border-background/80 bg-foreground/25 text-background opacity-70 shadow-sm backdrop-blur-sm transition-[background-color,border-color,opacity] hover:bg-foreground/45",
395
+ "pointer-events-auto absolute start-2 top-2 z-20 size-5 rounded border-background/80 bg-foreground/25 text-background opacity-70 shadow-sm transition-[background-color,border-color,opacity] hover:bg-foreground/45",
396
396
  selectionMode || selected
397
397
  ? "opacity-100 sm:opacity-100"
398
398
  : "sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100",
@@ -414,7 +414,7 @@ export function RecordingCard({
414
414
  )}
415
415
 
416
416
  {!displayFailed && !waitingForStorage && atRiskUpload && (
417
- <div className="absolute inset-x-2 bottom-2 rounded-md border border-amber-500/30 bg-background/95 p-2 text-start shadow-sm backdrop-blur">
417
+ <div className="absolute inset-x-2 bottom-2 rounded-md border border-amber-500/30 bg-background/95 p-2 text-start shadow-sm">
418
418
  <div className="flex items-start gap-2">
419
419
  <IconAlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-500" />
420
420
  <div className="min-w-0 flex-1">
@@ -432,7 +432,7 @@ export function RecordingCard({
432
432
  {(displayFailed || waitingForStorage) && (
433
433
  <div
434
434
  className={cn(
435
- "absolute inset-x-2 bottom-2 rounded-md border bg-background/95 p-2.5 text-start shadow-sm backdrop-blur",
435
+ "absolute inset-x-2 bottom-2 rounded-md border bg-background/95 p-2.5 text-start shadow-sm",
436
436
  waitingForStorage
437
437
  ? "border-primary/30"
438
438
  : "border-destructive/30",
@@ -98,7 +98,8 @@ export function useRecordingsCount(
98
98
  "list-recordings",
99
99
  { ...normalizedArgs, countOnly: true } as any,
100
100
  {
101
- select: (data: any) => (typeof data?.total === "number" ? data.total : 0),
101
+ select: (data: any) =>
102
+ typeof data?.total === "number" ? data.total : undefined,
102
103
  retry: false,
103
104
  throwOnError: false,
104
105
  },
@@ -48,7 +48,7 @@ function Skeleton() {
48
48
  );
49
49
  }
50
50
 
51
- const PAGE_SIZE = 100;
51
+ const PAGE_SIZE = 20;
52
52
 
53
53
  export default function TrashRoute() {
54
54
  const t = useT();
@@ -62,8 +62,6 @@ export default function TrashRoute() {
62
62
 
63
63
  const countArgs = useMemo(() => ({ view: "trash" as const }), []);
64
64
  const { data: totalCount } = useRecordingsCount(countArgs);
65
- const total = totalCount ?? 0;
66
- const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
67
65
 
68
66
  useEffect(() => {
69
67
  setPage(1);
@@ -71,10 +69,6 @@ export default function TrashRoute() {
71
69
  setLastSelectedId(null);
72
70
  }, [sort]);
73
71
 
74
- useEffect(() => {
75
- if (page > totalPages) setPage(totalPages);
76
- }, [page, totalPages]);
77
-
78
72
  const args = useMemo(
79
73
  () => ({
80
74
  view: "trash" as const,
@@ -87,6 +81,19 @@ export default function TrashRoute() {
87
81
  const { data, isLoading, isError, isFetching, refetch } = useRecordings(args);
88
82
  const recordings = (data?.recordings ?? []) as RecordingSummary[];
89
83
 
84
+ // A failed count is not zero clips: without it, offer "next" while the page
85
+ // comes back full so later pages stay reachable.
86
+ const totalKnown = typeof totalCount === "number";
87
+ const total = totalKnown ? totalCount : 0;
88
+ const pageIsFull = recordings.length >= PAGE_SIZE;
89
+ const totalPages = totalKnown
90
+ ? Math.max(1, Math.ceil(total / PAGE_SIZE))
91
+ : page + (pageIsFull ? 1 : 0);
92
+
93
+ useEffect(() => {
94
+ if (totalKnown && page > totalPages) setPage(totalPages);
95
+ }, [totalKnown, page, totalPages]);
96
+
90
97
  const restore = useActionMutation<any, { id: string }>("restore-recording");
91
98
  const purge = useActionMutation<any, { id: string }>(
92
99
  "delete-recording-permanent",
@@ -311,11 +318,12 @@ export default function TrashRoute() {
311
318
  {!isLoading && recordings.length > 0 && totalPages > 1 && (
312
319
  <div className="flex shrink-0 items-center justify-between gap-3 border-t border-border px-5 py-2.5">
313
320
  <span className="text-xs text-muted-foreground">
314
- {t("libraryGrid.paginationRange", {
315
- start: (page - 1) * PAGE_SIZE + 1,
316
- end: (page - 1) * PAGE_SIZE + recordings.length,
317
- total,
318
- })}
321
+ {totalKnown &&
322
+ t("libraryGrid.paginationRange", {
323
+ start: (page - 1) * PAGE_SIZE + 1,
324
+ end: (page - 1) * PAGE_SIZE + recordings.length,
325
+ total,
326
+ })}
319
327
  </span>
320
328
  <div className="flex items-center gap-2">
321
329
  <Button
@@ -329,7 +337,9 @@ export default function TrashRoute() {
329
337
  {t("libraryGrid.paginationPrevious")}
330
338
  </Button>
331
339
  <span className="text-xs text-muted-foreground">
332
- {t("libraryGrid.paginationPage", { page, totalPages })}
340
+ {totalKnown
341
+ ? t("libraryGrid.paginationPage", { page, totalPages })
342
+ : page}
333
343
  </span>
334
344
  <Button
335
345
  variant="outline"
@@ -7,6 +7,8 @@ time from the command menu (Cmd+K → "What's new").
7
7
 
8
8
  ### Fixed
9
9
 
10
+ - ChatGPT widget previews show saved document bodies without a Content session.
11
+ - Documents opened in ChatGPT show their saved content right away.
10
12
  - ChatGPT widgets show saved document bodies in read-only mode without a Content session.
11
13
  - Fixed Content pages that opened to an error in ChatGPT widgets
12
14
 
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: design-figma-parity
3
+ description: >-
4
+ Evidence rules for Design behavior parity claims and specs. Use when writing,
5
+ changing, reviewing, or triaging Design interaction behavior against Figma.
6
+ scope: dev
7
+ metadata:
8
+ internal: true
9
+ ---
10
+
11
+ # Design and Figma parity
12
+
13
+ Claude's packets, another agent's summary, clips, prior specs, and memory are
14
+ evidence leads, not product authority. Steve decides product chrome and any
15
+ intentional Design difference.
16
+
17
+ ## Rule
18
+
19
+ State only behavior measured in native Figma or deliberately chosen by Steve.
20
+ Keep every other behavior unmeasured; a screenshot pair, old spec, memory, or
21
+ parity log is not a native Figma oracle.
22
+
23
+ ## Evidence
24
+
25
+ - Cite a current `fig.*` record from `templates/design/parity/oracle/` for each
26
+ measured Figma behavior. Read and open its committed Figma artifact before
27
+ relying on it; a record proves provenance and integrity, not that its claim is
28
+ true.
29
+ - Keep the measured gesture, native observation, date, operator, trial count,
30
+ relevant values, Design build, and source artifacts with the record. Measure
31
+ the same selection and state that the claim describes. Do not generalize
32
+ beyond the captured conditions.
33
+ - A retracted or repeat-required record cannot support a test or behavior
34
+ change. Add a new record for a repeat; preserve the old record and its reason.
35
+ - Mark a deliberate product choice `chosen`, name the decision maker and reason,
36
+ and link the measured behavior it differs from when known. Never describe a
37
+ chosen difference as parity.
38
+ - If there is no usable record, say `unmeasured` and leave the behavior alone.
39
+ Ask Steve for a numbered gesture and the observable to capture when native
40
+ evidence is required. Do not infer behavior from clips, DOM state, old specs,
41
+ or another agent's summary.
42
+
43
+ ## Tests and review
44
+
45
+ - Add a failing regression test before behavior changes. Design interaction
46
+ PRs must select the required, bounded (<10 minute) product-regression lane.
47
+ It covers position/alignment/layout inside a Frame, overlap drag, duplicate
48
+ without ghosts, Delete, paste, inspector X/Y, and the four
49
+ `canvas-invariants` / `inspector-styles` regressions. Verify collected test
50
+ names before treating a green job as coverage; a file path alone is not proof.
51
+ - Parity tests cite `oracle: fig.<area>.<slug>` or state `oracle: none — <reason>`
52
+ in the test. Do not cite nonexistent ground-truth documents or treat a
53
+ self-authored test fixture as Figma evidence.
54
+ - A PR titled `test:` must contain only test, fixture, focused test-workflow, or
55
+ documentation changes. Shared CI orchestration and guard implementation use
56
+ a non-test title. Never quarantine a failure merely to turn CI green; a
57
+ quarantine needs a named owner, expiry, and open tracking issue.
58
+ - Keep regression fixes separate from feature changes until the required
59
+ regression checks pass. Report what was measured, what remains unmeasured,
60
+ the exact checks run, and any missing native capture.
61
+
62
+ ## Standing mandate
63
+
64
+ - Regressions come before parity features. A red required regression check means
65
+ stop and fix at the owning boundary before proceeding.
66
+ - A PR titled `test:` may contain only tests, fixtures, focused test-workflow,
67
+ or documentation changes. Shared CI orchestration and guard code use a
68
+ non-test title. A quarantine requires a named owner, expiry, and open issue.
69
+ - Keep at most three live Codex threads or worktrees for this effort. Reuse
70
+ them, create no new `design-*` worktree, start new PRs from fresh
71
+ `origin/main`, update an existing PR from its freshly fetched head, and keep
72
+ one owner per file. Never edit `PARITY-LOG.md`.
73
+ - After each shipped unit, report the oracle IDs, checks run, what remains
74
+ unmeasured, and any missing native capture. Advisory agent messages do not
75
+ authorize product decisions.
76
+
77
+ ## Recording a native capture
78
+
79
+ - Get the numbered gesture and observable from Steve before measuring a new
80
+ behavior. Do not operate the native Figma session or infer an interaction
81
+ from screenshots supplied without its stated gesture.
82
+ - Use a disposable local Design fixture and a Figma probe layer named exactly
83
+ `AN-ORACLE-PROBE:<id>`. Keep the private Figma file key out of the manifest
84
+ and repository. Keep the exact Figma page name in the local manifest only;
85
+ committed records use `not captured; private scratch page name withheld` and
86
+ omit page IDs.
87
+ - In Figma Desktop, create a Custom UI development plugin so Figma assigns its
88
+ plugin id. Keep its generated directory under `.tmp/` or outside the repo.
89
+ Build the bridge into that plugin with
90
+ `pnpm design:oracle-page-bridge -- --manifest <plugin-manifest.json>`; this
91
+ writes `plugin.js` and `ui.html` beside the manifest and preserves its id.
92
+ Then open the same file in the Figma tab attached to the harness CDP browser
93
+ (Chrome on port 9222 by default), run the registered plugin from Development,
94
+ and leave its small UI open. Confirm it says `Active Figma page: <name>` in
95
+ that Chrome tab. The bridge reads `figma.currentPage`; it does not edit the
96
+ file or send page data elsewhere.
97
+ - Run `pnpm design:oracle-record -- --manifest <probe.json> --plugin-manifest
98
+ <plugin-manifest.json>` with both manifests local; keep the probe manifest
99
+ under `.tmp/`. Include `id`, `claim`, `area`, `gesture`, `nativeObservation`,
100
+ `operator`, `measuredBy`, `trials`, `figmaPageName`, `probeMarker`, `designId`,
101
+ and the measured `values`. The recorder checks the logged-in
102
+ Figma tab in the CDP-connected Chrome, verifies the exact active page name
103
+ before capture and the same page identity afterward, then checks the exact
104
+ probe-layer name, matching local Design comment, and unique selected Design
105
+ probe layer before saving screenshots, inspector values, a comparison sheet,
106
+ and the hashed record. Keep the page active while capture runs: Figma page
107
+ change callbacks are asynchronous and may be coalesced, so the counter only
108
+ detects changes Figma reports. Inspect the record and artifacts before citing
109
+ them.
110
+ - A recorder failure exits 2 and removes staged files. Do not hand-write a
111
+ measured entry to work around a missing session or probe.
112
+
113
+ ## Related skills
114
+
115
+ - `design-clip-repro` — reproduce and verify Design editor behavior.
116
+ - `verifying-changes` — exercise the broken path before reporting a fix done.
@@ -1402,7 +1402,13 @@ export const editorChromeBridgeScript: string = `"use strict";
1402
1402
  var host = ensureEditorChromeHost();
1403
1403
  editorChromeHostObserver?.disconnect();
1404
1404
  editorChromeDocumentObserver?.disconnect();
1405
- editorChromeHostObserver = new MutationObserver(repairEditorChromeHost);
1405
+ editorChromeHostObserver = new MutationObserver(function() {
1406
+ if (!editorChromeHost || editorChromeNodes.some(function(node) {
1407
+ return node.parentNode !== editorChromeHost;
1408
+ })) {
1409
+ repairEditorChromeHost();
1410
+ }
1411
+ });
1406
1412
  editorChromeHostObserver.observe(host, { childList: true });
1407
1413
  editorChromeDocumentObserver = new MutationObserver(function() {
1408
1414
  if (!editorChromeHost || !editorChromeHost.isConnected || editorChromeHost.parentNode !== document.documentElement) {
@@ -3916,6 +3922,135 @@ export const editorChromeBridgeScript: string = `"use strict";
3916
3922
  }
3917
3923
  return el.parentElement;
3918
3924
  }
3925
+ function positionReferenceRectForElement(el) {
3926
+ var ancestor = el.parentElement;
3927
+ while (ancestor) {
3928
+ if (ancestor.getAttribute("data-an-primitive") === "frame") {
3929
+ return rectInfoForElement(ancestor);
3930
+ }
3931
+ ancestor = ancestor.parentElement;
3932
+ }
3933
+ var root = el.ownerDocument.documentElement;
3934
+ return {
3935
+ x: 0,
3936
+ y: 0,
3937
+ width: root.clientWidth,
3938
+ height: root.clientHeight
3939
+ };
3940
+ }
3941
+ function multiplyPositionTransforms(left, right) {
3942
+ return {
3943
+ a: left.a * right.a + left.c * right.b,
3944
+ b: left.b * right.a + left.d * right.b,
3945
+ c: left.a * right.c + left.c * right.d,
3946
+ d: left.b * right.c + left.d * right.d
3947
+ };
3948
+ }
3949
+ function positionElementTransform(styles) {
3950
+ var transform = styles.transform === "none" ? { a: 1, b: 0, c: 0, d: 1 } : new DOMMatrixReadOnly(styles.transform);
3951
+ var result = {
3952
+ a: transform.a,
3953
+ b: transform.b,
3954
+ c: transform.c,
3955
+ d: transform.d
3956
+ };
3957
+ var scaleValue = styles.getPropertyValue("scale");
3958
+ if (scaleValue && scaleValue !== "none") {
3959
+ var scaleParts = scaleValue.trim().split(/\\s+/);
3960
+ var scaleX = Number.parseFloat(scaleParts[0] || "1");
3961
+ var scaleY = Number.parseFloat(scaleParts[1] || scaleParts[0] || "1");
3962
+ result = multiplyPositionTransforms(
3963
+ { a: scaleX, b: 0, c: 0, d: scaleY },
3964
+ result
3965
+ );
3966
+ }
3967
+ var rotateValue = styles.getPropertyValue("rotate");
3968
+ if (rotateValue && rotateValue !== "none") {
3969
+ var rotateParts = rotateValue.trim().split(/\\s+/);
3970
+ var angle = rotateParts[rotateParts.length - 1] || "0deg";
3971
+ var axis = rotateParts.length > 1 ? rotateParts[0] : "z";
3972
+ var rotateFunction = axis === "x" || axis === "y" ? "rotate" + axis.toUpperCase() : "rotate";
3973
+ var rotation = new DOMMatrixReadOnly(rotateFunction + "(" + angle + ")");
3974
+ result = multiplyPositionTransforms(
3975
+ { a: rotation.a, b: rotation.b, c: rotation.c, d: rotation.d },
3976
+ result
3977
+ );
3978
+ }
3979
+ var zoom = Number.parseFloat(styles.getPropertyValue("zoom"));
3980
+ if (Number.isFinite(zoom) && zoom !== 1) {
3981
+ result = multiplyPositionTransforms(
3982
+ { a: zoom, b: 0, c: 0, d: zoom },
3983
+ result
3984
+ );
3985
+ }
3986
+ return result;
3987
+ }
3988
+ function establishesPositioningContext(styles) {
3989
+ var translate = styles.getPropertyValue("translate");
3990
+ var rotate = styles.getPropertyValue("rotate");
3991
+ var scale = styles.getPropertyValue("scale");
3992
+ var backdropFilter = styles.getPropertyValue("backdrop-filter");
3993
+ return translate !== "" && translate !== "none" || rotate !== "" && rotate !== "none" || scale !== "" && scale !== "none" || styles.transform !== "none" || styles.perspective !== "none" || styles.filter !== "none" || backdropFilter !== "" && backdropFilter !== "none" || /(?:^|\\s)(?:layout|paint|strict|content)(?:\\s|$)/.test(styles.contain) || /transform|perspective|filter|contain/.test(styles.willChange) || styles.contentVisibility === "auto";
3994
+ }
3995
+ function positionComputedStylesForElement(el, cache) {
3996
+ var cached = cache.get(el);
3997
+ if (cached) return cached;
3998
+ var styles = window.getComputedStyle(el);
3999
+ cache.set(el, styles);
4000
+ return styles;
4001
+ }
4002
+ function positionContainingBlockForElement(el, cache) {
4003
+ var fixed = positionComputedStylesForElement(el, cache).position === "fixed";
4004
+ var containingBlock = null;
4005
+ var ancestor = el.parentElement;
4006
+ while (ancestor) {
4007
+ var styles = positionComputedStylesForElement(ancestor, cache);
4008
+ if (fixed && establishesPositioningContext(styles) || !fixed && (styles.position !== "static" || establishesPositioningContext(styles))) {
4009
+ containingBlock = ancestor;
4010
+ break;
4011
+ }
4012
+ ancestor = ancestor.parentElement;
4013
+ }
4014
+ var transform = { a: 1, b: 0, c: 0, d: 1 };
4015
+ for (ancestor = containingBlock; ancestor; ancestor = ancestor.parentElement) {
4016
+ transform = multiplyPositionTransforms(
4017
+ positionElementTransform(
4018
+ positionComputedStylesForElement(ancestor, cache)
4019
+ ),
4020
+ transform
4021
+ );
4022
+ }
4023
+ if (!containingBlock) {
4024
+ return {
4025
+ origin: { x: 0, y: 0 },
4026
+ transform
4027
+ };
4028
+ }
4029
+ var htmlContainingBlock = containingBlock;
4030
+ var quaddedContainingBlock = containingBlock;
4031
+ var paddingQuad = quaddedContainingBlock.getBoxQuads?.({
4032
+ box: "padding"
4033
+ })?.[0];
4034
+ var scrollX = htmlContainingBlock.scrollLeft || 0;
4035
+ var scrollY = htmlContainingBlock.scrollTop || 0;
4036
+ if (paddingQuad) {
4037
+ return {
4038
+ origin: {
4039
+ x: paddingQuad.p1.x + window.scrollX - transform.a * scrollX - transform.c * scrollY,
4040
+ y: paddingQuad.p1.y + window.scrollY - transform.b * scrollX - transform.d * scrollY
4041
+ },
4042
+ transform
4043
+ };
4044
+ }
4045
+ var rect = rectInfoForElement(containingBlock);
4046
+ return {
4047
+ origin: {
4048
+ x: rect.x + transform.a * (htmlContainingBlock.clientLeft - scrollX) + transform.c * (htmlContainingBlock.clientTop - scrollY),
4049
+ y: rect.y + transform.b * (htmlContainingBlock.clientLeft - scrollX) + transform.d * (htmlContainingBlock.clientTop - scrollY)
4050
+ },
4051
+ transform
4052
+ };
4053
+ }
3919
4054
  function autoLayoutParentInfo(el) {
3920
4055
  var parent = designParentForElement(el);
3921
4056
  if (!parent || parent === document.body || parent === document.documentElement) {
@@ -5433,14 +5568,24 @@ export const editorChromeBridgeScript: string = `"use strict";
5433
5568
  )
5434
5569
  };
5435
5570
  }
5436
- function getElementInfo(el, portableComputedStylesCache, includePortableStyleSnapshot = true) {
5571
+ function getElementInfo(el, portableComputedStylesCache, includePortableStyleSnapshot = true, sharedPositionComputedStylesCache) {
5437
5572
  var cs = window.getComputedStyle(el);
5573
+ var positionComputedStylesCache = sharedPositionComputedStylesCache || /* @__PURE__ */ new WeakMap();
5574
+ positionComputedStylesCache.set(el, cs);
5438
5575
  var paintCs = window.getComputedStyle(vectorPaintTarget(el) || el);
5439
5576
  var boundingRect = rectInfoForElement(el);
5440
5577
  var componentName = componentNameForElement(el);
5441
5578
  var parentAutoLayout = autoLayoutParentInfo(el);
5442
5579
  var designParent = designParentForElement(el);
5443
5580
  var parentStyles = designParent ? window.getComputedStyle(designParent) : null;
5581
+ if (designParent && parentStyles) {
5582
+ positionComputedStylesCache.set(designParent, parentStyles);
5583
+ }
5584
+ var positionReferenceRect = positionReferenceRectForElement(el);
5585
+ var positionCoordinateContext = positionContainingBlockForElement(
5586
+ el,
5587
+ positionComputedStylesCache
5588
+ );
5444
5589
  var authoredSizeStyles = collectAuthoredSizeStyles(el);
5445
5590
  var parentDisplay = parentStyles ? parentStyles.display : void 0;
5446
5591
  var runtimeOnlyClone = isRuntimeOnlyClone(el);
@@ -5533,6 +5678,9 @@ export const editorChromeBridgeScript: string = `"use strict";
5533
5678
  styleSnapshotCaptureFailed: portableStyleSnapshot === null ? true : void 0,
5534
5679
  boundingRect,
5535
5680
  parentBoundingRect: designParent ? rectInfoForElement(designParent) : void 0,
5681
+ positionReferenceRect,
5682
+ positionContainingBlockOrigin: positionCoordinateContext.origin,
5683
+ positionContainingBlockTransform: positionCoordinateContext.transform,
5536
5684
  textContent: el.textContent ? el.textContent.slice(0, 200) : void 0,
5537
5685
  textContentTruncated: el.textContent ? el.textContent.length > 200 : void 0,
5538
5686
  htmlContent: el.innerHTML && el.innerHTML !== el.textContent ? el.innerHTML.slice(0, 4e3) : void 0,
@@ -5704,6 +5852,7 @@ export const editorChromeBridgeScript: string = `"use strict";
5704
5852
  }
5705
5853
  function collectSelectableElementInfos(deep, atPoint, includePortableStyleSnapshot = true) {
5706
5854
  var targets = collectSelectableElements(deep);
5855
+ var positionComputedStylesCache = /* @__PURE__ */ new WeakMap();
5707
5856
  if (atPoint) {
5708
5857
  targets = targets.filter(function(el) {
5709
5858
  return documentSpaceBoundsContainPoint(el, atPoint);
@@ -5711,14 +5860,24 @@ export const editorChromeBridgeScript: string = `"use strict";
5711
5860
  }
5712
5861
  if (!includePortableStyleSnapshot) {
5713
5862
  return targets.map(function(target) {
5714
- return getElementInfo(target, void 0, false);
5863
+ return getElementInfo(
5864
+ target,
5865
+ void 0,
5866
+ false,
5867
+ positionComputedStylesCache
5868
+ );
5715
5869
  });
5716
5870
  }
5717
5871
  portableStyleProbeDocument();
5718
5872
  var portableComputedStylesCache = createPortableStyleComputedStylesCache();
5719
5873
  try {
5720
5874
  return targets.map(function(target) {
5721
- return getElementInfo(target, portableComputedStylesCache, true);
5875
+ return getElementInfo(
5876
+ target,
5877
+ portableComputedStylesCache,
5878
+ true,
5879
+ positionComputedStylesCache
5880
+ );
5722
5881
  });
5723
5882
  } finally {
5724
5883
  if (portableComputedStylesCache) {
@@ -5795,6 +5954,7 @@ export const editorChromeBridgeScript: string = `"use strict";
5795
5954
  handle.setAttribute("data-agent-native-edit-handle", pos);
5796
5955
  var cursor = pos === "n" || pos === "s" ? "ns-resize" : pos === "e" || pos === "w" ? "ew-resize" : pos === "nw" || pos === "se" ? "nwse-resize" : "nesw-resize";
5797
5956
  handle.style.cssText = "position:absolute;z-index:1;width:7px;height:7px;border:1px solid var(--design-editor-accent-color);background:var(--design-editor-accent-contrast-color);box-sizing:border-box;border-radius:2px;box-shadow:0 1px 2px color-mix(in srgb,var(--design-editor-accent-color) 25%,transparent);pointer-events:auto;cursor:" + cursor + ";";
5957
+ if (pos.length === 2) handle.style.zIndex = "4";
5798
5958
  if (pos.indexOf("n") !== -1) handle.style.top = "-4px";
5799
5959
  if (pos.indexOf("s") !== -1) handle.style.bottom = "-4px";
5800
5960
  if (pos.indexOf("w") !== -1) handle.style.left = "-4px";
@@ -8698,6 +8858,7 @@ export const editorChromeBridgeScript: string = `"use strict";
8698
8858
  var isRadiusHandleVisible = pos === hoveredRadiusHandleKey || pos === activeRadiusHandleKey;
8699
8859
  handle.style.visibility = isRadiusHandleVisible ? "visible" : "hidden";
8700
8860
  handle.style.pointerEvents = isRadiusHandleVisible ? "auto" : "none";
8861
+ handle.style.zIndex = isRadiusHandleVisible ? "5" : "2";
8701
8862
  handle.style.width = size + "px";
8702
8863
  handle.style.height = size + "px";
8703
8864
  handle.style.borderWidth = 1.5 * line + "px";
@@ -20246,6 +20407,21 @@ export const editorChromeBridgeScript: string = `"use strict";
20246
20407
  }
20247
20408
  if (!focusInsideEdit) {
20248
20409
  if (!isEditorTypingTarget(activeNow)) {
20410
+ if (e.key === "Delete" || e.key === "Backspace") {
20411
+ var unfocusedEditHotkey = {
20412
+ key: e.key,
20413
+ code: e.code,
20414
+ metaKey: !!e.metaKey,
20415
+ ctrlKey: !!e.ctrlKey,
20416
+ shiftKey: !!e.shiftKey,
20417
+ altKey: !!e.altKey,
20418
+ repeat: !!e.repeat
20419
+ };
20420
+ stopNativeInteraction(e);
20421
+ if (finishActiveTextEdit) finishActiveTextEdit(true);
20422
+ postDesignHotkey(unfocusedEditHotkey);
20423
+ return;
20424
+ }
20249
20425
  try {
20250
20426
  activeTextEditEl.focus();
20251
20427
  collapseSelectionIntoContents(activeTextEditEl);
@@ -22283,6 +22459,9 @@ export const editorChromeBridgeScript: string = `"use strict";
22283
22459
  return;
22284
22460
  }
22285
22461
  if (e.data.applied) {
22462
+ if (moveWasInsert && move.el && move.el.isConnected) {
22463
+ move.el.removeAttribute("data-agent-native-transient-drag-clone");
22464
+ }
22286
22465
  if (!moveWasInsert && move.el && move.el.isConnected && move.target) {
22287
22466
  applyRuntimeReorder(move.el, move.target);
22288
22467
  selectedEl = move.el;
@@ -10,6 +10,7 @@ Read relevant guides before deeper work:
10
10
  - `.agents/skills/design-templates/SKILL.md` — when reusing existing Design work.
11
11
  - `.agents/skills/responsive-breakpoints/SKILL.md` — for breakpoint editing.
12
12
  - `.agents/skills/design-systems/SKILL.md` — for tokens, brand extraction, or Figma.
13
+ - `.agents/skills/design-figma-parity/SKILL.md` — for evidence rules on measured Figma behavior claims.
13
14
  - `.agents/skills/creative-context/SKILL.md` — for cross-app sources and governed context.
14
15
  - `.agents/skills/design-review-feedback/SKILL.md` — for persisted review comments.
15
16
  - `.agents/skills/export-handoff/SKILL.md` — for exports and coding handoffs.
@@ -3,6 +3,13 @@
3
3
  All notable user-facing changes to Design are documented here. Open it any time
4
4
  from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-10-07
7
+
8
+ ### Fixed
9
+
10
+ - Design keeps frame-relative positions and selections stable when moving or pasting layers.
11
+ - Position values stay aligned with selected layers in layouts with body margins.
12
+
6
13
  ## 2026-10-06
7
14
 
8
15
  ### Added