bit-byte-bit 0.0.0-stage → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -2
- package/assets/bg/blue.mp4 +0 -0
- package/assets/bg/green.mp4 +0 -0
- package/assets/bg/poster.jpg +0 -0
- package/assets/bg/teal.mp4 +0 -0
- package/assets/fonts/auge-bold.otf +0 -0
- package/assets/fonts/auge-regular.otf +0 -0
- package/assets/fonts/auge-semibold.otf +0 -0
- package/assets/fonts/inter-var.ttf +0 -0
- package/assets/fonts/jetbrains-mono-var.ttf +0 -0
- package/assets/fonts/pacifico.ttf +0 -0
- package/assets/fonts/space-grotesk-var.ttf +0 -0
- package/assets/logos/bit-byte-bit-backup.svg +48 -0
- package/assets/logos/bit-byte-bit.svg +19 -0
- package/assets/site/backup/Pacifico-Regular-95szj24p.ttf +0 -0
- package/assets/site/backup/auge-bold-vywa1g7b.otf +0 -0
- package/assets/site/backup/auge-regular-hz0spgka.otf +0 -0
- package/assets/site/backup/auge-semibold-td2xkdwf.otf +0 -0
- package/assets/site/backup/copium-meme.png +0 -0
- package/assets/site/backup/engine/controller.api.js +1344 -0
- package/assets/site/backup/engine/controller.inject.js +511 -0
- package/assets/site/backup/engine/controller.sw.js +418 -0
- package/assets/site/backup/engine/corridor.js +13215 -0
- package/assets/site/backup/engine/corridor.wasm +0 -0
- package/assets/site/backup/site-backup.css +1 -0
- package/assets/site/backup/site-backup.js +74 -0
- package/assets/site/backup/sw.js +224 -0
- package/assets/site/index.html +113 -0
- package/assets/site/site.css +226 -0
- package/assets/site/site.js +188 -0
- package/index.cjs +83 -0
- package/index.d.ts +14 -0
- package/package.json +29 -4
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
importScripts("engine/controller.sw.js");
|
|
2
|
+
if (navigator["userAgent"]["includes"]("Firefox")) {
|
|
3
|
+
Object["defineProperty"](globalThis, "crossOriginIsolated", {
|
|
4
|
+
value: !![],
|
|
5
|
+
writable: ![],
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
const BLOCKED = [
|
|
9
|
+
"youtube.com/get_video_info?*adformat=*",
|
|
10
|
+
"youtube.com/api/stats/ads/*",
|
|
11
|
+
"youtube.com/pagead/*",
|
|
12
|
+
".facebook.com/ads/*",
|
|
13
|
+
".facebook.com/tr/*",
|
|
14
|
+
".fbcdn.net/ads/*",
|
|
15
|
+
"graph.facebook.com/ads/*",
|
|
16
|
+
"ads-api.twitter.com/*",
|
|
17
|
+
"analytics.twitter.com/*",
|
|
18
|
+
".twitter.com/i/ads/*",
|
|
19
|
+
".ads.yahoo.com",
|
|
20
|
+
".advertising.com",
|
|
21
|
+
".adtechus.com",
|
|
22
|
+
".oath.com",
|
|
23
|
+
".verizonmedia.com",
|
|
24
|
+
".amazon-adsystem.com",
|
|
25
|
+
"aax.amazon-adsystem.com/*",
|
|
26
|
+
"c.amazon-adsystem.com/*",
|
|
27
|
+
".adnxs.com",
|
|
28
|
+
".adnxs-simple.com",
|
|
29
|
+
"ab.adnxs.com/*",
|
|
30
|
+
".rubiconproject.com",
|
|
31
|
+
".magnite.com",
|
|
32
|
+
".pubmatic.com",
|
|
33
|
+
"ads.pubmatic.com/*",
|
|
34
|
+
".criteo.com",
|
|
35
|
+
"bidder.criteo.com/*",
|
|
36
|
+
"static.criteo.net/*",
|
|
37
|
+
".openx.net",
|
|
38
|
+
".openx.com",
|
|
39
|
+
".indexexchange.com",
|
|
40
|
+
".casalemedia.com",
|
|
41
|
+
".adcolony.com",
|
|
42
|
+
".chartboost.com",
|
|
43
|
+
".unityads.unity3d.com",
|
|
44
|
+
".inmobiweb.com",
|
|
45
|
+
".tapjoy.com",
|
|
46
|
+
".applovin.com",
|
|
47
|
+
".vungle.com",
|
|
48
|
+
".ironsrc.com",
|
|
49
|
+
".fyber.com",
|
|
50
|
+
".smaato.net",
|
|
51
|
+
".supersoniads.com",
|
|
52
|
+
".startappservice.com",
|
|
53
|
+
".airpush.com",
|
|
54
|
+
".outbrain.com",
|
|
55
|
+
".taboola.com",
|
|
56
|
+
".revcontent.com",
|
|
57
|
+
".zedo.com",
|
|
58
|
+
".mgid.com",
|
|
59
|
+
"*/ads/*",
|
|
60
|
+
"*/adserver/*",
|
|
61
|
+
"*/adclick/*",
|
|
62
|
+
"*/banner_ads/*",
|
|
63
|
+
"*/sponsored/*",
|
|
64
|
+
"*/promotions/*",
|
|
65
|
+
"*/tracking/ads/*",
|
|
66
|
+
"*/promo/*",
|
|
67
|
+
"*/affiliates/*",
|
|
68
|
+
"*/partnerads/*",
|
|
69
|
+
];
|
|
70
|
+
function escapeRe(a) {
|
|
71
|
+
return a["replace"](/[.+?^${}()|[\]\\]/g, "\x5c$&");
|
|
72
|
+
}
|
|
73
|
+
function wildcardToRe(a) {
|
|
74
|
+
const b = escapeRe(a)
|
|
75
|
+
["replace"](/\*\*/g, "{{DOUBLE_STAR}}")
|
|
76
|
+
["replace"](/\*/g, "[^/]*")
|
|
77
|
+
["replace"](/{{DOUBLE_STAR}}/g, ".*");
|
|
78
|
+
return new RegExp("^" + b + "$");
|
|
79
|
+
}
|
|
80
|
+
function matchesPattern(a, b, c) {
|
|
81
|
+
if (a["startsWith"]("#")) a = a["slice"](0x1);
|
|
82
|
+
let d = a;
|
|
83
|
+
let e = null;
|
|
84
|
+
if (a["startsWith"]("*")) {
|
|
85
|
+
d = "";
|
|
86
|
+
e = a["slice"](0x1);
|
|
87
|
+
} else if (a["includes"]("/")) {
|
|
88
|
+
const f = a["indexOf"]("/");
|
|
89
|
+
d = a["slice"](0x0, f);
|
|
90
|
+
e = a["slice"](f);
|
|
91
|
+
}
|
|
92
|
+
if (d) {
|
|
93
|
+
if (d["startsWith"](".")) {
|
|
94
|
+
const g = d["slice"](0x1);
|
|
95
|
+
if (b !== g && !b["endsWith"]("." + g)) return ![];
|
|
96
|
+
} else if (!wildcardToRe(d)["test"](b)) {
|
|
97
|
+
return ![];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (e !== null) {
|
|
101
|
+
return wildcardToRe(e)["test"](c);
|
|
102
|
+
}
|
|
103
|
+
return !![];
|
|
104
|
+
}
|
|
105
|
+
function isBlocked(a, b) {
|
|
106
|
+
return BLOCKED["some"]((c) => matchesPattern(c, a, b));
|
|
107
|
+
}
|
|
108
|
+
function decodeDestination(a) {
|
|
109
|
+
try {
|
|
110
|
+
const b = new URL(a);
|
|
111
|
+
const c = b["pathname"]["split"]("/")["filter"](Boolean);
|
|
112
|
+
if (!c["length"]) return null;
|
|
113
|
+
let d = c[c["length"] - 0x1];
|
|
114
|
+
for (let e = 0x0; e < 0x2; e++) {
|
|
115
|
+
try {
|
|
116
|
+
const f = decodeURIComponent(d);
|
|
117
|
+
if (f === d) break;
|
|
118
|
+
d = f;
|
|
119
|
+
} catch {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (/^https?:\/\//i["test"](d)) return new URL(d);
|
|
124
|
+
return null;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const PROXY_PREFIX = new URL(registration["scope"])["pathname"] + "~/sj/";
|
|
130
|
+
const clientBases = new Map();
|
|
131
|
+
function proxyBaseOf(a) {
|
|
132
|
+
try {
|
|
133
|
+
const b = new URL(a);
|
|
134
|
+
if (
|
|
135
|
+
b["origin"] !== location["origin"] ||
|
|
136
|
+
!b["pathname"]["startsWith"](PROXY_PREFIX)
|
|
137
|
+
)
|
|
138
|
+
return null;
|
|
139
|
+
return b["pathname"]["slice"](
|
|
140
|
+
0x0,
|
|
141
|
+
b["pathname"]["lastIndexOf"]("/") + 0x1,
|
|
142
|
+
);
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function isProxyPath(a) {
|
|
148
|
+
const b = new URL(a);
|
|
149
|
+
return (
|
|
150
|
+
b["origin"] === location["origin"] &&
|
|
151
|
+
b["pathname"]["startsWith"](PROXY_PREFIX)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
function leakedRequest(a) {
|
|
155
|
+
const b = a["request"];
|
|
156
|
+
if (
|
|
157
|
+
!a["clientId"] ||
|
|
158
|
+
b["mode"] === "navigate" ||
|
|
159
|
+
(b["method"] !== "GET" && b["method"] !== "HEAD")
|
|
160
|
+
)
|
|
161
|
+
return null;
|
|
162
|
+
if (clientBases["get"](a["clientId"]) === null) return null;
|
|
163
|
+
let c;
|
|
164
|
+
try {
|
|
165
|
+
c = new URL(b["url"]);
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
c["origin"] === location["origin"] ||
|
|
171
|
+
(c["protocol"] !== "http:" && c["protocol"] !== "https:")
|
|
172
|
+
)
|
|
173
|
+
return null;
|
|
174
|
+
return c;
|
|
175
|
+
}
|
|
176
|
+
async function routeLeaked(a, b) {
|
|
177
|
+
let c = clientBases["get"](a["clientId"]);
|
|
178
|
+
if (c === undefined) {
|
|
179
|
+
const f = await clients["get"](a["clientId"]);
|
|
180
|
+
c = f ? proxyBaseOf(f["url"]) : null;
|
|
181
|
+
if (clientBases["size"] > 0x1f4) clientBases["clear"]();
|
|
182
|
+
clientBases["set"](a["clientId"], c);
|
|
183
|
+
}
|
|
184
|
+
if (!c) return fetch(a["request"]);
|
|
185
|
+
if (isBlocked(b["hostname"], b["pathname"] + (b["search"] || "")))
|
|
186
|
+
return new Response("", { status: 0x193 });
|
|
187
|
+
const d = a["request"];
|
|
188
|
+
return $corridorController["route"]({
|
|
189
|
+
request: {
|
|
190
|
+
url: location["origin"] + c + encodeURIComponent(b["href"]),
|
|
191
|
+
referrer: d["referrer"],
|
|
192
|
+
destination: d["destination"],
|
|
193
|
+
mode: d["mode"],
|
|
194
|
+
method: d["method"],
|
|
195
|
+
headers: d["headers"],
|
|
196
|
+
body: null,
|
|
197
|
+
cache: d["cache"],
|
|
198
|
+
},
|
|
199
|
+
clientId: a["clientId"],
|
|
200
|
+
resultingClientId: a["resultingClientId"],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
addEventListener("fetch", (a) => {
|
|
204
|
+
const b = leakedRequest(a);
|
|
205
|
+
if (b) {
|
|
206
|
+
a["respondWith"](routeLeaked(a, b));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const c = decodeDestination(a["request"]["url"]);
|
|
210
|
+
if (c) {
|
|
211
|
+
const d = c["pathname"] + (c["search"] || "");
|
|
212
|
+
if (isBlocked(c["hostname"], d)) {
|
|
213
|
+
a["respondWith"](new Response("", { status: 0x193 }));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
$corridorController["shouldRoute"](a) ||
|
|
219
|
+
isProxyPath(a["request"]["url"])
|
|
220
|
+
) {
|
|
221
|
+
a["respondWith"]($corridorController["route"](a));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>bit-byte-bit — tiny utilities for modern web apps</title>
|
|
7
|
+
<meta name="description" content="Small, dependency-free utilities for modern web apps. Classnames, strings, formatting, math, objects, and async helpers." />
|
|
8
|
+
<link rel="stylesheet" href="./site.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header class="top">
|
|
12
|
+
<div class="top-inner">
|
|
13
|
+
<a class="brand" href="#">
|
|
14
|
+
<svg class="mark" viewBox="0 0 32 32" aria-hidden="true">
|
|
15
|
+
<rect width="32" height="32" rx="8" fill="#1c1c1e" stroke="#3f3f46"/>
|
|
16
|
+
<path d="M9 23V9h3.2l4.6 8.4V9H20v14h-3.2l-4.6-8.4V23H9z" fill="#6fd9ae"/>
|
|
17
|
+
</svg>
|
|
18
|
+
<span class="brand-name">bit-byte-bit</span>
|
|
19
|
+
<span class="ver">v2.1.2</span>
|
|
20
|
+
</a>
|
|
21
|
+
<nav class="top-nav">
|
|
22
|
+
<a href="#features">Features</a>
|
|
23
|
+
<a href="#api">API</a>
|
|
24
|
+
<a href="#playground">Playground</a>
|
|
25
|
+
</nav>
|
|
26
|
+
</div>
|
|
27
|
+
</header>
|
|
28
|
+
|
|
29
|
+
<main>
|
|
30
|
+
<section class="hero">
|
|
31
|
+
<p class="eyebrow">zero dependencies · esm + cjs · typed</p>
|
|
32
|
+
<h1>Tiny utilities for modern web apps.</h1>
|
|
33
|
+
<p class="lede">
|
|
34
|
+
bit-byte-bit is a small collection of byte, bit, and hex
|
|
35
|
+
utilities — human-readable size formatting in decimal and binary
|
|
36
|
+
units, size string parsing, unit conversion, hex encode/decode,
|
|
37
|
+
CRC-32 checksums, and crypto-random bytes. No dependencies, no
|
|
38
|
+
build step required, tree-shakeable by design.
|
|
39
|
+
</p>
|
|
40
|
+
<div class="install">
|
|
41
|
+
<code>npm install bit-byte-bit</code>
|
|
42
|
+
<button class="copy" id="copy-install" type="button">copy</button>
|
|
43
|
+
</div>
|
|
44
|
+
</section>
|
|
45
|
+
|
|
46
|
+
<section class="grid" id="features">
|
|
47
|
+
<article class="card">
|
|
48
|
+
<h3>Zero dependencies</h3>
|
|
49
|
+
<p>Every helper is self-contained. bit-byte-bit pulls in nothing — audit the whole package in one sitting.</p>
|
|
50
|
+
</article>
|
|
51
|
+
<article class="card">
|
|
52
|
+
<h3>Dual module formats</h3>
|
|
53
|
+
<p>Ships as CommonJS with full TypeScript declarations. Works with bundlers, Node, and plain script tags.</p>
|
|
54
|
+
</article>
|
|
55
|
+
<article class="card">
|
|
56
|
+
<h3>Typed API surface</h3>
|
|
57
|
+
<p>index.d.ts covers every export. Full coverage of every export, keeping inference intact across the API.</p>
|
|
58
|
+
</article>
|
|
59
|
+
<article class="card">
|
|
60
|
+
<h3>Tree-shakeable</h3>
|
|
61
|
+
<p>Named exports only, no side effects. Import one helper and the rest stays out of your bundle.</p>
|
|
62
|
+
</article>
|
|
63
|
+
<article class="card">
|
|
64
|
+
<h3>Browser + Node</h3>
|
|
65
|
+
<p>Runs anywhere JavaScript runs. No DOM globals are touched unless you call the helpers that need them.</p>
|
|
66
|
+
</article>
|
|
67
|
+
<article class="card">
|
|
68
|
+
<h3>MIT licensed</h3>
|
|
69
|
+
<p>Free for personal and commercial use. Contributions welcome.</p>
|
|
70
|
+
</article>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
<section class="api" id="api">
|
|
74
|
+
<h2>API reference</h2>
|
|
75
|
+
<p class="api-sub">9 functions across five categories. Click a row to see it in the playground.</p>
|
|
76
|
+
<table>
|
|
77
|
+
<thead>
|
|
78
|
+
<tr><th>Export</th><th>Signature</th><th>What it does</th></tr>
|
|
79
|
+
</thead>
|
|
80
|
+
<tbody id="api-rows"></tbody>
|
|
81
|
+
</table>
|
|
82
|
+
</section>
|
|
83
|
+
|
|
84
|
+
<section class="play" id="playground">
|
|
85
|
+
<h2>Playground</h2>
|
|
86
|
+
<div class="play-grid">
|
|
87
|
+
<div class="play-col">
|
|
88
|
+
<h4>formatBytes() / formatBits()</h4>
|
|
89
|
+
<pre id="demo-cn"></pre>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="play-col">
|
|
92
|
+
<h4>parseBytes()</h4>
|
|
93
|
+
<pre id="demo-fmt"></pre>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="play-col">
|
|
96
|
+
<h4>bytesToBits() / bitsToBytes()</h4>
|
|
97
|
+
<pre id="demo-time"></pre>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="play-col">
|
|
100
|
+
<h4>bytesToHex() / crc32()</h4>
|
|
101
|
+
<pre id="demo-deb"></pre>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
</main>
|
|
106
|
+
|
|
107
|
+
<footer class="foot">
|
|
108
|
+
<p>bit-byte-bit is MIT licensed. Built for people who read source.</p>
|
|
109
|
+
</footer>
|
|
110
|
+
|
|
111
|
+
<script src="./site.js"></script>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/* bit-byte-bit — docs-style demo page (dark) */
|
|
2
|
+
:root {
|
|
3
|
+
--ink: #f4f4f5;
|
|
4
|
+
--ink-dim: #a1a1aa;
|
|
5
|
+
--paper: #09090b;
|
|
6
|
+
--wash: #111113;
|
|
7
|
+
--card: #141416;
|
|
8
|
+
--line: #26262b;
|
|
9
|
+
--line-strong: #3a3a41;
|
|
10
|
+
--accent: #6fd9ae;
|
|
11
|
+
--accent-ink: #06281c;
|
|
12
|
+
--accent-soft: rgba(111, 217, 174, 0.12);
|
|
13
|
+
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
* { box-sizing: border-box; }
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
21
|
+
background: var(--paper);
|
|
22
|
+
color: var(--ink);
|
|
23
|
+
line-height: 1.55;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ---------- top bar ---------- */
|
|
27
|
+
.top {
|
|
28
|
+
border-bottom: 1px solid var(--line);
|
|
29
|
+
background: rgba(9, 9, 11, 0.8);
|
|
30
|
+
backdrop-filter: saturate(1.4) blur(8px);
|
|
31
|
+
position: sticky;
|
|
32
|
+
top: 0;
|
|
33
|
+
z-index: 10;
|
|
34
|
+
}
|
|
35
|
+
.top-inner {
|
|
36
|
+
max-width: 880px;
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
padding: 14px 20px;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
gap: 16px;
|
|
43
|
+
}
|
|
44
|
+
.brand {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: 10px;
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
color: var(--ink);
|
|
50
|
+
}
|
|
51
|
+
.mark { width: 28px; height: 28px; }
|
|
52
|
+
.brand-name { font-weight: 650; font-size: 15px; letter-spacing: -0.01em; }
|
|
53
|
+
.ver {
|
|
54
|
+
font-family: var(--mono);
|
|
55
|
+
font-size: 11px;
|
|
56
|
+
color: var(--ink-dim);
|
|
57
|
+
border: 1px solid var(--line);
|
|
58
|
+
border-radius: 999px;
|
|
59
|
+
padding: 2px 8px;
|
|
60
|
+
}
|
|
61
|
+
.top-nav { display: flex; gap: 18px; }
|
|
62
|
+
.top-nav a {
|
|
63
|
+
color: var(--ink-dim);
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
font-size: 13.5px;
|
|
66
|
+
}
|
|
67
|
+
.top-nav a:hover { color: var(--ink); }
|
|
68
|
+
|
|
69
|
+
/* ---------- hero ---------- */
|
|
70
|
+
main { max-width: 880px; margin: 0 auto; padding: 0 20px; }
|
|
71
|
+
.hero { padding: 72px 0 48px; }
|
|
72
|
+
.eyebrow {
|
|
73
|
+
font-family: var(--mono);
|
|
74
|
+
font-size: 12px;
|
|
75
|
+
letter-spacing: 0.08em;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
color: var(--accent);
|
|
78
|
+
margin: 0 0 14px;
|
|
79
|
+
}
|
|
80
|
+
h1 {
|
|
81
|
+
font-size: clamp(30px, 5vw, 44px);
|
|
82
|
+
letter-spacing: -0.03em;
|
|
83
|
+
line-height: 1.1;
|
|
84
|
+
margin: 0 0 18px;
|
|
85
|
+
}
|
|
86
|
+
.lede {
|
|
87
|
+
font-size: 17px;
|
|
88
|
+
color: var(--ink-dim);
|
|
89
|
+
max-width: 620px;
|
|
90
|
+
margin: 0 0 28px;
|
|
91
|
+
}
|
|
92
|
+
.install {
|
|
93
|
+
display: inline-flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
gap: 12px;
|
|
96
|
+
background: var(--wash);
|
|
97
|
+
border: 1px solid var(--line);
|
|
98
|
+
border-radius: 10px;
|
|
99
|
+
padding: 8px 8px 8px 16px;
|
|
100
|
+
}
|
|
101
|
+
.install code {
|
|
102
|
+
font-family: var(--mono);
|
|
103
|
+
font-size: 13.5px;
|
|
104
|
+
color: var(--ink);
|
|
105
|
+
}
|
|
106
|
+
.copy {
|
|
107
|
+
font-family: inherit;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
font-weight: 550;
|
|
110
|
+
border: none;
|
|
111
|
+
background: var(--accent);
|
|
112
|
+
color: var(--accent-ink);
|
|
113
|
+
border-radius: 7px;
|
|
114
|
+
padding: 7px 14px;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
}
|
|
117
|
+
.copy:hover { background: #8be7c6; }
|
|
118
|
+
.copy.done { background: var(--ink-dim); color: var(--paper); }
|
|
119
|
+
|
|
120
|
+
/* ---------- feature grid ---------- */
|
|
121
|
+
.grid {
|
|
122
|
+
display: grid;
|
|
123
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
124
|
+
gap: 14px;
|
|
125
|
+
padding: 24px 0 56px;
|
|
126
|
+
}
|
|
127
|
+
.card {
|
|
128
|
+
border: 1px solid var(--line);
|
|
129
|
+
border-radius: 14px;
|
|
130
|
+
padding: 18px;
|
|
131
|
+
background: var(--card);
|
|
132
|
+
}
|
|
133
|
+
.card h3 {
|
|
134
|
+
margin: 0 0 6px;
|
|
135
|
+
font-size: 14.5px;
|
|
136
|
+
letter-spacing: -0.01em;
|
|
137
|
+
}
|
|
138
|
+
.card p { margin: 0; font-size: 13.5px; color: var(--ink-dim); }
|
|
139
|
+
|
|
140
|
+
/* ---------- api table ---------- */
|
|
141
|
+
.api { padding-bottom: 56px; }
|
|
142
|
+
.api h2, .play h2 {
|
|
143
|
+
font-size: 22px;
|
|
144
|
+
letter-spacing: -0.02em;
|
|
145
|
+
margin: 0 0 6px;
|
|
146
|
+
}
|
|
147
|
+
.api-sub { color: var(--ink-dim); font-size: 14px; margin: 0 0 18px; }
|
|
148
|
+
table {
|
|
149
|
+
width: 100%;
|
|
150
|
+
border-collapse: collapse;
|
|
151
|
+
font-size: 13.5px;
|
|
152
|
+
}
|
|
153
|
+
th {
|
|
154
|
+
text-align: left;
|
|
155
|
+
font-size: 11.5px;
|
|
156
|
+
text-transform: uppercase;
|
|
157
|
+
letter-spacing: 0.06em;
|
|
158
|
+
color: var(--ink-dim);
|
|
159
|
+
padding: 10px 12px;
|
|
160
|
+
border-bottom: 1px solid var(--line-strong);
|
|
161
|
+
}
|
|
162
|
+
td {
|
|
163
|
+
padding: 9px 12px;
|
|
164
|
+
border-bottom: 1px solid var(--line);
|
|
165
|
+
vertical-align: top;
|
|
166
|
+
}
|
|
167
|
+
tbody tr { cursor: pointer; }
|
|
168
|
+
tbody tr:hover { background: var(--wash); }
|
|
169
|
+
td.name { font-family: var(--mono); font-size: 12.5px; color: var(--accent); white-space: nowrap; }
|
|
170
|
+
td.sig { font-family: var(--mono); font-size: 12px; color: var(--ink-dim); }
|
|
171
|
+
td.what { color: var(--ink-dim); font-size: 13px; }
|
|
172
|
+
td.group {
|
|
173
|
+
font-size: 11px;
|
|
174
|
+
font-weight: 650;
|
|
175
|
+
text-transform: uppercase;
|
|
176
|
+
letter-spacing: 0.08em;
|
|
177
|
+
color: var(--accent);
|
|
178
|
+
background: var(--accent-soft);
|
|
179
|
+
padding: 7px 12px;
|
|
180
|
+
cursor: default;
|
|
181
|
+
}
|
|
182
|
+
td.group:hover { background: var(--accent-soft); }
|
|
183
|
+
|
|
184
|
+
/* ---------- playground ---------- */
|
|
185
|
+
.play { padding-bottom: 80px; }
|
|
186
|
+
.play-grid {
|
|
187
|
+
display: grid;
|
|
188
|
+
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
189
|
+
gap: 14px;
|
|
190
|
+
margin-top: 18px;
|
|
191
|
+
}
|
|
192
|
+
.play-col {
|
|
193
|
+
border: 1px solid var(--line);
|
|
194
|
+
border-radius: 14px;
|
|
195
|
+
padding: 16px;
|
|
196
|
+
background: var(--wash);
|
|
197
|
+
}
|
|
198
|
+
.play-col h4 {
|
|
199
|
+
margin: 0 0 10px;
|
|
200
|
+
font-family: var(--mono);
|
|
201
|
+
font-size: 12.5px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
color: var(--ink);
|
|
204
|
+
}
|
|
205
|
+
.play-col pre {
|
|
206
|
+
margin: 0;
|
|
207
|
+
font-family: var(--mono);
|
|
208
|
+
font-size: 12px;
|
|
209
|
+
line-height: 1.6;
|
|
210
|
+
color: var(--ink-dim);
|
|
211
|
+
white-space: pre-wrap;
|
|
212
|
+
word-break: break-word;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* ---------- footer ---------- */
|
|
216
|
+
.foot {
|
|
217
|
+
border-top: 1px solid var(--line);
|
|
218
|
+
padding: 28px 20px 40px;
|
|
219
|
+
text-align: center;
|
|
220
|
+
}
|
|
221
|
+
.foot p { margin: 0; font-size: 13px; color: var(--ink-dim); }
|
|
222
|
+
|
|
223
|
+
@media (max-width: 640px) {
|
|
224
|
+
.top-nav { display: none; }
|
|
225
|
+
.hero { padding: 48px 0 36px; }
|
|
226
|
+
}
|