grr-cli 1.0.0

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 MenuIcon from './MenuIcon.astro';

const base = import.meta.env.BASE_URL;
const currentPath = Astro.url.pathname;
const links = [
  { href: base, label: 'Overview' },
  { href: `${base}install/`, label: 'Install' },
  { href: `${base}docs/`, label: 'Docs' },
  { href: `${base}changelog/`, label: 'Changelog' },
  { href: `${base}compare/`, label: 'Compare' },
  { href: `${base}privacy/`, label: 'Privacy' },
  { href: `${base}terms/`, label: 'Terms' },
];
const isActive = (href: string) => {
  if (href === base) {
    return currentPath === base || currentPath === `${base}index.html`;
  }
  return currentPath.startsWith(href);
};
---

<header class="site-header navbar sticky top-0 z-40 h-auto min-h-16 border-b border-base-300 bg-base-100/88 backdrop-blur-md">
  <div class="container header-inner flex w-full min-w-0 flex-wrap items-center justify-between gap-x-4 min-h-16 py-2.5">
    <a class="brand inline-flex flex-none items-center gap-2.5 no-underline" href={base} aria-label="grr home">
      <img src={`${base}logo.svg`} alt="" width="256" height="256" class="h-[30px] w-[30px]" />
      <span class="brand-wordmark text-[19px] leading-6">grr</span>
    </a>
    <nav class="primary-nav hidden flex-1 md:flex" aria-label="Primary navigation">
      {
        links.map((link) => (
          <a class:list={{ active: isActive(link.href) }} href={link.href} aria-current={isActive(link.href) ? 'page' : undefined}>
            {link.label}
          </a>
        ))
      }
    </nav>
    <div class="flex flex-none items-center gap-2">
      {/* The search entry point. The overlay itself is mounted once by
          BaseLayout; the handler is document-delegated on [data-search-open],
          so this trigger survives the ClientRouter's header swaps. Without JS
          the button is hidden by CSS (html[data-search-ready]) and the
          <noscript> link to the docs index is the fallback route in. */}
      <div class="search" data-search-trigger>
        <button
          type="button"
          class="search-trigger"
          data-search-open
          aria-label="Search the site"
          aria-haspopup="dialog"
          title="Search (press /)"
        >
          <span class="search-icon" aria-hidden="true"></span>
          <span class="search-trigger-label" aria-hidden="true">Search</span>
          <kbd class="search-trigger-kbd" aria-hidden="true">/</kbd>
        </button>
        <script is:inline data-astro-rerun>document.documentElement.dataset.searchReady = '';</script>
        <noscript>
          <a class="search-noscript" href={`${base}docs/`}>Docs index</a>
        </noscript>
      </div>
      <button class="theme-toggle" type="button" data-theme-toggle aria-label="Toggle color theme" aria-pressed="false" title="Toggle color theme">
        <svg class="icon-moon" viewBox="0 0 48 48" width="20" height="20" aria-hidden="true" focusable="false" shape-rendering="crispEdges">
          <g fill="currentColor">
            <rect x="24" y="4" width="16" height="4"></rect>
            <rect x="16" y="8" width="24" height="4"></rect>
            <rect x="12" y="12" width="20" height="4"></rect>
            <rect x="12" y="16" width="16" height="4"></rect>
            <rect x="8" y="20" width="16" height="12"></rect>
            <rect x="12" y="32" width="16" height="4"></rect>
            <rect x="12" y="36" width="20" height="4"></rect>
            <rect x="16" y="40" width="24" height="4"></rect>
            <rect x="24" y="44" width="16" height="4"></rect>
          </g>
        </svg>
        <svg class="icon-sun" viewBox="0 0 48 48" width="20" height="20" aria-hidden="true" focusable="false" shape-rendering="crispEdges">
          <g fill="currentColor">
            <rect x="16" y="16" width="16" height="16"></rect>
            <rect x="20" y="0" width="8" height="8"></rect>
            <rect x="20" y="40" width="8" height="8"></rect>
            <rect x="0" y="20" width="8" height="8"></rect>
            <rect x="40" y="20" width="8" height="8"></rect>
            <rect x="36" y="4" width="8" height="8"></rect>
            <rect x="4" y="4" width="8" height="8"></rect>
            <rect x="36" y="36" width="8" height="8"></rect>
            <rect x="4" y="36" width="8" height="8"></rect>
          </g>
        </svg>
      </button>
      <a class="btn btn-primary btn-sm hidden items-center sm:inline-flex" href="https://github.com/debanjanbasu/grr-cli" target="_blank" rel="noreferrer">
        GitHub <span aria-hidden="true">↗</span>
      </a>
      {/* DaisyUI opens this on :focus-within, so the trigger only has to be
          focusable. It was a <div role="button">: a fake button is announced
          as one but does nothing on Enter/Space, and the dropdown panel added
          its own tabindex, so keyboard users landed on a non-interactive <ul>.
          A real <button> is focusable and activatable natively, and the panel
          no longer needs a tab stop of its own. */}
      <div class="dropdown dropdown-end md:hidden" data-nav-disclosure>
        <button
          type="button"
          class="btn btn-ghost btn-square min-h-12 min-w-12"
          aria-label="Open the navigation menu"
          aria-haspopup="menu"
          aria-expanded="false"
          aria-controls="mobile-nav-menu"
          data-nav-toggle
        >
          <MenuIcon />
        </button>
        <nav id="mobile-nav-menu" aria-label="Mobile navigation">
          <ul class="dropdown-content menu z-50 mt-2 w-56 border border-base-300 bg-base-200 p-2 shadow-lg">
          {
            links.map((link) => (
              <li>
                <a class:list={['min-h-12', { 'menu-active': isActive(link.href) }]} href={link.href} aria-current={isActive(link.href) ? 'page' : undefined}>
                  {link.label}
                </a>
              </li>
            ))
          }
        </ul>
        </nav>
      </div>
    </div>
  </div>
</header>

{/*
  The mobile nav is a DaisyUI focus-within dropdown: focusing the trigger opens
  it, and it closes when focus leaves the widget. aria-expanded has to mirror
  that real state, so this delegated listener flips it on focus in and out of
  the disclosure. It runs once per document (guarded) and is attached to the
  document rather than the button, so the header ClientRouter swaps in after a
  client-side navigation keeps working without re-binding.
*/}
<script is:inline>
  (function () {
    if (window.__grrMobileNavBound) return;
    window.__grrMobileNavBound = true;
    var setExpanded = function (widget, open) {
      var toggle = widget.querySelector('[data-nav-toggle]');
      if (toggle) toggle.setAttribute('aria-expanded', String(open));
    };
    document.addEventListener('focusin', function (event) {
      var widget = event.target && event.target.closest ? event.target.closest('[data-nav-disclosure]') : null;
      if (widget) setExpanded(widget, true);
    });
    document.addEventListener('focusout', function (event) {
      var widget = event.target && event.target.closest ? event.target.closest('[data-nav-disclosure]') : null;
      if (widget && !widget.contains(event.relatedTarget)) setExpanded(widget, false);
    });
  })();
</script>