PageSourceSearch

https://sakana.ai/js/blog-filter.js

js sakana.ai collected 2026-09-24 08:48:58 UTC 4,205 bytes, 130 lines download raw bytes

1/* Sakana AI Blog Filter
2 * Filters articles by label.
3 * Filter state is stored in URL query parameters:
4 *   ?label=research        → label filter
5 *   ?label=finance         → label filter
6 *
7 * Language switching (JP ↔ EN) is handled per-article in the post layout.
8 */
9(function () {
10  // Display name lookup — ordered for chip display
11  var LABEL_NAMES = {
12    'research':      'Research',
13    'product':       'Product',
14    'finance':       'Finance',
15    'defense-intel': 'Defense & Intel',
16    'partnerships':  'Partnerships',
17    'event-media':   'Event & Media',
18    'funding-ir':    'Funding & IR',
19    'inside-sakana': 'Inside Sakana AI'
20  };
21
22  // --- State ---
23  var activeLabel = null;   // string or null
24
25  // All <article> elements with a data-labels attribute
26  var articles = Array.prototype.slice.call(
27    document.querySelectorAll('article[data-labels]')
28  );
29
30  // ---------------------------------------------------------------
31  // URL helpers
32  // ---------------------------------------------------------------
33  function readParams() {
34    var p = new URLSearchParams(window.location.search);
35    activeLabel = p.get('label') || null;
36  }
37
38  function writeParams() {
39    var p = new URLSearchParams();
40    if (activeLabel) p.set('label', activeLabel);
41    var qs = p.toString();
42    history.replaceState(null, '', qs ? '?' + qs : window.location.pathname);
43  }
44
45  // ---------------------------------------------------------------
46  // Filtering
47  // ---------------------------------------------------------------
48  function applyFilter() {
49    articles.forEach(function (a) {
50      var labelList = a.dataset.labels ? a.dataset.labels.split(',') : [];
51      var ok = !activeLabel || labelList.indexOf(activeLabel) !== -1;
52      a.style.display = ok ? '' : 'none';
53    });
54  }
55
56  // Return labels present across all visible articles
57  function labelsInUse() {
58    var seen = {};
59    articles.forEach(function (a) {
60      if (a.dataset.labels) {
61        a.dataset.labels.split(',').forEach(function (l) {
62          if (l && LABEL_NAMES[l]) seen[l] = true;
63        });
64      }
65    });
66    // Return in LABEL_NAMES definition order
67    return Object.keys(LABEL_NAMES).filter(function (l) { return seen[l]; });
68  }
69
70  // ---------------------------------------------------------------
71  // Render
72  // ---------------------------------------------------------------
73  function render() {
74    var el = document.getElementById('label-chips');
75    if (!el) return;
76
77    var labels = labelsInUse();
78
79    var html = '<button class="cat-chip' + (!activeLabel ? ' active' : '') +
80               '" data-label="">All</button>';
81    labels.forEach(function (label) {
82      var active = activeLabel === label;
83      html += '<button class="cat-chip' + (active ? ' active' : '') +
84              '" data-label="' + label + '">' + LABEL_NAMES[label] + '</button>';
85    });
86    el.innerHTML = html;
87
88    el.querySelectorAll('.cat-chip').forEach(function (btn) {
89      btn.addEventListener('click', function () {
90        activeLabel = btn.dataset.label || null;
91        writeParams();
92        render();
93        applyFilter();
94      });
95    });
96
97    renderPubLink();
98  }
99
100  // Show a small link to pub.sakana.ai only when the Research filter is active
101  function renderPubLink() {
102    var container = document.getElementById('blog-filter');
103    if (!container) return;
104
105    var link = document.getElementById('pub-link');
106    if (activeLabel === 'research') {
107      if (!link) {
108        link = document.createElement('p');
109        link.id = 'pub-link';
110        link.className = 'pub-link';
111        link.style.marginTop = '1.4em';
112        link.style.marginBottom = '-0.5em';
113        link.innerHTML = '<a href="https://pub.sakana.ai/" ' +
114                         'target="_blank" rel="noopener">' +
115                         'See more interactive research blogs</a>';
116        container.appendChild(link);
117      }
118    } else if (link) {
119      link.parentNode.removeChild(link);
120    }
121  }
122
123  // ---------------------------------------------------------------
124  // Boot
125  // ---------------------------------------------------------------
126  readParams();
127  render();
128  applyFilter();
129
130})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.