@bridge-ds/toolbar 0.2.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.
@@ -1,15 +1,15 @@
1
1
  if (typeof document !== 'undefined' && !document.querySelector('style[data-bridge-toolbar-css="' + "Toolbar" + '"]')) {
2
2
  const style = document.createElement('style');
3
3
  style.setAttribute('data-bridge-toolbar-css', "Toolbar");
4
- style.textContent = "/* The Bridge toolbar floats over the admin's content column: bottom right by default, docked left or centre on demand,\n lifted above the page's primary buttons and the outlined surfaces when every dock would cover one. One row, 44px,\n on desktop; compact density so every control is 36px. The border stays neutral. Text: 14 and 12, plus the wordmark.\n Tokens only. */\n._anchor_nhlur_5 {\n position: fixed;\n inset-inline: var(--space-4);\n bottom: var(--space-4);\n z-index: var(--layer-sticky);\n display: flex;\n justify-content: flex-end;\n pointer-events: none;\n}\n\n._anchor_nhlur_5[data-dock=\"start\"] {\n justify-content: flex-start;\n}\n\n._anchor_nhlur_5[data-dock=\"center\"] {\n justify-content: center;\n}\n\n/* Never over the sidebar: the docks live in the content column (the shell's sidebar is size-60 wide above 1024px). */\n@media (min-width: 1025px) {\n ._anchor_nhlur_5 {\n inset-inline-start: calc(var(--size-60) + var(--space-4));\n }\n}\n\n._panel_nhlur_30,\n._pill_nhlur_31 {\n box-sizing: border-box;\n border: var(--border-width-default) solid var(--color-border-default);\n background-color: var(--color-surface-raised);\n box-shadow: var(--elevation-overlay);\n color: var(--color-text-default);\n font-family: var(--typography-ui-family);\n font-size: var(--typography-ui-size);\n line-height: var(--typography-ui-line-height);\n pointer-events: auto;\n}\n\n._panel_nhlur_30[data-dragging],\n._pill_nhlur_31[data-dragging] {\n box-shadow: var(--elevation-modal);\n}\n\n/* Desktop: one row, 44px tall with its border (36px controls); a note, when there is one, takes a second row. */\n._panel_nhlur_30 {\n display: grid;\n grid-template-areas:\n \"grip title actions collapse\"\n \"note note note note\";\n grid-template-rows: calc(var(--size-11) - 2 * var(--border-width-default)) auto;\n grid-template-columns: auto auto minmax(0, 1fr) auto;\n align-items: center;\n column-gap: var(--space-2);\n max-width: min(100%, var(--size-300));\n padding: 0 var(--space-1);\n border-radius: var(--radius-overlay);\n}\n\n._grip_nhlur_63 {\n grid-area: grip;\n cursor: grab;\n touch-action: none;\n}\n\n._panel_nhlur_30[data-dragging] ._grip_nhlur_63 {\n cursor: grabbing;\n}\n\n._title_nhlur_73 {\n display: flex;\n grid-area: title;\n align-items: baseline;\n gap: var(--space-2);\n min-width: 0;\n margin: 0;\n}\n\n._brand_nhlur_82 {\n flex: none;\n font-family: var(--font-family-display);\n font-size: var(--typography-ui-size);\n font-weight: var(--font-weight-semibold);\n}\n\n/* Desktop: the wordmark only; the change's name is in the pill and in the This change tooltip. */\n._panel_nhlur_30 ._name_nhlur_90 {\n display: none;\n}\n\n._name_nhlur_90,\n._pillTitle_nhlur_95 {\n overflow: hidden;\n font-weight: var(--font-weight-medium);\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n._actions_nhlur_102 {\n display: flex;\n grid-area: actions;\n align-items: center;\n justify-content: flex-end;\n gap: var(--space-2);\n min-width: 0;\n}\n\n._sides_nhlur_111 {\n flex: none;\n}\n\n._collapse_nhlur_115 {\n grid-area: collapse;\n}\n\n._phoneOnly_nhlur_119 {\n display: none;\n}\n\n._desktopOnly_nhlur_123 {\n display: contents;\n}\n\n._count_nhlur_127 {\n margin-inline-start: var(--space-1-5);\n color: var(--color-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n/* The colour pair of each changed token: a 24px split swatch, before half and after half. Values in the tooltip. */\n._swatches_nhlur_134 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-1);\n border-radius: var(--radius-inset);\n}\n\n._swatches_nhlur_134:focus-visible {\n outline: var(--focus-ring-width) solid var(--focus-ring-color);\n outline-offset: var(--focus-ring-offset);\n}\n\n._split_nhlur_146 {\n box-sizing: border-box;\n display: inline-flex;\n flex: none;\n width: var(--size-6);\n height: var(--size-6);\n overflow: hidden;\n border: var(--border-width-default) solid var(--color-border-strong);\n border-radius: var(--radius-pill);\n}\n\n._split_nhlur_146 > span {\n flex: 1 1 0;\n}\n\n/* Under 1.1:1 the two halves look alike: say so, so a near-white change reads as intended. */\n._subtle_nhlur_162 {\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n white-space: nowrap;\n}\n\n._more_nhlur_169 {\n white-space: nowrap;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n font-variant-numeric: tabular-nums;\n}\n\n._noteRow_nhlur_176 {\n grid-area: note;\n min-width: 0;\n padding-block-end: var(--space-1);\n}\n\n._note_nhlur_176 {\n margin: 0;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n._noteRow_nhlur_176 ._note_nhlur_176 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 var(--space-2);\n padding-inline-start: var(--space-2);\n color: var(--color-text-default);\n}\n\n._noteAction_nhlur_198 {\n flex: none;\n}\n\n._sheet_nhlur_202 {\n width: min(90vw, var(--size-80));\n}\n\n._list_nhlur_206 {\n display: grid;\n max-height: var(--size-60);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n list-style: none;\n}\n\n._item_nhlur_215 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n min-height: var(--control-height-md);\n}\n\n._screenLink_nhlur_223,\n._here_nhlur_224 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-1-5);\n min-height: var(--control-height-md);\n}\n\n._here_nhlur_224 {\n font-weight: var(--font-weight-semibold);\n}\n\n._meta_nhlur_235 {\n flex: none;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n/* Changed here: one focusable row per surface. Line 1 its role, line 2 its tokens (muted, 12). Nothing truncates. */\n._rows_nhlur_243 {\n display: grid;\n max-height: var(--size-60);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n list-style: none;\n}\n\n._row_nhlur_243 {\n height: auto;\n min-height: var(--control-height-md);\n padding-block: var(--space-1);\n justify-content: flex-start;\n text-align: start;\n}\n\n._rowText_nhlur_260 {\n display: grid;\n min-width: 0;\n font-weight: var(--font-weight-regular);\n white-space: normal;\n}\n\n._rowName_nhlur_267 {\n color: var(--color-text-default);\n font-size: var(--typography-ui-size);\n line-height: var(--typography-ui-line-height);\n}\n\n._rowMeta_nhlur_273 {\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n._tipText_nhlur_279 {\n display: grid;\n}\n\n._tipStep_nhlur_283 {\n opacity: 0.8;\n}\n\n._sheetFoot_nhlur_287 {\n margin-top: var(--space-2);\n}\n\n._sheetSection_nhlur_291 {\n margin-top: var(--space-2);\n padding-top: var(--space-2);\n border-top: var(--border-width-default) solid var(--color-border-default);\n}\n\n._sheetSection_nhlur_291:first-child {\n margin-top: 0;\n padding-top: 0;\n border-top: 0;\n}\n\n._sectionTitle_nhlur_303 {\n margin: 0;\n font-weight: var(--font-weight-semibold);\n}\n\n/* A separate notice, not part of a list. */\n._notice_nhlur_309 {\n display: flex;\n align-items: flex-start;\n gap: var(--space-1-5);\n margin: var(--space-2) 0 0;\n padding-top: var(--space-2);\n border-top: var(--border-width-default) solid var(--color-border-default);\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n/* In the sheet on phones only: the token pairs and the way back. */\n._sheetPhone_nhlur_322 {\n display: none;\n}\n\n._back_nhlur_326 {\n display: inline-flex;\n align-items: center;\n min-height: var(--control-height-md);\n}\n\n._pill_nhlur_31 {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto auto;\n align-items: center;\n gap: var(--space-2);\n max-width: min(100%, var(--size-120));\n padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4);\n border-radius: var(--radius-pill);\n}\n\n/* With a note under it, the pill becomes a small card. */\n._pill_nhlur_31[data-note] {\n border-radius: var(--radius-overlay);\n}\n\n._pill_nhlur_31 ._noteRow_nhlur_176 {\n grid-column: 1 / -1;\n padding-inline-end: var(--space-3);\n}\n\n._pill_nhlur_31 ._noteRow_nhlur_176 ._note_nhlur_176 {\n padding-inline-start: 0;\n}\n\n/* Show what changed: a dashed Deep Harbor frame (Bridge's tool colour, not the product's) on each changed surface,\n with a faint Deep Harbor tint and a role chip. Fixed layer: no pointer events, no focus, no layout. */\n._overlay_nhlur_358 {\n position: fixed;\n inset: 0;\n z-index: var(--layer-sticky);\n pointer-events: none;\n}\n\n._box_nhlur_365 {\n position: absolute;\n box-sizing: border-box;\n outline: var(--border-width-strong) dashed var(--color-action-primary-bg);\n outline-offset: var(--border-width-strong);\n}\n\n/* A page-sized surface: an inset frame, so it reads as a selection, not as the window edge. */\n._box_nhlur_365[data-page] {\n outline-offset: 0;\n border-radius: var(--radius-container);\n}\n\n/* No tint on the page frame: it would tint the very colour being compared. */\n._box_nhlur_365[data-page] ._tint_nhlur_379 {\n display: none;\n}\n\n/* No room above (top of the viewport): under the surface instead. */\n/* No room above (top of the viewport): inside the surface's top-left corner instead. */\n._box_nhlur_365[data-inside] ._chip_nhlur_385 {\n top: 0;\n bottom: auto;\n left: 0;\n}\n\n/* The surface a list row points at: solid frame, stronger tint. */\n._box_nhlur_365[data-emphasis] {\n outline-style: solid;\n}\n\n._box_nhlur_365[data-emphasis] ._tint_nhlur_379 {\n opacity: 0.1;\n}\n\n._box_nhlur_365[data-page] ._chip_nhlur_385 {\n top: var(--space-1);\n bottom: auto;\n left: var(--space-1);\n}\n\n._tint_nhlur_379 {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-color: var(--color-action-primary-bg);\n opacity: 0.04;\n}\n\n/* Above the surface's top-left corner, so it never hides the surface's own content. */\n/* Hidden by default: shown on hover, from its list row, and briefly when outlines turn on. */\n._chip_nhlur_385 {\n position: absolute;\n visibility: hidden;\n bottom: calc(100% + var(--border-width-strong) * 2);\n left: calc(var(--border-width-strong) * -2);\n max-width: var(--size-60);\n overflow: hidden;\n padding: 0 var(--space-1-5);\n border-radius: var(--radius-inset);\n background-color: var(--color-action-primary-bg);\n color: var(--color-action-primary-text);\n font-family: var(--typography-ui-family);\n font-size: var(--typography-caption-size);\n font-weight: var(--font-weight-medium);\n line-height: var(--typography-caption-line-height);\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n._chip_nhlur_385[data-shown] {\n visibility: visible;\n}\n\n._tip_nhlur_279 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n}\n\n@media (max-width: 760px) {\n ._anchor_nhlur_5 {\n inset-inline: var(--space-2);\n bottom: var(--space-2);\n }\n\n /* Phone, open: docked to the bottom edge as one 44px row (sides, Changes, outline toggle, collapse). */\n ._anchor_nhlur_5[data-open] {\n inset-inline: 0;\n bottom: 0;\n }\n\n ._panel_nhlur_30 {\n grid-template-areas:\n \"actions toggle collapse\"\n \"note note note\";\n grid-template-rows: calc(var(--size-11) - var(--border-width-default)) auto;\n grid-template-columns: minmax(0, 1fr) auto auto;\n column-gap: var(--space-2);\n width: 100%;\n padding: 0 var(--space-1);\n border-width: var(--border-width-default) 0 0;\n border-radius: 0;\n }\n\n ._title_nhlur_73 {\n display: none;\n }\n\n ._phoneOnly_nhlur_119 {\n display: inline-flex;\n grid-area: toggle;\n }\n\n ._grip_nhlur_63,\n ._brand_nhlur_82,\n ._studio_nhlur_481,\n ._desktopOnly_nhlur_123 {\n display: none;\n }\n\n ._actions_nhlur_102 {\n justify-content: space-between;\n }\n\n ._noteRow_nhlur_176 ._note_nhlur_176 {\n padding-inline-start: 0;\n }\n\n ._pill_nhlur_31 {\n grid-template-columns: minmax(0, 1fr) auto auto;\n }\n\n ._sheetPhone_nhlur_322 {\n display: block;\n }\n}\n\n/* Below 480px the note's action takes its own line. */\n@media (max-width: 480px) {\n ._noteAction_nhlur_198 {\n flex-basis: 100%;\n justify-content: flex-start;\n }\n}\n";
4
+ style.textContent = "/* The Bridge toolbar floats over the admin's content column: bottom right by default, docked left or centre on demand,\n lifted above the page's primary buttons and the outlined surfaces when every dock would cover one. One row, 44px,\n on desktop; compact density so every control is 36px. The border stays neutral. Text: 14 and 12, plus the wordmark.\n Tokens only. */\n._anchor_a8mxc_5 {\n position: fixed;\n inset-inline: var(--space-4);\n bottom: var(--space-4);\n z-index: var(--layer-sticky);\n display: flex;\n justify-content: flex-end;\n pointer-events: none;\n}\n\n._anchor_a8mxc_5[data-dock=\"start\"] {\n justify-content: flex-start;\n}\n\n._anchor_a8mxc_5[data-dock=\"center\"] {\n justify-content: center;\n}\n\n/* Never over the sidebar: the docks live in the content column (the shell's sidebar is size-60 wide above 1024px). */\n@media (min-width: 1025px) {\n ._anchor_a8mxc_5 {\n inset-inline-start: calc(var(--size-60) + var(--space-4));\n }\n}\n\n._panel_a8mxc_30,\n._pill_a8mxc_31 {\n box-sizing: border-box;\n border: var(--border-width-default) solid var(--color-border-default);\n background-color: var(--color-surface-raised);\n box-shadow: var(--elevation-overlay);\n color: var(--color-text-default);\n font-family: var(--typography-ui-family);\n font-size: var(--typography-ui-size);\n line-height: var(--typography-ui-line-height);\n pointer-events: auto;\n}\n\n/* The outlines layer comes first in the document: the bar, on the same layer, paints above it and its scrim. */\n._panel_a8mxc_30,\n._pill_a8mxc_31 {\n position: relative;\n z-index: var(--layer-sticky);\n}\n\n._panel_a8mxc_30[data-dragging],\n._pill_a8mxc_31[data-dragging] {\n box-shadow: var(--elevation-modal);\n}\n\n/* Desktop: one row, 44px tall with its border (36px controls): compare, then the changes of this screen, then the\n rest. A note, when there is one, takes a second row. */\n._panel_a8mxc_30 {\n display: grid;\n grid-template-areas:\n \"grip title sides nav actions collapse\"\n \"note note note note note note\";\n grid-template-rows: calc(var(--size-11) - 2 * var(--border-width-default)) auto;\n grid-template-columns: auto auto auto auto minmax(0, 1fr) auto;\n align-items: center;\n column-gap: var(--space-2);\n max-width: min(100%, var(--size-300));\n padding: 0 var(--space-1);\n border-radius: var(--radius-overlay);\n}\n\n._grip_a8mxc_71 {\n grid-area: grip;\n cursor: grab;\n touch-action: none;\n}\n\n._panel_a8mxc_30[data-dragging] ._grip_a8mxc_71 {\n cursor: grabbing;\n}\n\n._title_a8mxc_81 {\n display: flex;\n grid-area: title;\n align-items: baseline;\n gap: var(--space-2);\n min-width: 0;\n margin: 0;\n}\n\n._brand_a8mxc_90 {\n flex: none;\n font-family: var(--font-family-display);\n font-size: var(--typography-ui-size);\n font-weight: var(--font-weight-semibold);\n}\n\n/* Desktop: the wordmark only; the change's name is in the pill and in the This change tooltip. */\n._panel_a8mxc_30 ._name_a8mxc_98 {\n display: none;\n}\n\n._name_a8mxc_98,\n._pillTitle_a8mxc_103 {\n overflow: hidden;\n font-weight: var(--font-weight-medium);\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n._actions_a8mxc_110 {\n display: flex;\n grid-area: actions;\n align-items: center;\n justify-content: flex-end;\n gap: var(--space-2);\n min-width: 0;\n}\n\n._sides_a8mxc_119 {\n grid-area: sides;\n}\n\n/* The changes of this screen: the count (also the outlines toggle), Previous and Next, Isolate. */\n._nav_a8mxc_124 {\n display: flex;\n grid-area: nav;\n align-items: center;\n gap: var(--space-2);\n min-width: 0;\n}\n\n._countButton_a8mxc_132 {\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n._steps_a8mxc_137 {\n flex: none;\n}\n\n._collapse_a8mxc_141 {\n grid-area: collapse;\n}\n\n._count_a8mxc_132 {\n margin-inline-start: var(--space-1-5);\n color: var(--color-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n/* The colour pair of each changed token: a 24px split swatch, before half and after half. Values in the tooltip. */\n._swatches_a8mxc_152 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-1);\n border-radius: var(--radius-inset);\n}\n\n._swatches_a8mxc_152:focus-visible {\n outline: var(--focus-ring-width) solid var(--focus-ring-color);\n outline-offset: var(--focus-ring-offset);\n}\n\n._split_a8mxc_164 {\n box-sizing: border-box;\n display: inline-flex;\n flex: none;\n width: var(--size-6);\n height: var(--size-6);\n overflow: hidden;\n border: var(--border-width-default) solid var(--color-border-strong);\n border-radius: var(--radius-pill);\n}\n\n._split_a8mxc_164 > span {\n flex: 1 1 0;\n}\n\n/* Under 1.1:1 the two halves look alike: say so, so a near-white change reads as intended. */\n._subtle_a8mxc_180 {\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n white-space: nowrap;\n}\n\n._more_a8mxc_187 {\n white-space: nowrap;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n font-variant-numeric: tabular-nums;\n}\n\n._noteRow_a8mxc_194 {\n grid-area: note;\n min-width: 0;\n padding-block-end: var(--space-1);\n}\n\n._note_a8mxc_194 {\n margin: 0;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n._noteRow_a8mxc_194 ._note_a8mxc_194 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 var(--space-2);\n padding-inline-start: var(--space-2);\n color: var(--color-text-default);\n}\n\n._noteAction_a8mxc_216 {\n flex: none;\n}\n\n._sheet_a8mxc_220 {\n width: min(90vw, var(--size-80));\n}\n\n._list_a8mxc_224 {\n display: grid;\n max-height: var(--size-60);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n list-style: none;\n}\n\n._item_a8mxc_233 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n min-height: var(--control-height-md);\n}\n\n._screenLink_a8mxc_241,\n._here_a8mxc_242 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-1-5);\n min-height: var(--control-height-md);\n}\n\n._here_a8mxc_242 {\n font-weight: var(--font-weight-semibold);\n}\n\n._meta_a8mxc_253 {\n flex: none;\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n/* Changed here: one focusable row per surface. Line 1 its role, line 2 its tokens (muted, 12). Nothing truncates. */\n._rows_a8mxc_261 {\n display: grid;\n max-height: var(--size-60);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n list-style: none;\n}\n\n._row_a8mxc_261 {\n height: auto;\n min-height: var(--control-height-md);\n padding-block: var(--space-1);\n justify-content: flex-start;\n text-align: start;\n}\n\n._rowText_a8mxc_278 {\n display: grid;\n min-width: 0;\n font-weight: var(--font-weight-regular);\n white-space: normal;\n}\n\n._rowName_a8mxc_285 {\n color: var(--color-text-default);\n font-size: var(--typography-ui-size);\n line-height: var(--typography-ui-line-height);\n}\n\n._rowMeta_a8mxc_291 {\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n._tipText_a8mxc_297 {\n display: grid;\n}\n\n._tipStep_a8mxc_301 {\n opacity: 0.8;\n}\n\n._sheetFoot_a8mxc_305 {\n margin-top: var(--space-2);\n}\n\n._sheetSection_a8mxc_309 {\n margin-top: var(--space-2);\n padding-top: var(--space-2);\n border-top: var(--border-width-default) solid var(--color-border-default);\n}\n\n._sheetSection_a8mxc_309:first-child {\n margin-top: 0;\n padding-top: 0;\n border-top: 0;\n}\n\n._sectionTitle_a8mxc_321 {\n margin: 0;\n font-weight: var(--font-weight-semibold);\n}\n\n/* A separate notice, not part of a list. */\n._notice_a8mxc_327 {\n display: flex;\n align-items: flex-start;\n gap: var(--space-1-5);\n margin: var(--space-2) 0 0;\n padding-top: var(--space-2);\n border-top: var(--border-width-default) solid var(--color-border-default);\n color: var(--color-text-muted);\n font-size: var(--typography-caption-size);\n line-height: var(--typography-caption-line-height);\n}\n\n/* In the sheet on phones only: the token pairs and the way back. */\n._sheetPhone_a8mxc_340 {\n display: none;\n}\n\n._back_a8mxc_344 {\n display: inline-flex;\n align-items: center;\n min-height: var(--control-height-md);\n}\n\n._pill_a8mxc_31 {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto auto;\n align-items: center;\n gap: var(--space-2);\n max-width: min(100%, var(--size-120));\n padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4);\n border-radius: var(--radius-pill);\n}\n\n/* With a note under it, the pill becomes a small card. */\n._pill_a8mxc_31[data-note] {\n border-radius: var(--radius-overlay);\n}\n\n._pill_a8mxc_31 ._noteRow_a8mxc_194 {\n grid-column: 1 / -1;\n padding-inline-end: var(--space-3);\n}\n\n._pill_a8mxc_31 ._noteRow_a8mxc_194 ._note_a8mxc_194 {\n padding-inline-start: 0;\n}\n\n/* Show what changed: a dashed Deep Harbor frame (Bridge's tool colour, not the product's) on each changed surface,\n with a faint Deep Harbor tint and a role chip. Fixed layer: no pointer events, no focus, no layout. */\n._overlay_a8mxc_376 {\n position: fixed;\n inset: 0;\n z-index: var(--layer-sticky);\n pointer-events: none;\n}\n\n._box_a8mxc_383 {\n position: absolute;\n box-sizing: border-box;\n outline: var(--border-width-strong) dashed var(--color-action-primary-bg);\n outline-offset: var(--border-width-strong);\n}\n\n/* A page-sized surface: an inset frame, so it reads as a selection, not as the window edge. */\n._box_a8mxc_383[data-page] {\n outline-offset: 0;\n border-radius: var(--radius-container);\n}\n\n/* No tint on the page frame: it would tint the very colour being compared. */\n._box_a8mxc_383[data-page] ._tint_a8mxc_397 {\n display: none;\n}\n\n/* No room above (top of the viewport): under the surface instead. */\n/* No room above (top of the viewport): inside the surface's top-left corner instead. */\n._box_a8mxc_383[data-inside] ._chip_a8mxc_403 {\n top: 0;\n bottom: auto;\n left: 0;\n}\n\n/* The surface a list row points at: solid frame, stronger tint. */\n._box_a8mxc_383[data-emphasis] {\n outline-style: solid;\n}\n\n._box_a8mxc_383[data-emphasis] ._tint_a8mxc_397 {\n opacity: 0.1;\n}\n\n._box_a8mxc_383[data-page] ._chip_a8mxc_403 {\n top: var(--space-1);\n bottom: auto;\n left: var(--space-1);\n}\n\n._tint_a8mxc_397 {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-color: var(--color-action-primary-bg);\n opacity: 0.04;\n}\n\n/* Above the surface's top-left corner, so it never hides the surface's own content. */\n/* Hidden by default: shown on hover, from its list row, and briefly when outlines turn on. */\n._chip_a8mxc_403 {\n position: absolute;\n visibility: hidden;\n bottom: calc(100% + var(--border-width-strong) * 2);\n left: calc(var(--border-width-strong) * -2);\n max-width: var(--size-60);\n overflow: hidden;\n padding: 0 var(--space-1-5);\n border-radius: var(--radius-inset);\n background-color: var(--color-action-primary-bg);\n color: var(--color-action-primary-text);\n font-family: var(--typography-ui-family);\n font-size: var(--typography-caption-size);\n font-weight: var(--font-weight-medium);\n line-height: var(--typography-caption-line-height);\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n._chip_a8mxc_403[data-shown] {\n visibility: visible;\n}\n\n/* Isolate: the scrim of a dialog over the rest of the page, with a cut-out around each changed surface. */\n._scrim_a8mxc_458 {\n position: absolute;\n inset: 0;\n display: block;\n animation: _scrim-in_a8mxc_1 var(--motion-duration-moderate) var(--motion-easing-enter);\n}\n\n._scrimFill_a8mxc_465 {\n fill: var(--color-overlay-scrim);\n}\n\n@keyframes _scrim-in_a8mxc_1 {\n from {\n opacity: 0;\n }\n}\n\n._tip_a8mxc_297 {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n}\n\n@media (max-width: 760px) {\n ._anchor_a8mxc_5 {\n inset-inline: var(--space-2);\n bottom: var(--space-2);\n }\n\n /* Phone, open: docked to the bottom edge. Row 1: sides, Changes, collapse. Row 2, for a token change: the count,\n Previous and Next, Isolate. */\n ._anchor_a8mxc_5[data-open] {\n inset-inline: 0;\n bottom: 0;\n }\n\n ._panel_a8mxc_30 {\n grid-template-areas:\n \"sides actions collapse\"\n \"nav nav nav\"\n \"note note note\";\n grid-template-rows: calc(var(--size-11) - var(--border-width-default)) auto auto;\n grid-template-columns: minmax(0, 1fr) auto auto;\n column-gap: var(--space-2);\n width: 100%;\n padding: 0 var(--space-1);\n border-width: var(--border-width-default) 0 0;\n border-radius: 0;\n }\n\n ._title_a8mxc_81 {\n display: none;\n }\n\n ._grip_a8mxc_71,\n ._brand_a8mxc_90,\n ._studio_a8mxc_514 {\n display: none;\n }\n\n ._sides_a8mxc_119 {\n justify-self: start;\n }\n\n ._nav_a8mxc_124 {\n padding-block-end: var(--space-1);\n }\n\n ._noteRow_a8mxc_194 ._note_a8mxc_194 {\n padding-inline-start: 0;\n }\n\n ._pill_a8mxc_31 {\n grid-template-columns: minmax(0, 1fr) auto auto;\n }\n\n ._sheetPhone_a8mxc_340 {\n display: block;\n }\n}\n\n/* Below 480px the note's action takes its own line. */\n@media (max-width: 480px) {\n ._noteAction_a8mxc_216 {\n flex-basis: 100%;\n justify-content: flex-start;\n }\n}\n";
5
5
  document.head.appendChild(style);
6
6
  }
7
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
8
- import { useState, useEffect, useLayoutEffect, useMemo, useRef, useCallback } from "react";
9
- import { ThemeProvider, Tooltip, Button, ButtonGroup, IconButton, Popover, PopoverTrigger, PopoverContent, Checkbox, Icon, Link, Badge } from "@bridge-ds/react";
10
- import { GripVertical, ListChecks, ChevronUp, Check, Info, Minimize2, ScanSearch } from "lucide-react";
11
- import { g as githubReads, D as DEFAULT_PROXY, l as loadBuild, u as useDocumentScope } from "./index-3f0I3tAL.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 TOGETHER_HINT, P as PARAM, u as isSubtle, v as toHex, x as fromText, y as isFlipKey, z as placeToolbar, A as togetherLabel, B as changeSummary, C as dockAt, D as stepDock, E as DOCK_KEY } from "./session-CzlIsFDY.js";
7
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
8
+ import { useState, useEffect, useId, useLayoutEffect, useMemo, useRef, useCallback } from "react";
9
+ import { ThemeProvider, Tooltip, Button, ButtonGroup, IconButton, VisuallyHidden, Kbd, Popover, PopoverTrigger, PopoverContent, Checkbox, Icon, Link, Badge } from "@bridge-ds/react";
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-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() {
@@ -232,9 +232,6 @@ function surfaceLine(tokenNames, surfaces) {
232
232
  const tokens = `${names.length} ${names.length === 1 ? "token" : "tokens"} changed: ${names.slice(0, 3).join(", ")}${names.length > 3 ? ` and ${names.length - 3} more` : ""}`;
233
233
  return `${tokens}, on ${surfaces} ${surfaces === 1 ? "surface" : "surfaces"}`;
234
234
  }
235
- function outOfView(n) {
236
- return n === 1 ? "Nothing changed in view. 1 surface changes further down: scroll to see it." : `Nothing changed in view. ${n} surfaces change further down: scroll to see them.`;
237
- }
238
235
  const EMPTY_LINE = "This change doesn't touch this screen. Open Screens to see where it does.";
239
236
  const LANDMARKS = { main: "Content", header: "Header", nav: "Navigation", aside: "Sidebar", footer: "Footer", form: "Form", dialog: "Dialog" };
240
237
  const ROLES = { main: "Content", banner: "Header", navigation: "Navigation", complementary: "Sidebar", contentinfo: "Footer", dialog: "Dialog", toolbar: "Toolbar", tablist: "Tabs", table: "Table", grid: "Table", menu: "Menu", listbox: "List" };
@@ -315,6 +312,13 @@ function shownRect(el) {
315
312
  return w > 0 && h > 0 ? r : null;
316
313
  }
317
314
  const visible = (el) => el.getClientRects().length > 0;
315
+ function rendered(el) {
316
+ const check = el.checkVisibility;
317
+ if (check && !check.call(el, { opacityProperty: true, visibilityProperty: true })) return false;
318
+ const r = el.getBoundingClientRect();
319
+ return r.width > 0 && r.height > 0;
320
+ }
321
+ const isPage = (el) => el === document.documentElement || el === document.body;
318
322
  function changedElements(changedVars, sheet2, ignore, sheetIs = "before") {
319
323
  const live = sheet2;
320
324
  const t0 = performance.now();
@@ -420,7 +424,44 @@ function changedElements(changedVars, sheet2, ignore, sheetIs = "before") {
420
424
  const surfaces = outermost([...plain, ...charts.values()], (a, b) => a.el !== b.el && a.el.contains(b.el));
421
425
  return { elements, surfaces, ms: Math.round((performance.now() - t0) * 10) / 10 };
422
426
  }
423
- const IGNORE = "[data-bridge-toolbar], [data-bridge-overlay]";
427
+ const SAME_LINE = 8;
428
+ function readingOrder(places) {
429
+ return places.map((p, i) => ({ p, i })).sort((a, b) => (Math.abs(a.p.top - b.p.top) <= SAME_LINE ? a.p.left - b.p.left : a.p.top - b.p.top) || a.i - b.i).map((x) => x.i);
430
+ }
431
+ function stepTo(current, delta, count) {
432
+ if (count <= 0) return null;
433
+ if (current === null || current < 0 || current >= count) return delta === 1 ? 0 : count - 1;
434
+ return (current + delta + count) % count;
435
+ }
436
+ const changeCount = (n) => `${n} changed`;
437
+ const positionLabel = (position, n) => `${position + 1} / ${n} changed`;
438
+ const stepAnnouncement = (position, n, label) => `Changed element ${position + 1} of ${n}: ${label}`;
439
+ function elsewhereLine(screens) {
440
+ if (!screens.length) return "This change doesn't touch this screen.";
441
+ const names = screens.length <= 2 ? screens.join(" and ") : `${screens.slice(0, 2).join(", ")} and ${screens.length - 2} more`;
442
+ return `This change doesn't touch this screen. It shows on ${names}.`;
443
+ }
444
+ function isTyping(target) {
445
+ const t = target;
446
+ if (!t) return false;
447
+ if (t.isContentEditable) return true;
448
+ const tag = (t.tagName ?? "").toLowerCase();
449
+ if (tag === "input" || tag === "textarea" || tag === "select") return true;
450
+ return Boolean(t.closest && t.closest('[role="combobox"], [role="textbox"], [role="searchbox"]'));
451
+ }
452
+ function isEscape(e) {
453
+ return e.key === "Escape" && !e.altKey && !e.ctrlKey && !e.metaKey && !isTyping(e.target);
454
+ }
455
+ const TIP_ROOM = { width: 300, height: 64 };
456
+ function tipSide(box2, view, bar, room = TIP_ROOM) {
457
+ const floor = bar ? Math.min(view.height, bar.top) : view.height;
458
+ if (floor - box2.bottom >= room.height) return "bottom";
459
+ const fitsBeside = box2.top >= 0 && box2.top + room.height <= floor;
460
+ if (fitsBeside && view.width - box2.right >= room.width) return "right";
461
+ if (fitsBeside && box2.left >= room.width) return "left";
462
+ return "top";
463
+ }
464
+ const IGNORE$1 = "[data-bridge-toolbar], [data-bridge-overlay]";
424
465
  function useChangedElements(on, live, vars, screen, sheetIs = "before") {
425
466
  const [result, setResult] = useState(null);
426
467
  const key = vars.join(",");
@@ -429,12 +470,71 @@ function useChangedElements(on, live, vars, screen, sheetIs = "before") {
429
470
  setResult(null);
430
471
  return;
431
472
  }
432
- 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));
433
474
  const ids = [window.setTimeout(run, 0), window.setTimeout(run, 400), window.setTimeout(run, 1200)];
434
475
  return () => ids.forEach((id) => window.clearTimeout(id));
435
476
  }, [on, live, key, screen, sheetIs]);
436
477
  return result;
437
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
+ }
438
538
  const PIN_ATTR = "data-bridge-pin";
439
539
  const PIN_CLASS = "bridge-pin";
440
540
  const STYLE_ATTR = "data-bridge-pinned";
@@ -490,52 +590,52 @@ function usePinnedBridgeTokens(url, theme) {
490
590
  };
491
591
  }, [url, theme]);
492
592
  }
493
- const anchor = "_anchor_nhlur_5";
494
- const panel = "_panel_nhlur_30";
495
- const pill = "_pill_nhlur_31";
496
- const grip = "_grip_nhlur_63";
497
- const title = "_title_nhlur_73";
498
- const brand = "_brand_nhlur_82";
499
- const name = "_name_nhlur_90";
500
- const pillTitle = "_pillTitle_nhlur_95";
501
- const actions = "_actions_nhlur_102";
502
- const sides = "_sides_nhlur_111";
503
- const collapse = "_collapse_nhlur_115";
504
- const phoneOnly = "_phoneOnly_nhlur_119";
505
- const desktopOnly = "_desktopOnly_nhlur_123";
506
- const count = "_count_nhlur_127";
507
- const swatches = "_swatches_nhlur_134";
508
- const split = "_split_nhlur_146";
509
- const subtle = "_subtle_nhlur_162";
510
- const more = "_more_nhlur_169";
511
- const noteRow = "_noteRow_nhlur_176";
512
- const note = "_note_nhlur_176";
513
- const noteAction = "_noteAction_nhlur_198";
514
- const sheet = "_sheet_nhlur_202";
515
- const list = "_list_nhlur_206";
516
- const item = "_item_nhlur_215";
517
- const screenLink = "_screenLink_nhlur_223";
518
- const here = "_here_nhlur_224";
519
- const meta = "_meta_nhlur_235";
520
- const rows = "_rows_nhlur_243";
521
- const row = "_row_nhlur_243";
522
- const rowText = "_rowText_nhlur_260";
523
- const rowName = "_rowName_nhlur_267";
524
- const rowMeta = "_rowMeta_nhlur_273";
525
- const tipText = "_tipText_nhlur_279";
526
- const tipStep = "_tipStep_nhlur_283";
527
- const sheetFoot = "_sheetFoot_nhlur_287";
528
- const sheetSection = "_sheetSection_nhlur_291";
529
- const sectionTitle = "_sectionTitle_nhlur_303";
530
- const notice = "_notice_nhlur_309";
531
- const sheetPhone = "_sheetPhone_nhlur_322";
532
- const back = "_back_nhlur_326";
533
- const overlay = "_overlay_nhlur_358";
534
- const box = "_box_nhlur_365";
535
- const tint = "_tint_nhlur_379";
536
- const chip = "_chip_nhlur_385";
537
- const tip = "_tip_nhlur_279";
538
- const studio = "_studio_nhlur_481";
593
+ const anchor = "_anchor_a8mxc_5";
594
+ const panel = "_panel_a8mxc_30";
595
+ const pill = "_pill_a8mxc_31";
596
+ const grip = "_grip_a8mxc_71";
597
+ const title = "_title_a8mxc_81";
598
+ const brand = "_brand_a8mxc_90";
599
+ const name = "_name_a8mxc_98";
600
+ const pillTitle = "_pillTitle_a8mxc_103";
601
+ const actions = "_actions_a8mxc_110";
602
+ const sides = "_sides_a8mxc_119";
603
+ const nav = "_nav_a8mxc_124";
604
+ const countButton = "_countButton_a8mxc_132";
605
+ const steps = "_steps_a8mxc_137";
606
+ const collapse = "_collapse_a8mxc_141";
607
+ const split = "_split_a8mxc_164";
608
+ const subtle = "_subtle_a8mxc_180";
609
+ const noteRow = "_noteRow_a8mxc_194";
610
+ const note = "_note_a8mxc_194";
611
+ const noteAction = "_noteAction_a8mxc_216";
612
+ const sheet = "_sheet_a8mxc_220";
613
+ const list = "_list_a8mxc_224";
614
+ const item = "_item_a8mxc_233";
615
+ const screenLink = "_screenLink_a8mxc_241";
616
+ const here = "_here_a8mxc_242";
617
+ const meta = "_meta_a8mxc_253";
618
+ const rows = "_rows_a8mxc_261";
619
+ const row = "_row_a8mxc_261";
620
+ const rowText = "_rowText_a8mxc_278";
621
+ const rowName = "_rowName_a8mxc_285";
622
+ const rowMeta = "_rowMeta_a8mxc_291";
623
+ const tipText = "_tipText_a8mxc_297";
624
+ const tipStep = "_tipStep_a8mxc_301";
625
+ const sheetFoot = "_sheetFoot_a8mxc_305";
626
+ const sheetSection = "_sheetSection_a8mxc_309";
627
+ const sectionTitle = "_sectionTitle_a8mxc_321";
628
+ const notice = "_notice_a8mxc_327";
629
+ const sheetPhone = "_sheetPhone_a8mxc_340";
630
+ const back = "_back_a8mxc_344";
631
+ const overlay = "_overlay_a8mxc_376";
632
+ const box = "_box_a8mxc_383";
633
+ const tint = "_tint_a8mxc_397";
634
+ const chip = "_chip_a8mxc_403";
635
+ const scrim = "_scrim_a8mxc_458";
636
+ const scrimFill = "_scrimFill_a8mxc_465";
637
+ const tip = "_tip_a8mxc_297";
638
+ const studio = "_studio_a8mxc_514";
539
639
  const styles = {
540
640
  anchor,
541
641
  panel,
@@ -547,14 +647,12 @@ const styles = {
547
647
  pillTitle,
548
648
  actions,
549
649
  sides,
650
+ nav,
651
+ countButton,
652
+ steps,
550
653
  collapse,
551
- phoneOnly,
552
- desktopOnly,
553
- count,
554
- swatches,
555
654
  split,
556
655
  subtle,
557
- more,
558
656
  noteRow,
559
657
  note,
560
658
  noteAction,
@@ -581,10 +679,13 @@ const styles = {
581
679
  box,
582
680
  tint,
583
681
  chip,
682
+ scrim,
683
+ scrimFill,
584
684
  tip,
585
685
  studio
586
686
  };
587
687
  const INSET = 8;
688
+ const HOLE = 4;
588
689
  function boxOf(el) {
589
690
  const r = shownRect(el);
590
691
  if (!r) return null;
@@ -604,10 +705,13 @@ function ChangeOutlines({
604
705
  tip: tip2,
605
706
  onShown,
606
707
  emphasis,
607
- flash
708
+ flash,
709
+ isolate = false,
710
+ bar
608
711
  }) {
609
712
  const [tick, setTick] = useState(0);
610
713
  const [active, setActive] = useState(null);
714
+ const maskId = `bridge-isolate-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
611
715
  useEffect(() => {
612
716
  let frame = 0;
613
717
  const on = () => {
@@ -658,30 +762,61 @@ function ChangeOutlines({
658
762
  document.removeEventListener("focusin", focus);
659
763
  };
660
764
  }, [surfaces]);
661
- return /* @__PURE__ */ jsx(ThemeProvider, { theme, brand: "bridge", density: "compact", className: styles.overlay, "data-bridge-overlay": "", ...{ [PIN_ATTR]: "" }, "aria-hidden": "true", children: boxes.map((b, i) => {
662
- if (!b) return null;
663
- const s = surfaces[i];
664
- const showChip = flash || active === i || emphasis === i;
665
- const chip2 = /* @__PURE__ */ jsx("span", { className: styles.chip, "data-page": b.page || void 0, "data-shown": showChip || void 0, children: s.label });
666
- return /* @__PURE__ */ jsxs(
667
- "span",
668
- {
669
- className: styles.box,
670
- "data-page": b.page || void 0,
671
- "data-inside": !b.page && b.top < 24 || void 0,
672
- "data-emphasis": emphasis === i || void 0,
673
- style: { left: b.left, top: b.top, width: b.width, height: b.height, borderRadius: b.radius },
674
- children: [
675
- /* @__PURE__ */ jsx("span", { className: styles.tint }),
676
- active === i || emphasis === i ? (
677
- // Above the chip when there is room, else below it: never over the toolbar, which keeps clear of boxes.
678
- /* @__PURE__ */ jsx(Tooltip, { open: true, content: tip2(s), side: b.top < 64 ? "bottom" : "top", align: "start", className: PIN_CLASS, children: chip2 })
679
- ) : chip2
680
- ]
681
- },
682
- i
683
- );
684
- }) });
765
+ return /* @__PURE__ */ jsxs(ThemeProvider, { theme, brand: "bridge", density: "compact", className: styles.overlay, "data-bridge-overlay": "", "data-isolate": isolate || void 0, ...{ [PIN_ATTR]: "" }, "aria-hidden": "true", children: [
766
+ isolate ? /* @__PURE__ */ jsxs("svg", { className: styles.scrim, width: "100%", height: "100%", focusable: "false", children: [
767
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("mask", { id: maskId, children: [
768
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: "white" }),
769
+ boxes.map(
770
+ (b, i) => b && !b.page ? /* @__PURE__ */ jsx("rect", { x: b.left - HOLE, y: b.top - HOLE, width: b.width + 2 * HOLE, height: b.height + 2 * HOLE, rx: (parseFloat(b.radius) || 0) + HOLE, fill: "black" }, i) : null
771
+ )
772
+ ] }) }),
773
+ /* @__PURE__ */ jsx("rect", { className: styles.scrimFill, width: "100%", height: "100%", mask: `url(#${maskId})` })
774
+ ] }) : null,
775
+ boxes.map((b, i) => {
776
+ if (!b) return null;
777
+ const s = surfaces[i];
778
+ const showChip = flash || active === i || emphasis === i;
779
+ const chip2 = /* @__PURE__ */ jsx("span", { className: styles.chip, "data-page": b.page || void 0, "data-shown": showChip || void 0, children: s.label });
780
+ return /* @__PURE__ */ jsxs(
781
+ "span",
782
+ {
783
+ className: styles.box,
784
+ "data-page": b.page || void 0,
785
+ "data-inside": !b.page && b.top < 24 || void 0,
786
+ "data-emphasis": emphasis === i || void 0,
787
+ style: { left: b.left, top: b.top, width: b.width, height: b.height, borderRadius: b.radius },
788
+ children: [
789
+ (active === i || emphasis === i) && !b.page ? /* @__PURE__ */ jsx(
790
+ Tooltip,
791
+ {
792
+ open: true,
793
+ content: tip2(s),
794
+ side: tipSide({ left: b.left, top: b.top, right: b.left + b.width, bottom: b.top + b.height }, { width: window.innerWidth, height: window.innerHeight }, bar?.() ?? null),
795
+ align: "start",
796
+ className: PIN_CLASS,
797
+ children: /* @__PURE__ */ jsx("span", { className: styles.tint })
798
+ }
799
+ ) : /* @__PURE__ */ jsx("span", { className: styles.tint }),
800
+ (active === i || emphasis === i) && b.page ? /* @__PURE__ */ jsx(Tooltip, { open: true, content: tip2(s), side: "bottom", align: "start", className: PIN_CLASS, children: chip2 }) : chip2
801
+ ]
802
+ },
803
+ i
804
+ );
805
+ })
806
+ ] });
807
+ }
808
+ function ChangeNav({ countText, tip: tip2, outlinesOn, disabled, total, isolate, announce, onToggle, onStep, onIsolate }) {
809
+ return /* @__PURE__ */ jsxs("div", { className: styles.nav, role: "group", "aria-label": "What changed on this screen", children: [
810
+ /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: tip2, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "md", iconStart: ScanSearch, pressed: outlinesOn, "aria-keyshortcuts": "C", disabled, onClick: onToggle, className: styles.countButton, children: countText }) }),
811
+ total ? /* @__PURE__ */ jsxs(Fragment, { children: [
812
+ /* @__PURE__ */ jsxs(ButtonGroup, { appearance: "attached", "aria-label": "Step through the changed elements", className: styles.steps, children: [
813
+ /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: "Previous changed element", children: /* @__PURE__ */ jsx(IconButton, { icon: ChevronLeft, variant: "secondary", size: "md", label: "Previous changed element", onClick: () => onStep(-1) }) }),
814
+ /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: "Next changed element", children: /* @__PURE__ */ jsx(IconButton, { icon: ChevronRight, variant: "secondary", size: "md", label: "Next changed element", onClick: () => onStep(1) }) })
815
+ ] }),
816
+ /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: isolate ? "Show the whole page again. Press Esc." : "Dim everything but the changed elements. Esc turns it off.", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "md", pressed: isolate, "aria-keyshortcuts": isolate ? "Escape" : void 0, onClick: () => onIsolate(!isolate), children: "Isolate" }) })
817
+ ] }) : null,
818
+ /* @__PURE__ */ jsx(VisuallyHidden, { role: "status", children: announce })
819
+ ] });
685
820
  }
686
821
  function useSheet(css, applied) {
687
822
  const [el, setEl] = useState(null);
@@ -753,7 +888,6 @@ function useTogether(build, latest, changes, use, here2, applied) {
753
888
  };
754
889
  }
755
890
  const SIDE_LABEL = { before: "Live product", after: "This change" };
756
- const MAX_PAIRS = 3;
757
891
  function session() {
758
892
  try {
759
893
  return window.sessionStorage;
@@ -966,41 +1100,52 @@ function Toolbar({
966
1100
  };
967
1101
  }, [build, change, tg]);
968
1102
  const liveUrl = !tg && facts?.mode === "tokens" ? liveTokensUrl(build) : null;
969
- const [outlines, setOutlines] = useState(false);
1103
+ const [outlinePick, setOutlinePick] = useState(null);
1104
+ const outlines = outlinePick ?? open;
1105
+ const toggleOutlines = useCallback(() => setOutlinePick((p) => !(p ?? open)), [open]);
970
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";
971
1109
  const live = useLiveSheet(liveUrl, outlines && canOutline, side === "before" && facts?.mode === "tokens");
972
1110
  const onePairs = usePairs(tg ? null : facts, liveUrl, brand2, side === "before");
973
1111
  const pairs = tg ? T?.pairs ?? [] : onePairs;
974
1112
  const noDiff = tg ? T?.noDiff ?? null : facts?.mode === "tokens" ? noVisibleDifference(facts.files, { theme, brand: brand2 }) : null;
975
1113
  const outlinesOn = outlines && canOutline && !noDiff;
1114
+ const screenKey = `${hash}|${theme}|${tenantId}|${tg ? T?.included.join(",") ?? "" : ""}`;
976
1115
  const diff = useChangedElements(
977
1116
  outlinesOn,
978
1117
  tg ? T?.sheet ?? null : live.link,
979
1118
  (facts?.tokens ?? []).map((t) => t.cssVar),
980
- // Switching a change on or off repaints the combination: the outlines are measured again.
981
- `${hash}|${theme}|${tenantId}|${tg ? T?.included.join(",") ?? "" : ""}`,
1119
+ screenKey,
982
1120
  tg ? "after" : "before"
983
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;
984
1126
  const nameOf = (v) => facts?.tokens.find((t) => t.cssVar === v)?.name ?? v;
985
1127
  const [shown, setShown] = useState({ count: 0, obstacles: [] });
986
1128
  const setShownStable = useCallback((next) => setShown((p) => JSON.stringify(p) === JSON.stringify(next) ? p : next), []);
987
1129
  const outlineBoxes = useRef([]);
988
- outlineBoxes.current = outlinesOn ? shown.obstacles : [];
1130
+ outlineBoxes.current = showing ? shown.obstacles : [];
989
1131
  const [emphasis, setEmphasis] = useState(null);
990
1132
  const [flash, setFlash] = useState(false);
991
1133
  const [sheetOpen, setSheetOpen] = useState(false);
1134
+ const [position, setPosition] = useState(null);
1135
+ const [announce, setAnnounce] = useState("");
1136
+ const [isolate, setIsolate] = useState(false);
992
1137
  const phone = useMedia("(max-width: 760px)");
993
1138
  const reduced = useMedia("(prefers-reduced-motion: reduce)");
994
1139
  usePinnedBridgeTokens(liveTokensUrl(build), theme);
995
1140
  useEffect(() => {
996
- if (!outlinesOn || reduced) {
1141
+ if (!showing || reduced) {
997
1142
  setFlash(false);
998
1143
  return;
999
1144
  }
1000
1145
  setFlash(true);
1001
1146
  const id = window.setTimeout(() => setFlash(false), 1500);
1002
1147
  return () => window.clearTimeout(id);
1003
- }, [outlinesOn, reduced]);
1148
+ }, [showing, reduced]);
1004
1149
  useEffect(() => {
1005
1150
  if (!sheetOpen) setEmphasis(null);
1006
1151
  }, [sheetOpen]);
@@ -1016,6 +1161,13 @@ function Toolbar({
1016
1161
  useEffect(() => {
1017
1162
  if (shownIn && theme !== shownIn) setShownIn(null);
1018
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
+ }, []);
1019
1171
  useEffect(() => {
1020
1172
  const y = readScroll(window.location.search);
1021
1173
  if (y === null) return;
@@ -1041,18 +1193,18 @@ function Toolbar({
1041
1193
  }
1042
1194
  const here2 = { search: window.location.search, hash: window.location.hash };
1043
1195
  if (next === "before" && build.production) {
1044
- 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 }));
1045
1197
  } else if (next === "after" && returnTo) {
1046
1198
  window.location.assign(sameScreenOn(returnTo, here2, { change, scroll: window.scrollY }));
1047
1199
  }
1048
1200
  },
1049
- [side, facts, build, change, returnTo]
1201
+ [side, facts, build, change, returnTo, marks]
1050
1202
  );
1051
1203
  useEffect(() => {
1052
1204
  const onKey = (e) => {
1053
- if (canOutline && !noDiff && isKey(e, "c")) {
1205
+ if ((canOutline && !noDiff || canMark && marks?.length) && isKey(e, "c")) {
1054
1206
  e.preventDefault();
1055
- setOutlines((o) => !o);
1207
+ toggleOutlines();
1056
1208
  return;
1057
1209
  }
1058
1210
  if (!isFlipKey(e)) return;
@@ -1061,7 +1213,15 @@ function Toolbar({
1061
1213
  };
1062
1214
  window.addEventListener("keydown", onKey);
1063
1215
  return () => window.removeEventListener("keydown", onKey);
1064
- }, [choose, side, canOutline, noDiff]);
1216
+ }, [choose, side, canOutline, canMark, marks, noDiff, toggleOutlines]);
1217
+ useEffect(() => {
1218
+ if (!isolate) return;
1219
+ const onEscape = (e) => {
1220
+ if (isEscape(e)) setIsolate(false);
1221
+ };
1222
+ window.addEventListener("keydown", onEscape, true);
1223
+ return () => window.removeEventListener("keydown", onEscape, true);
1224
+ }, [isolate]);
1065
1225
  useLayoutEffect(() => {
1066
1226
  const el = root.current;
1067
1227
  const anchor2 = el?.parentElement;
@@ -1095,7 +1255,7 @@ function Toolbar({
1095
1255
  window.removeEventListener("hashchange", measure);
1096
1256
  document.body.style.paddingBottom = "";
1097
1257
  };
1098
- }, [dock, open, facts, shown, outlinesOn]);
1258
+ }, [dock, open, facts, shown, showing]);
1099
1259
  const toggleOpen = (next) => {
1100
1260
  setOpen(next);
1101
1261
  remember(OPEN_KEY, next ? "1" : "0");
@@ -1148,8 +1308,39 @@ function Toolbar({
1148
1308
  const behind = facts?.behind ? "Live has new changes since this change started." : null;
1149
1309
  const shared = screens?.length && screens.every((s) => s.tenant === screens[0].tenant && s.theme === screens[0].theme) ? screens[0] : null;
1150
1310
  const shopOf = (t) => shops.find((x) => x.id === t)?.name ?? t;
1151
- const surfaces = outlinesOn ? diff?.surfaces ?? [] : [];
1152
- const shownCount = outlinesOn && diff ? shown.count : null;
1311
+ const surfaces = useMemo(() => found ?? [], [found]);
1312
+ const shownCount = showing && found ? shown.count : null;
1313
+ const order = useMemo(() => {
1314
+ const on = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => rendered(s.el));
1315
+ const places = on.map(({ s }) => {
1316
+ if (isPage(s.el)) return { top: -1, left: 0 };
1317
+ const r = s.el.getBoundingClientRect();
1318
+ return { top: r.top + window.scrollY, left: r.left + window.scrollX };
1319
+ });
1320
+ return readingOrder(places).map((k) => on[k].i);
1321
+ }, [surfaces]);
1322
+ const total = showing && found ? order.length : null;
1323
+ const current = position !== null ? order[position] ?? null : null;
1324
+ useEffect(() => {
1325
+ setPosition(null);
1326
+ setAnnounce("");
1327
+ }, [screenKey, showing]);
1328
+ useEffect(() => {
1329
+ if (position !== null && position >= order.length) setPosition(null);
1330
+ }, [order.length, position]);
1331
+ const isolateOn = isolate && Boolean(total);
1332
+ const step = (delta) => {
1333
+ const next = stepTo(position, delta, order.length);
1334
+ if (next === null) return;
1335
+ const s = surfaces[order[next]];
1336
+ setPosition(next);
1337
+ setEmphasis(null);
1338
+ if (!isPage(s.el)) s.el.scrollIntoView({ block: "center", inline: "nearest", behavior: reduced ? "auto" : "smooth" });
1339
+ setAnnounce(stepAnnouncement(next, order.length, s.label));
1340
+ };
1341
+ const isHere = (sc) => sc.route === hash && sc.tenant === tenantId && sc.theme === theme;
1342
+ const elsewhere = (screens ?? []).filter((sc) => !isHere(sc));
1343
+ const screenHref = (sc) => `?${PARAM}=${change}&tenant=${encodeURIComponent(sc.tenant)}&theme=${encodeURIComponent(sc.theme)}#${sc.route}`;
1153
1344
  const activeHere = new Set((T?.pairs ?? []).flatMap((p) => p.token.from)).size;
1154
1345
  const note2 = shownIn ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1155
1346
  "Shown in ",
@@ -1162,7 +1353,17 @@ function Toolbar({
1162
1353
  ] }) : noDiff ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1163
1354
  /* @__PURE__ */ jsx("span", { children: noDifferenceText(noDiff, shops) }),
1164
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)}` })
1165
- ] }) : (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." }) : shownCount === 0 ? /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: diff?.surfaces.length ? outOfView(diff.surfaces.length) : tg ? TOGETHER_EMPTY_LINE : EMPTY_LINE }) : tg && T && side === "after" && activeHere < T.included.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: [
1357
+ /* @__PURE__ */ jsx("span", { children: elsewhereLine(elsewhere.map((sc) => sc.label)) }),
1358
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: screenHref(elsewhere[0]) }), onClick: goTo(elsewhere[0]), className: styles.noteAction, children: [
1359
+ "Go to ",
1360
+ elsewhere[0].label
1361
+ ] })
1362
+ ] }) : /* @__PURE__ */ jsx("p", { className: styles.note, role: "status", children: screens ? elsewhereLine([]) : EMPTY_LINE }) : total && shownCount === 0 && position === null ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1363
+ "Nothing changed in view. Next takes you to ",
1364
+ total === 1 ? "the changed element" : `the ${total} changed elements`,
1365
+ "."
1366
+ ] }) : tg && T && side === "after" && activeHere < T.included.length ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1166
1367
  activeHere,
1167
1368
  " of these ",
1168
1369
  T.included.length,
@@ -1178,62 +1379,86 @@ function Toolbar({
1178
1379
  const from = p?.token?.from;
1179
1380
  return tg && from?.length ? fromText(from, titleOf) : null;
1180
1381
  };
1181
- const pairTip = (p) => `${p.token.name}${p.token.step ? ` (${p.token.step})` : ""}${fromOf(p) ? `, ${fromOf(p)}` : ""}: ${p.color ? hexPair(p.before, p.after) : `${p.before} → ${p.after}`}`;
1182
1382
  const splitOf = (before, after) => /* @__PURE__ */ jsxs("span", { className: styles.split, "aria-hidden": "true", children: [
1183
1383
  /* @__PURE__ */ jsx("span", { style: { backgroundColor: before } }),
1184
1384
  /* @__PURE__ */ jsx("span", { style: { backgroundColor: after } })
1185
1385
  ] });
1186
1386
  const subtleOf = (before, after) => isSubtle(before, after) ? /* @__PURE__ */ jsx("span", { className: styles.subtle, children: "Subtle change" }) : null;
1187
- const shownPairs = visiblePairs.filter((p) => p.color).slice(0, MAX_PAIRS);
1188
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 }));
1189
- const swatches2 = visiblePairs.length ? /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: visiblePairs.slice(0, 6).map(pairTip).join(". ") + (visiblePairs.length > 6 ? `. And ${visiblePairs.length - 6} more.` : ""), children: /* @__PURE__ */ jsxs("span", { className: styles.swatches, tabIndex: 0, "aria-label": `Tokens this change edits: ${visiblePairs.map(pairTip).join(". ")}`, children: [
1190
- shownPairs.map((p) => /* @__PURE__ */ jsx("span", { children: splitOf(p.before, p.after) }, p.token.path)),
1191
- visiblePairs.length > shownPairs.length && /* @__PURE__ */ jsxs("span", { className: styles.more, children: [
1192
- "+",
1193
- visiblePairs.length - shownPairs.length,
1194
- " more ",
1195
- visiblePairs.length - shownPairs.length === 1 ? "token" : "tokens"
1196
- ] }),
1197
- shownPairs[0] ? subtleOf(shownPairs[0].before, shownPairs[0].after) : null
1198
- ] }) }) : null;
1199
1388
  const tokenNames = (facts?.tokens ?? []).map((t) => t.name);
1200
- const summary = shownCount === null ? null : surfaceLine(diff.surfaces.flatMap((s) => s.roots.map(nameOf)), shownCount);
1201
- const toggleTip = `${summary ?? `Outline what ${tokenNames.length === 1 ? tokenNames[0] : "this change"} changes on this screen.`} Press C.`;
1202
- const toggleLabel = shownCount === null ? "Show what changed" : `Show what changed · ${shownCount}`;
1203
- const outlineToggle = canOutline ? /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: toggleTip, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "md", pressed: outlinesOn, "aria-keyshortcuts": "C", disabled: Boolean(noDiff), onClick: () => setOutlines((o) => !o), children: toggleLabel }) }) : null;
1204
- const outlineIcon = canOutline ? /* @__PURE__ */ jsx(
1205
- IconButton,
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(
1394
+ ChangeNav,
1206
1395
  {
1207
- icon: ScanSearch,
1208
- size: "md",
1209
- variant: "ghost",
1210
- pressed: outlinesOn,
1211
- disabled: Boolean(noDiff),
1212
- label: shownCount === null ? "Show what changed" : `Show what changed, ${shownCount} ${shownCount === 1 ? "surface" : "surfaces"}`,
1213
- className: styles.phoneOnly,
1214
- onClick: () => setOutlines((o) => !o)
1396
+ countText,
1397
+ tip: toggleTip,
1398
+ outlinesOn: showing,
1399
+ disabled: canOutline && Boolean(noDiff),
1400
+ total,
1401
+ isolate: isolateOn,
1402
+ announce,
1403
+ onToggle: toggleOutlines,
1404
+ onStep: step,
1405
+ onIsolate: setIsolate
1215
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) }) }) })
1216
1416
  ) : null;
1217
1417
  const shownSurfaces = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => shownRect(s.el));
1218
1418
  const focusSurface = (i) => {
1219
1419
  const el = surfaces[i]?.el;
1220
1420
  if (!el) return;
1221
- el.scrollIntoView({ block: "center", behavior: reduced ? "auto" : "smooth" });
1421
+ if (!isPage(el)) el.scrollIntoView({ block: "center", behavior: reduced ? "auto" : "smooth" });
1222
1422
  setEmphasis(i);
1423
+ if (order.includes(i)) setPosition(order.indexOf(i));
1223
1424
  };
1224
1425
  const tokensOf = (x) => [...new Set(x.roots.map(nameOf))].join(", ");
1225
- const sides2 = /* @__PURE__ */ jsx(ButtonGroup, { appearance: "attached", "aria-label": "Compare", className: styles.sides, children: ["before", "after"].map((s) => /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: `${sideLabel(s)}: ${s === "before" ? "what customers see today." : modeDetail(facts)} Press B to flip.`, children: /* @__PURE__ */ jsx(
1226
- Button,
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.";
1435
+ const sides2 = /* @__PURE__ */ jsx(ButtonGroup, { appearance: "attached", "aria-label": "Compare", className: styles.sides, children: ["before", "after"].map((s) => /* @__PURE__ */ jsx(
1436
+ Tooltip,
1227
1437
  {
1228
- variant: "secondary",
1229
- size: "md",
1230
- pressed: side === s,
1231
- "aria-keyshortcuts": "B",
1232
- disabled: !canFlip && side !== s,
1233
- onClick: () => choose(s),
1234
- children: sideLabel(s)
1235
- }
1236
- ) }, s)) });
1438
+ className: PIN_CLASS,
1439
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
1440
+ sideLabel(s),
1441
+ ": ",
1442
+ s === "before" ? "what customers see today." : modeDetail(facts),
1443
+ " ",
1444
+ /* @__PURE__ */ jsx(Kbd, { keys: "B", size: "sm" }),
1445
+ " flips sides on the same screen."
1446
+ ] }),
1447
+ children: /* @__PURE__ */ jsx(
1448
+ Button,
1449
+ {
1450
+ variant: "secondary",
1451
+ size: "md",
1452
+ pressed: side === s,
1453
+ "aria-keyshortcuts": "B",
1454
+ disabled: !canFlip && side !== s,
1455
+ onClick: () => choose(s),
1456
+ children: sideLabel(s)
1457
+ }
1458
+ )
1459
+ },
1460
+ s
1461
+ )) });
1237
1462
  return /* @__PURE__ */ jsxs(
1238
1463
  ThemeProvider,
1239
1464
  {
@@ -1247,6 +1472,45 @@ function Toolbar({
1247
1472
  "data-dock": place.dock,
1248
1473
  style: place.lift ? { paddingBottom: `${place.lift}px` } : void 0,
1249
1474
  children: [
1475
+ showing && surfaces.length ? /* @__PURE__ */ jsx(
1476
+ ChangeOutlines,
1477
+ {
1478
+ surfaces,
1479
+ theme,
1480
+ emphasis: emphasis ?? current,
1481
+ flash,
1482
+ isolate: isolateOn,
1483
+ bar: () => root.current?.getBoundingClientRect() ?? null,
1484
+ onShown: setShownStable,
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
+ }
1492
+ const prop = s.props.find((p) => /^rgb/.test(p.before) && /^rgb/.test(p.after));
1493
+ const token = prop ? visiblePairs.find((p) => p.color && toHex(p.after) === toHex(prop.after) && toHex(p.before) === toHex(prop.before)) : null;
1494
+ const name2 = token?.token.name ?? [...new Set(s.roots.map(nameOf))].slice(0, 2).join(", ");
1495
+ const rootsFrom = [...new Set(s.roots.flatMap((r) => facts?.tokens.find((t) => t.cssVar === r)?.from ?? []))];
1496
+ const fromLine = fromOf(token) ?? (tg && rootsFrom.length ? fromText(rootsFrom, titleOf) : null);
1497
+ return /* @__PURE__ */ jsxs("span", { className: styles.tip, children: [
1498
+ prop ? splitOf(prop.before, prop.after) : null,
1499
+ /* @__PURE__ */ jsxs("span", { className: styles.tipText, children: [
1500
+ /* @__PURE__ */ jsxs("span", { children: [
1501
+ s.label,
1502
+ ": ",
1503
+ name2,
1504
+ prop ? `, ${hexPair(prop.before, prop.after)}` : ""
1505
+ ] }),
1506
+ token?.token.step ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: token.token.step }) : null,
1507
+ fromLine ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: fromLine.charAt(0).toUpperCase() + fromLine.slice(1) }) : null
1508
+ ] }),
1509
+ prop ? subtleOf(prop.before, prop.after) : null
1510
+ ] });
1511
+ }
1512
+ }
1513
+ ) : null,
1250
1514
  /* @__PURE__ */ jsxs(
1251
1515
  "div",
1252
1516
  {
@@ -1256,7 +1520,9 @@ function Toolbar({
1256
1520
  "aria-label": `Bridge: ${label}, ${title2}`,
1257
1521
  "data-side": side,
1258
1522
  "data-diff-ms": outlinesOn ? diff?.ms : void 0,
1523
+ "data-marks": canMark ? marks?.length ?? void 0 : void 0,
1259
1524
  "data-diff-count": shownCount ?? void 0,
1525
+ "data-diff-total": total ?? void 0,
1260
1526
  "data-diff-elements": outlinesOn ? diff?.elements.length : void 0,
1261
1527
  "data-note": note2 ? "" : void 0,
1262
1528
  style: drag ? { transform: `translateX(${drag}px)` } : void 0,
@@ -1281,19 +1547,15 @@ function Toolbar({
1281
1547
  /* @__PURE__ */ jsx("span", { className: styles.brand, children: "Bridge" }),
1282
1548
  /* @__PURE__ */ jsx("span", { className: styles.name, title: title2, children: title2 })
1283
1549
  ] }),
1284
- outlineIcon,
1550
+ sides2,
1551
+ nav2,
1285
1552
  /* @__PURE__ */ jsxs("div", { className: styles.actions, children: [
1286
- sides2,
1287
- /* @__PURE__ */ jsx("span", { className: styles.desktopOnly, children: swatches2 }),
1288
1553
  /* @__PURE__ */ jsxs(Popover, { open: sheetOpen, onOpenChange: setSheetOpen, children: [
1289
1554
  /* @__PURE__ */ jsx(
1290
1555
  PopoverTrigger,
1291
1556
  {
1292
- render: phone ? /* @__PURE__ */ jsx(IconButton, { icon: ListChecks, size: "md", variant: "ghost", label: shownCount ? `Changes, ${shownCount} changed here` : "Changes" }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", iconEnd: ChevronUp }),
1293
- children: phone ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
1294
- "Changes",
1295
- shownCount ? /* @__PURE__ */ jsx("span", { className: styles.count, children: shownCount }) : null
1296
- ] })
1557
+ render: phone ? /* @__PURE__ */ jsx(IconButton, { icon: ListChecks, size: "md", variant: "ghost", label: "Changes, screens and tokens" }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", iconEnd: ChevronUp }),
1558
+ children: phone ? null : /* @__PURE__ */ jsx(Fragment, { children: "Changes" })
1297
1559
  }
1298
1560
  ),
1299
1561
  /* @__PURE__ */ jsxs(PopoverContent, { side: "top", align: "center", className: `${styles.sheet} ${PIN_CLASS}`, "aria-label": "Changes", children: [
@@ -1326,6 +1588,28 @@ function Toolbar({
1326
1588
  "."
1327
1589
  ] }) : null
1328
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,
1329
1613
  outlinesOn && shownSurfaces.length ? /* @__PURE__ */ jsxs("section", { className: styles.sheetSection, "aria-label": `Changed here, ${shownSurfaces.length}`, children: [
1330
1614
  /* @__PURE__ */ jsxs("p", { className: styles.sectionTitle, children: [
1331
1615
  "Changed here · ",
@@ -1359,15 +1643,15 @@ function Toolbar({
1359
1643
  shared.theme
1360
1644
  ] }) : null,
1361
1645
  /* @__PURE__ */ jsx("ul", { className: styles.list, children: screens.map((sc) => {
1362
- const current = sc.route === hash && sc.tenant === tenantId && sc.theme === theme;
1646
+ const current2 = isHere(sc);
1363
1647
  const where = shared ? null : /* @__PURE__ */ jsxs("span", { className: styles.meta, children: [
1364
1648
  " ",
1365
1649
  shopOf(sc.tenant),
1366
1650
  ", ",
1367
1651
  sc.theme
1368
1652
  ] });
1369
- return /* @__PURE__ */ jsxs("li", { className: styles.item, "aria-current": current ? "page" : void 0, children: [
1370
- current ? /* @__PURE__ */ jsxs("span", { className: styles.here, children: [
1653
+ return /* @__PURE__ */ jsxs("li", { className: styles.item, "aria-current": current2 ? "page" : void 0, children: [
1654
+ current2 ? /* @__PURE__ */ jsxs("span", { className: styles.here, children: [
1371
1655
  /* @__PURE__ */ jsx(Icon, { icon: Check, size: "sm" }),
1372
1656
  sc.label,
1373
1657
  where
@@ -1375,7 +1659,7 @@ function Toolbar({
1375
1659
  Link,
1376
1660
  {
1377
1661
  variant: "standalone",
1378
- href: `?${PARAM}=${change}&tenant=${encodeURIComponent(sc.tenant)}&theme=${encodeURIComponent(sc.theme)}#${sc.route}`,
1662
+ href: screenHref(sc),
1379
1663
  onClick: goTo(sc),
1380
1664
  className: styles.screenLink,
1381
1665
  children: [
@@ -1384,17 +1668,17 @@ function Toolbar({
1384
1668
  ]
1385
1669
  }
1386
1670
  ),
1387
- /* @__PURE__ */ jsx("span", { className: styles.meta, children: current ? "You are here" : `${Math.max(1, Math.round(sc.percent))}% changed` })
1671
+ /* @__PURE__ */ jsx("span", { className: styles.meta, children: current2 ? "You are here" : `${Math.max(1, Math.round(sc.percent))}% changed` })
1388
1672
  ] }, `${sc.route}|${sc.tenant}|${sc.theme}`);
1389
1673
  }) })
1390
1674
  ] }) : null,
1391
- visiblePairs.length ? /* @__PURE__ */ jsxs("section", { className: `${styles.sheetSection} ${styles.sheetPhone}`, "aria-label": "Tokens this change edits", children: [
1675
+ visiblePairs.length ? /* @__PURE__ */ jsxs("section", { className: styles.sheetSection, "aria-label": "Tokens this change edits", children: [
1392
1676
  /* @__PURE__ */ jsx("p", { className: styles.sectionTitle, children: "Tokens" }),
1393
1677
  /* @__PURE__ */ jsx("ul", { className: styles.list, children: roleGroups.map((g) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
1394
1678
  /* @__PURE__ */ jsxs("span", { className: styles.rowText, children: [
1395
1679
  /* @__PURE__ */ jsx("span", { children: g.name }),
1396
1680
  /* @__PURE__ */ jsxs("span", { className: styles.rowMeta, children: [
1397
- g.pairs.length === 1 ? g.pairs[0].token.step ?? "" : `${g.pairs.length} steps`,
1681
+ g.pairs.length === 1 ? [g.pairs[0].token.step, g.pairs[0].color ? hexPair(g.pairs[0].before, g.pairs[0].after) : `${g.pairs[0].before} → ${g.pairs[0].after}`].filter(Boolean).join(", ") : `${g.pairs.length} steps`,
1398
1682
  isSubtle(g.pairs[0].before, g.pairs[0].after) ? ", subtle change" : ""
1399
1683
  ] })
1400
1684
  ] }),
@@ -1406,10 +1690,9 @@ function Toolbar({
1406
1690
  behind
1407
1691
  ] }) : null,
1408
1692
  /* @__PURE__ */ jsx("div", { className: styles.sheetPhone, children: /* @__PURE__ */ jsx(Link, { variant: "standalone", href: studio2, className: styles.back, children: "Back to Studio" }) }),
1409
- 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
1410
1694
  ] })
1411
1695
  ] }),
1412
- /* @__PURE__ */ jsx("span", { className: styles.desktopOnly, children: outlineToggle }),
1413
1696
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: studio2 }), className: styles.studio, children: "Back to Studio" })
1414
1697
  ] }),
1415
1698
  /* @__PURE__ */ jsx(Tooltip, { className: PIN_CLASS, content: "Collapse", children: /* @__PURE__ */ jsx(IconButton, { icon: Minimize2, size: "md", label: "Collapse the Bridge toolbar", className: styles.collapse, onClick: () => toggleOpen(false) }) }),
@@ -1423,38 +1706,7 @@ function Toolbar({
1423
1706
  note2 && /* @__PURE__ */ jsx("div", { className: styles.noteRow, children: note2 })
1424
1707
  ]
1425
1708
  }
1426
- ),
1427
- outlinesOn && surfaces.length ? /* @__PURE__ */ jsx(
1428
- ChangeOutlines,
1429
- {
1430
- surfaces,
1431
- theme,
1432
- emphasis,
1433
- flash,
1434
- onShown: setShownStable,
1435
- tip: (s) => {
1436
- const prop = s.props.find((p) => /^rgb/.test(p.before) && /^rgb/.test(p.after));
1437
- const token = prop ? visiblePairs.find((p) => p.color && toHex(p.after) === toHex(prop.after) && toHex(p.before) === toHex(prop.before)) : null;
1438
- const name2 = token?.token.name ?? [...new Set(s.roots.map(nameOf))].slice(0, 2).join(", ");
1439
- const rootsFrom = [...new Set(s.roots.flatMap((r) => facts?.tokens.find((t) => t.cssVar === r)?.from ?? []))];
1440
- const fromLine = fromOf(token) ?? (tg && rootsFrom.length ? fromText(rootsFrom, titleOf) : null);
1441
- return /* @__PURE__ */ jsxs("span", { className: styles.tip, children: [
1442
- prop ? splitOf(prop.before, prop.after) : null,
1443
- /* @__PURE__ */ jsxs("span", { className: styles.tipText, children: [
1444
- /* @__PURE__ */ jsxs("span", { children: [
1445
- s.label,
1446
- ": ",
1447
- name2,
1448
- prop ? `, ${hexPair(prop.before, prop.after)}` : ""
1449
- ] }),
1450
- token?.token.step ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: token.token.step }) : null,
1451
- fromLine ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: fromLine.charAt(0).toUpperCase() + fromLine.slice(1) }) : null
1452
- ] }),
1453
- prop ? subtleOf(prop.before, prop.after) : null
1454
- ] });
1455
- }
1456
- }
1457
- ) : null
1709
+ )
1458
1710
  ]
1459
1711
  }
1460
1712
  );
@@ -1483,4 +1735,4 @@ function useHash() {
1483
1735
  export {
1484
1736
  ToolbarRoot as default
1485
1737
  };
1486
- //# sourceMappingURL=Toolbar-CzR1TM9x.js.map
1738
+ //# sourceMappingURL=Toolbar-ByLHemKF.js.map