whetstone-cli 3.1.3

Installer and CLI for Claude Code token optimization (Headroom + RTK + Memory)
/* global React */

// ============================================================
// RELEASES — feed rendered from CHANGELOG.md.
//
// `window.WHETSTONE_CHANGELOG` is generated by
// `whetstone changelog-sync` (run at release time, see the
// justfile recipe) from the repo-root CHANGELOG.md. Each entry:
//
//   { ver, date, sections: [{ name, bullets[] }] }
//
// `name` is one of: breaking | added | changed | fixed |
// removed | deprecated | security. Render order is forced so
// BREAKING is visually first; the rest follow a fixed precedence.
// ============================================================

const SECTION_ORDER = [
  'breaking',
  'added',
  'changed',
  'fixed',
  'removed',
  'deprecated',
  'security',
];

const SECTION_LABEL = {
  breaking: 'BREAKING',
  added: 'ADDED',
  changed: 'CHANGED',
  fixed: 'FIXED',
  removed: 'REMOVED',
  deprecated: 'DEPRECATED',
  security: 'SECURITY',
};

const SECTION_GLYPH = {
  added: '+',
  changed: '~',
  fixed: '!',
  removed: '-',
  deprecated: '×',
  security: '',
};

function sortSections(sections) {
  return [...sections].sort(
    (a, b) => SECTION_ORDER.indexOf(a.name) - SECTION_ORDER.indexOf(b.name)
  );
}

function countsLine(sections) {
  return sortSections(sections)
    .map((s) => `${s.bullets.length} ${SECTION_LABEL[s.name].toLowerCase()}`)
    .join(' · ');
}

function ReleaseCard({ entry }) {
  const sorted = sortSections(entry.sections || []);
  const breaking = sorted.find((s) => s.name === 'breaking');
  const rest = sorted.filter((s) => s.name !== 'breaking');

  return (
    <div className="release">
      <div className="when">
        <span className="ver-pill">v{entry.ver}</span>
        <span>{entry.date}</span>
        <span style={{ opacity: 0.6 }}>git tag · v{entry.ver}</span>
      </div>

      <div className="body">
        {breaking && (
          <div
            style={{
              marginBottom: 'var(--s-3)',
              padding: 'var(--s-2) var(--s-3)',
              borderLeft: '3px solid var(--c-magenta, #d4339a)',
              background: 'color-mix(in oklab, var(--c-magenta, #d4339a) 8%, transparent)',
              fontFamily: 'var(--f-mono)',
              fontSize: 12,
            }}
          >
            <div
              style={{
                letterSpacing: '0.14em',
                textTransform: 'uppercase',
                marginBottom: 'var(--s-2)',
                opacity: 0.85,
              }}
            >
              ⚠ BREAKING
            </div>
            <ul style={{ margin: 0, paddingLeft: '1.2em' }}>
              {breaking.bullets.map((b, i) => (
                <li key={i}>{b}</li>
              ))}
            </ul>
          </div>
        )}

        {rest.map((s) => (
          <div key={s.name} style={{ marginBottom: 'var(--s-2)' }}>
            <div
              style={{
                fontFamily: 'var(--f-mono)',
                fontSize: 11,
                letterSpacing: '0.14em',
                color: 'var(--fg-dim)',
                marginBottom: 4,
              }}
            >
              {SECTION_LABEL[s.name]}
            </div>
            <ul>
              {s.bullets.map((b, i) => (
                <li key={i}>
                  <span style={{ opacity: 0.5, marginRight: 6 }}>
                    {SECTION_GLYPH[s.name] || '·'}
                  </span>
                  {b}
                </li>
              ))}
            </ul>
          </div>
        ))}
      </div>

      <div className="stats">
        <div>
          <b>{countsLine(entry.sections || [])}</b>
        </div>
      </div>
    </div>
  );
}

function Releases() {
  const releases = window.WHETSTONE_CHANGELOG || [];
  const visible = releases.slice(0, 4);

  return (
    <section className="section ws-wrap" id="releases">
      <div className="ws-sec-head">
        <div className="ws-sec-tag">07 · RELEASES</div>
        <h2>Recent shipments.</h2>
      </div>

      <div className="releases">
        {visible.map((entry) => (
          <ReleaseCard key={entry.ver} entry={entry} />
        ))}
      </div>

      <div
        style={{
          marginTop: 'var(--s-5)',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          flexWrap: 'wrap',
          gap: 'var(--s-3)',
        }}
      >
        <div
          style={{
            fontFamily: 'var(--f-mono)',
            fontSize: 12,
            color: 'var(--fg-dim)',
            letterSpacing: '0.14em',
            textTransform: 'uppercase',
          }}
        >
          // SHOWING {visible.length} OF {releases.length} · source CHANGELOG.md
        </div>
        <a
          className="ws-btn ws-btn--sm ws-btn--ghost"
          href="https://github.com/z19r/whetstone/blob/main/CHANGELOG.md"
          target="_blank"
          rel="noreferrer"
        >
          FULL CHANGELOG ↗
        </a>
      </div>
    </section>
  );
}

Object.assign(window, { Releases });