Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ jobs:
- name: Verify index
run: python3 scripts/build_index.py --check

- name: Test classmap contracts and promotion
run: python3 -m unittest discover -s scripts

- name: Validate exposure patches
run: python3 scripts/validate_expose.py
47 changes: 46 additions & 1 deletion 1030000/META.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"unresolved_missing": 0,
"stale": 0,
"cdp_hit_rate": 0.6389,
"overlay_entries": 280
"overlay_entries": 284
},
"required_paths": {
"main.playbar.buttons.button.wrapper": "verified_cdp",
Expand Down Expand Up @@ -51,5 +51,50 @@
"deep_navigation_succeeded": 7,
"css_overlay_conflicts": 0,
"css_overlay_ambiguous_excluded": 82
},
"regression_checks": {
"topbar_right_button": {
"date": "2026-09-15",
"spotify_version": "1.3.0.277",
"path": "main.topbar.right.button_t.wrapper",
"class": "nR3ekhH3c1IU7dX9smcR",
"staged_class": "main-topBar-buddyFeed",
"before_hitbox_px": [
16,
16
],
"after_hitbox_px": [
32,
32
],
"native_hitbox_px": [
32,
32
],
"gap_px": 8,
"verification": "Measured Bookmark, Full App Display, and Popup Lyrics after Rust apply and a client restart. Their hitboxes and gaps match the native notification and activity buttons. The unchanged 1.2.97 classmap resolves to the same semantic class, whose archived CSS also specifies 32px width and height."
},
"root_containers": {
"date": "2026-09-15",
"spotify_version": "1.3.0.277",
"mappings": {
"bQetA_KiP9n0DQVcGa7m": "Root__globalNav",
"PIP22o58Crv8RXY4wB2o": "Root__nav-bar",
"itzKVxWhS4n59fp_KIVc": "Root__now-playing-bar"
},
"verification": "Stock CSS and live child controls confirm the global-nav, left-sidebar, and now-playing-bar roles. After applying with Rust CLI 3.0.0-beta.17 on macOS Spotify 1.3.0.277, all three hooks exist and Text's Spicetify scheme paints them rgb(46, 40, 55). Navigation borders and spacing return; no Text CSS change was needed. Unthemed container rectangles, padding, borders, and backgrounds match the pre-apply measurements. Flow's player occupies its own 200px right column without overlapping the main view. Profile navigation, library sorting, the compact-library toggle and restoration, a Settings select, Bookmark, and the queue were exercised through pointer events. Module and native toolbar buttons retain 32px hitboxes and 8px gaps.",
"previous_version_check": {
"spotify_version": "1.2.94",
"method": "archived stock CSS inspection",
"verification": "The embedded CSS-map already maps qPaMr9Jzt0_Doy3C, vEHGULrufZMHBNrp, and YdGOYWQYr6kqh7KU to the same three hooks. Their stock declarations preserve the same grid areas, sizing, and player gutters as 1.3.0; black library and player container backgrounds are already present in 1.2.94. Older version maps remain unchanged. This is archived CSS evidence, not a live older-client check."
}
},
"settings_toggle_input": {
"date": "2026-09-15",
"spotify_version": "1.3.0.277",
"class": "utdiMuyxdKvPowN1CIBs",
"staged_class": "x-toggle-input",
"verification": "The native checkbox uses opacity:0, pointer-events:none, and position:absolute in five stock stylesheets. The missing semantic hook left stdlib Settings rows showing both a native checkbox and their styled indicator. Reapply restores the shared input styling; live Spicetify Settings inputs have opacity 0 and absolute positioning while their toggle indicators remain visible. Regression coverage fails without the mapping."
}
}
}
2 changes: 1 addition & 1 deletion 1030000/classmap-19f856aefd5.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"wrapper": "e-10860-legacy-button e-10860-button-tertiary--condensed-all"
},
"button_t": {
"wrapper": "e-10860-legacy-button e-10860-button-tertiary--condensed-all"
"wrapper": "nR3ekhH3c1IU7dX9smcR"
},
"upgrade_button": {
"wrapper": "e-10860-legacy-button e-10860-legacy-button-primary"
Expand Down
4 changes: 4 additions & 0 deletions 1030000/css-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"OweqfPQPNscHYqA1Ke4y": "profile-userEditDetails-labelText",
"P91StDUW8iiKx4vRlEpx": "artist-artistDiscography-headerContainer",
"PGUsx7TTOQt2hFluVtHK": "main-trackList-rowSectionEnd",
"PIP22o58Crv8RXY4wB2o": "Root__nav-bar",
"PSn5qg6vyMedF0JBEN5g": "artist-artistDiscography-headerImage",
"PeQnLdb9Gd0blACadFa7": "main-dropDown-isSafari",
"Pucs9mmakJk6QyGoxAdu": "x-settings-zoomButton",
Expand Down Expand Up @@ -140,6 +141,7 @@
"at0g9yMjiRK9mm2WoIqv": "x-settings-zoomButtonIconWrapper",
"axzh7Jhch7v2hTblCyPy": "main-nowPlayingView-headerCloseButton",
"azTGVyS_7WuUbhEDoCgH": "main-globalNav-historyButtonsContainer",
"bQetA_KiP9n0DQVcGa7m": "Root__globalNav",
"bQxJ0kehcml02vaIAnXT": "x-proxySettings-ProxySettings",
"bRRJlkldFGQjaL5FAFze": "main-globalNav-searchInputTextWrapper",
"bbnZAUOa3NQHoL3gTSPA": "main-yourLibraryX-library",
Expand Down Expand Up @@ -190,6 +192,7 @@
"iV28E0oeNJEXWZlJhIB3": "main-embedWidgetGenerator-dimensionLabel",
"ikEUGwFAayomtVGFTrSA": "main-embedWidgetGenerator-contentIframe",
"inPROcpr9gEShIW_XxcW": "view-homeShortcutsGrid-name",
"itzKVxWhS4n59fp_KIVc": "Root__now-playing-bar",
"j2bAoqgf5XIMUds91Cqx": "volume-bar__icon-button",
"jS5CsFdh2O_GQ7Ai6YfO": "main-embedWidgetGenerator-tooltipInitiator",
"jhHgzVPKuWbfL02G8MAz": "main-useDropTarget-base",
Expand Down Expand Up @@ -250,6 +253,7 @@
"uR1BMutIFRmR6BBWSAWo": "main-nowPlayingView-contextItemInfo",
"uguMZhYNPFPT0K39gHNI": "main-trackList-rowImagePlayButton",
"upDNlpL8xEkxmKWWw9IF": "lyrics-lyricsContent-provider",
"utdiMuyxdKvPowN1CIBs": "x-toggle-input",
"v6gpDRQbTaN60R0FTiAA": "main-playlistEditDetailsModal-characterCounter",
"vBiCRVHx1pQjZnYVFTSs": "main-embedWidgetGenerator-widthField",
"vvokofZHMkIo13xngnyn": "playlist-playlist-sectionTitleWrapper",
Expand Down
6 changes: 3 additions & 3 deletions index.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@
"1030000": {
"classmap": {
"file": "classmap-19f856aefd5.json",
"sha256": "51644e39368847eefdb9befd6684f13b4534cbc67adf7cbd0a3c3e63ba14d8ff"
"sha256": "17d9575c38a5a7cce9d6bb28665aff0c6e88b4ce4d94955bad297e19c97b33dc"
},
"cssMapOverlay": {
"file": "css-map.json",
"sha256": "a32733c0e135b556ac6eab8633d48a85c02d5f5d9ba595601116a148b698745e"
"sha256": "29351b698ec06c6731a6069628ed007218e20acd507b98d5a89248ba3770370b"
},
"meta": {
"file": "META.json",
"sha256": "a0f481a553daf219f4b3c1b9f34d5dfdc4152b01733729315ae146e031d2616c"
"sha256": "c1dfef890570cb5a02524558c9c6b8fd89f103bc59f08ce8d2079df0ceebfc6e"
},
"spotifyVersion": "1.3.0.277",
"status": "verified"
Expand Down
47 changes: 47 additions & 0 deletions scripts/test_classmap_contracts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Check classmap roles after the CSS-map rewrite used during staging."""

import json
import unittest

from build_index import ROOT, classmap_file


class TopbarButtonContractTests(unittest.TestCase):
def test_spotify_130_topbar_buttons_keep_the_native_hitbox_after_staging(self):
key = ROOT / "1030000"
mapping = json.loads(classmap_file(key).read_text())
overlay = json.loads((key / "css-map.json").read_text())
wrapper = mapping["main"]["topbar"]["right"]["button_t"]["wrapper"]
staged = [overlay.get(token, token) for token in wrapper.split()]
self.assertIn(
"main-topBar-buddyFeed", staged,
"The toolbar wrapper must retain the native button hitbox, "
"not just a generic Encore class that exists in the DOM.",
)
self.assertFalse(
any("--condensed-" in token for token in staged),
"Mapped classes must not strip the icon-only button padding.",
)


class RootContainerContractTests(unittest.TestCase):
def test_settings_toggle_hides_the_native_input_behind_its_indicator(self):
overlay = json.loads((ROOT / "1030000" / "css-map.json").read_text())
self.assertEqual(overlay.get("utdiMuyxdKvPowN1CIBs"), "x-toggle-input")

def test_spotify_130_restores_the_three_layout_container_hooks(self):
overlay = json.loads((ROOT / "1030000" / "css-map.json").read_text())
# Verified against stock grid-area declarations and live child controls.
expected = {
"bQetA_KiP9n0DQVcGa7m": "Root__globalNav",
"PIP22o58Crv8RXY4wB2o": "Root__nav-bar",
"itzKVxWhS4n59fp_KIVc": "Root__now-playing-bar",
}
for source, target in expected.items():
with self.subTest(container=target):
self.assertEqual(overlay.get(source), target)
self.assertEqual(list(overlay.values()).count(target), 1)


if __name__ == "__main__":
unittest.main()