@bridge-ds/toolbar 0.3.0 → 0.4.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 CHANGED
@@ -2,10 +2,19 @@
2
2
 
3
3
  The Bridge toolbar for a product built with the Bridge design system. Bridge Studio opens the product with
4
4
  `?bridge=<change>` ("Try it in the product"); the toolbar then lets anyone browse the real product and flip between
5
- **Live product** (before) and **This change** (after), with **Show what changed** for token changes.
5
+ **Live product** (before) and **This change** (after), with **Show what changed**.
6
6
 
7
7
  - **Code changes** (an exploration in the product's repository, a feature branch): Before is the product in
8
- production, After is the branch's preview. Flipping keeps the screen and the scroll position.
8
+ production, After is the branch's preview. Flipping keeps the screen and the scroll position. Production is another
9
+ origin, so the toolbar cannot measure the difference: the change marks each new or changed area of the UI with
10
+ `data-bridge-change="<short plain label>"` on its outermost element (Bridge tells Claude to, in every exploration).
11
+ On This change those areas are outlined, counted ("1 changed", "1 / 3 changed") and stepped through like token
12
+ changes, with Isolate, and the Changes list names them. On Live product the bar says how many wait on This change
13
+ ("1 changed on This change"). A change that marks nothing says so.
14
+
15
+ ```tsx
16
+ <div role="status" data-bridge-change="Orders waiting to ship alert">30 orders are waiting to ship</div>
17
+ ```
9
18
  - **Token changes** of the design system: the live tokens swap in without a reload. As soon as the toolbar is open,
10
19
  every surface that differs is outlined and counted in the bar ("13 changed", then "2 / 13 changed"). Previous and Next scroll to each one
11
20
  in reading order and outline it stronger than the others; Isolate dims the rest of the page (Esc turns it off). The
@@ -8,8 +8,8 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
8
8
  import { useState, useEffect, useId, useLayoutEffect, useMemo, useRef, useCallback } from "react";
9
9
  import { ThemeProvider, Tooltip, Button, ButtonGroup, IconButton, VisuallyHidden, Kbd, Popover, PopoverTrigger, PopoverContent, Checkbox, Icon, Link, Badge } from "@bridge-ds/react";
10
10
  import { ScanSearch, ChevronLeft, ChevronRight, GripVertical, ListChecks, ChevronUp, Check, Info, Minimize2 } from "lucide-react";
11
- import { g as githubReads, D as DEFAULT_PROXY, l as loadBuild, u as useDocumentScope } from "./index-CCTqUFz3.js";
12
- import { t as tokenChanges, c as changedScreens, b as changeMode, d as readableTitle, e as toggleIncluded, r as readChanges, w as whereAmI, f as readReturn, g as readUse, h as shouldRender, O as OPEN_KEY, i as readDock, l as liveTokensUrl, n as noVisibleDifference, j as readScroll, S as SCROLL_PARAM, k as sameScreenOn, m as studioChangesUrl, o as studioChangeUrl, T as TOGETHER_EMPTY_LINE, p as contrastRatio, q as toHex, u as fromText, v as TOGETHER_HINT, x as isSubtle, y as isFlipKey, z as placeToolbar, A as togetherLabel, P as PARAM, B as changeSummary, C as dockAt, D as stepDock, E as DOCK_KEY } from "./session-D6oH78KG.js";
11
+ import { g as githubReads, D as DEFAULT_PROXY, l as loadBuild, u as useDocumentScope } from "./index-cRndUN9A.js";
12
+ import { t as tokenChanges, c as changedScreens, b as changeMode, d as readableTitle, M as MARKS_PARAM, e as toggleIncluded, r as readChanges, w as whereAmI, f as readReturn, g as readUse, h as shouldRender, O as OPEN_KEY, i as readDock, l as liveTokensUrl, n as noVisibleDifference, j as readScroll, S as SCROLL_PARAM, k as sameScreenOn, m as studioChangesUrl, o as studioChangeUrl, T as TOGETHER_EMPTY_LINE, p as contrastRatio, q as toHex, u as fromText, v as TOGETHER_HINT, x as isSubtle, y as isFlipKey, z as placeToolbar, A as togetherLabel, P as PARAM, B as changeSummary, C as dockAt, D as stepDock, E as DOCK_KEY } from "./session-Dvma9jtT.js";
13
13
  const VISUAL_BRANCH = "bridge-visual";
14
14
  const cacheKey = (n) => `bridge-admin:facts:v3:${n}`;
15
15
  function session$1() {
@@ -461,7 +461,7 @@ function tipSide(box2, view, bar, room = TIP_ROOM) {
461
461
  if (fitsBeside && box2.left >= room.width) return "left";
462
462
  return "top";
463
463
  }
464
- const IGNORE = "[data-bridge-toolbar], [data-bridge-overlay]";
464
+ const IGNORE$1 = "[data-bridge-toolbar], [data-bridge-overlay]";
465
465
  function useChangedElements(on, live, vars, screen, sheetIs = "before") {
466
466
  const [result, setResult] = useState(null);
467
467
  const key = vars.join(",");
@@ -470,12 +470,71 @@ function useChangedElements(on, live, vars, screen, sheetIs = "before") {
470
470
  setResult(null);
471
471
  return;
472
472
  }
473
- const run = () => setResult(changedElements(key.split(","), live, (el) => Boolean(el.closest(IGNORE)), sheetIs));
473
+ const run = () => setResult(changedElements(key.split(","), live, (el) => Boolean(el.closest(IGNORE$1)), sheetIs));
474
474
  const ids = [window.setTimeout(run, 0), window.setTimeout(run, 400), window.setTimeout(run, 1200)];
475
475
  return () => ids.forEach((id) => window.clearTimeout(id));
476
476
  }, [on, live, key, screen, sheetIs]);
477
477
  return result;
478
478
  }
479
+ const CHANGE_ATTR = "data-bridge-change";
480
+ const MARKS_KEY = "bridge-admin:marks";
481
+ const IGNORE = "[data-bridge-toolbar], [data-bridge-overlay]";
482
+ function markerLabel(value) {
483
+ const v = (value ?? "").replace(/\s+/g, " ").trim();
484
+ return v ? v.charAt(0).toUpperCase() + v.slice(1) : "Changed area";
485
+ }
486
+ function markedElements(root = document) {
487
+ return Array.from(root.querySelectorAll(`[${CHANGE_ATTR}]`)).filter((el) => !el.closest(IGNORE)).map((el) => ({ el, pseudo: null, roots: [], props: [], label: markerLabel(el.getAttribute(CHANGE_ATTR)), keys: [], floating: false }));
488
+ }
489
+ function sameMarks(a, b) {
490
+ return Boolean(a) && a.length === b.length && a.every((x, i) => x.el === b[i].el && x.label === b[i].label);
491
+ }
492
+ const waitingText = (n) => n === null ? "See what changed" : `${n} changed on This change`;
493
+ const NO_MARKS_LINE = "This change does not mark what it changed yet. Compare Live product and This change.";
494
+ function readMarks(search, route, storage) {
495
+ const raw = new URLSearchParams(search).get(MARKS_PARAM);
496
+ if (raw !== null && /^\d{1,4}$/.test(raw)) {
497
+ try {
498
+ storage?.setItem(MARKS_KEY, JSON.stringify({ route, n: Number(raw) }));
499
+ } catch {
500
+ }
501
+ return Number(raw);
502
+ }
503
+ try {
504
+ const kept = JSON.parse(storage?.getItem(MARKS_KEY) ?? "null");
505
+ return kept && kept.route === route && typeof kept.n === "number" ? kept.n : null;
506
+ } catch {
507
+ return null;
508
+ }
509
+ }
510
+ const OWN = "[data-bridge-toolbar], [data-bridge-overlay]";
511
+ function useMarkedChanges(on, screen) {
512
+ const [marks, setMarks] = useState(null);
513
+ useEffect(() => {
514
+ if (!on) {
515
+ setMarks(null);
516
+ return;
517
+ }
518
+ const run = () => {
519
+ const next = markedElements();
520
+ setMarks((prev) => sameMarks(prev, next) ? prev : next);
521
+ };
522
+ const ids = [window.setTimeout(run, 0), window.setTimeout(run, 400), window.setTimeout(run, 1200)];
523
+ let frame = 0;
524
+ const mo = typeof MutationObserver === "undefined" ? null : new MutationObserver((records) => {
525
+ if (records.every((r) => r.target instanceof Element && r.target.closest(OWN))) return;
526
+ cancelAnimationFrame(frame);
527
+ frame = requestAnimationFrame(run);
528
+ });
529
+ mo?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [CHANGE_ATTR] });
530
+ return () => {
531
+ ids.forEach((id) => window.clearTimeout(id));
532
+ cancelAnimationFrame(frame);
533
+ mo?.disconnect();
534
+ };
535
+ }, [on, screen]);
536
+ return marks;
537
+ }
479
538
  const PIN_ATTR = "data-bridge-pin";
480
539
  const PIN_CLASS = "bridge-pin";
481
540
  const STYLE_ATTR = "data-bridge-pinned";
@@ -1045,6 +1104,8 @@ function Toolbar({
1045
1104
  const outlines = outlinePick ?? open;
1046
1105
  const toggleOutlines = useCallback(() => setOutlinePick((p) => !(p ?? open)), [open]);
1047
1106
  const canOutline = facts?.mode === "tokens" && (facts.tokens.length ?? 0) > 0;
1107
+ const isCode = !tg && Boolean(facts) && facts.mode !== "tokens";
1108
+ const canMark = isCode && side === "after";
1048
1109
  const live = useLiveSheet(liveUrl, outlines && canOutline, side === "before" && facts?.mode === "tokens");
1049
1110
  const onePairs = usePairs(tg ? null : facts, liveUrl, brand2, side === "before");
1050
1111
  const pairs = tg ? T?.pairs ?? [] : onePairs;
@@ -1058,11 +1119,15 @@ function Toolbar({
1058
1119
  screenKey,
1059
1120
  tg ? "after" : "before"
1060
1121
  );
1122
+ const marks = useMarkedChanges(canMark, screenKey);
1123
+ const marksOn = outlines && canMark && Boolean(marks?.length);
1124
+ const showing = outlinesOn || marksOn;
1125
+ const found = outlinesOn ? diff?.surfaces ?? null : marksOn ? marks : null;
1061
1126
  const nameOf = (v) => facts?.tokens.find((t) => t.cssVar === v)?.name ?? v;
1062
1127
  const [shown, setShown] = useState({ count: 0, obstacles: [] });
1063
1128
  const setShownStable = useCallback((next) => setShown((p) => JSON.stringify(p) === JSON.stringify(next) ? p : next), []);
1064
1129
  const outlineBoxes = useRef([]);
1065
- outlineBoxes.current = outlinesOn ? shown.obstacles : [];
1130
+ outlineBoxes.current = showing ? shown.obstacles : [];
1066
1131
  const [emphasis, setEmphasis] = useState(null);
1067
1132
  const [flash, setFlash] = useState(false);
1068
1133
  const [sheetOpen, setSheetOpen] = useState(false);
@@ -1073,14 +1138,14 @@ function Toolbar({
1073
1138
  const reduced = useMedia("(prefers-reduced-motion: reduce)");
1074
1139
  usePinnedBridgeTokens(liveTokensUrl(build), theme);
1075
1140
  useEffect(() => {
1076
- if (!outlinesOn || reduced) {
1141
+ if (!showing || reduced) {
1077
1142
  setFlash(false);
1078
1143
  return;
1079
1144
  }
1080
1145
  setFlash(true);
1081
1146
  const id = window.setTimeout(() => setFlash(false), 1500);
1082
1147
  return () => window.clearTimeout(id);
1083
- }, [outlinesOn, reduced]);
1148
+ }, [showing, reduced]);
1084
1149
  useEffect(() => {
1085
1150
  if (!sheetOpen) setEmphasis(null);
1086
1151
  }, [sheetOpen]);
@@ -1096,6 +1161,13 @@ function Toolbar({
1096
1161
  useEffect(() => {
1097
1162
  if (shownIn && theme !== shownIn) setShownIn(null);
1098
1163
  }, [theme, shownIn]);
1164
+ const waiting = useMemo(() => isCode && side === "before" ? readMarks(window.location.search, hash, session()) : null, [isCode, side, hash]);
1165
+ useEffect(() => {
1166
+ if (!new URLSearchParams(window.location.search).has(MARKS_PARAM)) return;
1167
+ const url = new URL(window.location.href);
1168
+ url.searchParams.delete(MARKS_PARAM);
1169
+ window.history.replaceState(window.history.state, "", url);
1170
+ }, []);
1099
1171
  useEffect(() => {
1100
1172
  const y = readScroll(window.location.search);
1101
1173
  if (y === null) return;
@@ -1121,16 +1193,16 @@ function Toolbar({
1121
1193
  }
1122
1194
  const here2 = { search: window.location.search, hash: window.location.hash };
1123
1195
  if (next === "before" && build.production) {
1124
- window.location.assign(sameScreenOn(build.production, here2, { change, returnTo: window.location.origin, scroll: window.scrollY }));
1196
+ window.location.assign(sameScreenOn(build.production, here2, { change, returnTo: window.location.origin, scroll: window.scrollY, marks: marks?.length ?? null }));
1125
1197
  } else if (next === "after" && returnTo) {
1126
1198
  window.location.assign(sameScreenOn(returnTo, here2, { change, scroll: window.scrollY }));
1127
1199
  }
1128
1200
  },
1129
- [side, facts, build, change, returnTo]
1201
+ [side, facts, build, change, returnTo, marks]
1130
1202
  );
1131
1203
  useEffect(() => {
1132
1204
  const onKey = (e) => {
1133
- if (canOutline && !noDiff && isKey(e, "c")) {
1205
+ if ((canOutline && !noDiff || canMark && marks?.length) && isKey(e, "c")) {
1134
1206
  e.preventDefault();
1135
1207
  toggleOutlines();
1136
1208
  return;
@@ -1141,7 +1213,7 @@ function Toolbar({
1141
1213
  };
1142
1214
  window.addEventListener("keydown", onKey);
1143
1215
  return () => window.removeEventListener("keydown", onKey);
1144
- }, [choose, side, canOutline, noDiff, toggleOutlines]);
1216
+ }, [choose, side, canOutline, canMark, marks, noDiff, toggleOutlines]);
1145
1217
  useEffect(() => {
1146
1218
  if (!isolate) return;
1147
1219
  const onEscape = (e) => {
@@ -1183,7 +1255,7 @@ function Toolbar({
1183
1255
  window.removeEventListener("hashchange", measure);
1184
1256
  document.body.style.paddingBottom = "";
1185
1257
  };
1186
- }, [dock, open, facts, shown, outlinesOn]);
1258
+ }, [dock, open, facts, shown, showing]);
1187
1259
  const toggleOpen = (next) => {
1188
1260
  setOpen(next);
1189
1261
  remember(OPEN_KEY, next ? "1" : "0");
@@ -1236,8 +1308,8 @@ function Toolbar({
1236
1308
  const behind = facts?.behind ? "Live has new changes since this change started." : null;
1237
1309
  const shared = screens?.length && screens.every((s) => s.tenant === screens[0].tenant && s.theme === screens[0].theme) ? screens[0] : null;
1238
1310
  const shopOf = (t) => shops.find((x) => x.id === t)?.name ?? t;
1239
- const surfaces = useMemo(() => outlinesOn ? diff?.surfaces ?? [] : [], [outlinesOn, diff]);
1240
- const shownCount = outlinesOn && diff ? shown.count : null;
1311
+ const surfaces = useMemo(() => found ?? [], [found]);
1312
+ const shownCount = showing && found ? shown.count : null;
1241
1313
  const order = useMemo(() => {
1242
1314
  const on = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => rendered(s.el));
1243
1315
  const places = on.map(({ s }) => {
@@ -1247,12 +1319,12 @@ function Toolbar({
1247
1319
  });
1248
1320
  return readingOrder(places).map((k) => on[k].i);
1249
1321
  }, [surfaces]);
1250
- const total = outlinesOn && diff ? order.length : null;
1322
+ const total = showing && found ? order.length : null;
1251
1323
  const current = position !== null ? order[position] ?? null : null;
1252
1324
  useEffect(() => {
1253
1325
  setPosition(null);
1254
1326
  setAnnounce("");
1255
- }, [screenKey, outlinesOn]);
1327
+ }, [screenKey, showing]);
1256
1328
  useEffect(() => {
1257
1329
  if (position !== null && position >= order.length) setPosition(null);
1258
1330
  }, [order.length, position]);
@@ -1281,7 +1353,7 @@ function Toolbar({
1281
1353
  ] }) : noDiff ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1282
1354
  /* @__PURE__ */ jsx("span", { children: noDifferenceText(noDiff, shops) }),
1283
1355
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", onClick: fixNoDiff, className: styles.noteAction, children: noDiff.kind === "theme" ? `Switch to ${noDiff.touched}` : `Switch to ${brandName(shops, noDiff.touched)}` })
1284
- ] }) : (outlinesOn || side === "before") && live.state === "error" ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: "Could not load the live product. Check your connection, then try again." }) : total === 0 ? tg ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: TOGETHER_EMPTY_LINE }) : elsewhere.length ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1356
+ ] }) : open && canMark && marks?.length === 0 ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: NO_MARKS_LINE }) : (outlinesOn || side === "before") && live.state === "error" ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: "Could not load the live product. Check your connection, then try again." }) : total === 0 ? tg ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: TOGETHER_EMPTY_LINE }) : elsewhere.length ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1285
1357
  /* @__PURE__ */ jsx("span", { children: elsewhereLine(elsewhere.map((sc) => sc.label)) }),
1286
1358
  /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: screenHref(elsewhere[0]) }), onClick: goTo(elsewhere[0]), className: styles.noteAction, children: [
1287
1359
  "Go to ",
@@ -1314,16 +1386,17 @@ function Toolbar({
1314
1386
  const subtleOf = (before, after) => isSubtle(before, after) ? /* @__PURE__ */ jsx("span", { className: styles.subtle, children: "Subtle change" }) : null;
1315
1387
  const roleGroups = [...visiblePairs.reduce((m, p) => m.set(p.token.name, [...m.get(p.token.name) ?? [], p]), /* @__PURE__ */ new Map())].map(([name2, list2]) => ({ name: name2, pairs: list2 }));
1316
1388
  const tokenNames = (facts?.tokens ?? []).map((t) => t.name);
1317
- const summary = shownCount === null ? null : surfaceLine(diff.surfaces.flatMap((s) => s.roots.map(nameOf)), total ?? shownCount);
1318
- const countText = !outlinesOn ? "Show what changed" : total === null ? "Finding changes" : total === 0 ? "Nothing changed here" : position !== null ? positionLabel(position, total) : changeCount(total);
1319
- const toggleTip = outlinesOn ? `${summary ? `${summary}. ` : ""}Press C to hide the outlines.` : `Outline what ${tokenNames.length === 1 ? tokenNames[0] : "this change"} changes on this screen. Press C.`;
1320
- const nav2 = canOutline ? /* @__PURE__ */ jsx(
1389
+ const markNames = (marks ?? []).map((m) => m.label);
1390
+ const summary = shownCount === null ? null : marksOn ? `Marked by this change: ${markNames.slice(0, 3).join(", ")}${markNames.length > 3 ? ` and ${markNames.length - 3} more` : ""}` : surfaceLine(diff.surfaces.flatMap((s) => s.roots.map(nameOf)), total ?? shownCount);
1391
+ const countText = !showing ? "Show what changed" : total === null ? "Finding changes" : total === 0 ? "Nothing changed here" : position !== null ? positionLabel(position, total) : changeCount(total);
1392
+ const toggleTip = showing ? `${summary ? `${summary}. ` : ""}Press C to hide the outlines.` : `Outline what ${tokenNames.length === 1 ? tokenNames[0] : "this change"} changes on this screen. Press C.`;
1393
+ const nav2 = canOutline || canMark && marks?.length ? /* @__PURE__ */ jsx(
1321
1394
  ChangeNav,
1322
1395
  {
1323
1396
  countText,
1324
1397
  tip: toggleTip,
1325
- outlinesOn,
1326
- disabled: Boolean(noDiff),
1398
+ outlinesOn: showing,
1399
+ disabled: canOutline && Boolean(noDiff),
1327
1400
  total,
1328
1401
  isolate: isolateOn,
1329
1402
  announce,
@@ -1331,6 +1404,15 @@ function Toolbar({
1331
1404
  onStep: step,
1332
1405
  onIsolate: setIsolate
1333
1406
  }
1407
+ ) : isCode && side === "before" && waiting !== 0 ? (
1408
+ // Live product: the marked areas are on This change. The count when the switch carried it, else where to look.
1409
+ /* @__PURE__ */ jsx("div", { className: styles.nav, children: /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: /* @__PURE__ */ jsxs(Fragment, { children: [
1410
+ "Go to This change to see ",
1411
+ waiting === 1 ? "it" : "them",
1412
+ " outlined. ",
1413
+ /* @__PURE__ */ jsx(Kbd, { keys: "B", size: "sm" }),
1414
+ " flips sides."
1415
+ ] }), children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "md", iconStart: ScanSearch, disabled: !canFlip, onClick: () => choose("after"), className: styles.countButton, children: waitingText(waiting) }) }) })
1334
1416
  ) : null;
1335
1417
  const shownSurfaces = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => shownRect(s.el));
1336
1418
  const focusSurface = (i) => {
@@ -1341,6 +1423,15 @@ function Toolbar({
1341
1423
  if (order.includes(i)) setPosition(order.indexOf(i));
1342
1424
  };
1343
1425
  const tokensOf = (x) => [...new Set(x.roots.map(nameOf))].join(", ");
1426
+ const focusMark = (i) => {
1427
+ const el = marks?.[i]?.el;
1428
+ if (!el) return;
1429
+ if (!marksOn) setOutlinePick(true);
1430
+ if (!isPage(el)) el.scrollIntoView({ block: "center", behavior: reduced ? "auto" : "smooth" });
1431
+ setEmphasis(i);
1432
+ if (order.includes(i)) setPosition(order.indexOf(i));
1433
+ };
1434
+ const footLine = tg ? facts ? "Screens are measured per change, not for this mix. Show what changed outlines this screen." : "Reading the changes." : !facts ? "Reading the change." : isCode ? side === "before" ? "What this change adds or changes is outlined on This change." : marks?.length ? null : NO_MARKS_LINE : "The screens this change affects are not measured yet.";
1344
1435
  const sides2 = /* @__PURE__ */ jsx(ButtonGroup, { appearance: "attached", "aria-label": "Compare", className: styles.sides, children: ["before", "after"].map((s) => /* @__PURE__ */ jsx(
1345
1436
  Tooltip,
1346
1437
  {
@@ -1381,7 +1472,7 @@ function Toolbar({
1381
1472
  "data-dock": place.dock,
1382
1473
  style: place.lift ? { paddingBottom: `${place.lift}px` } : void 0,
1383
1474
  children: [
1384
- outlinesOn && surfaces.length ? /* @__PURE__ */ jsx(
1475
+ showing && surfaces.length ? /* @__PURE__ */ jsx(
1385
1476
  ChangeOutlines,
1386
1477
  {
1387
1478
  surfaces,
@@ -1392,6 +1483,12 @@ function Toolbar({
1392
1483
  bar: () => root.current?.getBoundingClientRect() ?? null,
1393
1484
  onShown: setShownStable,
1394
1485
  tip: (s) => {
1486
+ if (marksOn) {
1487
+ return /* @__PURE__ */ jsx("span", { className: styles.tip, children: /* @__PURE__ */ jsxs("span", { className: styles.tipText, children: [
1488
+ /* @__PURE__ */ jsx("span", { children: s.label }),
1489
+ /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: "New or changed in this change" })
1490
+ ] }) });
1491
+ }
1395
1492
  const prop = s.props.find((p) => /^rgb/.test(p.before) && /^rgb/.test(p.after));
1396
1493
  const token = prop ? visiblePairs.find((p) => p.color && toHex(p.after) === toHex(prop.after) && toHex(p.before) === toHex(prop.before)) : null;
1397
1494
  const name2 = token?.token.name ?? [...new Set(s.roots.map(nameOf))].slice(0, 2).join(", ");
@@ -1423,6 +1520,7 @@ function Toolbar({
1423
1520
  "aria-label": `Bridge: ${label}, ${title2}`,
1424
1521
  "data-side": side,
1425
1522
  "data-diff-ms": outlinesOn ? diff?.ms : void 0,
1523
+ "data-marks": canMark ? marks?.length ?? void 0 : void 0,
1426
1524
  "data-diff-count": shownCount ?? void 0,
1427
1525
  "data-diff-total": total ?? void 0,
1428
1526
  "data-diff-elements": outlinesOn ? diff?.elements.length : void 0,
@@ -1490,6 +1588,28 @@ function Toolbar({
1490
1588
  "."
1491
1589
  ] }) : null
1492
1590
  ] }) : null,
1591
+ canMark && marks?.length ? /* @__PURE__ */ jsxs("section", { className: styles.sheetSection, "aria-label": `Changed here, ${marks.length}`, children: [
1592
+ /* @__PURE__ */ jsxs("p", { className: styles.sectionTitle, children: [
1593
+ "Changed here · ",
1594
+ marks.length
1595
+ ] }),
1596
+ /* @__PURE__ */ jsx("ul", { className: styles.rows, children: marks.map((m, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
1597
+ Button,
1598
+ {
1599
+ variant: "ghost",
1600
+ size: "md",
1601
+ fullWidth: true,
1602
+ className: styles.row,
1603
+ "data-emphasis": marksOn && emphasis === i || void 0,
1604
+ onMouseEnter: () => marksOn && setEmphasis(i),
1605
+ onMouseLeave: () => setEmphasis((e) => e === i ? null : e),
1606
+ onFocus: () => marksOn && setEmphasis(i),
1607
+ onBlur: () => setEmphasis((e) => e === i ? null : e),
1608
+ onClick: () => focusMark(i),
1609
+ children: /* @__PURE__ */ jsx("span", { className: styles.rowText, children: /* @__PURE__ */ jsx("span", { className: styles.rowName, children: m.label }) })
1610
+ }
1611
+ ) }, i)) })
1612
+ ] }) : null,
1493
1613
  outlinesOn && shownSurfaces.length ? /* @__PURE__ */ jsxs("section", { className: styles.sheetSection, "aria-label": `Changed here, ${shownSurfaces.length}`, children: [
1494
1614
  /* @__PURE__ */ jsxs("p", { className: styles.sectionTitle, children: [
1495
1615
  "Changed here · ",
@@ -1570,7 +1690,7 @@ function Toolbar({
1570
1690
  behind
1571
1691
  ] }) : null,
1572
1692
  /* @__PURE__ */ jsx("div", { className: styles.sheetPhone, children: /* @__PURE__ */ jsx(Link, { variant: "standalone", href: studio2, className: styles.back, children: "Back to Studio" }) }),
1573
- screens === null ? /* @__PURE__ */ jsx("p", { className: `${styles.note} ${styles.sheetFoot}`, children: tg ? facts ? "Screens are measured per change, not for this mix. Show what changed outlines this screen." : "Reading the changes." : facts ? "The screens this change affects are not measured yet." : "Reading the change." }) : screens.length === 0 ? /* @__PURE__ */ jsx("p", { className: `${styles.note} ${styles.sheetFoot}`, children: "No admin screen changes. Component examples may still change: Studio lists them." }) : null
1693
+ screens === null ? footLine ? /* @__PURE__ */ jsx("p", { className: `${styles.note} ${styles.sheetFoot}`, children: footLine }) : null : screens.length === 0 ? /* @__PURE__ */ jsx("p", { className: `${styles.note} ${styles.sheetFoot}`, children: "No admin screen changes. Component examples may still change: Studio lists them." }) : null
1574
1694
  ] })
1575
1695
  ] }),
1576
1696
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: studio2 }), className: styles.studio, children: "Back to Studio" })
@@ -1615,4 +1735,4 @@ function useHash() {
1615
1735
  export {
1616
1736
  ToolbarRoot as default
1617
1737
  };
1618
- //# sourceMappingURL=Toolbar-DJUb3p2c.js.map
1738
+ //# sourceMappingURL=Toolbar-ByLHemKF.js.map