grr-cli 2.1.4

Google tools from the terminal, at maximum performance: 401 methods across 14 Google APIs in one generated command tree, zero-config, over HTTP/3
Documentation
---
import { ClientRouter } from 'astro:transitions';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import SearchDialog from '../components/SearchDialog.astro';
import stats from '../data/stats.json';
import '../styles/global.css';

interface Props {
  title: string;
  description: string;
  noindex?: boolean;
  /**
   * Path the canonical/og:url should point at. Defaults to the route being
   * rendered; the 404 page overrides it with `/` so it never advertises a
   * canonical URL that does not exist.
   */
  canonicalPath?: string;
}

const { title, description, noindex = false, canonicalPath } = Astro.props;
const base = import.meta.env.BASE_URL;
const normalizedBase = base.endsWith('/') ? base : `${base}/`;
const site = Astro.site ?? new URL('https://grr-cli.pages.dev');
const relativePath = Astro.url.pathname.startsWith(normalizedBase)
  ? Astro.url.pathname.slice(normalizedBase.length)
  : Astro.url.pathname.replace(/^\/+/, '');
const canonical = new URL(`${normalizedBase}${canonicalPath ?? relativePath}`, site).toString();
// Social crawlers do not render SVG, so the rasterised card is the share image.
const socialImage = new URL(`${normalizedBase}og.png`, site).toString();
const socialImageAlt = 'grr pixel-art crab mascot beside a terminal running grr commands';
// The commit the deploy came from, when Cloudflare Pages built it (see the
// build-sha meta below). Empty in local and preview builds.
const commitSha = process.env.CF_PAGES_COMMIT_SHA ?? '';
// The home page passes `title="grr"` so it renders the exact brand title; every
// other route passes a real title and gets the `${title} · Rust Rewrite (grr)`
// suffix form. Both strings are verification-critical, so neither is derived
// from page copy.
const fullTitle = title === 'grr' ? 'Rust Rewrite (grr) — Google tools from the terminal' : `${title} · Rust Rewrite (grr)`;
const jsonLd = JSON.stringify({
  '@context': 'https://schema.org',
  '@type': 'SoftwareApplication',
  name: 'Rust Rewrite',
  alternateName: 'grr',
  applicationCategory: 'DeveloperApplication',
  applicationSubCategory: 'Command line utility',
  operatingSystem: 'macOS, Linux, Windows',
  license: 'https://opensource.org/licenses/MIT',
  url: site.toString(),
  image: socialImage,
  author: {
    '@type': 'Organization',
    name: 'grr project',
    url: 'https://github.com/debanjanbasu/grr-cli',
  },
  softwareVersion: stats.version,
  datePublished: '2026-09-22',
  isAccessibleForFree: true,
  sameAs: ['https://github.com/debanjanbasu/grr-cli', 'https://crates.io/crates/grr-cli'],
  codeRepository: 'https://github.com/debanjanbasu/grr-cli',
  description,
  offers: {
    '@type': 'Offer',
    price: 0,
    priceCurrency: 'USD',
  },
});
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <ClientRouter />
    <script is:inline>
      (function () {
        var applyStoredTheme = function () {
          try {
            var theme = localStorage.getItem('grr-theme');
            if (theme === 'bumblebee' || theme === 'dim') {
              document.documentElement.setAttribute('data-theme', theme);
            } else if (theme === 'light' || theme === 'dark') {
              /* Values from the pre-DaisyUI site: map them so a returning
                 visitor keeps the choice they already made. */
              document.documentElement.setAttribute('data-theme', theme === 'light' ? 'bumblebee' : 'dim');
            }
          } catch (error) {
            /* storage unavailable: stay on the system theme */
          }
        };
        applyStoredTheme();
        /* The ClientRouter swaps the document on client-side navigations; the
           stored choice is re-applied after every swap. */
        document.addEventListener('astro:after-swap', applyStoredTheme);
      })();
    </script>
    <meta name="viewport" content="width=device-width" />
    <meta name="description" content={description} />
    <meta name="author" content="grr project" />
    <meta name="theme-color" media="(prefers-color-scheme: light)" content="#fbf7f1" />
    <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1b130e" />
    <meta name="color-scheme" content="light dark" />
    <meta name="google-site-verification" content="eOuATr6yw8ALdCuyHxA_fan3dXicCibqOyHg1ZORrYo" />
    <!-- The commit this build came from: Cloudflare Pages sets
         CF_PAGES_COMMIT_SHA at build time, and scripts/verify-delivery.ts
         compares it against main to catch a stale or failed deploy (the
         deploy is a Cloudflare git integration — invisible from CI
         otherwise). Absent locally, which is fine: the check treats a
         missing marker as a warning. -->
    {commitSha && <meta name="build-sha" content={commitSha} />}
    <meta name="robots" content={noindex ? 'noindex, nofollow' : 'index, follow'} />
    <link rel="preload" href={`${normalizedBase}fonts/3270-Regular.woff2`} as="font" type="font/woff2" crossorigin />
    <link rel="canonical" href={canonical} />
    <link rel="icon" type="image/svg+xml" href={`${normalizedBase}favicon.svg`} sizes="any" />
    <link rel="icon" type="image/svg+xml" href={`${normalizedBase}favicon-32.svg`} sizes="32x32" />
    <link rel="icon" type="image/svg+xml" href={`${normalizedBase}favicon-16.svg`} sizes="16x16" />
    <meta property="og:type" content="website" />
    <meta property="og:site_name" content="Rust Rewrite" />
    <meta property="og:locale" content="en_US" />
    <meta property="og:title" content={fullTitle} />
    <meta property="og:description" content={description} />
    <meta property="og:url" content={canonical} />
    <meta property="og:image" content={socialImage} />
    <meta property="og:image:secure_url" content={socialImage} />
    <meta property="og:image:type" content="image/png" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />
    <meta property="og:image:alt" content={socialImageAlt} />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content={fullTitle} />
    <meta name="twitter:description" content={description} />
    <meta name="twitter:image" content={socialImage} />
    <meta name="twitter:image:alt" content={socialImageAlt} />
    <script type="application/ld+json" set:html={jsonLd}></script>
    <title>{fullTitle}</title>
  </head>
  <body>
    <a class="skip-link" href="#main-content">Skip to content</a>
    <div class="backdrop" aria-hidden="true"><span class="backdrop-stars-back"></span><span class="backdrop-stars-mid"></span></div>
    <div class="scroll-progress" aria-hidden="true"></div>
    <div class="site-shell">
      <Header />
      {/* Pagefind indexes only the elements carrying data-pagefind-body, so
          marking <main> keeps the header, nav and footer chrome out of the
          search index while every page's real content stays in. */}
      <main id="main-content" data-pagefind-body>
        <slot />
      </main>
      <Footer />
    </div>
    <SearchDialog />
    <script is:inline type="module">
      (function () {
        var root = document.documentElement;
        var query = window.matchMedia('(prefers-color-scheme: dark)');
        var isDark = function () {
          return root.dataset.theme === 'dim' || (query.matches && root.dataset.theme !== 'bumblebee');
        };
        var sync = function () {
          var button = document.querySelector('[data-theme-toggle]');
          if (button) button.setAttribute('aria-pressed', String(isDark()));
        };

        /* Theme toggle — delegated on the document, so one listener covers
           every element on the page and survives the ClientRouter body swaps:
           the header swapped in after a client-side navigation keeps working
           without re-binding. */
        var initThemeToggle = function () {
          if (window.__grrThemeBound) return;
          window.__grrThemeBound = true;
          document.addEventListener('click', function (event) {
            var target = event.target;
            var button = target && target.closest ? target.closest('[data-theme-toggle]') : null;
            if (!button) return;
            var next = isDark() ? 'bumblebee' : 'dim';
            root.dataset.theme = next;
            try {
              localStorage.setItem('grr-theme', next);
            } catch (error) {
              /* storage unavailable: toggle for this visit only */
            }
            sync();
          });
          if (query.addEventListener) {
            query.addEventListener('change', sync);
          }
        };

        /* Copy-to-clipboard, delegated.
           One listener for every code block on the page rather than a handler
           per block: the text is read back out of the DOM, so the command is
           never duplicated into a data attribute and the HTML stays smaller.
           Delegated on the document, so code windows swapped in by the
           ClientRouter keep working after a client-side navigation. */
        var initCopy = function () {
          if (window.__grrCopyBound) return;
          window.__grrCopyBound = true;
          function flash(button, label) {
            var state = button.querySelector('[data-copy-state]');
            if (!state) return;
            if (button.dataset.copyReset) clearTimeout(button.dataset.copyReset);
            state.textContent = label;
            button.classList.add('is-copied');
            button.dataset.copyReset = setTimeout(function () {
              state.textContent = 'Copy';
              button.classList.remove('is-copied');
            }, 1600);
          }

          /* Fallback when the async clipboard API is unavailable or rejects
             (older Safari, non-secure origins). Every synchronous copy call is
             deprecated, so select the command text in place and let the
             browser's own copy shortcut finish it — the flash already says
             'Press Ctrl+C'. */
          function selectForManualCopy(code) {
            var range = document.createRange();
            range.selectNodeContents(code);
            var selection = window.getSelection();
            if (!selection) return;
            selection.removeAllRanges();
            selection.addRange(range);
          }

          document.addEventListener('click', function (event) {
            var button = event.target.closest ? event.target.closest('[data-copy]') : null;
            if (!button) return;
            var block = button.closest('.code-window');
            var code = block && block.querySelector('pre code');
            if (!code) return;
            var text = code.textContent.replace(/\s+$/, '');
            event.preventDefault();

            if (navigator.clipboard && navigator.clipboard.writeText) {
              navigator.clipboard.writeText(text).then(
                function () {
                  flash(button, 'Copied');
                },
                function () {
                  selectForManualCopy(code);
                  flash(button, 'Press Ctrl+C');
                },
              );
            } else {
              selectForManualCopy(code);
              flash(button, 'Press Ctrl+C');
            }
          });
        };

        /* Idempotent inits, invoked on both the initial load and every
           astro:page-load the ClientRouter fires after a swap. */
        var init = function () {
          initThemeToggle();
          initCopy();
          sync();
        };
        init();
        document.addEventListener('astro:page-load', init);
      })();
    </script>
  </body>
</html>