@dogsbay/ssg 0.2.0-beta.152 → 0.2.0-beta.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package/THIRD-PARTY-NOTICES.txt +6 -6
- package/dist/package/browser.mjs +355 -88
- package/dist/package/chunks/{prebuilt-css-default-FYH7N4SF.mjs → prebuilt-css-default-ZJXSHZYU.mjs} +111 -81
- package/dist/package/chunks/{prebuilt-css-mintlify-KCT32TZ6.mjs → prebuilt-css-mintlify-42UHBQ4I.mjs} +111 -81
- package/dist/package/client.mjs +507 -44
- package/dist/package/css.mjs +46 -42
- package/dist/package/types/client/pagefind.d.ts +1 -0
- package/dist/package/types/client/search-facets.d.ts +35 -0
- package/dist/package/types/client/search-scope.d.ts +61 -0
- package/dist/package/types/client/search.d.ts +19 -8
- package/dist/package/types/core/build.d.ts +29 -0
- package/dist/package/types/core/canonical.d.ts +5 -0
- package/dist/package/types/core/config.d.ts +9 -0
- package/dist/package/types/core/links.d.ts +9 -0
- package/dist/package/types/core/taxonomy.d.ts +27 -0
- package/dist/package/types/core/unit-manifest.d.ts +2 -0
- package/dist/package/types/theme/compose.d.ts +2 -0
- package/dist/package/types/theme/regions.d.ts +22 -1
- package/package.json +29 -29
package/dist/package/client.mjs
CHANGED
|
@@ -196,7 +196,7 @@ if (document.readyState === "loading") {
|
|
|
196
196
|
document.addEventListener("astro:page-load", init);
|
|
197
197
|
|
|
198
198
|
// src/client/search.ts
|
|
199
|
-
import { loadDetails, parseQuery as parseQuery2, scriptData, search } from "@dogsbay/search/query";
|
|
199
|
+
import { loadDetails, parseQuery as parseQuery2, scriptData, search, searchSources } from "@dogsbay/search/query";
|
|
200
200
|
|
|
201
201
|
// src/client/pagefind.ts
|
|
202
202
|
import { parseQuery } from "@dogsbay/search/query";
|
|
@@ -221,14 +221,118 @@ function pagefindSearch(unitRoot) {
|
|
|
221
221
|
const more = Promise.all(hits.slice(10, 30).map((h, i) => toResult(h, i + 10)));
|
|
222
222
|
more.catch(() => {
|
|
223
223
|
});
|
|
224
|
-
|
|
224
|
+
const later = (from, n) => Promise.all(hits.slice(from, from + n).map((h, i) => toResult(h, from + i)));
|
|
225
|
+
return { results: first, more, later, total: hits.length, highlight: parseQuery(q).highlight };
|
|
225
226
|
};
|
|
226
227
|
}
|
|
227
228
|
function text(html) {
|
|
228
229
|
return new DOMParser().parseFromString(html, "text/html").body.textContent ?? "";
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
// src/client/search-scope.ts
|
|
233
|
+
function searchScopes(site, ownPrefix) {
|
|
234
|
+
const units = site?.units ?? [];
|
|
235
|
+
const own = units.find((u) => u.prefix === ownPrefix);
|
|
236
|
+
if (!own) return {};
|
|
237
|
+
const productKey = (u) => u.product ?? "";
|
|
238
|
+
const versionsOf = (product) => site?.products?.[product] ?? { versions: site?.versions, defaultVersion: site?.defaultVersion };
|
|
239
|
+
const versionDef = (u) => versionsOf(productKey(u)).versions?.find((v) => v.id === u.version);
|
|
240
|
+
const productLabel = (product) => site?.products?.[product]?.label ?? (product || site?.name || "");
|
|
241
|
+
const label = (u) => [
|
|
242
|
+
productKey(u) !== productKey(own) ? productLabel(productKey(u)) : "",
|
|
243
|
+
u.version !== void 0 ? versionDef(u)?.label ?? u.version : ""
|
|
244
|
+
].filter(Boolean).join(" \xB7 ");
|
|
245
|
+
const scoped = (u) => ({ prefix: u.prefix, product: productKey(u), label: label(u), own: u === own, versioned: u.version !== void 0 });
|
|
246
|
+
const sameLocale = units.filter((u) => u.locale === own.locale);
|
|
247
|
+
const own_ = versionsOf(productKey(own));
|
|
248
|
+
const order = (own_.versions ?? []).map((v) => v.id);
|
|
249
|
+
const rank = (u) => u === own ? -2 : u.version === own_.defaultVersion ? -1 : versionDef(u)?.prerelease ? order.length + order.indexOf(u.version) : order.indexOf(u.version);
|
|
250
|
+
const versions = sameLocale.filter((u) => productKey(u) === productKey(own) && u.version !== void 0 && (u === own || !versionDef(u)?.hidden)).sort((a, b) => rank(a) - rank(b));
|
|
251
|
+
const products = [];
|
|
252
|
+
for (const u of sameLocale) {
|
|
253
|
+
const key = productKey(u);
|
|
254
|
+
if (products.some((p) => productKey(p) === key)) continue;
|
|
255
|
+
if (key === productKey(own)) {
|
|
256
|
+
products.push(own);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const mine = sameLocale.filter((x) => productKey(x) === key);
|
|
260
|
+
const def = versionsOf(key).defaultVersion;
|
|
261
|
+
products.push(mine.find((x) => x.version === def) ?? mine.find((x) => !versionDef(x)?.hidden) ?? mine[0]);
|
|
262
|
+
}
|
|
263
|
+
const out = {};
|
|
264
|
+
if (versions.length > 1) out.versions = versions.map(scoped);
|
|
265
|
+
if (products.length > 1) out.products = products.map(scoped);
|
|
266
|
+
if (out.versions || out.products) out.version = [scoped(own)];
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
var OWN_UNIT_BOOST = 1.5;
|
|
270
|
+
|
|
271
|
+
// src/client/search-facets.ts
|
|
272
|
+
function docFacets(meta, id) {
|
|
273
|
+
const out = {};
|
|
274
|
+
const tags = meta.docs[id]?.[3];
|
|
275
|
+
if (tags?.length) out.tag = tags.map((x) => meta.tags[x]).filter((x) => typeof x === "string");
|
|
276
|
+
const ids = meta.docs[id]?.[4];
|
|
277
|
+
if (!ids || !meta.facets) return out;
|
|
278
|
+
meta.facets.forEach((f, i) => {
|
|
279
|
+
const v = ids[i];
|
|
280
|
+
if (v?.length) out[f.name] = v.map((x) => f.values[x]).filter((x) => typeof x === "string");
|
|
281
|
+
});
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
var norm = (v) => v.trim().toLowerCase();
|
|
285
|
+
function passes(values, filters, except) {
|
|
286
|
+
for (const [name, wanted] of Object.entries(filters)) {
|
|
287
|
+
if (name === except || !wanted.length) continue;
|
|
288
|
+
const have = new Set((values[name] ?? []).map(norm));
|
|
289
|
+
if (!wanted.some((w) => have.has(norm(w)))) return false;
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
function filterResults(results, valuesOf, filters) {
|
|
294
|
+
return results.filter((r) => passes(valuesOf(r), filters));
|
|
295
|
+
}
|
|
296
|
+
function countFacets(results, valuesOf, filters, names) {
|
|
297
|
+
const out = {};
|
|
298
|
+
for (const name of names) {
|
|
299
|
+
const counts = {};
|
|
300
|
+
const seen = /* @__PURE__ */ new Map();
|
|
301
|
+
for (const r of results) {
|
|
302
|
+
const values = valuesOf(r);
|
|
303
|
+
if (!values[name]?.length || !passes(values, filters, name)) continue;
|
|
304
|
+
const mine = /* @__PURE__ */ new Set();
|
|
305
|
+
for (const v of values[name]) {
|
|
306
|
+
if (mine.has(norm(v))) continue;
|
|
307
|
+
mine.add(norm(v));
|
|
308
|
+
const key = seen.get(norm(v)) ?? (seen.set(norm(v), v), v);
|
|
309
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
for (const w of filters[name] ?? []) if (!seen.has(norm(w))) counts[w] = 0;
|
|
313
|
+
if (Object.keys(counts).length) out[name] = counts;
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
function filtersFromParams(params, names) {
|
|
318
|
+
const out = {};
|
|
319
|
+
for (const f of params.getAll("filter")) {
|
|
320
|
+
const i = f.indexOf(":");
|
|
321
|
+
if (i <= 0) continue;
|
|
322
|
+
const name = f.slice(0, i);
|
|
323
|
+
const value = f.slice(i + 1);
|
|
324
|
+
if (!names.includes(name) || !value) continue;
|
|
325
|
+
if (!(out[name] ??= []).some((v) => norm(v) === norm(value))) out[name].push(value);
|
|
326
|
+
}
|
|
327
|
+
return out;
|
|
328
|
+
}
|
|
329
|
+
|
|
231
330
|
// src/client/search.ts
|
|
331
|
+
import { buildFacetTree, countActiveFilters, resolveFacetLabel, shapeFacets, sortFacetNames, toggleFilter } from "@dogsbay/search/facets";
|
|
332
|
+
var RETRY_AFTER_MS = 6e4;
|
|
333
|
+
var DIALOG_RESULTS = 30;
|
|
334
|
+
var PAGE_RESULTS = 20;
|
|
335
|
+
var SCOPES = ["version", "versions", "products"];
|
|
232
336
|
function initSearch(opts) {
|
|
233
337
|
const { signal } = opts;
|
|
234
338
|
const dialog = document.getElementById("db-search");
|
|
@@ -236,29 +340,119 @@ function initSearch(opts) {
|
|
|
236
340
|
const input = dialog.querySelector("[data-db-search-input]");
|
|
237
341
|
const list = dialog.querySelector("[data-part='listbox']");
|
|
238
342
|
const status = dialog.querySelector("[data-db-search-status]");
|
|
239
|
-
const
|
|
240
|
-
const data = scriptData(`${opts.unitRoot}_dogsbay/search/`, opts.loadScript, window);
|
|
241
|
-
const engine = document.body.dataset.search ?? "builtin";
|
|
242
|
-
const served = location.protocol !== "file:";
|
|
243
|
-
const pagefind = engine === "pagefind" || engine === "both" && served ? pagefindSearch(opts.unitRoot) : void 0;
|
|
244
|
-
const needsServer = engine === "pagefind" && !served;
|
|
343
|
+
const ui = wire(opts, { input, list, status, page: false });
|
|
245
344
|
dialog.addEventListener("db-dialog:open", () => {
|
|
246
345
|
input.focus();
|
|
247
346
|
input.select();
|
|
248
|
-
|
|
249
|
-
else if (!pagefind) void data.meta().catch(() => status.textContent = "Search index could not be loaded.");
|
|
347
|
+
ui.activate();
|
|
250
348
|
}, { signal });
|
|
251
349
|
document.addEventListener("keydown", (e) => {
|
|
252
350
|
const typing = e.target.closest("input, textarea, select, [contenteditable]");
|
|
253
351
|
if (e.key === "/" && !typing || e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey)) {
|
|
254
352
|
e.preventDefault();
|
|
255
|
-
|
|
353
|
+
const own = document.querySelector("[data-db-search-page] [data-db-search-input]");
|
|
354
|
+
if (own && !dialog.open) own.focus();
|
|
355
|
+
else if (!dialog.open) dialog.show();
|
|
256
356
|
else input.focus();
|
|
257
357
|
}
|
|
258
358
|
}, { signal });
|
|
259
|
-
|
|
359
|
+
dialog.addEventListener("click", (e) => {
|
|
260
360
|
if (e.target.closest("a")) dialog.close("api");
|
|
261
361
|
}, { signal });
|
|
362
|
+
}
|
|
363
|
+
function initSearchPage(opts) {
|
|
364
|
+
const root = document.querySelector("[data-db-search-page]");
|
|
365
|
+
if (!root) return;
|
|
366
|
+
const input = root.querySelector("[data-db-search-input]");
|
|
367
|
+
const list = root.querySelector("[data-db-search-list]");
|
|
368
|
+
const status = root.querySelector("[data-db-search-status]");
|
|
369
|
+
const params = readParams();
|
|
370
|
+
const p = params.get("p") || void 0;
|
|
371
|
+
const v = params.get("v") || void 0;
|
|
372
|
+
const scope = params.get("scope") ?? (params.get("q") ? "version" : void 0);
|
|
373
|
+
const ui = wire(opts, { input, list, status, page: true, params, scope, forward: p !== void 0 || v !== void 0 ? { product: p, version: v } : void 0 });
|
|
374
|
+
root.querySelector("form")?.addEventListener("submit", (e) => e.preventDefault(), { signal: opts.signal });
|
|
375
|
+
input.value = params.get("q") ?? "";
|
|
376
|
+
ui.activate();
|
|
377
|
+
if (input.value.trim()) ui.run();
|
|
378
|
+
}
|
|
379
|
+
function readParams() {
|
|
380
|
+
const q = new URLSearchParams(location.search);
|
|
381
|
+
if (location.hash.includes("=")) {
|
|
382
|
+
for (const [k, v] of new URLSearchParams(location.hash.slice(1))) if (!q.has(k)) q.set(k, v);
|
|
383
|
+
}
|
|
384
|
+
return q;
|
|
385
|
+
}
|
|
386
|
+
var inFragment = () => !location.search && location.hash.includes("=");
|
|
387
|
+
function wire(opts, { input, list, status, page, scope: askedScope, forward, params }) {
|
|
388
|
+
const { signal } = opts;
|
|
389
|
+
const hint = status.textContent ?? "";
|
|
390
|
+
const data = scriptData(`${opts.unitRoot}_dogsbay/search/`, opts.loadScript, window);
|
|
391
|
+
const engine = document.body.dataset.search ?? "builtin";
|
|
392
|
+
const served = location.protocol !== "file:";
|
|
393
|
+
const pagefind = engine === "pagefind" || engine === "both" && served ? pagefindSearch(opts.unitRoot) : void 0;
|
|
394
|
+
const needsServer = engine === "pagefind" && !served;
|
|
395
|
+
const siteRoot = opts.siteRoot ?? "";
|
|
396
|
+
const ownPrefix = opts.prefix ?? "";
|
|
397
|
+
let scopes = {};
|
|
398
|
+
let scope = "version";
|
|
399
|
+
let scopesLoaded;
|
|
400
|
+
const scopeKey = `db-search-scope:${new URL(siteRoot || "./", location.href).pathname}`;
|
|
401
|
+
const applyScopes = () => {
|
|
402
|
+
const site = window.__DOGSBAY_SITE__;
|
|
403
|
+
if (forward && site && forwardTo(site)) return;
|
|
404
|
+
forward = void 0;
|
|
405
|
+
if (pagefind) return;
|
|
406
|
+
scopes = searchScopes(site, ownPrefix);
|
|
407
|
+
const wanted = askedScope ?? session(() => sessionStorage.getItem(scopeKey));
|
|
408
|
+
const before = scope;
|
|
409
|
+
if (wanted && Object.prototype.hasOwnProperty.call(scopes, wanted)) scope = wanted;
|
|
410
|
+
chips();
|
|
411
|
+
if (scope !== before && input.value.trim()) void run(++seq);
|
|
412
|
+
};
|
|
413
|
+
const forwardTo = (site) => {
|
|
414
|
+
const units = site.units ?? [];
|
|
415
|
+
const own = units.find((u) => u.prefix === ownPrefix);
|
|
416
|
+
if (!own) return false;
|
|
417
|
+
const product = forward.product ?? own.product ?? "";
|
|
418
|
+
const mine = units.filter((u) => u.locale === own.locale && (u.product ?? "") === product);
|
|
419
|
+
const def = site.products?.[product]?.defaultVersion ?? site.defaultVersion;
|
|
420
|
+
const target = forward.version !== void 0 ? mine.find((u) => u.version === forward.version || u.prefix && u.prefix.split("/").pop() === forward.version) : mine.find((u) => u === own) ?? mine.find((u) => u.version === def) ?? mine[0];
|
|
421
|
+
if (!target || target.prefix === ownPrefix || !target.search) return false;
|
|
422
|
+
const keep = new URLSearchParams(params);
|
|
423
|
+
keep.delete("p");
|
|
424
|
+
keep.delete("v");
|
|
425
|
+
const query = keep.toString();
|
|
426
|
+
location.replace(`${rootOf(target.prefix)}${target.search}${query ? inFragment() ? `#${query}` : `?${query}` : ""}`);
|
|
427
|
+
return true;
|
|
428
|
+
};
|
|
429
|
+
const loadScopes = () => scopesLoaded ??= opts.loadScript(`${siteRoot}_dogsbay/site.js`).then(applyScopes, applyScopes);
|
|
430
|
+
const rootOf = (prefix) => `${siteRoot}${prefix ? `${prefix}/` : ""}`;
|
|
431
|
+
const sources = /* @__PURE__ */ new Map([[ownPrefix, data]]);
|
|
432
|
+
const dataOf = (prefix) => {
|
|
433
|
+
let d = sources.get(prefix);
|
|
434
|
+
if (!d) sources.set(prefix, d = scriptData(`${rootOf(prefix)}_dogsbay/search/`, opts.loadScript, window));
|
|
435
|
+
return d;
|
|
436
|
+
};
|
|
437
|
+
const failedAt = /* @__PURE__ */ new Map();
|
|
438
|
+
const facetsBox = (page ? list.closest("[data-db-search-page]") : list.closest("dialog"))?.querySelector("[data-db-search-facets]") ?? void 0;
|
|
439
|
+
const display = (() => {
|
|
440
|
+
try {
|
|
441
|
+
return JSON.parse(list.dataset.facets ?? "{}");
|
|
442
|
+
} catch {
|
|
443
|
+
return {};
|
|
444
|
+
}
|
|
445
|
+
})();
|
|
446
|
+
const facetNames = facetsBox ? Object.keys(display) : [];
|
|
447
|
+
let filters = page && params ? filtersFromParams(params, facetNames) : {};
|
|
448
|
+
const activate = () => {
|
|
449
|
+
if (forward) void loadScopes();
|
|
450
|
+
if (needsServer) status.textContent = list.dataset.needsServer ?? "";
|
|
451
|
+
else if (!pagefind) {
|
|
452
|
+
void data.meta().catch(() => status.textContent = "Search index could not be loaded.");
|
|
453
|
+
void loadScopes();
|
|
454
|
+
}
|
|
455
|
+
};
|
|
262
456
|
let seq = 0;
|
|
263
457
|
let parsed;
|
|
264
458
|
let timer;
|
|
@@ -275,51 +469,137 @@ function initSearch(opts) {
|
|
|
275
469
|
observer.unobserve(e.target);
|
|
276
470
|
}
|
|
277
471
|
}) : void 0;
|
|
472
|
+
const keepAddress = (q) => {
|
|
473
|
+
if (!page || forward) return;
|
|
474
|
+
const p = new URLSearchParams();
|
|
475
|
+
if (q) p.set("q", q);
|
|
476
|
+
if (scope !== "version") p.set("scope", scope);
|
|
477
|
+
for (const [name, values] of Object.entries(filters)) for (const v of values) p.append("filter", `${name}:${v}`);
|
|
478
|
+
const s = p.toString();
|
|
479
|
+
const hashed = inFragment();
|
|
480
|
+
session(() => history.replaceState(history.state, "", `${location.pathname}${s ? hashed ? `${location.search}#${s}` : `?${s}` : ""}`));
|
|
481
|
+
};
|
|
278
482
|
const run = async (mine) => {
|
|
279
483
|
const q = input.value.trim();
|
|
484
|
+
keepAddress(q);
|
|
280
485
|
if (!q) {
|
|
281
486
|
observer?.disconnect();
|
|
282
487
|
pending.clear();
|
|
283
488
|
filterBar.hidden = true;
|
|
489
|
+
if (facetsBox) facetsBox.hidden = true;
|
|
490
|
+
last = void 0;
|
|
284
491
|
list.replaceChildren();
|
|
492
|
+
more.hidden = seeAll.hidden = true;
|
|
285
493
|
status.textContent = hint;
|
|
286
494
|
return;
|
|
287
495
|
}
|
|
288
496
|
if (needsServer) return;
|
|
497
|
+
last = void 0;
|
|
289
498
|
try {
|
|
290
499
|
if (pagefind) {
|
|
291
500
|
const found = await pagefind(q);
|
|
292
501
|
if (mine !== seq) return;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
502
|
+
if (page) {
|
|
503
|
+
const rest = await found.more;
|
|
504
|
+
if (mine !== seq) return;
|
|
505
|
+
render(q, [...found.results, ...rest].slice(0, PAGE_RESULTS), found.highlight, found.total, found.later);
|
|
506
|
+
} else {
|
|
507
|
+
render(q, found.results, found.highlight, found.total);
|
|
508
|
+
const rest = await found.more;
|
|
509
|
+
if (mine === seq && rest.length) append(q, rest, found.highlight);
|
|
510
|
+
}
|
|
511
|
+
} else if (scope !== "version" && scopes[scope]) {
|
|
512
|
+
const now = Date.now();
|
|
513
|
+
const all = scopes[scope];
|
|
514
|
+
const units = all.filter((u) => u.own || now - (failedAt.get(u.prefix) ?? -Infinity) > RETRY_AFTER_MS);
|
|
515
|
+
const found = await searchSources(q, units.map((u) => ({ data: dataOf(u.prefix), boost: u.own ? OWN_UNIT_BOOST : 1, collapse: u.product })));
|
|
516
|
+
for (const i of found.missing) failedAt.set(units[i].prefix, Date.now());
|
|
517
|
+
if (mine !== seq) return;
|
|
518
|
+
parsed = found.parsed;
|
|
519
|
+
const shown = found.results.map((r) => {
|
|
520
|
+
const u = units[r.source];
|
|
521
|
+
return Object.assign(r, {
|
|
522
|
+
base: rootOf(u.prefix),
|
|
523
|
+
from: u.own ? void 0 : u.label,
|
|
524
|
+
also: r.others?.map((o) => units[o.source].label).filter(Boolean),
|
|
525
|
+
data: dataOf(u.prefix)
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
const missing = found.missing.length + all.length - units.length;
|
|
529
|
+
await show(mine, q, shown, found.parsed.highlight, missing ? ` \xB7 ${(list.dataset.unavailable ?? "{n}").replace("{n}", String(missing))}` : "");
|
|
296
530
|
} else {
|
|
297
531
|
const found = await search(q, data);
|
|
298
532
|
if (mine !== seq) return;
|
|
299
533
|
parsed = found.parsed;
|
|
300
|
-
|
|
534
|
+
await show(mine, q, found.results, found.parsed.highlight, "");
|
|
301
535
|
}
|
|
302
536
|
} catch {
|
|
303
|
-
if (mine
|
|
537
|
+
if (mine !== seq) return;
|
|
538
|
+
status.textContent = "Search index could not be loaded.";
|
|
539
|
+
if (facetsBox) facetsBox.hidden = true;
|
|
304
540
|
}
|
|
305
541
|
};
|
|
306
542
|
const filterBar = document.createElement("div");
|
|
307
543
|
filterBar.setAttribute("role", "group");
|
|
308
544
|
filterBar.setAttribute("aria-label", list.dataset.filterSection ?? "Filter by section");
|
|
309
|
-
filterBar.className = "flex flex-wrap gap-1 px-3 pb-2";
|
|
545
|
+
filterBar.className = page ? "flex flex-wrap gap-1 pb-3" : "flex flex-wrap gap-1 px-3 pb-2";
|
|
310
546
|
filterBar.hidden = true;
|
|
311
547
|
list.before(filterBar);
|
|
548
|
+
const scopeBar = document.createElement("div");
|
|
549
|
+
scopeBar.setAttribute("role", "group");
|
|
550
|
+
scopeBar.setAttribute("aria-label", list.dataset.scope ?? "Search in");
|
|
551
|
+
scopeBar.className = page ? "flex flex-wrap items-center gap-1 pb-3" : "flex flex-wrap items-center gap-1 px-3 pb-2";
|
|
552
|
+
scopeBar.hidden = true;
|
|
553
|
+
filterBar.before(scopeBar);
|
|
554
|
+
const seeAll = document.createElement("a");
|
|
555
|
+
seeAll.className = "mx-3 mb-2 mt-1 block rounded-md px-3 py-2 text-center text-sm font-medium hover:bg-accent";
|
|
556
|
+
seeAll.dataset.dbSearchSeeAll = "";
|
|
557
|
+
seeAll.hidden = true;
|
|
558
|
+
const more = document.createElement("button");
|
|
559
|
+
more.type = "button";
|
|
560
|
+
more.className = "mt-4 rounded-md border px-3 py-1.5 text-sm hover:bg-accent";
|
|
561
|
+
more.textContent = list.dataset.more ?? "More results";
|
|
562
|
+
more.hidden = true;
|
|
563
|
+
list.after(page ? more : seeAll);
|
|
564
|
+
const chips = () => {
|
|
565
|
+
const names = SCOPES.filter((n) => scopes[n]);
|
|
566
|
+
const label = { version: scopes.version?.[0]?.versioned === false ? list.dataset.scopeProduct : list.dataset.scopeVersion, versions: list.dataset.scopeVersions, products: list.dataset.scopeProducts };
|
|
567
|
+
scopeBar.replaceChildren(
|
|
568
|
+
...names.map((n) => {
|
|
569
|
+
const b = document.createElement("button");
|
|
570
|
+
b.type = "button";
|
|
571
|
+
b.dataset.scope = n;
|
|
572
|
+
b.setAttribute("aria-pressed", String(n === scope));
|
|
573
|
+
b.className = "rounded-md border px-2.5 py-0.5 text-xs hover:bg-accent aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
574
|
+
b.textContent = label[n] ?? n;
|
|
575
|
+
b.addEventListener("click", () => {
|
|
576
|
+
if (scope === n) return;
|
|
577
|
+
scope = n;
|
|
578
|
+
session(() => sessionStorage.setItem(scopeKey, n));
|
|
579
|
+
chips();
|
|
580
|
+
scopeBar.querySelector(`[data-scope="${n}"]`)?.focus();
|
|
581
|
+
if (input.value.trim()) void run(++seq);
|
|
582
|
+
else keepAddress("");
|
|
583
|
+
}, { signal });
|
|
584
|
+
return b;
|
|
585
|
+
})
|
|
586
|
+
);
|
|
587
|
+
scopeBar.hidden = names.length < 2;
|
|
588
|
+
};
|
|
312
589
|
signal?.addEventListener("abort", () => {
|
|
313
590
|
clearTimeout(timer);
|
|
314
591
|
seq++;
|
|
315
592
|
observer?.disconnect();
|
|
316
593
|
pending.clear();
|
|
317
594
|
filterBar.remove();
|
|
595
|
+
scopeBar.remove();
|
|
596
|
+
seeAll.remove();
|
|
597
|
+
more.remove();
|
|
318
598
|
});
|
|
319
599
|
const bare = (name) => name.replace(/"/g, "");
|
|
320
600
|
const sectionToken = (name) => `section:"${bare(name)}"`;
|
|
321
601
|
let refocus;
|
|
322
|
-
const
|
|
602
|
+
const sectionFilters = (q, all) => {
|
|
323
603
|
const counts = /* @__PURE__ */ new Map();
|
|
324
604
|
for (const r of all) if (r.section) counts.set(r.section, (counts.get(r.section) ?? 0) + 1);
|
|
325
605
|
const active = [...q.matchAll(/section:"([^"]*)"/gi)].map((m) => m[1].toLowerCase());
|
|
@@ -351,37 +631,199 @@ function initSearch(opts) {
|
|
|
351
631
|
refocus = void 0;
|
|
352
632
|
}
|
|
353
633
|
};
|
|
354
|
-
|
|
634
|
+
let last;
|
|
635
|
+
const show = async (mine, q, results, terms, note) => {
|
|
636
|
+
if (!facetNames.length) {
|
|
637
|
+
render(q, results, terms, results.length);
|
|
638
|
+
status.textContent += note;
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const metas = /* @__PURE__ */ new Map();
|
|
642
|
+
for (const r of results) {
|
|
643
|
+
const d = r.data ?? data;
|
|
644
|
+
if (!metas.has(d)) metas.set(d, await d.meta());
|
|
645
|
+
}
|
|
646
|
+
if (mine !== seq) return;
|
|
647
|
+
const values = new Map(results.map((r) => [r, docFacets(metas.get(r.data ?? data), r.id)]));
|
|
648
|
+
last = { q, results, terms, note, valuesOf: (r) => values.get(r) ?? {} };
|
|
649
|
+
applyFacets();
|
|
650
|
+
};
|
|
651
|
+
const applyFacets = () => {
|
|
652
|
+
if (!last) return;
|
|
653
|
+
const { q, results, terms, note, valuesOf } = last;
|
|
654
|
+
const kept = filterResults(results, valuesOf, filters);
|
|
655
|
+
renderFacets(countFacets(results, valuesOf, filters, facetNames));
|
|
656
|
+
render(q, kept, terms, kept.length);
|
|
657
|
+
status.textContent += note;
|
|
658
|
+
};
|
|
659
|
+
let refocusFacet;
|
|
660
|
+
const renderFacets = (counts) => {
|
|
661
|
+
if (!facetsBox) return;
|
|
662
|
+
const names = sortFacetNames(Object.keys(counts), display);
|
|
663
|
+
facetsBox.hidden = !names.length;
|
|
664
|
+
if (!names.length) return facetsBox.replaceChildren();
|
|
665
|
+
const box = (name, value, label, count) => {
|
|
666
|
+
const row = document.createElement("label");
|
|
667
|
+
row.className = "flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 hover:bg-accent";
|
|
668
|
+
const cb = document.createElement("input");
|
|
669
|
+
cb.type = "checkbox";
|
|
670
|
+
cb.dataset.facetName = name;
|
|
671
|
+
cb.dataset.facetValue = value;
|
|
672
|
+
cb.checked = (filters[name] ?? []).some((v) => v.toLowerCase() === value.toLowerCase());
|
|
673
|
+
cb.className = "size-3.5 rounded border-border accent-primary";
|
|
674
|
+
const text2 = document.createElement("span");
|
|
675
|
+
text2.className = "flex-1 truncate";
|
|
676
|
+
text2.textContent = label;
|
|
677
|
+
const n = document.createElement("span");
|
|
678
|
+
n.className = "text-xs text-muted-foreground";
|
|
679
|
+
n.textContent = String(count);
|
|
680
|
+
row.append(cb, text2, n);
|
|
681
|
+
return row;
|
|
682
|
+
};
|
|
683
|
+
const tree = (name, nodes) => {
|
|
684
|
+
const ul = document.createElement("ul");
|
|
685
|
+
ul.className = "space-y-0.5";
|
|
686
|
+
for (const node of nodes) {
|
|
687
|
+
const li = document.createElement("li");
|
|
688
|
+
if (node.hasValue) li.append(box(name, node.value, node.label, node.count));
|
|
689
|
+
else {
|
|
690
|
+
const span = document.createElement("span");
|
|
691
|
+
span.className = "block px-2 py-1 text-muted-foreground";
|
|
692
|
+
span.textContent = node.label;
|
|
693
|
+
li.append(span);
|
|
694
|
+
}
|
|
695
|
+
if (node.children.length) {
|
|
696
|
+
const sub = tree(name, node.children);
|
|
697
|
+
sub.classList.add("ml-4");
|
|
698
|
+
li.append(sub);
|
|
699
|
+
}
|
|
700
|
+
ul.append(li);
|
|
701
|
+
}
|
|
702
|
+
return ul;
|
|
703
|
+
};
|
|
704
|
+
const parts = [];
|
|
705
|
+
const active = countActiveFilters(filters);
|
|
706
|
+
if (active) {
|
|
707
|
+
const clear = document.createElement("button");
|
|
708
|
+
clear.type = "button";
|
|
709
|
+
clear.dataset.clearFilters = "";
|
|
710
|
+
clear.className = "mb-3 w-full rounded-md border border-border px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground";
|
|
711
|
+
clear.textContent = (list.dataset.facetsClear ?? "Clear all ({n})").replace("{n}", String(active));
|
|
712
|
+
parts.push(clear);
|
|
713
|
+
}
|
|
714
|
+
for (const name of names) {
|
|
715
|
+
const entries = shapeFacets({ [name]: counts[name] })[name] ?? [];
|
|
716
|
+
const set = document.createElement("fieldset");
|
|
717
|
+
set.className = "mb-3";
|
|
718
|
+
const legend = document.createElement("legend");
|
|
719
|
+
legend.className = "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground";
|
|
720
|
+
legend.textContent = display[name]?.title ?? name;
|
|
721
|
+
set.append(legend);
|
|
722
|
+
if (display[name]?.hierarchical || entries.some((e) => e.value.includes("/"))) set.append(tree(name, buildFacetTree(name, entries, { display })));
|
|
723
|
+
else {
|
|
724
|
+
const ul = document.createElement("ul");
|
|
725
|
+
ul.className = "space-y-0.5";
|
|
726
|
+
for (const e of entries) {
|
|
727
|
+
const li = document.createElement("li");
|
|
728
|
+
li.append(box(name, e.value, resolveFacetLabel(name, e.value, display), e.count));
|
|
729
|
+
ul.append(li);
|
|
730
|
+
}
|
|
731
|
+
set.append(ul);
|
|
732
|
+
}
|
|
733
|
+
parts.push(set);
|
|
734
|
+
}
|
|
735
|
+
facetsBox.replaceChildren(...parts);
|
|
736
|
+
if (refocusFacet !== void 0) {
|
|
737
|
+
const again = refocusFacet ? [...facetsBox.querySelectorAll("input[data-facet-name]")].find((c) => `${c.dataset.facetName}\0${c.dataset.facetValue}` === refocusFacet) : facetsBox.querySelector("[data-clear-filters]");
|
|
738
|
+
(again ?? input).focus();
|
|
739
|
+
refocusFacet = void 0;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
facetsBox?.addEventListener("change", (e) => {
|
|
743
|
+
const cb = e.target;
|
|
744
|
+
if (!cb.dataset.facetName) return;
|
|
745
|
+
const name = cb.dataset.facetName;
|
|
746
|
+
const value = cb.dataset.facetValue;
|
|
747
|
+
const selected = (filters[name] ?? []).find((v) => v.toLowerCase() === value.toLowerCase());
|
|
748
|
+
filters = toggleFilter(filters, name, selected ?? value);
|
|
749
|
+
refocusFacet = `${name}\0${value}`;
|
|
750
|
+
keepAddress(input.value.trim());
|
|
751
|
+
applyFacets();
|
|
752
|
+
}, { signal });
|
|
753
|
+
facetsBox?.addEventListener("click", (e) => {
|
|
754
|
+
if (!e.target.closest("[data-clear-filters]")) return;
|
|
755
|
+
filters = {};
|
|
756
|
+
refocusFacet = "";
|
|
757
|
+
keepAddress(input.value.trim());
|
|
758
|
+
applyFacets();
|
|
759
|
+
}, { signal });
|
|
760
|
+
let current2;
|
|
761
|
+
const render = (q, results, terms, total, later) => {
|
|
355
762
|
observer?.disconnect();
|
|
356
763
|
pending.clear();
|
|
357
|
-
|
|
358
|
-
|
|
764
|
+
sectionFilters(q, results);
|
|
765
|
+
const first = results.slice(0, page ? PAGE_RESULTS : DIALOG_RESULTS);
|
|
766
|
+
current2 = { q, results, terms, shown: first.length, total, later };
|
|
767
|
+
list.replaceChildren(...items(q, first, terms));
|
|
359
768
|
status.textContent = total ? `${total} result${total === 1 ? "" : "s"}` : `${list.dataset.noResults} \u201C${q}\u201D`;
|
|
769
|
+
more.hidden = !page || !hasMore();
|
|
770
|
+
seeAll.hidden = page || !total || !list.dataset.searchPage;
|
|
771
|
+
if (!seeAll.hidden) {
|
|
772
|
+
const p = new URLSearchParams({ q });
|
|
773
|
+
if (scope !== "version") p.set("scope", scope);
|
|
774
|
+
for (const [name, values] of Object.entries(filters)) for (const v of values) p.append("filter", `${name}:${v}`);
|
|
775
|
+
seeAll.href = `${list.dataset.searchPage}?${p}`;
|
|
776
|
+
seeAll.textContent = (list.dataset.seeAll ?? "See all {n} results").replace("{n}", String(total));
|
|
777
|
+
}
|
|
360
778
|
};
|
|
361
779
|
const append = (q, results, terms) => list.append(...items(q, results, terms));
|
|
780
|
+
const hasMore = () => !!current2 && (current2.results.length > current2.shown || !!current2.later && current2.shown < current2.total);
|
|
781
|
+
more.addEventListener("click", async () => {
|
|
782
|
+
const c = current2;
|
|
783
|
+
if (!c) return;
|
|
784
|
+
let next = c.results.slice(c.shown, c.shown + PAGE_RESULTS);
|
|
785
|
+
if (!next.length && c.later) {
|
|
786
|
+
next = await c.later(c.shown, PAGE_RESULTS).catch(() => []);
|
|
787
|
+
if (c !== current2) return;
|
|
788
|
+
c.results.push(...next);
|
|
789
|
+
}
|
|
790
|
+
const added = items(c.q, next, c.terms);
|
|
791
|
+
list.append(...added);
|
|
792
|
+
c.shown += next.length;
|
|
793
|
+
more.hidden = !next.length || !hasMore();
|
|
794
|
+
added[0]?.querySelector("a")?.focus();
|
|
795
|
+
}, { signal });
|
|
362
796
|
const items = (q, results, terms) => {
|
|
363
797
|
const items2 = [];
|
|
364
798
|
for (const r of results) {
|
|
365
|
-
const
|
|
799
|
+
const base = r.base ?? opts.unitRoot;
|
|
800
|
+
const result = option(base, r.url, terms, "block rounded-md p-3 hover:bg-accent");
|
|
366
801
|
const t = document.createElement("div");
|
|
367
802
|
t.className = "font-medium";
|
|
368
803
|
t.append(...highlight(r.title, terms));
|
|
369
|
-
|
|
370
|
-
|
|
804
|
+
result.a.appendChild(t);
|
|
805
|
+
const where = [r.from, r.section].filter(Boolean).join(" \xB7 ");
|
|
806
|
+
if (where) {
|
|
371
807
|
const s = document.createElement("div");
|
|
372
808
|
s.className = "text-xs text-muted-foreground";
|
|
373
|
-
s.textContent =
|
|
374
|
-
|
|
809
|
+
s.textContent = where;
|
|
810
|
+
result.a.appendChild(s);
|
|
375
811
|
}
|
|
376
812
|
const excerpt = document.createElement("div");
|
|
377
813
|
excerpt.className = "mt-1 line-clamp-2 text-xs text-muted-foreground";
|
|
378
|
-
|
|
379
|
-
|
|
814
|
+
result.a.appendChild(excerpt);
|
|
815
|
+
if (r.also?.length) {
|
|
816
|
+
const also = document.createElement("div");
|
|
817
|
+
also.className = "mt-1 text-xs text-muted-foreground";
|
|
818
|
+
also.textContent = `${list.dataset.alsoIn ?? "Also in"} ${r.also.join(", ")}`;
|
|
819
|
+
result.a.appendChild(also);
|
|
820
|
+
}
|
|
821
|
+
items2.push(result.li);
|
|
380
822
|
const fill = () => {
|
|
381
823
|
if (!r.fragment) return [];
|
|
382
824
|
if (r.fragment[0]) excerpt.replaceChildren(...highlight(r.fragment[0], terms));
|
|
383
825
|
return (r.sections ?? []).map(([text2, id]) => {
|
|
384
|
-
const sub = option(`${r.url}#${id}`, terms, "ml-4 block rounded-md border-l px-3 py-1.5 text-sm hover:bg-accent");
|
|
826
|
+
const sub = option(base, `${r.url}#${id}`, terms, "ml-4 block rounded-md border-l px-3 py-1.5 text-sm hover:bg-accent");
|
|
385
827
|
sub.a.append(...highlight(text2, terms));
|
|
386
828
|
return sub.li;
|
|
387
829
|
});
|
|
@@ -389,26 +831,36 @@ function initSearch(opts) {
|
|
|
389
831
|
if (r.fragment) items2.push(...fill());
|
|
390
832
|
else if (observer) {
|
|
391
833
|
const p = parsed ?? parseQuery2(q);
|
|
392
|
-
pending.set(
|
|
834
|
+
pending.set(result.li, () => void loadDetails(p, [r], r.data ?? data).then(() => result.li.after(...fill()), () => {
|
|
393
835
|
}));
|
|
394
|
-
observer.observe(
|
|
836
|
+
observer.observe(result.li);
|
|
395
837
|
}
|
|
396
838
|
}
|
|
397
839
|
return items2;
|
|
398
840
|
};
|
|
399
|
-
const option = (url, terms, className) => {
|
|
841
|
+
const option = (base, url, terms, className) => {
|
|
400
842
|
const li = document.createElement("li");
|
|
401
|
-
li.setAttribute("role", "none");
|
|
843
|
+
if (!page) li.setAttribute("role", "none");
|
|
402
844
|
li.className = "mb-1";
|
|
403
845
|
const a = document.createElement("a");
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
846
|
+
if (!page) {
|
|
847
|
+
a.setAttribute("data-part", "option");
|
|
848
|
+
a.dataset.value = url;
|
|
849
|
+
a.tabIndex = -1;
|
|
850
|
+
}
|
|
851
|
+
a.href = withHighlight(base + url, terms);
|
|
408
852
|
a.className = className;
|
|
409
853
|
li.appendChild(a);
|
|
410
854
|
return { li, a };
|
|
411
855
|
};
|
|
856
|
+
return { activate, run: () => void run(++seq) };
|
|
857
|
+
}
|
|
858
|
+
function session(f) {
|
|
859
|
+
try {
|
|
860
|
+
return f();
|
|
861
|
+
} catch {
|
|
862
|
+
return void 0;
|
|
863
|
+
}
|
|
412
864
|
}
|
|
413
865
|
function withHighlight(href, terms) {
|
|
414
866
|
if (!terms.length) return href;
|
|
@@ -557,20 +1009,28 @@ function isRelativeTarget(t) {
|
|
|
557
1009
|
|
|
558
1010
|
// src/client/mount.ts
|
|
559
1011
|
var loaded = /* @__PURE__ */ new Map();
|
|
1012
|
+
var LOAD_TIMEOUT_MS = 15e3;
|
|
560
1013
|
function loadScript(src) {
|
|
561
1014
|
let p = loaded.get(src);
|
|
562
1015
|
if (!p) {
|
|
563
|
-
|
|
1016
|
+
const mine = new Promise((resolve, reject) => {
|
|
564
1017
|
const s = document.createElement("script");
|
|
565
1018
|
s.src = src;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
1019
|
+
const timer = setTimeout(() => fail(), LOAD_TIMEOUT_MS);
|
|
1020
|
+
const fail = () => {
|
|
1021
|
+
clearTimeout(timer);
|
|
1022
|
+
s.onload = s.onerror = null;
|
|
1023
|
+
if (loaded.get(src) === mine) loaded.delete(src);
|
|
569
1024
|
reject(new Error(`could not load ${src}`));
|
|
570
1025
|
};
|
|
1026
|
+
s.onload = () => {
|
|
1027
|
+
clearTimeout(timer);
|
|
1028
|
+
resolve();
|
|
1029
|
+
};
|
|
1030
|
+
s.onerror = fail;
|
|
571
1031
|
document.head.appendChild(s);
|
|
572
1032
|
});
|
|
573
|
-
loaded.set(src, p);
|
|
1033
|
+
loaded.set(src, p = mine);
|
|
574
1034
|
}
|
|
575
1035
|
return p;
|
|
576
1036
|
}
|
|
@@ -693,7 +1153,10 @@ function mount(o = {}) {
|
|
|
693
1153
|
initPageActions(signal);
|
|
694
1154
|
if (o.switchers !== false) initSwitchers(siteRoot, signal);
|
|
695
1155
|
initAnchors(signal);
|
|
696
|
-
if (o.search !== false)
|
|
1156
|
+
if (o.search !== false) {
|
|
1157
|
+
initSearch({ unitRoot, siteRoot, prefix: body.dataset.prefix ?? "", loadScript, signal });
|
|
1158
|
+
initSearchPage({ unitRoot, siteRoot, prefix: body.dataset.prefix ?? "", loadScript, signal });
|
|
1159
|
+
}
|
|
697
1160
|
initHighlights(signal);
|
|
698
1161
|
initImageZoom(signal);
|
|
699
1162
|
const page = {
|