Skip to content
emty edited this page Jul 19, 2026 · 2 revisions
<title>Emtypyie.cli Wiki</title> <style> :root{ --header-h: 56px; --bar-blue: #4c5fd6; --bar-blue-dark: #3d4dc0; --sidebar-w: 260px; --toc-w: 240px; --bg: #ffffff; --text: #24292e; --text-muted: #57606a; --text-faint: #8b929c; --border: #e7e9ee; --link: #4c5fd6; --code-bg: #f4f5f9; --sidebar-active-bg: #eef0fb; --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; --font-pixel: "Press Start 2P", "Courier New", var(--font-mono); } html[data-theme="dark"]{ --bg: #0d1117; --text: #e6edf3; --text-muted: #9198a1; --text-faint: #6e7681; --border: #2a2f38; --link: #7d8fff; --code-bg: #161b22; --sidebar-active-bg: #1a1f2e; } *,*::before,*::after{ box-sizing:border-box; } html,body{ margin:0; padding:0; } body{ font-family: var(--font-body); color: var(--text); background: var(--bg); font-size: 15.5px; line-height: 1.7; transition: background 0.15s, color 0.15s; } a{ color: var(--link); text-decoration:none; } a:hover{ text-decoration: underline; } a:focus-visible, button:focus-visible, input:focus-visible, .contributor-card:focus-visible{ outline: 2px solid var(--bar-blue); outline-offset: 2px; border-radius: 3px; }

/* ── HEADER BAR ── */ header.topbar{ position: fixed; top:0; left:0; right:0; height: var(--header-h); background: var(--bar-blue); color:#fff; display:flex; align-items:center; gap:14px; padding: 0 20px; z-index: 200; } header.topbar a, header.topbar a:visited{ color: #fff; } header.topbar .brand{ display:flex; align-items:center; gap:10px; font-weight:600; font-size:16px; white-space:nowrap; } header.topbar .brand .brand-text, header.topbar .brand .brand-text a{ font-family: var(--font-pixel); font-size: 12px; letter-spacing: 0.02em; text-transform: uppercase; } header.topbar .brand .logo{ width:26px; height:26px; display:flex; align-items:center; justify-content:center; background: rgba(255,255,255,0.16); border-radius:6px; font-family: var(--font-mono); font-weight:700; font-size:13px; } .searchbox{ flex:1; max-width: 480px; display:flex; align-items:center; gap:8px; background: rgba(255,255,255,0.95); color:#333; border-radius: 20px; padding: 7px 14px; font-size: 13.5px; } .search-wrap{ position:relative; flex:1; max-width: 480px; } .search-results{ display:none; position:absolute; top: calc(100% + 6px); left:0; right:0; background: var(--bg); border:1px solid var(--border); border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.16); overflow:hidden; z-index: 300; max-height: 320px; overflow-y:auto; } .search-results.open{ display:block; } .search-result-item{ display:block; padding: 9px 14px; color: var(--text); font-size: 13.5px; border-bottom: 1px solid var(--border); cursor:pointer; } .search-result-item:last-child{ border-bottom:none; } .search-result-item:hover, .search-result-item.highlighted{ background: var(--code-bg); } .search-result-item .path{ display:block; font-size:11px; color: var(--text-faint); margin-top:2px; } .search-empty{ padding: 12px 14px; font-size: 13px; color: var(--text-faint); } .searchbox svg{ width:15px; height:15px; color:#7a7f8c; flex-shrink:0; } .searchbox input{ border:none; outline:none; background:none; flex:1; font-size:13.5px; color:#333; font-family:inherit; } .searchbox input::placeholder{ color:#9aa0ab; }

.site-cta{ display:inline-flex; align-items:center; gap:8px; background: var(--bar-blue); color:#fff; padding: 9px 18px; border-radius: 7px; font-size: 13.5px; font-weight:600; margin-bottom: 22px; transition: background 0.15s; } .site-cta:hover{ background: var(--bar-blue-dark); text-decoration:none; } .site-cta svg{ width:14px; height:14px; flex-shrink:0; } .topbar-right{ margin-left:auto; display:flex; align-items:center; gap:16px; white-space:nowrap; } .repo-chip{ display:flex; align-items:center; gap:8px; background: rgba(255,255,255,0.12); padding:5px 12px; border-radius: 6px; font-size:13px; } .repo-chip svg{ width:16px; height:16px; } .repo-stats{ display:flex; gap:10px; font-family: var(--font-mono); font-size:12px; opacity:0.9; } .repo-stats span{ display:flex; align-items:center; gap:4px; } .theme-toggle{ width:32px; height:32px; display:flex; align-items:center; justify-content:center; border-radius:6px; background: rgba(255,255,255,0.12); cursor:pointer; } .theme-toggle svg{ width:16px; height:16px; }

/* ── LAYOUT ── */ .layout{ display:flex; padding-top: var(--header-h); max-width: 1400px; margin: 0 auto; }

/* ── SIDEBAR ── */ aside.sidebar{ width: var(--sidebar-w); flex-shrink:0; padding: 28px 18px 40px 24px; position: sticky; top: var(--header-h); align-self: flex-start; height: calc(100vh - var(--header-h)); overflow-y:auto; } .side-title{ font-weight:700; font-size:15px; color:var(--text); margin-bottom: 14px; padding: 0 10px; } .side-nav{ list-style:none; margin:0; padding:0; } .side-nav li{ margin: 1px 0; } .side-nav a{ display:block; padding: 7px 10px; border-radius:6px; color: var(--text-muted); font-size: 14px; } .side-nav a:hover{ background: var(--code-bg); text-decoration:none; color:var(--text); } .side-nav a.active{ background: var(--sidebar-active-bg); color: var(--bar-blue); font-weight:600; text-decoration:none; } .side-nav .sub{ list-style:none; margin:2px 0 4px 14px; padding:0; border-left:1px solid var(--border); } .side-nav .sub a{ padding-left:16px; font-size:13.5px; } .side-caret{ display:flex; align-items:center; justify-content:space-between; } .side-caret svg{ width:11px; height:11px; opacity:0.6; transition: transform .15s; } .side-caret.open svg{ transform: rotate(90deg); }

/* ── MAIN CONTENT ── */ main.content{ flex:1; min-width: 0; padding: 40px 48px 80px; } main.content h1{ font-family: var(--font-pixel); font-size: 24px; font-weight:400; line-height: 1.5; margin: 0 0 26px; color: var(--text); letter-spacing: 0.01em; } main.content h2{ font-size: 22px; font-weight:700; margin: 40px 0 14px; padding-top: 6px; scroll-margin-top: 80px; } main.content h3{ font-size: 17px; font-weight:700; margin: 28px 0 10px; scroll-margin-top: 80px; } main.content p{ margin: 0 0 16px; color: var(--text); max-width: 760px; } main.content ul{ margin: 0 0 16px; padding-left: 22px; max-width: 760px; } main.content li{ margin-bottom:6px; } main.content strong{ font-weight:700; } code{ font-family: var(--font-mono); background: var(--code-bg); padding: 1px 6px; border-radius: 4px; font-size: 13px; color: #b3261e; } pre{ background: #292c34; color: #e3e6ee; padding: 16px 18px; border-radius: 6px; overflow-x:auto; margin: 4px 0 22px; font-size: 13px; line-height: 1.7; } pre code{ background:none; padding:0; color: inherit; } .tok-cmt{ color:#7f848e; font-style:italic; } html[data-theme="dark"] code{ color: #ff8177; }

table{ width:100%; border-collapse: collapse; margin: 8px 0 26px; max-width:760px; } @media (max-width: 600px){ table{ display:block; overflow-x:auto; white-space:nowrap; } } th,td{ text-align:left; padding: 9px 12px; border-bottom:1px solid var(--border); font-size:14px; } th{ color: var(--text-faint); font-weight:600; font-size:12.5px; text-transform:uppercase; letter-spacing:.03em; }

.notice{ display:flex; gap:12px; align-items:flex-start; background: #fdf3dc; border: 1px solid #f0dba6; border-radius: 8px; padding: 14px 16px; margin: 6px 0 24px; max-width: 760px; font-size: 13.5px; color: #6b4c00; } html[data-theme="dark"] .notice{ background: #2a2108; border-color: #4a3a10; color: #e8c979; } .notice .notice-icon{ flex-shrink:0; font-size:16px; line-height:1.3; } .notice strong{ color: inherit; }

.timeline{ list-style:none; margin: 4px 0 24px; padding:0; max-width: 760px; } .timeline li{ position:relative; padding: 0 0 20px 26px; border-left: 2px solid var(--border); margin-left: 4px; } .timeline li:last-child{ border-left-color: transparent; padding-bottom:0; } .timeline li::before{ content:''; position:absolute; left:-6px; top:2px; width:10px; height:10px; border-radius:50%; background: var(--bar-blue); border: 2px solid var(--bg); } .timeline .tl-tag{ font-family: var(--font-mono); font-size:11.5px; color: var(--bar-blue); display:block; margin-bottom:2px; } .timeline .tl-title{ font-weight:600; font-size:14.5px; color: var(--text); margin-bottom:3px; } .timeline .tl-desc{ font-size:13.5px; color: var(--text-muted); }

.contributor-card{ display:inline-flex; align-items:center; gap:10px; padding:8px 14px 8px 8px; border:1px solid var(--border); border-radius:8px; margin: 4px 8px 4px 0; transition: border-color 0.15s, transform 0.15s; } .contributor-card:hover{ border-color: var(--bar-blue); transform: translateY(-1px); } .contributor-pfp{ width:30px;height:30px;border-radius:50%; background:#eef0fb;color:var(--bar-blue); display:flex;align-items:center;justify-content:center; font-weight:700; font-size:13px; } .contributor-name{ font-weight:600; font-size:13.5px; } .contributor-role{ font-size:11.5px; color:var(--text-faint); display:block; }

.devlog{ border-left: 2px solid var(--border); padding: 4px 0 4px 20px; margin-bottom: 30px; position:relative; } .devlog::before{ content:''; position:absolute; left:-5px; top:8px; width:8px; height:8px; border-radius:50%; background: var(--bar-blue); } .devlog-meta{ font-family: var(--font-mono); font-size:11.5px; color:var(--text-faint); margin-bottom:4px; } .devlog-meta .v{ color: var(--bar-blue); } .devlog h3{ margin: 2px 0 8px; }

/* ── RIGHT SIDE PANEL ── */ aside.side-panel{ width: var(--toc-w); flex-shrink:0; padding: 40px 24px 40px 12px; position: sticky; top: var(--header-h); align-self: flex-start; height: calc(100vh - var(--header-h)); display:flex; flex-direction:column; gap:16px; } .panel-placeholder{ width:100%; flex: 1 1 0; min-height: 0; border: 1.5px dashed var(--border); border-radius: 10px; background: var(--code-bg); overflow: hidden; display:flex; align-items:center; justify-content:center; } .panel-placeholder img{ width:100%; height:100%; object-fit: cover; display:block; }

@media (max-width: 1100px){ aside.side-panel{ display:none; } } @media (max-width: 900px){ .search-wrap{ max-width: 260px; } .repo-chip span{ display:none; } .brand span[style]{ display:none; } } @media (max-width: 780px){ aside.sidebar{ display:none; } main.content{ padding: 28px 20px 60px; } .repo-stats{ display:none; } header.topbar{ gap:8px; padding: 0 12px; } .search-wrap{ max-width: none; } .site-cta{ width:100%; justify-content:center; } } @media (max-width: 480px){ .repo-chip{ display:none; } header.topbar .brand{ font-size:14px; } main.content h1{ font-size:18px; } .log-entry, .devlog{ padding-left:14px; } }

/* ── FOOTER ── */ footer.site-footer{ border-top: 1px solid var(--border); background: var(--code-bg); padding: 28px 48px 0; } .footer-inner{ max-width: 1400px; margin: 0 auto; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:16px; padding-bottom: 22px; } .footer-links{ display:flex; gap:20px; flex-wrap:wrap; font-size:13px; } .footer-links a{ color: var(--text-muted); } .footer-links a:hover{ color: var(--bar-blue); } .footer-note{ font-size:12.5px; color: var(--text-faint); } .footer-bottom{ border-top: 1px solid var(--border); padding: 14px 0; max-width: 1400px; margin: 0 auto; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px; font-size:12px; color: var(--text-faint); } .footer-bottom a{ color: var(--text-faint); } .footer-bottom a:hover{ color: var(--bar-blue); }

@media (max-width: 600px){ footer.site-footer{ padding: 24px 20px 0; } .footer-inner, .footer-bottom{ justify-content:center; text-align:center; } } </style>

wiki / emtypyie.cli
Emtypyie.cli
v··· ★ —
Emtypyie.cli Wiki
<h1 id="overview">Emtypyie.cli</h1>
<a class="site-cta" href="https://emtypyie.in/cli" target="_blank" rel="noopener">
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
  Visit main site — emtypyie.in/cli
</a>
<p style="margin-bottom:22px; margin-top:-8px;">
  For downloads and general project details. This wiki covers docs only.
</p>
<p>
  <strong>Emtypyie.cli</strong> is the package manager and terminal runtime for the
  EMTYPYIE ecosystem. Running <code>emtypyie</code> with no arguments prints a
  startup boot animation and drops you into an interactive shell where you can
  list, install, and manage projects pulled live from a CDN registry.
</p>
<p>
  The project has been maintained continuously since its alpha releases. It started
  as a Node.js-only tool distributed through npm; from <strong>v2.5.0 "Wandering
  Witches"</strong> onward it was rewritten from the ground up in C, and the C build
  is now the one going forward. See <a href="#history">History</a> below for the full timeline.
</p>

<div class="notice">
  <span class="notice-icon">⚠</span>
  <div>
    <strong>Node.js build is legacy.</strong> It's kept around for compatibility
    today, but support may be dropped in an upcoming release as the project
    continues to consolidate around the C build. If you're setting up fresh,
    prefer the C native install.
  </div>
</div>

<h3 id="history">History</h3>
<ul class="timeline">
  <li>
    <span class="tl-tag">Alpha</span>
    <div class="tl-title">Node.js-only, npm distribution</div>
    <div class="tl-desc">The original CLI was written entirely in Node.js and shipped only through npm.</div>
  </li>
  <li>
    <span class="tl-tag">v2.5.0 — "Wandering Witches"</span>
    <div class="tl-title">Major release: rewritten in C</div>
    <div class="tl-desc">The first major update. The core was rewritten from scratch in C11/C++17 as a dependency-free native binary. The Node.js build was kept alongside it for compatibility.</div>
  </li>
  <li>
    <span class="tl-tag">Since v2.5.0</span>
    <div class="tl-title">C is the primary path</div>
    <div class="tl-desc">All new features — Larpino, bakafetch, themes — land in the C build first. The Node.js build is maintained but is not guaranteed long-term.</div>
  </li>
  <li>
    <span class="tl-tag">Upcoming — v3.0.0</span>
    <div class="tl-title">Next major update</div>
    <div class="tl-desc">The next major release. Expected to bring the GUI online alongside the terminal runtime.</div>
  </li>
</ul>

<h3>Core features</h3>
<ul>
  <li>Interactive shell with a startup boot animation (skippable via flag or env var)</li>
  <li>Direct one-off commands without entering the shell, e.g. <code>emtypyie /list</code></li>
  <li>Live CDN-backed project registry — install, inspect, and update anything listed</li>
  <li>Auto-installer for common toolchains, e.g. <code>/get gcc</code> for GCC/G++</li>
  <li><strong>Larpino</strong> — a built-in LLAMA inference engine written directly into the C core</li>
  <li><code>/bf</code> — a neofetch-style system info screen ("bakafetch") with braille/Unicode art</li>
  <li>Five color themes: <code>slate</code>, <code>green</code>, <code>amber</code>, <code>violet</code>, <code>cyan</code></li>
</ul>

<h2 id="install">Install &amp; Launch</h2>
<p>The C native build is the recommended path — it's what current and future development targets. The Node.js build still works but is legacy; treat it as a fallback, not the default.</p>

<h3 id="install-windows">Windows (Winget / Chocolatey) — C native build, recommended</h3>
<pre><code><span class="tok-cmt"># Winget</span>

winget install myrachane.emtypyie-cli

# Chocolatey choco install emtypyie-cli

<h3 id="install-npm">npm (all platforms) — Node.js build, legacy</h3>
<pre><code>npm install -g emtypyie-cli</code></pre>
<p>This is the original distribution path from the project's alpha days. It still receives updates, but new features land in the C build first — and support here may end in a future release.</p>

<h3 id="install-launch">Launching the shell</h3>
<p>Once installed, <code>emtypyie</code> is on your <code>PATH</code>. Run it with no arguments to boot the interactive shell:</p>
<pre><code>emtypyie

# skip the boot animation emtypyie --no-animation # or set EMTYPYIE_NO_ANIM=1

Direct commands also work without ever entering the shell, e.g. emtypyie /list or emtypyie /get gcc.

<h2 id="commands">Commands</h2>
<table>
  <tr><th>Command</th><th>Description</th></tr>
  <tr><td><code>/help</code></td><td>Show help</td></tr>
  <tr><td><code>/list</code></td><td>List available projects from the CDN</td></tr>
  <tr><td><code>/get &lt;project&gt;</code></td><td>Install a project</td></tr>
  <tr><td><code>/get gcc</code></td><td>Auto-install GCC/G++ compiler</td></tr>
  <tr><td><code>/get larpino@1b</code></td><td>Download a LLAMA GGUF model and load it</td></tr>
  <tr><td><code>/info &lt;project&gt;</code></td><td>Show project details</td></tr>
  <tr><td><code>/flash &lt;project&gt;</code></td><td>Re-download the latest version</td></tr>
  <tr><td><code>/rm &lt;project&gt;</code></td><td>Remove a project</td></tr>
  <tr><td><code>/theme &lt;name&gt;</code></td><td>Change the color theme</td></tr>
  <tr><td><code>/bf</code></td><td>System info screen (bakafetch)</td></tr>
  <tr><td><code>/docs &lt;project&gt;</code></td><td>Open project docs</td></tr>
  <tr><td><code>/shell</code></td><td>Enter interactive mode</td></tr>
  <tr><td><code>/larpino enable|disable|status</code></td><td>Control the LLAMA inference engine</td></tr>
  <tr><td><code>/clear</code></td><td>Clear the screen</td></tr>
</table>

<h2 id="architecture">Architecture</h2>
<p>
  The C CLI (<code>Root4c</code>) is the primary build going forward — a single
  portable binary written in C11/C++17 with no runtime dependencies. It's what
  Larpino and bakafetch are built directly into, and where all new development
  happens. The Node.js CLI (<code>Root4node</code>) is the original alpha-era
  codebase, still published to npm, but is now considered legacy.
</p>
<table>
  <tr><th>Path</th><th>Description</th></tr>
  <tr><td><code>archive/vX.Y.Z/Root4c/</code></td><td>C CLI — portable single binary (primary)</td></tr>
  <tr><td><code>archive/vX.Y.Z/Root4node/</code></td><td>Node.js CLI — published to npm</td></tr>
  <tr><td><code>mainsite/</code></td><td>Website landing pages</td></tr>
  <tr><td><code>manifests/</code></td><td>Winget package manifests</td></tr>
  <tr><td><code>choco/</code></td><td>Chocolatey package</td></tr>
</table>

<h3 id="larpino">Larpino inference engine</h3>
<p><strong>Larpino</strong>, the built-in inference engine, handles:</p>
<ul>
  <li>GGUF-format LLAMA models — <code>Q4_0</code>, <code>Q4_1</code>, <code>Q5_0</code>, <code>Q5_1</code>, <code>Q8_0</code>, <code>F16</code>, <code>F32</code></li>
  <li>BPE tokenization with merge rules</li>
  <li>A KV-cached transformer (RoPE, SwiGLU, RMSNorm, grouped-query attention)</li>
  <li>Top-k / temperature sampling</li>
</ul>
<p>
  Project listings and metadata for <code>/list</code>, <code>/get</code>, and
  <code>/info</code> are fetched live from <code>cdn.emtypyie.in/dev</code> — the
  registry is never bundled with the binary.
</p>
<p><strong>Build from source:</strong></p>
<pre><code>cmake -B build

cmake --build build

<h3 id="releases">Release artifacts</h3>
<p>Each GitHub release ships two Windows executables:</p>
<table>
  <tr><th>ZIP</th><th>Contents</th><th>Source</th></tr>
  <tr><td><code>emtypyie-cli-windows-x64-2.5.8.zip</code></td><td><code>emtypyie.exe</code></td><td>Node.js (pkg)</td></tr>
  <tr><td><code>emtypyie-cli-native-windows-x64-2.5.8.zip</code></td><td><code>emtypyie.exe</code></td><td>C native build</td></tr>
</table>

<h2 id="devlogs">Dev Logs</h2>

<div class="devlog">
  <div class="devlog-meta">2026-07-19 · log #4 · <span class="v">v2.5.8</span></div>
  <h3>v2.5.8 — latest release</h3>
  <p>Latest tag off <code>main</code>. The C native build remains the primary path,
    and Larpino is stable enough to lean on as the default inference path for
    <code>/get larpino@1b</code>. Early work on a GUI has started, targeting the
    next major release, <strong>v3.0.0</strong>.</p>
</div>

<div class="devlog">
  <div class="devlog-meta">log #3 · <span class="v">Larpino + bakafetch</span></div>
  <h3>Larpino inference engine and bakafetch land in the C core</h3>
  <p>Wrote the LLAMA inference path directly into the C11/C++17 core instead of
    shelling out to an external runtime — GGUF loading, a BPE tokenizer with
    merge rules, a KV-cached transformer (RoPE, SwiGLU, RMSNorm, grouped-query
    attention), and top-k/temperature sampling all live in <code>Root4c</code>.
    <code>/larpino enable</code> drops straight into chat mode inside the shell.</p>
  <p>Alongside it, <code>/bf</code> shipped as a neofetch-style "bakafetch" system
    info screen with Unicode/braille art, and five color themes
    (<code>slate</code>, <code>green</code>, <code>amber</code>, <code>violet</code>,
    <code>cyan</code>) became switchable at runtime via <code>/theme</code>.</p>
</div>

<div class="devlog">
  <div class="devlog-meta">log #2 · <span class="v">v2.5.0 — "Wandering Witches"</span></div>
  <h3>Major release: rewritten in C</h3>
  <p>The first major update. The core was rewritten from scratch in C11/C++17 as
    <code>Root4c</code>, a single dependency-free native binary distributed via
    Winget and Chocolatey on Windows. The original Node.js codebase was kept as
    <code>Root4node</code>, still published to npm, but stopped being where new
    work happens.</p>
  <p>The CDN-backed registry for <code>/list</code> / <code>/get</code> / <code>/info</code>
    carried over unchanged, fetching live from <code>cdn.emtypyie.in/dev</code>
    rather than bundling project metadata into either binary.</p>
</div>

<div class="devlog">
  <div class="devlog-meta">log #1 · <span class="v">Alpha</span></div>
  <h3>Node.js-only, npm distribution</h3>
  <p>The project started life as a Node.js CLI, installed with <code>npm install -g</code>
    and with no native build to speak of. This is the codebase that would later
    become <code>Root4node</code> once the C rewrite began.</p>
</div>

<h2 id="contributors">Contributors</h2>
<div class="contributor-card">
  <img
    class="contributor-pfp"
    src="https://github.com/myrachane.png"
    alt="myrachane"
    width="30"
    height="30"
    style="object-fit:cover;font-size:0;"
    onerror="this.outerHTML = '<div class=&quot;contributor-pfp&quot;>M</div>'"
  />
  <div>
    <span class="contributor-name">myrachane</span>
    <span class="contributor-role">creator &amp; maintainer</span>
  </div>
</div>
<p><a href="https://github.com/myrachane/Emtypyie.cli" target="_blank" rel="noopener">See more on github.com/myrachane/Emtypyie.cli →</a></p>
monga
marin kitagawa blush
© 2026 Emtypyie. All rights reserved. Maintained by myrachane
<script> /* ── COPYRIGHT YEAR ── */ const copyYearEl = document.getElementById('copyYear'); if (copyYearEl) copyYearEl.textContent = new Date().getFullYear(); /* ── DISABLE RIGHT CLICK ── */ document.addEventListener('contextmenu', (e) => e.preventDefault()); /* ── THEME TOGGLE ── */ const htmlEl = document.documentElement; const themeToggle = document.getElementById('themeToggle'); const themeIcon = document.getElementById('themeIcon'); const MOON_SVG = ''; const SUN_SVG = ''; function applyTheme(mode){ htmlEl.setAttribute('data-theme', mode); themeIcon.innerHTML = mode === 'dark' ? MOON_SVG : SUN_SVG; themeIcon.setAttribute('fill', mode === 'dark' ? 'currentColor' : 'none'); } applyTheme('dark'); themeToggle.addEventListener('click', () => { const current = htmlEl.getAttribute('data-theme'); applyTheme(current === 'dark' ? 'light' : 'dark'); }); /* ── AUTOFETCH LATEST RELEASE + STAR COUNT FROM GITHUB ── */ const REPO = 'myrachane/Emtypyie.cli'; const versionEl = document.getElementById('releaseVersion'); const starEl = document.getElementById('starCount'); fetch(`https://api.github.com/repos/${REPO}/releases/latest`) .then(r => { if (!r.ok) throw new Error('no releases'); return r.json(); }) .then(data => { const tag = data.tag_name || data.name; if (tag) versionEl.textContent = tag.startsWith('v') ? tag : `v${tag}`; }) .catch(() => { versionEl.textContent = 'v2.5.8'; }); fetch(`https://api.github.com/repos/${REPO}`) .then(r => { if (!r.ok) throw new Error('no repo data'); return r.json(); }) .then(data => { const stars = data.stargazers_count; if (typeof stars === 'number') starEl.textContent = `★ ${stars}`; }) .catch(() => { starEl.textContent = '★ —'; }); /* ── SEARCH ── */ const searchInput = document.getElementById('searchInput'); const searchResults = document.getElementById('searchResults'); // Build a search index from every section/heading in the main content. const searchIndex = Array.from(document.querySelectorAll('main.content [id]')).map(el => { const heading = el.tagName.match(/^H[1-3]$/) ? el : el.querySelector('h1, h2, h3'); const title = (heading ? heading.textContent : el.id).replace(/\s+/g, ' ').trim(); // Grab the text of this block up to (but not including) the next heading, for context matching. let node = el.nextElementSibling; let bodyText = ''; while (node && !node.matches('h1, h2, h3')) { bodyText += ' ' + node.textContent; node = node.nextElementSibling; } return { id: el.id, title, text: (title + ' ' + bodyText).toLowerCase() }; }).filter(item => item.title); let activeIndex = -1; function renderResults(matches, query){ searchResults.innerHTML = ''; if (!query) { searchResults.classList.remove('open'); return; } if (matches.length === 0) { searchResults.innerHTML = '
No matches for "' + query + '"
'; searchResults.classList.add('open'); return; } matches.slice(0, 8).forEach((m, i) => { const item = document.createElement('div'); item.className = 'search-result-item' + (i === activeIndex ? ' highlighted' : ''); item.innerHTML = m.title + '#' + m.id + ''; item.addEventListener('click', () => goToResult(m.id)); searchResults.appendChild(item); }); searchResults.classList.add('open'); } function goToResult(id){ const target = document.getElementById(id); if (target) { window.scrollTo({ top: target.getBoundingClientRect().top + window.scrollY - 70, behavior: 'smooth' }); } searchResults.classList.remove('open'); searchInput.value = ''; searchInput.blur(); } let currentMatches = []; searchInput.addEventListener('input', () => { const query = searchInput.value.trim().toLowerCase(); activeIndex = -1; currentMatches = query ? searchIndex.filter(item => item.text.includes(query)) : []; renderResults(currentMatches, searchInput.value.trim()); }); searchInput.addEventListener('keydown', (e) => { if (!currentMatches.length) return; if (e.key === 'ArrowDown') { e.preventDefault(); activeIndex = Math.min(activeIndex + 1, Math.min(currentMatches.length, 8) - 1); renderResults(currentMatches, searchInput.value.trim()); } else if (e.key === 'ArrowUp') { e.preventDefault(); activeIndex = Math.max(activeIndex - 1, 0); renderResults(currentMatches, searchInput.value.trim()); } else if (e.key === 'Enter') { e.preventDefault(); const pick = currentMatches[activeIndex >= 0 ? activeIndex : 0]; if (pick) goToResult(pick.id); } else if (e.key === 'Escape') { searchResults.classList.remove('open'); searchInput.blur(); } }); document.addEventListener('click', (e) => { if (!e.target.closest('.search-wrap')) searchResults.classList.remove('open'); }); // Highlight active sidebar link on scroll const headings = document.querySelectorAll('main.content h1[id], main.content h2[id], main.content h3[id]'); const sideLinks = document.querySelectorAll('aside.sidebar a'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.id; sideLinks.forEach(l => l.classList.toggle('active', l.getAttribute('href') === '#' + id)); } }); }, { rootMargin: '-80px 0px -70% 0px' }); headings.forEach(h => observer.observe(h)); document.querySelectorAll('a[href^="#"]').forEach(link => { link.addEventListener('click', (e) => { const id = link.getAttribute('href').slice(1); const target = document.getElementById(id); if (target) { e.preventDefault(); window.scrollTo({ top: target.getBoundingClientRect().top + window.scrollY - 70, behavior: 'smooth' }); } }); }); </script>

Clone this wiki locally