@swmansion/argent 0.13.0 → 0.13.1-next.0
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/README.md +12 -11
- package/bin/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/simulator-server +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/linux/simulator-server +0 -0
- package/bin/linux-arm64/simulator-server +0 -0
- package/dist/cli-cmds.mjs +1904 -1601
- package/dist/installer.mjs +2297 -2036
- package/dist/mcp-server.mjs +233 -462
- package/dist/preview-ui/index.html +110 -41
- package/dist/preview-ui/theme.css +14 -1
- package/dist/preview-window/main.cjs +14 -1
- package/dist/tool-server.cjs +40072 -22246
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +9 -7
- package/rules/argent.md +7 -7
- package/skills/argent-android-emulator-setup/SKILL.md +1 -0
- package/skills/argent-device-interact/SKILL.md +2 -0
- package/skills/argent-tv-interact/SKILL.md +60 -0
- package/skills/argent-vega/SKILL.md +0 -76
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
.stage,
|
|
65
65
|
.vcard,
|
|
66
66
|
.float-controls,
|
|
67
|
+
.float-toolbelt,
|
|
67
68
|
.float-status,
|
|
68
69
|
.footbar,
|
|
69
70
|
.comment-pop,
|
|
@@ -83,6 +84,13 @@
|
|
|
83
84
|
gap: var(--gap-list);
|
|
84
85
|
z-index: var(--z-overlay);
|
|
85
86
|
}
|
|
87
|
+
/* macOS traffic lights (native window controls) occupy the top-left in
|
|
88
|
+
Electron. Shift the status pill clear of them so a "Connecting…" or
|
|
89
|
+
error message never lands under the buttons. A browser tab has no
|
|
90
|
+
buttons, so .native-frame is absent and the pill keeps its corner. */
|
|
91
|
+
.native-frame .float-status {
|
|
92
|
+
left: var(--traffic-light-gutter);
|
|
93
|
+
}
|
|
86
94
|
.status {
|
|
87
95
|
background: var(--color-surface);
|
|
88
96
|
border: var(--border-hairline) solid var(--color-border);
|
|
@@ -297,8 +305,9 @@
|
|
|
297
305
|
font-weight: var(--fw-semibold);
|
|
298
306
|
box-shadow: var(--shadow-card);
|
|
299
307
|
}
|
|
300
|
-
/* Floating top-right controls (theme
|
|
301
|
-
|
|
308
|
+
/* Floating top-right controls (just the theme toggle now — comment moved
|
|
309
|
+
to the bottom toolbelt, close is a native traffic light): square icon
|
|
310
|
+
buttons that sit over the stream, away from the slim drag toolbar. */
|
|
302
311
|
.float-controls {
|
|
303
312
|
position: absolute;
|
|
304
313
|
top: var(--footbar-bottom);
|
|
@@ -308,6 +317,19 @@
|
|
|
308
317
|
z-index: var(--z-overlay);
|
|
309
318
|
-webkit-app-region: no-drag;
|
|
310
319
|
}
|
|
320
|
+
/* Bottom-centre toolbelt — floats below the stream. Today it carries the
|
|
321
|
+
comment toggle; it's shaped to grow into a Figma-style bottom tool tab,
|
|
322
|
+
so it's a horizontal row centred on the window. */
|
|
323
|
+
.float-toolbelt {
|
|
324
|
+
position: absolute;
|
|
325
|
+
bottom: var(--footbar-bottom);
|
|
326
|
+
left: 50%;
|
|
327
|
+
transform: translateX(-50%);
|
|
328
|
+
display: flex;
|
|
329
|
+
gap: var(--gap-list);
|
|
330
|
+
z-index: var(--z-overlay);
|
|
331
|
+
-webkit-app-region: no-drag;
|
|
332
|
+
}
|
|
311
333
|
/* Icon-only controls: a uniform square button so theme / comment / close
|
|
312
334
|
read as one consistent cluster (no mismatched text pills). The glyph is
|
|
313
335
|
inline SVG drawn in currentColor, so it themes for free and the hover /
|
|
@@ -726,14 +748,14 @@
|
|
|
726
748
|
}
|
|
727
749
|
.spot-hit {
|
|
728
750
|
position: absolute;
|
|
729
|
-
cursor:
|
|
751
|
+
cursor: var(--cursor-comment);
|
|
730
752
|
z-index: var(--z-spot-hit);
|
|
731
753
|
/* The spotlight root sets pointer-events:none; this capture layer must
|
|
732
754
|
opt back in or hover/click never reaches it and nothing un-blurs. */
|
|
733
755
|
pointer-events: auto;
|
|
734
756
|
}
|
|
735
757
|
.inspecting .stage img {
|
|
736
|
-
cursor:
|
|
758
|
+
cursor: var(--cursor-comment);
|
|
737
759
|
}
|
|
738
760
|
|
|
739
761
|
/* Comment box bubble anchored next to the inspected element. */
|
|
@@ -1208,6 +1230,18 @@
|
|
|
1208
1230
|
<span id="hidden-badge" class="hidden-badge" hidden></span>
|
|
1209
1231
|
</div>
|
|
1210
1232
|
<div class="float-controls" id="float-controls">
|
|
1233
|
+
<button
|
|
1234
|
+
id="theme-btn"
|
|
1235
|
+
class="float-btn"
|
|
1236
|
+
type="button"
|
|
1237
|
+
aria-label="Theme"
|
|
1238
|
+
title="Theme"></button>
|
|
1239
|
+
</div>
|
|
1240
|
+
<!-- Bottom-centre toolbelt that floats below the stream. Holds the
|
|
1241
|
+
comment toggle for now; this is the seed of a Figma-style bottom
|
|
1242
|
+
tool tab we'll grow later. Window close/minimise live in the native
|
|
1243
|
+
traffic lights, not a handcrafted button. -->
|
|
1244
|
+
<div class="float-toolbelt" id="float-toolbelt">
|
|
1211
1245
|
<button
|
|
1212
1246
|
id="comment-btn"
|
|
1213
1247
|
class="float-btn"
|
|
@@ -1227,28 +1261,6 @@
|
|
|
1227
1261
|
d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
|
1228
1262
|
</svg>
|
|
1229
1263
|
</button>
|
|
1230
|
-
<button
|
|
1231
|
-
id="theme-btn"
|
|
1232
|
-
class="float-btn"
|
|
1233
|
-
type="button"
|
|
1234
|
-
aria-label="Theme"
|
|
1235
|
-
title="Theme"></button>
|
|
1236
|
-
<button
|
|
1237
|
-
id="close-btn"
|
|
1238
|
-
class="float-btn"
|
|
1239
|
-
type="button"
|
|
1240
|
-
aria-label="Close preview window"
|
|
1241
|
-
title="Close preview window">
|
|
1242
|
-
<svg
|
|
1243
|
-
viewBox="0 0 24 24"
|
|
1244
|
-
fill="none"
|
|
1245
|
-
stroke="currentColor"
|
|
1246
|
-
stroke-width="2"
|
|
1247
|
-
stroke-linecap="round"
|
|
1248
|
-
aria-hidden="true">
|
|
1249
|
-
<path d="M6 6 18 18M18 6 6 18" />
|
|
1250
|
-
</svg>
|
|
1251
|
-
</button>
|
|
1252
1264
|
</div>
|
|
1253
1265
|
<div class="offscreen-prompt" id="offscreen-prompt" hidden>
|
|
1254
1266
|
<div class="op-card">
|
|
@@ -2023,15 +2035,15 @@
|
|
|
2023
2035
|
}
|
|
2024
2036
|
|
|
2025
2037
|
// ── Events ──────────────────────────────────────────────────────────
|
|
2026
|
-
//
|
|
2027
|
-
//
|
|
2028
|
-
//
|
|
2029
|
-
//
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
}
|
|
2038
|
+
// Window close/minimise are the native macOS traffic lights now (the host
|
|
2039
|
+
// window uses titleBarStyle:"hidden"), so there's no handcrafted close
|
|
2040
|
+
// button to wire up. Closing the native window quits the host; the
|
|
2041
|
+
// tool-server re-opens it on the next round, exactly as the old button's
|
|
2042
|
+
// dismiss did. In Electron, flag the document so the top-left chrome can
|
|
2043
|
+
// sidestep the traffic lights (a browser tab has none).
|
|
2044
|
+
if (/Electron/i.test(navigator.userAgent)) {
|
|
2045
|
+
document.documentElement.classList.add("native-frame");
|
|
2046
|
+
}
|
|
2035
2047
|
|
|
2036
2048
|
// ── Theme (light / dark) ────────────────────────────────────────────
|
|
2037
2049
|
// Colours are light-dark(LIGHT, DARK) tokens in theme.css; forcing
|
|
@@ -2553,18 +2565,33 @@
|
|
|
2553
2565
|
);
|
|
2554
2566
|
let best = null,
|
|
2555
2567
|
bestScore = Infinity;
|
|
2568
|
+
// The size gate is a PREFERENCE for the first match, never an eliminator.
|
|
2569
|
+
// `fallback` keeps the nearest candidate even when the gate rejects it, so
|
|
2570
|
+
// a clearly on-screen element is never reported off-screen just because the
|
|
2571
|
+
// propose-time frame is a poor size hint: that frame is a thumbnail CROP
|
|
2572
|
+
// region (often a whole component) while `match` can anchor on a small
|
|
2573
|
+
// sub-element inside it — e.g. crop the header band, anchor on its tiny
|
|
2574
|
+
// "Export" button — so the seed size legitimately dwarfs the real match.
|
|
2575
|
+
let fallback = null,
|
|
2576
|
+
fbScore = Infinity;
|
|
2556
2577
|
for (const n of pool) {
|
|
2557
2578
|
const f = n.frame;
|
|
2558
2579
|
const score = anchor ? dist(f) : f.width * f.height;
|
|
2580
|
+
if (score < fbScore) {
|
|
2581
|
+
fbScore = score;
|
|
2582
|
+
fallback = n;
|
|
2583
|
+
}
|
|
2559
2584
|
if (anchor && !hasDesc && !sizeMatches(f)) continue; // size gate only bootstraps identity
|
|
2560
2585
|
if (score < bestScore) {
|
|
2561
2586
|
bestScore = score;
|
|
2562
2587
|
best = n;
|
|
2563
2588
|
}
|
|
2564
2589
|
}
|
|
2565
|
-
// A locked identity must never be dropped over a size wobble
|
|
2566
|
-
//
|
|
2567
|
-
|
|
2590
|
+
// A locked identity must never be dropped over a size wobble (hasDesc →
|
|
2591
|
+
// `best` is the nearest identity match). When the size gate rejects every
|
|
2592
|
+
// candidate (an unreliable propose-time frame), fall back to the nearest
|
|
2593
|
+
// match rather than reporting a visible element off-screen.
|
|
2594
|
+
return best || fallback;
|
|
2568
2595
|
}
|
|
2569
2596
|
|
|
2570
2597
|
// Topmost on-screen node whose frame contains a normalized point
|
|
@@ -3192,6 +3219,37 @@
|
|
|
3192
3219
|
else return home;
|
|
3193
3220
|
return { x: nx, y: home.y };
|
|
3194
3221
|
}
|
|
3222
|
+
// Deterministic de-overlap of the spring HOMES (targets), run once per
|
|
3223
|
+
// layoutCards pass — i.e. per describe poll, NOT per animation frame. That
|
|
3224
|
+
// is the whole point: the damped bubble springs then glide to homes that
|
|
3225
|
+
// already don't overlap, so there is no live correction force and thus
|
|
3226
|
+
// none of the micro-jitter / wedge-wiggle a per-frame position fix causes.
|
|
3227
|
+
// Stable top-down sweep: walking cards in y order, each one is dropped to
|
|
3228
|
+
// sit just below every card above it that shares its vertical lane. Moving
|
|
3229
|
+
// only ever downward makes it monotonic — it cannot oscillate. Pinned
|
|
3230
|
+
// (user-dropped) cards are fixed anchors the others flow past.
|
|
3231
|
+
function deOverlapHomes(items, cardW, gap, top, bottom) {
|
|
3232
|
+
const sorted = items.slice().sort((a, b) => a.rec.home.y - b.rec.home.y);
|
|
3233
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
3234
|
+
const cur = sorted[i];
|
|
3235
|
+
if (cur.rec.pinned) continue;
|
|
3236
|
+
let floor = top;
|
|
3237
|
+
for (let j = 0; j < i; j++) {
|
|
3238
|
+
const prev = sorted[j];
|
|
3239
|
+
// Same vertical lane? (cards in different columns never constrain.)
|
|
3240
|
+
const oX =
|
|
3241
|
+
Math.min(cur.rec.home.x, prev.rec.home.x) +
|
|
3242
|
+
cardW -
|
|
3243
|
+
Math.max(cur.rec.home.x, prev.rec.home.x);
|
|
3244
|
+
if (oX <= 0) continue;
|
|
3245
|
+
const prevBottom = prev.rec.home.y + prev.ch + gap;
|
|
3246
|
+
if (prevBottom > floor) floor = prevBottom;
|
|
3247
|
+
}
|
|
3248
|
+
// Drop to clear the cards above; clamp so it can't leave the window
|
|
3249
|
+
// (true overflow piles at the bottom rather than vanishing off-screen).
|
|
3250
|
+
if (cur.rec.home.y < floor) cur.rec.home.y = Math.min(floor, bottom - cur.ch);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3195
3253
|
function layoutCards() {
|
|
3196
3254
|
const now = Date.now();
|
|
3197
3255
|
const mr = mainEl.getBoundingClientRect();
|
|
@@ -3305,7 +3363,7 @@
|
|
|
3305
3363
|
// • The final maxHeight write is diffed against the inline
|
|
3306
3364
|
// value so unchanged frames are no-ops — keeps scrollTop.
|
|
3307
3365
|
const COL_PAD = 8;
|
|
3308
|
-
const GAP =
|
|
3366
|
+
const GAP = 8;
|
|
3309
3367
|
const want = vis.map((rec, i) => {
|
|
3310
3368
|
const body = rec.el.querySelector(".vcard-body");
|
|
3311
3369
|
if (body && rec.chromeH == null) {
|
|
@@ -3371,11 +3429,19 @@
|
|
|
3371
3429
|
// the "ready" pop. offsetTop ignores the transform (offsetParent is the
|
|
3372
3430
|
// position:relative #main), keeping the column height stable.
|
|
3373
3431
|
const footTop = footEl && !footEl.hidden ? footEl.offsetTop - COL_PAD : mh;
|
|
3374
|
-
|
|
3375
|
-
|
|
3432
|
+
// The footbar is anchored bottom-RIGHT, so only the column(s) that
|
|
3433
|
+
// actually sit above it should surrender height for it. A column clear
|
|
3434
|
+
// of the footbar's x-range gets the full window height — otherwise the
|
|
3435
|
+
// left column was needlessly squeezed (and its cards clipped) by a
|
|
3436
|
+
// footbar that only overlaps the right column.
|
|
3437
|
+
const footLeft = footEl && !footEl.hidden ? footEl.offsetLeft : mw;
|
|
3376
3438
|
for (const col of ["L", "R"]) {
|
|
3377
3439
|
const cwant = want.filter((w) => w.side === col);
|
|
3378
3440
|
if (cwant.length === 0) continue;
|
|
3441
|
+
const colX = col === "L" ? leftX : rightX;
|
|
3442
|
+
const colBottom =
|
|
3443
|
+
colX + cardW > footLeft ? Math.min(mh - COL_PAD, footTop) : mh - COL_PAD;
|
|
3444
|
+
const colH = colBottom - colTop;
|
|
3379
3445
|
const naturalSum = cwant.reduce((s, w) => s + w.natural, 0) + (cwant.length - 1) * GAP;
|
|
3380
3446
|
if (naturalSum <= colH) {
|
|
3381
3447
|
cwant.sort((a, b) => a.anchor - b.anchor);
|
|
@@ -3468,6 +3534,9 @@
|
|
|
3468
3534
|
w.rec.vel.y += Math.random() * 18 - 9;
|
|
3469
3535
|
}
|
|
3470
3536
|
}
|
|
3537
|
+
// Guarantee no two resting homes overlap. Deterministic + once per pass,
|
|
3538
|
+
// so the springs simply glide to clear targets (no live force, no jitter).
|
|
3539
|
+
deOverlapHomes(want, cardW, GAP, COL_PAD, mh - COL_PAD);
|
|
3471
3540
|
}
|
|
3472
3541
|
|
|
3473
3542
|
// Reuses SVG nodes (no per-frame teardown) and draws to the *eased*
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
--pad-code: 2px 6px;
|
|
132
132
|
--pad-pill: 3px 11px;
|
|
133
133
|
--pad-card-head: 7px 10px;
|
|
134
|
-
--pad-row:
|
|
134
|
+
--pad-row: 5px 10px; /* tighter vertical so a 2-variant card fits its column without clipping */
|
|
135
135
|
--pad-cmt: 6px 10px; /* in-card comment box */
|
|
136
136
|
--pad-input: 6px 8px; /* popover textareas */
|
|
137
137
|
--pad-pop: 10px; /* comment / annotation popover */
|
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
--footbar-bottom: 14px;
|
|
169
169
|
--footbar-right: 14px;
|
|
170
170
|
--resize-edge: 6px; /* drag-layer inset so the frameless window's edges still resize */
|
|
171
|
+
--traffic-light-gutter: 84px; /* left inset for top-left chrome to clear the native macOS window buttons (Electron only) */
|
|
171
172
|
--footbar-max-width: 70%;
|
|
172
173
|
--select-min-width: 240px;
|
|
173
174
|
--debug-min-width: 220px;
|
|
@@ -181,6 +182,18 @@
|
|
|
181
182
|
--stream-max-h: 80dvh; /* dvh, not vh: in a browser tab `vh` is the toolbar-retracted (large) viewport, so the centered stream overflowed the %-height container and got clipped at the bottom. `dvh` tracks the visible viewport. In the frameless Electron window (no toolbar) dvh == vh, so production is unchanged. */
|
|
182
183
|
--backdrop-blur: 1.67px; /* light haze over the dimmed stream (3× softer than the former 5px) */
|
|
183
184
|
|
|
185
|
+
/* ── Cursors ─────────────────────────────────────────────────────────── */
|
|
186
|
+
/* Comment-mode cursor — a Figma-style speech bubble that makes "you are
|
|
187
|
+
placing a comment" unmistakable (replaces the generic crosshair). Accent
|
|
188
|
+
fill + white outline & dots so it reads on the bright stream AND on the
|
|
189
|
+
dark spotlight scrim. "7 24" is the hotspot: the tail tip, where the click
|
|
190
|
+
actually lands. Baked as a data-URI because a cursor image can't reference
|
|
191
|
+
CSS vars — so the colours live here, in the theme, like every other value. */
|
|
192
|
+
--cursor-comment:
|
|
193
|
+
url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2228%22%20height%3D%2228%22%20viewBox%3D%220%200%2028%2028%22%3E%3Cpath%20d%3D%22M6%203h16a3%203%200%200%201%203%203v10a3%203%200%200%201-3%203h-9l-6%205v-5h-1a3%203%200%200%201-3-3V6a3%203%200%200%201%203-3z%22%20fill%3D%22%232f6fd0%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%221.5%22%20stroke-linejoin%3D%22round%22%2F%3E%3Ccircle%20cx%3D%229%22%20cy%3D%2211%22%20r%3D%221.4%22%20fill%3D%22%23ffffff%22%2F%3E%3Ccircle%20cx%3D%2214%22%20cy%3D%2211%22%20r%3D%221.4%22%20fill%3D%22%23ffffff%22%2F%3E%3Ccircle%20cx%3D%2219%22%20cy%3D%2211%22%20r%3D%221.4%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fsvg%3E")
|
|
194
|
+
7 24,
|
|
195
|
+
crosshair;
|
|
196
|
+
|
|
184
197
|
/* ── Opacities ───────────────────────────────────────────────────────── */
|
|
185
198
|
--opacity-disabled: 0.45;
|
|
186
199
|
--opacity-connector: 0.7;
|
|
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
// ../preview-window/src/main.ts
|
|
26
26
|
var import_electron = require("electron");
|
|
27
27
|
var readline = __toESM(require("node:readline"));
|
|
28
|
+
import_electron.app.setName("Argent Lens");
|
|
28
29
|
var TARGET_WIDTH = 1200;
|
|
29
30
|
var TARGET_HEIGHT = 820;
|
|
30
31
|
var ANIMATION_MS = 320;
|
|
@@ -105,7 +106,17 @@ async function createWindow() {
|
|
|
105
106
|
win = new import_electron.BrowserWindow({
|
|
106
107
|
width: TARGET_WIDTH,
|
|
107
108
|
height: TARGET_HEIGHT,
|
|
108
|
-
|
|
109
|
+
title: "Argent Lens",
|
|
110
|
+
// Native window controls (the macOS traffic lights) instead of a
|
|
111
|
+
// handcrafted close button: `titleBarStyle: "hidden"` keeps the real OS
|
|
112
|
+
// buttons in the top-left while hiding the rest of the title bar, so the
|
|
113
|
+
// window still reads as frameless. (`frame: false` would suppress the
|
|
114
|
+
// buttons entirely — that's what we used to do.)
|
|
115
|
+
titleBarStyle: "hidden",
|
|
116
|
+
// Inset the traffic lights to share the same 14px padding the in-window
|
|
117
|
+
// corner panels use (theme toggle, status pill), so the native buttons and
|
|
118
|
+
// the handcrafted chrome read as one consistent margin.
|
|
119
|
+
trafficLightPosition: { x: 14, y: 18 },
|
|
109
120
|
show: false,
|
|
110
121
|
// Transparent + no native shadow: the OS window is just a passthrough
|
|
111
122
|
// for the renderer. The visible "card" is whatever <html> + <body>
|
|
@@ -119,6 +130,8 @@ async function createWindow() {
|
|
|
119
130
|
sandbox: true
|
|
120
131
|
}
|
|
121
132
|
});
|
|
133
|
+
win.setWindowButtonVisibility?.(true);
|
|
134
|
+
win.setTitle("Argent Lens");
|
|
122
135
|
win.on("closed", () => {
|
|
123
136
|
win = null;
|
|
124
137
|
import_electron.app.quit();
|