@bridge-ds/toolbar 0.1.0 → 0.3.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,16 +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 { 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";
12
- import { l as loadBuild, u as useDocumentScope } from "./index-yvFRSnsp.js";
13
- const API = "https://api.github.com";
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-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";
14
13
  const VISUAL_BRANCH = "bridge-visual";
15
14
  const cacheKey = (n) => `bridge-admin:facts:v3:${n}`;
16
15
  function session$1() {
@@ -20,34 +19,31 @@ function session$1() {
20
19
  return null;
21
20
  }
22
21
  }
23
- async function json(url) {
24
- const r = await fetch(url, { headers: url.startsWith(API) ? { Accept: "application/vnd.github+json" } : {} });
25
- if (!r.ok) throw new Error(r.status === 403 || r.status === 429 ? "GitHub limits reads without an account to 60 an hour. Try again later." : `GitHub answered ${r.status}.`);
26
- return await r.json();
22
+ function readsOf(build) {
23
+ return githubReads({ team: build.ds ?? `${build.owner}/${build.repo}`, proxy: build.proxy === void 0 ? DEFAULT_PROXY : build.proxy });
27
24
  }
28
- async function readManifest(R, raw, n, sha) {
29
- const root = await json(`${R}/git/trees/${VISUAL_BRANCH}`).catch(() => null);
25
+ async function readManifest(gh, build, R, n, sha) {
26
+ const root = await gh.api(`${R}/git/trees/${VISUAL_BRANCH}`).catch(() => null);
30
27
  const pr = root?.tree.find((e) => e.path === `pr-${n}` && e.type === "tree");
31
28
  if (!pr) return null;
32
- const commits = await json(`${R}/git/trees/${pr.sha}`).catch(() => null);
29
+ const commits = await gh.api(`${R}/git/trees/${pr.sha}`).catch(() => null);
33
30
  if (!commits?.tree.some((e) => e.path === sha && e.type === "tree")) return null;
34
- return json(`${raw}/${VISUAL_BRANCH}/pr-${n}/${sha}/manifest.json`).catch(() => null);
31
+ return gh.file(build.owner, build.repo, VISUAL_BRANCH, `pr-${n}/${sha}/manifest.json`).then((t) => JSON.parse(t)).catch(() => null);
35
32
  }
36
- async function loadChange(build, n) {
33
+ async function loadChange(build, n, gh = readsOf(build)) {
37
34
  const store = session$1();
38
35
  try {
39
36
  const cached = store?.getItem(cacheKey(n));
40
37
  if (cached) return JSON.parse(cached);
41
38
  } catch {
42
39
  }
43
- const R = `${API}/repos/${build.owner}/${build.repo}`;
44
- const raw = `https://raw.githubusercontent.com/${build.owner}/${build.repo}`;
40
+ const R = `/repos/${build.owner}/${build.repo}`;
45
41
  const [pull, files] = await Promise.all([
46
- json(`${R}/pulls/${n}`),
47
- json(`${R}/pulls/${n}/files?per_page=100`)
42
+ gh.api(`${R}/pulls/${n}`),
43
+ gh.api(`${R}/pulls/${n}/files?per_page=100`)
48
44
  ]);
49
45
  const tokenFiles = files.filter((f) => f.filename.endsWith(".tokens.json")).slice(0, 8);
50
- const version = (sha, path) => json(`${raw}/${sha}/${path}`).catch(() => null);
46
+ const version = (sha, path) => gh.file(build.owner, build.repo, sha, path).then((t) => JSON.parse(t)).catch(() => null);
51
47
  const tokenReads = Promise.all(
52
48
  tokenFiles.map(
53
49
  async (f) => tokenChanges(
@@ -58,8 +54,8 @@ async function loadChange(build, n) {
58
54
  )
59
55
  );
60
56
  const [compare, manifest, tokens] = await Promise.all([
61
- json(`${R}/compare/${encodeURIComponent(pull.base.ref)}...${pull.head.sha}`).catch(() => null),
62
- readManifest(R, raw, n, pull.head.sha),
57
+ gh.api(`${R}/compare/${encodeURIComponent(pull.base.ref)}...${pull.head.sha}`).catch(() => null),
58
+ readManifest(gh, build, R, n, pull.head.sha),
63
59
  tokenReads
64
60
  ]);
65
61
  const names = files.map((f) => f.filename);
@@ -81,17 +77,16 @@ async function loadChange(build, n) {
81
77
  return facts;
82
78
  }
83
79
  const togetherKey = (n) => `bridge-admin:together:v1:${n}`;
84
- async function loadTogetherChange(build, n) {
80
+ async function loadTogetherChange(build, n, gh = readsOf(build)) {
85
81
  const store = session$1();
86
82
  try {
87
83
  const cached = store?.getItem(togetherKey(n));
88
84
  if (cached) return JSON.parse(cached);
89
85
  } catch {
90
86
  }
91
- const R = `${API}/repos/${build.owner}/${build.repo}`;
92
- const raw = `https://raw.githubusercontent.com/${build.owner}/${build.repo}`;
93
- const pull = await json(`${R}/pulls/${n}`);
94
- const compare = await json(
87
+ const R = `/repos/${build.owner}/${build.repo}`;
88
+ const pull = await gh.api(`${R}/pulls/${n}`);
89
+ const compare = await gh.api(
95
90
  `${R}/compare/${encodeURIComponent(build.defaultBranch ?? pull.base.ref)}...${pull.head.sha}`
96
91
  );
97
92
  const files = compare.files ?? [];
@@ -101,7 +96,7 @@ async function loadTogetherChange(build, n) {
101
96
  const start = compare.merge_base_commit?.sha;
102
97
  let source = null;
103
98
  if (tokenOnly && start) {
104
- const text = (sha, path) => fetch(`${raw}/${sha}/${path.split("/").map(encodeURIComponent).join("/")}`).then((r) => r.ok ? r.text() : Promise.reject(new Error(`GitHub answered ${r.status}.`)));
99
+ const text = (sha, path) => gh.file(build.owner, build.repo, sha, path);
105
100
  const mine = files.filter((f) => f.filename.startsWith(root) && f.filename.endsWith(".tokens.json"));
106
101
  const src = { number: n, base: {}, head: {}, files: mine.map((f) => f.filename.slice(root.length)) };
107
102
  await Promise.all(
@@ -237,9 +232,6 @@ function surfaceLine(tokenNames, surfaces) {
237
232
  const tokens = `${names.length} ${names.length === 1 ? "token" : "tokens"} changed: ${names.slice(0, 3).join(", ")}${names.length > 3 ? ` and ${names.length - 3} more` : ""}`;
238
233
  return `${tokens}, on ${surfaces} ${surfaces === 1 ? "surface" : "surfaces"}`;
239
234
  }
240
- function outOfView(n) {
241
- 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.`;
242
- }
243
235
  const EMPTY_LINE = "This change doesn't touch this screen. Open Screens to see where it does.";
244
236
  const LANDMARKS = { main: "Content", header: "Header", nav: "Navigation", aside: "Sidebar", footer: "Footer", form: "Form", dialog: "Dialog" };
245
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" };
@@ -320,6 +312,13 @@ function shownRect(el) {
320
312
  return w > 0 && h > 0 ? r : null;
321
313
  }
322
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;
323
322
  function changedElements(changedVars, sheet2, ignore, sheetIs = "before") {
324
323
  const live = sheet2;
325
324
  const t0 = performance.now();
@@ -425,6 +424,43 @@ function changedElements(changedVars, sheet2, ignore, sheetIs = "before") {
425
424
  const surfaces = outermost([...plain, ...charts.values()], (a, b) => a.el !== b.el && a.el.contains(b.el));
426
425
  return { elements, surfaces, ms: Math.round((performance.now() - t0) * 10) / 10 };
427
426
  }
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
+ }
428
464
  const IGNORE = "[data-bridge-toolbar], [data-bridge-overlay]";
429
465
  function useChangedElements(on, live, vars, screen, sheetIs = "before") {
430
466
  const [result, setResult] = useState(null);
@@ -495,52 +531,52 @@ function usePinnedBridgeTokens(url, theme) {
495
531
  };
496
532
  }, [url, theme]);
497
533
  }
498
- const anchor = "_anchor_nhlur_5";
499
- const panel = "_panel_nhlur_30";
500
- const pill = "_pill_nhlur_31";
501
- const grip = "_grip_nhlur_63";
502
- const title = "_title_nhlur_73";
503
- const brand = "_brand_nhlur_82";
504
- const name = "_name_nhlur_90";
505
- const pillTitle = "_pillTitle_nhlur_95";
506
- const actions = "_actions_nhlur_102";
507
- const sides = "_sides_nhlur_111";
508
- const collapse = "_collapse_nhlur_115";
509
- const phoneOnly = "_phoneOnly_nhlur_119";
510
- const desktopOnly = "_desktopOnly_nhlur_123";
511
- const count = "_count_nhlur_127";
512
- const swatches = "_swatches_nhlur_134";
513
- const split = "_split_nhlur_146";
514
- const subtle = "_subtle_nhlur_162";
515
- const more = "_more_nhlur_169";
516
- const noteRow = "_noteRow_nhlur_176";
517
- const note = "_note_nhlur_176";
518
- const noteAction = "_noteAction_nhlur_198";
519
- const sheet = "_sheet_nhlur_202";
520
- const list = "_list_nhlur_206";
521
- const item = "_item_nhlur_215";
522
- const screenLink = "_screenLink_nhlur_223";
523
- const here = "_here_nhlur_224";
524
- const meta = "_meta_nhlur_235";
525
- const rows = "_rows_nhlur_243";
526
- const row = "_row_nhlur_243";
527
- const rowText = "_rowText_nhlur_260";
528
- const rowName = "_rowName_nhlur_267";
529
- const rowMeta = "_rowMeta_nhlur_273";
530
- const tipText = "_tipText_nhlur_279";
531
- const tipStep = "_tipStep_nhlur_283";
532
- const sheetFoot = "_sheetFoot_nhlur_287";
533
- const sheetSection = "_sheetSection_nhlur_291";
534
- const sectionTitle = "_sectionTitle_nhlur_303";
535
- const notice = "_notice_nhlur_309";
536
- const sheetPhone = "_sheetPhone_nhlur_322";
537
- const back = "_back_nhlur_326";
538
- const overlay = "_overlay_nhlur_358";
539
- const box = "_box_nhlur_365";
540
- const tint = "_tint_nhlur_379";
541
- const chip = "_chip_nhlur_385";
542
- const tip = "_tip_nhlur_279";
543
- const studio = "_studio_nhlur_481";
534
+ const anchor = "_anchor_a8mxc_5";
535
+ const panel = "_panel_a8mxc_30";
536
+ const pill = "_pill_a8mxc_31";
537
+ const grip = "_grip_a8mxc_71";
538
+ const title = "_title_a8mxc_81";
539
+ const brand = "_brand_a8mxc_90";
540
+ const name = "_name_a8mxc_98";
541
+ const pillTitle = "_pillTitle_a8mxc_103";
542
+ const actions = "_actions_a8mxc_110";
543
+ const sides = "_sides_a8mxc_119";
544
+ const nav = "_nav_a8mxc_124";
545
+ const countButton = "_countButton_a8mxc_132";
546
+ const steps = "_steps_a8mxc_137";
547
+ const collapse = "_collapse_a8mxc_141";
548
+ const split = "_split_a8mxc_164";
549
+ const subtle = "_subtle_a8mxc_180";
550
+ const noteRow = "_noteRow_a8mxc_194";
551
+ const note = "_note_a8mxc_194";
552
+ const noteAction = "_noteAction_a8mxc_216";
553
+ const sheet = "_sheet_a8mxc_220";
554
+ const list = "_list_a8mxc_224";
555
+ const item = "_item_a8mxc_233";
556
+ const screenLink = "_screenLink_a8mxc_241";
557
+ const here = "_here_a8mxc_242";
558
+ const meta = "_meta_a8mxc_253";
559
+ const rows = "_rows_a8mxc_261";
560
+ const row = "_row_a8mxc_261";
561
+ const rowText = "_rowText_a8mxc_278";
562
+ const rowName = "_rowName_a8mxc_285";
563
+ const rowMeta = "_rowMeta_a8mxc_291";
564
+ const tipText = "_tipText_a8mxc_297";
565
+ const tipStep = "_tipStep_a8mxc_301";
566
+ const sheetFoot = "_sheetFoot_a8mxc_305";
567
+ const sheetSection = "_sheetSection_a8mxc_309";
568
+ const sectionTitle = "_sectionTitle_a8mxc_321";
569
+ const notice = "_notice_a8mxc_327";
570
+ const sheetPhone = "_sheetPhone_a8mxc_340";
571
+ const back = "_back_a8mxc_344";
572
+ const overlay = "_overlay_a8mxc_376";
573
+ const box = "_box_a8mxc_383";
574
+ const tint = "_tint_a8mxc_397";
575
+ const chip = "_chip_a8mxc_403";
576
+ const scrim = "_scrim_a8mxc_458";
577
+ const scrimFill = "_scrimFill_a8mxc_465";
578
+ const tip = "_tip_a8mxc_297";
579
+ const studio = "_studio_a8mxc_514";
544
580
  const styles = {
545
581
  anchor,
546
582
  panel,
@@ -552,14 +588,12 @@ const styles = {
552
588
  pillTitle,
553
589
  actions,
554
590
  sides,
591
+ nav,
592
+ countButton,
593
+ steps,
555
594
  collapse,
556
- phoneOnly,
557
- desktopOnly,
558
- count,
559
- swatches,
560
595
  split,
561
596
  subtle,
562
- more,
563
597
  noteRow,
564
598
  note,
565
599
  noteAction,
@@ -586,10 +620,13 @@ const styles = {
586
620
  box,
587
621
  tint,
588
622
  chip,
623
+ scrim,
624
+ scrimFill,
589
625
  tip,
590
626
  studio
591
627
  };
592
628
  const INSET = 8;
629
+ const HOLE = 4;
593
630
  function boxOf(el) {
594
631
  const r = shownRect(el);
595
632
  if (!r) return null;
@@ -609,10 +646,13 @@ function ChangeOutlines({
609
646
  tip: tip2,
610
647
  onShown,
611
648
  emphasis,
612
- flash
649
+ flash,
650
+ isolate = false,
651
+ bar
613
652
  }) {
614
653
  const [tick, setTick] = useState(0);
615
654
  const [active, setActive] = useState(null);
655
+ const maskId = `bridge-isolate-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
616
656
  useEffect(() => {
617
657
  let frame = 0;
618
658
  const on = () => {
@@ -663,30 +703,61 @@ function ChangeOutlines({
663
703
  document.removeEventListener("focusin", focus);
664
704
  };
665
705
  }, [surfaces]);
666
- 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) => {
667
- if (!b) return null;
668
- const s = surfaces[i];
669
- const showChip = flash || active === i || emphasis === i;
670
- const chip2 = /* @__PURE__ */ jsx("span", { className: styles.chip, "data-page": b.page || void 0, "data-shown": showChip || void 0, children: s.label });
671
- return /* @__PURE__ */ jsxs(
672
- "span",
673
- {
674
- className: styles.box,
675
- "data-page": b.page || void 0,
676
- "data-inside": !b.page && b.top < 24 || void 0,
677
- "data-emphasis": emphasis === i || void 0,
678
- style: { left: b.left, top: b.top, width: b.width, height: b.height, borderRadius: b.radius },
679
- children: [
680
- /* @__PURE__ */ jsx("span", { className: styles.tint }),
681
- active === i || emphasis === i ? (
682
- // Above the chip when there is room, else below it: never over the toolbar, which keeps clear of boxes.
683
- /* @__PURE__ */ jsx(Tooltip, { open: true, content: tip2(s), side: b.top < 64 ? "bottom" : "top", align: "start", className: PIN_CLASS, children: chip2 })
684
- ) : chip2
685
- ]
686
- },
687
- i
688
- );
689
- }) });
706
+ 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: [
707
+ isolate ? /* @__PURE__ */ jsxs("svg", { className: styles.scrim, width: "100%", height: "100%", focusable: "false", children: [
708
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("mask", { id: maskId, children: [
709
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: "white" }),
710
+ boxes.map(
711
+ (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
712
+ )
713
+ ] }) }),
714
+ /* @__PURE__ */ jsx("rect", { className: styles.scrimFill, width: "100%", height: "100%", mask: `url(#${maskId})` })
715
+ ] }) : null,
716
+ boxes.map((b, i) => {
717
+ if (!b) return null;
718
+ const s = surfaces[i];
719
+ const showChip = flash || active === i || emphasis === i;
720
+ const chip2 = /* @__PURE__ */ jsx("span", { className: styles.chip, "data-page": b.page || void 0, "data-shown": showChip || void 0, children: s.label });
721
+ return /* @__PURE__ */ jsxs(
722
+ "span",
723
+ {
724
+ className: styles.box,
725
+ "data-page": b.page || void 0,
726
+ "data-inside": !b.page && b.top < 24 || void 0,
727
+ "data-emphasis": emphasis === i || void 0,
728
+ style: { left: b.left, top: b.top, width: b.width, height: b.height, borderRadius: b.radius },
729
+ children: [
730
+ (active === i || emphasis === i) && !b.page ? /* @__PURE__ */ jsx(
731
+ Tooltip,
732
+ {
733
+ open: true,
734
+ content: tip2(s),
735
+ 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),
736
+ align: "start",
737
+ className: PIN_CLASS,
738
+ children: /* @__PURE__ */ jsx("span", { className: styles.tint })
739
+ }
740
+ ) : /* @__PURE__ */ jsx("span", { className: styles.tint }),
741
+ (active === i || emphasis === i) && b.page ? /* @__PURE__ */ jsx(Tooltip, { open: true, content: tip2(s), side: "bottom", align: "start", className: PIN_CLASS, children: chip2 }) : chip2
742
+ ]
743
+ },
744
+ i
745
+ );
746
+ })
747
+ ] });
748
+ }
749
+ function ChangeNav({ countText, tip: tip2, outlinesOn, disabled, total, isolate, announce, onToggle, onStep, onIsolate }) {
750
+ return /* @__PURE__ */ jsxs("div", { className: styles.nav, role: "group", "aria-label": "What changed on this screen", children: [
751
+ /* @__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 }) }),
752
+ total ? /* @__PURE__ */ jsxs(Fragment, { children: [
753
+ /* @__PURE__ */ jsxs(ButtonGroup, { appearance: "attached", "aria-label": "Step through the changed elements", className: styles.steps, children: [
754
+ /* @__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) }) }),
755
+ /* @__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) }) })
756
+ ] }),
757
+ /* @__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" }) })
758
+ ] }) : null,
759
+ /* @__PURE__ */ jsx(VisuallyHidden, { role: "status", children: announce })
760
+ ] });
690
761
  }
691
762
  function useSheet(css, applied) {
692
763
  const [el, setEl] = useState(null);
@@ -758,7 +829,6 @@ function useTogether(build, latest, changes, use, here2, applied) {
758
829
  };
759
830
  }
760
831
  const SIDE_LABEL = { before: "Live product", after: "This change" };
761
- const MAX_PAIRS = 3;
762
832
  function session() {
763
833
  try {
764
834
  return window.sessionStorage;
@@ -773,19 +843,20 @@ function remember(key, value) {
773
843
  }
774
844
  }
775
845
  function ToolbarRoot(props) {
776
- const { build: given, ds, product, studio: studio2, production, preview, deployEnv } = props;
846
+ const { ds, product, studio: studio2, production, preview, deployEnv, proxy } = props;
847
+ const given = useMemo(() => props.build && proxy !== void 0 ? { ...props.build, proxy } : props.build, [props.build, proxy]);
777
848
  const [build, setBuild] = useState(given ?? null);
778
849
  useEffect(() => {
779
850
  if (given || !ds || !product) return;
780
851
  let live = true;
781
- loadBuild(ds, product, { studio: studio2, production, preview, deployEnv }).then(
852
+ loadBuild(ds, product, { studio: studio2, production, preview, deployEnv, ...proxy !== void 0 ? { proxy } : {} }).then(
782
853
  (b) => live && setBuild(b),
783
854
  (err) => console.warn(`Bridge toolbar: ${err.message}`)
784
855
  );
785
856
  return () => {
786
857
  live = false;
787
858
  };
788
- }, [given, ds, product, studio2, production, preview, deployEnv]);
859
+ }, [given, ds, product, studio2, production, preview, deployEnv, proxy]);
789
860
  if (!build) return null;
790
861
  return /* @__PURE__ */ jsx(Decide, { build, useScope: props.scope ?? useDocumentScope, shops: props.shops ?? [], together: props.together ?? null });
791
862
  }
@@ -970,19 +1041,21 @@ function Toolbar({
970
1041
  };
971
1042
  }, [build, change, tg]);
972
1043
  const liveUrl = !tg && facts?.mode === "tokens" ? liveTokensUrl(build) : null;
973
- const [outlines, setOutlines] = useState(false);
1044
+ const [outlinePick, setOutlinePick] = useState(null);
1045
+ const outlines = outlinePick ?? open;
1046
+ const toggleOutlines = useCallback(() => setOutlinePick((p) => !(p ?? open)), [open]);
974
1047
  const canOutline = facts?.mode === "tokens" && (facts.tokens.length ?? 0) > 0;
975
1048
  const live = useLiveSheet(liveUrl, outlines && canOutline, side === "before" && facts?.mode === "tokens");
976
1049
  const onePairs = usePairs(tg ? null : facts, liveUrl, brand2, side === "before");
977
1050
  const pairs = tg ? T?.pairs ?? [] : onePairs;
978
1051
  const noDiff = tg ? T?.noDiff ?? null : facts?.mode === "tokens" ? noVisibleDifference(facts.files, { theme, brand: brand2 }) : null;
979
1052
  const outlinesOn = outlines && canOutline && !noDiff;
1053
+ const screenKey = `${hash}|${theme}|${tenantId}|${tg ? T?.included.join(",") ?? "" : ""}`;
980
1054
  const diff = useChangedElements(
981
1055
  outlinesOn,
982
1056
  tg ? T?.sheet ?? null : live.link,
983
1057
  (facts?.tokens ?? []).map((t) => t.cssVar),
984
- // Switching a change on or off repaints the combination: the outlines are measured again.
985
- `${hash}|${theme}|${tenantId}|${tg ? T?.included.join(",") ?? "" : ""}`,
1058
+ screenKey,
986
1059
  tg ? "after" : "before"
987
1060
  );
988
1061
  const nameOf = (v) => facts?.tokens.find((t) => t.cssVar === v)?.name ?? v;
@@ -993,6 +1066,9 @@ function Toolbar({
993
1066
  const [emphasis, setEmphasis] = useState(null);
994
1067
  const [flash, setFlash] = useState(false);
995
1068
  const [sheetOpen, setSheetOpen] = useState(false);
1069
+ const [position, setPosition] = useState(null);
1070
+ const [announce, setAnnounce] = useState("");
1071
+ const [isolate, setIsolate] = useState(false);
996
1072
  const phone = useMedia("(max-width: 760px)");
997
1073
  const reduced = useMedia("(prefers-reduced-motion: reduce)");
998
1074
  usePinnedBridgeTokens(liveTokensUrl(build), theme);
@@ -1056,7 +1132,7 @@ function Toolbar({
1056
1132
  const onKey = (e) => {
1057
1133
  if (canOutline && !noDiff && isKey(e, "c")) {
1058
1134
  e.preventDefault();
1059
- setOutlines((o) => !o);
1135
+ toggleOutlines();
1060
1136
  return;
1061
1137
  }
1062
1138
  if (!isFlipKey(e)) return;
@@ -1065,7 +1141,15 @@ function Toolbar({
1065
1141
  };
1066
1142
  window.addEventListener("keydown", onKey);
1067
1143
  return () => window.removeEventListener("keydown", onKey);
1068
- }, [choose, side, canOutline, noDiff]);
1144
+ }, [choose, side, canOutline, noDiff, toggleOutlines]);
1145
+ useEffect(() => {
1146
+ if (!isolate) return;
1147
+ const onEscape = (e) => {
1148
+ if (isEscape(e)) setIsolate(false);
1149
+ };
1150
+ window.addEventListener("keydown", onEscape, true);
1151
+ return () => window.removeEventListener("keydown", onEscape, true);
1152
+ }, [isolate]);
1069
1153
  useLayoutEffect(() => {
1070
1154
  const el = root.current;
1071
1155
  const anchor2 = el?.parentElement;
@@ -1152,8 +1236,39 @@ function Toolbar({
1152
1236
  const behind = facts?.behind ? "Live has new changes since this change started." : null;
1153
1237
  const shared = screens?.length && screens.every((s) => s.tenant === screens[0].tenant && s.theme === screens[0].theme) ? screens[0] : null;
1154
1238
  const shopOf = (t) => shops.find((x) => x.id === t)?.name ?? t;
1155
- const surfaces = outlinesOn ? diff?.surfaces ?? [] : [];
1239
+ const surfaces = useMemo(() => outlinesOn ? diff?.surfaces ?? [] : [], [outlinesOn, diff]);
1156
1240
  const shownCount = outlinesOn && diff ? shown.count : null;
1241
+ const order = useMemo(() => {
1242
+ const on = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => rendered(s.el));
1243
+ const places = on.map(({ s }) => {
1244
+ if (isPage(s.el)) return { top: -1, left: 0 };
1245
+ const r = s.el.getBoundingClientRect();
1246
+ return { top: r.top + window.scrollY, left: r.left + window.scrollX };
1247
+ });
1248
+ return readingOrder(places).map((k) => on[k].i);
1249
+ }, [surfaces]);
1250
+ const total = outlinesOn && diff ? order.length : null;
1251
+ const current = position !== null ? order[position] ?? null : null;
1252
+ useEffect(() => {
1253
+ setPosition(null);
1254
+ setAnnounce("");
1255
+ }, [screenKey, outlinesOn]);
1256
+ useEffect(() => {
1257
+ if (position !== null && position >= order.length) setPosition(null);
1258
+ }, [order.length, position]);
1259
+ const isolateOn = isolate && Boolean(total);
1260
+ const step = (delta) => {
1261
+ const next = stepTo(position, delta, order.length);
1262
+ if (next === null) return;
1263
+ const s = surfaces[order[next]];
1264
+ setPosition(next);
1265
+ setEmphasis(null);
1266
+ if (!isPage(s.el)) s.el.scrollIntoView({ block: "center", inline: "nearest", behavior: reduced ? "auto" : "smooth" });
1267
+ setAnnounce(stepAnnouncement(next, order.length, s.label));
1268
+ };
1269
+ const isHere = (sc) => sc.route === hash && sc.tenant === tenantId && sc.theme === theme;
1270
+ const elsewhere = (screens ?? []).filter((sc) => !isHere(sc));
1271
+ const screenHref = (sc) => `?${PARAM}=${change}&tenant=${encodeURIComponent(sc.tenant)}&theme=${encodeURIComponent(sc.theme)}#${sc.route}`;
1157
1272
  const activeHere = new Set((T?.pairs ?? []).flatMap((p) => p.token.from)).size;
1158
1273
  const note2 = shownIn ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1159
1274
  "Shown in ",
@@ -1166,7 +1281,17 @@ function Toolbar({
1166
1281
  ] }) : noDiff ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1167
1282
  /* @__PURE__ */ jsx("span", { children: noDifferenceText(noDiff, shops) }),
1168
1283
  /* @__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)}` })
1169
- ] }) : (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: [
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: [
1285
+ /* @__PURE__ */ jsx("span", { children: elsewhereLine(elsewhere.map((sc) => sc.label)) }),
1286
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: screenHref(elsewhere[0]) }), onClick: goTo(elsewhere[0]), className: styles.noteAction, children: [
1287
+ "Go to ",
1288
+ elsewhere[0].label
1289
+ ] })
1290
+ ] }) : /* @__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: [
1291
+ "Nothing changed in view. Next takes you to ",
1292
+ total === 1 ? "the changed element" : `the ${total} changed elements`,
1293
+ "."
1294
+ ] }) : tg && T && side === "after" && activeHere < T.included.length ? /* @__PURE__ */ jsxs("p", { className: styles.note, role: "status", children: [
1170
1295
  activeHere,
1171
1296
  " of these ",
1172
1297
  T.included.length,
@@ -1182,62 +1307,67 @@ function Toolbar({
1182
1307
  const from = p?.token?.from;
1183
1308
  return tg && from?.length ? fromText(from, titleOf) : null;
1184
1309
  };
1185
- 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}`}`;
1186
1310
  const splitOf = (before, after) => /* @__PURE__ */ jsxs("span", { className: styles.split, "aria-hidden": "true", children: [
1187
1311
  /* @__PURE__ */ jsx("span", { style: { backgroundColor: before } }),
1188
1312
  /* @__PURE__ */ jsx("span", { style: { backgroundColor: after } })
1189
1313
  ] });
1190
1314
  const subtleOf = (before, after) => isSubtle(before, after) ? /* @__PURE__ */ jsx("span", { className: styles.subtle, children: "Subtle change" }) : null;
1191
- const shownPairs = visiblePairs.filter((p) => p.color).slice(0, MAX_PAIRS);
1192
1315
  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 }));
1193
- 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: [
1194
- shownPairs.map((p) => /* @__PURE__ */ jsx("span", { children: splitOf(p.before, p.after) }, p.token.path)),
1195
- visiblePairs.length > shownPairs.length && /* @__PURE__ */ jsxs("span", { className: styles.more, children: [
1196
- "+",
1197
- visiblePairs.length - shownPairs.length,
1198
- " more ",
1199
- visiblePairs.length - shownPairs.length === 1 ? "token" : "tokens"
1200
- ] }),
1201
- shownPairs[0] ? subtleOf(shownPairs[0].before, shownPairs[0].after) : null
1202
- ] }) }) : null;
1203
1316
  const tokenNames = (facts?.tokens ?? []).map((t) => t.name);
1204
- const summary = shownCount === null ? null : surfaceLine(diff.surfaces.flatMap((s) => s.roots.map(nameOf)), shownCount);
1205
- const toggleTip = `${summary ?? `Outline what ${tokenNames.length === 1 ? tokenNames[0] : "this change"} changes on this screen.`} Press C.`;
1206
- const toggleLabel = shownCount === null ? "Show what changed" : `Show what changed · ${shownCount}`;
1207
- 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;
1208
- const outlineIcon = canOutline ? /* @__PURE__ */ jsx(
1209
- IconButton,
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(
1321
+ ChangeNav,
1210
1322
  {
1211
- icon: ScanSearch,
1212
- size: "md",
1213
- variant: "ghost",
1214
- pressed: outlinesOn,
1323
+ countText,
1324
+ tip: toggleTip,
1325
+ outlinesOn,
1215
1326
  disabled: Boolean(noDiff),
1216
- label: shownCount === null ? "Show what changed" : `Show what changed, ${shownCount} ${shownCount === 1 ? "surface" : "surfaces"}`,
1217
- className: styles.phoneOnly,
1218
- onClick: () => setOutlines((o) => !o)
1327
+ total,
1328
+ isolate: isolateOn,
1329
+ announce,
1330
+ onToggle: toggleOutlines,
1331
+ onStep: step,
1332
+ onIsolate: setIsolate
1219
1333
  }
1220
1334
  ) : null;
1221
1335
  const shownSurfaces = surfaces.map((s, i) => ({ s, i })).filter(({ s }) => shownRect(s.el));
1222
1336
  const focusSurface = (i) => {
1223
1337
  const el = surfaces[i]?.el;
1224
1338
  if (!el) return;
1225
- el.scrollIntoView({ block: "center", behavior: reduced ? "auto" : "smooth" });
1339
+ if (!isPage(el)) el.scrollIntoView({ block: "center", behavior: reduced ? "auto" : "smooth" });
1226
1340
  setEmphasis(i);
1341
+ if (order.includes(i)) setPosition(order.indexOf(i));
1227
1342
  };
1228
1343
  const tokensOf = (x) => [...new Set(x.roots.map(nameOf))].join(", ");
1229
- 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(
1230
- Button,
1344
+ const sides2 = /* @__PURE__ */ jsx(ButtonGroup, { appearance: "attached", "aria-label": "Compare", className: styles.sides, children: ["before", "after"].map((s) => /* @__PURE__ */ jsx(
1345
+ Tooltip,
1231
1346
  {
1232
- variant: "secondary",
1233
- size: "md",
1234
- pressed: side === s,
1235
- "aria-keyshortcuts": "B",
1236
- disabled: !canFlip && side !== s,
1237
- onClick: () => choose(s),
1238
- children: sideLabel(s)
1239
- }
1240
- ) }, s)) });
1347
+ className: PIN_CLASS,
1348
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
1349
+ sideLabel(s),
1350
+ ": ",
1351
+ s === "before" ? "what customers see today." : modeDetail(facts),
1352
+ " ",
1353
+ /* @__PURE__ */ jsx(Kbd, { keys: "B", size: "sm" }),
1354
+ " flips sides on the same screen."
1355
+ ] }),
1356
+ children: /* @__PURE__ */ jsx(
1357
+ Button,
1358
+ {
1359
+ variant: "secondary",
1360
+ size: "md",
1361
+ pressed: side === s,
1362
+ "aria-keyshortcuts": "B",
1363
+ disabled: !canFlip && side !== s,
1364
+ onClick: () => choose(s),
1365
+ children: sideLabel(s)
1366
+ }
1367
+ )
1368
+ },
1369
+ s
1370
+ )) });
1241
1371
  return /* @__PURE__ */ jsxs(
1242
1372
  ThemeProvider,
1243
1373
  {
@@ -1251,6 +1381,39 @@ function Toolbar({
1251
1381
  "data-dock": place.dock,
1252
1382
  style: place.lift ? { paddingBottom: `${place.lift}px` } : void 0,
1253
1383
  children: [
1384
+ outlinesOn && surfaces.length ? /* @__PURE__ */ jsx(
1385
+ ChangeOutlines,
1386
+ {
1387
+ surfaces,
1388
+ theme,
1389
+ emphasis: emphasis ?? current,
1390
+ flash,
1391
+ isolate: isolateOn,
1392
+ bar: () => root.current?.getBoundingClientRect() ?? null,
1393
+ onShown: setShownStable,
1394
+ tip: (s) => {
1395
+ const prop = s.props.find((p) => /^rgb/.test(p.before) && /^rgb/.test(p.after));
1396
+ const token = prop ? visiblePairs.find((p) => p.color && toHex(p.after) === toHex(prop.after) && toHex(p.before) === toHex(prop.before)) : null;
1397
+ const name2 = token?.token.name ?? [...new Set(s.roots.map(nameOf))].slice(0, 2).join(", ");
1398
+ const rootsFrom = [...new Set(s.roots.flatMap((r) => facts?.tokens.find((t) => t.cssVar === r)?.from ?? []))];
1399
+ const fromLine = fromOf(token) ?? (tg && rootsFrom.length ? fromText(rootsFrom, titleOf) : null);
1400
+ return /* @__PURE__ */ jsxs("span", { className: styles.tip, children: [
1401
+ prop ? splitOf(prop.before, prop.after) : null,
1402
+ /* @__PURE__ */ jsxs("span", { className: styles.tipText, children: [
1403
+ /* @__PURE__ */ jsxs("span", { children: [
1404
+ s.label,
1405
+ ": ",
1406
+ name2,
1407
+ prop ? `, ${hexPair(prop.before, prop.after)}` : ""
1408
+ ] }),
1409
+ token?.token.step ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: token.token.step }) : null,
1410
+ fromLine ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: fromLine.charAt(0).toUpperCase() + fromLine.slice(1) }) : null
1411
+ ] }),
1412
+ prop ? subtleOf(prop.before, prop.after) : null
1413
+ ] });
1414
+ }
1415
+ }
1416
+ ) : null,
1254
1417
  /* @__PURE__ */ jsxs(
1255
1418
  "div",
1256
1419
  {
@@ -1261,6 +1424,7 @@ function Toolbar({
1261
1424
  "data-side": side,
1262
1425
  "data-diff-ms": outlinesOn ? diff?.ms : void 0,
1263
1426
  "data-diff-count": shownCount ?? void 0,
1427
+ "data-diff-total": total ?? void 0,
1264
1428
  "data-diff-elements": outlinesOn ? diff?.elements.length : void 0,
1265
1429
  "data-note": note2 ? "" : void 0,
1266
1430
  style: drag ? { transform: `translateX(${drag}px)` } : void 0,
@@ -1285,19 +1449,15 @@ function Toolbar({
1285
1449
  /* @__PURE__ */ jsx("span", { className: styles.brand, children: "Bridge" }),
1286
1450
  /* @__PURE__ */ jsx("span", { className: styles.name, title: title2, children: title2 })
1287
1451
  ] }),
1288
- outlineIcon,
1452
+ sides2,
1453
+ nav2,
1289
1454
  /* @__PURE__ */ jsxs("div", { className: styles.actions, children: [
1290
- sides2,
1291
- /* @__PURE__ */ jsx("span", { className: styles.desktopOnly, children: swatches2 }),
1292
1455
  /* @__PURE__ */ jsxs(Popover, { open: sheetOpen, onOpenChange: setSheetOpen, children: [
1293
1456
  /* @__PURE__ */ jsx(
1294
1457
  PopoverTrigger,
1295
1458
  {
1296
- 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 }),
1297
- children: phone ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
1298
- "Changes",
1299
- shownCount ? /* @__PURE__ */ jsx("span", { className: styles.count, children: shownCount }) : null
1300
- ] })
1459
+ 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 }),
1460
+ children: phone ? null : /* @__PURE__ */ jsx(Fragment, { children: "Changes" })
1301
1461
  }
1302
1462
  ),
1303
1463
  /* @__PURE__ */ jsxs(PopoverContent, { side: "top", align: "center", className: `${styles.sheet} ${PIN_CLASS}`, "aria-label": "Changes", children: [
@@ -1363,15 +1523,15 @@ function Toolbar({
1363
1523
  shared.theme
1364
1524
  ] }) : null,
1365
1525
  /* @__PURE__ */ jsx("ul", { className: styles.list, children: screens.map((sc) => {
1366
- const current = sc.route === hash && sc.tenant === tenantId && sc.theme === theme;
1526
+ const current2 = isHere(sc);
1367
1527
  const where = shared ? null : /* @__PURE__ */ jsxs("span", { className: styles.meta, children: [
1368
1528
  " ",
1369
1529
  shopOf(sc.tenant),
1370
1530
  ", ",
1371
1531
  sc.theme
1372
1532
  ] });
1373
- return /* @__PURE__ */ jsxs("li", { className: styles.item, "aria-current": current ? "page" : void 0, children: [
1374
- current ? /* @__PURE__ */ jsxs("span", { className: styles.here, children: [
1533
+ return /* @__PURE__ */ jsxs("li", { className: styles.item, "aria-current": current2 ? "page" : void 0, children: [
1534
+ current2 ? /* @__PURE__ */ jsxs("span", { className: styles.here, children: [
1375
1535
  /* @__PURE__ */ jsx(Icon, { icon: Check, size: "sm" }),
1376
1536
  sc.label,
1377
1537
  where
@@ -1379,7 +1539,7 @@ function Toolbar({
1379
1539
  Link,
1380
1540
  {
1381
1541
  variant: "standalone",
1382
- href: `?${PARAM}=${change}&tenant=${encodeURIComponent(sc.tenant)}&theme=${encodeURIComponent(sc.theme)}#${sc.route}`,
1542
+ href: screenHref(sc),
1383
1543
  onClick: goTo(sc),
1384
1544
  className: styles.screenLink,
1385
1545
  children: [
@@ -1388,17 +1548,17 @@ function Toolbar({
1388
1548
  ]
1389
1549
  }
1390
1550
  ),
1391
- /* @__PURE__ */ jsx("span", { className: styles.meta, children: current ? "You are here" : `${Math.max(1, Math.round(sc.percent))}% changed` })
1551
+ /* @__PURE__ */ jsx("span", { className: styles.meta, children: current2 ? "You are here" : `${Math.max(1, Math.round(sc.percent))}% changed` })
1392
1552
  ] }, `${sc.route}|${sc.tenant}|${sc.theme}`);
1393
1553
  }) })
1394
1554
  ] }) : null,
1395
- visiblePairs.length ? /* @__PURE__ */ jsxs("section", { className: `${styles.sheetSection} ${styles.sheetPhone}`, "aria-label": "Tokens this change edits", children: [
1555
+ visiblePairs.length ? /* @__PURE__ */ jsxs("section", { className: styles.sheetSection, "aria-label": "Tokens this change edits", children: [
1396
1556
  /* @__PURE__ */ jsx("p", { className: styles.sectionTitle, children: "Tokens" }),
1397
1557
  /* @__PURE__ */ jsx("ul", { className: styles.list, children: roleGroups.map((g) => /* @__PURE__ */ jsxs("li", { className: styles.item, children: [
1398
1558
  /* @__PURE__ */ jsxs("span", { className: styles.rowText, children: [
1399
1559
  /* @__PURE__ */ jsx("span", { children: g.name }),
1400
1560
  /* @__PURE__ */ jsxs("span", { className: styles.rowMeta, children: [
1401
- g.pairs.length === 1 ? g.pairs[0].token.step ?? "" : `${g.pairs.length} steps`,
1561
+ 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`,
1402
1562
  isSubtle(g.pairs[0].before, g.pairs[0].after) ? ", subtle change" : ""
1403
1563
  ] })
1404
1564
  ] }),
@@ -1413,7 +1573,6 @@ function Toolbar({
1413
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
1414
1574
  ] })
1415
1575
  ] }),
1416
- /* @__PURE__ */ jsx("span", { className: styles.desktopOnly, children: outlineToggle }),
1417
1576
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", render: /* @__PURE__ */ jsx("a", { href: studio2 }), className: styles.studio, children: "Back to Studio" })
1418
1577
  ] }),
1419
1578
  /* @__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) }) }),
@@ -1427,38 +1586,7 @@ function Toolbar({
1427
1586
  note2 && /* @__PURE__ */ jsx("div", { className: styles.noteRow, children: note2 })
1428
1587
  ]
1429
1588
  }
1430
- ),
1431
- outlinesOn && surfaces.length ? /* @__PURE__ */ jsx(
1432
- ChangeOutlines,
1433
- {
1434
- surfaces,
1435
- theme,
1436
- emphasis,
1437
- flash,
1438
- onShown: setShownStable,
1439
- tip: (s) => {
1440
- const prop = s.props.find((p) => /^rgb/.test(p.before) && /^rgb/.test(p.after));
1441
- const token = prop ? visiblePairs.find((p) => p.color && toHex(p.after) === toHex(prop.after) && toHex(p.before) === toHex(prop.before)) : null;
1442
- const name2 = token?.token.name ?? [...new Set(s.roots.map(nameOf))].slice(0, 2).join(", ");
1443
- const rootsFrom = [...new Set(s.roots.flatMap((r) => facts?.tokens.find((t) => t.cssVar === r)?.from ?? []))];
1444
- const fromLine = fromOf(token) ?? (tg && rootsFrom.length ? fromText(rootsFrom, titleOf) : null);
1445
- return /* @__PURE__ */ jsxs("span", { className: styles.tip, children: [
1446
- prop ? splitOf(prop.before, prop.after) : null,
1447
- /* @__PURE__ */ jsxs("span", { className: styles.tipText, children: [
1448
- /* @__PURE__ */ jsxs("span", { children: [
1449
- s.label,
1450
- ": ",
1451
- name2,
1452
- prop ? `, ${hexPair(prop.before, prop.after)}` : ""
1453
- ] }),
1454
- token?.token.step ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: token.token.step }) : null,
1455
- fromLine ? /* @__PURE__ */ jsx("span", { className: styles.tipStep, children: fromLine.charAt(0).toUpperCase() + fromLine.slice(1) }) : null
1456
- ] }),
1457
- prop ? subtleOf(prop.before, prop.after) : null
1458
- ] });
1459
- }
1460
- }
1461
- ) : null
1589
+ )
1462
1590
  ]
1463
1591
  }
1464
1592
  );
@@ -1487,4 +1615,4 @@ function useHash() {
1487
1615
  export {
1488
1616
  ToolbarRoot as default
1489
1617
  };
1490
- //# sourceMappingURL=Toolbar-B-p9ZCvk.js.map
1618
+ //# sourceMappingURL=Toolbar-DJUb3p2c.js.map