@@ -609,7 +609,7 @@ def check_categories(entries: list) -> None:
609609 // walks the reader's filtered order instead of the full gallery.
610610 var SS_HASH = 'bdt-gallery-hash', SS_ORDER = 'bdt-gallery-order';
611611 var KINDS = ['', 'examples', 'showcase'];
612- var SORTS = ['default', 'az'];
612+ var SORTS = ['default', 'az', 'kind', 'cat' ];
613613 var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
614614
615615 // data-search holds title, slug, teaches, tags and category. Each
@@ -663,15 +663,19 @@ def check_categories(entries: list) -> None:
663663 } catch (e) {}
664664 }
665665
666- function writeHash() {
666+ // Discrete changes (chips, pills, kind, category, sort, reset) push a
667+ // history entry so Back undoes them; typing and density replace it.
668+ function writeHash(push) {
667669 var parts = [];
668670 if (state.q) parts.push('q=' + encodeURIComponent(state.q));
669671 if (state.tags.length) parts.push('tag=' + state.tags.map(encodeURIComponent).join(','));
670672 if (state.kind) parts.push('k=' + state.kind);
671673 if (state.cat) parts.push('c=' + state.cat);
672674 if (state.sort !== 'default') parts.push('s=' + state.sort);
673675 parts.push('d=' + state.density);
674- history.replaceState(null, '', location.pathname + location.search + '#' + parts.join('&'));
676+ var url = location.pathname + location.search + '#' + parts.join('&');
677+ if (push && url !== location.pathname + location.search + location.hash) history.pushState(null, '', url);
678+ else history.replaceState(null, '', url);
675679 remember();
676680 }
677681 var hashTimer = 0;
@@ -720,7 +724,7 @@ def check_categories(entries: list) -> None:
720724 b.textContent = it.label + ' \\ u00d7';
721725 b.setAttribute('aria-label', 'Remove filter: ' + it.label);
722726 b.addEventListener('click', function () {
723- it.drop(); syncChips(); applyFilters(); writeHash();
727+ it.drop(); syncChips(); applyFilters(); writeHash(true );
724728 });
725729 pillsEl.appendChild(b);
726730 });
@@ -746,6 +750,15 @@ def check_categories(entries: list) -> None:
746750 var x = a.getAttribute('data-name'), y = b.getAttribute('data-name');
747751 return x < y ? -1 : x > y ? 1 : 0;
748752 });
753+ } else if (state.sort === 'kind' || state.sort === 'cat') {
754+ // Stable by gallery order within each group. Examples have no
755+ // category and sort first; showcase pieces follow CATEGORIES order.
756+ var rank = function (c) {
757+ if (c.getAttribute('data-kind') !== 'showcase') return -1;
758+ return state.sort === 'cat' ? CATS.indexOf(c.getAttribute('data-category')) : 0;
759+ };
760+ var pos = new Map(cards.map(function (c, i) { return [c, i]; }));
761+ order.sort(function (a, b) { return (rank(a) - rank(b)) || (pos.get(a) - pos.get(b)); });
749762 }
750763 order.forEach(function (c) { grid.appendChild(c); });
751764 sortSel.value = state.sort;
@@ -796,7 +809,7 @@ def check_categories(entries: list) -> None:
796809 var at = state.tags.indexOf(t);
797810 if (at === -1) state.tags.push(t); else state.tags.splice(at, 1);
798811 }
799- syncChips(); applyFilters(); writeHash();
812+ syncChips(); applyFilters(); writeHash(true );
800813 });
801814 });
802815 // The chip row is one toolbar tab stop; arrows, Home and End move
@@ -827,7 +840,7 @@ def check_categories(entries: list) -> None:
827840 kindBtns.forEach(function (b) {
828841 b.addEventListener('click', function () {
829842 state.kind = b.getAttribute('data-kind-filter');
830- applyFilters(); writeHash();
843+ applyFilters(); writeHash(true );
831844 });
832845 });
833846 // A category only exists among showcase pieces, so picking one moves
@@ -836,12 +849,12 @@ def check_categories(entries: list) -> None:
836849 b.addEventListener('click', function () {
837850 state.cat = b.getAttribute('data-cat-filter');
838851 if (state.cat) state.kind = 'showcase';
839- applyFilters(); writeHash();
852+ applyFilters(); writeHash(true );
840853 });
841854 });
842855 sortSel.addEventListener('change', function () {
843856 state.sort = SORTS.indexOf(sortSel.value) !== -1 ? sortSel.value : 'default';
844- applySort(); writeHash();
857+ applySort(); writeHash(true );
845858 });
846859 densityBtns.forEach(function (b) {
847860 b.addEventListener('click', function () {
@@ -852,7 +865,7 @@ def check_categories(entries: list) -> None:
852865 resetFilters.addEventListener('click', function () {
853866 state.q = ''; state.tags = []; state.kind = ''; state.cat = ''; state.sort = 'default';
854867 q.value = '';
855- syncChips(); applySort(); applyFilters(); writeHash(); q.focus();
868+ syncChips(); applySort(); applyFilters(); writeHash(true ); q.focus();
856869 });
857870 filtersToggle.addEventListener('click', function () {
858871 var open = filtersEl.classList.toggle('open');
@@ -1657,6 +1670,8 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str, css_v:
16571670 ' <select id="sort">\n '
16581671 ' <option value="default">Gallery order</option>\n '
16591672 ' <option value="az">Name A–Z</option>\n '
1673+ ' <option value="kind">Examples first</option>\n '
1674+ ' <option value="cat">By category</option>\n '
16601675 ' </select>\n '
16611676 ' </label>\n '
16621677 ' <div class="density" role="group" aria-label="Card density">\n '
0 commit comments