@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +4 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +23 -13
- package/corpus/templates/clips/app/components/library/recording-card.tsx +3 -3
- package/corpus/templates/clips/app/hooks/use-library.ts +2 -1
- package/corpus/templates/clips/app/routes/_app.trash.tsx +23 -13
- package/corpus/templates/content/CHANGELOG.md +2 -0
- package/corpus/templates/design/.agents/skills/design-figma-parity/SKILL.md +116 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +183 -4
- package/corpus/templates/design/AGENTS.md +1 -0
- package/corpus/templates/design/CHANGELOG.md +7 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +252 -3
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +150 -11
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +12 -0
- package/corpus/templates/design/app/components/design/types.ts +13 -0
- package/corpus/templates/design/app/lib/design-editor-route.ts +11 -0
- package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +33 -29
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +18 -5
- package/corpus/templates/design/app/pages/design-editor/position-coordinate-context.ts +228 -0
- package/corpus/templates/design/changelog/2026-10-07-design-keeps-frame-relative-positions-and-selections-stable-.md +6 -0
- package/corpus/templates/design/changelog/2026-10-07-position-values-stay-aligned-with-selected-layers-in-layouts.md +6 -0
- package/corpus/templates/design/e2e/parity-oracle.ts +144 -0
- package/corpus/templates/design/parity/oracle/fig.canvas.evenly-spaced-row-drag.json +45 -0
- package/corpus/templates/design/parity/oracle/fig.canvas.overlap-drag.json +46 -0
- package/corpus/templates/design/parity/oracle/fig.canvas.position-relative-to-frame.json +57 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.align-center-within-group.json +36 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.align-left-within-group.json +43 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.empty-effects-title.json +42 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.empty-fill-title.json +41 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.empty-stroke-title.json +39 -0
- package/corpus/templates/design/parity/oracle/fig.inspector.populated-section-title.json +41 -0
- package/corpus/templates/design/scripts/runtime-budget-zoom.ts +56 -0
- package/corpus/templates/design/scripts/runtime-budget.ts +49 -14
- package/corpus/templates/dispatch/.agents/skills/recurring-jobs/SKILL.md +16 -1
- package/corpus/templates/dispatch/DEVELOPING.md +7 -0
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
- package/corpus/templates/factory/actions/save-factory-automation.ts +17 -5
- package/corpus/templates/factory/server/lib/factory-automation-config.ts +55 -0
- package/corpus/templates/factory/server/lib/factory-automation-history.ts +116 -14
- package/package.json +1 -1
package/corpus/README.md
CHANGED
|
@@ -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 =
|
|
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
|
-
{
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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 =
|
|
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
|
-
{
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
{
|
|
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(
|
|
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(
|
|
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(
|
|
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
|