Skip to content

Commit 06d1dfe

Browse files
committed
refactor: update navigation and terminal components across documentation
- Replaced checkbox-based navigation toggle with button for improved accessibility and usability in FAQ, Install, Keyboard, and Privacy pages. - Added JavaScript for mobile navigation toggle functionality. - Updated terminal command displays to use custom <hfo-terminal> component for consistency and improved readability. - Included necessary scripts for navigation and terminal functionality in relevant HTML files.
1 parent 0cb8dfc commit 06d1dfe

12 files changed

Lines changed: 310 additions & 376 deletions

File tree

.github/workflows/release.yml

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
name: Release
22

3+
# This workflow ONLY fires when a version tag (v*.*.*) is pushed. It does
4+
# NOT run on pushes to main, PRs, or any other branch activity. The
5+
# `if: startsWith(github.ref, 'refs/tags/v')` guard on every job makes it
6+
# impossible to trigger binary uploads from a non-tag ref, even via a
7+
# manual `workflow_dispatch`.
8+
#
9+
# npm publish is NOT automated here — the maintainer runs `npm publish`
10+
# locally after the tag is pushed. Rationale: publishing is infrequent,
11+
# requires a human-held token, and the additional CI step added little
12+
# value over a local run that also verifies the tarball by eye.
313
on:
414
push:
515
tags: ['v*.*.*']
@@ -10,36 +20,13 @@ permissions:
1020

1121
jobs:
1222
# -----------------------------------------------------------------------
13-
# Primary distribution channel. npm publish runs on Linux only; the
14-
# published package is cross-OS (pure Node + @pkg of ink).
15-
# -----------------------------------------------------------------------
16-
npm-publish:
17-
runs-on: ubuntu-latest
18-
steps:
19-
- uses: actions/checkout@v4
20-
- uses: pnpm/action-setup@v4
21-
- uses: actions/setup-node@v4
22-
with:
23-
node-version: 22
24-
cache: pnpm
25-
registry-url: https://registry.npmjs.org
26-
- run: pnpm install --frozen-lockfile
27-
- run: pnpm typecheck
28-
- run: pnpm lint
29-
- run: pnpm test
30-
- run: pnpm build
31-
- run: npm publish --access public
32-
env:
33-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
34-
35-
# -----------------------------------------------------------------------
36-
# Secondary distribution: standalone binaries per OS. Uses @yao-pkg/pkg
37-
# which has known rough edges with ESM-first deps (ink, execa). The job
38-
# is `continue-on-error: true` so a partial failure here never blocks the
39-
# npm release — users can always fall back to `npm i -g hfo-cli`.
23+
# Standalone binaries per OS. Uses @yao-pkg/pkg which has known rough
24+
# edges with ESM-first deps (ink, execa). The job is
25+
# `continue-on-error: true` so a partial failure here never blocks the
26+
# release — users can always fall back to `npm i -g hfo-cli`.
4027
# -----------------------------------------------------------------------
4128
binary:
42-
needs: npm-publish
29+
if: startsWith(github.ref, 'refs/tags/v')
4330
continue-on-error: true
4431
strategy:
4532
fail-fast: false

docs/404.html

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
1818
<link rel="icon" href="/assets/logo-mark.svg" type="image/svg+xml" />
1919
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
20+
<script src="/assets/terminal.js" defer></script>
2021

2122
<style>
2223
:root {
@@ -136,16 +137,10 @@ <h1>Page not found</h1>
136137
<a href="https://github.com/carrilloapps/hfo" rel="noreferrer noopener">GitHub<small>Source &amp; releases</small></a>
137138
</nav>
138139

139-
<div class="terminal">
140-
<div class="terminal-bar">
141-
<span class="dot red" aria-hidden="true"></span>
142-
<span class="dot yellow" aria-hidden="true"></span>
143-
<span class="dot green" aria-hidden="true"></span>
144-
<span style="margin-left:auto;margin-right:auto;">bash</span>
145-
</div>
146-
<pre><code><span class="tok-comment"># ...or just install it and carry on:</span>
147-
<span class="tok-prompt">$</span> <span class="tok-cmd">curl</span> -fsSL https://hfo.carrillo.app/install.sh | <span class="tok-cmd">sh</span></code></pre>
148-
</div>
140+
<hfo-terminal title="bash">
141+
<span class="tok-comment"># ...or just install it and carry on:</span>
142+
<span class="tok-prompt">$</span> <span class="tok-cmd">curl</span> -fsSL https://hfo.carrillo.app/install.sh | <span class="tok-cmd">sh</span>
143+
</hfo-terminal>
149144
</main>
150145

151146
</body>

docs/assets/nav.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Minimal nav-toggle + one-shot copy-button handler for hfo sub-pages.
3+
* Shared across /install, /cli, /keyboard, /privacy, /faq, /benchmarks.
4+
*
5+
* Nav toggle
6+
* • Mobile-only (desktop hides .nav-toggle via CSS).
7+
* • Uses a proper <button> so no ghost form controls render on desktop.
8+
* • Flips aria-expanded and toggles .is-open on the <nav>.
9+
* • Closes on outside-click and Escape.
10+
*
11+
* Copy buttons
12+
* • Any element with [data-copy] inside the page copies its attribute
13+
* to the clipboard on click and flashes "copied" for 1.2 s. Works
14+
* for the legacy .terminal-copy button and the new <hfo-terminal>.
15+
*/
16+
17+
(function wireNav() {
18+
const btn = document.getElementById('nav-toggle');
19+
const nav = document.getElementById('site-nav');
20+
if (!btn || !nav) return;
21+
22+
const close = () => {
23+
btn.setAttribute('aria-expanded', 'false');
24+
nav.classList.remove('is-open');
25+
};
26+
const open = () => {
27+
btn.setAttribute('aria-expanded', 'true');
28+
nav.classList.add('is-open');
29+
};
30+
31+
btn.addEventListener('click', () => {
32+
const isOpen = btn.getAttribute('aria-expanded') === 'true';
33+
isOpen ? close() : open();
34+
});
35+
document.addEventListener('click', (e) => {
36+
if (btn.contains(e.target) || nav.contains(e.target)) return;
37+
close();
38+
});
39+
document.addEventListener('keydown', (e) => {
40+
if (e.key === 'Escape') close();
41+
});
42+
})();
43+
44+
(function wireCopy() {
45+
document.addEventListener('click', (e) => {
46+
const btn = e.target.closest('[data-copy]');
47+
if (!btn) return;
48+
const text = btn.getAttribute('data-copy');
49+
if (!text || !navigator.clipboard) return;
50+
navigator.clipboard.writeText(text).then(() => {
51+
const prev = btn.textContent;
52+
const prevColor = btn.style.color;
53+
btn.textContent = 'copied';
54+
btn.style.color = 'var(--success)';
55+
setTimeout(() => { btn.textContent = prev; btn.style.color = prevColor; }, 1200);
56+
});
57+
});
58+
})();

docs/assets/page.css

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ pre {
8686
}
8787
pre code { background: none; border: 0; padding: 0; font-size: inherit; }
8888

89-
.container { max-width: 980px; margin: 0 auto; padding: 0 clamp(18px, 4vw, 36px); }
89+
/* Container width matches the landing's 1120px cap so every page feels
90+
part of the same product. Padding clamps identically. */
91+
.container { max-width: 1120px; margin: 0 auto; padding: 0 clamp(18px, 4vw, 36px); }
9092
.skip-link {
9193
position: absolute; top: -40px; left: 12px;
9294
background: var(--primary); color: var(--bg);
@@ -117,19 +119,21 @@ pre code { background: none; border: 0; padding: 0; font-size: inherit; }
117119
.brand:hover { text-decoration: none; color: var(--primary); }
118120
.brand svg { width: 28px; height: 28px; }
119121

120-
.nav-toggle-cb { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
121-
@media (min-width: 821px) { .nav-toggle-cb { display: none; } }
122+
/* Nav toggle — real <button>, not a hidden-checkbox hack. Hidden on
123+
desktop (display: none) so nothing leaks into the layout. JS in
124+
/assets/nav.js flips aria-expanded + toggles .is-open on .site-nav. */
122125
.nav-toggle {
123126
display: none; cursor: pointer; padding: 8px;
124127
border-radius: var(--radius-sm);
125128
color: var(--fg); background: transparent;
126129
border: 1px solid var(--border);
127130
align-items: center; justify-content: center;
128-
transition: border-color .15s ease, background .15s ease;
131+
transition: border-color .15s ease, background .15s ease, color .15s ease;
129132
margin-left: auto;
130133
}
131134
.nav-toggle svg { width: 22px; height: 22px; display: block; }
132135
.nav-toggle:hover { border-color: var(--border-strong); background: var(--bg-alt); }
136+
.nav-toggle[aria-expanded="true"] { border-color: var(--border-strong); background: var(--bg-alt); color: var(--primary); }
133137

134138
.site-nav { margin-left: auto; display: flex; gap: 4px; align-items: center; }
135139
.site-nav a {
@@ -156,11 +160,7 @@ pre code { background: none; border: 0; padding: 0; font-size: inherit; }
156160
border-radius: var(--radius-md);
157161
padding: 8px; box-shadow: var(--shadow); z-index: 20;
158162
}
159-
.nav-toggle-cb:checked ~ .site-nav { display: flex; }
160-
.nav-toggle-cb:checked ~ .nav-toggle {
161-
border-color: var(--border-strong);
162-
background: var(--bg-alt); color: var(--primary);
163-
}
163+
.site-nav.is-open { display: flex; }
164164
.site-nav a { padding: 10px 12px; font-size: 15px; }
165165
.nav-icon-link { justify-content: flex-start; }
166166
}

docs/assets/terminal.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* <hfo-terminal> — a single shared Web Component that renders the
3+
* terminal-styled code block used across every page on hfo.carrillo.app
4+
* (except the hero, which has its own one-of-a-kind markup).
5+
*
6+
* Author writes:
7+
* <hfo-terminal title="bash" copy="npm i -g hfo-cli">
8+
* <span class="tok-prompt">$</span> <span class="tok-cmd">npm</span> i -g hfo-cli
9+
* </hfo-terminal>
10+
*
11+
* The element upgrades on connect to the full .terminal structure:
12+
* <div class="terminal">
13+
* <div class="terminal-bar">…red/yellow/green dots, title, optional copy…</div>
14+
* <pre><code>…content…</code></pre>
15+
* </div>
16+
*
17+
* Rules:
18+
* • `title` is plain text, HTML-escaped before insertion.
19+
* • `copy` (optional) adds a copy button with data-copy; the copy
20+
* handler lives in nav.js so both legacy `.terminal-copy` buttons
21+
* and the ones inside this component share one listener.
22+
* • The inner HTML is preserved verbatim (including tok-* spans).
23+
* • Leading/trailing newlines inside the element are trimmed so the
24+
* rendered <pre> doesn't start with a blank line.
25+
* • Double-connection is guarded via [data-hydrated].
26+
*/
27+
28+
class HfoTerminal extends HTMLElement {
29+
connectedCallback() {
30+
if (this.dataset.hydrated === 'true') return;
31+
this.dataset.hydrated = 'true';
32+
33+
const title = this.getAttribute('title') || '';
34+
const copy = this.getAttribute('copy');
35+
36+
const content = this.innerHTML
37+
.replace(/^[ \t]*\r?\n+/, '') // drop leading newlines + their indent
38+
.replace(/\r?\n[ \t]*$/, ''); // drop trailing newlines + their indent
39+
40+
const copyBtn = copy
41+
? `<button type="button" class="terminal-copy" data-copy="${escapeAttr(copy)}" aria-label="Copy command">copy</button>`
42+
: '';
43+
44+
this.innerHTML =
45+
'<div class="terminal">' +
46+
'<div class="terminal-bar">' +
47+
'<span class="terminal-dot red" aria-hidden="true"></span>' +
48+
'<span class="terminal-dot yellow" aria-hidden="true"></span>' +
49+
'<span class="terminal-dot green" aria-hidden="true"></span>' +
50+
'<span class="terminal-title">' + escapeText(title) + '</span>' +
51+
copyBtn +
52+
'</div>' +
53+
'<pre><code>' + content + '</code></pre>' +
54+
'</div>';
55+
}
56+
}
57+
58+
function escapeText(s) {
59+
return String(s).replace(/[&<>"]/g, (c) => ({
60+
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;',
61+
}[c]));
62+
}
63+
function escapeAttr(s) {
64+
return String(s).replace(/"/g, '&quot;').replace(/&/g, '&amp;');
65+
}
66+
67+
customElements.define('hfo-terminal', HfoTerminal);

docs/benchmarks/index.html

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
</script>
4141

4242
<link rel="stylesheet" href="/assets/page.css" />
43+
<script src="/assets/nav.js" defer></script>
44+
<script src="/assets/terminal.js" defer></script>
4345
</head>
4446
<body>
4547

@@ -58,11 +60,10 @@
5860
</svg>
5961
<span>hfo</span>
6062
</a>
61-
<input type="checkbox" id="nav-open" class="nav-toggle-cb" aria-label="Toggle navigation menu" />
62-
<label for="nav-open" class="nav-toggle" aria-hidden="true">
63+
<button type="button" class="nav-toggle" id="nav-toggle" aria-label="Open navigation" aria-expanded="false" aria-controls="site-nav">
6364
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/></svg>
64-
</label>
65-
<nav class="site-nav" aria-label="Primary">
65+
</button>
66+
<nav class="site-nav" id="site-nav" aria-label="Primary">
6667
<a href="/install/">Install</a>
6768
<a href="/cli/">CLI</a>
6869
<a href="/keyboard/">Keyboard</a>
@@ -94,20 +95,13 @@ <h1>Benchmarks</h1>
9495
</header>
9596

9697
<h2>Run it</h2>
97-
<div class="terminal">
98-
<div class="terminal-bar">
99-
<span class="terminal-dot red" aria-hidden="true"></span>
100-
<span class="terminal-dot yellow" aria-hidden="true"></span>
101-
<span class="terminal-dot green" aria-hidden="true"></span>
102-
<span class="terminal-title">bash</span>
103-
<button type="button" class="terminal-copy" data-copy="hfo --bench llama3.1:8b --out bench.json">copy</button>
104-
</div>
105-
<pre><code><span class="tok-comment"># Quick run — prints per-prompt + aggregate tok/s and TTFT, nothing saved</span>
98+
<hfo-terminal title="bash" copy="hfo --bench llama3.1:8b --out bench.json">
99+
<span class="tok-comment"># Quick run — prints per-prompt + aggregate tok/s and TTFT, nothing saved</span>
106100
<span class="tok-prompt">$</span> <span class="tok-cmd">hfo</span> <span class="tok-flag">--bench</span> llama3.1:8b
107101

108102
<span class="tok-comment"># Full submission — writes a JSON file you can share</span>
109-
<span class="tok-prompt">$</span> <span class="tok-cmd">hfo</span> <span class="tok-flag">--bench</span> llama3.1:8b <span class="tok-flag">--out</span> <span class="tok-path">bench.json</span></code></pre>
110-
</div>
103+
<span class="tok-prompt">$</span> <span class="tok-cmd">hfo</span> <span class="tok-flag">--bench</span> llama3.1:8b <span class="tok-flag">--out</span> <span class="tok-path">bench.json</span>
104+
</hfo-terminal>
111105

112106
<div class="callout callout-info">
113107
<p><strong>Expected runtime</strong>: 15–60 seconds on consumer GPU, 1–3 minutes on CPU-only. The warmup prompt is excluded from the aggregate so cold-start latency doesn't skew the numbers.</p>
@@ -137,14 +131,8 @@ <h2>What the bench measures</h2>
137131
</ul>
138132

139133
<h2>Sample output</h2>
140-
<div class="terminal">
141-
<div class="terminal-bar">
142-
<span class="terminal-dot red" aria-hidden="true"></span>
143-
<span class="terminal-dot yellow" aria-hidden="true"></span>
144-
<span class="terminal-dot green" aria-hidden="true"></span>
145-
<span class="terminal-title">bash</span>
146-
</div>
147-
<pre><code>── Benchmarking qwen2.5-coder:7b-q5_k_m ────────────────────────────────────
134+
<hfo-terminal title="bash">
135+
── Benchmarking qwen2.5-coder:7b-q5_k_m ────────────────────────────────────
148136
Model qwen2.5-coder:7b-q5_k_m
149137
GPU NVIDIA GeForce RTX 4090
150138
VRAM 24 GB
@@ -160,20 +148,14 @@ <h2>Sample output</h2>
160148
── Aggregate ───────────────────────────────────────────────────────────────
161149
80.1 tok/s avg
162150
120 ms TTFT avg
163-
460 total output tokens in 5.7 s</code></pre>
164-
</div>
151+
460 total output tokens in 5.7 s
152+
</hfo-terminal>
165153

166154
<h2>Submission format</h2>
167155
<p>When you pass <code>--out &lt;file&gt;</code>, hfo writes a JSON document that follows the <code>hfo-bench-v1</code> schema:</p>
168156

169-
<div class="terminal">
170-
<div class="terminal-bar">
171-
<span class="terminal-dot red" aria-hidden="true"></span>
172-
<span class="terminal-dot yellow" aria-hidden="true"></span>
173-
<span class="terminal-dot green" aria-hidden="true"></span>
174-
<span class="terminal-title">bench.json</span>
175-
</div>
176-
<pre><code>{
157+
<hfo-terminal title="bench.json">
158+
{
177159
"tag": "qwen2.5-coder:7b-q5_k_m",
178160
"ollamaVersion": "0.21.3",
179161
"hfoVersion": "0.1.0",
@@ -205,8 +187,8 @@ <h2>Submission format</h2>
205187
},
206188
"submittedAt": "2026-04-23T20:14:32.114Z",
207189
"schema": "hfo-bench-v1"
208-
}</code></pre>
209-
</div>
190+
}
191+
</hfo-terminal>
210192

211193
<h2>Contribute a submission</h2>
212194
<p>The leaderboard is PR-based — no server, no telemetry, no account required. To contribute:</p>
@@ -259,20 +241,5 @@ <h2>What the benchmarks don't tell you</h2>
259241
</div>
260242
</footer>
261243

262-
<script>
263-
document.addEventListener('click', (e) => {
264-
const btn = e.target.closest('.terminal-copy');
265-
if (!btn) return;
266-
const text = btn.getAttribute('data-copy');
267-
if (!text || !navigator.clipboard) return;
268-
navigator.clipboard.writeText(text).then(() => {
269-
const prev = btn.textContent;
270-
btn.textContent = 'copied';
271-
btn.style.color = 'var(--success)';
272-
setTimeout(() => { btn.textContent = prev; btn.style.color = ''; }, 1200);
273-
});
274-
});
275-
</script>
276-
277244
</body>
278245
</html>

0 commit comments

Comments
 (0)