Adit Rahman commited on
Commit
7b33d33
·
1 Parent(s): 5247451

NanoFable static demo: all 8 packs served same-origin

Browse files
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.tpack filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,10 +1,16 @@
1
  ---
2
- title: Nanofable
3
- emoji: 🦀
4
- colorFrom: pink
5
  colorTo: gray
6
  sdk: static
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
  ---
2
+ title: NanoFable
3
+ emoji: 🦫
4
+ colorFrom: yellow
5
  colorTo: gray
6
  sdk: static
7
+ app_file: index.html
8
  pinned: false
9
  ---
10
 
11
+ # NanoFable — a tiny ternary storyteller, running in your browser
12
+
13
+ Every model runs client-side: the page fetches a `.tpack` (packed ternary / fp16 weights),
14
+ decodes it in JS, and generates tokens on your CPU. Nothing is sent to a server.
15
+
16
+ Weights: https://huggingface.co/collections/adrahmana — code: https://github.com/adit-rah/nanofable
assets/bard-still.png ADDED
assets/bard-talk.gif ADDED
fonts/eb-garamond-italic.woff2 ADDED
Binary file (25.4 kB). View file
 
fonts/eb-garamond.woff2 ADDED
Binary file (23.8 kB). View file
 
fonts/im-fell-english-italic.woff2 ADDED
Binary file (57.8 kB). View file
 
fonts/im-fell-english.woff2 ADDED
Binary file (59.9 kB). View file
 
index.html CHANGED
@@ -1,19 +1,95 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
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" />
6
+ <title>nanofable — a pocket storyteller</title>
7
+ <meta name="description" content="A ~1 MB ternary language model that finishes your story, running entirely in your browser." />
8
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>&#129718;</text></svg>" />
9
+ <link rel="preload" href="fonts/im-fell-english.woff2" as="font" type="font/woff2" crossorigin />
10
+ <link rel="preload" href="fonts/im-fell-english-italic.woff2" as="font" type="font/woff2" crossorigin />
11
+ <link rel="preload" href="fonts/eb-garamond.woff2" as="font" type="font/woff2" crossorigin />
12
+ <link rel="preload" href="fonts/eb-garamond-italic.woff2" as="font" type="font/woff2" crossorigin />
13
+ <link rel="stylesheet" href="style.css" />
14
+ </head>
15
+ <body>
16
+ <header class="site-head">
17
+ <div class="mark">nanofable<span class="mark-sub"> · a pocket storyteller</span></div>
18
+ <button id="install-btn" class="icon-btn" aria-label="Install all models" title="Install all models">
19
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
20
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" />
21
+ </svg>
22
+ </button>
23
+ </header>
24
+
25
+ <main id="main">
26
+ <section id="hero">
27
+ <h1 class="hero-title">Have a story to tell?</h1>
28
+ <p class="hero-sub">Bar'd is a <span id="hero-bytes">1.2&nbsp;MB</span> storyteller running entirely in your browser.
29
+ Give him an opening line, and he'll <u><em>probably</em></u> tell you a pretty good story.</p>
30
+ <picture>
31
+ <source media="(prefers-reduced-motion: reduce)" srcset="assets/bard-still.png" />
32
+ <img class="hero-bard" src="assets/bard-talk.gif" alt="Bar'd, the nanofable bard" width="465" height="330" />
33
+ </picture>
34
+ </section>
35
+ <section id="transcript" aria-live="polite"></section>
36
+ </main>
37
+
38
+ <div class="composer-wrap">
39
+ <form id="composer" class="composer">
40
+ <textarea id="input" rows="1" placeholder="Once upon a time&hellip;" autocomplete="off" spellcheck="true"></textarea>
41
+ <div class="composer-row">
42
+ <div class="composer-tools">
43
+ <div class="menu-anchor">
44
+ <button type="button" id="model-btn" class="chip" aria-haspopup="true" aria-expanded="false">
45
+ <span id="model-btn-label">loading&hellip;</span>
46
+ </button>
47
+ <div id="model-menu" class="menu" role="menu" hidden></div>
48
+ </div>
49
+ <div class="menu-anchor">
50
+ <button type="button" id="config-btn" class="chip chip-icon" aria-label="Sampling settings" title="Sampling settings" aria-haspopup="true" aria-expanded="false">
51
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true">
52
+ <line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/>
53
+ </svg>
54
+ </button>
55
+ <div id="config-menu" class="menu menu-config" hidden>
56
+ <p class="menu-heading">Sampling</p>
57
+ <label>Max tokens
58
+ <input id="cfg-max-tokens" type="number" min="1" max="400" step="1" value="150" />
59
+ </label>
60
+ <label>Temperature
61
+ <input id="cfg-temperature" type="number" min="0" max="2" step="0.1" value="0.8" />
62
+ </label>
63
+ <label>Top-k <span class="hint">(0 = off)</span>
64
+ <input id="cfg-top-k" type="number" min="0" max="4096" step="1" value="40" />
65
+ </label>
66
+ <label>Seed
67
+ <input id="cfg-seed" type="number" min="0" step="1" value="0" />
68
+ </label>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ <button id="send-btn" type="submit" class="send" aria-label="Continue the story" disabled>
73
+ <svg class="ic-quill" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
74
+ <path d="M20.24 12.24a6 6 0 0 0-8.49-8.49L5 10.5V19h8.5z" /><line x1="16" y1="8" x2="2" y2="22" />
75
+ </svg>
76
+ <svg class="ic-stop" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true">
77
+ <rect x="6" y="6" width="12" height="12" rx="2" />
78
+ </svg>
79
+ </button>
80
+ </div>
81
+ </form>
82
+ <p class="colophon">every word is written on your machine &mdash; nothing leaves the page</p>
83
+ </div>
84
+
85
+ <div id="toast-region" aria-live="assertive"></div>
86
+ <aside id="progress-card" hidden>
87
+ <p class="progress-title">Installing models</p>
88
+ <p id="progress-label" class="progress-label"></p>
89
+ <div class="progress-track"><div id="progress-fill" class="progress-fill"></div></div>
90
+ <p id="progress-overall" class="progress-overall"></p>
91
+ </aside>
92
+
93
+ <script type="module" src="js/app.js"></script>
94
+ </body>
95
  </html>
js/app.js ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // UI state machine: model registry/picker, install-all flow, config popover,
2
+ // validation toasts (bottom-left), download progress (bottom-right), and the
3
+ // token-streaming story renderer.
4
+
5
+ import { EOS_ID, loadTokenizer } from "./tokenizer.js";
6
+ import { fetchModel, isCached, loadManifest, parsePack } from "./pack.js";
7
+ import { Model, greedy, mulberry32, sampleToken } from "./model.js";
8
+ import { fixMojibake, mojibakeFilter } from "./display.js";
9
+ import {
10
+ RegExpMatcher,
11
+ englishDataset,
12
+ englishRecommendedTransformers,
13
+ } from "../vendor/obscenity.mjs";
14
+
15
+ const $ = (id) => document.getElementById(id);
16
+ const el = {
17
+ installBtn: $("install-btn"),
18
+ hero: $("hero"),
19
+ heroBytes: $("hero-bytes"),
20
+ transcript: $("transcript"),
21
+ main: $("main"),
22
+ composer: $("composer"),
23
+ input: $("input"),
24
+ sendBtn: $("send-btn"),
25
+ modelBtn: $("model-btn"),
26
+ modelBtnLabel: $("model-btn-label"),
27
+ modelMenu: $("model-menu"),
28
+ configBtn: $("config-btn"),
29
+ configMenu: $("config-menu"),
30
+ toastRegion: $("toast-region"),
31
+ progressCard: $("progress-card"),
32
+ progressLabel: $("progress-label"),
33
+ progressFill: $("progress-fill"),
34
+ progressOverall: $("progress-overall"),
35
+ };
36
+
37
+ const state = {
38
+ entries: [], // manifest entries + { available }
39
+ currentId: null,
40
+ model: null,
41
+ tokenizer: null,
42
+ matcher: new RegExpMatcher({
43
+ ...englishDataset.build(),
44
+ ...englishRecommendedTransformers,
45
+ }),
46
+ generating: false,
47
+ installing: false,
48
+ stopRequested: false,
49
+ config: { maxTokens: 150, temperature: 0.8, topK: 40, seed: 0 },
50
+ };
51
+
52
+ const fmtMB = (b) => `${(b / 1e6).toFixed(1)} MB`;
53
+ const tick = () => new Promise((r) => setTimeout(r, 0));
54
+
55
+ // ---- toasts (bottom-left) --------------------------------------------------
56
+
57
+ function toast(message) {
58
+ const t = document.createElement("div");
59
+ t.className = "toast";
60
+ t.textContent = message;
61
+ el.toastRegion.append(t);
62
+ setTimeout(() => t.remove(), 6000);
63
+ }
64
+
65
+ // ---- model registry / picker ------------------------------------------------
66
+
67
+ function currentEntry() {
68
+ return state.entries.find((e) => e.id === state.currentId);
69
+ }
70
+
71
+ function renderModelMenu() {
72
+ el.modelMenu.replaceChildren();
73
+ for (const e of state.entries) {
74
+ const item = document.createElement("button");
75
+ item.type = "button";
76
+ item.className = "menu-item" + (e.id === state.currentId ? " selected" : "");
77
+ item.disabled = !e.available;
78
+ const label = document.createElement("span");
79
+ label.textContent = e.label;
80
+ const size = document.createElement("span");
81
+ size.className = "size";
82
+ size.textContent = fmtMB(e.bytes);
83
+ item.append(label, size);
84
+ item.addEventListener("click", () => {
85
+ closeMenus();
86
+ if (e.id !== state.currentId) switchModel(e.id);
87
+ });
88
+ if (e.available) {
89
+ el.modelMenu.append(item);
90
+ } else {
91
+ // A disabled button swallows hover in some browsers, so the tooltip lives
92
+ // on an enabled wrapper around it.
93
+ const row = document.createElement("span");
94
+ row.className = "menu-row locked";
95
+ const tip = document.createElement("span");
96
+ tip.className = "menu-tip";
97
+ tip.textContent = "Not installed yet — press the install button (top left) to download it.";
98
+ row.append(item, tip);
99
+ el.modelMenu.append(row);
100
+ }
101
+ }
102
+ }
103
+
104
+ function updateChrome() {
105
+ const e = currentEntry();
106
+ el.modelBtnLabel.textContent = e
107
+ ? `${e.label} · ${fmtMB(e.bytes)}`
108
+ : "loading…";
109
+ if (e) el.heroBytes.textContent = fmtMB(e.bytes);
110
+ const allInstalled = state.entries.every((x) => x.available);
111
+ el.installBtn.disabled = state.installing || allInstalled;
112
+ el.installBtn.classList.toggle("done", allInstalled);
113
+ if (allInstalled) el.installBtn.title = "All models installed";
114
+ el.sendBtn.disabled = !state.model && !state.generating;
115
+ renderModelMenu();
116
+ }
117
+
118
+ async function switchModel(id) {
119
+ const entry = state.entries.find((e) => e.id === id);
120
+ if (!entry || state.generating) return;
121
+ el.modelBtnLabel.textContent = `loading ${entry.label}…`;
122
+ el.sendBtn.disabled = true;
123
+ try {
124
+ const buffer = await fetchModel(entry.url);
125
+ state.model = new Model(parsePack(buffer));
126
+ state.currentId = id;
127
+ } catch (err) {
128
+ toast(`Couldn't load ${entry.label} — ${err.message}`);
129
+ }
130
+ updateChrome();
131
+ }
132
+
133
+ // ---- install-all flow (progress bottom-right) --------------------------------
134
+
135
+ async function installAll() {
136
+ if (state.installing) return;
137
+ const missing = state.entries.filter((e) => !e.available);
138
+ if (!missing.length) return;
139
+ state.installing = true;
140
+ updateChrome();
141
+ el.progressCard.hidden = false;
142
+
143
+ let done = 0;
144
+ for (const entry of missing) {
145
+ el.progressLabel.textContent = `${entry.label} — ${fmtMB(entry.bytes)}`;
146
+ el.progressOverall.textContent = `${done + 1} of ${missing.length}`;
147
+ el.progressFill.style.width = "0";
148
+ try {
149
+ await fetchModel(entry.url, (loaded, total) => {
150
+ const pct = Math.min(100, (loaded / (total ?? entry.bytes)) * 100);
151
+ el.progressFill.style.width = `${pct}%`;
152
+ });
153
+ entry.available = true;
154
+ renderModelMenu();
155
+ } catch {
156
+ toast(`${entry.label} isn't available yet.`);
157
+ }
158
+ done++;
159
+ }
160
+
161
+ el.progressCard.hidden = true;
162
+ state.installing = false;
163
+ updateChrome();
164
+ }
165
+
166
+ // ---- config popover -----------------------------------------------------------
167
+
168
+ const cfgInputs = {
169
+ maxTokens: { el: $("cfg-max-tokens"), min: 1, max: 400, int: true },
170
+ temperature: { el: $("cfg-temperature"), min: 0, max: 2, int: false },
171
+ topK: { el: $("cfg-top-k"), min: 0, max: 4096, int: true },
172
+ seed: { el: $("cfg-seed"), min: 0, max: 2 ** 31 - 1, int: true },
173
+ };
174
+
175
+ for (const [key, c] of Object.entries(cfgInputs)) {
176
+ c.el.addEventListener("change", () => {
177
+ let v = Number(c.el.value);
178
+ if (!Number.isFinite(v)) v = state.config[key];
179
+ v = Math.min(c.max, Math.max(c.min, c.int ? Math.round(v) : v));
180
+ c.el.value = v;
181
+ state.config[key] = v;
182
+ });
183
+ }
184
+
185
+ // ---- menus ----------------------------------------------------------------------
186
+
187
+ function closeMenus() {
188
+ for (const [btn, menu] of [
189
+ [el.modelBtn, el.modelMenu],
190
+ [el.configBtn, el.configMenu],
191
+ ]) {
192
+ menu.hidden = true;
193
+ btn.setAttribute("aria-expanded", "false");
194
+ }
195
+ }
196
+
197
+ function toggleMenu(btn, menu) {
198
+ const open = menu.hidden;
199
+ closeMenus();
200
+ if (open) {
201
+ menu.hidden = false;
202
+ btn.setAttribute("aria-expanded", "true");
203
+ }
204
+ }
205
+
206
+ el.modelBtn.addEventListener("click", () => toggleMenu(el.modelBtn, el.modelMenu));
207
+ el.configBtn.addEventListener("click", () => toggleMenu(el.configBtn, el.configMenu));
208
+ document.addEventListener("click", (ev) => {
209
+ if (!ev.target.closest(".menu-anchor")) closeMenus();
210
+ });
211
+ document.addEventListener("keydown", (ev) => {
212
+ if (ev.key === "Escape") closeMenus();
213
+ });
214
+
215
+ // ---- transcript rendering --------------------------------------------------------
216
+
217
+ function addUserLine(text) {
218
+ const label = document.createElement("p");
219
+ label.className = "entry-user";
220
+ label.textContent = "You began";
221
+ el.transcript.append(label);
222
+
223
+ const story = document.createElement("p");
224
+ story.className = "story streaming";
225
+ const first = text.match(/\S/);
226
+ if (first) {
227
+ const cap = document.createElement("span");
228
+ cap.className = "dropcap";
229
+ cap.textContent = text.slice(0, first.index + 1).trimStart();
230
+ story.append(cap, document.createTextNode(text.slice(first.index + 1)));
231
+ } else {
232
+ story.append(document.createTextNode(text));
233
+ }
234
+ el.transcript.append(story);
235
+ return story;
236
+ }
237
+
238
+ function appendText(story, text) {
239
+ if (!text) return;
240
+ story.lastChild.nodeType === Node.TEXT_NODE
241
+ ? (story.lastChild.data += text)
242
+ : story.append(document.createTextNode(text));
243
+ el.main.scrollTop = el.main.scrollHeight;
244
+ }
245
+
246
+ function finishStory(story, note) {
247
+ story.classList.remove("streaming");
248
+ if (note) {
249
+ const n = document.createElement("p");
250
+ n.className = "story-note";
251
+ n.textContent = note;
252
+ story.after(n);
253
+ }
254
+ const fin = document.createElement("p");
255
+ fin.className = "fin";
256
+ fin.textContent = "❦";
257
+ el.transcript.append(fin);
258
+ }
259
+
260
+ // ---- generation -------------------------------------------------------------------
261
+
262
+ function setGenerating(on) {
263
+ state.generating = on;
264
+ document.body.classList.toggle("generating", on);
265
+ el.modelBtn.disabled = on;
266
+ el.configBtn.disabled = on;
267
+ el.input.disabled = on;
268
+ el.sendBtn.setAttribute("aria-label", on ? "Stop writing" : "Continue the story");
269
+ if (on) closeMenus();
270
+ }
271
+
272
+ // Attempts to phish keys/secrets out of the storyteller, or a pasted key itself.
273
+ const KEY_HUNT_RE =
274
+ /\b(?:api|secret|access|private|auth(?:orization)?)[\s_-]*(?:key|token|credential)s?\b|\bsk-[A-Za-z0-9]{8,}\b/i;
275
+
276
+ function validate(text, ids) {
277
+ if (!text.trim()) return "Write an opening line first.";
278
+ if (KEY_HUNT_RE.test(text))
279
+ return "Bar'd has no secrets here to tell :D";
280
+ if (state.matcher.hasMatch(text))
281
+ return "Guys, please don't swear.";
282
+ const ctx = state.model.cfg.ctx;
283
+ if (ids.length + state.config.maxTokens > ctx)
284
+ return `That opening is too long at ${ids.length} tokens of opening in addition to the ${state.config.maxTokens} max tokens exceeding the model's ${ctx}-token memory.`;
285
+ return null;
286
+ }
287
+
288
+ async function runStory(text) {
289
+ const ids = state.tokenizer.encode(text);
290
+ const problem = validate(text, ids);
291
+ if (problem) {
292
+ toast(problem);
293
+ return;
294
+ }
295
+
296
+ document.body.classList.add("storytime");
297
+ setGenerating(true);
298
+ state.stopRequested = false;
299
+ const story = addUserLine(fixMojibake(text));
300
+ const model = state.model;
301
+ const { maxTokens, temperature, topK, seed } = state.config;
302
+
303
+ try {
304
+ model.reset();
305
+ let logits = null;
306
+ for (let i = 0; i < ids.length; i++) {
307
+ logits = model.step(ids[i]);
308
+ if (i % 16 === 15) await tick();
309
+ }
310
+
311
+ const rng = mulberry32(seed);
312
+ const decoder = state.tokenizer.streamDecoder();
313
+ const display = mojibakeFilter();
314
+ let note = null;
315
+ for (let n = 0; n < maxTokens; n++) {
316
+ if (state.stopRequested) break;
317
+ const id = sampleToken(logits, { temperature, topK }, rng);
318
+ if (id === EOS_ID) break;
319
+ appendText(story, display.push(decoder.push(id)));
320
+ if (model.pos >= model.cfg.ctx) {
321
+ note = "The model reached its context limit and had to stop.";
322
+ break;
323
+ }
324
+ logits = model.step(id);
325
+ await tick();
326
+ }
327
+ appendText(story, display.push(decoder.flush()));
328
+ appendText(story, display.flush());
329
+ finishStory(story, note);
330
+ } catch (err) {
331
+ finishStory(story, `Something went wrong while writing: ${err.message}`);
332
+ }
333
+ setGenerating(false);
334
+ }
335
+
336
+ // ---- composer events -----------------------------------------------------------
337
+
338
+ el.composer.addEventListener("submit", (ev) => {
339
+ ev.preventDefault();
340
+ if (state.generating) {
341
+ state.stopRequested = true;
342
+ return;
343
+ }
344
+ if (!state.model) return;
345
+ const text = el.input.value;
346
+ el.input.value = "";
347
+ el.input.style.height = "auto";
348
+ runStory(text);
349
+ });
350
+
351
+ el.input.addEventListener("keydown", (ev) => {
352
+ if (ev.key === "Enter" && !ev.shiftKey) {
353
+ ev.preventDefault();
354
+ el.composer.requestSubmit();
355
+ }
356
+ });
357
+
358
+ el.input.addEventListener("input", () => {
359
+ el.input.style.height = "auto";
360
+ el.input.style.height = `${Math.min(el.input.scrollHeight, 144)}px`;
361
+ });
362
+
363
+ el.installBtn.addEventListener("click", installAll);
364
+
365
+ // ---- startup -------------------------------------------------------------------
366
+
367
+ async function main() {
368
+ try {
369
+ const [manifest, tokenizer] = await Promise.all([
370
+ loadManifest(),
371
+ loadTokenizer(),
372
+ ]);
373
+ state.tokenizer = tokenizer;
374
+ state.entries = await Promise.all(
375
+ manifest.models.map(async (e) => ({
376
+ ...e,
377
+ available: e.baked || (await isCached(e.url)),
378
+ }))
379
+ );
380
+
381
+ // Default: smallest gate-passing model that's available; fallback = baked.
382
+ const candidates = state.entries
383
+ .filter((e) => e.available && e.gate === true)
384
+ .sort((a, b) => a.bytes - b.bytes);
385
+ const first = candidates[0] ?? state.entries.find((e) => e.baked);
386
+ updateChrome();
387
+ await switchModel(first.id);
388
+ } catch (err) {
389
+ toast(`The storyteller couldn't start: ${err.message}`);
390
+ }
391
+ }
392
+
393
+ // Dev/parity hook (docs/demo_site.md): greedy token ids for a prompt.
394
+ window.__greedy = (prompt, n) =>
395
+ greedy(state.model, state.tokenizer.encode(prompt), n);
396
+
397
+ main();
js/display.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Display-time repair of the TinyStories corpus's CP1252 mojibake.
2
+ // The dataset stores non-ASCII punctuation double-encoded (each UTF-8 byte re-read
3
+ // as CP1252), so the model legitimately *generates* mojibake — the sequences are
4
+ // merged BPE tokens — and it can also emit *fragments* of them (a continuation
5
+ // token without its lead byte), which no lookup table of known sequences can
6
+ // anticipate. So the encoding is reversed generically instead: map each char back
7
+ // to its CP1252 byte, decode valid UTF-8 runs to the intended character, and treat
8
+ // anything that fails as mojibake residue.
9
+ //
10
+ // Residue policy differs by source:
11
+ // - model output (mojibakeFilter): residue is DROPPED — the corpus's visible text
12
+ // is ASCII + mojibake only, so an undecodable non-ASCII char from the model is a
13
+ // broken fragment, never content. Exception: a bare "â€" whose third byte was
14
+ // lost is the corpus's broken closing quote — rendered ”.
15
+ // - user input (fixMojibake): residue is KEPT — people legitimately type curly
16
+ // quotes, é, emoji.
17
+ // Token ids, the tokenizer, the dataset, and the parity hooks are untouched.
18
+
19
+ // CP1252 codepoint -> byte for the 0x80–0x9F specials. Every other char below
20
+ // 0x100 maps to itself — including the bytes CP1252 leaves *undefined* (e.g.
21
+ // 0x9D), which the dataset pipeline carried through as literal C1 controls.
22
+ const CP1252_HIGH = {
23
+ 0x20ac: 0x80, 0x201a: 0x82, 0x0192: 0x83, 0x201e: 0x84, 0x2026: 0x85,
24
+ 0x2020: 0x86, 0x2021: 0x87, 0x02c6: 0x88, 0x2030: 0x89, 0x0160: 0x8a,
25
+ 0x2039: 0x8b, 0x0152: 0x8c, 0x017d: 0x8e, 0x2018: 0x91, 0x2019: 0x92,
26
+ 0x201c: 0x93, 0x201d: 0x94, 0x2022: 0x95, 0x2013: 0x96, 0x2014: 0x97,
27
+ 0x02dc: 0x98, 0x2122: 0x99, 0x0161: 0x9a, 0x203a: 0x9b, 0x0153: 0x9c,
28
+ 0x017e: 0x9e, 0x0178: 0x9f,
29
+ };
30
+
31
+ // undefined = not CP1252-expressible, so it cannot be part of mojibake.
32
+ const byteOf = (ch) => {
33
+ const cp = ch.codePointAt(0);
34
+ return cp < 0x100 ? cp : CP1252_HIGH[cp];
35
+ };
36
+
37
+ const seqLen = (b) =>
38
+ b >= 0xc2 && b <= 0xdf ? 2 : b >= 0xe0 && b <= 0xef ? 3 : b >= 0xf0 && b <= 0xf4 ? 4 : 0;
39
+ const isCont = (b) => b >= 0x80 && b <= 0xbf;
40
+
41
+ const utf8 = new TextDecoder("utf-8", { fatal: true });
42
+
43
+ // One pass over `s`. When !atEnd, an unfinished candidate sequence at the tail is
44
+ // returned in `hold` for the next chunk to complete (streaming-safe splits).
45
+ function unmojibake(s, atEnd, keepResidue) {
46
+ let out = "";
47
+ let i = 0;
48
+ while (i < s.length) {
49
+ const ch = s[i];
50
+ const b = byteOf(ch);
51
+ if (b === undefined || b < 0x80) {
52
+ out += ch;
53
+ i++;
54
+ continue;
55
+ }
56
+ const need = seqLen(b);
57
+ const bytes = [b];
58
+ let j = i + 1;
59
+ while (j < s.length && bytes.length < need) {
60
+ const cb = byteOf(s[j]);
61
+ if (cb === undefined || !isCont(cb)) break;
62
+ bytes.push(cb);
63
+ j++;
64
+ }
65
+ if (need && bytes.length === need) {
66
+ try {
67
+ const decoded = utf8.decode(new Uint8Array(bytes));
68
+ if (decoded.codePointAt(0) > 0x9f) {
69
+ // (≤ 0x9f would be an invisible C1 control — treat as residue)
70
+ out += decoded;
71
+ i = j;
72
+ continue;
73
+ }
74
+ } catch {
75
+ /* not a real sequence — residue */
76
+ }
77
+ } else if (need && j === s.length && !atEnd) {
78
+ return { out, hold: s.slice(i) };
79
+ }
80
+ if (b === 0xe2 && bytes.length >= 2 && bytes[1] === 0x80) {
81
+ out += "”"; // corpus's broken closing quote (its 0x9d byte lost)
82
+ i += 2;
83
+ continue;
84
+ }
85
+ if (keepResidue) out += ch;
86
+ i++;
87
+ }
88
+ return { out, hold: "" };
89
+ }
90
+
91
+ export function fixMojibake(s) {
92
+ return unmojibake(s, true, true).out;
93
+ }
94
+
95
+ export function mojibakeFilter() {
96
+ let buf = "";
97
+ return {
98
+ push(text) {
99
+ const { out, hold } = unmojibake(buf + text, false, false);
100
+ buf = hold;
101
+ return out;
102
+ },
103
+ flush() {
104
+ const { out } = unmojibake(buf, true, false);
105
+ buf = "";
106
+ return out;
107
+ },
108
+ };
109
+ }
js/model.js ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Client-side forward pass for the nanofable decoder-only transformer, mirroring
2
+ // src/nanofable/model.py exactly: pre-norm RMSNorm (eps 1e-5), NeoX RoPE (base 10000),
3
+ // causal attention (1/sqrt(head_dim)), SwiGLU, tied embedding/LM head.
4
+ // Single-token steps with a KV cache; prefill is the same step run per prompt token.
5
+
6
+ const EPS = 1e-5;
7
+
8
+ // y = W x, W row-major [nOut, nIn]; ternary weights dispatch on .trits.
9
+ function linear(out, x, W, nOut, nIn) {
10
+ const w = W.trits ?? W.f32;
11
+ const scale = W.trits ? W.scale : 1;
12
+ for (let o = 0; o < nOut; o++) {
13
+ let acc = 0;
14
+ const row = o * nIn;
15
+ for (let i = 0; i < nIn; i++) acc += x[i] * w[row + i];
16
+ out[o] = acc * scale;
17
+ }
18
+ }
19
+
20
+ function rmsnorm(out, x, gain, n) {
21
+ let ss = 0;
22
+ for (let i = 0; i < n; i++) ss += x[i] * x[i];
23
+ const inv = 1 / Math.sqrt(ss / n + EPS);
24
+ for (let i = 0; i < n; i++) out[i] = x[i] * inv * gain[i];
25
+ }
26
+
27
+ function silu(v) {
28
+ return v / (1 + Math.exp(-v));
29
+ }
30
+
31
+ export class Model {
32
+ constructor({ header, tensors }) {
33
+ const cfg = header.config;
34
+ this.cfg = cfg;
35
+ this.headDim = cfg.n_embd / cfg.n_head;
36
+
37
+ const get = (name) => {
38
+ const t = tensors.get(name);
39
+ if (!t) throw new Error(`pack missing tensor ${name}`);
40
+ return t;
41
+ };
42
+ this.tokEmb = get("tok_emb.weight").f32; // [vocab, n_embd], also the tied head
43
+ this.finalNorm = get("final_norm.weight").f32;
44
+ this.layers = [];
45
+ for (let i = 0; i < cfg.n_layer; i++) {
46
+ const p = `blocks.${i}.`;
47
+ this.layers.push({
48
+ attnNorm: get(p + "attn_norm.weight").f32,
49
+ q: get(p + "attn.q.weight"), k: get(p + "attn.k.weight"),
50
+ v: get(p + "attn.v.weight"), o: get(p + "attn.o.weight"),
51
+ mlpNorm: get(p + "mlp_norm.weight").f32,
52
+ gate: get(p + "mlp.gate.weight"), up: get(p + "mlp.up.weight"),
53
+ down: get(p + "mlp.down.weight"),
54
+ });
55
+ }
56
+ this.mlpHidden = get("blocks.0.mlp.gate.weight").trits
57
+ ? tensors.get("blocks.0.mlp.gate.weight").trits.length / cfg.n_embd
58
+ : tensors.get("blocks.0.mlp.gate.weight").f32.length / cfg.n_embd;
59
+
60
+ // RoPE cache: cos/sin of pos * invFreq[j], j < headDim/2 (NeoX pairs j, j+hd/2).
61
+ const half = this.headDim / 2;
62
+ this.ropeCos = new Float32Array(cfg.ctx * half);
63
+ this.ropeSin = new Float32Array(cfg.ctx * half);
64
+ for (let p = 0; p < cfg.ctx; p++) {
65
+ for (let j = 0; j < half; j++) {
66
+ const angle = p / 10000 ** ((2 * j) / this.headDim);
67
+ this.ropeCos[p * half + j] = Math.cos(angle);
68
+ this.ropeSin[p * half + j] = Math.sin(angle);
69
+ }
70
+ }
71
+
72
+ // KV cache + scratch buffers.
73
+ const E = cfg.n_embd;
74
+ this.kCache = this.layers.map(() => new Float32Array(cfg.ctx * E));
75
+ this.vCache = this.layers.map(() => new Float32Array(cfg.ctx * E));
76
+ this.pos = 0;
77
+ this.x = new Float32Array(E);
78
+ this.xn = new Float32Array(E);
79
+ this.qB = new Float32Array(E);
80
+ this.kB = new Float32Array(E);
81
+ this.vB = new Float32Array(E);
82
+ this.attnB = new Float32Array(E);
83
+ this.projB = new Float32Array(E);
84
+ this.gB = new Float32Array(this.mlpHidden);
85
+ this.uB = new Float32Array(this.mlpHidden);
86
+ this.scores = new Float32Array(cfg.ctx);
87
+ this.logits = new Float32Array(cfg.vocab);
88
+ }
89
+
90
+ reset() {
91
+ this.pos = 0;
92
+ }
93
+
94
+ applyRope(vec, pos) {
95
+ const { n_head } = this.cfg;
96
+ const hd = this.headDim, half = hd / 2;
97
+ for (let h = 0; h < n_head; h++) {
98
+ const base = h * hd;
99
+ for (let j = 0; j < half; j++) {
100
+ const c = this.ropeCos[pos * half + j];
101
+ const s = this.ropeSin[pos * half + j];
102
+ const a = vec[base + j], b = vec[base + j + half];
103
+ vec[base + j] = a * c - b * s;
104
+ vec[base + j + half] = b * c + a * s;
105
+ }
106
+ }
107
+ }
108
+
109
+ // Feed one token at the current position; returns logits (valid until next step).
110
+ step(tokenId) {
111
+ const cfg = this.cfg, E = cfg.n_embd, hd = this.headDim;
112
+ const pos = this.pos;
113
+ if (pos >= cfg.ctx) throw new Error("context window full");
114
+ this.x.set(this.tokEmb.subarray(tokenId * E, (tokenId + 1) * E));
115
+
116
+ for (let l = 0; l < this.layers.length; l++) {
117
+ const L = this.layers[l];
118
+ // attention
119
+ rmsnorm(this.xn, this.x, L.attnNorm, E);
120
+ linear(this.qB, this.xn, L.q, E, E);
121
+ linear(this.kB, this.xn, L.k, E, E);
122
+ linear(this.vB, this.xn, L.v, E, E);
123
+ this.applyRope(this.qB, pos);
124
+ this.applyRope(this.kB, pos);
125
+ this.kCache[l].set(this.kB, pos * E);
126
+ this.vCache[l].set(this.vB, pos * E);
127
+
128
+ const K = this.kCache[l], V = this.vCache[l];
129
+ const invSqrt = 1 / Math.sqrt(hd);
130
+ for (let h = 0; h < cfg.n_head; h++) {
131
+ const ho = h * hd;
132
+ let max = -Infinity;
133
+ for (let p = 0; p <= pos; p++) {
134
+ let dot = 0;
135
+ const ko = p * E + ho;
136
+ for (let j = 0; j < hd; j++) dot += this.qB[ho + j] * K[ko + j];
137
+ const sc = dot * invSqrt;
138
+ this.scores[p] = sc;
139
+ if (sc > max) max = sc;
140
+ }
141
+ let sum = 0;
142
+ for (let p = 0; p <= pos; p++) {
143
+ const e = Math.exp(this.scores[p] - max);
144
+ this.scores[p] = e;
145
+ sum += e;
146
+ }
147
+ for (let j = 0; j < hd; j++) {
148
+ let acc = 0;
149
+ for (let p = 0; p <= pos; p++) acc += this.scores[p] * V[p * E + ho + j];
150
+ this.attnB[ho + j] = acc / sum;
151
+ }
152
+ }
153
+ linear(this.projB, this.attnB, L.o, E, E);
154
+ for (let i = 0; i < E; i++) this.x[i] += this.projB[i];
155
+
156
+ // mlp
157
+ rmsnorm(this.xn, this.x, L.mlpNorm, E);
158
+ linear(this.gB, this.xn, L.gate, this.mlpHidden, E);
159
+ linear(this.uB, this.xn, L.up, this.mlpHidden, E);
160
+ for (let i = 0; i < this.mlpHidden; i++) this.gB[i] = silu(this.gB[i]) * this.uB[i];
161
+ linear(this.projB, this.gB, L.down, E, this.mlpHidden);
162
+ for (let i = 0; i < E; i++) this.x[i] += this.projB[i];
163
+ }
164
+
165
+ rmsnorm(this.xn, this.x, this.finalNorm, E);
166
+ linear(this.logits, this.xn, { f32: this.tokEmb }, cfg.vocab, E);
167
+ this.pos++;
168
+ return this.logits;
169
+ }
170
+
171
+ // Run all prompt tokens; returns logits after the last one.
172
+ prefill(ids) {
173
+ let logits = null;
174
+ for (const id of ids) logits = this.step(id);
175
+ return logits;
176
+ }
177
+ }
178
+
179
+ // Deterministic PRNG for reproducible sampling (JS-vs-JS; torch RNG differs).
180
+ // The seed is scrambled splitmix-style first: raw small seeds (0, 1, 2 …) give
181
+ // mulberry32 a near-empty state whose first few outputs are biased tiny, which
182
+ // visibly skews the first sampled tokens.
183
+ export function mulberry32(seed) {
184
+ let a = (seed >>> 0) ^ 0x9e3779b9;
185
+ a = Math.imul(a ^ (a >>> 16), 0x21f0aaad);
186
+ a = Math.imul(a ^ (a >>> 15), 0x735a2d97);
187
+ a = (a ^ (a >>> 15)) >>> 0;
188
+ return function () {
189
+ a = (a + 0x6d2b79f5) | 0;
190
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
191
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
192
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
193
+ };
194
+ }
195
+
196
+ // Mirrors src/nanofable/generate.py next_token: temperature floor 1e-6, top-k keeps
197
+ // ties at the k-th value, top_k=0 disables the mask, then multinomial via CDF.
198
+ export function sampleToken(logits, { temperature, topK }, rand) {
199
+ const n = logits.length;
200
+ const t = Math.max(temperature, 1e-6);
201
+ const scaled = new Float32Array(n);
202
+ for (let i = 0; i < n; i++) scaled[i] = logits[i] / t;
203
+
204
+ if (topK) {
205
+ const k = Math.min(topK, n);
206
+ const sorted = Float32Array.from(scaled).sort().reverse();
207
+ const threshold = sorted[k - 1];
208
+ for (let i = 0; i < n; i++) if (scaled[i] < threshold) scaled[i] = -Infinity;
209
+ }
210
+
211
+ let max = -Infinity;
212
+ for (let i = 0; i < n; i++) if (scaled[i] > max) max = scaled[i];
213
+ let sum = 0;
214
+ for (let i = 0; i < n; i++) {
215
+ scaled[i] = Math.exp(scaled[i] - max);
216
+ sum += scaled[i];
217
+ }
218
+ const r = rand() * sum;
219
+ let cum = 0;
220
+ for (let i = 0; i < n; i++) {
221
+ cum += scaled[i];
222
+ if (r < cum) return i;
223
+ }
224
+ return n - 1;
225
+ }
226
+
227
+ // Dev/parity helper: greedy (argmax) continuation, comparable to a Python argmax loop.
228
+ export function greedy(model, ids, n) {
229
+ model.reset();
230
+ let logits = model.prefill(ids);
231
+ const out = [];
232
+ for (let i = 0; i < n && model.pos < model.cfg.ctx; i++) {
233
+ let best = 0;
234
+ for (let j = 1; j < logits.length; j++) if (logits[j] > logits[best]) best = j;
235
+ out.push(best);
236
+ logits = model.step(best);
237
+ }
238
+ return out;
239
+ }
js/pack.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // .tpack (pack format v1 "TCP1") loader + model download/cache helpers.
2
+ // Format is defined normatively in src/nanofable/pack.py.
3
+
4
+ const MAGIC = 0x31504354; // "TCP1" little-endian
5
+ const ALIGN = 8;
6
+ const CACHE_NAME = "nanofable-models";
7
+
8
+ // 243 -> 5 trits lookup, built once.
9
+ const TRIT_LUT = (() => {
10
+ const lut = new Int8Array(243 * 5);
11
+ for (let b = 0; b < 243; b++) {
12
+ let v = b;
13
+ for (let j = 0; j < 5; j++) {
14
+ lut[b * 5 + j] = (v % 3) - 1;
15
+ v = (v / 3) | 0;
16
+ }
17
+ }
18
+ return lut;
19
+ })();
20
+
21
+ function unpackTrits(bytes, numel) {
22
+ const out = new Int8Array(numel);
23
+ const full = Math.floor(numel / 5);
24
+ let o = 0;
25
+ for (let i = 0; i < full; i++) {
26
+ const base = bytes[i] * 5;
27
+ out[o++] = TRIT_LUT[base];
28
+ out[o++] = TRIT_LUT[base + 1];
29
+ out[o++] = TRIT_LUT[base + 2];
30
+ out[o++] = TRIT_LUT[base + 3];
31
+ out[o++] = TRIT_LUT[base + 4];
32
+ }
33
+ for (let j = 0; o < numel; j++) out[o++] = TRIT_LUT[bytes[full] * 5 + j];
34
+ return out;
35
+ }
36
+
37
+ function halfToFloat32(u16) {
38
+ const out = new Float32Array(u16.length);
39
+ for (let i = 0; i < u16.length; i++) {
40
+ const h = u16[i];
41
+ const sign = h & 0x8000 ? -1 : 1;
42
+ const exp = (h & 0x7c00) >> 10;
43
+ const frac = h & 0x03ff;
44
+ if (exp === 0) out[i] = sign * 2 ** -14 * (frac / 1024);
45
+ else if (exp === 0x1f) out[i] = frac ? NaN : sign * Infinity;
46
+ else out[i] = sign * 2 ** (exp - 15) * (1 + frac / 1024);
47
+ }
48
+ return out;
49
+ }
50
+
51
+ // Parse a pack into { header, tensors }. Tensor values are either
52
+ // { f32: Float32Array } or { trits: Int8Array, scale: number } — matmul-ready.
53
+ export function parsePack(buffer) {
54
+ const view = new DataView(buffer);
55
+ if (view.getUint32(0, true) !== MAGIC) throw new Error("not a TCP1 pack");
56
+ const headerLen = view.getUint32(4, true);
57
+ const header = JSON.parse(
58
+ new TextDecoder().decode(new Uint8Array(buffer, 8, headerLen))
59
+ );
60
+ const payload = Math.ceil((8 + headerLen) / ALIGN) * ALIGN;
61
+
62
+ const tensors = new Map();
63
+ for (const t of header.tensors) {
64
+ const numel = t.shape.reduce((a, b) => a * b, 1);
65
+ if (t.dtype === "trit5") {
66
+ const bytes = new Uint8Array(buffer, payload + t.offset, t.bytes);
67
+ tensors.set(t.name, { trits: unpackTrits(bytes, numel), scale: t.scale });
68
+ } else {
69
+ const u16 = new Uint16Array(buffer.slice(payload + t.offset, payload + t.offset + t.bytes));
70
+ tensors.set(t.name, { f32: halfToFloat32(u16) });
71
+ }
72
+ }
73
+ return { header, tensors };
74
+ }
75
+
76
+ export async function loadManifest(url = "manifest.json") {
77
+ const res = await fetch(url);
78
+ if (!res.ok) throw new Error(`manifest fetch failed: ${res.status}`);
79
+ return res.json();
80
+ }
81
+
82
+ const cacheAvailable = () => typeof caches !== "undefined";
83
+
84
+ export async function isCached(url) {
85
+ if (!cacheAvailable()) return false;
86
+ const cache = await caches.open(CACHE_NAME);
87
+ return (await cache.match(url)) !== undefined;
88
+ }
89
+
90
+ // Fetch a pack with download progress, storing it in the Cache API. Serves from
91
+ // cache when present. onProgress(loadedBytes, totalBytes|null) fires per chunk.
92
+ export async function fetchModel(url, onProgress) {
93
+ const cache = cacheAvailable() ? await caches.open(CACHE_NAME) : null;
94
+ const hit = cache && (await cache.match(url));
95
+ if (hit) return hit.arrayBuffer();
96
+
97
+ const res = await fetch(url);
98
+ if (!res.ok) throw new Error(`download failed (${res.status})`);
99
+ const total = Number(res.headers.get("Content-Length")) || null;
100
+ const reader = res.body.getReader();
101
+ const chunks = [];
102
+ let loaded = 0;
103
+ for (;;) {
104
+ const { done, value } = await reader.read();
105
+ if (done) break;
106
+ chunks.push(value);
107
+ loaded += value.length;
108
+ if (onProgress) onProgress(loaded, total);
109
+ }
110
+ const data = new Uint8Array(loaded);
111
+ let o = 0;
112
+ for (const c of chunks) {
113
+ data.set(c, o);
114
+ o += c.length;
115
+ }
116
+ if (cache) {
117
+ await cache.put(url, new Response(data.buffer.slice(0), {
118
+ headers: { "Content-Type": "application/octet-stream" },
119
+ }));
120
+ }
121
+ return data.buffer;
122
+ }
js/tokenizer.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ByteLevel-BPE port of the frozen 4k tokenizer (web/tokenizer.json — byte-identity
2
+ // with artifacts/tokenizer/tokenizer.json is guarded by tests/test_pack.py).
3
+ // Mirrors src/nanofable/generate.py: no BOS prepended; special ids skipped on decode.
4
+
5
+ export const BOS_ID = 0, EOS_ID = 1, PAD_ID = 2;
6
+
7
+ // GPT-2 byte <-> unicode table: printable bytes map to themselves, the rest to 256+n.
8
+ function byteUnicodeTables() {
9
+ const bs = [];
10
+ for (let b = 33; b <= 126; b++) bs.push(b);
11
+ for (let b = 161; b <= 172; b++) bs.push(b);
12
+ for (let b = 174; b <= 255; b++) bs.push(b);
13
+ const byteToChar = new Array(256), charToByte = new Map();
14
+ let n = 0;
15
+ for (let b = 0; b < 256; b++) {
16
+ const cp = bs.includes(b) ? b : 256 + n++;
17
+ byteToChar[b] = String.fromCharCode(cp);
18
+ charToByte.set(byteToChar[b], b);
19
+ }
20
+ return { byteToChar, charToByte };
21
+ }
22
+
23
+ // GPT-2 pre-tokenizer (ByteLevel use_regex:true, add_prefix_space:false).
24
+ const PRETOKEN_RE =
25
+ /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
26
+
27
+ export class Tokenizer {
28
+ constructor(tokenizerJson) {
29
+ const model = tokenizerJson.model;
30
+ this.vocab = new Map(Object.entries(model.vocab));
31
+ this.idToToken = new Array(this.vocab.size);
32
+ for (const [tok, id] of this.vocab) this.idToToken[id] = tok;
33
+ this.ranks = new Map(model.merges.map(([a, b], i) => [a + " " + b, i]));
34
+ const { byteToChar, charToByte } = byteUnicodeTables();
35
+ this.byteToChar = byteToChar;
36
+ this.charToByte = charToByte;
37
+ this.cache = new Map(); // pretoken -> ids
38
+ this.utf8 = new TextEncoder();
39
+ }
40
+
41
+ // Greedy BPE over one pre-token's byte-level chars.
42
+ bpe(chars) {
43
+ let parts = chars;
44
+ for (;;) {
45
+ let best = null, bestRank = Infinity;
46
+ for (let i = 0; i < parts.length - 1; i++) {
47
+ const r = this.ranks.get(parts[i] + " " + parts[i + 1]);
48
+ if (r !== undefined && r < bestRank) { bestRank = r; best = i; }
49
+ }
50
+ if (best === null) return parts;
51
+ parts = parts
52
+ .slice(0, best)
53
+ .concat([parts[best] + parts[best + 1]], parts.slice(best + 2));
54
+ }
55
+ }
56
+
57
+ encode(text) {
58
+ const ids = [];
59
+ for (const m of text.matchAll(PRETOKEN_RE)) {
60
+ const pre = m[0];
61
+ let cached = this.cache.get(pre);
62
+ if (!cached) {
63
+ const bytes = this.utf8.encode(pre);
64
+ const chars = Array.from(bytes, (b) => this.byteToChar[b]);
65
+ cached = this.bpe(chars)
66
+ .map((t) => this.vocab.get(t))
67
+ .filter((id) => id !== undefined);
68
+ this.cache.set(pre, cached);
69
+ }
70
+ ids.push(...cached);
71
+ }
72
+ return ids;
73
+ }
74
+
75
+ decode(ids) {
76
+ const d = this.streamDecoder();
77
+ return ids.map((id) => d.push(id)).join("") + d.flush();
78
+ }
79
+
80
+ // Streaming decoder: emits text per token, holding back incomplete UTF-8 chars.
81
+ streamDecoder() {
82
+ const utf8 = new TextDecoder("utf-8", { fatal: false });
83
+ const self = this;
84
+ return {
85
+ push(id) {
86
+ if (id <= PAD_ID) return ""; // specials skipped, as in HF decode
87
+ const tok = self.idToToken[id];
88
+ if (tok === undefined) return "";
89
+ const bytes = Uint8Array.from(tok, (c) => self.charToByte.get(c));
90
+ return utf8.decode(bytes, { stream: true });
91
+ },
92
+ flush() {
93
+ return utf8.decode();
94
+ },
95
+ };
96
+ }
97
+ }
98
+
99
+ export async function loadTokenizer(url = "tokenizer.json") {
100
+ const res = await fetch(url);
101
+ if (!res.ok) throw new Error(`tokenizer fetch failed: ${res.status}`);
102
+ return new Tokenizer(await res.json());
103
+ }
manifest.json ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "comment": "Model registry for the HF Space build. All 8 packs are served same-origin from models/ \u2014 no cross-origin fetch, no presigned-CDN redirect. Naming follows the HF release: NanoFable-<params>-<precision>.",
3
+ "models": [
4
+ {
5
+ "id": "nanofable-1m-ternary",
6
+ "tier": "tiny",
7
+ "params": "1M",
8
+ "precision": "ternary",
9
+ "label": "NanoFable-1M-ternary",
10
+ "url": "models/nanofable-1m-ternary.tpack",
11
+ "bytes": 1225968,
12
+ "ppl": null,
13
+ "judge": null,
14
+ "gate": null,
15
+ "baked": false
16
+ },
17
+ {
18
+ "id": "nanofable-1m-fp16",
19
+ "tier": "tiny",
20
+ "params": "1M",
21
+ "precision": "fp16",
22
+ "label": "NanoFable-1M-fp16",
23
+ "url": "models/nanofable-1m-fp16.tpack",
24
+ "bytes": 2758680,
25
+ "ppl": null,
26
+ "judge": null,
27
+ "gate": null,
28
+ "baked": false
29
+ },
30
+ {
31
+ "id": "nanofable-6m-ternary",
32
+ "tier": "small",
33
+ "params": "6M",
34
+ "precision": "ternary",
35
+ "label": "NanoFable-6M-ternary",
36
+ "url": "models/nanofable-6m-ternary.tpack",
37
+ "bytes": 3074144,
38
+ "ppl": null,
39
+ "judge": null,
40
+ "gate": null,
41
+ "baked": false
42
+ },
43
+ {
44
+ "id": "nanofable-6m-fp16",
45
+ "tier": "small",
46
+ "params": "6M",
47
+ "precision": "fp16",
48
+ "label": "NanoFable-6M-fp16",
49
+ "url": "models/nanofable-6m-fp16.tpack",
50
+ "bytes": 11743256,
51
+ "ppl": null,
52
+ "judge": null,
53
+ "gate": null,
54
+ "baked": false
55
+ },
56
+ {
57
+ "id": "nanofable-16m-ternary",
58
+ "tier": "medium",
59
+ "params": "16M",
60
+ "precision": "ternary",
61
+ "label": "NanoFable-16M-ternary",
62
+ "url": "models/nanofable-16m-ternary.tpack",
63
+ "bytes": 5999152,
64
+ "ppl": null,
65
+ "judge": null,
66
+ "gate": null,
67
+ "baked": false
68
+ },
69
+ {
70
+ "id": "nanofable-16m-fp16",
71
+ "tier": "medium",
72
+ "params": "16M",
73
+ "precision": "fp16",
74
+ "label": "NanoFable-16M-fp16",
75
+ "url": "models/nanofable-16m-fp16.tpack",
76
+ "bytes": 31477816,
77
+ "ppl": null,
78
+ "judge": null,
79
+ "gate": null,
80
+ "baked": true
81
+ },
82
+ {
83
+ "id": "nanofable-28m-ternary",
84
+ "tier": "large",
85
+ "params": "28M",
86
+ "precision": "ternary",
87
+ "label": "NanoFable-28M-ternary",
88
+ "url": "models/nanofable-28m-ternary.tpack",
89
+ "bytes": 9358936,
90
+ "ppl": null,
91
+ "judge": null,
92
+ "gate": null,
93
+ "baked": false
94
+ },
95
+ {
96
+ "id": "nanofable-28m-fp16",
97
+ "tier": "large",
98
+ "params": "28M",
99
+ "precision": "fp16",
100
+ "label": "NanoFable-28M-fp16",
101
+ "url": "models/nanofable-28m-fp16.tpack",
102
+ "bytes": 55599464,
103
+ "ppl": null,
104
+ "judge": null,
105
+ "gate": null,
106
+ "baked": false
107
+ }
108
+ ]
109
+ }
models/nanofable-16m-fp16.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6e7712d73dd1863474a7313ba6414c7c30c5e132de641d8eef318a405fa88f8c
3
+ size 31477816
models/nanofable-16m-ternary.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:79116ddc471c2f333960a69a61a6b452b2e92772c375053997eab7442d76775e
3
+ size 5999152
models/nanofable-1m-fp16.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e30a197b4917e0f5e84d76043e1fd60ff70724f360afb04592ea59d1a7d29d38
3
+ size 2758680
models/nanofable-1m-ternary.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d4a83324c7cac8c83fd5add3fd8575d94119f84ee81217d41b4af24f0dcf4f0
3
+ size 1225968
models/nanofable-28m-fp16.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:605a15ee37f622d3664a2cc7a371a60e7f7abca8724026959e748630d40d26e2
3
+ size 55599464
models/nanofable-28m-ternary.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5718b4e7c7c42f346a5718c5511ce6e7b3560e8a21d0ab460ea6589bd9e42c15
3
+ size 9358936
models/nanofable-6m-fp16.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6a5c86f0add80fdd9a60a03934b564f2cab22660acc23a135b98f2724ec17223
3
+ size 11743256
models/nanofable-6m-ternary.tpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:845e8bd6deef46fa13d139d977d0374ec91feea939635e56e354d110623a305c
3
+ size 3074144
style.css CHANGED
@@ -1,28 +1,461 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
28
  }
 
1
+ /* nanofable demo — storybook-on-parchment. Tokens from the design plan:
2
+ parchment ground, ink text, quill-blue accent, gilt for the illuminated touches. */
3
+
4
+ /* Self-hosted fonts (latin subsets), preloaded from index.html so they're ready at
5
+ first paint — no swap-reflow on load, and no third-party request. */
6
+ @font-face {
7
+ font-family: "IM Fell English";
8
+ font-style: normal;
9
+ font-weight: 400;
10
+ font-display: swap;
11
+ src: url("fonts/im-fell-english.woff2") format("woff2");
12
+ }
13
+ @font-face {
14
+ font-family: "IM Fell English";
15
+ font-style: italic;
16
+ font-weight: 400;
17
+ font-display: swap;
18
+ src: url("fonts/im-fell-english-italic.woff2") format("woff2");
19
+ }
20
+ @font-face {
21
+ font-family: "EB Garamond";
22
+ font-style: normal;
23
+ font-weight: 400 600;
24
+ font-display: swap;
25
+ src: url("fonts/eb-garamond.woff2") format("woff2");
26
+ }
27
+ @font-face {
28
+ font-family: "EB Garamond";
29
+ font-style: italic;
30
+ font-weight: 400 600;
31
+ font-display: swap;
32
+ src: url("fonts/eb-garamond-italic.woff2") format("woff2");
33
+ }
34
+
35
+ :root {
36
+ --parchment: #f6f1e5;
37
+ --vellum: #fbf8f0;
38
+ --ink: #2e2a24;
39
+ --faded: #857b6b;
40
+ --quill: #3a5169;
41
+ --gilt: #b08d3e;
42
+ --madder: #8c3b2e; /* error accent only */
43
+ --line: rgba(46, 42, 36, 0.14);
44
+ --font-display: "IM Fell English", "Iowan Old Style", serif;
45
+ --font-body: "EB Garamond", Georgia, serif;
46
+ --font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
47
+ --col: 44rem;
48
+ }
49
+
50
+ * { box-sizing: border-box; margin: 0; }
51
+
52
+ html, body { height: 100%; }
53
+
54
  body {
55
+ display: flex;
56
+ flex-direction: column;
57
+ background: var(--parchment);
58
+ color: var(--ink);
59
+ font-family: var(--font-body);
60
+ font-size: 17px;
61
+ }
62
+
63
+ /* ---- header ---- */
64
+
65
+ .site-head {
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: space-between;
69
+ padding: 0.9rem 1.25rem;
70
  }
71
 
72
+ .mark {
73
+ font-family: var(--font-display);
74
+ font-size: 1.15rem;
75
+ letter-spacing: 0.02em;
76
+ }
77
+
78
+ .mark-sub { color: var(--faded); font-style: italic; font-size: 0.95rem; }
79
+
80
+ .icon-btn {
81
+ display: grid;
82
+ place-items: center;
83
+ width: 2.4rem;
84
+ height: 2.4rem;
85
+ border: 1px solid var(--line);
86
+ border-radius: 50%;
87
+ background: var(--vellum);
88
+ color: var(--quill);
89
+ cursor: pointer;
90
+ }
91
+
92
+ .icon-btn:hover { border-color: var(--quill); }
93
+ .icon-btn:disabled { color: var(--faded); cursor: default; border-color: var(--line); }
94
+ .icon-btn.done { color: var(--gilt); }
95
+
96
+ /* ---- main / hero / transcript ---- */
97
+
98
+ #main {
99
+ flex: 1;
100
+ overflow-y: auto;
101
+ padding: 0 1.25rem;
102
+ }
103
+
104
+ #hero {
105
+ max-width: var(--col);
106
+ margin: 12vh auto 0;
107
+ text-align: center;
108
+ }
109
+
110
+ body.storytime #hero { display: none; }
111
+
112
+ .hero-bard {
113
+ width: 93px; /* the art's native pixel grid (465 / 5) — keeps pixels even */
114
+ height: auto;
115
+ image-rendering: pixelated;
116
+ margin-top: 1.5rem;
117
  }
118
 
119
+ .hero-title {
120
+ font-family: var(--font-display);
121
+ font-weight: 400;
122
+ font-size: clamp(2.1rem, 6vw, 3.3rem);
123
+ line-height: 1.15;
124
+ }
125
+
126
+ .hero-sub {
127
+ margin: 1.1rem auto 0;
128
+ max-width: 34rem;
129
+ color: var(--faded);
130
+ font-size: 1.05rem;
131
+ line-height: 1.55;
132
+ }
133
+
134
+ #transcript {
135
+ max-width: var(--col);
136
+ margin: 0 auto;
137
+ padding: 1.5rem 0 2rem;
138
+ }
139
+
140
+ .entry-user {
141
+ margin-top: 2.25rem;
142
+ font-family: var(--font-ui);
143
+ font-size: 0.72rem;
144
+ letter-spacing: 0.14em;
145
+ text-transform: uppercase;
146
+ color: var(--faded);
147
+ }
148
+
149
+ .story {
150
+ margin-top: 0.8rem;
151
+ font-size: 1.16rem;
152
+ line-height: 1.72;
153
+ white-space: pre-wrap;
154
+ overflow-wrap: anywhere; /* unbroken token runs (e.g. "NNNN…") wrap, never overflow */
155
+ }
156
+
157
+ /* Raised initial: inline so it shares the first line's baseline — the line sits
158
+ level with the letter's bottom, and the letter towers above it. */
159
+ .story .dropcap {
160
+ font-family: var(--font-display);
161
+ font-size: 2.9em;
162
+ line-height: 1;
163
+ padding-right: 0.06em;
164
+ color: var(--gilt);
165
+ }
166
+
167
+ .story.streaming::after {
168
+ content: "";
169
+ display: inline-block;
170
+ width: 2px;
171
+ height: 1em;
172
+ margin-left: 2px;
173
+ vertical-align: -0.15em;
174
+ background: var(--quill);
175
+ animation: inkblink 1.1s steps(2, start) infinite;
176
+ }
177
+
178
+ @keyframes inkblink { to { visibility: hidden; } }
179
+
180
+ .story-note {
181
+ margin-top: 0.6rem;
182
+ font-family: var(--font-ui);
183
+ font-size: 0.75rem;
184
+ color: var(--faded);
185
+ }
186
+
187
+ .fin {
188
+ margin-top: 0.9rem;
189
+ text-align: center;
190
+ color: var(--gilt);
191
+ font-size: 1rem;
192
+ }
193
+
194
+ /* ---- composer ---- */
195
+
196
+ .composer-wrap { padding: 0 1.25rem calc(0.75rem + env(safe-area-inset-bottom)); }
197
+
198
+ .composer {
199
+ max-width: var(--col);
200
+ margin: 0 auto;
201
+ background: var(--vellum);
202
+ border: 1px solid var(--line);
203
+ border-radius: 16px;
204
+ box-shadow: 0 2px 14px rgba(46, 42, 36, 0.07);
205
+ padding: 0.85rem 0.9rem 0.65rem;
206
+ }
207
+
208
+ .composer:focus-within { border-color: var(--quill); }
209
+
210
+ #input {
211
+ width: 100%;
212
+ border: 0;
213
+ background: transparent;
214
+ resize: none;
215
+ font-family: var(--font-body);
216
+ font-size: 1.08rem;
217
+ line-height: 1.5;
218
+ color: var(--ink);
219
+ max-height: 9rem;
220
  }
221
 
222
+ #input:focus { outline: none; }
223
+ #input::placeholder { color: var(--faded); font-style: italic; }
224
+
225
+ .composer-row {
226
+ display: flex;
227
+ align-items: flex-end;
228
+ justify-content: space-between;
229
+ margin-top: 0.55rem;
230
+ gap: 0.5rem;
231
+ }
232
+
233
+ .composer-tools { display: flex; gap: 0.4rem; }
234
+
235
+ .menu-anchor { position: relative; }
236
+
237
+ .chip {
238
+ display: inline-flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ gap: 0.35rem;
242
+ font-family: var(--font-ui);
243
+ font-size: 0.78rem;
244
+ color: var(--ink);
245
+ background: transparent;
246
+ border: 1px solid var(--line);
247
+ border-radius: 999px;
248
+ padding: 0.34rem 0.75rem;
249
+ cursor: pointer;
250
+ white-space: nowrap;
251
+ }
252
+
253
+ /* Reserve the chip's width up front so "loading…" -> model name doesn't reflow. */
254
+ #model-btn { min-width: 11.5rem; }
255
+
256
+ .chip:hover:not(:disabled) { border-color: var(--quill); color: var(--quill); }
257
+ .chip:disabled { color: var(--faded); cursor: default; }
258
+ .chip-icon { padding: 0.34rem 0.55rem; }
259
+
260
+ .send {
261
+ display: grid;
262
+ place-items: center;
263
+ width: 2.5rem;
264
+ height: 2.5rem;
265
+ border: 0;
266
+ border-radius: 50%;
267
+ background: var(--quill);
268
+ color: var(--parchment);
269
+ cursor: pointer;
270
+ flex: 0 0 auto;
271
+ }
272
+
273
+ .send:hover:not(:disabled) { background: #2e4157; }
274
+ .send:disabled { background: var(--line); color: var(--faded); cursor: default; }
275
+ .send .ic-stop { display: none; }
276
+ body.generating .send .ic-stop { display: block; }
277
+ body.generating .send .ic-quill { display: none; }
278
+
279
+ .colophon {
280
+ max-width: var(--col);
281
+ margin: 0.5rem auto 0;
282
+ text-align: center;
283
+ font-family: var(--font-ui);
284
+ font-size: 0.7rem;
285
+ letter-spacing: 0.04em;
286
+ color: var(--faded);
287
+ }
288
+
289
+ /* ---- menus ---- */
290
+
291
+ .menu {
292
+ position: absolute;
293
+ bottom: calc(100% + 0.5rem);
294
+ left: 0;
295
+ min-width: 15rem;
296
+ background: var(--vellum);
297
+ border: 1px solid var(--line);
298
+ border-radius: 12px;
299
+ box-shadow: 0 6px 24px rgba(46, 42, 36, 0.14);
300
+ padding: 0.4rem;
301
+ z-index: 20;
302
+ font-family: var(--font-ui);
303
+ }
304
+
305
+ .menu-heading {
306
+ font-size: 0.68rem;
307
+ letter-spacing: 0.14em;
308
+ text-transform: uppercase;
309
+ color: var(--faded);
310
+ padding: 0.35rem 0.6rem 0.45rem;
311
+ }
312
+
313
+ .menu-item {
314
+ display: flex;
315
+ align-items: baseline;
316
+ justify-content: space-between;
317
+ gap: 1rem;
318
+ width: 100%;
319
+ border: 0;
320
+ background: transparent;
321
+ border-radius: 8px;
322
+ padding: 0.45rem 0.6rem;
323
+ font-size: 0.82rem;
324
+ color: var(--ink);
325
+ cursor: pointer;
326
+ text-align: left;
327
+ }
328
+
329
+ .menu-item:hover:not(:disabled) { background: rgba(58, 81, 105, 0.08); }
330
+ .menu-item:disabled { color: var(--faded); cursor: not-allowed; }
331
+ .menu-item .size { color: var(--faded); font-size: 0.74rem; }
332
+ .menu-item.selected { color: var(--quill); font-weight: 600; }
333
+ .menu-item.selected::before { content: "✓ "; }
334
+
335
+ .menu-row { display: block; position: relative; }
336
+
337
+ .menu-tip {
338
+ position: absolute;
339
+ left: calc(100% + 0.5rem);
340
+ top: 50%;
341
+ transform: translateY(-50%);
342
+ width: 12rem;
343
+ background: var(--ink);
344
+ color: var(--parchment);
345
+ border-radius: 8px;
346
+ padding: 0.4rem 0.55rem;
347
+ font-size: 0.72rem;
348
+ line-height: 1.35;
349
+ box-shadow: 0 6px 24px rgba(46, 42, 36, 0.18);
350
+ opacity: 0;
351
+ visibility: hidden;
352
+ transition: opacity 0.12s ease;
353
+ pointer-events: none;
354
+ z-index: 30;
355
+ }
356
+
357
+ .menu-row.locked:hover .menu-tip { opacity: 1; visibility: visible; }
358
+
359
+ .menu-config label {
360
+ display: flex;
361
+ align-items: center;
362
+ justify-content: space-between;
363
+ gap: 1rem;
364
+ padding: 0.3rem 0.6rem;
365
+ font-size: 0.82rem;
366
+ }
367
+
368
+ .menu-config .hint { color: var(--faded); font-size: 0.72rem; }
369
+
370
+ .menu-config input {
371
+ width: 4.6rem;
372
+ font-family: var(--font-ui);
373
+ font-size: 0.82rem;
374
+ color: var(--ink);
375
+ background: var(--parchment);
376
+ border: 1px solid var(--line);
377
+ border-radius: 6px;
378
+ padding: 0.25rem 0.4rem;
379
+ }
380
+
381
+ /* ---- toasts (bottom-left) & progress (bottom-right) ---- */
382
+
383
+ #toast-region {
384
+ position: fixed;
385
+ bottom: 1rem;
386
+ left: 1rem;
387
+ display: flex;
388
+ flex-direction: column;
389
+ gap: 0.5rem;
390
+ z-index: 40;
391
+ max-width: min(22rem, calc(100vw - 2rem));
392
+ pointer-events: none; /* toasts are read-only; never block the composer under them */
393
+ }
394
+
395
+ .toast {
396
+ background: var(--vellum);
397
+ border: 1px solid var(--line);
398
+ border-left: 3px solid var(--madder);
399
+ border-radius: 10px;
400
+ box-shadow: 0 4px 18px rgba(46, 42, 36, 0.12);
401
+ padding: 0.6rem 0.85rem;
402
+ font-family: var(--font-ui);
403
+ font-size: 0.82rem;
404
+ line-height: 1.45;
405
+ }
406
+
407
+ #progress-card {
408
+ position: fixed;
409
+ bottom: 1rem;
410
+ right: 1rem;
411
+ width: min(19rem, calc(100vw - 2rem));
412
+ background: var(--vellum);
413
+ border: 1px solid var(--line);
414
+ border-radius: 12px;
415
+ box-shadow: 0 4px 18px rgba(46, 42, 36, 0.12);
416
+ padding: 0.75rem 0.9rem;
417
+ font-family: var(--font-ui);
418
+ z-index: 40;
419
+ }
420
+
421
+ .progress-title {
422
+ font-size: 0.68rem;
423
+ letter-spacing: 0.14em;
424
+ text-transform: uppercase;
425
+ color: var(--faded);
426
+ }
427
+
428
+ .progress-label { font-size: 0.82rem; margin-top: 0.35rem; }
429
+
430
+ .progress-track {
431
+ height: 4px;
432
+ margin-top: 0.5rem;
433
+ border-radius: 2px;
434
+ background: var(--line);
435
+ overflow: hidden;
436
+ }
437
+
438
+ .progress-fill {
439
+ height: 100%;
440
+ width: 0;
441
+ border-radius: 2px;
442
+ background: var(--quill);
443
+ transition: width 0.15s ease-out;
444
+ }
445
+
446
+ .progress-overall { font-size: 0.72rem; color: var(--faded); margin-top: 0.4rem; }
447
+
448
+ /* ---- a11y & responsive ---- */
449
+
450
+ :focus-visible { outline: 2px solid var(--quill); outline-offset: 2px; }
451
+
452
+ @media (prefers-reduced-motion: reduce) {
453
+ .story.streaming::after { animation: none; }
454
+ .progress-fill { transition: none; }
455
  }
456
 
457
+ @media (max-width: 540px) {
458
+ #hero { margin-top: 7vh; }
459
+ .site-head, #main, .composer-wrap { padding-left: 0.8rem; padding-right: 0.8rem; }
460
+ .chip { font-size: 0.72rem; padding: 0.3rem 0.6rem; }
461
  }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
vendor/obscenity.mjs ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // obscenity@0.4.6 (MIT, https://github.com/jo3-l/obscenity) — self-contained ESM
2
+ // bundle fetched from https://cdn.jsdelivr.net/npm/obscenity@0.4.6/+esm — do not edit.
3
+ /**
4
+ * Bundled by jsDelivr using Rollup v4.62.2 and esbuild v0.28.1.
5
+ * Original file: /npm/obscenity@0.4.6/dist/index.mjs
6
+ *
7
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
8
+ */
9
+ var C={},T={},P={},ee;function v(){if(ee)return P;ee=1,Object.defineProperty(P,"__esModule",{value:!0}),P.isHighSurrogate=a,P.isLowSurrogate=l,P.convertSurrogatePairToCodePoint=e,P.isWordChar=n,P.isDigit=r,P.isAlphabetic=o,P.isLowerCase=d,P.isUpperCase=t,P.invertCaseOfAlphabeticChar=h,P.getAndAssertSingleCodePoint=i;function a(s){return 55296<=s&&s<=56319}function l(s){return 56320<=s&&s<=57343}function e(s,c){return(s-55296)*1024+c-56320+65536}function n(s){return r(s)||o(s)}function r(s){return 48<=s&&s<=57}function o(s){return d(s)||t(s)}function d(s){return 97<=s&&s<=122}function t(s){return 65<=s&&s<=90}function h(s){return s^32}function i(s){if([...s].length!==1)throw new RangeError("Expected the input string to be one code point in length.");return s.codePointAt(0)}return P}var te;function re(){if(te)return T;te=1,Object.defineProperty(T,"__esModule",{value:!0}),T.keepStartCensorStrategy=l,T.keepEndCensorStrategy=e,T.asteriskCensorStrategy=n,T.grawlixCensorStrategy=r,T.fixedPhraseCensorStrategy=o,T.fixedCharCensorStrategy=d,T.randomCharFromSetCensorStrategy=t;const a=v();function l(h){return i=>i.overlapsAtStart?h(i):String.fromCodePoint(i.input.codePointAt(i.startIndex))+h({...i,matchLength:i.matchLength-1})}function e(h){return i=>{if(i.overlapsAtEnd)return h(i);const s=String.fromCodePoint(i.input.codePointAt(i.endIndex));return h({...i,matchLength:i.matchLength-1})+s}}function n(){return d("*")}function r(){return t("%@$&*")}function o(h){return()=>h}function d(h){return(0,a.getAndAssertSingleCodePoint)(h),i=>h.repeat(i.matchLength)}function t(h){const i=[...h];if(i.length<2)throw new Error("The character set passed must have at least 2 characters.");return s=>{if(s.matchLength===0)return"";let c=Math.floor(Math.random()*i.length),f=i[c];for(let p=1;p<s.matchLength;p++){let m=Math.floor(Math.random()*(i.length-1));m>=c&&m++,c=m,f+=i[m]}return f}}return T}var b={},D={},B={},ae;function De(){if(ae)return B;ae=1,Object.defineProperty(B,"__esModule",{value:!0}),B.compareIntervals=a;function a(l,e,n,r){return l<n?-1:n<l?1:e<r?-1:r<e?1:0}return B}var ne;function V(){if(ne)return D;ne=1,Object.defineProperty(D,"__esModule",{value:!0}),D.compareMatchByPositionAndId=l;const a=De();function l(e,n){const r=(0,a.compareIntervals)(e.startIndex,e.endIndex,n.startIndex,n.endIndex);return r!==0?r:e.termId===n.termId?0:e.termId<n.termId?-1:1}return D}var se;function Be(){if(se)return b;se=1,Object.defineProperty(b,"__esModule",{value:!0}),b.TextCensor=void 0;const a=V(),l=re();class e{strategy=(0,l.grawlixCensorStrategy)();setStrategy(r){return this.strategy=r,this}applyTo(r,o){if(o.length===0)return r;const d=[...o].sort(a.compareMatchByPositionAndId);let t="",h=0;for(let i=0;i<d.length;i++){const s=d[i];if(h>s.endIndex)continue;const c=s.startIndex<h;c||(t+=r.slice(h,s.startIndex));const f=Math.max(h,s.startIndex),p=i<d.length-1&&s.endIndex>=d[i+1].startIndex&&s.endIndex<d[i+1].endIndex;t+=this.strategy({...s,startIndex:f,input:r,overlapsAtStart:c,overlapsAtEnd:p}),h=s.endIndex+1}return t+=r.slice(h),t}}return b.TextCensor=e,b}var W={},N={},ie;function oe(){if(ie)return N;ie=1,Object.defineProperty(N,"__esModule",{value:!0}),N.assignIncrementingIds=a;function a(l){let e=0;return l.map(n=>({id:e++,pattern:n}))}return N}var de;function le(){if(de)return W;de=1,Object.defineProperty(W,"__esModule",{value:!0}),W.PhraseBuilder=W.DataSet=void 0;const a=oe();class l{containers=[];patternCount=0;patternIdToPhraseContainer=new Map;addAll(r){for(const o of r.containers)this.registerContainer(o);return this}removePhrasesIf(r){this.patternCount=0,this.patternIdToPhraseContainer.clear();const o=this.containers.splice(0);for(const d of o)r(d)||this.registerContainer(d);return this}addPhrase(r){const o=r(new e).build();return this.registerContainer(o),this}getPayloadWithPhraseMetadata(r){const o=this.patternIdToPhraseContainer.get(r.termId);if(o===void 0)throw new Error(`The pattern with ID ${r.termId} does not exist in this dataset.`);return{...r,phraseMetadata:this.containers[o].metadata}}build(){return{blacklistedTerms:(0,a.assignIncrementingIds)(this.containers.flatMap(r=>r.patterns)),whitelistedTerms:this.containers.flatMap(r=>r.whitelistedTerms)}}registerContainer(r){const o=this.containers.push(r)-1;for(let d=0,t=this.patternCount;d<r.patterns.length;d++,t++)this.patternIdToPhraseContainer.set(t,o),this.patternCount++}}W.DataSet=l;class e{patterns=[];whitelistedTerms=[];metadata;addPattern(r){return this.patterns.push(r),this}addWhitelistedTerm(r){return this.whitelistedTerms.push(r),this}setMetadata(r){return this.metadata=r,this}build(){return{patterns:this.patterns,whitelistedTerms:this.whitelistedTerms,metadata:this.metadata}}}return W.PhraseBuilder=e,W}var Y={},he;function Ne(){return he||(he=1,Object.defineProperty(Y,"__esModule",{value:!0})),Y}var S={},y={},_={},ce;function G(){if(ce)return _;ce=1,Object.defineProperty(_,"__esModule",{value:!0}),_.SyntaxKind=void 0;var a;return(function(l){l[l.Optional=0]="Optional",l[l.Wildcard=1]="Wildcard",l[l.Literal=2]="Literal",l[l.BoundaryAssertion=3]="BoundaryAssertion"})(a||(_.SyntaxKind=a={})),_}var ue;function $e(){if(ue)return y;ue=1,Object.defineProperty(y,"__esModule",{value:!0}),y.potentiallyMatchesEmptyString=l,y.compilePatternToRegExp=e,y.getRegExpStringForNode=r;const a=G();function l(o){return o.nodes.every(d=>d.kind===a.SyntaxKind.Optional)}function e(o){let d="";o.requireWordBoundaryAtStart&&(d+="\\b");for(const t of o.nodes)d+=r(t);return o.requireWordBoundaryAtEnd&&(d+="\\b"),new RegExp(d,"gs")}const n=["[",".","*","+","?","^","$","{","}","(",")","|","[","\\","]"].map(o=>o.charCodeAt(0));function r(o){switch(o.kind){case a.SyntaxKind.Literal:{let d="";for(const t of o.chars)n.includes(t)&&(d+="\\"),d+=String.fromCodePoint(t);return d}case a.SyntaxKind.Optional:return`(?:${r(o.childNode)})?`;case a.SyntaxKind.Wildcard:return"."}}return y}var k={},fe;function Ue(){if(fe)return k;fe=1,Object.defineProperty(k,"__esModule",{value:!0}),k.TransformerSet=void 0;let a=class{transformers;statefulTransformers;constructor(e){this.transformers=e,this.statefulTransformers=Array.from({length:this.transformers.length});for(let n=0;n<this.transformers.length;n++){const r=this.transformers[n];r.type===1&&(this.statefulTransformers[n]=r.factory())}}applyTo(e){let n=e;for(let r=0;r<this.transformers.length&&n!==void 0;r++){const o=this.transformers[r];o.type===0?n=o.transform(n):n=this.statefulTransformers[r].transform(n)}return n}resetAll(){for(let e=0;e<this.transformers.length;e++)this.transformers[e].type===1&&this.statefulTransformers[e].reset()}};return k.TransformerSet=a,k}var I={},pe;function J(){if(pe)return I;pe=1,Object.defineProperty(I,"__esModule",{value:!0}),I.CharacterIterator=void 0;const a=v();let l=class{_input;lastPosition=-1;currentPosition=0;_lastWidth=0;constructor(n){this._input=n??""}get input(){return this._input}setInput(n){return this._input=n,this.reset(),this}reset(){this.lastPosition=-1,this.currentPosition=0,this._lastWidth=0}next(){if(this.done)return{done:!0,value:void 0};this.lastPosition=this.currentPosition;const n=this._input.charCodeAt(this.currentPosition++);if(this._lastWidth=1,this.done||!(0,a.isHighSurrogate)(n))return{done:!1,value:n};const r=this._input.charCodeAt(this.currentPosition);return(0,a.isLowSurrogate)(r)?(this._lastWidth++,this.currentPosition++,{done:!1,value:(0,a.convertSurrogatePairToCodePoint)(n,r)}):{done:!1,value:n}}get position(){return this.lastPosition}get lastWidth(){return this._lastWidth}get done(){return this.currentPosition>=this._input.length}[Symbol.iterator](){return this}};return I.CharacterIterator=l,I}var w={},me;function Ke(){if(me)return w;me=1,Object.defineProperty(w,"__esModule",{value:!0}),w.IntervalCollection=void 0;let a=class{dirty=!1;intervals=[];insert(e,n){this.intervals.push([e,n]),this.dirty=!0}query(e,n){if(this.intervals.length===0)return!1;this.dirty&&(this.dirty=!1,this.intervals.sort((r,o)=>r[0]<o[0]?-1:o[0]<r[0]?1:0));for(const r of this.intervals){if(r[0]>e)break;if(r[0]<=e&&n<=r[1])return!0}return!1}values(){return this.intervals.values()}[Symbol.iterator](){return this.values()}};return w.IntervalCollection=a,w}var ge;function Fe(){if(ge)return S;ge=1,Object.defineProperty(S,"__esModule",{value:!0}),S.RegExpMatcher=void 0;const a=$e(),l=Ue(),e=v(),n=J(),r=Ke(),o=V();class d{blacklistedTerms;whitelistedTerms;blacklistMatcherTransformers;whitelistMatcherTransformers;constructor({blacklistedTerms:h,whitelistedTerms:i=[],blacklistMatcherTransformers:s=[],whitelistMatcherTransformers:c=[]}){this.blacklistedTerms=this.compileTerms(h),this.validateWhitelistedTerms(i),this.whitelistedTerms=i,this.blacklistMatcherTransformers=new l.TransformerSet(s),this.whitelistMatcherTransformers=new l.TransformerSet(c)}getAllMatches(h,i=!1){const s=this.getWhitelistedIntervals(h),[c,f]=this.applyTransformers(h,this.blacklistMatcherTransformers),p=[];for(const m of this.blacklistedTerms)for(const g of f.matchAll(m.regExp)){const Z=c[g.index];let M=c[g.index+g[0].length-1];M<h.length-1&&(0,e.isHighSurrogate)(h.charCodeAt(M))&&(0,e.isLowSurrogate)(h.charCodeAt(M+1))&&M++,s.query(Z,M)||p.push({termId:m.id,startIndex:Z,endIndex:M,matchLength:[...g[0]].length})}return i&&p.sort(o.compareMatchByPositionAndId),p}hasMatch(h){const i=this.getWhitelistedIntervals(h),[s,c]=this.applyTransformers(h,this.blacklistMatcherTransformers);for(const f of this.blacklistedTerms)for(const p of c.matchAll(f.regExp)){const m=s[p.index];let g=s[p.index+p[0].length-1];if(g<h.length-1&&(0,e.isHighSurrogate)(h.charCodeAt(g))&&(0,e.isLowSurrogate)(h.charCodeAt(g+1))&&g++,!i.query(m,g))return!0}return!1}getWhitelistedIntervals(h){const i=new r.IntervalCollection,[s,c]=this.applyTransformers(h,this.whitelistMatcherTransformers);for(const f of this.whitelistedTerms){let p=0;for(let m=c.indexOf(f,p);m!==-1;m=c.indexOf(f,p)){let g=s[m+f.length-1];g<h.length-1&&(0,e.isHighSurrogate)(h.charCodeAt(g))&&(0,e.isLowSurrogate)(h.charCodeAt(g+1))&&g++,i.insert(s[m],g),p=m+f.length}}return i}applyTransformers(h,i){const s=[];let c="";const f=new n.CharacterIterator(h);for(const p of f){const m=i.applyTo(p);if(m!==void 0)for(c+=String.fromCodePoint(m);s.length<c.length;)s.push(f.position)}return i.resetAll(),[s,c]}compileTerms(h){const i=[],s=new Set;for(const c of h){if(s.has(c.id))throw new Error(`Duplicate blacklisted term ID ${c.id}.`);if((0,a.potentiallyMatchesEmptyString)(c.pattern))throw new Error(`Pattern with ID ${c.id} potentially matches empty string; this is unsupported.`);i.push({id:c.id,regExp:(0,a.compilePatternToRegExp)(c.pattern)}),s.add(c.id)}return i}validateWhitelistedTerms(h){if(h.some(i=>i.length===0))throw new Error("Whitelisted term set contains empty string; this is unsupported.")}}return S.RegExpMatcher=d,S}var x={},Pe;function Te(){if(Pe)return x;Pe=1,Object.defineProperty(x,"__esModule",{value:!0}),x.ParserError=void 0;class a extends Error{name="ParserError";line;column;constructor(e,n,r){super(`${n}:${r}: ${e}`),this.line=n,this.column=r}}return x.ParserError=a,x}var A={},E={},ve;function He(){if(ve)return E;ve=1,Object.defineProperty(E,"__esModule",{value:!0}),E.Parser=void 0;const a=v(),l=J(),e=G(),n=Te(),r=[92,91,93,63,124],o=r.map(h=>`'${String.fromCodePoint(h)}'`).join(", "),d=-1;let t=class{input="";line=1;column=1;position=0;lastColumn=1;lastWidth=0;parse(i){this.setInput(i);const s=[],c=this.nextNode(),f=c?.kind===e.SyntaxKind.BoundaryAssertion;c&&!f&&s.push(c);let p=!1;for(;!this.done;){const m=this.mark(),g=this.nextNode();if(g.kind!==e.SyntaxKind.BoundaryAssertion){s.push(g);continue}this.done||this.reportError("Boundary assertions are not supported in this position; they are only allowed at the start / end of the pattern.",m),p=!0}return{requireWordBoundaryAtStart:f,requireWordBoundaryAtEnd:p,nodes:s}}setInput(i){return this.input=i,this.line=1,this.column=1,this.position=0,this.lastColumn=1,this.lastWidth=0,this}nextNode(){switch(this.peek()){case d:return;case 91:return this.parseOptional();case 93:this.reportError("Unexpected ']' with no corresponding '['.");case 63:return this.parseWildcard();case 124:return this.parseBoundaryAssertion();default:return this.parseLiteral()}}get done(){return this.position>=this.input.length}parseOptional(){const i=this.mark();this.next();const s=this.mark();this.done&&this.reportError("Unexpected unclosed '['.",i),this.accept("[")&&this.reportError("Unexpected nested optional node.",s);const c=this.nextNode();return c.kind===e.SyntaxKind.BoundaryAssertion&&this.reportError("Boundary assertions are not supported in this position; they are only allowed at the start / end of the pattern.",s),this.accept("]")||this.reportError("Unexpected unclosed '['."),{kind:e.SyntaxKind.Optional,childNode:c}}parseWildcard(){return this.next(),{kind:e.SyntaxKind.Wildcard}}parseBoundaryAssertion(){return this.next(),{kind:e.SyntaxKind.BoundaryAssertion}}parseLiteral(){const i=[];for(;!this.done;){if(this.accept("[]?|")){this.backup();break}const s=this.next();if(s===92){this.done&&(this.backup(),this.reportError("Unexpected trailing backslash."));const c=this.next();if(!r.includes(c)){const f=String.fromCodePoint(c);this.backup(),this.reportError(`Cannot escape character '${f}'; the only characters that can be escaped are the following: ${o}.`)}i.push(c)}else i.push(s)}return{kind:e.SyntaxKind.Literal,chars:i}}reportError(i,{line:s=this.line,column:c=this.column}={}){throw new n.ParserError(i,s,c)}mark(){return{line:this.line,column:this.column}}accept(i){const s=this.next(),c=new l.CharacterIterator(i);for(const f of c)if(f===s)return!0;return this.backup(),!1}peek(){const i=this.next();return this.backup(),i}next(){if(this.done)return d;const i=this.input.charCodeAt(this.position++);if(this.lastWidth=1,i===10)return this.lastColumn=this.column,this.column=1,this.line++,i;if(this.lastColumn=this.column++,!(0,a.isHighSurrogate)(i)||this.done)return i;const s=this.input.charCodeAt(this.position);return(0,a.isLowSurrogate)(s)?(this.position++,this.lastWidth++,(0,a.convertSurrogatePairToCodePoint)(i,s)):i}backup(){this.position-=this.lastWidth,this.column=this.lastColumn,this.lastWidth===1&&this.input.charCodeAt(this.position)===10&&this.line--}};return E.Parser=t,E}var Ce;function We(){if(Ce)return A;Ce=1,Object.defineProperty(A,"__esModule",{value:!0}),A.pattern=e,A.parseRawPattern=n;const a=He(),l=new a.Parser;function e(r,...o){let d=r.raw[0];for(const[t,h]of o.entries())d+=String(h),d+=r.raw[t+1];return l.parse(d)}function n(r){return l.parse(r)}return A}var Q={},$={},R={},Me;function q(){if(Me)return R;Me=1,Object.defineProperty(R,"__esModule",{value:!0}),R.createSimpleTransformer=a,R.createStatefulTransformer=l;function a(e){return{type:0,transform:e}}function l(e){return{type:1,factory:e}}return R}var O={},ye;function ze(){if(ye)return O;ye=1,Object.defineProperty(O,"__esModule",{value:!0}),O.CollapseDuplicatesTransformer=void 0;class a{defaultThreshold;customThresholds;remaining=-1;lastChar=-1;constructor({defaultThreshold:e,customThresholds:n}){this.defaultThreshold=e,this.customThresholds=n}transform(e){if(e===this.lastChar)return this.remaining-- >0?e:void 0;const n=this.customThresholds.get(e)??this.defaultThreshold;return this.remaining=n-1,this.lastChar=e,n>0?e:void 0}reset(){this.remaining=-1,this.lastChar=-1}}return O.CollapseDuplicatesTransformer=a,O}var be;function Se(){if(be)return $;be=1,Object.defineProperty($,"__esModule",{value:!0}),$.collapseDuplicatesTransformer=n;const a=v(),l=q(),e=ze();function n({defaultThreshold:o=1,customThresholds:d=new Map}={}){const t=r(d);return(0,l.createStatefulTransformer)(()=>new e.CollapseDuplicatesTransformer({defaultThreshold:o,customThresholds:t}))}function r(o){const d=new Map;for(const[t,h]of o){if(h<0)throw new RangeError("Expected all thresholds to be non-negative.");const i=(0,a.getAndAssertSingleCodePoint)(t);d.set(i,h)}return d}return $}var U={},K={},_e;function X(){if(_e)return K;_e=1,Object.defineProperty(K,"__esModule",{value:!0}),K.remapCharactersTransformer=n;const a=v(),l=J(),e=q();function n(o){const d=r(o);return(0,e.createSimpleTransformer)(t=>d.get(t)??t)}function r(o){const d=new Map,t=o instanceof Map?o.entries():Object.entries(o);for(const[h,i]of t){const s=(0,a.getAndAssertSingleCodePoint)(h),c=new l.CharacterIterator(i);for(const f of c)d.set(f,s)}return d}return K}var j={},ke;function Ve(){return ke||(ke=1,Object.defineProperty(j,"__esModule",{value:!0}),j.confusables=void 0,j.confusables=new Map([[" "," "],["0","\u24FF"],["1","\u24F5\u278A\u2474\xB9\u{1D7CF}\u{1D7D9}\uFF11\u{1D7F7}\u{1D7E3}\u2488\u{1D7ED}1\u2780\u2081\u2460\u2776\u2960"],["2","\u24F6\u2489\u2475\u278B\u01BB\xB2\u14BF\u{1D7DA}\uFF12\u{1D7EE}\u{1D7E4}\u14BE\u{1D7F8}\u01A7\u{1D7D0}\u2461\u1D24\u2082\u2781\u2777\u161D\u01A8"],["3","\xB3\u2CCC\uA7AB\u{1D7D1}\u2128\u{1D7DB}\u{1D7EF}\u{1D7E5}\uA76A\u278C\u0417\u021C\u24F7\u04E0\u01B7\uFF13\u{1D7F9}\u2476\u248A\u0292\u0293\u01EF\u01EE\u01BA\u{1D574}\u1DBE\u0437\u19A1\u2782\u2462\u2083\u1D9A\u1D23\u1D1F\u2778\u0498\u0499\u04EC\u04E1\u04ED\u04DF\u04DE"],["4","\u278D\u04B6\u13CE\u{1D7DC}\u04B7\u24F8\u04B8\u04B9\u04F4\u04F5\u1DA3\uFF14\u0447\u3129\u2074\u2783\u2084\u2463\u2779\u04CB\u2477\u248B"],["5","\u{1D7F1}\u24F9\u278E\u01BC\u{1D7D3}\u{1D7FB}\u{1D7DD}\u{1D7E7}\uFF15\u2784\u2085\u2464\u2075\u277A\u01BD\u2478\u248C"],["6","\u2CD2\u13EE\u{1D7DE}\u{1D7E8}\u{1D7D4}\u278F\u24FA\u03EC\u03ED\u2076\u0431\uFF16\u19C8\u2465\u2785\u2086\u277B\u2479\u248D"],["7","\u24FB\u{104D2}\u2790\uFF17\u2077\u2466\u2087\u277C\u2786\u247A\u248E"],["8","\u{1031A}\u2791\u24FC\uFF18\u{1D7E0}\u{1D7EA}\u09EA\u2078\u2088\u{1D7F4}\u2787\u2467\u277D\u{1D7FE}\u{1D7D6}\u247B\u248F"],["9","\uA76E\u2CCA\u24FD\u2792\u0A67\u09ED\u0B68\uFF19\u{1D7EB}\u{1D7FF}\u{1D7D7}\u2079\u2089\u0533\u2788\u2468\u277E\u247C\u2490"],["A","\u{1F130}\u13AF\u{102A0}\u{1D56C}\u{1D71C}\u{1D434}\uA4EE\u13AA\u{1D6A8}\uAB7A\u{1D756}\u{1F150}\u212B\u2200\u{1F1E6}\u20B3\u{1F170}\u{1D49C}\u{1D608}\u{1D400}\u{1D538}\u0434\u01FA\u15C5\u24B6\uFF21\u0391\u1F8B\u15E9\u0102\xC3\xC5\u01CD\u0200\u0202\u0100\u023A\u0104\u028C\u039B\u03BB\u019B\u1D00\u1D2C\u0414\u0410\u120D\xC4\u2090\u1571\xAA\u01DE\u04D2\u0386\u1EA0\u1EA2\u1EA6\u1EA8\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1FB8\u1FB9\u1FBA\u1FBB\u1FBC\u1F88\u1F89\u1F8A\u1F8C\u1F8D\u1F8E\u1F8F\u1F08\u1F09\u1F0A\u1F0B\u1F0C\u1F0D\u1F0E\u1F0F\u1E00\u0226\u01E0\u04D0\xC0\xC1\xC2\u1EA4\u1EAA\u{1D6E2}\u{1D4D0}\u{1D670}\u{1D63C}"],["a","\u2202\u237A\u24D0\u0571\u01DF\u1D43\u1D8F\u249C\u0430\u0252\uFF41\u03B1\u0203\u0201\u0E04\u01CE\u10DB\xE4\u0251\u0101\u0250\u0105\u1F84\u1E9A\u1EA1\u1EA3\u01E1\u1EA7\u1EB5\u1E01\u0227\u04D1\u04D3\xE3\xE5\u03AC\u1F70\u1F71\u0103\u1EA9\u1EB1\u1EB3\u1EB7\u1F80\u1F81\u1F82\u1F83\u1F85\u1F86\u1FB0\u1FB1\u1FB2\u1FB3\u1FB4\u1D90\u1FB6\u1FB7\u1F00\u1F01\u1F02\u1F03\u1F04\u1F05\u1F06\u1F07\u1F87\u1EAD\u1EAF\xE0\xE1\xE2\u1EA5\u1EAB\u01FB\u2C65\u{1D41A}\u{1D44E}\u{1D482}\u{1D4B6}\u{1D4EA}\u{1D51E}\u{1D552}\u{1D586}\u{1D5BA}\u{1D5EE}\u{1D622}\u{1D656}\u{1D68A}\u{1D6C2}\u{1D6FC}\u{1D736}\u{1D770}\u{1D7AA}\u2376"],["B","\u{10301}\u{1D469}\u{1D56D}\u{1F131}\u{102A1}\u{1D5A1}\u{1D63D}\uA4D0\u{1D5D5}\u{1D609}\u{1D71D}\u{10282}\u{1D6A9}\u{1D401}\u{1D6E3}\u{1D757}\u{1D435}\u{1D671}\u{1D539}\u13F4\u13FC\u{1D791}\uA7B4\u{1D505}\u{1F151}\u0E3F\u{1D4D1}\u15FF\u15FE\u15FD\u{1F171}\u24B7\uFF22\u0432\u03D0\u15F7\u0181\u4E43\xDF\u10EA\u10E9\u0E56\u03B2\u026E\u0411\u0545\u0E52\u1656\u0299\u1D2E\u1D47\u130C\u1E04\u212C\u0392\u0412\u1E9E\u1E02\u1E06\u0243\u0D26\u15F9\u15F8\u1D5D\u165E\u165F\u165D\u16D2\u1657\u1658\u1D03\u{1F1E7}"],["b","\u13CF\u{1D41B}\u{1D623}\u{1D4B7}\u{1D51F}\u{1D4EB}\u{1D587}\u{1D5BB}\u{1D44F}\u{1D657}\u{1D553}\u{1D483}\u{1D5EF}\u{1D68B}\u266D\u1473\u1488\uFF42\u159A\u1579\u157A\u24D1\u1E03\u1E05\u048D\u044A\u1E07\u0183\u0253\u0185\u15AF\u0184\u042C\u1472\xFE\u0182\u249D\u042A\u1D80\u147F\u1480\u1482\u1481\u147E\u044C\u0180\u048C\u0462\u0463\u150E"],["C","\u13DF\u2CA4\u{1F132}\uA4DA\u{102A2}\u{10302}\u{1F172}\u{10415}\u{1F152}\u263E\u010C\xC7\u24B8\uFF23\u2183\u0187\u1455\u3108\xA2\u096E\u21BB\u0108\u03FE\u0547\u023B\u1645\u1D9C\u249E\u0106\u0480\u010A\xA9\u091F\u0186\u2102\u212D\u03F9\u0421\u531A\u1E08\u04AA\u0297\u1456\u1461\u1462\u1463\u1464\u1465\u216D\u{1D402}\u{1D436}\u{1D46A}\u{1D49E}\u{1D4D2}\u{1D56E}\u{1D5A2}\u{1D5D6}\u{1D60A}\u{1D63E}\u150D"],["c","\u2CA5\u{1043D}\uABAF\u0109\uFF43\u24D2\u0107\u010D\u010B\xE7\u0481\u0188\u1E09\u023C\u2184\u0441\u122D\u1D04\u03F2\u04AB\uA49D\u03C2\u027D\u03DB\u{1D672}\u1466\u19DA\u{1D41C}\u{1D450}\u{1D484}\u{1D4B8}\u{1D4EC}\u{1D520}\u{1D554}\u{1D588}\u{1D5BC}\u{1D5F0}\u{1D624}\u{1D658}\u{1D68C}\u20B5\u{1F1E8}\u1974\u14BC\u217D"],["D","\u13A0\u{1F133}\u{1D521}\u{1D589}\u{1D53B}\u{1D5D7}\u{1D60B}\u{1D673}\u{1D437}\u{1D4D3}\u{1D403}\u{1D46B}\u{1D56F}\u{1D5A3}\u{1D507}\u{1D63F}\uAB70\u2145\u{1D49F}\uA4D3\u{1F173}\u{1F153}\u24B9\uFF24\u0189\u15EA\u018A\xD0\u053A\u1D05\u1D30\u2181\u1E0A\u0110\xDE\u216E\u15DE\u146F\u010E\u1E0C\u1E10\u1E12\u1E0E\u15EB\u15EC\u15DF\u15E0\u1D9B\u1D06\u{1F1E9}"],["d","\u13E7\uA4D2\u{1D4ED}\u1D6D\u20AB\u0503\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u1D48\u249F\u0501\u217E\u1D81\u0500\u147A\u147B\u147C\u147D\u1484\u1470\u1471\u1D91\u{1D555}\u{1D5BD}\u{1D451}\u{1D625}\u{1D485}\u{1D659}\u{1D41D}\u{1D5F1}\u{1D68D}\u2146\u{1D4B9}\u02A0\u056A"],["E","\uAB7C\u{1F134}\u{1D640}\u{1D53C}\u{10286}\u{1D6AC}\uA4F0\u{1D75A}\u{1D794}\u{1D4D4}\u{1D46C}\u{1D5D8}\u{1F174}\u{1F154}\u24BA\u0388\uFF25\u018E\u1F1D\u156E\u0190\u30E2\u0404\u1D07\u1D31\u1D49\xC9\u4E47\u0401\u0246\uA085\u20AC\xC8\u2130\u0395\u0415\u2D39\u13AC\u0112\u0114\u0116\u0118\u011A\xCA\xCB\u0510\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u1E14\u1E16\u1EBA\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u1E18\u1E1A\u1F18\u1F19\u1F1A\u1F1B\u1F1C\u1FC8\u1FC9\u04D6\u1F72\u1F73\u0400\u03F5\u{1F1EA}"],["e","\u{1D452}\u{1D4EE}\u{1D556}\u{1D58A}\u{1D626}\u{1D5F2}\u{1D68E}\u{1D65A}\u{1D486}\u{1D522}\u{1D5BE}\u{1D41E}\u04BE\u04BF\u24D4\uFF45\u24A0\xE8\u19C9\xE9\u1D92\xEA\u0258\u1F14\u1EC1\u1EBF\u1EC5\u0AEF\u01DD\u0454\u03B5\u0113\u04BD\u025B\u1EC3\u1EBD\u1E15\u1E17\u0115\u0117\xEB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u0247\u2091\u0119\u1E1D\u1E19\u1E1B\u212E\u0435\u0511\u0450\u04D7\u1971\u0451\u1F10\u1F11\u1F12\u1F13\u1F15\u212F"],["F","\u{1F135}\u{10287}\u{1D509}\u{1D60D}\u{102A5}\uA4DD\uA798\u{1F175}\u{1F155}\u{1D4D5}\u24BB\uFF26\u0493\u0492\u15B4\u0191\u0532\u03DD\u127B\u1E1E\u2131\u03DC\u20A3\u{1F1EB}\u2132"],["f","\u{1D41F}\u{1D58B}\u24D5\uFF46\u0192\u1E1F\u0283\u0562\u1DA0\u24A1\u017F\uA2B0\u0284\u2231\u1D82\u{1D627}"],["G","\uA4D6\u13F3\u{1F136}\u13C0\u13FB\u{1D53E}\u{1D4D6}\u{1D46E}\u{1D572}\uAB90\u{1D4A2}\u{1D642}\u{1D5A6}\u{1D676}\u{1D50A}\u{1D43A}\u{1D406}\u{1F176}\u{1F156}\u24BC\uFF27\u0262\u0193\u029B\u0122\u161C\u1D33\u01F4\u0120\u050C\u011C\u1E20\u011E\u01E6\u01E4\u050D\u20B2\u{1F1EC}\u2141"],["g","\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u0581\u0AED\u01E5\u0260\uFEED\uFEEE\u1D4D\u24A2\u210A\u0261\u19C1\u{1D420}\u{1D454}\u{1D488}\u{1D4F0}\u{1D524}\u{1D558}\u{1D58C}\u{1D5C0}\u{1D5F4}\u{1D628}\u{1D65C}\u{1D690}"],["H","\u{1F137}\u{1D722}\uA4E7\u{1D60F}\u{1D43B}\u{1D75C}\u{1D5A7}\u{102CF}\u{1D5DB}\uAB8B\u210D\u13BB\u210C\u2C8E\u{1D46F}\u{1D796}\u{1F177}\u{1F157}\u12DE\u01F6\u050B\u24BD\uFF28\u0124\u16BA\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u04A2\u04A3\u04A4\u1FCA\u1FCB\u1FCC\u1F28\u1F29\u1F2A\u1F2B\u1F2D\u1F2E\u1F2F\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u04C9\u04C8\u04A5\u0389\u043D\u5344\u2653\u{1D4D7}\u210B\u041D\u{1D407}\u{1D643}\u{1D677}\u029C\u{1D6E8}\u0397\u{1D6AE}\u157C\u04C7\u1D34\u1D78\u{1F1ED}"],["h","\u04BA\u24A3\u0452\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u0570\u04BB\u12A8\u12A9\u12AA\u12AB\u0266\u210E\u{1D421}\u{1D489}\u{1D4BD}\u{1D4F1}\u{1D525}\u{1D559}\u{1D58D}\u{1D5C1}\u{1D5F5}\u{1D629}\u{1D65D}\u{1D691}\u056B\u02B0\u144B\u15C1\u0267\u3093\u0265"],["I","\u{1F138}\u0407\uA024\u13C6\u{1F178}\u{1F158}\u0625\uFE87\u0673\u0623\uFE83\u0672\u0675\u24BE\uFF29\u17F8\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197\u30A7\u30A8\u1FD8\u1FD9\u1FDA\u1FDB\u1F38\u1F39\u1F3A\u1F3B\u1F3C\u1F3D\u1F3E\u2160\u03AA\u038A\u026A\u1DA6\u144A\u1963\u{1D6EA}\u{1D408}\u{1D644}\u{1D678}\u{1D4F5}\u{1D661}\u{1D43C}\u1D35\u{1D6B0}\u{1D470}\u{1F1EE}"],["i","\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u1E2D\u1FD0\u1FD1\u1FD2\u1FD3\u1FD6\u1FD7\u1F30\u1F31\u1F32\u2170\u217C\u2223\u2D4F\uFFE8\u05C0\u0627\u0661\u06F1\u07CA\u16C1\u1F33\u1F34\u1F35\u0268\u0456\u1F76\u1F77\u1D96\u{1D526}\u{1D692}\u{1D778}\u{1D5C2}\u{1D422}\u{1D55A}\u{1D58E}\u{1D5F6}\u{1D62A}\u{1D65E}\u03AF\u2071\u1D62\u{1D4F2}\u24A4"],["J","\u{1F139}\u{1F179}\u{1F159}\u24BF\uFF2A\u0408\u029D\u148D\u05E0\uFF8C\u0134\u0286\u0E27\u0644\u0575\u0296\u1D0A\u1D36\uFEDD\u130B\u0248\u2C7C\u0542\u0E45\u10B1\u012F\u13AB\u0237\u4E3F\u2110\u2111\u1498\u1499\u149A\u149B\u14B4\u14B5\u148E\u148F\u{1F1EF}"],["j","\u24D9\uFF4A\u03F3\u02B2\u24A5\u0249\u0135\u01F0\u0458\u06B6\u1DA8\u{1D4BF}\u{1D62B}\u{1D5F7}\u{1D457}\u{1D65F}\u{1D527}\u{1D48B}\u{1D5C3}\u{1D4F3}\u{1D55B}\u{1D693}\u{1D58F}\u{1D423}"],["K","\u{1D5DE}\u{1F13A}\u{1D725}\u{1D612}\uA4D7\u{1D646}\u{1D542}\u2C94\u{1D50E}\u{1D6EB}\u13E6\u{1D799}\u{1D4A6}\u{1F17A}\u{1F15A}\u20AD\u24C0\uFF2B\u0138\u1E30\u045C\u0198\u043A\u04A0\u03BA\u049B\u049F\u04C4\u029E\u049A\u041A\u04A1\u1D0B\u1D37\u1D4F\u24A6\u16D5\u040C\u1315\u1E32\u039A\u212A\u049C\u049D\u049E\u0136\u1E34\u01E8\u2C69\u03D7\u04C3\u{1F1F0}"],["k","\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\u1D84\u{1D424}\u{1D62C}\u{1D5C4}\u{1D55C}\u{1D705}\u{1D718}\u{1D73F}\u{1D752}\u{1D779}\u{1D78C}\u{1D7B3}\u{1D660}\u{1D694}\u{1D458}\u{1D48C}\u03F0\u{1D6CB}\u{1D6DE}\u{1D7C6}\u{1D5F8}\u{1D4F4}\u{1D4C0}"],["L","\u{1F13B}\u{1041B}\u2CD0\u{1D473}\u{1D67B}\u{10443}\u{1D4DB}\u2CD1\uABAE\u13DE\uA4E1\u{1F17B}\u{1F15B}\uFE88\u2514\u24C1\u0582\uFF2C\u013F\u14AA\u4E5A\u0546\u029F\uA4F6\u03B9\u053C\u1D38\u02E1\u0139\u1228\u1E36\u2097\u0393\u056C\u013B\u1102\u216C\u2112\u2C62\u1967\u1968\u14BB\u14B6\u14B7\u1DAB\uFE8E\u14BA\u14B9\u14B8\u14AB\u23B3\u3125\u0141\u2C60\uFE84\u023D\u{1F1F1}"],["l","\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u04C0\u2113\u1E3D\u1E3B\u0142\uFF9A\u026D\u019A\u026B\u2C61|\u0196\u24A7\u0285\u01C0\u05D5\u05DF\u0399\u0406\uFF5C\u1DA9\u04CF\u{1D4D8}\u{1D540}\u{1D5A8}\u{1D5DC}\u{1D610}\u{1D425}\u{1D459}\u{1D48D}\u{1D4C1}\u{1D529}\u{1D55D}\u{1D591}\u{1D5C5}\u{1D5F9}\u{1D62D}\u{1D695}\u{1D724}\u{1D75E}\u0131\u{1D6A4}\u0269\u1FBE\u{1D6CA}\u{1D704}\u{1D73E}\u{1D7B2}"],["M","\u{1F13C}\u{10311}\u{102B0}\uA4DF\u2C98\u13B7\u{1F17C}\u{1F15C}\u24C2\uFF2D\u043C\u1E42\u0D71\u15F0\u5DDE\u163B\u10DD\u0E53\u264F\u028D\u164F\u1D0D\u1D39\u1D50\u24A8\u1E3E\u041C\u1E40\u0BF1\u216F\u2133\u039C\u03FA\u16D6\u04CD\u04CE\u{1D40C}\u{1D440}\u{1D474}\u{1D4DC}\u{1D510}\u{1D544}\u{1D578}\u{1D5AC}\u{1D5E0}\u{1D614}\u{1D648}\u{1D67C}\u{1D6B3}\u{1D6ED}\u{1D727}\u{1D761}\u{1D79B}\u{1F1F2}"],["m","\u20A5\u1D6F\u{1D592}\u{1D426}\u{1D5C6}\u{1D52A}\u{1D55E}\u{1D4C2}\u24DC\uFF4D\u0D28\u1662\u0D69\u1E3F\u1E41\u217F\u03FB\u1E43\u1320\u0271\u17F3\u1D86\u{1D662}\u{1D4F6}\u{1D696}\u{1D45A}\u{1D5FA}\u19D5\u19D7"],["N","\u{1F13D}\u2115\uA4E0\u{1D6EE}\u{1D762}\u{1D67D}\u{1D6B4}\u{1D475}\u{1D441}\u2C9A\u{1D40D}\u{1D4A9}\u{1D79C}\u{1D5E1}\u{1D615}\u{1D728}\u{1D4DD}\u{1D5AD}\u{1F17D}\u20A6\u{1F15D}\u0419\u040D\u24C3\u048B\u17F7\uFF2E\u1D0E\u0274\u019D\u144E\u51E0\u0438\u0548\u057C\u0418\u05D4\u041B\u03C0\u1D3A\u1DB0\u0143\u5200\u12AD\u1E44\u207F\xD1\u041F\u039D\u1D28\u0578\u03D6\u01F8\u0147\u1E46\u0145\u1E4A\u1E48\u0E17\u014A\u04E2\u04E3\u04E4\u04E5\u045B\u045D\u0439\u1962\u048A\u1D3B\u{1F1F3}"],["n","\u05D7\u{1D48F}\u{1D4F7}\u{1D663}\u{1D45B}\u{1D593}\u{1D52B}\u{1D5C7}\u{1D697}\u{1D5FB}\u1952\u24DD\u03AE\uFF4E\u01F9\u1D12\u0144\xF1\u1F97\u03B7\u1E45\u0148\u1E47\u0272\u0146\u1E4B\u1E49\u0572\u0E96\u054C\u019E\u014B\u24A9\u0E20\u0E01\u0273\u043F\u0149\u043B\u0509\u0220\u1F20\u1F21\u1FC3\u0564\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1FC4\u1FC6\u1FC7\u1FC2\u1F22\u1F23\u1F24\u1F25\u1F26\u1F27\u1F74\u1F75\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u0235\u{1D6C8}\u{1D702}\u{1D73C}\u{1D776}\u{1D7B0}\u{1D55F}\u{1D62F}\u{1D427}\u{1D4C3}\u1D87\u1D70\u1965\u2229"],["O","\uA132\u{1F13E}\u{10292}\u{1D7EC}\uA4F3\u2C9E\u{10404}\u{102AB}\u{104C2}\u{1D79E}\u{1F15E}\u2365\u25EF\u2D41\u2296\uFF10\u229D\u{1D764}\u0472\u03F4\u{1D6B6}\u{1D72A}\u047A\u04E6\u04E8\u04EA\u038C\u0298\u{1D40E}\u01D1\xD2\u014E\xD3\xD4\xD5\u020C\u020E\u31FF\u274D\u24C4\uFF2F\u1F4B\u30ED\u2764\u0AE6\u2295\xD8\u0424\u053E\u0398\u01A0\u1D3C\u1D52\u24AA\u0150\xD6\u2092\xA4\u25CA\u03A6\u3007\u039F\u041E\u0555\u0B20\u0D20\u0BE6\u05E1\u1ED2\u1ED0\u1ED6\u1ED4\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u022E\u0230\u022A\u1ECE\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u01FE\u019F\u2D54\u07C0\u17F0\u235C\u2394\u2395\u29B0\u29B1\u29B2\u29B3\u29B4\u29B5\u29B6\u29B7\u29B8\u29B9\u29BA\u29BB\u29BC\u29BD\u29BE\u29BF\u29C0\u29C1\u29C2\u29C3\u1F48\u1F49\u1F4A\u1F4C\u1F4D"],["o","\u{1D698}\u{1D6D0}\u{1D5C8}\u{1D7BC}\u101D\u2C9F\u{1D664}\u1040\u{1042C}\u{1D52C}\u{104EA}\u{1D4F8}\u{1F1F4}\u2364\u25CB\u03D9\u{1F17E}\u{1D4AA}\u{1D5AE}\u{1D7E2}\u{1D7F6}\u{1D67E}\u{1D630}\u{1D5FC}\u{1D560}\u{1D70A}\u{1D428}\u{1D77E}\u{1D7B8}\u1424\u24DE\u0473\u19D0\u1972\xF0\uFF4F\u0C20\u199E\u0553\xF2\u04E9\u04E7\xF3\xBA\u014D\xF4\u01D2\u020F\u014F\u1ED3\u1ED1\u020D\u1ED7\u1ED5\xF5\u03C3\u1E4D\u022D\u1E4F\u1F44\u1E51\u1E53\u022F\u022B\u0E4F\u1D0F\u0151\xF6\u047B\u043E\u12D0\u01ED\u0231\u09E6\u0B66\u0665\u0C66\u0CE6\u0D66\u0E50\u0ED0\u03BF\u0585\u1D11\u0966\u0A66\u1ECF\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\xF8\u01FF\u0275\u056E\u1F40\u1F41\u03CC\u1F78\u1F79\u1F42\u1F43\u1F45"],["P","\u{1F13F}\uA4D1\u{1D6B8}\u{1D67F}\u{1D7A0}\u{1D64B}\uABB2\u2CA2\u{1D4AB}\u{1D766}\u{1D443}\u{1D477}\u{1D5E3}\u{1D40F}\u{10295}\u{1D72C}\u{1D617}\u{1D4DF}\u{1D5AF}\u{1D6F2}\u13E2\u{1F15F}\u048E\u{1F17F}\u24C5\uFF30\u01A4\u146D\u5C38\u1E56\u0580\u03C6\u0584\u1D18\u1D3E\u1D56\u24AB\u1E54\uFF71\u05E7\u0420\u12E8\u1D29\u2C63\u2119\u03A1\u1FEC\u1478\u1476\u1477\u1479\u146C\u146E\u{1F1F5}\u20B1"],["p","\u048F\u2117\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\u1FE5\u03C1\u0440\u01BF\u01F7\u1FE4\u2374\u{1D4F9}\u{1D4C5}\u{1D429}\u{1D45D}\u{1D491}\u{1D52D}\u{1D561}\u{1D595}\u{1D5C9}\u{1D5FD}\u{1D631}\u{1D665}\u{1D699}\u{1D6D2}\u{1D746}\u{1D7BA}\u{1D70C}\u{1D780}"],["Q","\u{1F140}\u{1F180}\u{1F160}\u24C6\uFF31\u211A\u2D55\u051A\u{1D410}\u{1D444}\u{1D478}\u{1D4AC}\u{1D4E0}\u{1D680}\u{1D618}\u{1D64C}\u{1D5B0}\u{1D57C}\u{1D514}\u{1D5E4}\u{1F1F6}"],["q","\u24E0\uFF51\u0563\u24AC\u06F9\u0566\u146B\u024B\u024A\u051B\u{1D5CA}\u{1D45E}\u{1D632}\u{1D562}\u{1D69A}\u{1D492}\u{1D596}\u{1D42A}\u{1D52E}\u{1D4FA}\u{1D666}"],["R","\u211E\u211F\uAB71\u13D2\u{104B4}\uABA2\u13A1\uA4E3\u{1F181}\u{1F161}\u24C7\uFF32\u1D19\u0212\u0280\u1587\u044F\u5C3A\u0154\u042F\u0AB0\u01A6\u1D3F\u12EA\u1E5A\u024C\u0281\u211B\u211C\u211D\u1E58\u0158\u0210\u1E5C\u0156\u1E5E\u2C64\u{1D411}\u{1D445}\u{1D479}\u{1D4E1}\u{1D57D}\u{1D5B1}\u{1D5E5}\u{1D619}\u{1D64D}\u{1D681}\u16B1\u{1F1F7}\u1D1A"],["r","\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u0433\u0550\u027E\u196C\u1E5F\u024D\u02B3\u24AD\u027C\u0453\u1D26\u1D89\u{1D42B}\u{1D45F}\u{1D493}\u{1D4C7}\u{1D4FB}\u{1D52F}\u{1D563}\u{1D597}\u{1D5CB}\u{1D5FF}\u{1D633}\u{1D667}\u1D72\u0491\u1D63"],["S","\u{1F142}\uA1D9\u{1D4E2}\u{1D5E6}\u13DA\u{1D4AE}\u13D5\u{1D682}\u{1D412}\uA4E2\u{1D5B2}\u{1D516}\u{1D64E}\u{10296}\u{1D57E}\u{10420}\u{1D61A}\u{1D54A}\u{1D446}\u{1D47A}\u{1F182}\u{1F162}\u24C8\uFF33\u1E68\u015E\u0586\u054F\u0218\u02E2\u24AE\u0405\u1E60\u0160\u015A\u1E64\u015C\u1E66\u1E62\u0D1F\u1515\u1516\u1522\u1521\u1523\u1524"],["s","\u24E2\uA731\u{10448}\uABAA\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u0282\u1E63\u1E69\u0455\u015F\u0219\u023F\u1D8A\u0C15\u{1D42C}\u{1D460}\u{1D494}\u{1D4C8}\u{1D4FC}\u{1D530}\u{1D564}\u{1D598}\u{1D5CC}\u{1D600}\u{1D634}\u{1D668}\u{1D69C}\u078E\u{1F1F8}"],["T","\u{1F143}\u{1F183}\u{10315}\u{1D6BB}\u{1D6F5}\u{1D54B}\u{1D57F}\u{1D47B}\u{102B1}\u{10297}\u{1D5B3}\u{1D64F}\u{1F768}\u{1D769}\u{1D7A3}\u{1D683}\u{1D61B}\u{1D447}\uA4D4\u27D9\u{1D413}\u2CA6\u{1D5E7}\u22A4\u{1D517}\u13A2\uAB72\u{1D4AF}\u{1F163}\u23C7\u23C9\u24C9\uFF34\u0442\u04AC\u04AD\u01AC\u30A4\u0166\u0535\u03C4\u1D1B\u1D40\uFF72\u1355\u03EE\u0164\u22A5\u01AE\u03A4\u0422\u4E0B\u1E6A\u1E6C\u021A\u0162\u1E70\u1E6E\u4E05\u4E01\u142A\u{1D6D5}\u{1D70F}\u{1D749}\u{1D783}\u{1D7BD}\u{1D4E3}\u3112\u{1F1F9}\u1325"],["t","\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0236\u0A6E\u0567\u0287\u2020\u0163\u1E71\u1E6F\u01AD\u0167\u1D57\u24AF\u0288\u0565\u01AB\u{1D42D}\u{1D461}\u{1D495}\u{1D4C9}\u{1D4FD}\u{1D531}\u{1D565}\u{1D599}\u{1D5CD}\u{1D601}\u{1D635}\u{1D669}\u{1D69D}\u30CA"],["U","\u{1F144}\uA4F4\u{104CE}\uA4A4\u{1F184}\u{1F164}\u0168\u016C\u016E\u1457\u1458\u01D3\u01D5\u01D7\u01D9\u24CA\uFF35\u0216\u144C\u51F5\u01B1\u0574\u0531\uA4F5\u0426\u016A\u0544\u01B2\u1640\u1D41\u1D58\u24B0\u0170\u0AAA\xDC\u054D\xD9\xDA\xDB\u1E78\u1E7A\u01DB\u1EE6\u0214\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244\u1969\u1467\u222A\u162E\u22C3\u{1D414}\u{1D448}\u{1D47C}\u{1D4B0}\u{1D4E4}\u{1D518}\u{1D54C}\u{1D580}\u{1D5B4}\u{1D5E8}\u{1D61C}\u{1D650}\u{1D684}\u{1F1FA}"],["u","\u1F7A\u1F7B\u24E4\uFF55\xF9\u0169\u016B\u1EEB\u1E77\u1E79\u016D\u01D6\u1EEF\u1959\u01DA\u01DC\u1F57\u03C5\u03B0\u0E19\u057D\u028A\u01D8\u01D4\xFA\u016F\u1D1C\u0171\u0173\u0E22\xFB\u1E7B\u0446\u1201\xFC\u1D7E\u1D64\xB5\u028B\u1EE7\u0215\u0217\u01B0\u1EE9\u1EED\u1EF1\u1EE5\u1E73\u1E75\u0289\u1FE0\u1FE1\u1FE2\u1FE3\u1FE6\u1FE7\u1F50\u1F51\u03CB\u03CD\u1F52\u1F53\u1F54\u1F55\u1F56\u1954\u{1D42E}\u{1D462}\u{1D496}\u{1D4CA}\u{1D4FE}\u{1D532}\u{1D566}\u{1D59A}\u{1D5CE}\u1D99"],["V","\u{1F145}\uA4E6\u{1D47D}\u{1D5B5}\u{1D61D}\u13D9\u{1D685}\u{1D651}\u{1D415}\u{1F185}\u{1F165}\u24CB\uFF36\u142F\u0474\u1D5B\u24B1\u06F7\u1E7E\u2174\u2164\u1E7C\u0667\u2D38\u0476\u143A\u143B\u{1F1FB}\u{1D4E5}"],["v","\u1200\u24E5\uFF56\u{1D710}\u{1D74A}\u1E7D\u1E7F\u0C6E\u0E07\u0475\u05E2\u1D20\u03BD\u05D8\u1D65\u0477\u17F4\u1601\u{1D66B}\u{1D69F}\u{1D6CE}\u{1D708}\u{1D742}\u{1D77C}\u{1D7B6}\u{1D637}\u{1D603}\u{1D4FF}"],["W","\u{1F146}\u13D4\u13B3\u{1D47E}\uA4EA\u{1D4B2}\u{1D61E}\u{1F186}\u24CC\u{1F166}\uFF57\uFF37\u1E82\u1FA7\u15EF\u1955\u5C71\u0460\u0E9F\u0C1A\u0561\u0429\u0428\u03CE\u0449\u0E2C\u0448\u164E\u1D42\u02B7\u24B2\u0E1D\u1220\u1E84\u051C\u1E80\u0174\u1E86\u1E88\u0D27\u163A\u047F\u1661\u019C\u20A9\u{1F1FC}"],["w","\u1E81\uAB83\u1E83\u24E6\u2375\u0175\u1E87\u1E85\u1E98\u1E89\u2C73\u1F7C\u1F60\u1F61\u1F62\u1F63\u03C9\u1F64\u1F65\u1F66\u1F67\u1FF2\u1FF3\u1FF4\u1FF6\u1FF7\u2C72\u0461\u051D\u1D21\u1F7D\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u026F\u{1D755}\u{1D7C9}\u{1D78F}"],["X","\u{1F7A8}\u{1F7A9}\u{1F7AA}\u{1F147}\u{1F7AB}\u{1F7AC}\u{10317}\u2CAC\uA4EB\u{1D583}\u{1D7A6}\u{1D61F}\u{10290}\u{1D6BE}\u{1D76C}\u{1D732}\uA7B3\u{10322}\u{1D5B7}\u{1D44B}\u{1D54F}\u{1D51B}\u{102B4}\u{1D5EB}\u{1F187}\u{1F167}\u274C\u24CD\u{1D4E7}\uFF38\u1E8A\u166D\u03C7\u3128\u{1D4B3}\u04FE\u10EF\u04FC\u04B3\u0416\u03A7\u04B2\u1D61\u02E3\u24B3\u05D0\u1238\u1E8C\uA2BC\u2169\u0425\u2573\u166E\u1541\u157D\u2179\u16B7\u2D5D\u{1D653}\u{1D687}\u4E42\u{1D417}\u{1F1FD}"],["x","\u24E7\uFF58\u0445\u1E8B\xD7\u2093\u292B\u292C\u2A2F\u1E8D\u1D8D\u{1D66D}\u04FD\u{1D639}\u{1D431}\u{1D6A1}\u2A30\uFF92\u{1D501}"],["Y","\u2CA8\u{1D688}\u{1D44C}\u{1D5EC}\u{1D418}\uA4EC\u{1D480}\u{1D730}\u{102B2}\u{1F188}\u{1F168}\u24CE\uFF39\u1F5B\u01B3\u311A\u028F\u2144\u03D4\uFFE5\xA5\u054E\u03D3\u03B3\u05E5\u04F2\u0427\u040E\u1203\u0178\u024E\u03E4\u03A5\u03D2\u04AE\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u1EF6\u1EF4\u1FE8\u1FE9\u1FEA\u1FEB\u1F59\u1F5D\u1F5F\u03AB\u038E\u04EE\u04F0\u04B0\u04B1\u{1D550}\u{1F1FE}"],["y","\u{1F148}\u13BD\u13A9\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u0443\u10E7\u1E99\u1EF5\u01B4\u024F\u1D5E\u0263\u02B8\u1D8C\u04AF\u24B4\u04F3\u04F1\u04EF\u045E\u0423\u028E"],["Z","\u{1F149}\uA4DC\u{1D5ED}\u{1D419}\u2621\u13C3\u{1D621}\u{1F189}\u{1F169}\u24CF\uFF3A\u1E94\u01B5\u4E59\u1E90\u0224\u1DBB\u24B5\u0179\u2124\u0396\u017B\u017D\u1E92\u2C6B\u{1F1FF}"],["z","\uAB93\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u1D22\u130A\u0290\u2C6C\u1D8E\u0291\u1646"]])),j}var Ie;function we(){if(Ie)return U;Ie=1,Object.defineProperty(U,"__esModule",{value:!0}),U.resolveConfusablesTransformer=e;const a=X(),l=Ve();function e(){return(0,a.remapCharactersTransformer)(l.confusables)}return U}var F={},L={},xe;function Ye(){return xe||(xe=1,Object.defineProperty(L,"__esModule",{value:!0}),L.dictionary=void 0,L.dictionary=new Map([["a","@4"],["c","("],["e","3"],["g","6"],["i","1|!"],["l","/"],["o","0"],["s","$5"],["t","7"],["z","2"]])),L}var Ae;function Ee(){if(Ae)return F;Ae=1,Object.defineProperty(F,"__esModule",{value:!0}),F.resolveLeetSpeakTransformer=e;const a=X(),l=Ye();function e(){return(0,a.remapCharactersTransformer)(l.dictionary)}return F}var H={},Re;function qe(){if(Re)return H;Re=1,Object.defineProperty(H,"__esModule",{value:!0}),H.toAsciiLowerCaseTransformer=e;const a=v(),l=q();function e(){return(0,l.createSimpleTransformer)(n=>(0,a.isUpperCase)(n)?(0,a.invertCaseOfAlphabeticChar)(n):n)}return H}var Oe;function Ge(){return Oe||(Oe=1,(function(a){Object.defineProperty(a,"__esModule",{value:!0}),a.englishDataset=a.englishRecommendedTransformers=a.englishRecommendedWhitelistMatcherTransformers=a.englishRecommendedBlacklistMatcherTransformers=void 0;const l=le(),e=We(),n=Se(),r=we(),o=Ee(),d=qe();a.englishRecommendedBlacklistMatcherTransformers=[(0,r.resolveConfusablesTransformer)(),(0,o.resolveLeetSpeakTransformer)(),(0,d.toAsciiLowerCaseTransformer)(),(0,n.collapseDuplicatesTransformer)({defaultThreshold:1,customThresholds:new Map([["b",2],["e",2],["o",2],["l",2],["s",2],["g",2]])})],a.englishRecommendedWhitelistMatcherTransformers=[(0,d.toAsciiLowerCaseTransformer)(),(0,n.collapseDuplicatesTransformer)({defaultThreshold:Number.POSITIVE_INFINITY,customThresholds:new Map([[" ",1]])})],a.englishRecommendedTransformers={blacklistMatcherTransformers:a.englishRecommendedBlacklistMatcherTransformers,whitelistMatcherTransformers:a.englishRecommendedWhitelistMatcherTransformers},a.englishDataset=new l.DataSet().addPhrase(t=>t.setMetadata({originalWord:"abo"}).addPattern((0,e.pattern)`|ab[b]o[s]|`)).addPhrase(t=>t.setMetadata({originalWord:"abeed"}).addPattern((0,e.pattern)`ab[b]eed`)).addPhrase(t=>t.setMetadata({originalWord:"africoon"}).addPattern((0,e.pattern)`africoon`)).addPhrase(t=>t.setMetadata({originalWord:"anal"}).addPattern((0,e.pattern)`|anal`).addWhitelistedTerm("analabos").addWhitelistedTerm("analagous").addWhitelistedTerm("analav").addWhitelistedTerm("analy").addWhitelistedTerm("analog").addWhitelistedTerm("an al").addPattern((0,e.pattern)`danal`).addPattern((0,e.pattern)`eanal`).addPattern((0,e.pattern)`fanal`).addWhitelistedTerm("fan al").addPattern((0,e.pattern)`ganal`).addWhitelistedTerm("gan al").addPattern((0,e.pattern)`ianal`).addWhitelistedTerm("ian al").addPattern((0,e.pattern)`janal`).addWhitelistedTerm("trojan al").addPattern((0,e.pattern)`kanal`).addPattern((0,e.pattern)`lanal`).addWhitelistedTerm("lan al").addPattern((0,e.pattern)`lanal`).addWhitelistedTerm("lan al").addPattern((0,e.pattern)`oanal|`).addPattern((0,e.pattern)`panal`).addWhitelistedTerm("pan al").addPattern((0,e.pattern)`qanal`).addPattern((0,e.pattern)`ranal`).addPattern((0,e.pattern)`sanal`).addPattern((0,e.pattern)`tanal`).addWhitelistedTerm("tan al").addPattern((0,e.pattern)`uanal`).addWhitelistedTerm("uan al").addPattern((0,e.pattern)`vanal`).addWhitelistedTerm("van al").addPattern((0,e.pattern)`wanal`).addPattern((0,e.pattern)`xanal`).addWhitelistedTerm("texan al").addPattern((0,e.pattern)`yanal`).addPattern((0,e.pattern)`zanal`)).addPhrase(t=>t.setMetadata({originalWord:"anus"}).addPattern((0,e.pattern)`anus`).addWhitelistedTerm("an us").addWhitelistedTerm("tetanus").addWhitelistedTerm("uranus").addWhitelistedTerm("janus").addWhitelistedTerm("manus")).addPhrase(t=>t.setMetadata({originalWord:"arabush"}).addPattern((0,e.pattern)`arab[b]ush`)).addPhrase(t=>t.setMetadata({originalWord:"arse"}).addPattern((0,e.pattern)`|ars[s]e`).addWhitelistedTerm("arsen")).addPhrase(t=>t.setMetadata({originalWord:"ass"}).addPattern((0,e.pattern)`|ass`).addWhitelistedTerm("45s").addWhitelistedTerm("assa").addWhitelistedTerm("assem").addWhitelistedTerm("assen").addWhitelistedTerm("asser").addWhitelistedTerm("assess").addWhitelistedTerm("asset").addWhitelistedTerm("assev").addWhitelistedTerm("assi").addWhitelistedTerm("assoc").addWhitelistedTerm("assoi").addWhitelistedTerm("assu")).addPhrase(t=>t.setMetadata({originalWord:"bastard"}).addPattern((0,e.pattern)`bas[s]tard`)).addPhrase(t=>t.setMetadata({originalWord:"bestiality"}).addPattern((0,e.pattern)`be[e][a]s[s]tial`)).addPhrase(t=>t.setMetadata({originalWord:"bitch"}).addPattern((0,e.pattern)`bitch`).addPattern((0,e.pattern)`bich|`)).addPhrase(t=>t.setMetadata({originalWord:"blowjob"}).addPattern((0,e.pattern)`b[b]l[l][o]wj[o]b`)).addPhrase(t=>t.setMetadata({originalWord:"bollocks"}).addPattern((0,e.pattern)`bol[l]ock`)).addPhrase(t=>t.setMetadata({originalWord:"boob"}).addPattern((0,e.pattern)`boob`)).addPhrase(t=>t.setMetadata({originalWord:"boonga"}).addPattern((0,e.pattern)`boonga`).addWhitelistedTerm("baboon ga")).addPhrase(t=>t.setMetadata({originalWord:"buttplug"}).addPattern((0,e.pattern)`buttplug`)).addPhrase(t=>t.setMetadata({originalWord:"chingchong"}).addPattern((0,e.pattern)`chingchong`)).addPhrase(t=>t.setMetadata({originalWord:"chink"}).addPattern((0,e.pattern)`chink`).addWhitelistedTerm("chin k")).addPhrase(t=>t.setMetadata({originalWord:"cock"}).addPattern((0,e.pattern)`|cock|`).addPattern((0,e.pattern)`|cocks`).addPattern((0,e.pattern)`|cockp`).addPattern((0,e.pattern)`|cocke[e]|`).addWhitelistedTerm("cockney")).addPhrase(t=>t.setMetadata({originalWord:"cuck"}).addPattern((0,e.pattern)`cuck`).addWhitelistedTerm("cuckoo")).addPhrase(t=>t.setMetadata({originalWord:"cum"}).addPattern((0,e.pattern)`|cum`).addWhitelistedTerm("cumu").addWhitelistedTerm("cumb")).addPhrase(t=>t.setMetadata({originalWord:"cunt"}).addPattern((0,e.pattern)`|cunt`).addPattern((0,e.pattern)`cunt|`)).addPhrase(t=>t.setMetadata({originalWord:"deepthroat"}).addPattern((0,e.pattern)`deepthro[o]at`).addPattern((0,e.pattern)`deepthro[o]t`)).addPhrase(t=>t.setMetadata({originalWord:"dick"}).addPattern((0,e.pattern)`|dck|`).addPattern((0,e.pattern)`dick`).addWhitelistedTerm("benedick").addWhitelistedTerm("dickens").addWhitelistedTerm("dickety").addWhitelistedTerm("dickory")).addPhrase(t=>t.setMetadata({originalWord:"dildo"}).addPattern((0,e.pattern)`dildo`)).addPhrase(t=>t.setMetadata({originalWord:"doggystyle"}).addPattern((0,e.pattern)`d[o]g[g]ys[s]t[y]l[l]`)).addPhrase(t=>t.setMetadata({originalWord:"double penetration"}).addPattern((0,e.pattern)`double penetra`)).addPhrase(t=>t.setMetadata({originalWord:"dyke"}).addPattern((0,e.pattern)`dyke`).addWhitelistedTerm("van dyke")).addPhrase(t=>t.setMetadata({originalWord:"ejaculate"}).addPattern((0,e.pattern)`e[e]jacul`).addPattern((0,e.pattern)`e[e]jakul`).addPattern((0,e.pattern)`e[e]acul[l]ate`)).addPhrase(t=>t.setMetadata({originalWord:"fag"}).addPattern((0,e.pattern)`|fag`).addPattern((0,e.pattern)`fggot`)).addPhrase(t=>t.setMetadata({originalWord:"felch"}).addPattern((0,e.pattern)`fe[e]l[l]ch`)).addPhrase(t=>t.setMetadata({originalWord:"fellatio"}).addPattern((0,e.pattern)`f[e][e]llat`)).addPhrase(t=>t.setMetadata({originalWord:"finger bang"}).addPattern((0,e.pattern)`fingerbang`)).addPhrase(t=>t.setMetadata({originalWord:"fisting"}).addPattern((0,e.pattern)`fistin`)).addPhrase(t=>t.setMetadata({originalWord:"fuck"}).addPattern((0,e.pattern)`f[?]ck`).addPattern((0,e.pattern)`|fk`).addPattern((0,e.pattern)`|fu|`).addPattern((0,e.pattern)`|fuk`).addWhitelistedTerm("fick").addWhitelistedTerm("kung-fu").addWhitelistedTerm("kung fu")).addPhrase(t=>t.setMetadata({originalWord:"gangbang"}).addPattern((0,e.pattern)`g[?]ngbang`)).addPhrase(t=>t.setMetadata({originalWord:"handjob"}).addPattern((0,e.pattern)`h[?]ndjob`)).addPhrase(t=>t.setMetadata({originalWord:"hentai"}).addPattern((0,e.pattern)`h[e][e]ntai`)).addPhrase(t=>t.setMetadata({originalWord:"hooker"}).addPattern((0,e.pattern)`hooker`)).addPhrase(t=>t.setMetadata({originalWord:"incest"}).addPattern((0,e.pattern)`incest`)).addPhrase(t=>t.setMetadata({originalWord:"jerk off"}).addPattern((0,e.pattern)`jerkoff`)).addPhrase(t=>t.setMetadata({originalWord:"jizz"}).addPattern((0,e.pattern)`jizz`)).addPhrase(t=>t.setMetadata({originalWord:"kike"}).addPattern((0,e.pattern)`kike`)).addPhrase(t=>t.setMetadata({originalWord:"lubejob"}).addPattern((0,e.pattern)`lubejob`)).addPhrase(t=>t.setMetadata({originalWord:"masturbate"}).addPattern((0,e.pattern)`m[?]sturbate`).addPattern((0,e.pattern)`masterbate`)).addPhrase(t=>t.setMetadata({originalWord:"negro"}).addPattern((0,e.pattern)`negro`).addWhitelistedTerm("montenegro").addWhitelistedTerm("negron").addWhitelistedTerm("stoneground").addWhitelistedTerm("winegrow")).addPhrase(t=>t.setMetadata({originalWord:"nigger"}).addPattern((0,e.pattern)`n[i]gger`).addPattern((0,e.pattern)`n[i]gga`).addPattern((0,e.pattern)`|nig|`).addPattern((0,e.pattern)`|nigs|`).addWhitelistedTerm("snigger")).addPhrase(t=>t.setMetadata({originalWord:"orgasm"}).addPattern((0,e.pattern)`[or]gasm`).addWhitelistedTerm("gasma")).addPhrase(t=>t.setMetadata({originalWord:"orgy"}).addPattern((0,e.pattern)`orgy`).addPattern((0,e.pattern)`orgies`).addWhitelistedTerm("porgy")).addPhrase(t=>t.setMetadata({originalWord:"penis"}).addPattern((0,e.pattern)`pe[e]nis`).addPattern((0,e.pattern)`|pnis`).addWhitelistedTerm("pen is")).addPhrase(t=>t.setMetadata({originalWord:"piss"}).addPattern((0,e.pattern)`|piss`)).addPhrase(t=>t.setMetadata({originalWord:"porn"}).addPattern((0,e.pattern)`|prn|`).addPattern((0,e.pattern)`porn`).addWhitelistedTerm("p orna")).addPhrase(t=>t.setMetadata({originalWord:"prick"}).addPattern((0,e.pattern)`|prick[s]|`)).addPhrase(t=>t.setMetadata({originalWord:"pussy"}).addPattern((0,e.pattern)`p[u]ssy`)).addPhrase(t=>t.setMetadata({originalWord:"rape"}).addPattern((0,e.pattern)`|rape`).addPattern((0,e.pattern)`|rapis[s]t`).addWhitelistedTerm("rapper")).addPhrase(t=>t.setMetadata({originalWord:"retard"}).addPattern((0,e.pattern)`retard`)).addPhrase(t=>t.setMetadata({originalWord:"scat"}).addPattern((0,e.pattern)`|s[s]cat|`)).addPhrase(t=>t.setMetadata({originalWord:"semen"}).addPattern((0,e.pattern)`|s[s]e[e]me[e]n`)).addPhrase(t=>t.setMetadata({originalWord:"sex"}).addPattern((0,e.pattern)`|s[s]e[e]x|`).addPattern((0,e.pattern)`|s[s]e[e]xy|`)).addPhrase(t=>t.setMetadata({originalWord:"shit"}).addPattern((0,e.pattern)`|shit`).addPattern((0,e.pattern)`shit|`).addWhitelistedTerm("s hit").addWhitelistedTerm("sh it").addWhitelistedTerm("shi t").addWhitelistedTerm("shitake")).addPhrase(t=>t.setMetadata({originalWord:"slut"}).addPattern((0,e.pattern)`s[s]lut`)).addPhrase(t=>t.setMetadata({originalWord:"spastic"}).addPattern((0,e.pattern)`|spastic`)).addPhrase(t=>t.setMetadata({originalWord:"tit"}).addPattern((0,e.pattern)`|tit|`).addPattern((0,e.pattern)`|tits|`).addPattern((0,e.pattern)`|titt`).addPattern((0,e.pattern)`|tiddies`).addPattern((0,e.pattern)`|tities`)).addPhrase(t=>t.setMetadata({originalWord:"tranny"}).addPattern((0,e.pattern)`|trany`)).addPhrase(t=>t.setMetadata({originalWord:"turd"}).addPattern((0,e.pattern)`|turd`).addWhitelistedTerm("turducken")).addPhrase(t=>t.setMetadata({originalWord:"twat"}).addPattern((0,e.pattern)`|twat`).addWhitelistedTerm("twattle")).addPhrase(t=>t.setMetadata({originalWord:"vagina"}).addPattern((0,e.pattern)`vagina`).addPattern((0,e.pattern)`|v[?]gina`)).addPhrase(t=>t.setMetadata({originalWord:"wank"}).addPattern((0,e.pattern)`|wank`)).addPhrase(t=>t.setMetadata({originalWord:"whore"}).addPattern((0,e.pattern)`|wh[o]re|`).addPattern((0,e.pattern)`|who[o]res[s]|`).addWhitelistedTerm("who're"))})(Q)),Q}var z={},je;function Je(){if(je)return z;je=1,Object.defineProperty(z,"__esModule",{value:!0}),z.skipNonAlphabeticTransformer=e;const a=v(),l=q();function e(){return(0,l.createSimpleTransformer)(n=>(0,a.isAlphabetic)(n)?n:void 0)}return z}var Le;function Qe(){return Le||(Le=1,(function(a){var l=C&&C.__createBinding||(Object.create?(function(n,r,o,d){d===void 0&&(d=o);var t=Object.getOwnPropertyDescriptor(r,o);(!t||("get"in t?!r.__esModule:t.writable||t.configurable))&&(t={enumerable:!0,get:function(){return r[o]}}),Object.defineProperty(n,d,t)}):(function(n,r,o,d){d===void 0&&(d=o),n[d]=r[o]})),e=C&&C.__exportStar||function(n,r){for(var o in n)o!=="default"&&!Object.prototype.hasOwnProperty.call(r,o)&&l(r,n,o)};Object.defineProperty(a,"__esModule",{value:!0}),e(re(),a),e(Be(),a),e(le(),a),e(oe(),a),e(Ne(),a),e(V(),a),e(Fe(),a),e(G(),a),e(Te(),a),e(We(),a),e(Ge(),a),e(Se(),a),e(X(),a),e(we(),a),e(Ee(),a),e(Je(),a),e(q(),a),e(qe(),a)})(C)),C}var u=Qe();const Xe=u.DataSet,Ze=u.ParserError,et=u.PhraseBuilder,tt=u.RegExpMatcher,rt=u.SyntaxKind,at=u.TextCensor,nt=u.assignIncrementingIds,st=u.asteriskCensorStrategy,it=u.collapseDuplicatesTransformer,ot=u.compareMatchByPositionAndId,dt=u.createSimpleTransformer,lt=u.createStatefulTransformer,ht=u.englishDataset,ct=u.englishRecommendedBlacklistMatcherTransformers,ut=u.englishRecommendedTransformers,ft=u.englishRecommendedWhitelistMatcherTransformers,pt=u.fixedCharCensorStrategy,mt=u.fixedPhraseCensorStrategy,gt=u.grawlixCensorStrategy,Pt=u.keepEndCensorStrategy,Tt=u.keepStartCensorStrategy,vt=u.parseRawPattern,Ct=u.pattern,Wt=u.randomCharFromSetCensorStrategy,Mt=u.remapCharactersTransformer,yt=u.resolveConfusablesTransformer,bt=u.resolveLeetSpeakTransformer,St=u.skipNonAlphabeticTransformer,_t=u.toAsciiLowerCaseTransformer;export{Xe as DataSet,Ze as ParserError,et as PhraseBuilder,tt as RegExpMatcher,rt as SyntaxKind,at as TextCensor,nt as assignIncrementingIds,st as asteriskCensorStrategy,it as collapseDuplicatesTransformer,ot as compareMatchByPositionAndId,dt as createSimpleTransformer,lt as createStatefulTransformer,u as default,ht as englishDataset,ct as englishRecommendedBlacklistMatcherTransformers,ut as englishRecommendedTransformers,ft as englishRecommendedWhitelistMatcherTransformers,pt as fixedCharCensorStrategy,mt as fixedPhraseCensorStrategy,gt as grawlixCensorStrategy,Pt as keepEndCensorStrategy,Tt as keepStartCensorStrategy,vt as parseRawPattern,Ct as pattern,Wt as randomCharFromSetCensorStrategy,Mt as remapCharactersTransformer,yt as resolveConfusablesTransformer,bt as resolveLeetSpeakTransformer,St as skipNonAlphabeticTransformer,_t as toAsciiLowerCaseTransformer};
10
+ //# sourceMappingURL=/sm/b7af592ac93a00f32c7ead0f5ef304fa1fc6ab77d340e750e7f59feb8bbe9488.map