Skip to content

Commit f0140b2

Browse files
committed
docs: revamp landing page, add interactive playground, update README
Landing page (index.html): - Fix incorrect create() parameter names and hero description - Replace text-only cards with visual encode/decode infographic using 18-decimal ERC20 example (1e18 wei >> 32) - Add "Ask an AI" section with copy-and-open buttons for Claude, ChatGPT, Gemini, Perplexity - Add missing v7.1.0 API entries (requireAligned, requireMinStep) - Widen layout from 960px to 1200px Playground (playground.html): - Interactive scheme builder with sliders for discardedBitWidth and encodedBitWidth - 256-bit zone bar visualization, live encode/decode results with precision analysis and decode range bar - 5 presets from the showcase contracts - Live Solidity snippet and copyable prompt output - Concept intro with dismissible explanation README: - Add requireAligned(), requireMinStep() to API table - Add BelowMinStep error to Errors section
1 parent 649666b commit f0140b2

File tree

3 files changed

+1127
-925
lines changed

3 files changed

+1127
-925
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ The `Quant` value type is a `uint16` with the following bit layout:
6161
| `q.ceil(value)` | Rounds `value` up to the nearest step boundary. Reverts with `CeilOverflow` when rounding up would exceed `type(uint256).max`. |
6262
| `q.remainder(value)` | Resolution lost if `value` were floor-encoded (`value mod stepSize`). |
6363
| `q.isAligned(value)` | True if `value` is step-aligned (no resolution loss on encode). |
64+
| `q.requireAligned(value)` | Reverts with `NotAligned` if `value` is not a multiple of the step size. |
65+
| `q.requireMinStep(value)` | Reverts with `BelowMinStep` if `value` is non-zero and smaller than the step size. Zero is always allowed. |
6466
| `q.stepSize()` | Smallest non-zero value the scheme can represent (`2^discardedBitWidth`). |
6567
| `q.max()` | Largest value the scheme can represent: `(2^encodedBitWidth - 1) << discardedBitWidth`. |
6668

@@ -71,6 +73,7 @@ error BadConfig(uint256 discardedBitWidth, uint256 encodedBitWidth);
7173
error Overflow(uint256 value, uint256 max);
7274
error NotAligned(uint256 value, uint256 stepSize);
7375
error CeilOverflow(uint256 value);
76+
error BelowMinStep(uint256 value, uint256 stepSize);
7477
```
7578

7679
### Solidity usage

index.html

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@
3232

3333
/* ── Nav ── */
3434
.nav { position: sticky; top: 0; z-index: 100; background: rgba(8,9,11,0.82); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-bottom: 1px solid var(--border); }
35-
.nav-inner { max-width: 960px; margin: 0 auto; padding: 0 24px; height: 56px; display: flex; align-items: center; justify-content: space-between; }
35+
.nav-inner { max-width: 1200px; margin: 0 auto; padding: 0 24px; height: 56px; display: flex; align-items: center; justify-content: space-between; }
3636
.nav-logo { font-family: 'Azeret Mono', monospace; font-size: 0.85rem; font-weight: 600; color: var(--fg-0); text-decoration: none; letter-spacing: -0.02em; }
3737
.nav-logo:hover { color: var(--accent); text-decoration: none; }
3838
.nav-links { display: flex; align-items: center; gap: 24px; }
3939
.nav-links a { font-size: 0.85rem; font-weight: 500; color: var(--fg-2); text-decoration: none; transition: color 0.2s; }
4040
.nav-links a:hover { color: var(--fg-0); text-decoration: none; }
4141
.nav-links a.active { color: var(--accent); }
4242

43-
.container { max-width: 960px; margin: 0 auto; padding: 0 24px; }
43+
.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
4444

4545
/* ── Buttons ── */
4646
.btn { display: inline-flex; align-items: center; gap: 6px; padding: 10px 22px; border-radius: 6px; font-family: 'Sora', sans-serif; font-size: 0.9rem; font-weight: 600; text-decoration: none; transition: all 0.2s; cursor: pointer; border: none; }
@@ -94,6 +94,46 @@
9494
.footer p { font-size: 0.85rem; color: var(--fg-2); }
9595
.footer a { color: var(--fg-2); } .footer a:hover { color: var(--accent); }
9696

97+
/* ── Infographic ── */
98+
.ig-pipeline { display: flex; align-items: center; gap: 0; margin-bottom: 20px; overflow-x: auto; }
99+
.ig-stage { flex: 1; background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 12px; text-align: center; min-width: 130px; }
100+
.ig-hl { border-color: var(--accent-dim); background: rgba(34,211,238,0.04); }
101+
.ig-label { font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-2); margin-bottom: 2px; }
102+
.ig-num { font-family: 'Azeret Mono', monospace; font-size: 1.1rem; font-weight: 700; color: var(--fg-0); margin-bottom: 10px; }
103+
.ig-bar { display: flex; height: 20px; border-radius: 3px; overflow: hidden; margin-bottom: 4px; }
104+
.ig-seg { display: flex; align-items: center; justify-content: center; min-width: 1px; overflow: hidden; }
105+
.ig-seg span { font-family: 'Azeret Mono', monospace; font-size: 0.58rem; font-weight: 500; color: rgba(255,255,255,0.7); white-space: nowrap; }
106+
.ig-kept { background: var(--accent-dim); }
107+
.ig-lost { background: rgba(251,191,36,0.3); }
108+
.ig-zeros { background: rgba(255,255,255,0.06); border: 1px dashed rgba(255,255,255,0.12); }
109+
.ig-empty { background: rgba(255,255,255,0.03); }
110+
.ig-sub { font-size: 0.72rem; color: var(--fg-2); }
111+
.ig-legend { display: flex; gap: 14px; justify-content: center; margin-top: 5px; }
112+
.ig-legend-item { font-family: 'Azeret Mono', monospace; font-size: 0.6rem; color: var(--fg-2); display: flex; align-items: center; gap: 5px; }
113+
.ig-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
114+
.ig-dot.ig-kept { background: var(--accent-dim); }
115+
.ig-dot.ig-lost { background: rgba(251,191,36,0.3); }
116+
.ig-dot.ig-zeros { background: rgba(255,255,255,0.12); border: 1px dashed rgba(255,255,255,0.25); }
117+
.ig-arrow { padding: 0 10px; text-align: center; flex-shrink: 0; }
118+
.ig-arrow-op { font-family: 'Azeret Mono', monospace; font-size: 0.85rem; font-weight: 600; color: var(--fg-2); }
119+
.ig-arrow-lbl { font-size: 0.62rem; color: var(--fg-2); text-transform: uppercase; letter-spacing: 0.05em; }
120+
.ig-bridge { font-size: 0.9rem; color: var(--fg-1); line-height: 1.7; margin-bottom: 20px; }
121+
.ig-slots { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
122+
.ig-slot-side { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; }
123+
.ig-slot-side-hl { border-color: var(--accent-dim); }
124+
.ig-slot-hd { font-size: 0.78rem; font-weight: 600; color: var(--fg-1); margin-bottom: 10px; }
125+
.ig-slot-row { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
126+
.ig-slot-n { font-family: 'Azeret Mono', monospace; font-size: 0.65rem; color: var(--fg-2); width: 14px; flex-shrink: 0; }
127+
.ig-slot-bar { flex: 1; display: flex; height: 28px; border-radius: 3px; overflow: hidden; }
128+
129+
/* ── Ask AI ── */
130+
.ask-ai { background: var(--bg-1); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 24px; }
131+
.ask-q { font-size: 0.92rem; color: var(--fg-1); line-height: 1.7; margin-bottom: 18px; padding: 14px 16px; background: var(--bg-2); border-radius: var(--radius); border-left: 3px solid var(--accent-dim); }
132+
.ask-btns { display: flex; gap: 10px; flex-wrap: wrap; }
133+
.ask-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 18px; border-radius: 6px; font-family: 'Sora', sans-serif; font-size: 0.85rem; font-weight: 500; border: 1px solid var(--border); background: var(--bg-2); color: var(--fg-0); cursor: pointer; transition: all 0.2s; }
134+
.ask-btn:hover { border-color: var(--accent-dim); color: var(--accent); }
135+
.ask-btn.copied { border-color: var(--green-dim); color: var(--green); }
136+
97137
/* ── Scroll reveal ── */
98138
.sr { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; }
99139
.sr.visible { opacity: 1; transform: translateY(0); }
@@ -105,6 +145,10 @@
105145
.api-item { flex-direction: column; gap: 4px; }
106146
.api-desc { text-align: left; }
107147
.nav-links { gap: 16px; }
148+
.ig-pipeline { flex-direction: column; }
149+
.ig-arrow { padding: 4px 0; }
150+
.ig-slots { grid-template-columns: 1fr; }
151+
.ask-btns { flex-direction: column; }
108152
}
109153
</style>
110154
</head>
@@ -127,7 +171,7 @@
127171
<section class="hero">
128172
<span class="hero-tag">Solidity Library</span>
129173
<h1>Stop paying for resolution you don't need.</h1>
130-
<p class="hero-desc">A Solidity library that compresses uint256 values into smaller types for tighter struct packing and lower gas costs.</p>
174+
<p class="hero-desc">A Solidity library for shift-based quantization of unsigned integers, enabling tighter struct packing and lower gas costs.</p>
131175
<div class="hero-cta">
132176
<a class="btn btn-primary" href="https://github.com/0xferit/uint-quantization-lib">View on GitHub</a>
133177
<a class="btn btn-outline" href="https://github.com/0xferit/uint-quantization-lib/blob/main/README.md">Documentation</a>
@@ -159,24 +203,76 @@ <h2 class="section-title sr">Quick Start</h2>
159203
</section>
160204

161205
<section class="section">
162-
<h2 class="section-title sr">Why Quantization?</h2>
163-
<div class="card-grid">
164-
<div class="card sr" style="transition-delay:0.1s">
165-
<h3>Gas Savings</h3>
166-
<p>Compress values that don't need full uint256 resolution so more fields fit per storage slot, cutting SSTORE/SLOAD costs.</p>
206+
<h2 class="section-title sr">How It Works</h2>
207+
<div class="ig-pipeline sr" style="transition-delay:0.1s">
208+
<div class="ig-stage">
209+
<div class="ig-label">Original</div>
210+
<div class="ig-num">1e18 wei</div>
211+
<div class="ig-bar">
212+
<div class="ig-seg ig-kept" style="flex:224"></div>
213+
<div class="ig-seg ig-lost" style="flex:32"></div>
214+
</div>
215+
<div class="ig-legend">
216+
<span class="ig-legend-item"><span class="ig-dot ig-kept"></span>kept: 224 bits</span>
217+
<span class="ig-legend-item"><span class="ig-dot ig-lost"></span>discarded: 32 bits</span>
218+
</div>
219+
<div class="ig-sub">256 bits total (1 token, 18 decimals)</div>
220+
</div>
221+
<div class="ig-arrow">
222+
<div class="ig-arrow-op">&gt;&gt; 32</div>
223+
<div class="ig-arrow-lbl">encode</div>
224+
</div>
225+
<div class="ig-stage ig-hl">
226+
<div class="ig-label">Stored</div>
227+
<div class="ig-num">232,830,643 wei</div>
228+
<div class="ig-bar">
229+
<div class="ig-seg ig-kept" style="flex:1"></div>
230+
</div>
231+
<div class="ig-sub">fits in uint224</div>
232+
</div>
233+
<div class="ig-arrow">
234+
<div class="ig-arrow-op">&lt;&lt; 32</div>
235+
<div class="ig-arrow-lbl">decode</div>
236+
</div>
237+
<div class="ig-stage">
238+
<div class="ig-label">Restored</div>
239+
<div class="ig-num">&asymp; 1e18 wei</div>
240+
<div class="ig-bar">
241+
<div class="ig-seg ig-kept" style="flex:224"></div>
242+
<div class="ig-seg ig-zeros" style="flex:32"></div>
243+
</div>
244+
<div class="ig-legend">
245+
<span class="ig-legend-item"><span class="ig-dot ig-kept"></span>preserved: 224 bits</span>
246+
<span class="ig-legend-item"><span class="ig-dot ig-zeros"></span>zero-filled: 32 bits</span>
247+
</div>
248+
<div class="ig-sub">max error &lt; 2^32 wei (~4.3 gwei)</div>
249+
</div>
250+
</div>
251+
</section>
252+
253+
<section class="section" style="padding-top:0">
254+
<h2 class="section-title sr">Ask an AI</h2>
255+
<div class="ask-ai sr" style="transition-delay:0.1s">
256+
<p class="ask-q" id="askQuestion">Explain how the uint-quantization-lib Solidity library (https://github.com/0xferit/uint-quantization-lib) works. How does its shift-based quantization reduce gas costs, and what are the tradeoffs?</p>
257+
<div class="ask-btns">
258+
<button class="ask-btn" data-url="https://claude.ai/new">Ask Claude</button>
259+
<button class="ask-btn" data-url="https://chatgpt.com">Ask ChatGPT</button>
260+
<button class="ask-btn" data-url="https://gemini.google.com/app">Ask Gemini</button>
261+
<button class="ask-btn" data-url="https://www.perplexity.ai/search">Ask Perplexity</button>
167262
</div>
168263
</div>
169264
</section>
170265

171266
<section class="section">
172267
<h2 class="section-title sr">API</h2>
173268
<div class="api-list sr" style="transition-delay:0.1s">
174-
<div class="api-item"><code class="api-fn">create(shift, targetBits)</code><span class="api-desc">Create a quantization scheme</span></div>
269+
<div class="api-item"><code class="api-fn">create(discardedBitWidth, encodedBitWidth)</code><span class="api-desc">Create a quantization scheme</span></div>
175270
<div class="api-item"><code class="api-fn">encode(value)</code><span class="api-desc">Floor-encode a value (reverts on overflow)</span></div>
176-
<div class="api-item"><code class="api-fn">encode(value, true)</code><span class="api-desc">Strict encode (reverts if value is not step-aligned)</span></div>
177-
<div class="api-item"><code class="api-fn">decode(encoded)</code><span class="api-desc">Restore lower-bound value</span></div>
178-
<div class="api-item"><code class="api-fn">decodeMax(encoded)</code><span class="api-desc">Restore upper-bound value</span></div>
179-
<div class="api-item"><code class="api-fn">fits(value)</code><span class="api-desc">Check if value is representable</span></div>
271+
<div class="api-item"><code class="api-fn">encode(value, true)</code><span class="api-desc">Strict encode (reverts if not step-aligned)</span></div>
272+
<div class="api-item"><code class="api-fn">decode(encoded) / decodeMax(encoded)</code><span class="api-desc">Restore lower-bound or upper-bound value</span></div>
273+
<div class="api-item"><code class="api-fn">fits(value) / fitsEncoded(encoded)</code><span class="api-desc">Check if value is representable</span></div>
274+
<div class="api-item"><code class="api-fn">isAligned(value) / remainder(value)</code><span class="api-desc">Check step alignment and lost bits</span></div>
275+
<div class="api-item"><code class="api-fn">requireAligned(value) / requireMinStep(value)</code><span class="api-desc">Guard helpers that revert on bad input</span></div>
180276
<div class="api-item"><code class="api-fn">floor(value) / ceil(value)</code><span class="api-desc">Snap to nearest step boundary</span></div>
181277
<div class="api-item"><code class="api-fn">stepSize() / max()</code><span class="api-desc">Query scheme parameters</span></div>
182278
</div>
@@ -194,6 +290,22 @@ <h2 class="section-title sr">API</h2>
194290
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });
195291
}, { threshold: 0.1 });
196292
document.querySelectorAll('.sr').forEach(el => srObs.observe(el));
293+
294+
/* ── Ask AI ── */
295+
document.querySelectorAll('.ask-btn').forEach(function(btn) {
296+
var label = btn.textContent;
297+
btn.addEventListener('click', function() {
298+
var q = document.getElementById('askQuestion').textContent;
299+
navigator.clipboard.writeText(q).then(function() {
300+
var url = btn.dataset.url;
301+
if (url.indexOf('perplexity') !== -1) url += '?q=' + encodeURIComponent(q);
302+
window.open(url, '_blank');
303+
btn.classList.add('copied');
304+
btn.textContent = 'Copied! Paste in chat';
305+
setTimeout(function() { btn.classList.remove('copied'); btn.textContent = label; }, 3000);
306+
});
307+
});
308+
});
197309
})();
198310
</script>
199311
</body>

0 commit comments

Comments
 (0)